# HG changeset patch # User durandn # Date 1435936486 -7200 # Node ID e2a3375c5a0a0992371e4ca03aaa22d429f48b87 # Parent 7e83c61b1f87d57ca67126465d427cec7d655795 imported mdplayer source into remie project to separate development on remie player from development from platform player diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/jquery.qtip.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/css/jq-css/jquery.qtip.css Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,337 @@ +/* +* qTip - The jQuery tooltip plugin +* http://craigsworks.com/projects/qtip/ +* +* Version: 2.0.0pre +* Copyright 2009 Craig Michael Thompson - http://craigsworks.com +* +* Dual licensed under MIT or GPL Version 2 licenses +* http://en.wikipedia.org/wiki/MIT_License +* http://en.wikipedia.org/wiki/GNU_General_Public_License +* +* Date: Mon Sep 6 15:10:26 2010 +0100 +*/ + +.ui-tooltip-accessible{ + left: -10000em !important; + top: -10000em !important; + display: block !important; + visibility: hidden !important; +} + + /* IE6 ONLY - Width detection fix */ + * html .ui-tooltip-accessible{ + position: static !important; + float: left !important; + } + +.ui-tooltip, .qtip{ + position: absolute; + display: none; + + max-width: 280px; + min-width: 50px; +} + + .ui-tooltip .ui-tooltip-wrapper{ + position: relative; + overflow: hidden; + + border-width: 3px; + border-style: solid; + } + + .ui-tooltip .ui-tooltip-content{ + position: relative; + padding: 5px 9px; + + text-align: left; + word-wrap: break-word; + overflow: hidden; + } + + .ui-tooltip .ui-tooltip-titlebar{ + position: relative; + padding: 6px 35px 6px 10px; + + font-weight: bold; + } + + /*! Default close button class */ + .ui-tooltip .ui-tooltip-close{ + position: absolute; + height: 16px; + width: 16px; + padding: 1px; + right: 5px; + top: 5px; + + cursor: pointer; + line-height: 0px; + font-size: 0px; + } + + .ui-tooltip .ui-tooltip-close .ui-icon{ + height: 16px; + width: 16px; + display: block; + + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); + } + .ui-tooltip .ui-tooltip-close:hover .ui-icon{ + opacity: 1; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + } + + /*! Custom button class */ + .ui-tooltip .ui-tooltip-button{ + float: right; + } + + /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */ + .ui-tooltip-focus{ + + } + + +/*! Default tooltip style */ +.ui-tooltip-wrapper{ + border-color: #F9E98E; + background-color: #FBF7AA; + color: #A27D35; +} + + .ui-tooltip .ui-tooltip-titlebar{ + background-color: #F0DE7D; + } + + .ui-tooltip .ui-tooltip-close .ui-icon{ + background: #A27D35 url('images/close.png') no-repeat 50% 50%; + } + +/*! Plain tooltip style */ +.ui-tooltip-plain .ui-tooltip-wrapper{ + border-color: black; + border-width: 1px; + + background-color: white; + color: black; +} + + .ui-tooltip-plain .ui-tooltip-titlebar{ + background-color: white; + } + + .ui-tooltip-plain .ui-tooltip-close .ui-icon{ + background: black; + } + +/*! Light tooltip style */ +.ui-tooltip-light .ui-tooltip-wrapper{ + border-color: #E2E2E2; + background-color: white; + color: #454545; +} + + .ui-tooltip-light .ui-tooltip-titlebar{ + background-color: #f1f1f1; + } + + .ui-tooltip-light .ui-tooltip-close .ui-icon{ + background: #454545 url('images/close-light.png') no-repeat 50% 50%; + } + + +/*! Dark tooltip style */ +.ui-tooltip-dark .ui-tooltip-wrapper{ + border-color: #303030; + background-color: #505050; + color: #f3f3f3; +} + + .ui-tooltip-dark .ui-tooltip-titlebar{ + background-color: #404040; + } + + .ui-tooltip-dark .ui-tooltip-close .ui-icon{ + background: #f3f3f3 url('images/close-dark.png') no-repeat 50% 50%; + } + + +/*! Red tooltip style */ +.ui-tooltip-red .ui-tooltip-wrapper{ + border-color: #D95252; + background-color: #F78B83; + color: #912323; +} + + .ui-tooltip-red .ui-tooltip-titlebar{ + background-color: #F06D65; + } + + .ui-tooltip-red .ui-tooltip-close .ui-icon{ + background: #9C2F2F url('images/close-red.png') no-repeat 50% 50%; + } + + +/*! Green tooltip style */ +.ui-tooltip-green .ui-tooltip-wrapper{ + border-color: #90D93F; + background-color: #CAED9E; + color: #3F6219; +} + + .ui-tooltip-green .ui-tooltip-titlebar{ + background-color: #B0DE78; + } + + .ui-tooltip-green .ui-tooltip-close .ui-icon{ + background: #58792E url('images/close-green.png') no-repeat 50% 50%; + } + + +/*! Blue tooltip style */ +.ui-tooltip-blue .ui-tooltip-wrapper{ + border-color: #ADD9ED; + background-color: #E5F6FE; + color: #5E99BD; +} + + .ui-tooltip-blue .ui-tooltip-titlebar{ + background-color: #D0E9F5; + } + + .ui-tooltip-blue .ui-tooltip-close .ui-icon{ + background: #4D9FBF url('images/close-blue.png') no-repeat 50% 50%; + } + + +/*! Add shadows to your tooltips in all supported browsers */ +.ui-tooltip-shadow{ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3); + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3); + + filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray'); + -ms-filter:"progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray')"; +} + +div.ui-tooltip-tip{ + position: absolute; + margin: 0 auto; + overflow: hidden; + + line-height: 0.1px; + font-size: 1px; + + background: transparent !important; + border: 0px !important; + z-index: 10; +} + + div.ui-tooltip-tip *{ + line-height: 0.1px; + font-size: 0.1px; + } + + div.ui-tooltip-tip div{ + background: transparent !important; + border: 0px dashed transparent; + } + +#qtip-overlay{ + position: absolute; + left: -10000em; + top: -10000em; + + background-color: black; + cursor: pointer; + + /* Set this to any below 15000 (default starting z-index for qTips) */ + z-index: 14999; +} + +/*! Youtube tooltip style */ +div.ui-tooltip-youtube .ui-tooltip-wrapper{ + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + + -webkit-box-shadow: 0 0 3px #333; + -moz-box-shadow: 0 0 3px #333; + box-shadow: 0 0 3px #333; + + border-color: #CCCCCC; + border-width: 1px; + + color: white; +} + + div.ui-tooltip-youtube .ui-tooltip-close .ui-icon{ + background: #4D9FBF url('images/close-dark.png') no-repeat 50% 50%; + } + + div.ui-tooltip-youtube .ui-tooltip-titlebar, + div.ui-tooltip-youtube .ui-tooltip-content{ + background-color: rgba(0, 0, 0, 0.85); + } + + /* IE6 Fallback */ + * html div.ui-tooltip-youtube-wrapper{ + background-color: black; + } + + +/* jQuery TOOLS Tooltip style */ +div.ui-tooltip-jtools .ui-tooltip-wrapper{ + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + + border-color: white; + border-width: 2px; + + -webkit-box-shadow: 0 0 12px #333; + -moz-box-shadow: 0 0 12px #333; + box-shadow: 0 0 12px #333; + + background-color: rgba(0, 0, 0, 0.7); + background-image: -moz-linear-gradient(top, #717171, #232323); + background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323)); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#232323); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#232323)"; + + color: white; +} + + div.ui-tooltip-jtools .ui-tooltip-titlebar, + div.ui-tooltip-jtools .ui-tooltip-content{ + background: none; + } + + div.ui-tooltip-jtools .ui-tooltip-close .ui-icon{ + background: black url('images/close-dark.png') no-repeat 50% 50%; + } + + +/* Cluetip style */ +div.ui-tooltip-cluetip .ui-tooltip-wrapper{ + border-width: 0; + + -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); + box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); +} + + div.ui-tooltip-cluetip .ui-tooltip-titlebar{ + background-color: #87876A; + color: white; + } + div.ui-tooltip-cluetip .ui-tooltip-content{ + background-color: #D9D9C2; + color: #111; + } + diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_flat_75_ffffff_40x100.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_flat_75_ffffff_40x100.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_65_ffffff_1x400.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_65_ffffff_1x400.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_75_dadada_1x400.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_75_dadada_1x400.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_222222_256x240.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_222222_256x240.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_2e83ff_256x240.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_2e83ff_256x240.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_454545_256x240.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_454545_256x240.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_888888_256x240.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_888888_256x240.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_cd0a0a_256x240.png Binary file server/src/remie/static/remie/css/jq-css/themes/base/images/ui-icons_cd0a0a_256x240.png has changed diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/css/jq-css/themes/base/jquery-ui.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/css/jq-css/themes/base/jquery-ui.css Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,570 @@ +/* + * jQuery UI CSS Framework @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } +/* + * jQuery UI Accordion @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; }/* + * jQuery UI Autocomplete @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { position: absolute; cursor: default; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} +/* + * jQuery UI Button @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Datepicker @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* + * jQuery UI Dialog @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* + * jQuery UI Progressbar @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* + * jQuery UI Resizable @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* + * jQuery UI Selectable @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } +/* + * jQuery UI Slider @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* + * jQuery UI Tabs @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI CSS Framework @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/ + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; } +.ui-widget-content a { color: #222222/*{fcContent}*/; } +.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; } +.ui-widget-header a { color: #222222/*{fcHeader}*/; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } +.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/LdtPlayer-release.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/LdtPlayer-release.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,6487 @@ +/* + * + * Copyright 2010-2012 Institut de recherche et d'innovation + * contributor(s) : Karim Hamidou, Samuel Huron, Raphael Velt, Thibaut Cavalie + * + * contact@iri.centrepompidou.fr + * http://www.iri.centrepompidou.fr + * + * This software is a computer program whose purpose is to show and add annotations on a video . + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. +*/ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ + +(function(global){ + var _$LAB = global.$LAB, + + // constants for the valid keys of the options object + _UseLocalXHR = "UseLocalXHR", + _AlwaysPreserveOrder = "AlwaysPreserveOrder", + _AllowDuplicates = "AllowDuplicates", + _CacheBust = "CacheBust", + /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ + _BasePath = "BasePath", + + // stateless variables used across all $LAB instances + root_page = /^[^?#]*\//.exec(location.href)[0], + root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], + append_to = document.head || document.getElementsByTagName("head"), + + // inferences... ick, but still necessary + opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style), + +/*!START_DEBUG*/ + // console.log() and console.error() wrappers + log_msg = function(){}, + log_error = log_msg, +/*!END_DEBUG*/ + + // feature sniffs (yay!) + test_script_elem = document.createElement("script"), + explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29 + real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append? + script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order + + // XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers) + xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko + ; + +/*!START_DEBUG*/ + // define console wrapper functions if applicable + if (global.console && global.console.log) { + if (!global.console.error) global.console.error = global.console.log; + log_msg = function(msg) { global.console.log(msg); }; + log_error = function(msg,err) { global.console.error(msg,err); }; + } +/*!END_DEBUG*/ + + // test for function + function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; } + + // test for array + function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } + + // make script URL absolute/canonical + function canonical_uri(src,base_path) { + var absolute_regex = /^\w+\:\/\//; + + // is `src` is protocol-relative (begins with // or ///), prepend protocol + if (/^\/\/\/?/.test(src)) { + src = location.protocol + src; + } + // is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /) + else if (!absolute_regex.test(src) && src.charAt(0) != "/") { + // prepend `base_path`, if any + src = (base_path || "") + src; + } + // make sure to return `src` as absolute + return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src); + } + + // merge `source` into `target` + function merge_objs(source,target) { + for (var k in source) { if (source.hasOwnProperty(k)) { + target[k] = source[k]; // TODO: does this need to be recursive for our purposes? + }} + return target; + } + + // does the chain group have any ready-to-execute scripts? + function check_chain_group_scripts_ready(chain_group) { + var any_scripts_ready = false; + for (var i=0; i 0) { + for (var i=0; i=0;) { + val = queue.shift(); + $L = $L[val.type].apply(null,val.args); + } + return $L; + }, + + // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB + noConflict:function(){ + global.$LAB = _$LAB; + return instanceAPI; + }, + + // create another clean instance of $LAB + sandbox:function(){ + return create_sandbox(); + } + }; + + return instanceAPI; + } + + // create the main instance of $LAB + global.$LAB = create_sandbox(); + + + /* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html + NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?). + + The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does + proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked + document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. + For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or + fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs. + */ + (function(addEvent,domLoaded,handler){ + if (document.readyState == null && document[addEvent]){ + document.readyState = "loading"; + document[addEvent](domLoaded,handler = function(){ + document.removeEventListener(domLoaded,handler,false); + document.readyState = "complete"; + },false); + } + })("addEventListener","DOMContentLoaded"); + +})(this);/* + mustache.js — Logic-less templates in JavaScript + + See http://mustache.github.com/ for more info. +*/ + +var Mustache = function () { + var _toString = Object.prototype.toString; + + Array.isArray = Array.isArray || function (obj) { + return _toString.call(obj) == "[object Array]"; + } + + var _trim = String.prototype.trim, trim; + + if (_trim) { + trim = function (text) { + return text == null ? "" : _trim.call(text); + } + } else { + var trimLeft, trimRight; + + // IE doesn't match non-breaking spaces with \s. + if ((/\S/).test("\xA0")) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; + } else { + trimLeft = /^\s+/; + trimRight = /\s+$/; + } + + trim = function (text) { + return text == null ? "" : + text.toString().replace(trimLeft, "").replace(trimRight, ""); + } + } + + var escapeMap = { + "&": "&", + "<": "<", + ">": ">", + '"': '"', + "'": ''' + }; + + function escapeHTML(string) { + return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { + return escapeMap[s] || s; + }); + } + + var regexCache = {}; + var Renderer = function () {}; + + Renderer.prototype = { + otag: "{{", + ctag: "}}", + pragmas: {}, + buffer: [], + pragmas_implemented: { + "IMPLICIT-ITERATOR": true + }, + context: {}, + + render: function (template, context, partials, in_recursion) { + // reset buffer & set context + if (!in_recursion) { + this.context = context; + this.buffer = []; // TODO: make this non-lazy + } + + // fail fast + if (!this.includes("", template)) { + if (in_recursion) { + return template; + } else { + this.send(template); + return; + } + } + + // get the pragmas together + template = this.render_pragmas(template); + + // render the template + var html = this.render_section(template, context, partials); + + // render_section did not find any sections, we still need to render the tags + if (html === false) { + html = this.render_tags(template, context, partials, in_recursion); + } + + if (in_recursion) { + return html; + } else { + this.sendLines(html); + } + }, + + /* + Sends parsed lines + */ + send: function (line) { + if (line !== "") { + this.buffer.push(line); + } + }, + + sendLines: function (text) { + if (text) { + var lines = text.split("\n"); + for (var i = 0; i < lines.length; i++) { + this.send(lines[i]); + } + } + }, + + /* + Looks for %PRAGMAS + */ + render_pragmas: function (template) { + // no pragmas + if (!this.includes("%", template)) { + return template; + } + + var that = this; + var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) { + return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g"); + }); + + return template.replace(regex, function (match, pragma, options) { + if (!that.pragmas_implemented[pragma]) { + throw({message: + "This implementation of mustache doesn't understand the '" + + pragma + "' pragma"}); + } + that.pragmas[pragma] = {}; + if (options) { + var opts = options.split("="); + that.pragmas[pragma][opts[0]] = opts[1]; + } + return ""; + // ignore unknown pragmas silently + }); + }, + + /* + Tries to find a partial in the curent scope and render it + */ + render_partial: function (name, context, partials) { + name = trim(name); + if (!partials || partials[name] === undefined) { + throw({message: "unknown_partial '" + name + "'"}); + } + if (!context || typeof context[name] != "object") { + return this.render(partials[name], context, partials, true); + } + return this.render(partials[name], context[name], partials, true); + }, + + /* + Renders inverted (^) and normal (#) sections + */ + render_section: function (template, context, partials) { + if (!this.includes("#", template) && !this.includes("^", template)) { + // did not render anything, there were no sections + return false; + } + + var that = this; + + var regex = this.getCachedRegex("render_section", function (otag, ctag) { + // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder + return new RegExp( + "^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1) + + otag + // {{ + "(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3) + ctag + // }} + + "\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped + + otag + // {{ + "\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag). + ctag + // }} + + "\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped. + + "g"); + }); + + + // for each {{#foo}}{{/foo}} section do... + return template.replace(regex, function (match, before, type, name, content, after) { + // before contains only tags, no sections + var renderedBefore = before ? that.render_tags(before, context, partials, true) : "", + + // after may contain both sections and tags, so use full rendering function + renderedAfter = after ? that.render(after, context, partials, true) : "", + + // will be computed below + renderedContent, + + value = that.find(name, context); + + if (type === "^") { // inverted section + if (!value || Array.isArray(value) && value.length === 0) { + // false or empty list, render it + renderedContent = that.render(content, context, partials, true); + } else { + renderedContent = ""; + } + } else if (type === "#") { // normal section + if (Array.isArray(value)) { // Enumerable, Let's loop! + renderedContent = that.map(value, function (row) { + return that.render(content, that.create_context(row), partials, true); + }).join(""); + } else if (that.is_object(value)) { // Object, Use it as subcontext! + renderedContent = that.render(content, that.create_context(value), + partials, true); + } else if (typeof value == "function") { + // higher order section + renderedContent = value.call(context, content, function (text) { + return that.render(text, context, partials, true); + }); + } else if (value) { // boolean section + renderedContent = that.render(content, context, partials, true); + } else { + renderedContent = ""; + } + } + + return renderedBefore + renderedContent + renderedAfter; + }); + }, + + /* + Replace {{foo}} and friends with values from our view + */ + render_tags: function (template, context, partials, in_recursion) { + // tit for tat + var that = this; + + var new_regex = function () { + return that.getCachedRegex("render_tags", function (otag, ctag) { + return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g"); + }); + }; + + var regex = new_regex(); + var tag_replace_callback = function (match, operator, name) { + switch(operator) { + case "!": // ignore comments + return ""; + case "=": // set new delimiters, rebuild the replace regexp + that.set_delimiters(name); + regex = new_regex(); + return ""; + case ">": // render partial + return that.render_partial(name, context, partials); + case "{": // the triple mustache is unescaped + case "&": // & operator is an alternative unescape method + return that.find(name, context); + default: // escape the value + return escapeHTML(that.find(name, context)); + } + }; + var lines = template.split("\n"); + for(var i = 0; i < lines.length; i++) { + lines[i] = lines[i].replace(regex, tag_replace_callback, this); + if (!in_recursion) { + this.send(lines[i]); + } + } + + if (in_recursion) { + return lines.join("\n"); + } + }, + + set_delimiters: function (delimiters) { + var dels = delimiters.split(" "); + this.otag = this.escape_regex(dels[0]); + this.ctag = this.escape_regex(dels[1]); + }, + + escape_regex: function (text) { + // thank you Simon Willison + if (!arguments.callee.sRE) { + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + arguments.callee.sRE = new RegExp( + '(\\' + specials.join('|\\') + ')', 'g' + ); + } + return text.replace(arguments.callee.sRE, '\\$1'); + }, + + /* + find `name` in current `context`. That is find me a value + from the view object + */ + find: function (name, context) { + name = trim(name); + + // Checks whether a value is thruthy or false or 0 + function is_kinda_truthy(bool) { + return bool === false || bool === 0 || bool; + } + + var value; + + // check for dot notation eg. foo.bar + if (name.match(/([a-z_]+)\./ig)) { + var childValue = this.walk_context(name, context); + if (is_kinda_truthy(childValue)) { + value = childValue; + } + } else { + if (is_kinda_truthy(context[name])) { + value = context[name]; + } else if (is_kinda_truthy(this.context[name])) { + value = this.context[name]; + } + } + + if (typeof value == "function") { + return value.apply(context); + } + if (value !== undefined) { + return value; + } + // silently ignore unkown variables + return ""; + }, + + walk_context: function (name, context) { + var path = name.split('.'); + // if the var doesn't exist in current context, check the top level context + var value_context = (context[path[0]] != undefined) ? context : this.context; + var value = value_context[path.shift()]; + while (value != undefined && path.length > 0) { + value_context = value; + value = value[path.shift()]; + } + // if the value is a function, call it, binding the correct context + if (typeof value == "function") { + return value.apply(value_context); + } + return value; + }, + + // Utility methods + + /* includes tag */ + includes: function (needle, haystack) { + return haystack.indexOf(this.otag + needle) != -1; + }, + + // by @langalex, support for arrays of strings + create_context: function (_context) { + if (this.is_object(_context)) { + return _context; + } else { + var iterator = "."; + if (this.pragmas["IMPLICIT-ITERATOR"]) { + iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; + } + var ctx = {}; + ctx[iterator] = _context; + return ctx; + } + }, + + is_object: function (a) { + return a && typeof a == "object"; + }, + + /* + Why, why, why? Because IE. Cry, cry cry. + */ + map: function (array, fn) { + if (typeof array.map == "function") { + return array.map(fn); + } else { + var r = []; + var l = array.length; + for(var i = 0; i < l; i++) { + r.push(fn(array[i])); + } + return r; + } + }, + + getCachedRegex: function (name, generator) { + var byOtag = regexCache[this.otag]; + if (!byOtag) { + byOtag = regexCache[this.otag] = {}; + } + + var byCtag = byOtag[this.ctag]; + if (!byCtag) { + byCtag = byOtag[this.ctag] = {}; + } + + var regex = byCtag[name]; + if (!regex) { + regex = byCtag[name] = generator(this.otag, this.ctag); + } + + return regex; + } + }; + + return({ + name: "mustache.js", + version: "0.5.0-dev", + + /* + Turns a template and view into HTML + */ + to_html: function (template, view, partials, send_fun) { + var renderer = new Renderer(); + if (send_fun) { + renderer.send = send_fun; + } + renderer.render(template, view || {}, partials); + if (!send_fun) { + return renderer.buffer.join("\n"); + } + } + }); +}(); +// Underscore.js 1.2.3 +// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the MIT license. +// Portions of Underscore are inspired or borrowed from Prototype, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore +(function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== +c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c, +h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.concat,H=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,I=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&& +define.amd?define("underscore",function(){return b}):s._=b;b.VERSION="1.2.3";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a==null&&(a=[]);if(x&&a.reduce===x)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(y&&a.reduceRight===y)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g, +c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;D(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.filter===z)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(A&&a.every===A)return a.every(c, +b);j(a,function(a,g,h){if(!(e=e&&c.call(b,a,g,h)))return o});return e};var D=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(B&&a.some===B)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return q&&a.indexOf===q?a.indexOf(c)!=-1:b=D(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a, +d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex= +function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after= +function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=I||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments, +1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a=== +Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)== +"[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),function(c){J(c, +b[c]=a[c])})};var K=0;b.uniqueId=function(a){var b=K++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g, +"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},J=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);H.call(a,this._wrapped);return u(c.apply(b, +a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this); +/* main file */ +// Why is it called main ? It only loads the libs ! + +if ( window.IriSP === undefined && window.__IriSP === undefined ) { + /** + @class + the object under which everything goes. + */ + IriSP = {}; + + /** Alias to IriSP for backward compatibility */ + __IriSP = IriSP; +} + +/* underscore comes bundled with the player and we need + it ASAP, so load it that way +*/ + +IriSP._ = window._.noConflict(); +IriSP.underscore = IriSP._; + +IriSP.getLib = function(lib) { + return ( + IriSP.libFiles.useCdn && typeof IriSP.libFiles.cdn[lib] == "string" + ? IriSP.libFiles.cdn[lib] + : ( + typeof IriSP.libFiles.locations[lib] == "string" + ? IriSP.libFiles.locations[lib] + : ( + typeof IriSP.libFiles.inDefaultDir[lib] == "string" + ? IriSP.libFiles.defaultDir + IriSP.libFiles.inDefaultDir[lib] + : null + ) + ) + ) +} + +IriSP.loadLibs = function( config, metadata_url, callback ) { + // Localize jQuery variable + IriSP.jQuery = null; + var $L = $LAB.script(IriSP.getLib("jQuery")).script(IriSP.getLib("swfObject")).wait() + .script(IriSP.getLib("jQueryUI")); + + if (config.player.type === "jwplayer" || config.player.type === "allocine") { + // load our popcorn.js lookalike + $L.script(IriSP.getLib("jwplayer")); + } else { + // load the real popcorn + $L.script(IriSP.getLib("popcorn")).script(IriSP.getLib("popcorn.code")); + if (config.player.type === "youtube") { + $L.script(IriSP.getLib("popcorn.youtube")); + } + if (config.player.type === "vimeo") + $L.script(IriSP.getLib("popcorn.vimeo")); + + /* do nothing for html5 */ + } + + /* widget specific requirements */ + for (var idx in config.gui.widgets) { + if (config.gui.widgets[idx].type === "PolemicWidget" || + config.gui.widgets[idx].type === "StackGraphWidget" || + config.gui.widgets[idx].type === "SparklineWidget") { + $L.script(IriSP.getLib("raphael")); + } + if (config.gui.widgets[idx].type === "TraceWidget") { + $L.script(IriSP.getLib("tracemanager")) + } + } + + // same for modules + /* + for (var idx in config.modules) { + if (config.modules[idx].type === "PolemicWidget") + $L.script(IriSP.getLib("raphaelJs")); + } + */ + + $L.wait(function() { + IriSP.jQuery = window.jQuery.noConflict( true ); + + var css_link_jquery = IriSP.jQuery( "", { + rel: "stylesheet", + type: "text/css", + href: IriSP.getLib("cssjQueryUI"), + 'class': "dynamic_css" + } ); + var css_link_custom = IriSP.jQuery( "", { + rel: "stylesheet", + type: "text/css", + href: config.gui.css, + 'class': "dynamic_css" + } ); + + css_link_jquery.appendTo('head'); + css_link_custom.appendTo('head'); + + IriSP.setupDataLoader(); + IriSP.__dataloader.get(metadata_url, + function(data) { + /* save the data so that we could re-use it to + configure the video + */ + IriSP.__jsonMetadata = data; + callback.call(window) }); + }); +}; +IriSP.annotation_template = "{{! template for an annotation displayed in a segmentWidget }}
"; +IriSP.annotationWidget_template = "{{! template for the annotation widget }}
"; +IriSP.annotation_loading_template = "{{! template shown while the annotation widget is loading }}
 
Chargement...
"; +IriSP.annotationsListWidget_template = "{{! template for the annotation list widget }}
"; +IriSP.arrowWidget_template = "
"; +IriSP.createAnnotationWidget_template = "{{! template for the annotation creation widget }}
{{^cinecast_version}}
{{/cinecast_version}}
{{#show_from_field}} {{/show_from_field}}
{{^user_avatar}} {{/user_avatar}} {{#user_avatar}} {{/user_avatar}}
{{#tags.length}}
    {{#tags}}
  • {{/tags}}
{{#random_tags}} {{/random_tags}} {{/tags.length}} {{#polemic_mode}} {{#polemics.length}}
    {{#polemics}}
  • {{/polemics}}
{{/polemics.length}} {{/polemic_mode}}
"; +IriSP.createAnnotation_errorMessage_template = "

{{l10n.empty_annotation}}

"; +IriSP.loading_template = "
{{l10n.loading_wait}}
"; +IriSP.overlay_marker_template = "{{! the template for the small bars which is z-indexed over our segment widget }}
"; +IriSP.player_template = "{{! template for the radio player }}
{{^disable_annotate_btn}}
{{/disable_annotate_btn}} {{^disable_search_btn}}
{{/disable_search_btn}}
00:00
/
00:00
"; +IriSP.search_template = "{{! template for the search container }}
"; +IriSP.share_template = "{{! social network sharing template }} "; +IriSP.sliceWidget_template = "{{! template for the slice widget }}
{{! the whole bar }}
{{! the zone which represents our slice }}
"; +IriSP.slideShareWidget_template = "{{! template for the slideShare widget of the other }}
"; +IriSP.sliderWidget_template = "{{! template for the slider widget - it's composed of two divs we one overlayed on top of the other }}
"; +IriSP.tooltip_template = "{{! template used by the jquery ui tooltip }}
{{title}}
{{begin}} : {{end}}
{{description}}
"; +IriSP.tooltipWidget_template = "{{! template for the tooltip widget }}
"; +IriSP.tweetWidget_template = "{{! template for the tweet widget }}";/* utils.js - various utils that don't belong anywhere else */ + +/* trace function, for debugging */ + +IriSP.traceNum = 0; +IriSP.trace = function( msg, value ) { +/* + if( IriSP.config.gui.debug === true ) { + IriSP.traceNum += 1; + IriSP.jQuery( "
"+IriSP.traceNum+" - "+msg+" : "+value+"
" ).appendTo( "#Ldt-output" ); + } +*/ +}; + +/* used in callbacks - because in callbacks we lose "this", + we need to have a special function which wraps "this" in + a closure. This way, the +*/ +IriSP.wrap = function (obj, fn) { + return function() { + var args = Array.prototype.slice.call(arguments, 0); + return fn.apply(obj, args); + } +} + +/* convert a time to a percentage in the media */ +IriSP.timeToPourcent = function(time, timetotal){ + var time = Math.abs(time); + var timetotal = Math.abs(timetotal); + + return Math.floor((time/timetotal) * 100); +}; + +IriSP.padWithZeros = function(num) { + if (Math.abs(num) < 10) { + return "0" + num.toString(); + } else { + return num.toString(); + } +}; + +/* convert a number of milliseconds to a tuple of the form + [hours, minutes, seconds] +*/ +IriSP.msToTime = function(ms) { + return IriSP.secondsToTime(ms / 1000); +} +/* convert a number of seconds to a tuple of the form + [hours, minutes, seconds] +*/ +IriSP.secondsToTime = function(secs) { + var hours = Math.abs(parseInt( secs / 3600 ) % 24); + var minutes = Math.abs(parseInt( secs / 60 ) % 60); + var seconds = parseFloat(Math.abs(secs % 60).toFixed(0)); + + var toString_fn = function() { + var ret = ""; + if (hours > 0) + ret = IriSP.padWithZeros(this.hours) + ":"; + ret += IriSP.padWithZeros(this.minutes) + ":" + IriSP.padWithZeros(this.seconds); + + return ret; + } + return {"hours" : hours, "minutes" : minutes, "seconds" : seconds, toString: toString_fn}; +}; + +/* format a tweet - replaces @name by a link to the profile, #hashtag, etc. */ +IriSP.formatTweet = function(tweet) { + /* + an array of arrays which hold a regexp and its replacement. + */ + var regExps = [ + /* copied from http://codegolf.stackexchange.com/questions/464/shortest-url-regex-match-in-javascript/480#480 */ + [/((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)/gi, "$1"], + [/@(\w+)/gi, "@$1"], // matches a @handle + [/#(\w+)/gi, "#$1"],// matches a hashtag + [/(\+\+)/gi, "$1"], + [/(--)/gi, "$1"], + [/(==)/gi, "$1"], + [/(\?\?)/gi, "$1"] + ]; + + var i = 0; + for(i = 0; i < regExps.length; i++) { + tweet = tweet.replace(regExps[i][0], regExps[i][1]); + } + + return tweet; +}; + +IriSP.countProperties = function(obj) { + var count = 0; + + for(var prop in obj) { + if(obj.hasOwnProperty(prop)) + ++count; + } + + return count; +}; + +// conversion de couleur Decimal vers HexaDecimal || 000 si fff +IriSP.DEC_HEXA_COLOR = function (dec) { + var val = +dec; + var str = val.toString(16); + var zeroes = ""; + if (str.length < 6) { + for (var i = 0; i < 6 - str.length; i++) + zeroes += "0"; + } + return zeroes + str; +}; + +/* shortcut to have global variables in templates */ +IriSP.templToHTML = function(template, values) { + var params = IriSP.underscore.extend( + { "defaults" : IriSP.default_templates_vars, + "l10n" : IriSP.i18n.getMessages() + }, + values); + return Mustache.to_html(template, params); +}; + +/* we need to be stricter than encodeURIComponent, + because of twitter +*/ +IriSP.encodeURI = function(str) { + return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28'). + replace(/\)/g, '%29').replace(/\*/g, '%2A'); +} + +IriSP.jqEscape = function(text) { + return text.replace(/(:|\.)/g,'\\$1') +} + +IriSP.jqId = function (text) { + return IriSP.jQuery('#' + IriSP.jqEscape(text)); + } + +IriSP.__guidCounter = 0; +IriSP.guid = function(prefix) { + IriSP.__guidCounter += 1; + return prefix + IriSP.__guidCounter; +}; + +/** returns an url to share on facebook */ +IriSP.mkFbUrl = function(url, text) { + if (typeof(text) === "undefined") + text = "I'm watching "; + + return "http://www.facebook.com/share.php?u=" + IriSP.encodeURI(text) + IriSP.shorten_url(url); +}; + +/** returns an url to share on twitter */ +IriSP.mkTweetUrl = function(url, text) { + if (typeof(text) === "undefined") + text = "I'm watching "; + + return "http://twitter.com/home?status=" + IriSP.encodeURI(text) + IriSP.shorten_url(url); +}; + +/** returns an url to share on google + */ +IriSP.mkGplusUrl = function(url, text) { + return "https://plusone.google.com/_/+1/confirm?hl=en&url=" + IriSP.shorten_url(url); +}; + +/** test if a value is null or undefined */ +IriSP.null_or_undefined = function(val) { + return (typeof(val) === "undefined" || val === null); +}; + +/** get a property that can have multiple names **/ + +IriSP.get_aliased = function(_obj, _aliases) { + for (var _i = 0; _i < _aliases.length; _i++) { + if (typeof _obj[_aliases[_i]] !== "undefined") { + return _obj[_aliases[_i]]; + } + } + return null; +} + +/** issue a call to an url shortener and return the shortened url */ +IriSP.shorten_url = function(url) { + return encodeURIComponent(url); +}; + + +/* for ie compatibility +if (Object.prototype.__defineGetter__&&!Object.defineProperty) { + Object.defineProperty=function(obj,prop,desc) { + if ("get" in desc) obj.__defineGetter__(prop,desc.get); + if ("set" in desc) obj.__defineSetter__(prop,desc.set); + } +} +*/ + +/* Creates regexps from text */ +IriSP.regexpFromText = function(_text) { + return new RegExp('(' + _text.replace(/(\W)/gim,'\\$1') + ')','gim'); +} +/* wrapper that simulates popcorn.js because + popcorn is a bit unstable at the time */ + +IriSP.PopcornReplacement = { +}; + +/** base class for our popcorn-compatible players. + */ +IriSP.PopcornReplacement.player = function(container, options) { + /* the jwplayer calls the callbacks in the global space so we need to + preserve them using IriSP.wrap */ + this.callbacks = { + onReady: IriSP.wrap(this, this.__initApi), + onTime: IriSP.wrap(this, this.__timeHandler), + onPlay: IriSP.wrap(this, this.__playHandler), + onPause: IriSP.wrap(this, this.__pauseHandler), + onSeek: IriSP.wrap(this, this.__seekHandler) + }; + + this.media = { + "paused": true, + "muted": false + }; + + this.container = container.slice(1); //eschew the '#' + + this.msgPump = {}; /* dictionnary used to receive and send messages */ + this.__codes = []; /* used to schedule the execution of a piece of code in + a segment (similar to the popcorn.code plugin). */ + + this._options = options; + +}; + +IriSP.PopcornReplacement.player.prototype.listen = function(msg, callback) { + if (!this.msgPump.hasOwnProperty(msg)) + this.msgPump[msg] = []; + + this.msgPump[msg].push(callback); +}; + +IriSP.PopcornReplacement.player.prototype.trigger = function(msg, params) { + if (!this.msgPump.hasOwnProperty(msg)) + return; + + var d = this.msgPump[msg]; + + for(var i = 0; i < d.length; i++) { + d[i].call(window, params); + } + +}; + +IriSP.PopcornReplacement.player.prototype.guid = function(prefix) { + var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); + + return prefix + str; +}; + +/** init the api after that flash player has been setup - called by the callback + defined by the embedded flash player +*/ +IriSP.PopcornReplacement.player.prototype.__initApi = function() { + this.trigger("loadedmetadata"); // we've done more than loading metadata of course, + // but popcorn doesn't need to know more. + this.media.muted = this.playerFns.getMute(); + /* some programmed segments are supposed to be run at the beginning */ + var i = 0; + for(i = 0; i < this.__codes.length; i++) { + var c = this.__codes[i]; + if (0 == c.start) { + c.onStart(); + } + + if (0 == c.end) { + c.onEnd(); + } + } +}; + +/* +IriSP.PopcornReplacement.jwplayer = function(container, options) { + IriSP.PopcornReplacement._container = container.slice(1); //eschew the '#' + options.events = { + onReady: IriSP.PopcornReplacement.__initApi, + onTime: IriSP.PopcornReplacement.__timeHandler, + onPlay: IriSP.PopcornReplacement.__playHandler, + onPause: IriSP.PopcornReplacement.__pauseHandler, + onSeek: IriSP.PopcornReplacement.__seekHandler + } + + jwplayer(IriSP.PopcornReplacement._container).setup(options); + IriSP.PopcornReplacement.media.duration = options.duration; + return IriSP.PopcornReplacement; +}; +*/ + +IriSP.PopcornReplacement.player.prototype.currentTime = function(time) { + if (typeof(time) === "undefined") { + return this.playerFns.getPosition(); + } else { + var currentTime = +time; + this.playerFns.seek(currentTime); + return currentTime; + } +}; + +IriSP.PopcornReplacement.player.prototype.play = function() { + this.media.paused = false; + this.trigger("play"); + //IriSP.PopcornReplacement.trigger("playing"); + this.playerFns.play(); +}; + +IriSP.PopcornReplacement.player.prototype.pause = function() { +// if ( !this.media.paused ) { + this.media.paused = true; + this.trigger( "pause" ); + this.playerFns.pause(); +// } +}; + +IriSP.PopcornReplacement.player.prototype.muted = function(val) { + if (typeof(val) !== "undefined") { + + if (this.playerFns.getMute() !== val) { + if (val) { + this.playerFns.setMute(true); + this.media.muted = true; + } else { + this.playerFns.setMute(false); + this.media.muted = false; + } + + this.trigger( "volumechange" ); + } + + return this.playerFns.getMute(); + } else { + return this.playerFns.getMute(); + } +}; + +IriSP.PopcornReplacement.player.prototype.volume = function(val) { + if (typeof this.playerFns.getVolume == "undefined" || typeof this.playerFns.setVolume == "undefined") { + return false; + } + var _vol = this.playerFns.getVolume(); + if (typeof(val) !== "undefined" && parseFloat(val) !== NaN) { + val = Math.max(0, Math.min(1, val)); + if (parseFloat(val) != parseFloat(_vol)) { + this.playerFns.setVolume(val); + this.trigger("volumechange"); + _vol = this.playerFns.getVolume(); + } + } + return _vol; +}; + +IriSP.PopcornReplacement.player.prototype.mute = IriSP.PopcornReplacement.player.prototype.muted; + +IriSP.PopcornReplacement.player.prototype.code = function(options) { + this.__codes.push(options); + return this; +}; + +/* called everytime the player updates itself + (onTime event) + */ + +IriSP.PopcornReplacement.player.prototype.__timeHandler = function(event) { + var pos = event.position; + + var i = 0; + for(i = 0; i < this.__codes.length; i++) { + var c = this.__codes[i]; + + if (pos >= c.start && pos < c.end && + pos - 1 <= c.start) { + c.onStart(); + } + + if (pos > c.start && pos > c.end && + pos - 1 <= c.end) { + c.onEnd(); + } + + } + + this.trigger("timeupdate"); +}; + +IriSP.PopcornReplacement.player.prototype.__seekHandler = function(event) { + var i = 0; + + for(i = 0; i < this.__codes.length; i++) { + var c = this.__codes[i]; + + if (event.position >= c.start && event.position < c.end) { + c.onEnd(); + } + } + + for(i = 0; i < this.__codes.length; i++) { + var c = this.__codes[i]; + + if (typeof(event.offset) === "undefined") + event.offset = 0; + + if (event.offset >= c.start && event.offset < c.end) { + c.onStart(); + } + + } + + /* this signal sends as an extra argument the position in the video. + As far as I know, this argument is not provided by popcorn */ + this.trigger("seeked", event.offset); +}; + +IriSP.PopcornReplacement.player.prototype.__playHandler = function(event) { + this.media.paused = false; + this.trigger("play"); +}; + +IriSP.PopcornReplacement.player.prototype.__pauseHandler = function(event) { + this.media.paused = true; + this.trigger("pause"); +}; + +IriSP.PopcornReplacement.player.prototype.roundTime = function() { + var currentTime = this.currentTime(); + return Math.round(currentTime); +};/* data.js - this file deals with how the players gets and sends data */ + +IriSP.DataLoader = function() { + this._cache = {}; + + /* + A structure to hold callbacks for specific urls. We need it because + ajax calls are asynchronous, so it means that sometimes we ask + multiple times for a ressource because the first call hasn't been + received yet. + */ + this._callbacks = {}; +}; + +IriSP.DataLoader.prototype.get = function(url, callback, force_reload) { + var base_url = url.split("&")[0]; + if (typeof force_reload != "undefined" && force_reload && this._cache.hasOwnProperty(base_url)) { + delete this._cache[base_url] + } + if (this._cache.hasOwnProperty(base_url)) { + callback(this._cache[base_url]); + } else { + if (!this._callbacks.hasOwnProperty(base_url)) { + this._callbacks[base_url] = [callback]; + /* we need a closure because this gets lost when it's called back */ + + // uncomment you don't want to use caching. + // IriSP.jQuery.get(url, callback); + + var func = function(data) { + this._cache[base_url] = data; + var i = 0; + + for (i = 0; i < this._callbacks[base_url].length; i++) { + this._callbacks[base_url][i](this._cache[base_url]); + } + delete this._callbacks[base_url]; + }; + + /* automagically choose between json and jsonp */ + if (url.indexOf(document.location.hostname) === -1 && + url.indexOf("http://") !== -1 /* not a relative url */ ) { + // we contacting a foreign domain, use JSONP + + IriSP.jQuery.get(url, {}, IriSP.wrap(this, func), "jsonp"); + } else { + + // otherwise, hey, whatever rows your boat + IriSP.jQuery.get(url, IriSP.wrap(this, func)); + } + + } else { + /* simply push the callback - it'll get called when the ressource + has been received */ + + this._callbacks[base_url].push(callback); + + } + } +} + +/* the base abstract "class" */ +IriSP.Serializer = function(DataLoader, url) { + this._DataLoader = DataLoader; + this._url = url; + this._data = []; +}; + +IriSP.Serializer.prototype.serialize = function(data) { }; +IriSP.Serializer.prototype.deserialize = function(data) {}; + +IriSP.Serializer.prototype.currentMedia = function() { +}; + +IriSP.Serializer.prototype.getDuration = function() { +}; + +IriSP.Serializer.prototype.sync = function(callback) { + callback.call(this, this._data); +}; + +IriSP.SerializerFactory = function(DataLoader) { + this._dataloader = DataLoader; +}; + +IriSP.SerializerFactory.prototype.getSerializer = function(metadataOptions) { + /* This function returns serializer set-up with the correct + configuration - takes a metadata struct describing the metadata source + */ + if (metadataOptions === undefined) + /* return an empty serializer */ + return IriSP.Serializer("", ""); + + switch(metadataOptions.type) { + case "json": + return new IriSP.JSONSerializer(this._dataloader, metadataOptions.src); + break; + + case "dummy": /* only used for unit testing - not defined in production */ + return new IriSP.MockSerializer(this._dataloader, metadataOptions.src); + break; + + case "empty": + return new IriSP.Serializer("", "empty"); + break; + + default: + return undefined; + } +}; +IriSP.language = 'en'; + +IriSP.libFiles = { + defaultDir : "js/libs/", + inDefaultDir : { + jQuery : "jquery.min.js", + jQueryUI : "jquery-ui.min.js", + jQueryToolTip : "jquery.tools.min.js", + swfObject : "swfobject.js", + //cssjQueryUI : "jquery-ui.css", + popcorn : "popcorn.js", + jwplayer : "jwplayer.js", + raphael : "raphael.js", + "popcorn.mediafragment" : "popcorn.mediafragment.js", + "popcorn.code" : "popcorn.code.js", + "popcorn.jwplayer" : "popcorn.jwplayer.js", + "popcorn.youtube" : "popcorn.youtube.js", + "tracemanager" : "tracemanager.js" + }, + locations : { + // use to define locations outside defautl_dir + }, + cdn : { + jQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.17/jquery-ui.js", + jQueryToolTip : "http://cdn.jquerytools.org/1.2.4/all/jquery.tools.min.js", + swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", + cssjQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/base/jquery-ui.css" + }, + useCdn : false +} + +IriSP.widgetsDefaults = { + "LayoutManager" : { + spacer_div_height : 0 + }, + "PlayerWidget" : { + + }, + "AnnotationsWidget" : { + "share_text" : "I'm watching " + }, + "TweetsWidget" : { + default_profile_picture : "https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png", + tweet_display_period : 10000 // how long do we show a tweet ? + }, + "SliderWidget" : { + minimize_period : 850 // how long does the slider stays maximized after the user leaves the zone ? + }, + "SegmentsWidget" : { + cinecast_version : false + }, + "createAnnotationWidget" : { + tags : [ + { + "id" : "digitalstudies", + "meta" : { + "description" : "#digital-studies" + } + }, + { + "id" : "amateur", + "meta" : { + "description" : "#amateur" + }, + } + ], + remote_tags : false, + random_tags : false, + show_from_field : false, + disable_share : false, + return_delay : 10000, + polemic_mode : true, /* enable polemics ? */ + polemics : [{ + "className" : "positive", + "keyword" : "++" + }, { + "className" : "negative", + "keyword" : "--" + }, { + "className" : "reference", + "keyword" : "==" + }, { + "className" : "question", + "keyword" : "??" + }], + cinecast_version : false, /* put to false to enable the platform version, true for the festival cinecast one. */ + + /* where does the widget PUT the annotations - this is a mustache template. id refers to the id of the media ans is filled + by the widget. + */ + api_endpoint_template : "", // platform_url + "/ldtplatform/api/ldt/annotations/{{id}}.json", + api_method : "PUT" + }, + "SparklineWidget" : { + lineColor : "#7492b4", + fillColor : "#aeaeb8", + lineWidth : 2, + cinecast_version : false + }, + "AnnotationsListWidget" : { + ajax_mode : true, /* use ajax to get information about the annotations. + if set to false, only search in the annotations for the + current project. */ + /* the platform generates some funky urls. We replace them afterwards to point to the + correct place - this setting will probably be overwritten by the platform + implementers. + Note that the player has to replace the variables between {{ and }} by its own values. + */ + ajax_url : "", //platform_url + "/ldtplatform/api/ldt/segments/{{media}}/{{begin}}/{{end}}", + ajax_granularity : 10000, /* how much ms should we look before and after the current timecode */ + default_thumbnail : "http://ldt.iri.centrepompidou.fr/static/site/ldt/css/imgs/video_sequence.png", + project_url : "", //platform_url + "/ldtplatform/ldt/front/player/" + /* the beginning of a link to the new front */ + cinecast_version : false, + refresh_interval : 10000 + }, + "StackGraphWidget" : { + defaultcolor : "#585858", + tags : [ + { + "keywords" : [ "++" ], + "description" : "positif", + "color" : "#1D973D" + }, + { + "keywords" : [ "--" ], + "description" : "negatif", + "color" : "#CE0A15" + }, + { + "keywords" : [ "==" ], + "description" : "reference", + "color" : "#C5A62D" + }, + { + "keywords" : [ "??" ], + "description" : "question", + "color" : "#036AAE" + } + ], + streamgraph : false + } +}/* the widget classes and definitions */ + +/** + * @class Widget is an "abstract" class. It's mostly used to define some properties common to every widget. + * + * Note that widget constructors are never called directly by the user. Instead, the widgets are instantiated by functions + * defined in init.js + * + * @constructor + * @param Popcorn a reference to the popcorn Object + * @param config configuration options for the widget + * @param Serializer a serializer instance from which the widget reads data fromCharCode +*/ +IriSP.Widget = function(Popcorn, config, Serializer) { + + if (config === undefined || config === null) { + config = {} + } + + this._Popcorn = Popcorn; + this._config = config; + this._serializer = Serializer; + + if (config.hasOwnProperty("container")) { + this._id = config.container; + this.selector = IriSP.jQuery("#" + this._id); + } + + if (config.hasOwnProperty("spacer")) { + this._spacerId = config.spacer; + this.spacer = IriSP.jQuery("#" + this._spacerId); + } + + + if (config.hasOwnProperty("width")) { + // this.width and not this._width because we consider it public. + this.width = config.width; + } + + if (config.hasOwnProperty("height")) { + this.height = config.height; + } + + if (config.hasOwnProperty("heightmax")) { + this.heightmax = config.heightmax; + } + + if (config.hasOwnProperty("widthmax")) { + this.widthmax = config.widthmax; + } + + if (config.hasOwnProperty("layoutManager")) { + this.layoutManager = config.layoutManager; + } + if (typeof this.selector != "undefined") { + this.selector.addClass("Ldt-TraceMe").addClass("Ldt-Widget"); + this.selector.attr("widget-type", this._config.type); + } + + // Parsing Widget Defaults + var _this = this; + + if (typeof config.type == "string" && typeof IriSP.widgetsDefaults[config.type] == "object") { + IriSP._(IriSP.widgetsDefaults[config.type]).each(function(_v, _k) { + if (typeof config[_k] != "undefined") { + _this[_k] = config[_k]; + } else { + _this[_k] = _v; + } + }); + } + +}; + + +IriSP.Widget.prototype.currentMedia = function() { + return this._serializer.currentMedia(); +} + +IriSP.Widget.prototype.getDuration = function() { + return this._serializer.getDuration(); +} + +/** + * This method responsible of drawing a widget on screen. + */ +IriSP.Widget.prototype.draw = function() { + /* implemented by "sub-classes" */ +}; + +/** + * Optional method if you want your widget to support redraws. + */ +IriSP.Widget.prototype.redraw = function() { + /* implemented by "sub-classes" */ +}; +/* modules are non-graphical entities, similar to widgets */ + +IriSP.Module = function(Popcorn, config, Serializer) { + + if (config === undefined || config === null) { + config = {} + } + + this._Popcorn = Popcorn; + this._config = config; + this._serializer = Serializer; +}; +/* layout.js - very basic layout management */ + +/** + @class a layout manager manages a div and the layout of objects + inside it. +*/ +IriSP.LayoutManager = function(options) { + this._Popcorn = null; + this._widgets = []; + + this._div = "LdtPlayer"; + this._width = 640; + + if (options === undefined) { + options = {}; + }; + + if (options.hasOwnProperty('container')) { + this._div = options.container; + } + + if (options.hasOwnProperty('width')) { + this._width = options.width; + } + + if (options.hasOwnProperty('height')) { + this._height = options.height; + } + + /* this is a shortcut */ + this.selector = IriSP.jQuery("#" + this._div); + + this.selector.css({ + "width": this._width, + "clear": "both" + }); + + if (this._height !== undefined) + this.selector.css("height", this._height); +}; + +/** + Set the popcorn instance used by the manager. + + we need this special setter because of a chicken and egg problem : + we want the manager to use popcorn but the popcorn div will be managed + by the manager. So we need a way to set the instance the manager uses +*/ + +IriSP.LayoutManager.prototype.setPopcornInstance = function(popcorn) { + this._Popcorn = popcorn; +} + +/** create a subdiv with an unique id, and a spacer div as well. + @param widgetName the name of the widget. + @return an array of the form [createdivId, spacerdivId]. +*/ +IriSP.LayoutManager.prototype.createDiv = function(widgetName) { + if (typeof(widgetName) === "undefined") + widgetName = ""; + + var newDiv = IriSP.guid(this._div + "_widget_" + widgetName + "_"); + var spacerDiv = IriSP.guid("LdtPlayer_spacer_"); + this._widgets.push([widgetName, newDiv]); + + var divTempl = "
"); + + if (options.hasOwnProperty("width")) + IriSP.jQuery("#" + containerDiv).css("width", options.width); + + if (options.hasOwnProperty("height")) + IriSP.jQuery("#" + containerDiv).css("height", options.height); + + pop = Popcorn("#" + tmpId); + break; + + case "jwplayer": + var opts = IriSP.jQuery.extend({}, options); + delete opts.container; + delete opts.type; + + + /* Try to guess options.file and options.streamer only if file and streamer + are not already defined in the configuration */ + if (options.provider === "rtmp" && !opts.hasOwnProperty("file") && !opts.hasOwnProperty("streamer")) { + /* exit if we can't access the metadata */ + if (typeof(IriSP.__jsonMetadata) === "undefined") { + break; + }; + + // the json format is totally illogical + //opts.streamer = IriSP.__jsonMetadata["medias"][0]["meta"]["item"]["value"]; + //var source = IriSP.__jsonMetadata["medias"][0]["href"]; + + // the source if a full url but jwplayer wants an url relative to the + // streamer url, so we've got to remove the common part. + //opts.file = source.slice(opts.streamer.length); + + /* sometimes we get served a file with a wrong path and streamer. + as a streamer is of the form rtmp://domain/path/ and the media is + the rest, we uglily do this : + */ + opts.file = ""; + opts.streamer = ""; + var fullPath = IriSP.get_aliased(IriSP.__jsonMetadata["medias"][0], ["href","url"]); + + if (fullPath === null) { + console.log("no url or href field defined in the metadata."); + } + + var pathSplit = fullPath.split('/'); + + for (var i = 0; i < pathSplit.length; i++) { + if (i < 4) { + opts.streamer += pathSplit[i] + "/"; + } else { + opts.file += pathSplit[i]; + /* omit the last slash if we're on the last element */ + if (i < pathSplit.length - 1) + opts.file += "/"; + } + } + } else { + /* other providers type, video for instance - + pass everything as is */ + } + + if (!options.hasOwnProperty("flashplayer")) { + opts.flashplayer = IriSP.jwplayer_swf_path; + } + + if (!options.hasOwnProperty("controlbar.position")) { + opts["controlbar.position"] = "none"; + } + + pop = new IriSP.PopcornReplacement.jwplayer("#" + containerDiv, opts); + break; + + case "youtube": + var opts = IriSP.jQuery.extend({}, options); + delete opts.container; + opts.controls = 0; + opts.autostart = false; + templ = "width: {{width}}px; height: {{height}}px;"; + var str = Mustache.to_html(templ, {width: opts.width, height: opts.height}); + // Popcorn.youtube wants us to specify the size of the player in the style attribute of its container div. + IriSP.jQuery("#" + containerDiv).attr("style", str); + + pop = Popcorn.youtube("#" + containerDiv, opts.video, opts); + break; + + case "dailymotion": + pop = new IriSP.PopcornReplacement.dailymotion("#" + containerDiv, options); + break; + + case "allocine": + /* pass the options as-is to the allocine player and let it handle everything */ + pop = new IriSP.PopcornReplacement.allocine("#" + containerDiv, options); + break; + + default: + pop = undefined; + }; + + return pop; +}; + +/** Configure the gui and instantiate the widgets passed as parameters + @param guiOptions the gui object as seen in the examples. + */ +IriSP.configureWidgets = function (popcornInstance, layoutManager, guiOptions) { + + var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader); + var params = {width: guiOptions.width, height: guiOptions.height}; + + var default_options = guiOptions.default_options; + if (IriSP.null_or_undefined(default_options)) + default_options = {}; + + var ret_widgets = []; + var index; + for (index = 0; index < guiOptions.widgets.length; index++) { + var widget = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, guiOptions.widgets[index], default_options); + + ret_widgets.push(widget); + }; + + return ret_widgets; +}; + +/** configure modules. @see configureWidgets */ +IriSP.configureModules = function (popcornInstance, modulesList) { + if (IriSP.null_or_undefined(modulesList)) + return; + + var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader); + var ret_modules = []; + var index; + + for (index = 0; index < modulesList.length; index++) { + var moduleConfig = modulesList[index]; + + var serializer = serialFactory.getSerializer(moduleConfig.metadata); + var module = new IriSP[moduleConfig.type](popcornInstance, moduleConfig, serializer); + ret_modules.push(module); + }; + + return ret_modules; +}; + +/** instantiate a widget - only called by configureWidgets, never by the user. Handles widget + dependencies. + @param popcornInstance popcorn instance the widget will user + @param serialFactory serializer factory to instantiate the widget with + @param layoutManager layout manager + @param widgetConfig configuration options for the widget + @param defaultOptions a dictionnary with some options defined for every widget. + */ +IriSP.instantiateWidget = function(popcornInstance, serialFactory, layoutManager, widgetConfig, defaultOptions) { + + if (IriSP.null_or_undefined(defaultOptions)) { + defaultOptions = {}; + } + if (IriSP.null_or_undefined(widgetConfig)) { + return; + } + + widgetConfig = IriSP.underscore.defaults(widgetConfig, defaultOptions); + + var arr = IriSP.jQuery.extend({}, widgetConfig); + + /* create a div for those widgets who didn't already specify a container; */ + if (!arr.hasOwnProperty("container")) { + /* create div returns us a container for the widget and a spacer */ + var ret = layoutManager.createDiv(widgetConfig.type); + var container = ret[0]; + var spacer = ret[1]; + arr.container = container; + arr.spacer = spacer; + arr.layoutManager = layoutManager; + } + var serializer = serialFactory.getSerializer(widgetConfig.metadata); + + if (typeof serializer == "undefined") + debugger; + + // instantiate the object passed as a string + var widget = new IriSP[widgetConfig.type](popcornInstance, arr, serializer); + + if (widgetConfig.hasOwnProperty("requires")) { + // also create the widgets this one depends on. + // the dependency widget is available in the parent widget context as + // this.WidgetName (for instance, this.TipWidget); + + var i = 0; + for(i = 0; i < widgetConfig.requires.length; i++) { + var widgetName = widgetConfig.requires[i]["type"], + _configobj = IriSP.jQuery.extend({}, widgetConfig.requires[i]), + _div = document.createElement('div'), + _container = IriSP.guid(arr.container + '_' + widgetName + '_'); + _configobj.container = _container; + _div.id = _container; + widget.selector.append(_div); + widget[widgetName] = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, _configobj, defaultOptions); + } + } + + serializer.sync(IriSP.wrap(widget, function() { this.draw(); })); + return widget; +}; + +/** single point of entry for the metadataplayer */ +IriSP.initPlayer = function(config, metadata_url) { + document.getElementById(config.gui.container).innerHTML = IriSP.templToHTML(IriSP.loading_template, config.gui); + IriSP.loadLibs(config, metadata_url, + function() { + + var layoutManager = new IriSP.LayoutManager(config.gui); + + var pop = IriSP.configurePopcorn(layoutManager, config.player); + + IriSP._widgets = IriSP.configureWidgets(pop, layoutManager, config.gui); + IriSP._modules = IriSP.configureModules(pop, config.modules); + IriSP.jQuery('#Ldt-loader').detach(); + }); +};IriSP.I18n = function() { + this.messages = {}; + this.base_lang = 'en'; +} + +IriSP.I18n.prototype.getLanguage = function(lang) { + var _lang = ( + typeof lang != "undefined" + ? lang + : ( + typeof IriSP.language != "undefined" + ? IriSP.language + : this.base_lang + ) + ); + return ( + typeof this.messages[_lang] == "object" + ? _lang + : ( + typeof this.messages[this.base_lang] == "object" + ? this.base_lang + : null + ) + ) +} + +IriSP.I18n.prototype.getMessages = function(lang) { + var _lang = this.getLanguage(lang); + return ( + _lang != null + ? this.messages[_lang] + : {} + ); +} + +IriSP.I18n.prototype.getMessage = function(message, lang) { + var _msgs = this.getMessages(lang); + return ( + typeof _msgs[message] != "undefined" + ? _msgs[message] + : message + ) +} + +IriSP.I18n.prototype.addMessage = function(lang, messagekey, messagevalue) { + if (typeof this.messages[lang] == "undefined") { + this.messages[lang] = {}; + } + this.messages[lang][messagekey] = messagevalue; +} + +IriSP.I18n.prototype.addMessages = function(messagesObj) { + var _this = this; + IriSP.underscore(messagesObj).each(function(_messages, _lang) { + IriSP.underscore(_messages).each(function(_value, _key) { + _this.addMessage(_lang, _key, _value); + }) + }); +} + +IriSP.i18n = new IriSP.I18n(); + +IriSP.i18n.addMessages({ + "fr": { + "loading_wait": "Chargement en cours, veuillez patienter…" + }, + "en": { + "loading_wait": "Loading, please wait…" + } +}) +/* To wrap a player the develop should create a new class derived from +the IriSP.PopcornReplacement.player and defining the correct functions */ + +/** allocine player wrapper */ +IriSP.PopcornReplacement.allocine = function(container, options) { +// console.log("Calling allocine player"); + /* appel du parent pour initialiser les structures communes à tous les players */ + IriSP.PopcornReplacement.player.call(this, container, options); + + var _this = this; + + /* Définition des fonctions de l'API - */ + + this.playerFns = { + play : function() { + return _this.apiCall("play"); + }, + pause : function() { + return _this.apiCall("pause"); + }, + getPosition : function() { + return _this.apiCall("getSeek","return") || 0; + }, + seek : function(pos) { + return _this.apiCall("seek",pos); + }, + getMute : function() { + return _this.apiCall("getMute","return"); + }, + setMute : function(p) { + return _this.apiCall("setMute", p); + } + } + + window.onReady = IriSP.wrap(this, this.ready); + window.onAllocineStateChange = IriSP.wrap(this, this.stateHandler); + window.onTime = IriSP.wrap(this, this.progressHandler); + + var _videoUrl = ( + typeof options.directVideoPath == "string" + ? options.directVideoPath + : IriSP.get_aliased(IriSP.__jsonMetadata["medias"][0], ["href","url"]) + ); + var _flashVars = { + "streamFMS" : true, + "adVast" : false, + "lg" : "fr_cinecast", + "autoPlay" : options.autoPlay, + "directVideoTitle" : "", + "urlAcData" : options.urlAcData, + "directVideoPath" : _videoUrl, + "host" : "http://allocine.fr" + } + + if (typeof IriSP.__jsonMetadata["medias"][0].meta == "object" && typeof IriSP.__jsonMetadata["medias"][0].meta.subtitles == "string") { + _flashVars.subTitlePath = IriSP.__jsonMetadata["medias"][0].meta.subtitles; + } + + + var params = { + "allowScriptAccess" : "always", + "wmode": "opaque", + "flashvars" : IriSP.jQuery.param(_flashVars), + "allowfullscreen" : true + }; + var atts = { + id : this.container + }; + swfobject.embedSWF(options.acPlayerUrl, this.container, options.width, options.height, "10", null, null, params, atts); + +}; + +IriSP.PopcornReplacement.allocine.prototype = new IriSP.PopcornReplacement.player("", {}); + +IriSP.PopcornReplacement.allocine.prototype.ready = function() { + this.player = document.getElementById(this.container); + this.player.addEventListener("onStateChange", "onAllocineStateChange"); + this.player.cueVideoByUrl(this._options.video); + this.callbacks.onReady(); +}; + +IriSP.PopcornReplacement.allocine.prototype.progressHandler = function(progressInfo) { + this.callbacks.onTime({ + position: progressInfo.mediaTime + }); +} + + +IriSP.PopcornReplacement.allocine.prototype.apiCall = function(_method, _arg) { + if (this.player) { + try { + if (typeof _arg == "undefined") { + return this.player.sendToActionScript(_method); + } else { + return this.player.sendToActionScript(_method, _arg); + } + } catch(e) { + console.error('Exception while requesting AcPlayer for "' + _method + (typeof _arg == "undefined" ? '' : '" with argument "' + _arg ) + '"\n', e); + return false; + } + } else { + return false; + } +} + +IriSP.PopcornReplacement.allocine.prototype.stateHandler = function(state) { + console.log("stateHandler"); + switch(state) { + case 1: + this.callbacks.onPlay(); + break; + + case 2: + this.callbacks.onPause(); + break; + + case 3: + this.callbacks.onSeek({ + position: this.player.getCurrentTime() + }); + break; + + /* + case 5: + this.callbacks.onReady(); + break; + */ + } + +};/* To wrap a player the develop should create a new class derived from +the IriSP.PopcornReplacement.player and defining the correct functions */ + +/** jwplayer player wrapper */ +IriSP.PopcornReplacement.dailymotion = function(container, options) { + console.log("Calling"); + /* appel du parent pour initialiser les structures communes à tous les players */ + IriSP.PopcornReplacement.player.call(this, container, options); + + var _this = this; + + /* Définition des fonctions de l'API - */ + + this.playerFns = { + play : function() { + if (_this.player) { + return _this.player.playVideo(); + } else { + return false; + } + }, + pause : function() { + if (_this.player) { + return _this.player.pauseVideo(); + } else { + return false; + } + }, + getPosition : function() { + if (_this.player) { + return _this.player.getCurrentTime(); + } else { + return 0; + } + }, + seek : function(pos) { + if (_this.player) { + return _this.player.seekTo(pos); + } else { + return false; + } + }, + getMute : function() { + if (_this.player) { + return _this.player.isMuted(); + } else { + return false; + } + }, + setMute : function(p) { + if (_this.player) { + if (p) { + _this.player.mute(); + } + else { + _this.player.unMute(); + } + } + }, + getVolume : function() { + if (_this.player) { + return _this.player.getVolume() / 100; + } else { + return false; + } + }, + setVolume : function(p) { + if (_this.player) { + _this.player.setVolume(Math.floor(100 * p)); + } + }, + } + + window.onDailymotionPlayerReady = IriSP.wrap(this, this.ready); + window.onDailymotionStateChange = IriSP.wrap(this, this.stateHandler); + window.onDailymotionVideoProgress = IriSP.wrap(this, this.progressHandler); + + var params = { + "allowScriptAccess" : "always", + "wmode": "opaque" + }; + var atts = { + id : this.container + }; + swfobject.embedSWF("http://www.dailymotion.com/swf?chromeless=1&enableApi=1", this.container, options.width, options.height, "8", null, null, params, atts); + +}; + +IriSP.PopcornReplacement.dailymotion.prototype = new IriSP.PopcornReplacement.player("", {}); + +IriSP.PopcornReplacement.dailymotion.prototype.ready = function() { + + this.player = document.getElementById(this.container); + + this.player.addEventListener("onStateChange", "onDailymotionStateChange"); + this.player.addEventListener("onVideoProgress", "onDailymotionVideoProgress"); + this.player.cueVideoByUrl(this._options.video); + + this.callbacks.onReady(); +}; + +IriSP.PopcornReplacement.dailymotion.prototype.progressHandler = function(progressInfo) { + + this.callbacks.onTime({ + position: progressInfo.mediaTime + }); +} + +IriSP.PopcornReplacement.dailymotion.prototype.stateHandler = function(state) { + + switch(state) { + case 1: + this.callbacks.onPlay(); + break; + + case 2: + this.callbacks.onPause(); + break; + + case 3: + this.callbacks.onSeek({ + position: this.player.getCurrentTime() + }); + break; + + /* + case 5: + this.callbacks.onReady(); + break; + */ + } + +};/* To wrap a player the develop should create a new class derived from + the IriSP.PopcornReplacement.player and defining the correct functions */ + +/** jwplayer player wrapper */ +IriSP.PopcornReplacement.jwplayer = function(container, options) { + /* appel du parent pour initialiser les structures communes à tous les players */ + IriSP.PopcornReplacement.player.call(this, container, options); + + this.media.duration = options.duration; /* optional */ + + /* Définition des fonctions de l'API - */ + this.playerFns = { + play: function() { return jwplayer(this.container).play(); }, + pause: function() { return jwplayer(this.container).pause(); }, + getPosition: function() { return jwplayer(this.container).getPosition(); }, + seek: function(pos) { return jwplayer(this.container).seek(pos); }, + getMute: function() { return jwplayer(this.container).getMute() }, + setMute: function(p) { return jwplayer(this.container).setMute(p); }, + getVolume: function() { return jwplayer(this.container).getVolume() / 100; }, + setVolume: function(p) { return jwplayer(this.container).setVolume(Math.floor(100*p)); } + } + + options.events = this.callbacks; + + jwplayer(this.container).setup(options); +}; + +IriSP.PopcornReplacement.jwplayer.prototype = new IriSP.PopcornReplacement.player("", {}); +/* embed module - listens and relay hash changes to a parent window. */ + +IriSP.EmbedModule = function(Popcorn, config, Serializer) { + IriSP.Module.call(this, Popcorn, config, Serializer); + + window.addEventListener('message', IriSP.wrap(this, this.handleMessages), false); + this._Popcorn.listen("IriSP.Mediafragment.hashchange", IriSP.wrap(this, this.relayChanges)); +}; + +IriSP.EmbedModule.prototype = new IriSP.Module(); + +IriSP.EmbedModule.prototype.handleMessages = function(e) { + if (e.data.type === "hashchange") { + window.location.hash = e.data.value; + } +}; + +IriSP.EmbedModule.prototype.relayChanges = function(newHash) { + window.parent.postMessage({type: "hashchange", value: newHash}, "*"); + return; +};/* mediafragment module */ + +IriSP.MediaFragment = function(Popcorn, config, Serializer) { + IriSP.Module.call(this, Popcorn, config, Serializer); + + this.mutex = false; /* a mutex because we access the url from two different functions */ + + this._Popcorn.listen( "loadedmetadata", IriSP.wrap(this,this.advanceTime)); + this._Popcorn.listen( "pause", IriSP.wrap(this,this.updateTime)); + this._Popcorn.listen( "seeked", IriSP.wrap(this,this.updateTime)); + this._Popcorn.listen( "IriSP.PolemicTweet.click", IriSP.wrap(this,this.updateAnnotation)); + this._Popcorn.listen( "IriSP.SegmentsWidget.click", IriSP.wrap(this,this.updateAnnotation)); + + window.onhashchange = IriSP.wrap(this, this.advanceTime); +}; + +IriSP.MediaFragment.prototype = new IriSP.Module(); + +IriSP.MediaFragment.prototype.advanceTime = function() { + var url = window.location.href; + + if ( url.split( "#" )[ 1 ] != null ) { + pageoffset = url.split( "#" )[1]; + + if ( pageoffset.substring(0, 2) === "t=") { + // timecode + if ( pageoffset.substring( 2 ) != null ) { + var offsettime = pageoffset.substring( 2 ); + this._Popcorn.currentTime( parseFloat(offsettime) ); + + /* we have to trigger this signal manually because of a + bug in the jwplayer */ + this._Popcorn.trigger("seeked", parseFloat(offsettime)); + } + } else if ( pageoffset.substring(0, 3) === "id=") { + // annotation + var annotationId = pageoffset.substring( 3 ); + // there's no better way than that because + // of possible race conditions + this._serializer.sync(IriSP.wrap(this, function() { + this.lookupAnnotation.call(this, annotationId); + })); + } + } +}; + +/** handler for the seeked signal. It may have or may have not an argument. + @param time if not undefined, the time we're seeking to +*/ +IriSP.MediaFragment.prototype.updateTime = function(time) { + if (this.mutex === true) { + return; + } + + var history = window.history; + if ( !history.pushState ) { + return false; + } + + if (IriSP.null_or_undefined(time) || typeof(time) != "number") { + var ntime = this._Popcorn.currentTime().toFixed(2) + } else { + var ntime = time.toFixed(2); + } + + // used to relay the new hash to the embedder + this._Popcorn.trigger("IriSP.Mediafragment.hashchange", "#t=" + ntime); + + splitArr = window.location.href.split( "#" ) + history.replaceState( {}, "", splitArr[0] + "#t=" + ntime ); +}; + + +IriSP.MediaFragment.prototype.updateAnnotation = function(annotationId) { + var _this = this; + this.mutex = true; + + var history = window.history; + if ( !history.pushState ) { + return false; + } + + + // used to relay the new hash to the embedder + this._Popcorn.trigger("IriSP.Mediafragment.hashchange", "#id=" + annotationId); + + splitArr = window.location.href.split( "#" ) + history.replaceState( {}, "", splitArr[0] + "#id=" + annotationId); + + + // reset the mutex afterwards to prevent the module from reacting to his own changes. + window.setTimeout(function() { _this.mutex = false }, 50); +}; + +// lookup and seek to the beginning of an annotation +IriSP.MediaFragment.prototype.lookupAnnotation = function(annotationId) { + var _this = this; + this.mutex = true; + + var annotation = undefined; + var annotations = this._serializer._data.annotations; + + var i; + for (i = 0; i < annotations.length; i++) { + if (annotations[i].id === annotationId) { + annotation = annotations[i]; + break; + } + } + + if (typeof(annotation) !== "undefined") { + this._Popcorn.currentTime(annotation.begin / 1000); + + /* we have to trigger this signal manually because of a + bug in the jwplayer */ + this._Popcorn.trigger("seeked", annotation.begin / 1000); + this._Popcorn.trigger("IriSP.Mediafragment.showAnnotation", annotationId); + } + + window.setTimeout(function() { _this.mutex = false }, 50); +}; +IriSP.AnnotationsListWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + this.__counter = 0; + this.__oldList = []; + this.searchRe = null; + this._ajax_cache = []; + var _this = this; + + this._Popcorn.listen("IriSP.search", function(searchString) { + _this.searchHandler(searchString); + }); + this._Popcorn.listen("IriSP.search.closed", function() { + _this.searchHandler(false); + }); + this._Popcorn.listen("IriSP.search.cleared", function() { + _this.searchHandler(false); + }); +}; + + +IriSP.AnnotationsListWidget.prototype = new IriSP.Widget(); + +IriSP.AnnotationsListWidget.prototype.clear = function() { +}; + +IriSP.AnnotationsListWidget.prototype.clearWidget = function() { +}; + +IriSP.AnnotationsListWidget.prototype.searchHandler = function(searchString) { + this.searchRe = (searchString && searchString.length) ? IriSP.regexpFromText(searchString) : null; + if (this.ajax_mode && !this.cinecast_version) { + var _this = this, + _annotations = ( + this.searchRe === null + ? this._ajax_cache + : IriSP.underscore.filter(this._ajax_cache, function(_a) { + return (_this.searchRe.test(_a.desc) || _this.searchRe.test(_a.title)); + }) + ); + this.do_redraw(_annotations); + if (_annotations.length) { + this._Popcorn.trigger("IriSP.search.matchFound"); + } else { + this._Popcorn.trigger("IriSP.search.noMatchFound"); + } + } else { + this.drawList(); + } +} + +/** effectively redraw the widget - called by drawList */ +IriSP.AnnotationsListWidget.prototype.do_redraw = function(list) { + var _html = IriSP.templToHTML( + IriSP.annotationsListWidget_template, { + annotations: list + }), + _this = this; + + this.selector.html(_html); + + this.selector.find('.Ldt-AnnotationsList-Tag-Li').click(function() { + _this._Popcorn.trigger("IriSP.search.triggeredSearch", IriSP.jQuery(this).text().trim()); + }) + + if (this.searchRe !== null) { + this.selector.find(".Ldt-AnnotationsList-Title a, .Ldt-AnnotationsList-Description") + .each(function() { + var _$ = IriSP.jQuery(this); + _$.html(_$.text().trim().replace(_this.searchRe, '$1')) + }) + } +}; + +IriSP.AnnotationsListWidget.prototype.transformAnnotation = function(a) { + var _this = this; + return { + "id" : a.id, + "title": this.cinecast_version ? IriSP.get_aliased(a.meta, ['creator_name', 'creator']) : a.content.title, + "desc" : this.cinecast_version ? a.content.data : a.content.description, + "begin": IriSP.msToTime(a.begin), + "end" : IriSP.msToTime(a.end), + "thumbnail" : (typeof a.meta == "object" && typeof a.meta.thumbnail == "string") ? a.meta.thumbnail : this.default_thumbnail, + "url" : (typeof a.meta == "object" && typeof a.meta.url == "string") ? a.meta.url : null, + "created_at" :(typeof a.meta == "object" && typeof a.meta.created == "string") ? Date.parse(a.meta.created.replace(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}\:\d{2}\:\d{2}).*$/,"$2/$3/$1 $4 UTC+0000")) : null, + "tags": typeof a.tags == "object" + ? IriSP.underscore(a.tags) + .chain() + .map(function(_t) { + if (typeof _t == "string") { + return _t.replace(/^.*:/,'#'); + } else { + if (typeof _t['id-ref'] != "undefined") { + var _f = IriSP.underscore.find(_this._serializer._data.tags, function(_tag) { + return _tag.id == _t['id-ref']; + }); + if (typeof _f != "undefined") { + return IriSP.get_aliased(_f.meta, ['dc:title', 'title']); + } + } + } + return null; + }) + .filter(function(_t) { + return _t !== null && _t !== "" + }) + .value() + : [] + } +} + +/** draw the annotation list */ +IriSP.AnnotationsListWidget.prototype.drawList = function(force_redraw) { + var _this = this; + +// var view_type = this._serializer.getContributions(); + var annotations = this._serializer._data.annotations; + var currentTime = this._Popcorn.currentTime(); + var list = []; + +/* if (typeof(view_type) === "undefined") { + return; +} */ + for (i = 0; i < annotations.length; i++) { + var obj = this.transformAnnotation(annotations[i]); + obj.iterator = i; + obj.distance = Math.abs((annotations[i].end + annotations[i].begin) / 2000 - currentTime); + if (!this.cinecast_version || annotations[i].type == "cinecast:UserAnnotation") { + list.push(obj); + } + + } + + if (this.searchRe !== null) { + list = list.filter(function(_a) { + return (_this.searchRe.test(_a.desc) || _this.searchRe.test(_a.title)); + }); + if (list.length) { + this._Popcorn.trigger("IriSP.search.matchFound"); + } else { + this._Popcorn.trigger("IriSP.search.noMatchFound"); + } + } + list = IriSP.underscore(list) + .chain() + .sortBy(function(_o) { + return _o.distance; + }) + .first(10) + .sortBy(function(_o) { + return (_this.cinecast_version ? - _o.created_at : _o.iterator); + }) + .value(); + var idList = IriSP.underscore.pluck(list, "id").sort(); + + if (!IriSP.underscore.isEqual(this.__oldList, idList) || this.lastSearch !== this.searchRe || typeof(force_redraw) !== "undefined") { + this.do_redraw(list); + this.__oldList = idList; + this.lastSearch = this.searchRe; + } + /* save for next call */ + + +}; + +IriSP.AnnotationsListWidget.prototype.ajaxRedraw = function(timecode) { + + /* the seeked signal sometimes passes an argument - depending on if we're using + our popcorn lookalike or the real thing - if it's the case, use it as it's + more precise than currentTime which sometimes contains the place we where at */ + if (IriSP.null_or_undefined(timecode) || typeof(timecode) != "number") { + var tcode = this._Popcorn.currentTime(); + } else { + var tcode = timecode; + } + + + /* the platform gives us a special url - of the type : http://path/{{media}}/{{begin}}/{{end}} + we double the braces using regexps and we feed it to mustache to build the correct url + we have to do that because the platform only knows at run time what view it's displaying. + */ + + var media_id = this.currentMedia()["id"]; + var duration = this.getDuration(); + + var begin_timecode = (Math.floor(tcode) - 300) * 1000; + if (begin_timecode < 0) + begin_timecode = 0; + + var end_timecode = (Math.floor(tcode) + 300) * 1000; + if (end_timecode > duration) + end_timecode = duration; + + var templ = Mustache.to_html(this.ajax_url, {media: media_id, begin: begin_timecode, + end: end_timecode}); + + /* we create on the fly a serializer to get the ajax */ + var serializer = new IriSP.JSONSerializer(IriSP.__dataloader, templ); + serializer.sync(IriSP.wrap(this, function(json) { this.processJson(json, serializer) })); +}; + +/** process the received json - it's a bit hackish */ +IriSP.AnnotationsListWidget.prototype.processJson = function(json, serializer) { + /* FIXME: DRY the whole thing */ + var annotations = serializer._data.annotations; + if (IriSP.null_or_undefined(annotations)) + return; + + /* + commented in case we wanted to discriminate against some annotation types. + var view_types = serializer.getIds("Contributions"); + */ + var l = []; + + var media = this.currentMedia()["id"]; + + for (i = 0; i < annotations.length; i++) { + var obj = this.transformAnnotation(annotations[i]) + if (typeof obj.url == "undefined" || !obj.url) { + /* only if the annotation isn't present in the document create an + external link */ + if (this.annotations_ids.indexOf(obj.id.toLowerCase()) == -1) { + // braindead url; jacques didn't want to create a new one in the platform, + // so we append the cutting id to the url. + obj.url = this.project_url + "/" + media + "/" + + annotations[i].meta.project + "/" + + annotations[i].meta["id-ref"] + '#id=' + annotations[i].id; + + // obj.url = document.location.href.split("#")[0] + "/" + annotation.meta.project; + } + } + l.push(obj); + } + this._ajax_cache = l; + this.do_redraw(l); +}; +IriSP.AnnotationsListWidget.prototype.draw = function() { + + /* build a table of the annotations present in the document for faster + lookup + */ + this.annotations_ids = IriSP.underscore(this._serializer._data.annotations).map(function(_a) { + return _a.id.toLowerCase(); + }); + + var _this = this; + + if (!this.ajax_mode || this.cinecast_version) { + var _throttled = IriSP.underscore.throttle(function() { + _this.drawList(); + }, 1500); + _throttled(); + this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", _throttled); + this._Popcorn.listen("timeupdate", _throttled); + if (this.cinecast_version) { + window.setInterval(function() { + var _tmpSerializer = new IriSP.JSONSerializer(IriSP.__dataloader, _this._config.metadata.src, true); + _tmpSerializer.sync(function(json) { + _this.annotations_ids = IriSP.underscore(_this._serializer._data.annotations).map(function(_a) { + return _a.id.toLowerCase(); + }); + IriSP.underscore(json.annotations).each(function(_a) { + var _j = _this.annotations_ids.indexOf(_a.id); + if (_j == -1) { + _this._serializer._data.annotations.push(_a); + _this.annotations_ids.push(_a.id); + } else { + _this._serializer._data.annotations[_j] = _a; + } + _throttled(); + }); + }, true); // true is for force_refresh + },this.refresh_interval); + } + } else { + /* update the widget when the video has finished loading and when it's seeked and paused */ + this._Popcorn.listen("seeked", IriSP.wrap(this, this.ajaxRedraw)); + this._Popcorn.listen("loadedmetadata", IriSP.wrap(this, this.ajaxRedraw)); + this._Popcorn.listen("paused", IriSP.wrap(this, this.ajaxRedraw)); + + this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.ajaxRedraw)); + } + +};/* Internationalization for this widget */ + +IriSP.i18n.addMessages( + { + "fr": { + "keywords": "Mots-clés" + }, + "en": { + "keywords": "Keywords" + } + } +); + +IriSP.AnnotationsWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + /* flag used when we're creating an annotation */ + this._hidden = false; +}; + + +IriSP.AnnotationsWidget.prototype = new IriSP.Widget(); + +IriSP.AnnotationsWidget.prototype.clear = function() { + this.selector.find(".Ldt-SaTitle").text(""); + this.selector.find(".Ldt-SaDescription").text(""); + this.selector.find(".Ldt-SaKeywordText").text(""); +}; + +IriSP.AnnotationsWidget.prototype.displayAnnotation = function(annotation) { + var title = annotation.content.title; + var description = annotation.content.description; + var keywords = ""; + var begin = +annotation.begin / 1000; + var end = +annotation.end / 1000; + var duration = this.getDuration(); + var tags = ""; + + var title_templ = "{{title}} - ( {{begin}} - {{end}} )"; + var endstr = Mustache.to_html(title_templ, {title: title, begin: IriSP.secondsToTime(begin), end: IriSP.secondsToTime(end)}); + + this.selector.find(".Ldt-SaTitle").text(endstr); + this.selector.find(".Ldt-SaDescription").text(description); + + + if (!IriSP.null_or_undefined(annotation.tags) && !IriSP.null_or_undefined(this._serializer._data.tags)) { + /* save the tag id and keywords in a unique structure */ + var tag_list = {}; + for (var i = 0; i < this._serializer._data.tags.length; i++) { + var id = this._serializer._data.tags[i]["id"]; + var keyword = IriSP.get_aliased(this._serializer._data.tags[i]["meta"], ["dc:title", "title"]); + + tag_list[id] = keyword; + } + + /* then browse the list of defined tags for the current annotation */ + for (var i = 0; i < annotation.tags.length; i++) { + if (tag_list.hasOwnProperty(annotation.tags[i]["id-ref"])) + tags += tag_list[annotation.tags[i]["id-ref"]] + ", "; + } + } + + tags = IriSP.i18n.getMessage("keywords") + ": " + tags.slice(0, tags.length - 2); + + this.selector.find(".Ldt-SaKeywords").text(tags); + + // update sharing buttons + var url = document.location.href + "#id=" + annotation.id; + this.selector.find(".Ldt-fbShare").attr("href", IriSP.mkFbUrl(url, this.share_text)); + this.selector.find(".Ldt-TwShare").attr("href", IriSP.mkTweetUrl(url, this.share_text)); + this.selector.find(".Ldt-GplusShare").attr("href", IriSP.mkGplusUrl(url, this.share_text)); +}; + +IriSP.AnnotationsWidget.prototype.clearWidget = function() { + /* retract the pane between two annotations */ + this.selector.find(".Ldt-SaTitle").text(""); + this.selector.find(".Ldt-SaDescription").text(""); + this.selector.find(".Ldt-SaKeywordText").html(""); + this.selector.find(".Ldt-ShowAnnotation").slideUp(); +}; + +IriSP.AnnotationsWidget.prototype.draw = function() { + var _this = this; + + var annotationMarkup = IriSP.templToHTML(IriSP.annotationWidget_template); + this.selector.append(annotationMarkup); + + this._Popcorn.listen("IriSP.AnnotationsWidget.show", + IriSP.wrap(this, this.show)); + this._Popcorn.listen("IriSP.AnnotationsWidget.hide", + IriSP.wrap(this, this.hide)); + + var legal_ids = []; + if (typeof(this._serializer.getChapitrage()) !== "undefined") + legal_ids.push(this._serializer.getChapitrage()); + else + legal_ids = this._serializer.getNonTweetIds(); + + var annotations = this._serializer._data.annotations; + var i; + + for (i in annotations) { + var annotation = annotations[i]; + var begin = Math.round((+ annotation.begin) / 1000); + var end = Math.round((+ annotation.end) / 1000); + + if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" + && !IriSP.underscore.include(legal_ids, annotation.meta["id-ref"])) { + continue; + } + + + var conf = {start: begin, end: end, + onStart: + function(annotation) { + return function() { + _this.displayAnnotation(annotation); + + } }(annotation), + onEnd: + function() { _this.clearWidget.call(_this); } + }; + this._Popcorn = this._Popcorn.code(conf); + } + +}; + +IriSP.AnnotationsWidget.prototype.hide = function() { + if (this._hidden == false) { + this.selector.hide(); + this._hidden = true; + } +}; + +IriSP.AnnotationsWidget.prototype.show = function() { + if (this._hidden == true) { + this.selector.show(); + this._hidden = false; + } +};IriSP.ArrowWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + + this._oldAnnotation = null; + this._blockArrow = false; +}; + + +IriSP.ArrowWidget.prototype = new IriSP.Widget(); + +IriSP.ArrowWidget.prototype.clear = function() { + +}; + +IriSP.ArrowWidget.prototype.clearWidget = function() { +}; + +IriSP.ArrowWidget.prototype.draw = function() { + var templ = Mustache.to_html(IriSP.arrowWidget_template, {}); + this.selector.append(templ); + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler)); + this._Popcorn.listen("IriSP.ArrowWidget.blockArrow", IriSP.wrap(this, this.blockArrow)); + this._Popcorn.listen("IriSP.ArrowWidget.releaseArrow", IriSP.wrap(this, this.releaseArrow)); + +}; + +IriSP.ArrowWidget.prototype.timeUpdateHandler = function(percents) { + if (this._blockArrow) + return; + + var currentTime = this._Popcorn.currentTime(); + var currentAnnotation = this._serializer.currentChapitre(currentTime); + if (IriSP.null_or_undefined(currentAnnotation)) { + var c_annots = this._serializer.currentAnnotation(currentTime) + if (c_annots.length != 0) + var currentAnnotation = c_annots[0]; // FIXME : use the others ? + else + return; + } + + /* move the arrow only if the current annotation changes */ + if (currentAnnotation != this._oldAnnotation) { + var begin = (+ currentAnnotation.begin) / 1000; + var end = (+ currentAnnotation.end) / 1000; + + var duration = this.getDuration() / 1000; + var middle_time = (begin + end) / 2; + var percents = middle_time / duration; + + // we need to apply a fix because the arrow has a certain length + // it's half the length of the arrow (27 / 2). We need to convert + // it in percents though. + var totalWidth = this.selector.width(); + var pixels = percents * totalWidth; + var correction = (27 / 2); + var corrected_pixels = pixels - correction; + + /* make sure that the arrow is aligned with the pattern + of the widget under it */ + if (corrected_pixels % 3 != 0) + corrected_pixels -= (corrected_pixels % 3 - 1); + + /* don't move out of the screen */ + if (corrected_pixels <= 0) + corrected_pixels = 0; + + if (corrected_pixels <= 15) { + this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge") + .addClass("Ldt-arrowLeftEdge"); + } else if (corrected_pixels >= totalWidth - 25) { + this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge") + .addClass("Ldt-arrowRightEdge"); + } else { + this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge") + .addClass("Ldt-arrowCenter"); + } + + this.selector.children(".Ldt-arrowWidget").animate({"left" : corrected_pixels + "px"}); + + this._oldAnnotation = currentAnnotation; + } +}; + +/** Block the arrow for instance when the user is annotating */ +IriSP.ArrowWidget.prototype.blockArrow = function() { + this._blockArrow = true; +}; + +IriSP.ArrowWidget.prototype.releaseArrow = function() { + this._blockArrow = false; +}; +/* Internationalization for this widget */ + +IriSP.i18n.addMessages( + { + "en": { + "submit": "Submit", + "add_keywords": "Add keywords", + "add_polemic_keywords": "Add polemic keywords", + "your_name": "Your name", + "type_here": "Type your annotation here.", + "wait_while_processed": "Please wait while your request is being processed...", + "error_while_contacting": "An error happened while contacting the server. Your annotation has not been saved.", + "empty_annotation": "Your annotation is empty. Please write something before submitting.", + "annotation_saved": "Thank you, your annotation has been saved.", + "share_annotation": "Would you like to share it on social networks ?", + "share_on": "Share on", + "more_tags": "More tags" + }, + "fr": { + "submit": "Envoyer", + "add_keywords": "Ajouter des mots-clés", + "add_polemic_keywords": "Ajouter des mots-clés polémiques", + "your_name": "Votre nom", + "type_here": "Rédigez votre annotation ici.", + "wait_while_processed": "Veuillez patienter pendant le traitement de votre requête...", + "error_while_contacting": "Une erreur s'est produite en contactant le serveur. Votre annotation n'a pas été enregistrée", + "empty_annotation": "Votre annotation est vide. Merci de rédiger un texte avant de l'envoyer.", + "annotation_saved": "Merci, votre annotation a été enregistrée.", + "share_annotation": "Souhaitez-vous la partager sur les réseaux sociaux ?", + "share_on": "Partager sur", + "more_tags": "Plus de mots-clés" + } + } +); + +IriSP.createAnnotationWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + this._hidden = true; + + if (!IriSP.null_or_undefined(IriSP.user)) { + if (!IriSP.null_or_undefined(IriSP.user.avatar)) { + this.user_avatar = IriSP.user.avatar; + } + if (!IriSP.null_or_undefined(IriSP.user.name)) { + this.user_name = IriSP.user.name; + } + } + + /* variables to save the current position of the slicer */ + if (this.cinecast_version) { + this.sliceLeft = 0; + this.sliceWidth = 0; + } +}; + + +IriSP.createAnnotationWidget.prototype = new IriSP.Widget(); + +IriSP.createAnnotationWidget.prototype.clear = function() { + this.selector.find(".Ldt-SaTitle").text(""); + this.selector.find(".Ldt-SaDescription").text(""); + this.selector.find(".Ldt-SaKeywordText").text(""); +}; + +IriSP.createAnnotationWidget.prototype.draw = function() { + var _this = this; + if (typeof this.remote_tags == "object") { + IriSP.jQuery.getJSON((typeof this.remote_tags.alias == "string" ? this.remote_tags.alias : this.remote_tags.url), function(_json) { + _this.tags = _json.tags; + _this.drawCallback(); + }); + } else { + this.drawCallback(); + } +} + +IriSP.createAnnotationWidget.prototype.drawCallback = function() { + var _this = this; + + var annotationMarkup = IriSP.templToHTML(IriSP.createAnnotationWidget_template, + this); + + this.selector.append(annotationMarkup); + + if (!this.cinecast_version) + this.selector.hide(); + else { + this.showStartScreen(); + } + + if (this.random_tags) { + this.selector.find(".Ldt-createAnnotation-keywords li").hide(); + this.showMoreTags(); + this.selector.find('.Ldt-createAnnotation-moar-keywordz').click(function() { + _this.showMoreTags(); + }) + } + // Add onclick event to both polemic and keywords buttons + + this.selector.find(".Ldt-createAnnotation-keyword-button, .Ldt-createAnnotation-polemic-button").click(function() { + _this.addKeyword(IriSP.jQuery(this).text()); + return false; + }); + + // js_mod is a custom event because there's no simple way to test for a js + // change in a textfield. + this.selector.find(".Ldt-createAnnotation-Description") + .bind("propertychange keyup input paste click js_mod", IriSP.wrap(this, this.handleTextChanges)); + + /* the cinecast version of the player is supposed to pause when the user clicks on the button */ + + /* the cinecast version expects the user to comment on a defined segment. + As the widget is always shown, we need a way to update it's content as + time passes. We do this like we did with the annotationsWidget : we schedule + a .code start function which will be called at the right time. + */ + if (this.cinecast_version) { + var legal_ids; + if (typeof(this._serializer.getChapitrage()) !== "undefined") + legal_id = this._serializer.getChapitrage(); + else + legal_id = this._serializer.getNonTweetIds()[0]; + + var annotations = this._serializer._data.annotations; + var i; + + for (i in annotations) { + var annotation = annotations[i]; + if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" + && legal_id !== annotation.meta["id-ref"]) { + continue; + } + + code = {start: annotation.begin / 1000, end: annotation.end / 1000, + onStart: function(annotation) { return function() { + if (typeof(annotation.content) !== "undefined") + _this.selector.find(".Ldt-createAnnotation-Title").html(annotation.content.title); + + _this._currentAnnotation = annotation; + var beginTime = IriSP.msToTime(annotation.begin); + var endTime = IriSP.msToTime(annotation.end); + var timeTemplate = IriSP.templToHTML("- ({{begin}} - {{ end }})", {begin: beginTime, end: endTime }); + _this.selector.find(".Ldt-createAnnotation-TimeFrame").html(timeTemplate); + } }(annotation) + }; + + this._Popcorn.code(code); + } + } + + this.selector.find(".Ldt-createAnnotation-submitButton").click(IriSP.wrap(this, this.handleButtonClick)); + + if (!this.cinecast_version) { + this._Popcorn.listen("IriSP.PlayerWidget.AnnotateButton.clicked", + IriSP.wrap(this, this.handleAnnotateSignal)); + + // handle clicks on the cancel button too. + this.selector.find(".Ldt-createAnnotation-Minimize").click(IriSP.wrap(this, + function() { + // we've got to simulate the pressing of the button because there's no + // other way to minimize the widget and show the widgets that were hidden + // same time + this._Popcorn.trigger("IriSP.PlayerWidget.AnnotateButton.clicked"); + } + )); + } +}; + +IriSP.createAnnotationWidget.prototype.showMoreTags = function() { + for (var j=0; j < this.random_tags; j++) { + var _jq = this.selector.find(".Ldt-createAnnotation-keywords li:hidden"); + if (_jq.length > 1) { + IriSP.jQuery(_jq[Math.floor(_jq.length*Math.random())]).show(); + } else { + _jq.show(); + break; + } + } + if (this.selector.find(".Ldt-createAnnotation-keywords li:hidden").length == 0) { + this.selector.find('.Ldt-createAnnotation-moar-keywordz').hide(); + } +} + +/* Handles adding keywords and polemics */ +IriSP.createAnnotationWidget.prototype.addKeyword = function(_keyword) { + var _field = this.selector.find(".Ldt-createAnnotation-Description"), + _rx = IriSP.regexpFromText(_keyword), + _contents = _field.val(); + _contents = ( _rx.test(_contents) + ? _contents.replace(_rx,"").replace(" "," ").replace(/(^\s+|\s+$)/g,'') + : _contents.replace(/(^\s+|\s+$)/g,'') + " " + _keyword + ); + _field.val(_contents.replace(/(^\s+|\s+$)/g,'')).trigger("js_mod"); +} + +/** handles clicks on the annotate button. Works only for the non-cinecast version */ +IriSP.createAnnotationWidget.prototype.handleAnnotateSignal = function() { + + if (this._hidden == false && this._state == 'startScreen') { + this.selector.hide(); + this._hidden = true; + + // free the arrow. + this._Popcorn.trigger("IriSP.ArrowWidget.releaseArrow"); + this._Popcorn.trigger("IriSP.SliceWidget.hide"); + this._Popcorn.trigger("IriSP.AnnotationsWidget.show"); + + } else { + this._Popcorn.trigger("IriSP.AnnotationsWidget.hide"); + this.showStartScreen(); + this.selector.show(); + this._hidden = false; + var currentTime = this._Popcorn.currentTime(); + + // block the arrow. + this._Popcorn.trigger("IriSP.ArrowWidget.blockArrow"); + + var duration = this.getDuration(); + + var currentChapter = this._serializer.currentChapitre(currentTime); + + if (IriSP.null_or_undefined(currentChapter)) { + var left = this.selector.width() / 2; + var width = this.selector.width() / 10; + } else { + var left = (currentChapter.begin / duration) * this.selector.width(); + var width = (currentChapter.end / duration) * this.selector.width() - left; + } + + // slider position and length is kept in percents. + this.sliceLeft = (left / this.selector.width()) * 100; + this.sliceWidth = (width / this.selector.width()) * 100; + + this._Popcorn.trigger("IriSP.SliceWidget.position", [left, width]); + this._Popcorn.listen("IriSP.SliceWidget.zoneChange", IriSP.wrap(this, this.handleSliderChanges)); + this._Popcorn.trigger("IriSP.SliceWidget.show"); + + if (!IriSP.null_or_undefined(currentChapter)) { + this.selector.find(".Ldt-createAnnotation-Title").html(currentChapter.content.title); + + this._currentcurrentChapter = currentChapter; + var beginTime = IriSP.msToTime(currentChapter.begin); + var endTime = IriSP.msToTime(currentChapter.end); + var timeTemplate = IriSP.templToHTML("- ({{begin}} - {{ end }})", {begin: beginTime, end: endTime }); + this.selector.find(".Ldt-createAnnotation-TimeFrame").html(timeTemplate); + } + } +}; + + +/** watch for changes in the textfield and change the buttons accordingly */ +IriSP.createAnnotationWidget.prototype.handleTextChanges = function(event) { + var contents = this.selector.find(".Ldt-createAnnotation-Description").val(); + if (this.cinecast_version) { + this._Popcorn.pause(); + } + this.selector.find(".Ldt-createAnnotation-btnblock button").each(function() { + var _rx = IriSP.regexpFromText(IriSP.jQuery(this).text()); + if (_rx.test(contents)) { + IriSP.jQuery(this).parent().addClass("Ldt-createAnnotation-active-button"); + } else { + IriSP.jQuery(this).parent().removeClass("Ldt-createAnnotation-active-button"); + } + }); + +}; + +IriSP.createAnnotationWidget.prototype.showStartScreen = function() { + this.selector.find(".Ldt-createAnnotation-screen").hide(); + this.selector.find(".Ldt-createAnnotation-startScreen").show(); + + var jqTextfield = this.selector.find(".Ldt-createAnnotation-Description"); // handle on the textfield. used for the closure + + /* test if the browser supports the placeholder attribute */ + if (!IriSP.null_or_undefined(jqTextfield.get(0).placeholder)) { + jqTextfield.attr("placeholder", IriSP.i18n.getMessage('type_here')); + } else { + jqTextfield.val(IriSP.i18n.getMessage('type_here')); + jqTextfield.one("click", IriSP.wrap(this, function() { jqTextfield.val(""); })); + } + + + + this._state = "startScreen"; +}; + +IriSP.createAnnotationWidget.prototype.showWaitScreen = function() { + this.selector.find(".Ldt-createAnnotation-screen").hide(); + this.selector.find(".Ldt-createAnnotation-waitScreen").show(); + this._state = "waitScreen"; +}; + +IriSP.createAnnotationWidget.prototype.showErrorScreen = function() { + this.selector.find(".Ldt-createAnnotation-screen").hide(); + this.selector.find(".Ldt-createAnnotation-errorScreen").show(); + this._state = "errorScreen"; + var _this = this; + window.setTimeout(function() { _this.showStartScreen(); }, 2000); +}; + +/** update show the final screen with links to share the created annotation */ +IriSP.createAnnotationWidget.prototype.showEndScreen = function(annotation) { + this.selector.find(".Ldt-createAnnotation-screen").hide(); + + if (this.cinecast_version) { + this.selector.find(".Ldt-createAnnotation-Title").parent().show(); + } + + var url = ( (typeof annotation.meta == "object" && typeof annotation.meta.url == "string" && annotation.meta.url.length) + ? annotation.meta.url + : ( document.location.href + "#id=" + annotation.id ) ); + var twStatus = IriSP.mkTweetUrl(url); + var gpStatus = IriSP.mkGplusUrl(url); + var fbStatus = IriSP.mkFbUrl(url); + + this.selector.find(".Ldt-createAnnotation-endScreen-TweetLink").attr("href", twStatus); + this.selector.find(".Ldt-createAnnotation-endScreen-FbLink").attr("href", fbStatus); + this.selector.find(".Ldt-createAnnotation-endScreen-GplusLink").attr("href", gpStatus); + + this.selector.find(".Ldt-createAnnotation-endScreen").show(); + this._state = "endScreen"; +}; + +/** handle clicks on "send annotation" button */ +IriSP.createAnnotationWidget.prototype.handleButtonClick = function(event) { + var _this = this; + var textfield = this.selector.find(".Ldt-createAnnotation-Description"); + var contents = textfield.val(); + + if (contents === "") { + if (this.selector.find(".Ldt-createAnnotation-errorMessage").length === 0) { + this.selector.find(".Ldt-createAnnotation-Container") + .after(IriSP.templToHTML(IriSP.createAnnotation_errorMessage_template)); + textfield.css("background-color", "#d93c71"); + } else { + this.selector.find(".Ldt-createAnnotation-errorMessage").show(); + } + + textfield.one("js_mod propertychange keyup input paste", IriSP.wrap(this, function() { + var contents = textfield.val(); + + if (contents !== "") { + this.selector.find(".Ldt-createAnnotation-errorMessage").hide(); + textfield.css("background-color", ""); + } + })); + } else { + this.showWaitScreen(); + + this.sendLdtData(contents, function(annotation) { + if (_this.cinecast_version) { + if (_this._Popcorn.media.paused) + _this._Popcorn.play(); + } + + if (_this._state == "waitScreen") { + _this.showEndScreen(annotation); + if (_this.cinecast_version) { + window.setTimeout(function() { _this.showStartScreen(); }, _this.return_delay); + } + } + // hide the slicer widget + if (!_this.cinecast_version) { + _this._Popcorn.trigger("IriSP.SliceWidget.hide"); + } + }); + } +}; + +IriSP.createAnnotationWidget.prototype.handleSliderChanges = function(params) { + this.sliceLeft = params[0]; + this.sliceWidth = params[1]; +}; + +IriSP.createAnnotationWidget.prototype.sendLdtData = function(contents, callback) { + var _this = this; + var apiJson = { + format : "http://advene.org/ns/cinelab/", + annotations : [ + {} + ], + meta: {}}; + var annotation = apiJson.annotations[0]; + + annotation.media = this.currentMedia()["id"]; + + if (this.cinecast_version) { + annotation.begin = Math.round(this._Popcorn.currentTime() * 1000); + annotation.end = annotation.begin; + } else { + var duration = this.getDuration(); + annotation.begin = +((duration * (this.sliceLeft / 100)).toFixed(0)); + annotation.end = +((duration * ((this.sliceWidth + this.sliceLeft) / 100)).toFixed(0)); + } + + // boundary checks + annotation.begin = Math.max(0, annotation.begin); + annotation.end = Math.min(this.getDuration(), annotation.end); + + annotation.type = ( this.cinecast_version ? "cinecast:UserAnnotation" : ( this._serializer.getContributions() || "" )); + if (typeof(annotation.type) === "undefined") + annotation.type = ""; + + annotation.type_title = "Contributions"; + annotation.content = {}; + annotation.content.data = contents; + if (this.cinecast_version) { + var _extract = IriSP.underscore(this._serializer._data.annotations) + .filter(function(_a) { + return (_a.begin <= annotation.begin && _a.end >= annotation.begin && _a.type == "cinecast:MovieExtract"); + }) + if (_extract.length) { + annotation.extract = _extract[0].id; + } + } + + var meta = apiJson.meta; + + + var _username = this.selector.find(".Ldt-createAnnotation-userName").val(); + meta.creator = ( + (_username && _username.length) + ? _username + : ( + (!IriSP.null_or_undefined(IriSP.user) && !IriSP.null_or_undefined(IriSP.user.name)) + ? IriSP.user.name + : "Anonymous user" + ) + ); + + meta.created = Date().toString(); + + var _tags = []; + IriSP._(this.tags).each(function(_v) { + var _rx = IriSP.regexpFromText(_v.meta.description); + if (_rx.test(contents)) { + _tags.push(_v.id); + } + }); + + if (typeof this.remote_tags == "object") { + _tags = IriSP._(_tags).map(function(_t) { + return _this.remote_tags.id + ':' + _t + }); + if (typeof apiJson.imports == "undefined") { + apiJson.imports = []; + } + apiJson.imports.push({ + "id" : this.remote_tags.id, + "url" : this.remote_tags.url + }) + } + annotation.tags = IriSP.underscore.uniq(_tags); + + var jsonString = JSON.stringify(apiJson); + var project_id = this._serializer._data.meta.id; + + //TODO: extract magic url + var url = Mustache.to_html(this.api_endpoint_template, + {id: project_id}); + + IriSP.jQuery.ajax({ + url: url, + type: this.api_method, + contentType: 'application/json', + data: jsonString, + //dataType: 'json', + success: IriSP.wrap(this, function(json, textStatus, XMLHttpRequest) { + /* add the annotation to the annotation and tell the world */ + var annotation = json.annotations[0]; + + if (!this.cinecast_version) { + /* if the media doesn't have a contributions line, we need to add one */ + if (typeof(this._serializer.getContributions()) === "undefined") { + /* set up a basic view */ + var tmp_view = {"dc:contributor": "perso", "dc:creator": "perso", "dc:title": "Contributions", + "id": json.annotations[0].type} + + + IriSP.get_aliased(this._serializer._data, ["annotation_types", "annotation-types"]).push(tmp_view); + } + + delete annotation.tags; + annotation.content.description = annotation.content.data; + annotation.content.title = ""; + delete annotation.content.data; + annotation.id = json.annotations[0].id; + + annotation.meta = meta; + annotation.meta["id-ref"] = json.annotations[0]["type"]; + } else { + annotation.type = "cinecast:UserAnnotation"; + } + // everything is shared so there's no need to propagate the change + var _an_ids = IriSP.underscore(this._serializer._data.annotations).map(function(_a) { + return _a.id.toLowerCase(); + }); + if (IriSP._(_an_ids).indexOf(annotation.id.toLowerCase()) == -1) { + _this._serializer._data.annotations.push(annotation); + } + + _this._Popcorn.trigger("IriSP.createAnnotationWidget.addedAnnotation", annotation); + this.selector.find(".Ldt-createAnnotation-Description").val("").trigger("js_mod"); + callback(annotation); + }), + error: + function(jqXHR, textStatus, errorThrown) { + console.log("an error occured while contacting " + + url + " and sending " + jsonString + textStatus ); + _this.showErrorScreen(); } }); +};IriSP.HelloWorldWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); +} + +IriSP.HelloWorldWidget.prototype = new IriSP.Widget(); + +IriSP.HelloWorldWidget.prototype.draw = function() { + this.selector + .html('Hello, world') + .css({ + "text-align" : "center", + "padding": "10px 0", + "font-size" : "14px" + }); + + console.log(this); +} +/* Internationalization for this widget */ + +IriSP.i18n.addMessages( + { + "en": { + "play_pause": "Play/Pause", + "mute_unmute": "Mute/Unmute", + "play": "Play", + "pause": "Pause", + "mute": "Mute", + "unmute": "Unmute", + "annotate": "Annotate", + "search": "Search", + "elapsed_time": "Elapsed time", + "total_time": "Total time", + "volume": "Volume", + "volume_control": "Volume control" + }, + "fr": { + "play_pause": "Lecture/Pause", + "mute_unmute": "Couper/Activer le son", + "play": "Lecture", + "pause": "Pause", + "mute": "Couper le son", + "unmute": "Activer le son", + "annotate": "Annoter", + "search": "Rechercher", + "elapsed_time": "Durée écoulée", + "total_time": "Durée totale", + "volume": "Niveau sonore", + "volume_control": "Réglage du niveau sonore" + } + } +); + + +IriSP.PlayerWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + + this._searchBlockOpen = false; + this._searchLastValue = ""; +}; + +IriSP.PlayerWidget.prototype = new IriSP.Widget(); + +IriSP.PlayerWidget.prototype.draw = function() { + var self = this; + var width = this.width; + var height = this.height; + var heightS = this.height-20; + + var playerTempl = IriSP.templToHTML(IriSP.player_template, this._config); + this.selector.append(playerTempl); + + this.selector.children(".Ldt-controler").show(); + + // handle clicks by the user on the video. + this._Popcorn.listen("play", IriSP.wrap(this, this.playButtonUpdater)); + this._Popcorn.listen("pause", IriSP.wrap(this, this.playButtonUpdater)); + + this._Popcorn.listen("volumechange", IriSP.wrap(this, this.volumeUpdater)); + + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeDisplayUpdater)); + // update the time display for the first time. + this._Popcorn.listen("loadedmetadata", IriSP.wrap(this, this.timeDisplayUpdater)); + + this._Popcorn.listen("IriSP.search.matchFound", IriSP.wrap(this, this.searchMatch)); + this._Popcorn.listen("IriSP.search.noMatchFound", IriSP.wrap(this, this.searchNoMatch)); + this._Popcorn.listen("IriSP.search.triggeredSearch", IriSP.wrap(this, this.triggeredSearch)); + + + this.selector.find(".Ldt-CtrlPlay").click(function() { self.playHandler.call(self); }); + this.selector.find(".Ldt-CtrlAnnotate").click(function() + { self._Popcorn.trigger("IriSP.PlayerWidget.AnnotateButton.clicked"); }); + this.selector.find(".Ldt-CtrlSearch").click(function() { self.searchButtonHandler.call(self); }); + + var _volctrl = this.selector.find(".Ldt-Ctrl-Volume-Control"); + this.selector.find('.Ldt-CtrlSound') + .click(function() { self.muteHandler.call(self); } ) + .mouseover(function() { + _volctrl.show(); + }) + .mouseout(function() { + _volctrl.hide(); + }); + _volctrl.mouseover(function() { + _volctrl.show(); + }).mouseout(function() { + _volctrl.hide(); + }); + + /* + var searchButtonPos = this.selector.find(".Ldt-CtrlSearch").position(); + var searchBox = Mustache.to_html(IriSP.search_template, {margin_left : searchButtonPos.left + "px"}); + this.selector.find(".Ldt-CtrlSearch").after(searchBox); + */ + + // trigger an IriSP.PlayerWidget.MouseOver to the widgets that are interested (i.e : sliderWidget) + this.selector.hover(function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOver"); }, + function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOut"); }); + this.selector.find(".Ldt-Ctrl-Volume-Cursor").draggable({ + axis: "x", + drag: function(event, ui) { + var _vol = Math.max(0, Math.min( 1, ui.position.left / (ui.helper.parent().width() - ui.helper.outerWidth()))); + ui.helper.attr("title",IriSP.i18n.getMessage('volume')+': ' + Math.floor(100*_vol) + '%'); + self._Popcorn.volume(_vol); + }, + containment: "parent" + }); + + setTimeout(function() { + self.volumeUpdater(); + }, 1000); /* some player - jwplayer notable - save the state of the mute button between sessions */ +}; + +/* Update the elasped time div */ +IriSP.PlayerWidget.prototype.timeDisplayUpdater = function() { + + if (this._previousSecond === undefined) { + this._previousSecond = this._Popcorn.roundTime(); + } + else { + /* we're still in the same second, so it's not necessary to update time */ + if (this._Popcorn.roundTime() == this._previousSecond) + return; + + } + + // we get it at each call because it may change. + var duration = this.getDuration() / 1000; + var totalTime = IriSP.secondsToTime(duration); + var elapsedTime = IriSP.secondsToTime(this._Popcorn.currentTime()); + + this.selector.find(".Ldt-ElapsedTime").html(elapsedTime.toString()); + this.selector.find(".Ldt-TotalTime").html(totalTime.toString()); + this._previousSecond = this._Popcorn.roundTime(); +}; + +/* update the icon of the button - separate function from playHandler + because in some cases (for instance, when the user directly clicks on + the jwplayer window) we have to change the icon without playing/pausing +*/ +IriSP.PlayerWidget.prototype.playButtonUpdater = function() { + var status = this._Popcorn.media.paused; + + if ( status == true ){ + /* the background sprite is changed by adding/removing the correct classes */ + this.selector.find(".Ldt-CtrlPlay").attr("title", IriSP.i18n.getMessage('play')); + this.selector.find(".Ldt-CtrlPlay").removeClass("Ldt-CtrlPlay-PauseState").addClass("Ldt-CtrlPlay-PlayState"); + } else { + this.selector.find(".Ldt-CtrlPlay").attr("title", IriSP.i18n.getMessage('pause')); + this.selector.find(".Ldt-CtrlPlay").removeClass("Ldt-CtrlPlay-PlayState").addClass("Ldt-CtrlPlay-PauseState"); + } + + return; +}; + + +IriSP.PlayerWidget.prototype.playHandler = function() { + var status = this._Popcorn.media.paused; + + if ( status == true ){ + this._Popcorn.play(); + } else { + this._Popcorn.pause(); + } +}; + +IriSP.PlayerWidget.prototype.muteHandler = function() { + this._Popcorn.mute(!this._Popcorn.muted()); +}; + +IriSP.PlayerWidget.prototype.volumeUpdater = function() { + var _muted = this._Popcorn.muted(), + _vol = this._Popcorn.volume(); + if (_vol === false) { + _vol = .5; + } + var _soundCtl = this.selector.find(".Ldt-CtrlSound"); + _soundCtl.removeClass("Ldt-CtrlSound-Mute Ldt-CtrlSound-Half Ldt-CtrlSound-Full"); + if (_muted) { + _soundCtl.attr("title", IriSP.i18n.getMessage('unmute')) + .addClass("Ldt-CtrlSound-Mute"); + } else { + _soundCtl.attr("title", IriSP.i18n.getMessage('mute')) + .addClass(_vol < .5 ? "Ldt-CtrlSound-Half" : "Ldt-CtrlSound-Full" ) + } + var _cursor = this.selector.find(".Ldt-Ctrl-Volume-Cursor"); + _cursor.css({ + "left": ( _muted ? 0 : Math.floor(_vol * (_cursor.parent().width() - _cursor.outerWidth())) ) + "px" + }) +}; + +IriSP.PlayerWidget.prototype.showSearchBlock = function() { + var self = this; + + if (this._searchBlockOpen == false) { + this.selector.find(".LdtSearch").show("blind", { direction: "horizontal"}, 100); + this.selector.find(".LdtSearchInput").css('background-color','#fff'); + + this._searchBlockOpen = true; + this.selector.find(".LdtSearchInput").bind('keyup', null, function() { self.searchHandler.call(self); } ); + this.selector.find(".LdtSearchInput").focus(); + + // we need this variable because some widget can find a match in + // their data while at the same time other's don't. As we want the + // search field to become green when there's a match, we need a + // variable to remember that we had one. + this._positiveMatch = false; + + // tell the world the field is open + this._Popcorn.trigger("IriSP.search.open"); + } +}; + +IriSP.PlayerWidget.prototype.hideSearchBlock = function() { + if (this._searchBlockOpen == true) { + this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value'); + this.selector.find(".LdtSearchInput").attr('value',''); + this.selector.find(".LdtSearch").hide("blind", { direction: "horizontal"}, 75); + + // unbind the watcher event. + this.selector.find(".LdtSearchInput").unbind('keypress set'); + this._searchBlockOpen = false; + + this._positiveMatch = false; + + this._Popcorn.trigger("IriSP.search.closed"); + } +}; + +/** react to clicks on the search button */ +IriSP.PlayerWidget.prototype.searchButtonHandler = function() { + var self = this; + + /* show the search field if it is not shown */ + if ( this._searchBlockOpen == false ) { + this.showSearchBlock(); + this.selector.find(".LdtSearchInput").attr('value', this._searchLastValue); + this._Popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural. + } else { + this.hideSearchBlock(); + } +}; + +/** this handler is called whenever the content of the search + field changes */ +IriSP.PlayerWidget.prototype.searchHandler = function() { + this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value'); + this._positiveMatch = false; + + // do nothing if the search field is empty, instead of highlighting everything. + if (this._searchLastValue == "") { + this._Popcorn.trigger("IriSP.search.cleared"); + this.selector.find(".LdtSearchInput").css('background-color',''); + } else { + this._Popcorn.trigger("IriSP.search", this._searchLastValue); + } +}; + +/** + handler for the IriSP.search.found message, which is sent by some views when they + highlight a match. +*/ +IriSP.PlayerWidget.prototype.searchMatch = function() { + this._positiveMatch = true; + this.selector.find(".LdtSearchInput").css('background-color','#e1ffe1'); +}; + +/** the same, except that no value could be found */ +IriSP.PlayerWidget.prototype.searchNoMatch = function() { + if (this._positiveMatch !== true) + this.selector.find(".LdtSearchInput").css('background-color', "#d62e3a"); +}; + +/** react to an IriSP.Player.triggeredSearch - that is, when + a widget ask the PlayerWidget to do a search on his behalf */ +IriSP.PlayerWidget.prototype.triggeredSearch = function(searchString) { + this.showSearchBlock(); + this.selector.find(".LdtSearchInput").attr('value', searchString); + this._Popcorn.trigger("IriSP.search", searchString); // trigger the search to make it more natural. +}; + + +/* + * + * Copyright 2010 Institut de recherche et d'innovation + * contributor(s) : Samuel Huron + * + * contact@iri.centrepompidou.fr + * http://www.iri.centrepompidou.fr + * + * This software is a computer program whose purpose is to show and add annotations on a video . + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. +*/ +// CHART TIMELINE / VERSION PROTOTYPE :: + +/** the polemic widget */ +IriSP.PolemicWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + + this.userPol = new Array(); + this.userNoPol = new Array(); + this.userst = new Array(); + this.numberOfTweet = 0; + this.Users; + this.TweetPolemic; + this.yMax = this.height; + this.PaperSlider; + this.heightOfChart; + this.tweets = new Array(); + this.svgElements = {}; + + this.oldSearchMatches = []; +}; + +IriSP.PolemicWidget.prototype = new IriSP.Widget(); + +IriSP.PolemicWidget.prototype.draw = function() { + + // variable + // yMax + + var self = this; + var yCoef = 2; // coef for height of 1 tweet + var frameSize = 5; // frame size + var margin = 1; // marge between frame + var lineSize = this.width; // timeline pixel width + var nbrframes = lineSize/frameSize; // frame numbers + var numberOfTweet = 0; // number of tweet overide later + var duration = this.getDuration(); // timescale width + var frameLength = lineSize / frameSize; // frame timescale + var timeline; + var colors = new Array("","#1D973D","#036AAE","#CE0A15","#C5A62D","#585858"); + + // array + //var tweets = new Array(); + var element = new Array(); + var cluster = new Array(); + var frames = new Array(frameLength); + var slices = new Array(); + + + // Classes ======================================================================= + var Frames = function(){ + + var Myclusters; + var x; + var y; + var width; + var height; + }; + Frames = function(json){ + // make my clusters + // ou Frame vide + }; + Frames.prototype.draw = function(){ + }; + Frames.prototype.zoom = function(){ + }; + Frames.prototype.inside = function(){ + }; + var Clusters = function(){ + var Object; + var yDist; + var x; + var y; + var width; + var height; + }; + Clusters = function(json){ + // make my object + }; + var Tweet = function(){ + }; + // Classes ======================================================================= + + // Refactoring (parametere) ************************************************************ + // color translastion + var qTweet_0 =0; + var qTweet_Q =0; + var qTweet_REF=0; + var qTweet_OK =0; + var qTweet_KO =0; + function colorTranslation(value){ + if(value == "Q"){ + qTweet_Q+=1; + return 2; + }else if(value =="REF"){ + qTweet_REF+=1; + return 4; + }else if(value =="OK"){ + qTweet_OK+=1; + return 1; + }else if(value =="KO"){ + qTweet_KO+=1; + return 3; + }else if(value ==""){ + qTweet_0+=1; + return 5; + } + } + + + this._serializer.sync(function(data) { loaded_callback.call(self, data); return; }); + + function loaded_callback (json) { + var view_type = this._serializer.getTweets(); + + + if (typeof(view_type) === "undefined") { + var view_type = this._serializer.getTweetIds()[0]; + if (typeof(view_type) === "undefined") { + // default to guessing if nothing else works. + var view = json.views[0]; + + if(typeof(view.annotation_types) !== "undefined") { + /* we need to be backward compatible with the old files which used to + feature only two lines : Chapitrage and Tweets. We've added a + "Contributions" line so we need to discriminate against that */ + if (view.annotation_types.length === 2 && typeof(this._serializer.getContributions()) === "undefined") { + var view_type = view.annotation_types[1]; + } else { + console.log("PolemicWidget: invalid file - minimizing"); + return; + } + } + } + } + + // Make and define the Raphael area + this.paper = Raphael(document.getElementById(this._id), this._config.width, this._config.height); + + // event handlers + this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); })); + this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler)); + this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler)); + this.selector.mouseleave(IriSP.wrap(this, function() { self.TooltipWidget.hide.call(self.TooltipWidget); })); + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater)); + this._Popcorn.listen("IriSP.Mediafragment.showAnnotation", IriSP.wrap(this, this.showAnnotation)); + + for(var i = 0; i < json.annotations.length; i++) { + var item = json.annotations[i]; + var MyTime = Math.floor(item.begin/duration*lineSize); + var Myframe = Math.floor(MyTime/lineSize*frameLength); + + if (typeof(item.meta) !== "undefined" + && typeof(item.meta["id-ref"]) !== "undefined" + && item.meta["id-ref"] === view_type) { + + var MyTJson = {}, + _source = IriSP.get_aliased(item.meta, ['dc:source', 'source']); + if (_source !== null) { + var MyTJson = JSON.parse(_source['content']); + } + + if (item.content['polemics'] != undefined + && item.content['polemics'][0] != null) { + + // a tweet can have many polemics at the same time. + for(var j=0; j max) { + max = moy; + } + } + + var tweetDrawed = new Array(); + var TweetHeight = 5; + var newHeight = TweetHeight * max + 10; + + + if (newHeight > this.height) { + this.paper.setSize(this.width, newHeight); + this.height = newHeight; + console.log("resizeing"); + } + + + // DRAW TWEETS ============================================ + for(var i = 0; i < nbrframes; i++) { + var addEheight = 5; + if (frames[i] != undefined){ + // by type + + for (var j = 6; j > -1; j--) { + if (frames[i].qualifVol[j] != undefined) { + // show tweet by type + for (var k = 0; k < frames[i].mytweetsID.length; k++) { + + if (frames[i].mytweetsID[k].qualification == j) { + var x = i * frameSize; + var y = this.height - addEheight; + + if (this.yMax > y) { + this.yMax = y; + } + + /* some tweets seem to be duplicated - so we make a check before + creating a new rect */ + if (this.svgElements.hasOwnProperty(frames[i].mytweetsID[k].cinecast_id)) + continue; + + var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */) + .attr({stroke:"#00","stroke-width":0.1, fill: colors[j]}); + + addEheight += TweetHeight; + + /* stick a lot of things into e because that's the easiest way + to do it */ + e.color = colors[j]; + e.time = frames[i].mytweetsID[k].timeframe; + e.title = frames[i].mytweetsID[k].title; + e.id = frames[i].mytweetsID[k].cinecast_id; + this.svgElements[e.id] = e; + + IriSP.jQuery(e.node).mouseenter(function(element) { return function (_e) { + self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), element.attrs.x + element.attrs.width / 2, element.attrs.y - 2); + element.displayed = true; + self._Popcorn.trigger("IriSP.TraceWidget.MouseEvents", { + "widget" : "StackGraphWidget", + "type": "mousemove", + "x": _e.pageX, + "y": _e.pageY, + "annotation_id": element.id + }); + }}(e)).mousedown(function(element) { return function () { + self._Popcorn.currentTime(element.time/1000); + self._Popcorn.trigger("IriSP.PolemicTweet.click", element.id); + } + }(e)); + + IriSP.jQuery(e.node).attr('id', 't' + k + ''); + IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title); + IriSP.jQuery(e.node).attr('begin', frames[i].mytweetsID[k].timeframe); + } + } + } + } + } + + } + // DRAW UI :: resize border and bgd + this.paperBackground = this.paper.rect(0, 0, this.width, this.height).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1}); + + // outer borders + this.outerBorders = []; + this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); + this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1})); + + // inner borders + this.innerBorders = []; + this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); + this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1})); + this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8})); + this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1})); + + + + this.paperSlider = this.paper.rect(0, 0, 0, this.height).attr({fill:"#D4D5D5", stroke: "none", opacity: 1}); + + // the small white line displayed over the slider. + this.sliderTip = this.paper.rect(0, 0, 1, this.height).attr({fill:"#fc00ff", stroke: "none", opacity: 1}); + // decalage + // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1}); + + + this.paperSlider.toBack(); + this.paperBackground.toBack(); + this.sliderTip.toFront(); + } + + +} + +/** update the positionMarker as time passes */ +IriSP.PolemicWidget.prototype.sliderUpdater = function() { + + var time = +this._Popcorn.currentTime(); + var duration = this.getDuration(); + + this.paperSlider.attr("width", time * (this.width / (duration / 1000))); + + this.sliderTip.attr("x", time * (this.width / (duration / 1000))); +}; + +/** reacts to IriSP.search events */ +IriSP.PolemicWidget.prototype.searchHandler = function(searchString) { + if (searchString == "") + return; + + var matches = this._serializer.searchTweetsOccurences(searchString); + + if (IriSP.countProperties(matches) > 0) { + this._Popcorn.trigger("IriSP.search.matchFound"); + } else { + this._Popcorn.trigger("IriSP.search.noMatchFound"); + } + + + // decrease the opacity of the other elements. + for (var id in this.svgElements) { + var e = this.svgElements[id]; + e.attr({fill: e.color, opacity: 0.4}); + } + + + for (var id in matches) { + if (this.svgElements.hasOwnProperty(id)) { + var e = this.svgElements[id]; + this.svgElements[id].attr({fill: "#fc00ff", opacity: 1}); + } + } + + this.oldSearchMatches = matches; +}; + +/** reacts to IriSP.search.cleared messages */ +IriSP.PolemicWidget.prototype.searchFieldClearedHandler = function() { + for (var id in this.svgElements) { + var e = this.svgElements[id]; + e.attr({fill: e.color, opacity: 1}); + } +}; + +/** reacts to IriSP.search.closed messages by clearing the highlighted elements */ +IriSP.PolemicWidget.prototype.searchFieldClosedHandler = function() { + for (var id in this.svgElements) { + var e = this.svgElements[id]; + e.attr({fill: e.color, opacity: 1}); + } + +}; + +IriSP.PolemicWidget.prototype.showAnnotation = function(id) { + if (this.svgElements.hasOwnProperty(id)) { + var e = this.svgElements[id]; + this.TooltipWidget.show(e.title, e.attr("fill"), e.x - 103, e.y - 160); + } +}; +IriSP.SegmentsWidget = function(Popcorn, config, Serializer) { + + var self = this; + IriSP.Widget.call(this, Popcorn, config, Serializer); + this.oldSearchMatches = []; + + // event handlers + this._Popcorn.listen("IriSP.search", function(searchString) { self.searchHandler.call(self, searchString); }); + this._Popcorn.listen("IriSP.search.closed", function() { self.searchFieldClosedHandler.call(self); }); + this._Popcorn.listen("IriSP.search.cleared", function() { self.searchFieldClearedHandler.call(self); }); + + this.defaultColors = ["#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"] +}; + +IriSP.SegmentsWidget.prototype = new IriSP.Widget(); + +IriSP.SegmentsWidget.prototype.draw = function() { + + var self = this; + var annotations = this._serializer._data.annotations; + + this.selector.addClass("Ldt-SegmentsWidget"); + this.selector.append(Mustache.to_html(IriSP.overlay_marker_template)); + + this.positionMarker = this.selector.find(".Ldt-SegmentPositionMarker"); + + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.positionUpdater)); + var duration = this.getDuration(); + + if (this.cinecast_version) { + var segments_annotations = IriSP.underscore.filter( + this._serializer._data.annotations, + function(_a) { + return _a.type == "cinecast:MovieExtract"; + } + ); + } + else { + + var view_type = this._serializer.getChapitrage(); + if (typeof(view_type) === "undefined") { + view_type = this._serializer.getNonTweetIds()[0]; + } + + + var i = 0; + + var segments_annotations = []; + + for (i = 0; i < annotations.length; i++) { + var annotation = annotations[i]; + + /* filter the annotations whose type is not the one we want */ + if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" + && annotation.meta["id-ref"] != view_type) { + continue; + } + + segments_annotations.push(annotation); + } +} + var _w = this.selector.width(); + var lastSegment = IriSP.underscore.max(segments_annotations, function(annotation) { return annotation.end; }); + + for (i = 0; i < segments_annotations.length; i++) { + + var annotation = segments_annotations[i]; + var begin = (+ annotation.begin); + var end = (+ annotation.end); + var id = annotation.id; + + var startPixel = Math.floor(_w * (begin / duration)); + + var endPixel = Math.floor(_w * (end / duration)); + if (annotation.id !== lastSegment.id) + var pxWidth = endPixel - startPixel -1; + else + /* the last segment has no segment following it */ + var pxWidth = endPixel - startPixel; + + var divTitle = this.cinecast_version + ? annotation.content.data + : annotation.content.title + ( annotation.content.title ? "
" : "" ) + annotation.content.description.replace(/(^.{120,140})[\s].+$/,'$1…'); + + var thumbUrl = annotation.meta.thumbnail || ''; + + var hexa_color = typeof(annotation.content.color) !== "undefined" + ? '#' + IriSP.DEC_HEXA_COLOR(annotation.content.color) + : typeof(annotation.color) !== "undefined" + ? '#' + IriSP.DEC_HEXA_COLOR(annotation.color) + : this.defaultColors[i % this.defaultColors.length]; + + /* + if (hexa_color === "FFCC00") + hexa_color = "333"; + */ + if (hexa_color.length == 5) + hexa_color = hexa_color + '00'; + + + var annotationTemplate = Mustache.to_html(IriSP.annotation_template, + {"divTitle" : divTitle, "id" : id, "startPixel" : startPixel, + "pxWidth" : pxWidth, "hexa_color" : hexa_color, + "seekPlace" : Math.round(begin/1000), "thumbnailUrl": thumbUrl}); + + + this.selector.append(annotationTemplate); + + /* add a special class to the last segment and change its border */ + if (annotation.id === lastSegment.id) { + IriSP.jqId(id).addClass("Ldt-lastSegment").css("border-color", hexa_color); + } + } + // react to mediafragment messages. + this._Popcorn.listen("IriSP.Mediafragment.showAnnotation", + function(id, divTitle) { + + var divObject = IriSP.jqId(id); + if (divObject.length) { + divObject.fadeTo(0,1); + var offset_x = divObject.position().left + divObject.outerWidth() / 2; + self.TooltipWidget.show(divObject.attr("title"), IriSP.jQuery(this).css("background-color"), offset_x, 0); + IriSP.jQuery(document).one("mousemove", function() { divObject.fadeTo(0,.5); + self.TooltipWidget.hide(); }); + } + }); + + this.selector.find(".Ldt-iri-chapter") + .fadeTo(0, .5) + .click(function() { + self._Popcorn.trigger("IriSP.SegmentsWidget.click", this.id); + self._Popcorn.currentTime(IriSP.jQuery(this).attr("data-seek")); + }) + .mouseover( function(event) { + var divObject = IriSP.jQuery(this); + divObject.fadeTo(0,1); + var offset_x = divObject.position().left + divObject.outerWidth() / 2; + var thumb = divObject.attr("thumbnail-url"); + var txt = divObject.attr("title") + (thumb && thumb.length ? '
' : ''); + self.TooltipWidget.show(txt, IriSP.jQuery(this).css("background-color"), offset_x, 0); + }) + .mouseout(function(){ + IriSP.jQuery(this).fadeTo(0,.5); + self.TooltipWidget.hide(); + }); +}; + +/* restores the view after a search */ +IriSP.SegmentsWidget.prototype.clear = function() { + this.selector.children(".Ldt-iri-chapter").fadeTo(0,.5); +}; + +IriSP.SegmentsWidget.prototype.clickHandler = function(annotation) { + this._Popcorn.trigger("IriSP.SegmentsWidget.click", annotation.id); + var begin = (+ annotation.begin) / 1000; + this._Popcorn.currentTime(Math.round(begin)); +}; + +IriSP.SegmentsWidget.prototype.searchHandler = function(searchString) { + + if (searchString == "") + return; + + var matches = this._serializer.searchOccurences(searchString); + + if (IriSP.countProperties(matches) > 0) { + this._Popcorn.trigger("IriSP.search.matchFound"); + } else { + this._Popcorn.trigger("IriSP.search.noMatchFound"); + } + + // un-highlight all the blocks + this.selector.children(".Ldt-iri-chapter").css("opacity", 0.1); + + // then highlight the ones with matches. + for (var id in matches) { + var factor = 0.5 + matches[id] * 0.2; + this.selector.find("#"+id).dequeue(); + this.selector.find("#"+id).animate({opacity:factor}, 200); + } + + + this.oldSearchMatches = matches; +}; + +IriSP.SegmentsWidget.prototype.searchFieldClearedHandler = function() { + this.clear(); +}; + +IriSP.SegmentsWidget.prototype.searchFieldClosedHandler = function() { + this.clear(); +}; + +IriSP.SegmentsWidget.prototype.positionUpdater = function() { + var duration = this.getDuration() / 1000; + var time = this._Popcorn.currentTime(); + //var position = ((time / duration) * 100).toFixed(2); + var position = ((time / duration) * 100).toFixed(2); + + this.positionMarker.css("left", position + "%"); +}; + +IriSP.SegmentsWidget.prototype.showAnnotation = function() { + +}; +/** A widget to create a new segment */ +IriSP.SliceWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + +}; + +IriSP.SliceWidget.prototype = new IriSP.Widget(); + +IriSP.SliceWidget.prototype.draw = function() { + var templ = Mustache.to_html(IriSP.sliceWidget_template); + this.selector.append(templ); + + this.sliceZone = this.selector.find(".Ldt-sliceZone"); + + /* global variables used to keep the position and width + of the zone. + */ + this.zoneLeft = 0; + this.zoneWidth = 0; + + this.leftHandle = this.selector.find(".Ldt-sliceLeftHandle"); + this.rightHandle = this.selector.find(".Ldt-sliceRightHandle"); + + var left = this.selector.offset().left; + var top = this.selector.offset().top; + + // a bug in firefox makes it use the wrong format + if (!IriSP.jQuery.browser.mozilla) { + // contain the handles correctly - we cannot set + // containment: parent because it wouldn't allow to select the + // whole slice, so we have to compute a box in which the slice is + // allowed to move. + var containment = [left - 8, top, this.selector.width() + left, top]; + + // var containment = [left - 16, top, this.selector.width() + left - 8, top]; + this.leftHandle.draggable({axis: "x", + drag: IriSP.wrap(this, this.leftHandleDragged), + containment: containment + }); + + containment = [left, top, this.selector.width() + left, top]; + // containment = [left, top, this.selector.width() + left - 8, top]; + this.rightHandle.draggable({axis: "x", + drag: IriSP.wrap(this, this.rightHandleDragged), + containment: containment + }); + + } else { // firefox + // we need to define a containment specific to firefox. + + var containment = [left - 16, top, this.selector.width() + left - 8, top]; + this.leftHandle.draggable({axis: "x", + drag: IriSP.wrap(this, this.leftHandleDragged), + containment: containment + }); + + containment = [left, top, this.selector.width() + left - 8, top]; + this.rightHandle.draggable({axis: "x", + drag: IriSP.wrap(this, this.rightHandleDragged), + containment: containment + }); + } + + this.leftHandle.css("position", "absolute"); + this.rightHandle.css("position", "absolute"); + + this._Popcorn.listen("IriSP.SliceWidget.position", + IriSP.wrap(this, this.positionSliceHandler)); + + this._Popcorn.listen("IriSP.SliceWidget.show", IriSP.wrap(this, this.show)); + this._Popcorn.listen("IriSP.SliceWidget.hide", IriSP.wrap(this, this.hide)); + this.selector.hide(); +}; + +/** responds to an "IriSP.SliceWidget.position" message + @param params an array with the first element being the left distance in + percents and the second element the width of the slice in pixels +*/ +IriSP.SliceWidget.prototype.positionSliceHandler = function(params) { + left = params[0]; + width = params[1]; + + this.zoneLeft = left; + this.zoneWidth = width; + this.sliceZone.css("left", left + "px"); + this.sliceZone.css("width", width + "px"); + this.leftHandle.css("left", (left - 7) + "px"); + this.rightHandle.css("left", left + width + "px"); + + this._leftHandleOldLeft = left - 7; + this._rightHandleOldLeft = left + width; +}; + +/** handle a dragging of the left handle */ +IriSP.SliceWidget.prototype.leftHandleDragged = function(event, ui) { + /* we have a special variable, this._leftHandleOldLeft, to keep the + previous position of the handle. We do that to know in what direction + is the handle being dragged + */ + + var currentX = this.leftHandle.offset().left; + var rightHandleX = Math.floor(this.rightHandle.position()["left"]); + + var container_offset = this.selector.offset().left; + + if (Math.floor(ui.position.left) >= rightHandleX - 7) { + /* prevent the handle from moving past the right handle */ + ui.position.left = rightHandleX - 7; + } + + this.zoneWidth = rightHandleX - Math.floor(ui.position.left) - 7; + this.zoneLeft = Math.floor(ui.position.left) + 8; + + this.sliceZone.css("width", this.zoneWidth); + this.sliceZone.css("left", this.zoneLeft + "px"); + + this._leftHandleOldLeft = ui.position.left; + this.broadcastChanges(); + +}; + +/** handle a dragging of the right handle */ +IriSP.SliceWidget.prototype.rightHandleDragged = function(event, ui) { + /* we have a special variable, this._leftHandleOldLeft, to keep the + previous position of the handle. We do that to know in what direction + is the handle being dragged + */ + + var currentX = this.leftHandle.position()["left"]; + var leftHandleX = Math.floor(this.leftHandle.position()["left"]); + + var container_offset = this.selector.offset().left + this.selector.width(); + + if (Math.floor(ui.position.left) < leftHandleX + 7) { + /* prevent the handle from moving past the left handle */ + ui.position.left = leftHandleX + 7; + } + + this.zoneWidth = Math.floor(ui.position.left) - (leftHandleX + 7); + + this.sliceZone.css("width", this.zoneWidth); + //this.sliceZone.css("left", this.zoneLeft + "px"); + this._rightHandleOldLeft = Math.floor(this._rightHandleOldLeft); + this.broadcastChanges(); +}; + +/** tell to the world that the coordinates of the slice have + changed +*/ +IriSP.SliceWidget.prototype.broadcastChanges = function() { + var leftPercent = (this.zoneLeft / this.selector.width()) * 100; + var zonePercent = (this.zoneWidth / this.selector.width()) * 100; + + this._Popcorn.trigger("IriSP.SliceWidget.zoneChange", [leftPercent, zonePercent]); +}; + +IriSP.SliceWidget.prototype.show = function() { + this.selector.show(); +}; + +IriSP.SliceWidget.prototype.hide = function() { + this.selector.hide(); +}; +/** A widget to display slide show from embed slide share */ +IriSP.SlideShareWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + // Default flash embed size + this.embed_width = 425; + this.embed_height = 355; + if(this._config.embed_width){ + this.embed_width = this._config.embed_width; + } + if(this._config.embed_height){ + this.embed_height = this._config.embed_height; + } +}; + +IriSP.SlideShareWidget.prototype = new IriSP.Widget(); + +IriSP.SlideShareWidget.prototype.draw = function() { + var self = this; + + // If the div supposed to host the slides does not exist, we cancel + if(this.selector.length==0){ + if(console){ if(console.log){ console.log("No div for slideshare widget, this widget is canceled. id = " + this._id); } } + return; + } + var templ = Mustache.to_html(IriSP.slideShareWidget_template); + this.selector.append(templ); + + // global variables used to keep the position and width of the zone. + this.zoneLeft = 0; + this.zoneWidth = 0; + // global variable to save the last slide url + this.lastSSFullUrl = ""; + this.lastSSUrl = ""; + this.lastSSId = ""; + this.containerDiv = this.selector.find('.SlideShareContainer'); + + // Synchro management + this._disableUpdate = false; + this.buttonsDiv = this.selector.find('.SlideShareButtons'); + this.buttonsDiv.width(this.embed_width - 2); // -2 because of css borders 328 -> 235px + this.buttonsDiv.find('.left_icon').css("margin-left",(this.embed_width-96)+"px"); + this.buttonsDiv.find('.ss_sync_on').click(function(event) { self.unSyncHandler.call(self, event); }); + this.buttonsDiv.find('.ss_sync_off').click(function(event) { self.syncHandler.call(self, event); }); + this.buttonsDiv.find('.ss_sync_off').hide(); + this.buttonsDiv.hide(); + + // Update the slide from timeupdate event + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.slideShareUpdater)); + + this._Popcorn.listen("IriSP.SlideShareWidget.show", IriSP.wrap(this, this.show)); + this._Popcorn.listen("IriSP.SlideShareWidget.hide", IriSP.wrap(this, this.hide)); + + // Get data from "slideshare" cutting/annotation-type + var annotations = this._serializer._data.annotations; + var view_type = this._serializer.getSlideShareType(); + if(typeof(view_type) === "undefined") { + if(console){ if(console.log){ console.log("No annotation-type for slideshare widget, this widget is canceled and the container is visible hidden."); } } + this.selector.hide(); + return; + } + var i = 0; + this.segments_slides = []; + var nb_annot = annotations.length; + for (i = 0; i < nb_annot; i++) { + var annotation = annotations[i]; + /* filter the annotations whose type is not the one we want */ + if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined" + && annotation.meta["id-ref"] != view_type) { + continue; + } + this.segments_slides.push(annotation); + } +}; + +/* update the slider and the position marker as time passes */ +IriSP.SlideShareWidget.prototype.slideShareUpdater = function() { + // If it is asked not to synchronize, we do nothing + if(this._disableUpdate) + return; + + var self = this; + + // We search if a segments_slides is in the current timecode + var time = this._Popcorn.currentTime() * 1000; + var nb_slides = this.segments_slides.length; + var forceEmpty = false; + for (i = 0; i < nb_slides; i++) { + var segment_slide = this.segments_slides[i]; + if(segment_slide.begin
'; + this.containerDiv.html(embed_code); + } + else{ + // If the presentation was already loaded, we only use the ss js api to load the wanted slide number + var embed = null; + if(this.lastSSId!=""){ + // If the presentation was loaded from a public url, we get the div from its id. + embed = document.getElementsByName("__sse" + this.lastSSId)[0]; + } + else if(this.lastSSUrl.substring(0,7)!="http://"){ + // If the presentation was loaded from a private id, we get the div from dom tree. + embed = this.containerDiv.children()[0].children[0]; + } + if(embed){ + embed.jumpTo(parseInt(slideNb)); + } + } + return; + } + } + } + } + if(forceEmpty==true){ + this.lastSSFullUrl = ""; + this.lastSSUrl = ""; + this.lastSSId = ""; + this.containerDiv.html(""); + this.buttonsDiv.hide(); + } + +}; + +// Functions to stop or trigger sync between timeupdate event and slides +IriSP.SlideShareWidget.prototype.unSyncHandler = function() { + //console.log("slideShare NO SYNC !"); + this._disableUpdate = true; + this.buttonsDiv.find('.ss_sync_on').hide(); + this.buttonsDiv.find('.ss_sync_off').show(); +}; +IriSP.SlideShareWidget.prototype.syncHandler = function() { + //console.log("slideShare SYNC PLEASE !"); + this._disableUpdate = false; + this.buttonsDiv.find('.ss_sync_on').show(); + this.buttonsDiv.find('.ss_sync_off').hide(); +}; + + +/** responds to an "IriSP.SlideShareWidget.position" message + @param params an array with the first element being the left distance in + percents and the second element the width of the slice in pixels +*/ +IriSP.SlideShareWidget.prototype.positionSlideShareHandler = function(params) { + //console.log("positionSlideShareHandler"); +}; + + +IriSP.SlideShareWidget.prototype.show = function() { + this.selector.show(); +}; + +IriSP.SlideShareWidget.prototype.hide = function() { + this.selector.hide(); +}; +IriSP.SliderWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); +}; + +IriSP.SliderWidget.prototype = new IriSP.Widget(); + +IriSP.SliderWidget.prototype.draw = function() { + var self = this; + + this.selector.append(Mustache.to_html(IriSP.sliderWidget_template, {})); + this.selector.addClass("Ldt-SliderMinimized"); + + this.sliderBackground = this.selector.find(".Ldt-sliderBackground"); + this.sliderForeground = this.selector.find(".Ldt-sliderForeground"); + this.positionMarker = this.selector.find(".Ldt-sliderPositionMarker"); + + + // a special variable to stop methods from tinkering + // with the positionMarker when the user is dragging it + this.draggingOngoing = false; + + // another special variable used by the timeout handler to + // open or close the slider. + this.sliderMaximized = false; + this.timeOutId = null; + + + this.positionMarker.draggable({axis: "x", + start: IriSP.wrap(this, this.positionMarkerDraggingStartedHandler), + stop: IriSP.wrap(this, this.positionMarkerDraggedHandler), + containment: "parent" + }); + this.positionMarker.css("position", "absolute"); + + this.sliderBackground.click(function(event) { self.backgroundClickHandler.call(self, event); }); + this.sliderForeground.click(function(event) { self.foregroundClickHandler.call(self, event); }); + + this.selector.hover(IriSP.wrap(this, this.mouseOverHandler), IriSP.wrap(this, this.mouseOutHandler)); + + // update the positions + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater)); + + // special messages : + this._Popcorn.listen("IriSP.PlayerWidget.MouseOver", IriSP.wrap(this, this.mouseOverHandler)); + this._Popcorn.listen("IriSP.PlayerWidget.MouseOut", IriSP.wrap(this, this.mouseOutHandler)); +}; + +/* update the slider and the position marker as time passes */ +IriSP.SliderWidget.prototype.sliderUpdater = function() { + if(this.draggingOngoing || this._disableUpdate) + return; + + var time = this._Popcorn.currentTime(); + + var duration = this.getDuration() / 1000; + var percents = time / duration; + + /* we do these complicated calculations to center exactly + the position Marker */ + + var divWidth = this.selector.width(); + var pixels = Math.floor(this.selector.width() * percents); + var positionMarker_width = this.positionMarker.width(); + var correction = (positionMarker_width / 2); + + /* check that we don't leave the left side */ + var newPos = pixels - correction; + if (newPos <= 0) + newPos = 0; + + /* check that we don't leave the right side */ + var rightEdgePos = pixels + 1 * correction; + + if (rightEdgePos >= divWidth) + newPos = divWidth - 1 * correction - 1; + + this.sliderForeground.css("width", pixels + "px"); + this.positionMarker.css("left", newPos + "px"); + +}; + +IriSP.SliderWidget.prototype.backgroundClickHandler = function(event) { + /* this piece of code is a little bit convoluted - here's how it works : + we want to handle clicks on the progress bar and convert those to seeks in the media. + However, jquery only gives us a global position, and we want a number of pixels relative + to our container div, so we get the parent position, and compute an offset to this position, + and finally compute the progress ratio in the media. + Finally we multiply this ratio with the duration to get the correct time + */ + + var parentOffset = this.sliderBackground.parent().offset(); + var width = this.sliderBackground.width(); + var relX = event.pageX - parentOffset.left; + + var duration = this.getDuration() / 1000; + var newTime = ((relX / width) * duration).toFixed(2); + + this._Popcorn.currentTime(newTime); +}; + +/* same function as the previous one, except that it handles clicks + on the foreground element */ +IriSP.SliderWidget.prototype.foregroundClickHandler = function(event) { + var parentOffset = this.sliderForeground.parent().offset(); + var width = this.sliderBackground.width(); + var relX = event.pageX - parentOffset.left; + + var duration = this.getDuration() / 1000; + var newTime = ((relX / width) * duration).toFixed(2); + + this._Popcorn.currentTime(newTime); +}; + +/* handles mouse over the slider */ +IriSP.SliderWidget.prototype.mouseOverHandler = function(event) { + + if (this.timeOutId !== null) { + window.clearTimeout(this.timeOutId); + } + + this.sliderMaximized = true; + + this.sliderBackground.animate({"height": "9px"}, 100); + this.sliderForeground.animate({"height": "9px"}, 100); + this.positionMarker.animate({"height": "9px", "width": "9px"}, 100); + //this.positionMarker.css("margin-top", "-4px"); + +// this.selector.removeClass("Ldt-SliderMinimized"); +// this.selector.addClass("Ldt-SliderMaximized"); +}; + +/* handles when the mouse leaves the slider */ +IriSP.SliderWidget.prototype.mouseOutHandler = function(event) { + + this.timeOutId = window.setTimeout(IriSP.wrap(this, this.minimizeOnTimeout), + this.minimize_period); +}; + +IriSP.SliderWidget.prototype.minimizeOnTimeout = function(event) { + this.sliderBackground.animate({"height": "5px"}, 100); + this.sliderForeground.animate({"height": "5px"}, 100); + this.positionMarker.animate({"height": "5px", "width": "5px"}, 100); + this.positionMarker.css("margin-top", "0px"); + this.sliderMinimized = true; + +// this.selector.removeClass("Ldt-SliderMaximized"); +// this.selector.addClass("Ldt-SliderMinimized"); + +}; + +// called when the user starts dragging the position indicator +IriSP.SliderWidget.prototype.positionMarkerDraggingStartedHandler = function(event, ui) { + this.draggingOngoing = true; +}; + +IriSP.SliderWidget.prototype.positionMarkerDraggedHandler = function(event, ui) { + +/* this._disableUpdate = true; // disable slider position updates while dragging is ongoing. + window.setTimeout(IriSP.wrap(this, function() { this._disableUpdate = false; }), 500); +*/ + var parentOffset = this.sliderForeground.parent().offset(); + var width = this.sliderBackground.width(); + var relX = event.originalEvent.pageX - parentOffset.left; + + var duration = this.getDuration() / 1000; + var newTime = ((relX / width) * duration).toFixed(2); + this._Popcorn.currentTime(newTime); + + this.draggingOngoing = false; +}; + +/** @class The constructor for the sparkline widget */ +IriSP.SparklineWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + + this._oldAnnotation = null; + this._results = []; + + this.slices = this._config.slices || Math.floor(this.width/20); + if (!this.width) { + this.width = this.selector.width(); + } + if (!this.height) { + this.height = 40; + } + this.selector.css("height", this.height + "px"); + if (this._config.background) { + this.selector.css("background", this._config.background); + } +}; + + +IriSP.SparklineWidget.prototype = new IriSP.Widget(); + +IriSP.SparklineWidget.prototype.clear = function() { + +}; + +/** draw the sparkline using jquery sparkline */ +IriSP.SparklineWidget.prototype.draw = function() { + this.duration = this.getDuration(); + this.paper = new Raphael(this.selector[0], this.width, this.height); + var _this = this; + + var views = this._serializer._data.views; + var stat_view; + if (!IriSP.null_or_undefined(views)) { + for (var i = 0; i < views.length; i++) { + var view = views[i]; + if (view.id === "stat") { + stat_view = view; + break; + } + } + } + + var _ = IriSP.underscore; + // If we've found the correct view, feed the directly the data from the view + // to jquery sparkline. Otherwise, compute it ourselves. + if (!IriSP.null_or_undefined(stat_view)) { + //console.log("sparklinewidget : using stats embedded in the json"); + var _results = stat_view.meta.stat.split(","); + } else { + var _annotations = this._serializer._data.annotations; + if (this.cinecast_version) { + _annotations = _(_annotations).filter(function(_a) { + return _a.type !== "cinecast:MovieExtract"; + }); + } + var _sliceDuration = Math.floor( this.duration / this.slices), + _results = _(_.range(this.slices)).map(function(_i) { + return _(_annotations).filter(function(_a){ + return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration) + }).length; + }); + } + var _max = Math.max(1, _(_results).max()), + _h = this.height, + _scale = (_h - this.lineWidth) / _max, + _width = this.width / this.slices, + _y = _(_results).map(function(_v) { + return _h - (_scale * _v); + }), + _d = _(_y).reduce(function(_memo, _v, _k) { + return _memo + ( _k + ? 'C' + (_k * _width) + ' ' + _y[_k - 1] + ' ' + (_k * _width) + ' ' + _v + ' ' + ((_k + .5) * _width) + ' ' + _v + : 'M0 ' + _v + 'L' + (.5*_width) + ' ' + _v ) + },'') + 'L' + this.width + ' ' + _y[_y.length - 1], + _d2 = _d + 'L' + this.width + ' ' + this.height + 'L0 ' + this.height; + this.paper.path(_d2).attr({ + "stroke" : "none", + "fill" : this.fillColor + }); + + this.paper.path(_d).attr({ + "fill" : "none", + "stroke" : this.lineColor, + "stroke-width" : this.lineWidth + }); + + this.rectangleProgress = this.paper.rect(0,0,0,this.height) + .attr({ + "stroke" : "none", + "fill" : "#808080", + "opacity" : .3 + }); + this.ligneProgress = this.paper.path("M0 0L0 "+this.height).attr({"stroke":"#ff00ff", "line-width" : 2}); + // save the results in an array so that we can re-use them when a new annotation + // is added. + this._results = _results; + + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler)); +// this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.handleNewAnnotation)); + + this.selector.click(IriSP.wrap(this, this.clickHandler)); +}; + +/** react to a timeupdate event */ +IriSP.SparklineWidget.prototype.timeUpdateHandler = function() { + var _currentTime = this._Popcorn.currentTime(), + _x = (1000 * _currentTime / this.duration) * this.width; + this.rectangleProgress.attr({ + "width" : _x + }); + this.ligneProgress.attr({ + "path" : "M" + _x + " 0L" + _x + " " + this.height + }); + +} + +/** handle clicks on the widget */ +IriSP.SparklineWidget.prototype.clickHandler = function(event) { + var relX = event.pageX - this.selector.offset().left; + var newTime = ((relX / this.width) * this.duration/1000).toFixed(2); + + this._Popcorn.trigger("IriSP.SparklineWidget.clicked", newTime); + this._Popcorn.currentTime(newTime); +}; + +/** react when a new annotation is added */ +IriSP.SparklineWidget.prototype.handleNewAnnotation = function(annotation) { +// var num_columns = this._results.length; +// var duration = this._serializer.getDuration(); +// var time_step = Math.round(duration / num_columns); /* the time interval between two columns */ +// var begin = +annotation.begin; +// var end = +annotation.end; +// +// /* increment all the values between the beginning and the end of the annotation */ +// var index_begin = Math.floor(begin / time_step); +// var index_end = Math.floor(end / time_step); +// +// for (var i = index_begin; i < Math.min(index_end, this._results.length); i++) { +// this._results[i]++; +// } +// +// this.selector.find(".Ldt-sparkLine").sparkline(this._results, {lineColor: "#7492b4", fillColor: "#aeaeb8", +// spotColor: "#b70056", +// width: this.width, height: this.height}); +};IriSP.StackGraphWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); +} + +IriSP.StackGraphWidget.prototype = new IriSP.Widget(); + +IriSP.StackGraphWidget.prototype.draw = function() { + var _ = IriSP._; + this.height = this._config.height || 50; + this.width = this.selector.width(); + this.slices = this._config.slices || ~~(this.width/(this.streamgraph ? 20 : 5)); + _(this.tags).each(function(_a) { + _a.regexp = new RegExp(_(_a.keywords).map(function(_k) { + return _k.replace(/([\W])/gm,'\\$1'); + }).join("|"),"im") + }); + this.paper = new Raphael(this.selector[0], this.width, this.height); + this.groups = []; + this.duration = this.getDuration(); + + var _annotationType = this._serializer.getTweets(), + _sliceDuration = ~~ ( this.duration / this.slices), + _annotations = this._serializer._data.annotations, + _groupedAnnotations = _(_.range(this.slices)).map(function(_i) { + return _(_annotations).filter(function(_a){ + return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration) + }); + }), + _max = IriSP._(_groupedAnnotations).max(function(_g) { + return _g.length + }).length, + _scale = this.height / _max, + _width = this.width / this.slices, + _showTitle = !this._config.excludeTitle, + _showDescription = !this._config.excludeDescription; + + + var _paths = _(this.tags).map(function() { + return []; + }); + _paths.push([]); + + for (var i = 0; i < this.slices; i++) { + var _group = _groupedAnnotations[i]; + if (_group) { + var _vol = _(this.tags).map(function() { + return 0; + }); + for (var j = 0; j < _group.length; j++){ + var _txt = (_showTitle ? _group[j].content.title : '') + ' ' + (_showDescription ? _group[j].content.description : '') + var _tags = _(this.tags).map(function(_tag) { + return (_txt.search(_tag.regexp) == -1 ? 0 : 1) + }), + _nbtags = _(_tags).reduce(function(_a,_b) { + return _a + _b; + }, 0); + if (_nbtags) { + IriSP._(_tags).each(function(_v, _k) { + _vol[_k] += (_v / _nbtags); + }); + } + } + var _nbtags = _(_vol).reduce(function(_a,_b) { + return _a + _b; + }, 0), + _nbneutre = _group.length - _nbtags, + _h = _nbneutre * _scale, + _base = this.height - _h; + if (!this.streamgraph) { + this.paper.rect(i * _width, _base, _width - 1, _h ).attr({ + "stroke" : "none", + "fill" : this.defaultcolor + }); + } + _paths[0].push(_base); + for (var j = 0; j < this.tags.length; j++) { + _h = _vol[j] * _scale; + _base = _base - _h; + if (!this.streamgraph) { + this.paper.rect(i * _width, _base, _width - 1, _h ).attr({ + "stroke" : "none", + "fill" : this.tags[j].color + }); + } + _paths[j+1].push(_base); + } + this.groups.push(_(_vol).map(function(_v) { + return _v / _group.length; + })) + } else { + for (var j = 0; j < _paths.length; j++) { + _paths[j].push(this.height); + } + this.groups.push(_(this.tags).map(function() { + return 0; + })); + } + } + + if (this.streamgraph) { + for (var j = _paths.length - 1; j >= 0; j--) { + var _d = _(_paths[j]).reduce(function(_memo, _v, _k) { + return _memo + ( _k + ? 'C' + (_k * _width) + ' ' + _paths[j][_k - 1] + ' ' + (_k * _width) + ' ' + _v + ' ' + ((_k + .5) * _width) + ' ' + _v + : 'M0 ' + _v + 'L' + (.5*_width) + ' ' + _v ) + },'') + 'L' + this.width + ' ' + _paths[j][_paths[j].length - 1] + 'L' + this.width + ' ' + this.height + 'L0 ' + this.height; + this.paper.path(_d).attr({ + "stroke" : "none", + "fill" : (j ? this.tags[j-1].color : this.defaultcolor) + }); + } + } + this.rectangleFocus = this.paper.rect(0,0,_width,this.height) + .attr({ + "stroke" : "none", + "fill" : "#ff00ff", + "opacity" : 0 + }) + this.rectangleProgress = this.paper.rect(0,0,0,this.height) + .attr({ + "stroke" : "none", + "fill" : "#808080", + "opacity" : .3 + }); + this.ligneProgress = this.paper.path("M0 0L0 "+this.height).attr({"stroke":"#ff00ff", "line-width" : 2}) + + this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler)); + var _this = this; + this.selector + .click(IriSP.wrap(this, this.clickHandler)) + .mousemove(function(_e) { + _this.updateTooltip(_e); + // Trace + var relX = _e.pageX - _this.selector.offset().left; + var _duration = _this.getDuration(); + var _time = parseInt((relX / _this.width) * _duration); + _this._Popcorn.trigger("IriSP.TraceWidget.MouseEvents", { + "widget" : "StackGraphWidget", + "type": "mousemove", + "x": _e.pageX, + "y": _e.pageY, + "time": _time + }); + + }) + .mouseout(function() { + _this.TooltipWidget.hide(); + _this.rectangleFocus.attr({ + "opacity" : 0 + }) + }) +} + +IriSP.StackGraphWidget.prototype.timeUpdateHandler = function() { + var _currentTime = this._Popcorn.currentTime(), + _x = (1000 * _currentTime / this.duration) * this.width; + this.rectangleProgress.attr({ + "width" : _x + }); + this.ligneProgress.attr({ + "path" : "M" + _x + " 0L" + _x + " " + this.height + }) +} + +IriSP.StackGraphWidget.prototype.clickHandler = function(event) { + /* Ctrl-C Ctrl-V'ed from another widget + */ + + var relX = event.pageX - this.selector.offset().left; + var newTime = ((relX / this.width) * this.duration/1000).toFixed(2); + this._Popcorn.trigger("IriSP.StackGraphWidget.clicked", newTime); + this._Popcorn.currentTime(newTime); +}; + +IriSP.StackGraphWidget.prototype.updateTooltip = function(event) { + var _segment = Math.max(0,Math.min(this.groups.length - 1, Math.floor(this.slices * (event.pageX - this.selector.offset().left)/this.width))), + _valeurs = this.groups[_segment], + _width = this.width / this.slices, + _html = '
    ' + IriSP._(this.tags).map(function(_tag, _i) { + return '
  • ' + + ~~(100 * _valeurs[_i]) + + '% de ' + + _tag.description + + '
  • '; + }).join('') + '
'; + this.TooltipWidget._shown = false; // Vraiment, on ne peut pas ouvrir le widget s'il n'est pas encore ouvert ? + this.TooltipWidget.show('','',(_segment + .5)* this.width / this.slices, 0); + this.TooltipWidget.selector.find(".tip").html(_html); + this.rectangleFocus.attr({ + "x" : _segment * _width, + "opacity" : .4 + }) +} + +IriSP.TagCloudWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); +} + +IriSP.TagCloudWidget.prototype = new IriSP.Widget(); + +IriSP.TagCloudWidget.prototype.draw = function() { + + var _urlRegExp = /https?:\/\/[0-9a-zA-Z\.%\/-_]+/g, + _stopWords = [ + 'aussi', 'and', 'avec', 'aux', 'bien', 'car', 'cette', 'comme', 'dans', 'donc', 'des', 'elle', 'encore', 'entre', 'est', + 'être', 'eux', 'faire', 'fait', 'http', 'ici', 'ils', 'les', 'leur', 'leurs', 'mais', 'mes', 'même', 'mon', 'notre', + 'non', 'nos', 'nous', 'ont', 'par', 'pas', 'peu', 'peut', 'plus', 'pour', 'que', 'qui', 'sans', 'ses' ,'son', 'sont', 'sur', + 'tes', 'très', 'the', 'ton', 'tous', 'tout', 'une', 'votre', 'vos', 'vous' ], + _regexpword = /[^\s\.&;,'"!\?\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g, + _words = {}, + _showTitle = !this._config.excludeTitle, + _showDescription = !this._config.excludeDescription, + _excludePattern = this._config.excludePattern || null, + _tagCount = this._config.tagCount || 30; + if (typeof this._config.excludeWords !== "undefined" && this._config.excludeWords.length) { + IriSP._(this._config.excludeWords).each(function(_w) { + _stopWords.push(_w.toLowerCase()); + }); + } + + IriSP._(this._serializer._data.annotations).each(function(_annotation) { + if (_annotation.content && _annotation.content.description) { + var _txt = (_showTitle ? _annotation.content.title : '') + ' ' + (_showDescription ? _annotation.content.description : '') + IriSP._(_txt.toLowerCase().replace(_urlRegExp, '').match(_regexpword)).each(function(_mot) { + if (_stopWords.indexOf(_mot) == -1 && (_excludePattern == null || !_excludePattern.test(_mot))) { + _words[_mot] = 1 + (_words[_mot] || 0); + } + }) + } + }); + + _words = IriSP._(_words) + .chain() + .map(function(_v, _k) { + return { + "word" : _k, + "count" : _v + } + }) + .filter(function(_v) { + return _v.count > 2; + }) + .sortBy(function(_v) { + return - _v.count; + }) + .first(_tagCount) + .value(); + var _max = _words[0].count, + _min = Math.min(_words[_words.length - 1].count, _max - 1), + _scale = 16 / Math.sqrt(_max - _min), + _this = this, + _html = '
    ' + + IriSP._(_words) + .chain() + .shuffle() + .map(function(_word) { + var _size = 10 + _scale * Math.sqrt(_word.count - _min); + return '
  • ' + + _word.word + + '
  • ' + }) + .value() + .join("") + + '
'; + this.selector + .addClass("Ldt-TagCloud") + .html(_html); + this.selector.find("li").click(function() { + var _txt = this.textContent.replace(/(^[\s]+|[\s]+$)/g,''); + _this._Popcorn.trigger("IriSP.search.triggeredSearch", _txt); + }); + this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { + var _rgxp = new RegExp("(" + searchString.replace(/(\W)/g,'\\$1') + ")","gi"); + this.selector.find("li").each(function(_i, _e) { + _e.innerHTML = searchString.length ? + _e.textContent.replace(_rgxp,'$1') + : _e.textContent; + }); + })); + this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.endsearch)); + this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.endsearch)); +} + +IriSP.TagCloudWidget.prototype.endsearch = function() { + this.selector.find("li").each(function(_i, _e) { + _e.innerHTML = _e.textContent; + }); +} +/* this widget displays a small tooltip */ +IriSP.TooltipWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + this._shown = false; + this._displayedText = ""; + this._hideTimeout = -1; +}; + + +IriSP.TooltipWidget.prototype = new IriSP.Widget(); + +IriSP.TooltipWidget.prototype.draw = function() { + var templ = Mustache.to_html(IriSP.tooltipWidget_template); + // position the widget absolutely relative to document. --- NOOOO !!!! + this.selector.css({ + "position": "absolute", + "top": 0, + "left": 0 + }); + this.selector.parent().css({ + "position": "relative" + }); + this.selector.append(templ); + var _this = this; + this.selector.mouseover(function() { + _this.hide(); + }); + this.hide(); + +}; + +IriSP.TooltipWidget.prototype.clear = function() { + this.selector.find(".tiptext").html(""); +}; + +IriSP.TooltipWidget.prototype.show = function(text, color, x, y) { + + if (this._displayedText == text && this._shown) + return; + + this.selector.find(".tipcolor").css("background-color", color); + this._displayedText = text; + this.selector.find(".tiptext").html(text); + + var _tip = this.selector.find(".tip"); + _tip.show(); + _tip.css({ + "left": Math.floor(x - _tip.outerWidth() / 2)+"px", + "top": Math.floor(y - _tip.outerHeight())+"px" + }); + this._shown = true; +}; + +IriSP.TooltipWidget.prototype.hide = function() { + this.selector.find(".tip").hide(); + this._shown = false; +};IriSP.TraceWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + this.lastEvent = ""; + var _this = this, + _listeners = { + "IriSP.createAnnotationWidget.addedAnnotation" : 0, + "IriSP.search.open" : 0, + "IriSP.search.closed" : 0, + "IriSP.search" : 0, + "IriSP.search.cleared" : 0, + "IriSP.search.matchFound" : 0, + "IriSP.search.noMatchFound" : 0, + "IriSP.search.triggeredSearch" : 0, + "IriSP.TraceWidget.MouseEvents" : 0, + "play" : 0, + "pause" : 0, + "volumechange" : 0, + "seeked" : 0, + "play" : 0, + "pause" : 0, + "timeupdate" : 2000 + }; + IriSP._(_listeners).each(function(_ms, _listener) { + var _f = function(_arg) { + _this.eventHandler(_listener, _arg); + } + if (_ms) { + _f = IriSP.underscore.throttle(_f, _ms); + } + _this._Popcorn.listen(_listener, _f); + }); + this._Popcorn.listen("timeupdate", IriSP.underscore.throttle(function(_arg) { + _this.eventHandler(_listener, _arg); + })); + + this.tracer = IriSP.TraceManager(IriSP.jQuery).init_trace("test", this._config); + this.tracer.set_default_subject("default_subject"); + this.tracer.trace("StartTracing", { "hello": "world" }); + +} + +IriSP.TraceWidget.prototype = new IriSP.Widget(); + +IriSP.TraceWidget.prototype.draw = function() { + this.mouseLocation = ''; + var _this = this; + IriSP.jQuery(".Ldt-Widget").bind("click mouseover mouseout dragstart dragstop", function(_e) { + var _widget = IriSP.jQuery(this).attr("widget-type"), + _class = _e.target.className; + var _data = { + "type": _e.type, + "x": _e.clientX, + "y": _e.clientY, + "widget": _widget + } + if (typeof _class == "string" && _class.indexOf('Ldt-TraceMe') != -1) { + var _name = _e.target.localName, + _id = _e.target.id, + _text = _e.target.textContent.trim(), + _title = _e.target.title, + _value = _e.target.value; + _data.target = _name + (_id.length ? '#' + IriSP.jqEscape(_id) : '') + (_class.length ? ('.' + IriSP.jqEscape(_class).replace(/\s/g,'.')).replace(/\.Ldt-(Widget|TraceMe)/g,'') : ''); + if (typeof _title == "string" && _title.length && _title.length < 140) { + _data.title = _title; + } + if (typeof _text == "string" && _text.length && _text.length < 140) { + _data.text = _text; + } + if (typeof _value == "string" && _value.length) { + _data.value = _value; + } + _this._Popcorn.trigger('IriSP.TraceWidget.MouseEvents', _data); + } else { + //console.log(_e.type+','+_this.mouseLocation+','+_widget); + if (_e.type == "mouseover") { + if (_this.mouseLocation != _widget) { + _this._Popcorn.trigger('IriSP.TraceWidget.MouseEvents', _data); + } else { + if (typeof _this.moTimeout != "undefined") { + clearTimeout(_this.moTimeout); + delete _this.moTimeout; + } + } + } + if (_e.type == "click") { + _this._Popcorn.trigger('IriSP.TraceWidget.MouseEvents', _data); + } + if (_e.type == "mouseout") { + if (typeof _this.moTimeout != "undefined") { + clearTimeout(_this.moTimeout); + } + _this.moTimeout = setTimeout(function() { + if (_data.widget != _this.mouseLocation) { + _this._Popcorn.trigger('IriSP.TraceWidget.MouseEvents', _data); + } + },100); + } + } + _this.mouseLocation = _widget; + }); +} + +IriSP.TraceWidget.prototype.eventHandler = function(_listener, _arg) { + var _traceName = 'Mdp_'; + if (typeof _arg == "string" || typeof _arg == "number") { + _arg = { "value" : _arg } + } + if (typeof _arg == "undefined") { + _arg = {} + } + switch(_listener) { + case 'IriSP.TraceWidget.MouseEvents': + _traceName += _arg.widget + '_' + _arg.type; + delete _arg.widget; + delete _arg.type; + break; + case 'timeupdate': + case 'play': + case 'pause': + _arg.time = this._Popcorn.currentTime() * 1000; + case 'seeked': + case 'volumechange': + _traceName += 'Popcorn_' + _listener; + break; + default: + _traceName += _listener.replace('IriSP.','').replace('.','_'); + } + this.lastEvent = _traceName; + this.tracer.trace(_traceName, _arg); + if (this._config.js_console) { + console.log("tracer.trace('" + _traceName + "', " + JSON.stringify(_arg) + ");"); + } +} +/* a widget that displays tweet - used in conjunction with the polemicWidget */ + +IriSP.TweetsWidget = function(Popcorn, config, Serializer) { + IriSP.Widget.call(this, Popcorn, config, Serializer); + + this._displayingTweet = false; + this._timeoutId = undefined; + this._hidden = false; /* hidden means that the createAnnotationWidget is shown */ +}; + + +IriSP.TweetsWidget.prototype = new IriSP.Widget(); + + +IriSP.TweetsWidget.prototype.drawTweet = function(annotation) { + if (this._hidden) + return; + + var title = IriSP.formatTweet(annotation.content.title); + var img = annotation.content.img.src; + if (typeof(img) === "undefined" || img === "" || img === "None") { + img = this.default_profile_picture; + } + + var imageMarkup = IriSP.templToHTML("user image", + {src : img}); + + if (typeof(IriSP.get_aliased(annotation.meta, ["dc:source", "source"]).content) !== "undefined") { + var tweetContents = JSON.parse(IriSP.get_aliased(annotation.meta, ["dc:source", "source"]).content); + var creator = tweetContents.user.screen_name; + var real_name = tweetContents.user.name; + + imageMarkup = IriSP.templToHTML("user image", + {src : img, creator: creator}); + + var formatted_date = new Date(tweetContents.created_at).toLocaleDateString(); + title = IriSP.templToHTML("@{{creator}} - " + + "
{{real_name}}
" + + "
{{{ contents }}}
" + + "
{{ date }}
", + {creator: creator, real_name: real_name, contents : title, date : formatted_date}); + + this.selector.find(".Ldt-TweetReply").attr("href", "http://twitter.com/home?status=@" + creator + ":%20"); + + + var rtText = Mustache.to_html("http://twitter.com/home?status=RT @{{creator}}: {{text}}", + {creator: creator, text: IriSP.encodeURI(annotation.content.title)}); + this.selector.find(".Ldt-Retweet").attr("href", rtText); + } + + this.selector.find(".Ldt-tweetContents").html(title); + this.selector.find(".Ldt-tweetAvatar").html(imageMarkup); + this.selector.show("blind", 250); +}; + +IriSP.TweetsWidget.prototype.displayTweet = function(annotation) { + if (this._displayingTweet === false) { + this._displayingTweet = true; + } else { + window.clearTimeout(this._timeoutId); + } + + this.drawTweet(annotation); + + var time = this._Popcorn.currentTime(); + this._timeoutId = window.setTimeout(IriSP.wrap(this, this.clearPanel), this.tweet_display_period); +}; + + +IriSP.TweetsWidget.prototype.clearPanel = function() { + this._displayingTweet = false; + this._timeoutId = undefined; + this.closePanel(); + +}; + +IriSP.TweetsWidget.prototype.closePanel = function() { + if (this._timeoutId != undefined) { + /* we're called from the "close window" link */ + /* cancel the timeout */ + window.clearTimeout(this._timeoutId); + this._timeoutId = null; + } + + this.selector.hide("blind", 400); + +}; + +/* cancel the timeout if the user clicks on the keep panel open button */ +IriSP.TweetsWidget.prototype.keepPanel = function() { + if (this._timeoutId != undefined) { + /* we're called from the "close window" link */ + /* cancel the timeout */ + window.clearTimeout(this._timeoutId); + this._timeoutId = null; + } +}; + +IriSP.TweetsWidget.prototype.draw = function() { + var _this = this; + + var tweetMarkup = IriSP.templToHTML(IriSP.tweetWidget_template, {"share_template" : IriSP.share_template}); + this.selector.append(tweetMarkup); + this.selector.hide(); + this.selector.find(".Ldt-tweetWidgetMinimize").click(IriSP.wrap(this, this.closePanel)); + this.selector.find(".Ldt-tweetWidgetKeepOpen").click(IriSP.wrap(this, this.keepPanel)); + + this._Popcorn.listen("IriSP.PolemicTweet.click", IriSP.wrap(this, this.PolemicTweetClickHandler)); + this._Popcorn.listen("IriSP.PlayerWidget.AnnotateButton.clicked", + IriSP.wrap(this, this.handleAnnotateSignal)); +}; + +IriSP.TweetsWidget.prototype.PolemicTweetClickHandler = function(tweet_id) { + var index, annotation; + for (index in this._serializer._data.annotations) { + annotation = this._serializer._data.annotations[index]; + + if (annotation.id === tweet_id) + break; + } + + if (annotation.id !== tweet_id) + /* we haven't found it */ + return; + + this.displayTweet(annotation); + return; +}; + +/** handle clicks on the annotate button by hiding/showing itself */ +IriSP.TweetsWidget.prototype.handleAnnotateSignal = function() { + if (this._hidden == false) { + this.selector.hide(); + this._hidden = true; + } else { + if (this._displayingTweet !== false) + this.selector.show(); + + + this._hidden = false; + } +};/** @class This class implement a serializer for the JSON-Cinelab format + @params DataLoader a dataloader reference + @url the url from which to get our cinelab + */ +IriSP.JSONSerializer = function(DataLoader, url) { + IriSP.Serializer.call(this, DataLoader, url); +}; + +IriSP.JSONSerializer.prototype = new IriSP.Serializer(); + +/** serialize data */ +IriSP.JSONSerializer.prototype.serialize = function(data) { + return JSON.stringify(data); +}; + +/** deserialize data */ +IriSP.JSONSerializer.prototype.deserialize = function(data) { + return JSON.parse(data); +}; + +/** load JSON-cinelab data and also sort the annotations by start time + @param callback function to call when the data is ready. + */ +IriSP.JSONSerializer.prototype.sync = function(callback, force_refresh) { + /* we don't have to do much because jQuery handles json for us */ + + var self = this; + + var fn = function(data) { + //TODO: seems taht data can be null here + if (data !== null) { + self._data = data; + if (typeof(self._data["annotations"]) === "undefined" || + self._data["annotations"] === null) + self._data["annotations"] = []; + + // sort the data too + self._data["annotations"].sort(function(a, b) + { var a_begin = +a.begin; + var b_begin = +b.begin; + return a_begin - b_begin; + }); + } + callback(data); + }; + this._DataLoader.get(this._url, fn, force_refresh); +}; + +/** @return the metadata about the media being read FIXME: always return the first media. */ +IriSP.JSONSerializer.prototype.currentMedia = function() { + return (typeof this._data.medias == "object" && this._data.medias.length) ? this._data.medias[0] : IriSP.__jsonMetadata.medias[0]; +}; + +IriSP.JSONSerializer.prototype.getDuration = function() { + var _m = this.currentMedia(); + if (_m === null || typeof _m.meta == "undefined") { + return 0; + } + return +(IriSP.get_aliased(_m.meta, ["dc:duration", "duration"]) || 0); +} + + +/** searches for an annotation which matches title, description and keyword + "" matches any field. + Note: it ignores tweets. + @return a list of matching ids. +*/ +IriSP.JSONSerializer.prototype.searchAnnotations = function(title, description, keyword) { + /* we can have many types of annotations. We want search to only look for regular segments */ + /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either + null or undefined. + */ + var view; + + if (typeof(this._data.views) !== "undefined" && this._data.views !== null) + view = this._data.views[0]; + + var searchViewType = ""; + + if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { + searchViewType = view.annotation_types[0]; + } + + var filterfn = function(annotation) { + if( searchViewType != "" && + typeof(annotation.meta) !== "undefined" && + typeof(annotation.meta["id-ref"]) !== "undefined" && + annotation.meta["id-ref"] !== searchViewType) { + return true; // don't pass + } else { + return false; + } + }; + + return this.searchAnnotationsFilter(title, description, keyword, filterfn); + +}; + +/* only look for tweets */ +IriSP.JSONSerializer.prototype.searchTweets = function(title, description, keyword) { + /* we can have many types of annotations. We want search to only look for regular segments */ + /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either + null or undefined. + */ + + var searchViewType = this.getTweets(); + if (typeof(searchViewType) === "undefined") { + var view; + + if (typeof(this._data.views) !== "undefined" && this._data.views !== null) + view = this._data.views[0]; + + if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) { + searchViewType = view.annotation_types[0]; + } + } + var filterfn = function(annotation) { + if( searchViewType != "" && + typeof(annotation.meta) !== "undefined" && + typeof(annotation.meta["id-ref"]) !== "undefined" && + annotation.meta["id-ref"] === searchViewType) { + return false; // pass + } else { + return true; + } + }; + + return this.searchAnnotationsFilter(title, description, keyword, filterfn); + +}; + +/** + search an annotation according to its title, description and keyword + @param filter a function to filter the results with. Used to select between annotation types. + */ +IriSP.JSONSerializer.prototype.searchAnnotationsFilter = function(title, description, keyword, filter) { + + var rTitle; + var rDescription; + var rKeyword; + /* match anything if given the empty string */ + if (title == "") + title = ".*"; + if (description == "") + description = ".*"; + if (keyword == "") + keyword = ".*"; + + rTitle = new RegExp(title, "i"); + rDescription = new RegExp(description, "i"); + rKeyword = new RegExp(keyword, "i"); + + var ret_array = []; + + var i; + for (i in this._data.annotations) { + var annotation = this._data.annotations[i]; + + /* filter the annotations whose type is not the one we want */ + if (filter(annotation)) { + continue; + } + + if (rTitle.test(annotation.content.title) && + rDescription.test(annotation.content.description)) { + /* FIXME : implement keyword support */ + ret_array.push(annotation); + } + } + + return ret_array; +}; + +/** breaks a string in words and searches each of these words. Returns an array + of objects with the id of the annotation and its number of occurences. + + @param searchString a string of words. + FIXME: optimize ? seems to be n^2 in the worst case. +*/ +IriSP.JSONSerializer.prototype.searchOccurences = function(searchString) { + var ret = { }; + var keywords = searchString.split(/\s+/); + + for (var i in keywords) { + var keyword = keywords[i]; + + // search this keyword in descriptions and title + var found_annotations = [] + found_annotations = found_annotations.concat(this.searchAnnotations(keyword, "", "")); + found_annotations = found_annotations.concat(this.searchAnnotations("", keyword, "")); + + for (var j in found_annotations) { + var current_annotation = found_annotations[j]; + + if (!ret.hasOwnProperty(current_annotation.id)) { + ret[current_annotation.id] = 1; + } else { + ret[current_annotation.id] += 1; + } + + } + + }; + + return ret; +}; + +/** breaks a string in words and searches each of these words. Returns an array + of objects with the id of the annotation and its number of occurences. + + FIXME: optimize ? seems to be n^2 in the worst case. +*/ +IriSP.JSONSerializer.prototype.searchTweetsOccurences = function(searchString) { + var ret = { }; + var keywords = searchString.split(/\s+/); + + for (var i in keywords) { + var keyword = keywords[i]; + + // search this keyword in descriptions and title + var found_annotations = [] + found_annotations = found_annotations.concat(this.searchTweets(keyword, "", "")); + found_annotations = found_annotations.concat(this.searchTweets("", keyword, "")); + + for (var j in found_annotations) { + var current_annotation = found_annotations[j]; + + if (!ret.hasOwnProperty(current_annotation.id)) { + ret[current_annotation.id] = 1; + } else { + ret[current_annotation.id] += 1; + } + + } + + }; + + return ret; +}; + +/** returns all the annotations that are displayable at the moment + NB: only takes account the first type of annotations - ignores tweets + currentTime is in seconds. + + @param currentTime the time at which we search. + @param (optional) the if of the type of the annotations we want to get. + */ + +IriSP.JSONSerializer.prototype.currentAnnotations = function(currentTime, id) { + var view; + var currentTimeMs = 1000 * currentTime; + + if (typeof(id) === "undefined") { + var legal_ids = this.getNonTweetIds(); + } else { + legal_ids = [id]; + } + + var ret_array = []; + + var i; + + for (i in this._data.annotations) { + var annotation = this._data.annotations[i]; + + if (IriSP.underscore.include(legal_ids, annotation.meta["id-ref"]) && + annotation.begin <= currentTimeMs && + annotation.end >= currentTimeMs) + ret_array.push(annotation); + } + + if (ret_array == []) { + console.log("ret_array empty, ", legal_ids); + } + + return ret_array; +}; + +/** return the current chapitre + @param currentTime the current time, in seconds. +*/ +IriSP.JSONSerializer.prototype.currentChapitre = function(currentTime) { + return this.currentAnnotations(currentTime, this.getChapitrage())[0]; +}; + +/** returns a list of ids of tweet lines (aka: groups in cinelab) */ +IriSP.JSONSerializer.prototype.getTweetIds = function() { + if (IriSP.null_or_undefined(this._data.lists) || IriSP.null_or_undefined(this._data.lists) || + IriSP.null_or_undefined(this._data.views) || IriSP.null_or_undefined(this._data.views[0])) + return []; + + + /* Get the displayable types + We've got to jump through a few hoops because the json sometimes defines + fields with underscores and sometimes with dashes + */ + var annotation_types = IriSP.get_aliased(this._data.views[0], ["annotation_types", "annotation-types"]); + if (annotation_types === null) { + console.log("neither view.annotation_types nor view.annotation-types are defined"); + return; + } + + var available_types = IriSP.get_aliased(this._data, ["annotation_types", "annotation-types"]); + if (available_types === null) { + console.log("neither view.annotation_types nor view.annotation-types are defined"); + return; + } + + var potential_types = []; + + // Get the list of types which contain "Tw" in their content + for (var i = 0; i < available_types.length; i++) { + if (/Tw/i.test(IriSP.get_aliased(available_types[i], ['dc:title', 'title']))) { + potential_types.push(available_types[i].id); + } + } + + // Get the intersection of both. + var tweetsId = IriSP.underscore.intersection(annotation_types, potential_types); + + return tweetsId; +}; + +/** this function returns a list of lines which are not tweet lines */ +IriSP.JSONSerializer.prototype.getNonTweetIds = function() { + if (IriSP.null_or_undefined(this._data.lists) || IriSP.null_or_undefined(this._data.lists) || + IriSP.null_or_undefined(this._data.views) || IriSP.null_or_undefined(this._data.views[0])) + return []; + + /* Get the displayable types + We've got to jump through a few hoops because the json sometimes defines + fields with underscores and sometimes with dashes + */ + var annotation_types = IriSP.get_aliased(this._data.views[0], ["annotation_types", "annotation-types"]); + if (annotation_types === null) { + console.log("neither view.annotation_types nor view.annotation-types are defined"); + return; + } + + var available_types = IriSP.get_aliased(this._data, ["annotation_types", "annotation-types"]); + if (available_types === null) { + console.log("neither view.annotation_types nor view.annotation-types are defined"); + return; + } + + var potential_types = []; + + // Get the list of types which do not contain "Tw" in their content + for (var i = 0; i < available_types.length; i++) { + if (!(/Tw/i.test(IriSP.get_aliased(available_types[i], ['dc:title', 'title'])))) { + potential_types.push(available_types[i].id); + } + } + + // Get the intersection of both. + var nonTweetsId = IriSP.underscore.intersection(annotation_types, potential_types); + + return nonTweetsId; + +}; + +/** return the id of the ligne de temps which contains name + @param name of the ligne de temps +*/ +IriSP.JSONSerializer.prototype.getId = function(name) { + var available_types = IriSP.get_aliased(this._data, ["annotation_types", "annotation-types"]); + + if (available_types == null) + return; + + name = name.toUpperCase(); + var e; + e = IriSP.underscore.find(available_types, + function(entry) { + if (IriSP.get_aliased(entry, ['dc:title', 'title']) === null) + return false; + return (entry["dc:title"].toUpperCase().indexOf(name) !== -1); + }); + + if (typeof(e) === "undefined") + return; + + var id = e.id; + + return id; +}; + +/** return the list of id's of the ligne de temps which contains name + @param name of the ligne de temps +*/ +IriSP.JSONSerializer.prototype.getIds = function(name) { + var available_types = IriSP.get_aliased(this._data, ["annotation_types", "annotation-types"]); + + if (available_types == null) + return; + + name = name.toUpperCase(); + var e = []; + e = IriSP.underscore.filter(available_types, + function(entry) { return (IriSP.get_aliased(entry, ['dc:title', 'title']).toUpperCase().indexOf(name) !== -1) }); + return IriSP.underscore.pluck(e, "id"); +}; + +/** return the id of the ligne de temps named "Chapitrage" */ +IriSP.JSONSerializer.prototype.getChapitrage = function() { + var val = this.getId("Chapitrage"); + if (typeof(val) === "undefined") + val = this.getId("Chapter"); + if (typeof(val) === "undefined") + val = this.getId("Chapit"); + if (typeof(val) === "undefined") + val = this.getId("Chap"); + + return val; +}; + +/** return the id of the ligne de temps named "Tweets" */ +IriSP.JSONSerializer.prototype.getTweets = function() { + var val = this.getId("Tweets"); + if (typeof(val) === "undefined") + val = this.getId("Tweet"); + if (typeof(val) === "undefined") + val = this.getId("Twitter"); + if (typeof(val) === "undefined") + val = this.getId("twit"); + if (typeof(val) === "undefined") + val = this.getId("Polemic"); + + return val; +}; + +/** return the id of the ligne de temps named "Contributions" */ +IriSP.JSONSerializer.prototype.getContributions = function() { + var val = this.getId("Contribution"); + if (typeof(val) === "undefined") + val = this.getId("Particip"); + if (typeof(val) === "undefined") + val = this.getId("Contr"); + if (typeof(val) === "undefined") + val = this.getId("Publ"); + + return val; +}; + +/** return the id of the ligne de temps named "Slideshare" */ +IriSP.JSONSerializer.prototype.getSlideShareType = function() { + var val = this.getId("slideshare"); + if (typeof(val) === "undefined") + val = this.getId("Slides"); + if (typeof(val) === "undefined") + val = this.getId("Slide"); + if (typeof(val) === "undefined") + val = this.getId("slide-Share"); + if (typeof(val) === "undefined") + val = this.getId("slide Share"); + + return val; +}; \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/LdtPlayer.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/LdtPlayer.min.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,264 @@ +/* + * + * Copyright 2010-2012 Institut de recherche et d'innovation + * contributor(s) : Karim Hamidou, Samuel Huron + * + * contact@iri.centrepompidou.fr + * http://www.iri.centrepompidou.fr + * + * This software is a computer program whose purpose is to show and add annotations on a video . + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. +*/ +(function(a){function b(x,F){var I=/^\w+\:\/\//;if(/^\/\/\/?/.test(x))x=location.protocol+x;else if(!I.test(x)&&x.charAt(0)!="/")x=(F||"")+x;return I.test(x)?x:(x.charAt(0)=="/"?L:D)+x}function c(x,F){for(var I in x)if(x.hasOwnProperty(I))F[I]=x[I];return F}function g(x,F,I,N){x.onload=x.onreadystatechange=function(){if(!(x.readyState&&x.readyState!="complete"&&x.readyState!="loaded"||F[I])){x.onload=x.onreadystatechange=null;N()}}}function h(x){x.ready=x.finished=true;for(var F=0;F0){for(var M=0;M=0;){r=d.shift();l=l[r.type].apply(null,r.args)}return l},noConflict:function(){a.$LAB=u;return j},sandbox:function(){return p()}}}var u=a.$LAB,z="UseLocalXHR",n="AlwaysPreserveOrder",s="AllowDuplicates",q="CacheBust",m="Debug",w="BasePath",D=/^[^?#]*\//.exec(location.href)[0],L=/^\w+\:\/\/\/?[^\/]+/.exec(D)[0],J=document.head||document.getElementsByTagName("head"),H=a.opera&&Object.prototype.toString.call(a.opera)== +"[object Opera]"||"MozAppearance"in document.documentElement.style,G=function(){},y=G,C=document.createElement("script"),v=typeof C.preload=="boolean",f=v||C.readyState&&C.readyState=="uninitialized",B=!f&&C.async===true,P=!f&&!B&&!H;if(a.console&&a.console.log){if(!a.console.error)a.console.error=a.console.log;G=function(x){a.console.log(x)};y=function(x,F){a.console.error(x,F)}}a.$LAB=p();(function(x,F,I){if(document.readyState==null&&document[x]){document.readyState="loading";document[x](F,I=function(){document.removeEventListener(F, +I,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); +var Mustache=function(){function a(n){return String(n).replace(/&(?!\w+;)|[<>"']/g,function(s){return p[s]||s})}var b=Object.prototype.toString;Array.isArray=Array.isArray||function(n){return b.call(n)=="[object Array]"};var c=String.prototype.trim,g;if(c)g=function(n){return n==null?"":c.call(n)};else{var h,k;if(/\S/.test("\u00a0")){h=/^[\s\xA0]+/;k=/[\s\xA0]+$/}else{h=/^\s+/;k=/\s+$/}g=function(n){return n==null?"":n.toString().replace(h,"").replace(k,"")}}var p={"&":"&","<":"<",">":">", +'"':""","'":"'"},u={},z=function(){};z.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":true},context:{},render:function(n,s,q,m){if(!m){this.context=s;this.buffer=[]}if(!this.includes("",n))if(m)return n;else{this.send(n);return}n=this.render_pragmas(n);var w=this.render_section(n,s,q);if(w===false)w=this.render_tags(n,s,q,m);if(m)return w;else this.sendLines(w)},send:function(n){n!==""&&this.buffer.push(n)},sendLines:function(n){if(n){n=n.split("\n"); +for(var s=0;s|&|\\{|%)?([^#\\^]+?)\\1?"+y+"+","g")})},L=D(),J=function(G,y,C){switch(y){case "!":return"";case "=":w.set_delimiters(C);L=D();return"";case ">":return w.render_partial(C,s,q);case "{":case "&":return w.find(C,s);default:return a(w.find(C,s))}};n=n.split("\n");for(var H=0;H0;){m=w;w=w[q.shift()]}if(typeof w=="function")return w.apply(m);return w},includes:function(n,s){return s.indexOf(this.otag+n)!=-1},create_context:function(n){if(this.is_object(n))return n;else{var s=".";if(this.pragmas["IMPLICIT-ITERATOR"])s=this.pragmas["IMPLICIT-ITERATOR"].iterator;var q={};q[s]=n;return q}},is_object:function(n){return n&&typeof n=="object"},map:function(n,s){if(typeof n.map=="function")return n.map(s);else{for(var q=[],m=n.length,w=0;w2;d==null&&(d=[]);if(w&&d.reduce===w)return l&&(e=f.bind(e,l)),o?d.reduce(e,j):d.reduce(e);B(d,function(r,A,E){o?j=e.call(l,j,r,A,E):(j=r,o=true)});if(!o)throw new TypeError("Reduce of empty array with no initial value");return j};f.reduceRight=f.foldr=function(d,e,j,l){var o=arguments.length>2;d==null&&(d=[]);if(D&&d.reduceRight===D)return l&&(e=f.bind(e,l)),o?d.reduceRight(e,j):d.reduceRight(e);var r=f.toArray(d).reverse();l&&!o&&(e=f.bind(e,l));return o?f.reduce(r, +e,j,l):f.reduce(r,e)};f.find=f.detect=function(d,e,j){var l;P(d,function(o,r,A){if(e.call(j,o,r,A))return l=o,true});return l};f.filter=f.select=function(d,e,j){var l=[];if(d==null)return l;if(L&&d.filter===L)return d.filter(e,j);B(d,function(o,r,A){e.call(j,o,r,A)&&(l[l.length]=o)});return l};f.reject=function(d,e,j){var l=[];if(d==null)return l;B(d,function(o,r,A){e.call(j,o,r,A)||(l[l.length]=o)});return l};f.every=f.all=function(d,e,j){var l=true;if(d==null)return l;if(J&&d.every===J)return d.every(e, +j);B(d,function(o,r,A){if(!(l=l&&e.call(j,o,r,A)))return g});return l};var P=f.some=f.any=function(d,e,j){e||(e=f.identity);var l=false;if(d==null)return l;if(H&&d.some===H)return d.some(e,j);B(d,function(o,r,A){if(l||(l=e.call(j,o,r,A)))return g});return!!l};f.include=f.contains=function(d,e){var j=false;if(d==null)return j;return G&&d.indexOf===G?d.indexOf(e)!=-1:j=P(d,function(l){return l===e})};f.invoke=function(d,e){var j=p.call(arguments,2);return f.map(d,function(l){return(e.call?e||l:l[e]).apply(l, +j)})};f.pluck=function(d,e){return f.map(d,function(j){return j[e]})};f.max=function(d,e,j){if(!e&&f.isArray(d))return Math.max.apply(Math,d);if(!e&&f.isEmpty(d))return-Infinity;var l={computed:-Infinity};B(d,function(o,r,A){r=e?e.call(j,o,r,A):o;r>=l.computed&&(l={value:o,computed:r})});return l.value};f.min=function(d,e,j){if(!e&&f.isArray(d))return Math.min.apply(Math,d);if(!e&&f.isEmpty(d))return Infinity;var l={computed:Infinity};B(d,function(o,r,A){r=e?e.call(j,o,r,A):o;rA?1:0}),"value")};f.groupBy=function(d,e){var j={},l=f.isFunction(e)?e:function(o){return o[e]};B(d,function(o,r){var A=l(o,r);(j[A]||(j[A]=[])).push(o)});return j};f.sortedIndex= +function(d,e,j){j||(j=f.identity);for(var l=0,o=d.length;l>1;j(d[r])=0})})};f.difference=function(d){var e=f.flatten(p.call(arguments,1));return f.filter(d,function(j){return!f.include(e,j)})};f.zip=function(){for(var d=p.call(arguments),e=f.max(f.pluck(d,"length")),j=Array(e),l=0;l=0;j--)e=[d[j].apply(this,e)];return e[0]}};f.after=function(d, +e){return d<=0?e():function(){if(--d<1)return e.apply(this,arguments)}};f.keys=C||function(d){if(d!==Object(d))throw new TypeError("Invalid object");var e=[],j;for(j in d)s.call(d,j)&&(e[e.length]=j);return e};f.values=function(d){return f.map(d,f.identity)};f.functions=f.methods=function(d){var e=[],j;for(j in d)f.isFunction(d[j])&&e.push(j);return e.sort()};f.extend=function(d){B(p.call(arguments,1),function(e){for(var j in e)e[j]!==void 0&&(d[j]=e[j])});return d};f.defaults=function(d){B(p.call(arguments, +1),function(e){for(var j in e)d[j]==null&&(d[j]=e[j])});return d};f.clone=function(d){return!f.isObject(d)?d:f.isArray(d)?d.slice():f.extend({},d)};f.tap=function(d,e){e(d);return d};f.isEqual=function(d,e){return a(d,e,[])};f.isEmpty=function(d){if(f.isArray(d)||f.isString(d))return d.length===0;for(var e in d)if(s.call(d,e))return false;return true};f.isElement=function(d){return!!(d&&d.nodeType==1)};f.isArray=k||function(d){return n.call(d)=="[object Array]"};f.isObject=function(d){return d=== +Object(d)};f.isArguments=function(d){return n.call(d)=="[object Arguments]"};if(!f.isArguments(arguments))f.isArguments=function(d){return!(!d||!s.call(d,"callee"))};f.isFunction=function(d){return n.call(d)=="[object Function]"};f.isString=function(d){return n.call(d)=="[object String]"};f.isNumber=function(d){return n.call(d)=="[object Number]"};f.isNaN=function(d){return d!==d};f.isBoolean=function(d){return d===true||d===false||n.call(d)=="[object Boolean]"};f.isDate=function(d){return n.call(d)== +"[object Date]"};f.isRegExp=function(d){return n.call(d)=="[object RegExp]"};f.isNull=function(d){return d===null};f.isUndefined=function(d){return d===void 0};f.noConflict=function(){b._=c;return this};f.identity=function(d){return d};f.times=function(d,e,j){for(var l=0;l/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};f.mixin=function(d){B(f.functions(d),function(e){V(e, +f[e]=d[e])})};var F=0;f.uniqueId=function(d){var e=F++;return d?d+e:e};f.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};f.template=function(d,e){var j=f.templateSettings;j="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+d.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(j.escape,function(o,r){return"',_.escape("+r.replace(/\\'/g,"'")+"),'"}).replace(j.interpolate,function(o,r){return"',"+r.replace(/\\'/g, +"'")+",'"}).replace(j.evaluate||null,function(o,r){return"');"+r.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";var l=new Function("obj","_",j);return e?l(e,f):function(o){return l.call(this,o,f)}};var I=function(d){this._wrapped=d};f.prototype=I.prototype;var N=function(d,e){return e?f(d).chain():d},V=function(d,e){I.prototype[d]=function(){var j=p.call(arguments);z.call(j,this._wrapped);return N(e.apply(f, +j),this._chain)}};f.mixin(f);B("pop,push,reverse,shift,sort,splice,unshift".split(","),function(d){var e=h[d];I.prototype[d]=function(){e.apply(this._wrapped,arguments);return N(this._wrapped,this._chain)}});B(["concat","join","slice"],function(d){var e=h[d];I.prototype[d]=function(){return N(e.apply(this._wrapped,arguments),this._chain)}});I.prototype.chain=function(){this._chain=true;return this};I.prototype.value=function(){return this._wrapped}}).call(this); +if(window.IriSP===undefined&&window.__IriSP===undefined)__IriSP=IriSP={};IriSP._=window._.noConflict();IriSP.underscore=IriSP._; +IriSP.loadLibs=function(a,b,c,g){IriSP.jQuery=null;var h=$LAB.script(a.jQuery).script(a.swfObject).wait().script(a.jQueryUI);if(b.player.type==="jwplayer")h=h.script(a.jwplayer);else{h=h.script(a.popcorn).script(a["popcorn.code"]);if(b.player.type==="youtube")h=h.script(a["popcorn.youtube"]);if(b.player.type==="vimeo")h=h.script(a["popcorn.vimeo"])}for(var k in b.gui.widgets){if(b.gui.widgets[k].type==="PolemicWidget"||b.gui.widgets[k].type==="StackGraphWidget")h.script(a.raphael);b.gui.widgets[k].type=== +"SparklineWidget"&&h.script(a.jquery_sparkline)}h.wait(function(){IriSP.jQuery=window.jQuery.noConflict(true);var p=IriSP.jQuery("",{rel:"stylesheet",type:"text/css",href:a.cssjQueryUI,"class":"dynamic_css"}),u=IriSP.jQuery("",{rel:"stylesheet",type:"text/css",href:b.gui.css,"class":"dynamic_css"});p.appendTo("head");u.appendTo("head");IriSP.setupDataLoader();IriSP.__dataloader.get(c,function(z){IriSP.__jsonMetadata=z;g.call(window)})})};IriSP.SparklineWidget_template="
Loading
"; +IriSP.annotation_template="{{! template for an annotation displayed in a segmentWidget }}
";IriSP.annotationWidget_template="{{! template for the annotation widget }}
")&&c[0]);return a>4?a:!1}();return a},m.isInternetExplorer=function(){var a=m.isInternetExplorer.cached=typeof m.isInternetExplorer.cached!="undefined"?m.isInternetExplorer.cached:Boolean(m.getInternetExplorerMajorVersion());return a},m.emulated={pushState:!Boolean(a.history&&a.history.pushState&&a.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(e.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(e.userAgent)),hashChange:Boolean(!("onhashchange"in a||"onhashchange"in d)||m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8)},m.enabled=!m.emulated.pushState,m.bugs={setHash:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),safariPoll:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),ieDoubleCheck:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<7)},m.isEmptyObject=function(a){for(var b in a)return!1;return!0},m.cloneObject=function(a){var b,c;return a?(b=k.stringify(a),c=k.parse(b)):c={},c},m.getRootUrl=function(){var a=d.location.protocol+"//"+(d.location.hostname||d.location.host);if(d.location.port||!1)a+=":"+d.location.port;return a+="/",a},m.getBaseHref=function(){var a=d.getElementsByTagName("base"),b=null,c="";return a.length===1&&(b=a[0],c=b.href.replace(/[^\/]+$/,"")),c=c.replace(/\/+$/,""),c&&(c+="/"),c},m.getBaseUrl=function(){var a=m.getBaseHref()||m.getBasePageUrl()||m.getRootUrl();return a},m.getPageUrl=function(){var a=m.getState(!1,!1),b=(a||{}).url||d.location.href,c;return c=b.replace(/\/+$/,"").replace(/[^\/]+$/,function(a,b,c){return/\./.test(a)?a:a+"/"}),c},m.getBasePageUrl=function(){var a=d.location.href.replace(/[#\?].*/,"").replace(/[^\/]+$/,function(a,b,c){return/[^\/]$/.test(a)?"":a}).replace(/\/+$/,"")+"/";return a},m.getFullUrl=function(a,b){var c=a,d=a.substring(0,1);return b=typeof b=="undefined"?!0:b,/[a-z]+\:\/\//.test(a)||(d==="/"?c=m.getRootUrl()+a.replace(/^\/+/,""):d==="#"?c=m.getPageUrl().replace(/#.*/,"")+a:d==="?"?c=m.getPageUrl().replace(/[\?#].*/,"")+a:b?c=m.getBaseUrl()+a.replace(/^(\.\/)+/,""):c=m.getBasePageUrl()+a.replace(/^(\.\/)+/,"")),c.replace(/\#$/,"")},m.getShortUrl=function(a){var b=a,c=m.getBaseUrl(),d=m.getRootUrl();return m.emulated.pushState&&(b=b.replace(c,"")),b=b.replace(d,"/"),m.isTraditionalAnchor(b)&&(b="./"+b),b=b.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),b},m.store={},m.idToState=m.idToState||{},m.stateToId=m.stateToId||{},m.urlToId=m.urlToId||{},m.storedStates=m.storedStates||[],m.savedStates=m.savedStates||[],m.normalizeStore=function(){m.store.idToState=m.store.idToState||{},m.store.urlToId=m.store.urlToId||{},m.store.stateToId=m.store.stateToId||{}},m.getState=function(a,b){typeof a=="undefined"&&(a=!0),typeof b=="undefined"&&(b=!0);var c=m.getLastSavedState();return!c&&b&&(c=m.createStateObject()),a&&(c=m.cloneObject(c),c.url=c.cleanUrl||c.url),c},m.getIdByState=function(a){var b=m.extractId(a.url),c;if(!b){c=m.getStateString(a);if(typeof m.stateToId[c]!="undefined")b=m.stateToId[c];else if(typeof m.store.stateToId[c]!="undefined")b=m.store.stateToId[c];else{for(;;){b=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof m.idToState[b]=="undefined"&&typeof m.store.idToState[b]=="undefined")break}m.stateToId[c]=b,m.idToState[b]=a}}return b},m.normalizeState=function(a){var b,c;if(!a||typeof a!="object")a={};if(typeof a.normalized!="undefined")return a;if(!a.data||typeof a.data!="object")a.data={};b={},b.normalized=!0,b.title=a.title||"",b.url=m.getFullUrl(m.unescapeString(a.url||d.location.href)),b.hash=m.getShortUrl(b.url),b.data=m.cloneObject(a.data),b.id=m.getIdByState(b),b.cleanUrl=b.url.replace(/\??\&_suid.*/,""),b.url=b.cleanUrl,c=!m.isEmptyObject(b.data);if(b.title||c)b.hash=m.getShortUrl(b.url).replace(/\??\&_suid.*/,""),/\?/.test(b.hash)||(b.hash+="?"),b.hash+="&_suid="+b.id;return b.hashedUrl=m.getFullUrl(b.hash),(m.emulated.pushState||m.bugs.safariPoll)&&m.hasUrlDuplicate(b)&&(b.url=b.hashedUrl),b},m.createStateObject=function(a,b,c){var d={data:a,title:b,url:c};return d=m.normalizeState(d),d},m.getStateById=function(a){a=String(a);var c=m.idToState[a]||m.store.idToState[a]||b;return c},m.getStateString=function(a){var b,c,d;return b=m.normalizeState(a),c={data:b.data,title:a.title,url:a.url},d=k.stringify(c),d},m.getStateId=function(a){var b,c;return b=m.normalizeState(a),c=b.id,c},m.getHashByState=function(a){var b,c;return b=m.normalizeState(a),c=b.hash,c},m.extractId=function(a){var b,c,d;return c=/(.*)\&_suid=([0-9]+)$/.exec(a),d=c?c[1]||a:a,b=c?String(c[2]||""):"",b||!1},m.isTraditionalAnchor=function(a){var b=!/[\/\?\.]/.test(a);return b},m.extractState=function(a,b){var c=null,d,e;return b=b||!1,d=m.extractId(a),d&&(c=m.getStateById(d)),c||(e=m.getFullUrl(a),d=m.getIdByUrl(e)||!1,d&&(c=m.getStateById(d)),!c&&b&&!m.isTraditionalAnchor(a)&&(c=m.createStateObject(null,null,e))),c},m.getIdByUrl=function(a){var c=m.urlToId[a]||m.store.urlToId[a]||b;return c},m.getLastSavedState=function(){return m.savedStates[m.savedStates.length-1]||b},m.getLastStoredState=function(){return m.storedStates[m.storedStates.length-1]||b},m.hasUrlDuplicate=function(a){var b=!1,c;return c=m.extractState(a.url),b=c&&c.id!==a.id,b},m.storeState=function(a){return m.urlToId[a.url]=a.id,m.storedStates.push(m.cloneObject(a)),a},m.isLastSavedState=function(a){var b=!1,c,d,e;return m.savedStates.length&&(c=a.id,d=m.getLastSavedState(),e=d.id,b=c===e),b},m.saveState=function(a){return m.isLastSavedState(a)?!1:(m.savedStates.push(m.cloneObject(a)),!0)},m.getStateByIndex=function(a){var b=null;return typeof a=="undefined"?b=m.savedStates[m.savedStates.length-1]:a<0?b=m.savedStates[m.savedStates.length+a]:b=m.savedStates[a],b},m.getHash=function(){var a=m.unescapeHash(d.location.hash);return a},m.unescapeString=function(b){var c=b,d;for(;;){d=a.unescape(c);if(d===c)break;c=d}return c},m.unescapeHash=function(a){var b=m.normalizeHash(a);return b=m.unescapeString(b),b},m.normalizeHash=function(a){var b=a.replace(/[^#]*#/,"").replace(/#.*/,"");return b},m.setHash=function(a,b){var c,e,f;return b!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.setHash,args:arguments,queue:b}),!1):(c=m.escapeHash(a),m.busy(!0),e=m.extractState(a,!0),e&&!m.emulated.pushState?m.pushState(e.data,e.title,e.url,!1):d.location.hash!==c&&(m.bugs.setHash?(f=m.getPageUrl(),m.pushState(null,null,f+"#"+c,!1)):d.location.hash=c),m)},m.escapeHash=function(b){var c=m.normalizeHash(b);return c=a.escape(c),m.bugs.hashEscape||(c=c.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),c},m.getHashByUrl=function(a){var b=String(a).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return b=m.unescapeHash(b),b},m.setTitle=function(a){var b=a.title,c;b||(c=m.getStateByIndex(0),c&&c.url===a.url&&(b=c.title||m.options.initialTitle));try{d.getElementsByTagName("title")[0].innerHTML=b.replace("<","<").replace(">",">").replace(" & "," & ")}catch(e){}return d.title=b,m},m.queues=[],m.busy=function(a){typeof a!="undefined"?m.busy.flag=a:typeof m.busy.flag=="undefined"&&(m.busy.flag=!1);if(!m.busy.flag){h(m.busy.timeout);var b=function(){var a,c,d;if(m.busy.flag)return;for(a=m.queues.length-1;a>=0;--a){c=m.queues[a];if(c.length===0)continue;d=c.shift(),m.fireQueueItem(d),m.busy.timeout=g(b,m.options.busyDelay)}};m.busy.timeout=g(b,m.options.busyDelay)}return m.busy.flag},m.busy.flag=!1,m.fireQueueItem=function(a){return a.callback.apply(a.scope||m,a.args||[])},m.pushQueue=function(a){return m.queues[a.queue||0]=m.queues[a.queue||0]||[],m.queues[a.queue||0].push(a),m},m.queue=function(a,b){return typeof a=="function"&&(a={callback:a}),typeof b!="undefined"&&(a.queue=b),m.busy()?m.pushQueue(a):m.fireQueueItem(a),m},m.clearQueue=function(){return m.busy.flag=!1,m.queues=[],m},m.stateChanged=!1,m.doubleChecker=!1,m.doubleCheckComplete=function(){return m.stateChanged=!0,m.doubleCheckClear(),m},m.doubleCheckClear=function(){return m.doubleChecker&&(h(m.doubleChecker),m.doubleChecker=!1),m},m.doubleCheck=function(a){return m.stateChanged=!1,m.doubleCheckClear(),m.bugs.ieDoubleCheck&&(m.doubleChecker=g(function(){return m.doubleCheckClear(),m.stateChanged||a(),!0},m.options.doubleCheckInterval)),m},m.safariStatePoll=function(){var b=m.extractState(d.location.href),c;if(!m.isLastSavedState(b))c=b;else return;return c||(c=m.createStateObject()),m.Adapter.trigger(a,"popstate"),m},m.back=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.back,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.back(!1)}),n.go(-1),!0)},m.forward=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.forward,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.forward(!1)}),n.go(1),!0)},m.go=function(a,b){var c;if(a>0)for(c=1;c<=a;++c)m.forward(b);else{if(!(a<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(c=-1;c>=a;--c)m.back(b)}return m};if(m.emulated.pushState){var o=function(){};m.pushState=m.pushState||o,m.replaceState=m.replaceState||o}else m.onPopState=function(b,c){var e=!1,f=!1,g,h;return m.doubleCheckComplete(),g=m.getHash(),g?(h=m.extractState(g||d.location.href,!0),h?m.replaceState(h.data,h.title,h.url,!1):(m.Adapter.trigger(a,"anchorchange"),m.busy(!1)),m.expectedStateId=!1,!1):(e=m.Adapter.extractEventData("state",b,c)||!1,e?f=m.getStateById(e):m.expectedStateId?f=m.getStateById(m.expectedStateId):f=m.extractState(d.location.href),f||(f=m.createStateObject(null,null,d.location.href)),m.expectedStateId=!1,m.isLastSavedState(f)?(m.busy(!1),!1):(m.storeState(f),m.saveState(f),m.setTitle(f),m.Adapter.trigger(a,"statechange"),m.busy(!1),!0))},m.Adapter.bind(a,"popstate",m.onPopState),m.pushState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.pushState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.pushState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0},m.replaceState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.replaceState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.replaceState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0};if(f){try{m.store=k.parse(f.getItem("History.store"))||{}}catch(p){m.store={}}m.normalizeStore()}else m.store={},m.normalizeStore();m.Adapter.bind(a,"beforeunload",m.clearAllIntervals),m.Adapter.bind(a,"unload",m.clearAllIntervals),m.saveState(m.storeState(m.extractState(d.location.href,!0))),f&&(m.onUnload=function(){var a,b;try{a=k.parse(f.getItem("History.store"))||{}}catch(c){a={}}a.idToState=a.idToState||{},a.urlToId=a.urlToId||{},a.stateToId=a.stateToId||{};for(b in m.idToState){if(!m.idToState.hasOwnProperty(b))continue;a.idToState[b]=m.idToState[b]}for(b in m.urlToId){if(!m.urlToId.hasOwnProperty(b))continue;a.urlToId[b]=m.urlToId[b]}for(b in m.stateToId){if(!m.stateToId.hasOwnProperty(b))continue;a.stateToId[b]=m.stateToId[b]}m.store=a,m.normalizeStore(),f.setItem("History.store",k.stringify(a))},m.intervalList.push(i(m.onUnload,m.options.storeInterval)),m.Adapter.bind(a,"beforeunload",m.onUnload),m.Adapter.bind(a,"unload",m.onUnload));if(!m.emulated.pushState){m.bugs.safariPoll&&m.intervalList.push(i(m.safariStatePoll,m.options.safariPollInterval));if(e.vendor==="Apple Computer, Inc."||(e.appCodeName||"")==="Mozilla")m.Adapter.bind(a,"hashchange",function(){m.Adapter.trigger(a,"popstate")}),m.getHash()&&m.Adapter.onDomLoad(function(){m.Adapter.trigger(a,"hashchange")})}},m.init()}(window) \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/jquery.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/jquery.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,9789 @@ +/*! + * jQuery JavaScript Library v1.10.2 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03T13:48Z + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<10 + // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + location = window.location, + document = window.document, + docElem = document.documentElement, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.2", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.10.2 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent.attachEvent && parent !== parent.top ) { + parent.attachEvent( "onbeforeunload", function() { + setDocument(); + }); + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; + } + }); +} + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each(function() { + if ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("';o.extend(q.prototype,{_scroll:function(){var e=this.qtip.elements.overlay;e&&(e[0].style.top=o(t).scrollTop()+"px")},init:function(i){var s=i.tooltip;1>o("select, object").length&&(this.bgiframe=i.elements.bgiframe=o(qe).appendTo(s),i._bind(s,"tooltipmove",this.adjustBGIFrame,this._ns,this)),this.redrawContainer=o("
",{id:$+"-rcontainer"}).appendTo(e.body),i.elements.overlay&&i.elements.overlay.addClass("qtipmodal-ie6fix")&&(i._bind(t,["scroll","resize"],this._scroll,this._ns,this),i._bind(s,["tooltipshow"],this._scroll,this._ns,this)),this.redraw()},adjustBGIFrame:function(){var t,e,i=this.qtip.tooltip,s={height:i.outerHeight(S),width:i.outerWidth(S)},o=this.qtip.plugins.tip,n=this.qtip.elements.tip;e=parseInt(i.css("borderLeftWidth"),10)||0,e={left:-e,top:-e},o&&n&&(t="x"===o.corner.precedance?[I,L]:[k,B],e[t[1]]-=n[t[0]]()),this.bgiframe.css(e).css(s)},redraw:function(){if(1>this.qtip.rendered||this.drawing)return self;var t,e,i,s,o=this.qtip.tooltip,n=this.qtip.options.style,r=this.qtip.options.position.container;return this.qtip.drawing=1,n.height&&o.css(k,n.height),n.width?o.css(I,n.width):(o.css(I,"").appendTo(this.redrawContainer),e=o.width(),1>e%2&&(e+=1),i=o.css("maxWidth")||"",s=o.css("minWidth")||"",t=(i+s).indexOf("%")>-1?r.width()/100:0,i=(i.indexOf("%")>-1?t:1)*parseInt(i,10)||e,s=(s.indexOf("%")>-1?t:1)*parseInt(s,10)||0,e=i+s?Math.min(Math.max(e,s),i):e,o.css(I,Math.round(e)).appendTo(r)),this.drawing=0,self},destroy:function(){this.bgiframe&&this.bgiframe.remove(),this.qtip._unbind([t,this.qtip.tooltip],this._ns)}}),_e=N.ie6=function(t){return 6===BROWSER.ie?new q(t):S},_e.initialize="render",z.ie6={"^content|style$":function(){this.redraw()}}})})(window,document); \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/jquery.sparkline.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/jquery.sparkline.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,1271 @@ +/** +* +* jquery.sparkline.js +* +* v1.6 +* (c) Splunk, Inc +* Contact: Gareth Watts (gareth@splunk.com) +* http://omnipotent.net/jquery.sparkline/ +* +* Generates inline sparkline charts from data supplied either to the method +* or inline in HTML +* +* Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag +* (Firefox 2.0+, Safari, Opera, etc) +* +* License: New BSD License +* +* Copyright (c) 2010, Splunk Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* * Neither the name of Splunk Inc nor the names of its contributors may +* be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* +* Usage: +* $(selector).sparkline(values, options) +* +* If values is undefined or set to 'html' then the data values are read from the specified tag: +*

Sparkline: 1,4,6,6,8,5,3,5

+* $('.sparkline').sparkline(); +* There must be no spaces in the enclosed data set +* +* Otherwise values must be an array of numbers or null values +*

Sparkline: This text replaced if the browser is compatible

+* $('#sparkline1').sparkline([1,4,6,6,8,5,3,5]) +* $('#sparkline2').sparkline([1,4,6,null,null,5,3,5]) +* +* Values can also be specified in an HTML comment, or as a values attribute: +*

Sparkline:

+*

Sparkline:

+* $('.sparkline').sparkline(); +* +* For line charts, x values can also be specified: +*

Sparkline: 1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5

+* $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ]) +* +* By default, options should be passed in as teh second argument to the sparkline function: +* $('.sparkline').sparkline([1,2,3,4], {type: 'bar'}) +* +* Options can also be set by passing them on the tag itself. This feature is disabled by default though +* as there's a slight performance overhead: +* $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true}) +*

Sparkline: loading

+* Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix) +* +* Supported options: +* lineColor - Color of the line used for the chart +* fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart +* width - Width of the chart - Defaults to 3 times the number of values in pixels +* height - Height of the chart - Defaults to the height of the containing element +* chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied +* chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied +* chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax +* chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied +* chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied +* composite - If true then don't erase any existing chart attached to the tag, but draw +* another chart over the top - Note that width and height are ignored if an +* existing chart is detected. +* tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values' +* enableTagOptions - Whether to check tags for sparkline options +* tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark' +* +* There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default), +* 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box' +* line - Line chart. Options: +* spotColor - Set to '' to not end each line in a circular spot +* minSpotColor - If set, color of spot at minimum value +* maxSpotColor - If set, color of spot at maximum value +* spotRadius - Radius in pixels +* lineWidth - Width of line in pixels +* normalRangeMin +* normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal" +* or expected range of values +* normalRangeColor - Color to use for the above bar +* drawNormalOnTop - Draw the normal range above the chart fill color if true +* defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart +* +* bar - Bar chart. Options: +* barColor - Color of bars for postive values +* negBarColor - Color of bars for negative values +* zeroColor - Color of bars with zero values +* nullColor - Color of bars with null values - Defaults to omitting the bar entirely +* barWidth - Width of bars in pixels +* colorMap - Optional mappnig of values to colors to override the *BarColor values above +* can be an Array of values to control the color of individual bars +* barSpacing - Gap between bars in pixels +* zeroAxis - Centers the y-axis around zero if true +* +* tristate - Charts values of win (>0), lose (<0) or draw (=0) +* posBarColor - Color of win values +* negBarColor - Color of lose values +* zeroBarColor - Color of draw values +* barWidth - Width of bars in pixels +* barSpacing - Gap between bars in pixels +* colorMap - Optional mappnig of values to colors to override the *BarColor values above +* can be an Array of values to control the color of individual bars +* +* discrete - Options: +* lineHeight - Height of each line in pixels - Defaults to 30% of the graph height +* thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor +* thresholdColor +* +* bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ... +* options: +* targetColor - The color of the vertical target marker +* targetWidth - The width of the target marker in pixels +* performanceColor - The color of the performance measure horizontal bar +* rangeColors - Colors to use for each qualitative range background color +* +* pie - Pie chart. Options: +* sliceColors - An array of colors to use for pie slices +* offset - Angle in degrees to offset the first slice - Try -90 or +90 +* +* box - Box plot. Options: +* raw - Set to true to supply pre-computed plot points as values +* values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier +* When set to false you can supply any number of values and the box plot will +* be computed for you. Default is false. +* showOutliers - Set to true (default) to display outliers as circles +* outlierIRQ - Interquartile range used to determine outliers. Default 1.5 +* boxLineColor - Outline color of the box +* boxFillColor - Fill color for the box +* whiskerColor - Line color used for whiskers +* outlierLineColor - Outline color of outlier circles +* outlierFillColor - Fill color of the outlier circles +* spotRadius - Radius of outlier circles +* medianColor - Line color of the median line +* target - Draw a target cross hair at the supplied value (default undefined) +* +* +* +* Examples: +* $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false }); +* $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 }); +* $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }): +* $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' }); +* $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' }); +* $('#pie').sparkline([1,1,2], { type:'pie' }); +*/ + + +(function($) { + + /* + * Default configuration settings + */ + var defaults = { + // Settings common to most/all chart types + common: { + type : 'line', + lineColor : '#00f', + fillColor : '#cdf', + defaultPixelsPerValue : 3, + width : 'auto', + height : 'auto', + composite : false, + tagValuesAttribute: 'values', + tagOptionsPrefix: 'spark', + enableTagOptions: false + }, + // Defaults for line charts + line: { + spotColor : '#f80', + spotRadius : 1.5, + minSpotColor : '#f80', + maxSpotColor : '#f80', + lineWidth: 1, + normalRangeMin : undefined, + normalRangeMax : undefined, + normalRangeColor : '#ccc', + drawNormalOnTop: false, + chartRangeMin : undefined, + chartRangeMax : undefined, + chartRangeMinX : undefined, + chartRangeMaxX : undefined + }, + // Defaults for bar charts + bar: { + barColor : '#00f', + negBarColor : '#f44', + zeroColor: undefined, + nullColor: undefined, + zeroAxis : undefined, + barWidth : 4, + barSpacing : 1, + chartRangeMax: undefined, + chartRangeMin: undefined, + chartRangeClip: false, + colorMap : undefined + }, + // Defaults for tristate charts + tristate: { + barWidth : 4, + barSpacing : 1, + posBarColor: '#6f6', + negBarColor : '#f44', + zeroBarColor : '#999', + colorMap : {} + }, + // Defaults for discrete charts + discrete: { + lineHeight: 'auto', + thresholdColor: undefined, + thresholdValue : 0, + chartRangeMax: undefined, + chartRangeMin: undefined, + chartRangeClip: false + }, + // Defaults for bullet charts + bullet: { + targetColor : 'red', + targetWidth : 3, // width of the target bar in pixels + performanceColor : 'blue', + rangeColors : ['#D3DAFE', '#A8B6FF', '#7F94FF' ], + base : undefined // set this to a number to change the base start number + }, + // Defaults for pie charts + pie: { + sliceColors : ['#f00', '#0f0', '#00f'] + }, + // Defaults for box plots + box: { + raw: false, + boxLineColor: 'black', + boxFillColor: '#cdf', + whiskerColor: 'black', + outlierLineColor: '#333', + outlierFillColor: 'white', + medianColor: 'red', + showOutliers: true, + outlierIQR: 1.5, + spotRadius: 1.5, + target: undefined, + targetColor: '#4a2', + chartRangeMax: undefined, + chartRangeMin: undefined + } + }; + + // Provide a cross-browser interface to a few simple drawing primitives + var VCanvas_base, VCanvas_canvas, VCanvas_vml; + $.fn.simpledraw = function(width, height, use_existing) { + if (use_existing && this[0].VCanvas) { + return this[0].VCanvas; + } + if (width === undefined) { + width=$(this).innerWidth(); + } + if (height === undefined) { + height=$(this).innerHeight(); + } + if ($.browser.hasCanvas) { + return new VCanvas_canvas(width, height, this); + } else if ($.browser.msie) { + return new VCanvas_vml(width, height, this); + } else { + return false; + } + }; + + var pending = []; + + + $.fn.sparkline = function(uservalues, userOptions) { + return this.each(function() { + var options = new $.fn.sparkline.options(this, userOptions); + var render = function() { + var values, width, height; + if (uservalues==='html' || uservalues===undefined) { + var vals = this.getAttribute(options.get('tagValuesAttribute')); + if (vals===undefined || vals===null) { + vals = $(this).html(); + } + values = vals.replace(/(^\s*\s*$)|\s+/g, '').split(','); + } else { + values = uservalues; + } + + width = options.get('width')=='auto' ? values.length*options.get('defaultPixelsPerValue') : options.get('width'); + if (options.get('height') == 'auto') { + if (!options.get('composite') || !this.VCanvas) { + // must be a better way to get the line height + var tmp = document.createElement('span'); + tmp.innerHTML = 'a'; + $(this).html(tmp); + height = $(tmp).innerHeight(); + $(tmp).remove(); + } + } else { + height = options.get('height'); + } + + $.fn.sparkline[options.get('type')].call(this, values, options, width, height); + }; + // jQuery 1.3.0 completely changed the meaning of :hidden :-/ + if (($(this).html() && $(this).is(':hidden')) || ($.fn.jquery < "1.3.0" && $(this).parents().is(':hidden')) || !$(this).parents('body').length) { + pending.push([this, render]); + } else { + render.call(this); + } + }); + }; + + $.fn.sparkline.defaults = defaults; + + + $.sparkline_display_visible = function() { + for (var i=pending.length-1; i>=0; i--) { + var el = pending[i][0]; + if ($(el).is(':visible') && !$(el).parents().is(':hidden')) { + pending[i][1].call(el); + pending.splice(i, 1); + } + } + }; + + + /** + * User option handler + */ + var UNSET_OPTION = {}; + var normalizeValue = function(val) { + switch(val) { + case 'undefined': + val = undefined; + break; + case 'null': + val = null; + break; + case 'true': + val = true; + break; + case 'false': + val = false; + break; + default: + var nf = parseFloat(val); + if (val == nf) { + val = nf; + } + } + return val; + }; + $.fn.sparkline.options = function(tag, userOptions) { + var extendedOptions; + this.userOptions = userOptions = userOptions || {}; + this.tag = tag; + this.tagValCache = {}; + var defaults = $.fn.sparkline.defaults; + var base = defaults.common; + this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix); + + var tagOptionType = this.getTagSetting('type'); + if (tagOptionType === UNSET_OPTION) { + extendedOptions = defaults[userOptions.type || base.type]; + } else { + extendedOptions = defaults[tagOptionType]; + } + this.mergedOptions = $.extend({}, base, extendedOptions, userOptions); + }; + + + $.fn.sparkline.options.prototype.getTagSetting = function(key) { + var val, i, prefix = this.tagOptionsPrefix; + if (prefix === false || prefix === undefined) { + return UNSET_OPTION; + } + if (this.tagValCache.hasOwnProperty(key)) { + val = this.tagValCache.key; + } else { + val = this.tag.getAttribute(prefix + key); + if (val === undefined || val === null) { + val = UNSET_OPTION; + } else if (val.substr(0, 1) == '[') { + val = val.substr(1, val.length-2).split(','); + for(i=val.length; i--;) { + val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, '')); + } + } else if (val.substr(0, 1) == '{') { + var pairs= val.substr(1, val.length-2).split(','); + val = {}; + for(i=pairs.length; i--;) { + var keyval = pairs[i].split(':', 2); + val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, '')); + } + } else { + val = normalizeValue(val); + } + this.tagValCache.key = val; + } + return val; + }; + + $.fn.sparkline.options.prototype.get = function(key) { + var tagOption = this.getTagSetting(key); + if (tagOption !== UNSET_OPTION) { + return tagOption; + } + return this.mergedOptions[key]; + }; + + + /** + * Line charts + */ + $.fn.sparkline.line = function(values, options, width, height) { + var xvalues = [], yvalues = [], yminmax = []; + for (var i=0; imaxy) { + maxy = normalRangeMax; + } + } + if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')maxy)) { + maxy = options.get('chartRangeMax'); + } + if (options.get('chartRangeMinX')!==undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX')maxx)) { + maxx = options.get('chartRangeMaxX'); + } + var rangex = maxx-minx === 0 ? 1 : maxx-minx; + var rangey = maxy-miny === 0 ? 1 : maxy-miny; + var vl = yvalues.length-1; + + if (vl<1) { + this.innerHTML = ''; + return; + } + + var target = $(this).simpledraw(width, height, options.get('composite')); + if (target) { + var canvas_width = target.pixel_width; + var canvas_height = target.pixel_height; + var canvas_top = 0; + var canvas_left = 0; + + var spotRadius = options.get('spotRadius'); + if (spotRadius && (canvas_width < (spotRadius*4) || canvas_height < (spotRadius*4))) { + spotRadius = 0; + } + if (spotRadius) { + // adjust the canvas size as required so that spots will fit + if (options.get('minSpotColor') || (options.get('spotColor') && yvalues[vl]==miny)) { + canvas_height -= Math.ceil(spotRadius); + } + if (options.get('maxSpotColor') || (options.get('spotColor') && yvalues[vl]==maxy)) { + canvas_height -= Math.ceil(spotRadius); + canvas_top += Math.ceil(spotRadius); + } + if (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[0]==miny || yvalues[0]==maxy)) { + canvas_left += Math.ceil(spotRadius); + canvas_width -= Math.ceil(spotRadius); + } + if (options.get('spotColor') || (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[vl]==miny||yvalues[vl]==maxy))) { + canvas_width -= Math.ceil(spotRadius); + } + } + + + canvas_height--; + + var drawNormalRange = function() { + if (normalRangeMin!==undefined) { + var ytop = canvas_top+Math.round(canvas_height-(canvas_height*((normalRangeMax-miny)/rangey))); + var height = Math.round((canvas_height*(normalRangeMax-normalRangeMin))/rangey); + target.drawRect(canvas_left, ytop, canvas_width, height, undefined, options.get('normalRangeColor')); + } + }; + + if (!options.get('drawNormalOnTop')) { + drawNormalRange(); + } + + var path = []; + var paths = [path]; + var x, y, vlen=yvalues.length; + for(i=0; i maxy) { + y=maxy; + } + if (!path.length) { + // previous value was null + path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+canvas_height]); + } + path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((y-miny)/rangey)))]); + } + } + var lineshapes = []; + var fillshapes = []; + var plen=paths.length; + for(i=0; i2) { + // else we want the first value + path[0] = [ path[0][0], path[1][1] ]; + } + lineshapes.push(path); + } + + // draw the fill first, then optionally the normal range, then the line on top of that + plen = fillshapes.length; + for(i=0; imax)) { + max = options.get('chartRangeMax'); + } + var zeroAxis = options.get('zeroAxis'); + if (zeroAxis === undefined) { + zeroAxis = min<0; + } + var range = max-min === 0 ? 1 : max-min; + + var colorMapByIndex, colorMapByValue; + if ($.isArray(options.get('colorMap'))) { + colorMapByIndex = options.get('colorMap'); + colorMapByValue = null; + } else { + colorMapByIndex = null; + colorMapByValue = options.get('colorMap'); + } + + var target = $(this).simpledraw(width, height, options.get('composite')); + if (target) { + var color, + canvas_height = target.pixel_height, + yzero = min<0 && zeroAxis ? canvas_height-Math.round(canvas_height * (Math.abs(min)/range))-1 : canvas_height-1; + + for(i=values.length; i--;) { + var x = i*(options.get('barWidth')+options.get('barSpacing')), + y, + val = values[i]; + if (val===null) { + if (options.get('nullColor')) { + color = options.get('nullColor'); + val = (zeroAxis && min<0) ? 0 : min; + height = 1; + y = (zeroAxis && min<0) ? yzero : canvas_height - height; + } else { + continue; + } + } else { + if (val < min) { + val=min; + } + if (val > max) { + val=max; + } + color = (val < 0) ? options.get('negBarColor') : options.get('barColor'); + if (zeroAxis && min<0) { + height = Math.round(canvas_height*((Math.abs(val)/range)))+1; + y = (val < 0) ? yzero : yzero-height; + } else { + height = Math.round(canvas_height*((val-min)/range))+1; + y = canvas_height-height; + } + if (val===0 && options.get('zeroColor')!==undefined) { + color = options.get('zeroColor'); + } + if (colorMapByValue && colorMapByValue[val]) { + color = colorMapByValue[val]; + } else if (colorMapByIndex && colorMapByIndex.length>i) { + color = colorMapByIndex[i]; + } + if (color===null) { + continue; + } + } + target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color); + } + } else { + // Remove the tag contents if sparklines aren't supported + this.innerHTML = ''; + } + }; + + + /** + * Tristate charts + */ + $.fn.sparkline.tristate = function(values, options, width, height) { + values = $.map(values, Number); + width = (values.length * options.get('barWidth')) + ((values.length-1) * options.get('barSpacing')); + + var colorMapByIndex, colorMapByValue; + if ($.isArray(options.get('colorMap'))) { + colorMapByIndex = options.get('colorMap'); + colorMapByValue = null; + } else { + colorMapByIndex = null; + colorMapByValue = options.get('colorMap'); + } + + var target = $(this).simpledraw(width, height, options.get('composite')); + if (target) { + var canvas_height = target.pixel_height, + half_height = Math.round(canvas_height/2); + + for(var i=values.length; i--;) { + var x = i*(options.get('barWidth')+options.get('barSpacing')), + y, color; + if (values[i] < 0) { + y = half_height; + height = half_height-1; + color = options.get('negBarColor'); + } else if (values[i] > 0) { + y = 0; + height = half_height-1; + color = options.get('posBarColor'); + } else { + y = half_height-1; + height = 2; + color = options.get('zeroBarColor'); + } + if (colorMapByValue && colorMapByValue[values[i]]) { + color = colorMapByValue[values[i]]; + } else if (colorMapByIndex && colorMapByIndex.length>i) { + color = colorMapByIndex[i]; + } + if (color===null) { + continue; + } + target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color); + } + } else { + // Remove the tag contents if sparklines aren't supported + this.innerHTML = ''; + } + }; + + + /** + * Discrete charts + */ + $.fn.sparkline.discrete = function(values, options, width, height) { + values = $.map(values, Number); + width = options.get('width')=='auto' ? values.length*2 : width; + var interval = Math.floor(width / values.length); + + var target = $(this).simpledraw(width, height, options.get('composite')); + if (target) { + var canvas_height = target.pixel_height, + line_height = options.get('lineHeight') == 'auto' ? Math.round(canvas_height * 0.3) : options.get('lineHeight'), + pheight = canvas_height - line_height, + min = Math.min.apply(Math, values), + max = Math.max.apply(Math, values); + if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')max)) { + max = options.get('chartRangeMax'); + } + var range = max-min; + + for(var i=values.length; i--;) { + var val = values[i]; + if (val < min) { + val=min; + } + if (val > max) { + val=max; + } + var x = (i*interval), + ytop = Math.round(pheight-pheight*((val-min)/range)); + target.drawLine(x, ytop, x, ytop+line_height, (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor')); + } + } else { + // Remove the tag contents if sparklines aren't supported + this.innerHTML = ''; + } + + }; + + + /** + * Bullet charts + */ + $.fn.sparkline.bullet = function(values, options, width, height) { + values = $.map(values, Number); + // target, performance, range1, range2, range3 + + width = options.get('width')=='auto' ? '4.0em' : width; + + var target = $(this).simpledraw(width, height, options.get('composite')); + if (target && values.length>1) { + var canvas_width = target.pixel_width-Math.ceil(options.get('targetWidth')/2), + canvas_height = target.pixel_height, + min = Math.min.apply(Math, values), + max = Math.max.apply(Math, values); + + if (options.get('base') === undefined) { + min = min < 0 ? min : 0; + } else { + min = options.get('base'); + } + var range = max-min; + + // draw range values + for(var i=2, vlen=values.length; i1) { + var canvas_width = target.pixel_width, + canvas_height = target.pixel_height, + radius = Math.floor(Math.min(canvas_width, canvas_height)/2), + total = 0, + next = 0, + circle = 2*Math.PI; + + for(var i=values.length; i--;) { + total += values[i]; + } + + if (options.get('offset')) { + next += (2*Math.PI)*(options.get('offset')/360); + } + var vlen = values.length; + for(i=0; i 0) { // avoid divide by zero + end = next + (circle*(values[i]/total)); + } + target.drawPieSlice(radius, radius, radius, start, end, undefined, options.get('sliceColors')[i % options.get('sliceColors').length]); + next = end; + } + } + }; + + + /** + * Box plots + */ + var quartile = function(values, q) { + if (q==2) { + var vl2 = Math.floor(values.length/2); + return values.length % 2 ? values[vl2] : (values[vl2]+values[vl2+1])/2; + } else { + var vl4 = Math.floor(values.length/4); + return values.length % 2 ? (values[vl4*q]+values[vl4*q+1])/2 : values[vl4*q]; + } + }; + + $.fn.sparkline.box = function(values, options, width, height) { + values = $.map(values, Number); + width = options.get('width')=='auto' ? '4.0em' : width; + + var minvalue = options.get('chartRangeMin')===undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'), + maxvalue = options.get('chartRangeMax')===undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'), + target = $(this).simpledraw(width, height, options.get('composite')), + vlen = values.length, + lwhisker, loutlier, q1, q2, q3, rwhisker, routlier; + + if (target && values.length>1) { + var canvas_width = target.pixel_width, + canvas_height = target.pixel_height; + if (options.get('raw')) { + if (options.get('showOutliers') && values.length>5) { + loutlier=values[0]; lwhisker=values[1]; q1=values[2]; q2=values[3]; q3=values[4]; rwhisker=values[5]; routlier=values[6]; + } else { + lwhisker=values[0]; q1=values[1]; q2=values[2]; q3=values[3]; rwhisker=values[4]; + } + } else { + values.sort(function(a, b) { return a-b; }); + q1 = quartile(values, 1); + q2 = quartile(values, 2); + q3 = quartile(values, 3); + var iqr = q3-q1; + if (options.get('showOutliers')) { + lwhisker=undefined; rwhisker=undefined; + for(var i=0; i q1-(iqr*options.get('outlierIQR'))) { + lwhisker = values[i]; + } + if (values[i] < q3+(iqr*options.get('outlierIQR'))) { + rwhisker = values[i]; + } + } + loutlier = values[0]; + routlier = values[vlen-1]; + } else { + lwhisker = values[0]; + rwhisker = values[vlen-1]; + } + } + + var unitsize = canvas_width / (maxvalue-minvalue+1), + canvas_left = 0; + if (options.get('showOutliers')) { + canvas_left = Math.ceil(options.get('spotRadius')); + canvas_width -= 2*Math.ceil(options.get('spotRadius')); + unitsize = canvas_width / (maxvalue-minvalue+1); + if (loutlier < lwhisker) { + target.drawCircle((loutlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor')); + } + if (routlier > rwhisker) { + target.drawCircle((routlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor')); + } + } + + // box + target.drawRect( + Math.round((q1-minvalue)*unitsize+canvas_left), + Math.round(canvas_height*0.1), + Math.round((q3-q1)*unitsize), + Math.round(canvas_height*0.8), + options.get('boxLineColor'), + options.get('boxFillColor')); + // left whisker + target.drawLine( + Math.round((lwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/2), + Math.round((q1-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/2), + options.get('lineColor')); + target.drawLine( + Math.round((lwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/4), + Math.round((lwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height-canvas_height/4), + options.get('whiskerColor')); + // right whisker + target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/2), + Math.round((q3-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/2), + options.get('lineColor')); + target.drawLine( + Math.round((rwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height/4), + Math.round((rwhisker-minvalue)*unitsize+canvas_left), + Math.round(canvas_height-canvas_height/4), + options.get('whiskerColor')); + // median line + target.drawLine( + Math.round((q2-minvalue)*unitsize+canvas_left), + Math.round(canvas_height*0.1), + Math.round((q2-minvalue)*unitsize+canvas_left), + Math.round(canvas_height*0.9), + options.get('medianColor')); + if (options.get('target')) { + var size = Math.ceil(options.get('spotRadius')); + target.drawLine( + Math.round((options.get('target')-minvalue)*unitsize+canvas_left), + Math.round((canvas_height/2)-size), + Math.round((options.get('target')-minvalue)*unitsize+canvas_left), + Math.round((canvas_height/2)+size), + options.get('targetColor')); + target.drawLine( + Math.round((options.get('target')-minvalue)*unitsize+canvas_left-size), + Math.round(canvas_height/2), + Math.round((options.get('target')-minvalue)*unitsize+canvas_left+size), + Math.round(canvas_height/2), + options.get('targetColor')); + } + } else { + // Remove the tag contents if sparklines aren't supported + this.innerHTML = ''; + } + }; + + + // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier + // This is accessible as $(foo).simpledraw() + + if ($.browser.msie && !document.namespaces.v) { + document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML'); + } + + if ($.browser.hasCanvas === undefined) { + var t = document.createElement('canvas'); + $.browser.hasCanvas = t.getContext!==undefined; + } + + VCanvas_base = function(width, height, target) { + }; + + VCanvas_base.prototype = { + init : function(width, height, target) { + this.width = width; + this.height = height; + this.target = target; + if (target[0]) { + target=target[0]; + } + target.VCanvas = this; + }, + + drawShape : function(path, lineColor, fillColor, lineWidth) { + alert('drawShape not implemented'); + }, + + drawLine : function(x1, y1, x2, y2, lineColor, lineWidth) { + return this.drawShape([ [x1,y1], [x2,y2] ], lineColor, lineWidth); + }, + + drawCircle : function(x, y, radius, lineColor, fillColor) { + alert('drawCircle not implemented'); + }, + + drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) { + alert('drawPieSlice not implemented'); + }, + + drawRect : function(x, y, width, height, lineColor, fillColor) { + alert('drawRect not implemented'); + }, + + getElement : function() { + return this.canvas; + }, + + _insert : function(el, target) { + $(target).html(el); + } + }; + + VCanvas_canvas = function(width, height, target) { + return this.init(width, height, target); + }; + + VCanvas_canvas.prototype = $.extend(new VCanvas_base(), { + _super : VCanvas_base.prototype, + + init : function(width, height, target) { + this._super.init(width, height, target); + this.canvas = document.createElement('canvas'); + if (target[0]) { + target=target[0]; + } + target.VCanvas = this; + $(this.canvas).css({ display:'inline-block', width:width, height:height, verticalAlign:'top' }); + this._insert(this.canvas, target); + this.pixel_height = $(this.canvas).height(); + this.pixel_width = $(this.canvas).width(); + this.canvas.width = this.pixel_width; + this.canvas.height = this.pixel_height; + $(this.canvas).css({width: this.pixel_width, height: this.pixel_height}); + }, + + _getContext : function(lineColor, fillColor, lineWidth) { + var context = this.canvas.getContext('2d'); + if (lineColor !== undefined) { + context.strokeStyle = lineColor; + } + context.lineWidth = lineWidth===undefined ? 1 : lineWidth; + if (fillColor !== undefined) { + context.fillStyle = fillColor; + } + return context; + }, + + drawShape : function(path, lineColor, fillColor, lineWidth) { + var context = this._getContext(lineColor, fillColor, lineWidth); + context.beginPath(); + context.moveTo(path[0][0]+0.5, path[0][1]+0.5); + for(var i=1, plen=path.length; i'; + this.canvas.insertAdjacentHTML('beforeEnd', groupel); + this.group = $(this.canvas).children()[0]; + }, + + drawShape : function(path, lineColor, fillColor, lineWidth) { + var vpath = []; + for(var i=0, plen=path.length; i' + + ' '; + this.group.insertAdjacentHTML('beforeEnd', vel); + }, + + drawCircle : function(x, y, radius, lineColor, fillColor) { + x -= radius+1; + y -= radius+1; + var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" '; + var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" '; + var vel = ''; + this.group.insertAdjacentHTML('beforeEnd', vel); + + }, + + drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) { + if (startAngle == endAngle) { + return; // VML seems to have problem when start angle equals end angle. + } + if ((endAngle - startAngle) == (2*Math.PI)) { + startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0 + endAngle = (2*Math.PI); + } + + var startx = x + Math.round(Math.cos(startAngle) * radius); + var starty = y + Math.round(Math.sin(startAngle) * radius); + var endx = x + Math.round(Math.cos(endAngle) * radius); + var endy = y + Math.round(Math.sin(endAngle) * radius); + + // Prevent very small slices from being mistaken as a whole pie + if (startx==endx && starty==endy && (endAngle-startAngle) < Math.PI) { + return; + } + + var vpath = [ x-radius, y-radius, x+radius, y+radius, startx, starty, endx, endy ]; + var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" '; + var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" '; + var vel = '' + + ' '; + this.group.insertAdjacentHTML('beforeEnd', vel); + }, + + drawRect : function(x, y, width, height, lineColor, fillColor) { + return this.drawShape( [ [x, y], [x, y+height], [x+width, y+height], [x+width, y], [x, y] ], lineColor, fillColor); + } + }); + +})(jQuery); diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/jquery.sparkline.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/jquery.sparkline.min.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,94 @@ +/* jquery.sparkline 1.6 - http://omnipotent.net/jquery.sparkline/ +** Licensed under the New BSD License - see above site for details */ + +(function($){var defaults={common:{type:'line',lineColor:'#00f',fillColor:'#cdf',defaultPixelsPerValue:3,width:'auto',height:'auto',composite:false,tagValuesAttribute:'values',tagOptionsPrefix:'spark',enableTagOptions:false},line:{spotColor:'#f80',spotRadius:1.5,minSpotColor:'#f80',maxSpotColor:'#f80',lineWidth:1,normalRangeMin:undefined,normalRangeMax:undefined,normalRangeColor:'#ccc',drawNormalOnTop:false,chartRangeMin:undefined,chartRangeMax:undefined,chartRangeMinX:undefined,chartRangeMaxX:undefined},bar:{barColor:'#00f',negBarColor:'#f44',zeroColor:undefined,nullColor:undefined,zeroAxis:undefined,barWidth:4,barSpacing:1,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:false,colorMap:undefined},tristate:{barWidth:4,barSpacing:1,posBarColor:'#6f6',negBarColor:'#f44',zeroBarColor:'#999',colorMap:{}},discrete:{lineHeight:'auto',thresholdColor:undefined,thresholdValue:0,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:false},bullet:{targetColor:'red',targetWidth:3,performanceColor:'blue',rangeColors:['#D3DAFE','#A8B6FF','#7F94FF'],base:undefined},pie:{sliceColors:['#f00','#0f0','#00f']},box:{raw:false,boxLineColor:'black',boxFillColor:'#cdf',whiskerColor:'black',outlierLineColor:'#333',outlierFillColor:'white',medianColor:'red',showOutliers:true,outlierIQR:1.5,spotRadius:1.5,target:undefined,targetColor:'#4a2',chartRangeMax:undefined,chartRangeMin:undefined}};var VCanvas_base,VCanvas_canvas,VCanvas_vml;$.fn.simpledraw=function(width,height,use_existing){if(use_existing&&this[0].VCanvas){return this[0].VCanvas;} +if(width===undefined){width=$(this).innerWidth();} +if(height===undefined){height=$(this).innerHeight();} +if($.browser.hasCanvas){return new VCanvas_canvas(width,height,this);}else if($.browser.msie){return new VCanvas_vml(width,height,this);}else{return false;}};var pending=[];$.fn.sparkline=function(uservalues,userOptions){return this.each(function(){var options=new $.fn.sparkline.options(this,userOptions);var render=function(){var values,width,height;if(uservalues==='html'||uservalues===undefined){var vals=this.getAttribute(options.get('tagValuesAttribute'));if(vals===undefined||vals===null){vals=$(this).html();} +values=vals.replace(/(^\s*\s*$)|\s+/g,'').split(',');}else{values=uservalues;} +width=options.get('width')=='auto'?values.length*options.get('defaultPixelsPerValue'):options.get('width');if(options.get('height')=='auto'){if(!options.get('composite')||!this.VCanvas){var tmp=document.createElement('span');tmp.innerHTML='a';$(this).html(tmp);height=$(tmp).innerHeight();$(tmp).remove();}}else{height=options.get('height');} +$.fn.sparkline[options.get('type')].call(this,values,options,width,height);};if(($(this).html()&&$(this).is(':hidden'))||($.fn.jquery<"1.3.0"&&$(this).parents().is(':hidden'))||!$(this).parents('body').length){pending.push([this,render]);}else{render.call(this);}});};$.fn.sparkline.defaults=defaults;$.sparkline_display_visible=function(){for(var i=pending.length-1;i>=0;i--){var el=pending[i][0];if($(el).is(':visible')&&!$(el).parents().is(':hidden')){pending[i][1].call(el);pending.splice(i,1);}}};var UNSET_OPTION={};var normalizeValue=function(val){switch(val){case'undefined':val=undefined;break;case'null':val=null;break;case'true':val=true;break;case'false':val=false;break;default:var nf=parseFloat(val);if(val==nf){val=nf;}} +return val;};$.fn.sparkline.options=function(tag,userOptions){var extendedOptions;this.userOptions=userOptions=userOptions||{};this.tag=tag;this.tagValCache={};var defaults=$.fn.sparkline.defaults;var base=defaults.common;this.tagOptionsPrefix=userOptions.enableTagOptions&&(userOptions.tagOptionsPrefix||base.tagOptionsPrefix);var tagOptionType=this.getTagSetting('type');if(tagOptionType===UNSET_OPTION){extendedOptions=defaults[userOptions.type||base.type];}else{extendedOptions=defaults[tagOptionType];} +this.mergedOptions=$.extend({},base,extendedOptions,userOptions);};$.fn.sparkline.options.prototype.getTagSetting=function(key){var val,i,prefix=this.tagOptionsPrefix;if(prefix===false||prefix===undefined){return UNSET_OPTION;} +if(this.tagValCache.hasOwnProperty(key)){val=this.tagValCache.key;}else{val=this.tag.getAttribute(prefix+key);if(val===undefined||val===null){val=UNSET_OPTION;}else if(val.substr(0,1)=='['){val=val.substr(1,val.length-2).split(',');for(i=val.length;i--;){val[i]=normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g,''));}}else if(val.substr(0,1)=='{'){var pairs=val.substr(1,val.length-2).split(',');val={};for(i=pairs.length;i--;){var keyval=pairs[i].split(':',2);val[keyval[0].replace(/(^\s*)|(\s*$)/g,'')]=normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g,''));}}else{val=normalizeValue(val);} +this.tagValCache.key=val;} +return val;};$.fn.sparkline.options.prototype.get=function(key){var tagOption=this.getTagSetting(key);if(tagOption!==UNSET_OPTION){return tagOption;} +return this.mergedOptions[key];};$.fn.sparkline.line=function(values,options,width,height){var xvalues=[],yvalues=[],yminmax=[];for(var i=0;imaxy){maxy=normalRangeMax;}} +if(options.get('chartRangeMin')!==undefined&&(options.get('chartRangeClip')||options.get('chartRangeMin')maxy)){maxy=options.get('chartRangeMax');} +if(options.get('chartRangeMinX')!==undefined&&(options.get('chartRangeClipX')||options.get('chartRangeMinX')maxx)){maxx=options.get('chartRangeMaxX');} +var rangex=maxx-minx===0?1:maxx-minx;var rangey=maxy-miny===0?1:maxy-miny;var vl=yvalues.length-1;if(vl<1){this.innerHTML='';return;} +var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_width=target.pixel_width;var canvas_height=target.pixel_height;var canvas_top=0;var canvas_left=0;var spotRadius=options.get('spotRadius');if(spotRadius&&(canvas_width<(spotRadius*4)||canvas_height<(spotRadius*4))){spotRadius=0;} +if(spotRadius){if(options.get('minSpotColor')||(options.get('spotColor')&&yvalues[vl]==miny)){canvas_height-=Math.ceil(spotRadius);} +if(options.get('maxSpotColor')||(options.get('spotColor')&&yvalues[vl]==maxy)){canvas_height-=Math.ceil(spotRadius);canvas_top+=Math.ceil(spotRadius);} +if(options.get('minSpotColor')||options.get('maxSpotColor')&&(yvalues[0]==miny||yvalues[0]==maxy)){canvas_left+=Math.ceil(spotRadius);canvas_width-=Math.ceil(spotRadius);} +if(options.get('spotColor')||(options.get('minSpotColor')||options.get('maxSpotColor')&&(yvalues[vl]==miny||yvalues[vl]==maxy))){canvas_width-=Math.ceil(spotRadius);}} +canvas_height--;var drawNormalRange=function(){if(normalRangeMin!==undefined){var ytop=canvas_top+Math.round(canvas_height-(canvas_height*((normalRangeMax-miny)/rangey)));var height=Math.round((canvas_height*(normalRangeMax-normalRangeMin))/rangey);target.drawRect(canvas_left,ytop,canvas_width,height,undefined,options.get('normalRangeColor'));}};if(!options.get('drawNormalOnTop')){drawNormalRange();} +var path=[];var paths=[path];var x,y,vlen=yvalues.length;for(i=0;imaxy){y=maxy;} +if(!path.length){path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)),canvas_top+canvas_height]);} +path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)),canvas_top+Math.round(canvas_height-(canvas_height*((y-miny)/rangey)))]);}} +var lineshapes=[];var fillshapes=[];var plen=paths.length;for(i=0;i2){path[0]=[path[0][0],path[1][1]];} +lineshapes.push(path);} +plen=fillshapes.length;for(i=0;imax)){max=options.get('chartRangeMax');} +var zeroAxis=options.get('zeroAxis');if(zeroAxis===undefined){zeroAxis=min<0;} +var range=max-min===0?1:max-min;var colorMapByIndex,colorMapByValue;if($.isArray(options.get('colorMap'))){colorMapByIndex=options.get('colorMap');colorMapByValue=null;}else{colorMapByIndex=null;colorMapByValue=options.get('colorMap');} +var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var color,canvas_height=target.pixel_height,yzero=min<0&&zeroAxis?canvas_height-Math.round(canvas_height*(Math.abs(min)/range))-1:canvas_height-1;for(i=values.length;i--;){var x=i*(options.get('barWidth')+options.get('barSpacing')),y,val=values[i];if(val===null){if(options.get('nullColor')){color=options.get('nullColor');val=(zeroAxis&&min<0)?0:min;height=1;y=(zeroAxis&&min<0)?yzero:canvas_height-height;}else{continue;}}else{if(valmax){val=max;} +color=(val<0)?options.get('negBarColor'):options.get('barColor');if(zeroAxis&&min<0){height=Math.round(canvas_height*((Math.abs(val)/range)))+1;y=(val<0)?yzero:yzero-height;}else{height=Math.round(canvas_height*((val-min)/range))+1;y=canvas_height-height;} +if(val===0&&options.get('zeroColor')!==undefined){color=options.get('zeroColor');} +if(colorMapByValue&&colorMapByValue[val]){color=colorMapByValue[val];}else if(colorMapByIndex&&colorMapByIndex.length>i){color=colorMapByIndex[i];} +if(color===null){continue;}} +target.drawRect(x,y,options.get('barWidth')-1,height-1,color,color);}}else{this.innerHTML='';}};$.fn.sparkline.tristate=function(values,options,width,height){values=$.map(values,Number);width=(values.length*options.get('barWidth'))+((values.length-1)*options.get('barSpacing'));var colorMapByIndex,colorMapByValue;if($.isArray(options.get('colorMap'))){colorMapByIndex=options.get('colorMap');colorMapByValue=null;}else{colorMapByIndex=null;colorMapByValue=options.get('colorMap');} +var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_height=target.pixel_height,half_height=Math.round(canvas_height/2);for(var i=values.length;i--;){var x=i*(options.get('barWidth')+options.get('barSpacing')),y,color;if(values[i]<0){y=half_height;height=half_height-1;color=options.get('negBarColor');}else if(values[i]>0){y=0;height=half_height-1;color=options.get('posBarColor');}else{y=half_height-1;height=2;color=options.get('zeroBarColor');} +if(colorMapByValue&&colorMapByValue[values[i]]){color=colorMapByValue[values[i]];}else if(colorMapByIndex&&colorMapByIndex.length>i){color=colorMapByIndex[i];} +if(color===null){continue;} +target.drawRect(x,y,options.get('barWidth')-1,height-1,color,color);}}else{this.innerHTML='';}};$.fn.sparkline.discrete=function(values,options,width,height){values=$.map(values,Number);width=options.get('width')=='auto'?values.length*2:width;var interval=Math.floor(width/values.length);var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_height=target.pixel_height,line_height=options.get('lineHeight')=='auto'?Math.round(canvas_height*0.3):options.get('lineHeight'),pheight=canvas_height-line_height,min=Math.min.apply(Math,values),max=Math.max.apply(Math,values);if(options.get('chartRangeMin')!==undefined&&(options.get('chartRangeClip')||options.get('chartRangeMin')max)){max=options.get('chartRangeMax');} +var range=max-min;for(var i=values.length;i--;){var val=values[i];if(valmax){val=max;} +var x=(i*interval),ytop=Math.round(pheight-pheight*((val-min)/range));target.drawLine(x,ytop,x,ytop+line_height,(options.get('thresholdColor')&&val1){var canvas_width=target.pixel_width-Math.ceil(options.get('targetWidth')/2),canvas_height=target.pixel_height,min=Math.min.apply(Math,values),max=Math.max.apply(Math,values);if(options.get('base')===undefined){min=min<0?min:0;}else{min=options.get('base');} +var range=max-min;for(var i=2,vlen=values.length;i1){var canvas_width=target.pixel_width,canvas_height=target.pixel_height,radius=Math.floor(Math.min(canvas_width,canvas_height)/2),total=0,next=0,circle=2*Math.PI;for(var i=values.length;i--;){total+=values[i];} +if(options.get('offset')){next+=(2*Math.PI)*(options.get('offset')/360);} +var vlen=values.length;for(i=0;i0){end=next+(circle*(values[i]/total));} +target.drawPieSlice(radius,radius,radius,start,end,undefined,options.get('sliceColors')[i%options.get('sliceColors').length]);next=end;}}};var quartile=function(values,q){if(q==2){var vl2=Math.floor(values.length/2);return values.length%2?values[vl2]:(values[vl2]+values[vl2+1])/2;}else{var vl4=Math.floor(values.length/4);return values.length%2?(values[vl4*q]+values[vl4*q+1])/2:values[vl4*q];}};$.fn.sparkline.box=function(values,options,width,height){values=$.map(values,Number);width=options.get('width')=='auto'?'4.0em':width;var minvalue=options.get('chartRangeMin')===undefined?Math.min.apply(Math,values):options.get('chartRangeMin'),maxvalue=options.get('chartRangeMax')===undefined?Math.max.apply(Math,values):options.get('chartRangeMax'),target=$(this).simpledraw(width,height,options.get('composite')),vlen=values.length,lwhisker,loutlier,q1,q2,q3,rwhisker,routlier;if(target&&values.length>1){var canvas_width=target.pixel_width,canvas_height=target.pixel_height;if(options.get('raw')){if(options.get('showOutliers')&&values.length>5){loutlier=values[0];lwhisker=values[1];q1=values[2];q2=values[3];q3=values[4];rwhisker=values[5];routlier=values[6];}else{lwhisker=values[0];q1=values[1];q2=values[2];q3=values[3];rwhisker=values[4];}}else{values.sort(function(a,b){return a-b;});q1=quartile(values,1);q2=quartile(values,2);q3=quartile(values,3);var iqr=q3-q1;if(options.get('showOutliers')){lwhisker=undefined;rwhisker=undefined;for(var i=0;iq1-(iqr*options.get('outlierIQR'))){lwhisker=values[i];} +if(values[i]rwhisker){target.drawCircle((routlier-minvalue)*unitsize+canvas_left,canvas_height/2,options.get('spotRadius'),options.get('outlierLineColor'),options.get('outlierFillColor'));}} +target.drawRect(Math.round((q1-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.1),Math.round((q3-q1)*unitsize),Math.round(canvas_height*0.8),options.get('boxLineColor'),options.get('boxFillColor'));target.drawLine(Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),Math.round((q1-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),options.get('lineColor'));target.drawLine(Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/4),Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height-canvas_height/4),options.get('whiskerColor'));target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),Math.round((q3-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),options.get('lineColor'));target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/4),Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height-canvas_height/4),options.get('whiskerColor'));target.drawLine(Math.round((q2-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.1),Math.round((q2-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.9),options.get('medianColor'));if(options.get('target')){var size=Math.ceil(options.get('spotRadius'));target.drawLine(Math.round((options.get('target')-minvalue)*unitsize+canvas_left),Math.round((canvas_height/2)-size),Math.round((options.get('target')-minvalue)*unitsize+canvas_left),Math.round((canvas_height/2)+size),options.get('targetColor'));target.drawLine(Math.round((options.get('target')-minvalue)*unitsize+canvas_left-size),Math.round(canvas_height/2),Math.round((options.get('target')-minvalue)*unitsize+canvas_left+size),Math.round(canvas_height/2),options.get('targetColor'));}}else{this.innerHTML='';}};if($.browser.msie&&!document.namespaces.v){document.namespaces.add('v','urn:schemas-microsoft-com:vml','#default#VML');} +if($.browser.hasCanvas===undefined){var t=document.createElement('canvas');$.browser.hasCanvas=t.getContext!==undefined;} +VCanvas_base=function(width,height,target){};VCanvas_base.prototype={init:function(width,height,target){this.width=width;this.height=height;this.target=target;if(target[0]){target=target[0];} +target.VCanvas=this;},drawShape:function(path,lineColor,fillColor,lineWidth){alert('drawShape not implemented');},drawLine:function(x1,y1,x2,y2,lineColor,lineWidth){return this.drawShape([[x1,y1],[x2,y2]],lineColor,lineWidth);},drawCircle:function(x,y,radius,lineColor,fillColor){alert('drawCircle not implemented');},drawPieSlice:function(x,y,radius,startAngle,endAngle,lineColor,fillColor){alert('drawPieSlice not implemented');},drawRect:function(x,y,width,height,lineColor,fillColor){alert('drawRect not implemented');},getElement:function(){return this.canvas;},_insert:function(el,target){$(target).html(el);}};VCanvas_canvas=function(width,height,target){return this.init(width,height,target);};VCanvas_canvas.prototype=$.extend(new VCanvas_base(),{_super:VCanvas_base.prototype,init:function(width,height,target){this._super.init(width,height,target);this.canvas=document.createElement('canvas');if(target[0]){target=target[0];} +target.VCanvas=this;$(this.canvas).css({display:'inline-block',width:width,height:height,verticalAlign:'top'});this._insert(this.canvas,target);this.pixel_height=$(this.canvas).height();this.pixel_width=$(this.canvas).width();this.canvas.width=this.pixel_width;this.canvas.height=this.pixel_height;$(this.canvas).css({width:this.pixel_width,height:this.pixel_height});},_getContext:function(lineColor,fillColor,lineWidth){var context=this.canvas.getContext('2d');if(lineColor!==undefined){context.strokeStyle=lineColor;} +context.lineWidth=lineWidth===undefined?1:lineWidth;if(fillColor!==undefined){context.fillStyle=fillColor;} +return context;},drawShape:function(path,lineColor,fillColor,lineWidth){var context=this._getContext(lineColor,fillColor,lineWidth);context.beginPath();context.moveTo(path[0][0]+0.5,path[0][1]+0.5);for(var i=1,plen=path.length;i';this.canvas.insertAdjacentHTML('beforeEnd',groupel);this.group=$(this.canvas).children()[0];},drawShape:function(path,lineColor,fillColor,lineWidth){var vpath=[];for(var i=0,plen=path.length;i'+' ';this.group.insertAdjacentHTML('beforeEnd',vel);},drawCircle:function(x,y,radius,lineColor,fillColor){x-=radius+1;y-=radius+1;var stroke=lineColor===undefined?' stroked="false" ':' strokeWeight="1" strokeColor="'+lineColor+'" ';var fill=fillColor===undefined?' filled="false"':' fillColor="'+fillColor+'" filled="true" ';var vel='';this.group.insertAdjacentHTML('beforeEnd',vel);},drawPieSlice:function(x,y,radius,startAngle,endAngle,lineColor,fillColor){if(startAngle==endAngle){return;} +if((endAngle-startAngle)==(2*Math.PI)){startAngle=0.0;endAngle=(2*Math.PI);} +var startx=x+Math.round(Math.cos(startAngle)*radius);var starty=y+Math.round(Math.sin(startAngle)*radius);var endx=x+Math.round(Math.cos(endAngle)*radius);var endy=y+Math.round(Math.sin(endAngle)*radius);if(startx==endx&&starty==endy&&(endAngle-startAngle)'+' ';this.group.insertAdjacentHTML('beforeEnd',vel);},drawRect:function(x,y,width,height,lineColor,fillColor){return this.drawShape([[x,y],[x,y+height],[x+width,y+height],[x+width,y],[x,y]],lineColor,fillColor);}});})(jQuery); \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/jquery.textchange.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/jquery.textchange.min.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,10 @@ +/*! + * jQuery TextChange Plugin + * http://www.zurb.com/playground/jquery-text-change-custom-event + * + * Copyright 2010, ZURB + * Released under the MIT License + */ + (function(a){a.event.special.textchange={setup:function(){a(this).data("lastValue",this.contentEditable==="true"?a(this).html():a(this).val());a(this).bind("keyup.textchange",a.event.special.textchange.handler);a(this).bind("cut.textchange paste.textchange input.textchange",a.event.special.textchange.delayedHandler)},teardown:function(){a(this).unbind(".textchange")},handler:function(){a.event.special.textchange.triggerIfChanged(a(this))},delayedHandler:function(){var b=a(this);setTimeout(function(){a.event.special.textchange.triggerIfChanged(b)}, + 25)},triggerIfChanged:function(b){var c=b[0].contentEditable==="true"?b.html():b.val();if(c!==b.data("lastValue")){b.trigger("textchange",b.data("lastValue"));b.data("lastValue",c)}}};a.event.special.hastext={setup:function(){a(this).bind("textchange",a.event.special.hastext.handler)},teardown:function(){a(this).unbind("textchange",a.event.special.hastext.handler)},handler:function(b,c){c===""&&c!==a(this).val()&&a(this).trigger("hastext")}};a.event.special.notext={setup:function(){a(this).bind("textchange", + a.event.special.notext.handler)},teardown:function(){a(this).unbind("textchange",a.event.special.notext.handler)},handler:function(b,c){a(this).val()===""&&a(this).val()!==c&&a(this).trigger("notext")}}})(jQuery); \ No newline at end of file diff -r 7e83c61b1f87 -r e2a3375c5a0a server/src/remie/static/remie/js/jquery.tools.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/remie/static/remie/js/jquery.tools.min.js Fri Jul 03 17:14:46 2015 +0200 @@ -0,0 +1,116 @@ +/* + * jQuery Tools 1.2.4 - The missing UI library for the Web + * + * [toolbox.flashembed, toolbox.history, toolbox.expose, toolbox.mousewheel, tabs, tabs.slideshow, tooltip, tooltip.slide, tooltip.dynamic, scrollable, scrollable.autoscroll, scrollable.navigator, overlay, overlay.apple, dateinput, rangeinput, validator] + * + * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. + * + * http://flowplayer.org/tools/ + * + * jquery.event.wheel.js - rev 1 + * Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) + * Liscensed under the MIT License (MIT-LICENSE.txt) + * http://www.opensource.org/licenses/mit-license.php + * Created: 2008-07-01 | Updated: 2008-07-14 + * + * ----- + * + * File generated: Wed Aug 18 09:10:10 GMT 2010 + */ +(function(){function f(a,b){if(b)for(var c in b)if(b.hasOwnProperty(c))a[c]=b[c];return a}function l(a,b){var c=[];for(var d in a)if(a.hasOwnProperty(d))c[d]=b(a[d]);return c}function m(a,b,c){if(e.isSupported(b.version))a.innerHTML=e.getHTML(b,c);else if(b.expressInstall&&e.isSupported([6,65]))a.innerHTML=e.getHTML(f(b,{src:b.expressInstall}),{MMredirectURL:location.href,MMplayerType:"PlugIn",MMdoctitle:document.title});else{if(!a.innerHTML.replace(/\s/g,"")){a.innerHTML="

Flash version "+b.version+ +" or greater is required

"+(g[0]>0?"Your version is "+g:"You have no flash plugin installed")+"

"+(a.tagName=="A"?"

Click here to download latest version

":"

Download latest version from here

");if(a.tagName=="A")a.onclick=function(){location.href=k}}if(b.onFail){var d=b.onFail.call(this);if(typeof d=="string")a.innerHTML=d}}if(i)window[b.id]=document.getElementById(b.id);f(this,{getRoot:function(){return a},getOptions:function(){return b},getConf:function(){return c}, +getApi:function(){return a.firstChild}})}var i=document.all,k="http://www.adobe.com/go/getflashplayer",n=typeof jQuery=="function",o=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,j={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};window.attachEvent&&window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}}); +window.flashembed=function(a,b,c){if(typeof a=="string")a=document.getElementById(a.replace("#",""));if(a){if(typeof b=="string")b={src:b};return new m(a,f(f({},j),b),c)}};var e=f(window.flashembed,{conf:j,getVersion:function(){var a,b;try{b=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(c){try{b=(a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"))&&a.GetVariable("$version")}catch(d){try{b=(a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"))&&a.GetVariable("$version")}catch(h){}}}return(b= +o.exec(b))?[b[1],b[3]]:[0,0]},asString:function(a){if(a===null||a===undefined)return null;var b=typeof a;if(b=="object"&&a.push)b="array";switch(b){case "string":a=a.replace(new RegExp('(["\\\\])',"g"),"\\$1");a=a.replace(/^\s?(\d+\.?\d+)%/,"$1pct");return'"'+a+'"';case "array":return"["+l(a,function(d){return e.asString(d)}).join(",")+"]";case "function":return'"function()"';case "object":b=[];for(var c in a)a.hasOwnProperty(c)&&b.push('"'+c+'":'+e.asString(a[c]));return"{"+b.join(",")+"}"}return String(a).replace(/\s/g, +" ").replace(/\'/g,'"')},getHTML:function(a,b){a=f({},a);var c='';a.width=a.height=a.id=a.w3c=a.src=null;a.onFail=a.version=a.expressInstall=null;for(var d in a)if(a[d])c+= +'';a="";if(b){for(var h in b)if(b[h]){d=b[h];a+=h+"="+(/function|object/.test(typeof d)?e.asString(d):d)+"&"}a=a.slice(0,-1);c+='"}c+="";return c},isSupported:function(a){return g[0]>a[0]||g[0]==a[0]&&g[1]>=a[1]}}),g=e.getVersion();if(n){jQuery.tools=jQuery.tools||{version:"1.2.4"};jQuery.tools.flashembed={conf:j};jQuery.fn.flashembed=function(a,b){return this.each(function(){$(this).data("flashembed",flashembed(this, +a,b))})}}})(); +(function(b){function h(c){if(c){var a=d.contentWindow.document;a.open().close();a.location.hash=c}}var g,d,f,i;b.tools=b.tools||{version:"1.2.4"};b.tools.history={init:function(c){if(!i){if(b.browser.msie&&b.browser.version<"8"){if(!d){d=b("