web/LdtPlayer-release.js
changeset 0 d4dc097a6083
child 9 61d507c98678
equal deleted inserted replaced
-1:000000000000 0:d4dc097a6083
       
     1 /* 
       
     2  * 	
       
     3  *	Copyright 2010 Institut de recherche et d'innovation 
       
     4  *	contributor(s) : Samuel Huron 
       
     5  *	 
       
     6  *	contact@iri.centrepompidou.fr
       
     7  *	http://www.iri.centrepompidou.fr 
       
     8  *	 
       
     9  *	This software is a computer program whose purpose is to show and add annotations on a video .
       
    10  *	This software is governed by the CeCILL-C license under French law and
       
    11  *	abiding by the rules of distribution of free software. You can  use, 
       
    12  *	modify and/ or redistribute the software under the terms of the CeCILL-C
       
    13  *	license as circulated by CEA, CNRS and INRIA at the following URL
       
    14  *	"http://www.cecill.info". 
       
    15  *	
       
    16  *	The fact that you are presently reading this means that you have had
       
    17  *	knowledge of the CeCILL-C license and that you accept its terms.
       
    18 */
       
    19 (function(global, document) {
       
    20 
       
    21   // Popcorn.js does not support archaic browsers
       
    22   if ( !document.addEventListener ) {
       
    23     global.Popcorn = {
       
    24       isSupported: false
       
    25     };
       
    26 
       
    27     var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " +
       
    28           "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
       
    29           "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/);
       
    30 
       
    31     while ( methods.length ) {
       
    32       global.Popcorn[ methods.shift() ] = function() {};
       
    33     }
       
    34     return;
       
    35   }
       
    36 
       
    37   var
       
    38 
       
    39   AP = Array.prototype,
       
    40   OP = Object.prototype,
       
    41 
       
    42   forEach = AP.forEach,
       
    43   slice = AP.slice,
       
    44   hasOwn = OP.hasOwnProperty,
       
    45   toString = OP.toString,
       
    46 
       
    47   // Copy global Popcorn (may not exist)
       
    48   _Popcorn = global.Popcorn,
       
    49 
       
    50   //  ID string matching
       
    51   rIdExp  = /^(#([\w\-\_\.]+))$/,
       
    52 
       
    53   //  Ready fn cache
       
    54   readyStack = [],
       
    55   readyBound = false,
       
    56   readyFired = false,
       
    57 
       
    58   //  Non-public internal data object
       
    59   internal = {
       
    60     events: {
       
    61       hash: {},
       
    62       apis: {}
       
    63     }
       
    64   },
       
    65 
       
    66   //  Non-public `requestAnimFrame`
       
    67   //  http://paulirish.com/2011/requestanimationframe-for-smart-animating/
       
    68   requestAnimFrame = (function(){
       
    69     return global.requestAnimationFrame ||
       
    70       global.webkitRequestAnimationFrame ||
       
    71       global.mozRequestAnimationFrame ||
       
    72       global.oRequestAnimationFrame ||
       
    73       global.msRequestAnimationFrame ||
       
    74       function( callback, element ) {
       
    75         global.setTimeout( callback, 16 );
       
    76       };
       
    77   }()),
       
    78 
       
    79   refresh = function( obj ) {
       
    80     var currentTime = obj.media.currentTime,
       
    81       animation = obj.options.frameAnimation,
       
    82       disabled = obj.data.disabled,
       
    83       tracks = obj.data.trackEvents,
       
    84       animating = tracks.animating,
       
    85       start = tracks.startIndex,
       
    86       registryByName = Popcorn.registryByName,
       
    87       animIndex = 0,
       
    88       byStart, natives, type;
       
    89 
       
    90     start = Math.min( start + 1, tracks.byStart.length - 2 );
       
    91 
       
    92     while ( start > 0 && tracks.byStart[ start ] ) {
       
    93 
       
    94       byStart = tracks.byStart[ start ];
       
    95       natives = byStart._natives;
       
    96       type = natives && natives.type;
       
    97 
       
    98       if ( !natives ||
       
    99           ( !!registryByName[ type ] || !!obj[ type ] ) ) {
       
   100 
       
   101         if ( ( byStart.start <= currentTime && byStart.end > currentTime ) &&
       
   102                 disabled.indexOf( type ) === -1 ) {
       
   103 
       
   104           if ( !byStart._running ) {
       
   105             byStart._running = true;
       
   106             natives.start.call( obj, null, byStart );
       
   107 
       
   108             // if the 'frameAnimation' option is used,
       
   109             // push the current byStart object into the `animating` cue
       
   110             if ( animation &&
       
   111                 ( byStart && byStart._running && byStart.natives.frame ) ) {
       
   112 
       
   113               natives.frame.call( obj, null, byStart, currentTime );
       
   114             }
       
   115           }
       
   116         } else if ( byStart._running === true ) {
       
   117 
       
   118           byStart._running = false;
       
   119           natives.end.call( obj, null, byStart );
       
   120 
       
   121           if ( animation && byStart._natives.frame ) {
       
   122             animIndex = animating.indexOf( byStart );
       
   123             if ( animIndex >= 0 ) {
       
   124               animating.splice( animIndex, 1 );
       
   125             }
       
   126           }
       
   127         }
       
   128       }
       
   129 
       
   130       start--;
       
   131     }
       
   132   },
       
   133 
       
   134   //  Declare constructor
       
   135   //  Returns an instance object.
       
   136   Popcorn = function( entity, options ) {
       
   137     //  Return new Popcorn object
       
   138     return new Popcorn.p.init( entity, options || null );
       
   139   };
       
   140 
       
   141   //  Popcorn API version, automatically inserted via build system.
       
   142   Popcorn.version = "@VERSION";
       
   143 
       
   144   //  Boolean flag allowing a client to determine if Popcorn can be supported
       
   145   Popcorn.isSupported = true;
       
   146 
       
   147   //  Instance caching
       
   148   Popcorn.instances = [];
       
   149 
       
   150   //  Declare a shortcut (Popcorn.p) to and a definition of
       
   151   //  the new prototype for our Popcorn constructor
       
   152   Popcorn.p = Popcorn.prototype = {
       
   153 
       
   154     init: function( entity, options ) {
       
   155 
       
   156       var matches;
       
   157 
       
   158       //  Supports Popcorn(function () { /../ })
       
   159       //  Originally proposed by Daniel Brooks
       
   160 
       
   161       if ( typeof entity === "function" ) {
       
   162 
       
   163         //  If document ready has already fired
       
   164         if ( document.readyState === "interactive" || document.readyState === "complete" ) {
       
   165 
       
   166           entity( document, Popcorn );
       
   167 
       
   168           return;
       
   169         }
       
   170         //  Add `entity` fn to ready stack
       
   171         readyStack.push( entity );
       
   172 
       
   173         //  This process should happen once per page load
       
   174         if ( !readyBound ) {
       
   175 
       
   176           //  set readyBound flag
       
   177           readyBound = true;
       
   178 
       
   179           var DOMContentLoaded  = function() {
       
   180 
       
   181             readyFired = true;
       
   182 
       
   183             //  Remove global DOM ready listener
       
   184             document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
       
   185 
       
   186             //  Execute all ready function in the stack
       
   187             for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) {
       
   188 
       
   189               readyStack[ i ].call( document, Popcorn );
       
   190 
       
   191             }
       
   192             //  GC readyStack
       
   193             readyStack = null;
       
   194           };
       
   195 
       
   196           //  Register global DOM ready listener
       
   197           document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
       
   198         }
       
   199 
       
   200         return;
       
   201       }
       
   202 
       
   203       //  Check if entity is a valid string id
       
   204       matches = rIdExp.exec( entity );
       
   205 
       
   206       //  Get media element by id or object reference
       
   207       this.media = matches && matches.length && matches[ 2 ] ?
       
   208                      document.getElementById( matches[ 2 ] ) :
       
   209                      entity;
       
   210 
       
   211       //  Create an audio or video element property reference
       
   212       this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media;
       
   213 
       
   214       //  Register new instance
       
   215       Popcorn.instances.push( this );
       
   216 
       
   217       this.options = options || {};
       
   218 
       
   219       this.isDestroyed = false;
       
   220 
       
   221       this.data = {
       
   222 
       
   223         // Executed by either timeupdate event or in rAF loop
       
   224         timeUpdate: Popcorn.nop,
       
   225 
       
   226         // Allows disabling a plugin per instance
       
   227         disabled: [],
       
   228 
       
   229         // Stores DOM event queues by type
       
   230         events: {},
       
   231 
       
   232         // Stores Special event hooks data
       
   233         hooks: {},
       
   234 
       
   235         // Store track event history data
       
   236         history: [],
       
   237 
       
   238         // Stores ad-hoc state related data]
       
   239         state: {
       
   240           volume: this.media.volume
       
   241         },
       
   242 
       
   243         // Store track event object references by trackId
       
   244         trackRefs: {},
       
   245 
       
   246         // Playback track event queues
       
   247         trackEvents: {
       
   248           byStart: [{
       
   249 
       
   250             start: -1,
       
   251             end: -1
       
   252           }],
       
   253           byEnd: [{
       
   254             start: -1,
       
   255             end: -1
       
   256           }],
       
   257           animating: [],
       
   258           startIndex: 0,
       
   259           endIndex: 0,
       
   260           previousUpdateTime: -1
       
   261         }
       
   262       };
       
   263 
       
   264       //  Wrap true ready check
       
   265       var isReady = function( that ) {
       
   266 
       
   267         var duration, videoDurationPlus;
       
   268 
       
   269         if ( that.media.readyState >= 2 ) {
       
   270           //  Adding padding to the front and end of the arrays
       
   271           //  this is so we do not fall off either end
       
   272 
       
   273           duration = that.media.duration;
       
   274           //  Check for no duration info (NaN)
       
   275           videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
       
   276 
       
   277           Popcorn.addTrackEvent( that, {
       
   278             start: videoDurationPlus,
       
   279             end: videoDurationPlus
       
   280           });
       
   281 
       
   282           if ( that.options.frameAnimation ) {
       
   283             //  if Popcorn is created with frameAnimation option set to true,
       
   284             //  requestAnimFrame is used instead of "timeupdate" media event.
       
   285             //  This is for greater frame time accuracy, theoretically up to
       
   286             //  60 frames per second as opposed to ~4 ( ~every 15-250ms)
       
   287             that.data.timeUpdate = function () {
       
   288 
       
   289               Popcorn.timeUpdate( that, {} );
       
   290 
       
   291               that.trigger( "timeupdate" );
       
   292 
       
   293               !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
       
   294             };
       
   295 
       
   296             !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
       
   297 
       
   298           } else {
       
   299 
       
   300             that.data.timeUpdate = function( event ) {
       
   301               Popcorn.timeUpdate( that, event );
       
   302             };
       
   303 
       
   304             if ( !that.isDestroyed ) {
       
   305               that.media.addEventListener( "timeupdate", that.data.timeUpdate, false );
       
   306             }
       
   307           }
       
   308         } else {
       
   309           global.setTimeout(function() {
       
   310             isReady( that );
       
   311           }, 1 );
       
   312         }
       
   313       };
       
   314 
       
   315       isReady( this );
       
   316 
       
   317       return this;
       
   318     }
       
   319   };
       
   320 
       
   321   //  Extend constructor prototype to instance prototype
       
   322   //  Allows chaining methods to instances
       
   323   Popcorn.p.init.prototype = Popcorn.p;
       
   324 
       
   325   Popcorn.forEach = function( obj, fn, context ) {
       
   326 
       
   327     if ( !obj || !fn ) {
       
   328       return {};
       
   329     }
       
   330 
       
   331     context = context || this;
       
   332 
       
   333     var key, len;
       
   334 
       
   335     // Use native whenever possible
       
   336     if ( forEach && obj.forEach === forEach ) {
       
   337       return obj.forEach( fn, context );
       
   338     }
       
   339 
       
   340     if ( toString.call( obj ) === "[object NodeList]" ) {
       
   341       for ( key = 0, len = obj.length; key < len; key++ ) {
       
   342         fn.call( context, obj[ key ], key, obj );
       
   343       }
       
   344       return obj;
       
   345     }
       
   346 
       
   347     for ( key in obj ) {
       
   348       if ( hasOwn.call( obj, key ) ) {
       
   349         fn.call( context, obj[ key ], key, obj );
       
   350       }
       
   351     }
       
   352     return obj;
       
   353   };
       
   354 
       
   355   Popcorn.extend = function( obj ) {
       
   356     var dest = obj, src = slice.call( arguments, 1 );
       
   357 
       
   358     Popcorn.forEach( src, function( copy ) {
       
   359       for ( var prop in copy ) {
       
   360         dest[ prop ] = copy[ prop ];
       
   361       }
       
   362     });
       
   363 
       
   364     return dest;
       
   365   };
       
   366 
       
   367 
       
   368   // A Few reusable utils, memoized onto Popcorn
       
   369   Popcorn.extend( Popcorn, {
       
   370     noConflict: function( deep ) {
       
   371 
       
   372       if ( deep ) {
       
   373         global.Popcorn = _Popcorn;
       
   374       }
       
   375 
       
   376       return Popcorn;
       
   377     },
       
   378     error: function( msg ) {
       
   379       throw new Error( msg );
       
   380     },
       
   381     guid: function( prefix ) {
       
   382       Popcorn.guid.counter++;
       
   383       return  ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter );
       
   384     },
       
   385     sizeOf: function( obj ) {
       
   386       var size = 0;
       
   387 
       
   388       for ( var prop in obj ) {
       
   389         size++;
       
   390       }
       
   391 
       
   392       return size;
       
   393     },
       
   394     isArray: Array.isArray || function( array ) {
       
   395       return toString.call( array ) === "[object Array]";
       
   396     },
       
   397 
       
   398     nop: function() {},
       
   399 
       
   400     position: function( elem ) {
       
   401 
       
   402       var clientRect = elem.getBoundingClientRect(),
       
   403           bounds = {},
       
   404           doc = elem.ownerDocument,
       
   405           docElem = document.documentElement,
       
   406           body = document.body,
       
   407           clientTop, clientLeft, scrollTop, scrollLeft, top, left;
       
   408 
       
   409       //  Determine correct clientTop/Left
       
   410       clientTop = docElem.clientTop || body.clientTop || 0;
       
   411       clientLeft = docElem.clientLeft || body.clientLeft || 0;
       
   412 
       
   413       //  Determine correct scrollTop/Left
       
   414       scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
       
   415       scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
       
   416 
       
   417       //  Temp top/left
       
   418       top = Math.ceil( clientRect.top + scrollTop - clientTop );
       
   419       left = Math.ceil( clientRect.left + scrollLeft - clientLeft );
       
   420 
       
   421       for ( var p in clientRect ) {
       
   422         bounds[ p ] = Math.round( clientRect[ p ] );
       
   423       }
       
   424 
       
   425       return Popcorn.extend({}, bounds, { top: top, left: left });
       
   426     },
       
   427 
       
   428     disable: function( instance, plugin ) {
       
   429 
       
   430       var disabled = instance.data.disabled;
       
   431 
       
   432       if ( disabled.indexOf( plugin ) === -1 ) {
       
   433         disabled.push( plugin );
       
   434       }
       
   435 
       
   436       refresh( instance );
       
   437 
       
   438       return instance;
       
   439     },
       
   440     enable: function( instance, plugin ) {
       
   441 
       
   442       var disabled = instance.data.disabled,
       
   443           index = disabled.indexOf( plugin );
       
   444 
       
   445       if ( index > -1 ) {
       
   446         disabled.splice( index, 1 );
       
   447       }
       
   448 
       
   449       refresh( instance );
       
   450 
       
   451       return instance;
       
   452     },
       
   453     destroy: function( instance ) {
       
   454       var events = instance.data.events,
       
   455           singleEvent, item, fn;
       
   456 
       
   457       //  Iterate through all events and remove them
       
   458       for ( item in events ) {
       
   459         singleEvent = events[ item ];
       
   460         for ( fn in singleEvent ) {
       
   461           delete singleEvent[ fn ];
       
   462         }
       
   463         events[ item ] = null;
       
   464       }
       
   465 
       
   466       if ( !instance.isDestroyed ) {
       
   467         instance.data.timeUpdate && instance.media.removeEventListener( "timeupdate", instance.data.timeUpdate, false );
       
   468         instance.isDestroyed = true;
       
   469       }
       
   470     }
       
   471   });
       
   472 
       
   473   //  Memoized GUID Counter
       
   474   Popcorn.guid.counter = 1;
       
   475 
       
   476   //  Factory to implement getters, setters and controllers
       
   477   //  as Popcorn instance methods. The IIFE will create and return
       
   478   //  an object with defined methods
       
   479   Popcorn.extend(Popcorn.p, (function() {
       
   480 
       
   481       var methods = "load play pause currentTime playbackRate volume duration preload playbackRate " +
       
   482                     "autoplay loop controls muted buffered readyState seeking paused played seekable ended",
       
   483           ret = {};
       
   484 
       
   485 
       
   486       //  Build methods, store in object that is returned and passed to extend
       
   487       Popcorn.forEach( methods.split( /\s+/g ), function( name ) {
       
   488 
       
   489         ret[ name ] = function( arg ) {
       
   490 
       
   491           if ( typeof this.media[ name ] === "function" ) {
       
   492 
       
   493             // Support for shorthanded play(n)/pause(n) jump to currentTime
       
   494             // If arg is not null or undefined and called by one of the
       
   495             // allowed shorthandable methods, then set the currentTime
       
   496             // Supports time as seconds or SMPTE
       
   497             if ( arg != null && /play|pause/.test( name ) ) {
       
   498               this.media.currentTime = Popcorn.util.toSeconds( arg );
       
   499             }
       
   500 
       
   501             this.media[ name ]();
       
   502 
       
   503             return this;
       
   504           }
       
   505 
       
   506 
       
   507           if ( arg != null ) {
       
   508 
       
   509             this.media[ name ] = arg;
       
   510 
       
   511             return this;
       
   512           }
       
   513 
       
   514           return this.media[ name ];
       
   515         };
       
   516       });
       
   517 
       
   518       return ret;
       
   519 
       
   520     })()
       
   521   );
       
   522 
       
   523   Popcorn.forEach( "enable disable".split(" "), function( method ) {
       
   524     Popcorn.p[ method ] = function( plugin ) {
       
   525       return Popcorn[ method ]( this, plugin );
       
   526     };
       
   527   });
       
   528 
       
   529   Popcorn.extend(Popcorn.p, {
       
   530 
       
   531     //  Rounded currentTime
       
   532     roundTime: function() {
       
   533       return -~this.media.currentTime;
       
   534     },
       
   535 
       
   536     //  Attach an event to a single point in time
       
   537     exec: function( time, fn ) {
       
   538 
       
   539       //  Creating a one second track event with an empty end
       
   540       Popcorn.addTrackEvent( this, {
       
   541         start: time,
       
   542         end: time + 1,
       
   543         _running: false,
       
   544         _natives: {
       
   545           start: fn || Popcorn.nop,
       
   546           end: Popcorn.nop,
       
   547           type: "exec"
       
   548         }
       
   549       });
       
   550 
       
   551       return this;
       
   552     },
       
   553 
       
   554     // Mute the calling media, optionally toggle
       
   555     mute: function( toggle ) {
       
   556 
       
   557       var event = toggle == null || toggle === true ? "muted" : "unmuted";
       
   558 
       
   559       // If `toggle` is explicitly `false`,
       
   560       // unmute the media and restore the volume level
       
   561       if ( event === "unmuted" ) {
       
   562         this.media.muted = false;
       
   563         this.media.volume = this.data.state.volume;
       
   564       }
       
   565 
       
   566       // If `toggle` is either null or undefined,
       
   567       // save the current volume and mute the media element
       
   568       if ( event === "muted" ) {
       
   569         this.data.state.volume = this.media.volume;
       
   570         this.media.muted = true;
       
   571       }
       
   572 
       
   573       // Trigger either muted|unmuted event
       
   574       this.trigger( event );
       
   575 
       
   576       return this;
       
   577     },
       
   578 
       
   579     // Convenience method, unmute the calling media
       
   580     unmute: function( toggle ) {
       
   581 
       
   582       return this.mute( toggle == null ? false : !toggle );
       
   583     },
       
   584 
       
   585     // Get the client bounding box of an instance element
       
   586     position: function() {
       
   587       return Popcorn.position( this.media );
       
   588     },
       
   589 
       
   590     // Toggle a plugin's playback behaviour (on or off) per instance
       
   591     toggle: function( plugin ) {
       
   592       return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin );
       
   593     },
       
   594 
       
   595     // Set default values for plugin options objects per instance
       
   596     defaults: function( plugin, defaults ) {
       
   597 
       
   598       // If an array of default configurations is provided,
       
   599       // iterate and apply each to this instance
       
   600       if ( Popcorn.isArray( plugin ) ) {
       
   601 
       
   602         Popcorn.forEach( plugin, function( obj ) {
       
   603           for ( var name in obj ) {
       
   604             this.defaults( name, obj[ name ] );
       
   605           }
       
   606         }, this );
       
   607 
       
   608         return this;
       
   609       }
       
   610 
       
   611       if ( !this.options.defaults ) {
       
   612         this.options.defaults = {};
       
   613       }
       
   614 
       
   615       if ( !this.options.defaults[ plugin ] ) {
       
   616         this.options.defaults[ plugin ] = {};
       
   617       }
       
   618 
       
   619       Popcorn.extend( this.options.defaults[ plugin ], defaults );
       
   620 
       
   621       return this;
       
   622     }
       
   623   });
       
   624 
       
   625   Popcorn.Events  = {
       
   626     UIEvents: "blur focus focusin focusout load resize scroll unload",
       
   627     MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick",
       
   628     Events: "loadstart progress suspend emptied stalled play pause " +
       
   629             "loadedmetadata loadeddata waiting playing canplay canplaythrough " +
       
   630             "seeking seeked timeupdate ended ratechange durationchange volumechange"
       
   631   };
       
   632 
       
   633   Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " +
       
   634                            Popcorn.Events.MouseEvents + " " +
       
   635                            Popcorn.Events.Events;
       
   636 
       
   637   internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ];
       
   638 
       
   639   // Privately compile events table at load time
       
   640   (function( events, data ) {
       
   641 
       
   642     var apis = internal.events.apiTypes,
       
   643     eventsList = events.Natives.split( /\s+/g ),
       
   644     idx = 0, len = eventsList.length, prop;
       
   645 
       
   646     for( ; idx < len; idx++ ) {
       
   647       data.hash[ eventsList[idx] ] = true;
       
   648     }
       
   649 
       
   650     apis.forEach(function( val, idx ) {
       
   651 
       
   652       data.apis[ val ] = {};
       
   653 
       
   654       var apiEvents = events[ val ].split( /\s+/g ),
       
   655       len = apiEvents.length,
       
   656       k = 0;
       
   657 
       
   658       for ( ; k < len; k++ ) {
       
   659         data.apis[ val ][ apiEvents[ k ] ] = true;
       
   660       }
       
   661     });
       
   662   })( Popcorn.Events, internal.events );
       
   663 
       
   664   Popcorn.events = {
       
   665 
       
   666     isNative: function( type ) {
       
   667       return !!internal.events.hash[ type ];
       
   668     },
       
   669     getInterface: function( type ) {
       
   670 
       
   671       if ( !Popcorn.events.isNative( type ) ) {
       
   672         return false;
       
   673       }
       
   674 
       
   675       var eventApi = internal.events,
       
   676         apis = eventApi.apiTypes,
       
   677         apihash = eventApi.apis,
       
   678         idx = 0, len = apis.length, api, tmp;
       
   679 
       
   680       for ( ; idx < len; idx++ ) {
       
   681         tmp = apis[ idx ];
       
   682 
       
   683         if ( apihash[ tmp ][ type ] ) {
       
   684           api = tmp;
       
   685           break;
       
   686         }
       
   687       }
       
   688       return api;
       
   689     },
       
   690     //  Compile all native events to single array
       
   691     all: Popcorn.Events.Natives.split( /\s+/g ),
       
   692     //  Defines all Event handling static functions
       
   693     fn: {
       
   694       trigger: function( type, data ) {
       
   695 
       
   696         var eventInterface, evt;
       
   697         //  setup checks for custom event system
       
   698         if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) {
       
   699 
       
   700           eventInterface  = Popcorn.events.getInterface( type );
       
   701 
       
   702           if ( eventInterface ) {
       
   703 
       
   704             evt = document.createEvent( eventInterface );
       
   705             evt.initEvent( type, true, true, global, 1 );
       
   706 
       
   707             this.media.dispatchEvent( evt );
       
   708 
       
   709             return this;
       
   710           }
       
   711 
       
   712           //  Custom events
       
   713           Popcorn.forEach( this.data.events[ type ], function( obj, key ) {
       
   714 
       
   715             obj.call( this, data );
       
   716 
       
   717           }, this );
       
   718 
       
   719         }
       
   720 
       
   721         return this;
       
   722       },
       
   723       listen: function( type, fn ) {
       
   724 
       
   725         var self = this,
       
   726             hasEvents = true,
       
   727             eventHook = Popcorn.events.hooks[ type ],
       
   728             origType = type,
       
   729             tmp;
       
   730 
       
   731         if ( !this.data.events[ type ] ) {
       
   732           this.data.events[ type ] = {};
       
   733           hasEvents = false;
       
   734         }
       
   735 
       
   736         // Check and setup event hooks
       
   737         if ( eventHook ) {
       
   738 
       
   739           // Execute hook add method if defined
       
   740           if ( eventHook.add ) {
       
   741             eventHook.add.call( this, {}, fn );
       
   742           }
       
   743 
       
   744           // Reassign event type to our piggyback event type if defined
       
   745           if ( eventHook.bind ) {
       
   746             type = eventHook.bind;
       
   747           }
       
   748 
       
   749           // Reassign handler if defined
       
   750           if ( eventHook.handler ) {
       
   751             tmp = fn;
       
   752 
       
   753             fn = function wrapper( event ) {
       
   754               eventHook.handler.call( self, event, tmp );
       
   755             };
       
   756           }
       
   757 
       
   758           // assume the piggy back event is registered
       
   759           hasEvents = true;
       
   760 
       
   761           // Setup event registry entry
       
   762           if ( !this.data.events[ type ] ) {
       
   763             this.data.events[ type ] = {};
       
   764             // Toggle if the previous assumption was untrue
       
   765             hasEvents = false;
       
   766           }
       
   767         }
       
   768 
       
   769         //  Register event and handler
       
   770         this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn;
       
   771 
       
   772         // only attach one event of any type
       
   773         if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
       
   774 
       
   775           this.media.addEventListener( type, function( event ) {
       
   776 
       
   777             Popcorn.forEach( self.data.events[ type ], function( obj, key ) {
       
   778               if ( typeof obj === "function" ) {
       
   779                 obj.call( self, event );
       
   780               }
       
   781             });
       
   782 
       
   783           }, false);
       
   784         }
       
   785         return this;
       
   786       },
       
   787       unlisten: function( type, fn ) {
       
   788 
       
   789         if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) {
       
   790 
       
   791           delete this.data.events[ type ][ fn ];
       
   792 
       
   793           return this;
       
   794         }
       
   795 
       
   796         this.data.events[ type ] = null;
       
   797 
       
   798         return this;
       
   799       }
       
   800     },
       
   801     hooks: {
       
   802       canplayall: {
       
   803         bind: "canplaythrough",
       
   804         add: function( event, callback ) {
       
   805 
       
   806           var state = false;
       
   807 
       
   808           if ( this.media.readyState ) {
       
   809 
       
   810             callback.call( this, event );
       
   811 
       
   812             state = true;
       
   813           }
       
   814 
       
   815           this.data.hooks.canplayall = {
       
   816             fired: state
       
   817           };
       
   818         },
       
   819         // declare special handling instructions
       
   820         handler: function canplayall( event, callback ) {
       
   821 
       
   822           if ( !this.data.hooks.canplayall.fired ) {
       
   823             // trigger original user callback once
       
   824             callback.call( this, event );
       
   825 
       
   826             this.data.hooks.canplayall.fired = true;
       
   827           }
       
   828         }
       
   829       }
       
   830     }
       
   831   };
       
   832 
       
   833   //  Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
       
   834   Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) {
       
   835     Popcorn.p[ key ] = Popcorn.events.fn[ key ];
       
   836   });
       
   837 
       
   838   // Internal Only - Adds track events to the instance object
       
   839   Popcorn.addTrackEvent = function( obj, track ) {
       
   840 
       
   841     // Determine if this track has default options set for it
       
   842     // If so, apply them to the track object
       
   843     if ( track && track._natives && track._natives.type &&
       
   844         ( obj.options.defaults && obj.options.defaults[ track._natives.type ] ) ) {
       
   845 
       
   846       track = Popcorn.extend( {}, obj.options.defaults[ track._natives.type ], track );
       
   847     }
       
   848 
       
   849     if ( track._natives ) {
       
   850       //  Supports user defined track event id
       
   851       track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id;
       
   852 
       
   853       //  Push track event ids into the history
       
   854       obj.data.history.push( track._id );
       
   855     }
       
   856 
       
   857     track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
       
   858     track.end   = Popcorn.util.toSeconds( track.end, obj.options.framerate );
       
   859 
       
   860     //  Store this definition in an array sorted by times
       
   861     var byStart = obj.data.trackEvents.byStart,
       
   862         byEnd = obj.data.trackEvents.byEnd,
       
   863         startIndex, endIndex,
       
   864         currentTime;
       
   865 
       
   866     for ( startIndex = byStart.length - 1; startIndex >= 0; startIndex-- ) {
       
   867 
       
   868       if ( track.start >= byStart[ startIndex ].start ) {
       
   869         byStart.splice( startIndex + 1, 0, track );
       
   870         break;
       
   871       }
       
   872     }
       
   873 
       
   874     for ( endIndex = byEnd.length - 1; endIndex >= 0; endIndex-- ) {
       
   875 
       
   876       if ( track.end > byEnd[ endIndex ].end ) {
       
   877         byEnd.splice( endIndex + 1, 0, track );
       
   878         break;
       
   879       }
       
   880     }
       
   881 
       
   882     // Display track event immediately if it's enabled and current
       
   883     if ( track._natives &&
       
   884         ( !!Popcorn.registryByName[ track._natives.type ] || !!obj[ track._natives.type ] ) ) {
       
   885 
       
   886       currentTime = obj.media.currentTime;
       
   887       if ( track.end > currentTime &&
       
   888         track.start <= currentTime &&
       
   889         obj.data.disabled.indexOf( track._natives.type ) === -1 ) {
       
   890 
       
   891         track._running = true;
       
   892         track._natives.start.call( obj, null, track );
       
   893 
       
   894         if ( obj.options.frameAnimation &&
       
   895           track._natives.frame ) {
       
   896 
       
   897           obj.data.trackEvents.animating.push( track );
       
   898           track._natives.frame.call( obj, null, track, currentTime );
       
   899         }
       
   900       }
       
   901     }
       
   902 
       
   903     // update startIndex and endIndex
       
   904     if ( startIndex <= obj.data.trackEvents.startIndex &&
       
   905       track.start <= obj.data.trackEvents.previousUpdateTime ) {
       
   906 
       
   907       obj.data.trackEvents.startIndex++;
       
   908     }
       
   909 
       
   910     if ( endIndex <= obj.data.trackEvents.endIndex &&
       
   911       track.end < obj.data.trackEvents.previousUpdateTime ) {
       
   912 
       
   913       obj.data.trackEvents.endIndex++;
       
   914     }
       
   915 
       
   916     this.timeUpdate( obj, null, true );
       
   917 
       
   918     // Store references to user added trackevents in ref table
       
   919     if ( track._id ) {
       
   920       Popcorn.addTrackEvent.ref( obj, track );
       
   921     }
       
   922   };
       
   923 
       
   924   // Internal Only - Adds track event references to the instance object's trackRefs hash table
       
   925   Popcorn.addTrackEvent.ref = function( obj, track ) {
       
   926     obj.data.trackRefs[ track._id ] = track;
       
   927 
       
   928     return obj;
       
   929   };
       
   930 
       
   931   Popcorn.removeTrackEvent  = function( obj, trackId ) {
       
   932 
       
   933     var historyLen = obj.data.history.length,
       
   934         indexWasAt = 0,
       
   935         byStart = [],
       
   936         byEnd = [],
       
   937         animating = [],
       
   938         history = [];
       
   939 
       
   940     Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) {
       
   941       // Preserve the original start/end trackEvents
       
   942       if ( !o._id ) {
       
   943         byStart.push( obj.data.trackEvents.byStart[i] );
       
   944         byEnd.push( obj.data.trackEvents.byEnd[i] );
       
   945       }
       
   946 
       
   947       // Filter for user track events (vs system track events)
       
   948       if ( o._id ) {
       
   949 
       
   950         // Filter for the trackevent to remove
       
   951         if ( o._id !== trackId ) {
       
   952           byStart.push( obj.data.trackEvents.byStart[i] );
       
   953           byEnd.push( obj.data.trackEvents.byEnd[i] );
       
   954         }
       
   955 
       
   956         //  Capture the position of the track being removed.
       
   957         if ( o._id === trackId ) {
       
   958           indexWasAt = i;
       
   959           o._natives._teardown && o._natives._teardown.call( obj, o );
       
   960         }
       
   961       }
       
   962 
       
   963     });
       
   964 
       
   965     if ( obj.data.trackEvents.animating.length ) {
       
   966       Popcorn.forEach( obj.data.trackEvents.animating, function( o, i, context ) {
       
   967         // Preserve the original start/end trackEvents
       
   968         if ( !o._id ) {
       
   969           animating.push( obj.data.trackEvents.animating[i] );
       
   970         }
       
   971 
       
   972         // Filter for user track events (vs system track events)
       
   973         if ( o._id ) {
       
   974           // Filter for the trackevent to remove
       
   975           if ( o._id !== trackId ) {
       
   976             animating.push( obj.data.trackEvents.animating[i] );
       
   977           }
       
   978         }
       
   979       });
       
   980     }
       
   981 
       
   982     //  Update
       
   983     if ( indexWasAt <= obj.data.trackEvents.startIndex ) {
       
   984       obj.data.trackEvents.startIndex--;
       
   985     }
       
   986 
       
   987     if ( indexWasAt <= obj.data.trackEvents.endIndex ) {
       
   988       obj.data.trackEvents.endIndex--;
       
   989     }
       
   990 
       
   991     obj.data.trackEvents.byStart = byStart;
       
   992     obj.data.trackEvents.byEnd = byEnd;
       
   993     obj.data.trackEvents.animating = animating;
       
   994 
       
   995     for ( var i = 0; i < historyLen; i++ ) {
       
   996       if ( obj.data.history[ i ] !== trackId ) {
       
   997         history.push( obj.data.history[ i ] );
       
   998       }
       
   999     }
       
  1000 
       
  1001     // Update ordered history array
       
  1002     obj.data.history = history;
       
  1003 
       
  1004     // Update track event references
       
  1005     Popcorn.removeTrackEvent.ref( obj, trackId );
       
  1006   };
       
  1007 
       
  1008   // Internal Only - Removes track event references from instance object's trackRefs hash table
       
  1009   Popcorn.removeTrackEvent.ref = function( obj, trackId ) {
       
  1010     delete obj.data.trackRefs[ trackId ];
       
  1011 
       
  1012     return obj;
       
  1013   };
       
  1014 
       
  1015   // Return an array of track events bound to this instance object
       
  1016   Popcorn.getTrackEvents = function( obj ) {
       
  1017 
       
  1018     var trackevents = [],
       
  1019       refs = obj.data.trackEvents.byStart,
       
  1020       length = refs.length,
       
  1021       idx = 0,
       
  1022       ref;
       
  1023 
       
  1024     for ( ; idx < length; idx++ ) {
       
  1025       ref = refs[ idx ];
       
  1026       // Return only user attributed track event references
       
  1027       if ( ref._id ) {
       
  1028         trackevents.push( ref );
       
  1029       }
       
  1030     }
       
  1031 
       
  1032     return trackevents;
       
  1033   };
       
  1034 
       
  1035   // Internal Only - Returns an instance object's trackRefs hash table
       
  1036   Popcorn.getTrackEvents.ref = function( obj ) {
       
  1037     return obj.data.trackRefs;
       
  1038   };
       
  1039 
       
  1040   // Return a single track event bound to this instance object
       
  1041   Popcorn.getTrackEvent = function( obj, trackId ) {
       
  1042     return obj.data.trackRefs[ trackId ];
       
  1043   };
       
  1044 
       
  1045   // Internal Only - Returns an instance object's track reference by track id
       
  1046   Popcorn.getTrackEvent.ref = function( obj, trackId ) {
       
  1047     return obj.data.trackRefs[ trackId ];
       
  1048   };
       
  1049 
       
  1050   Popcorn.getLastTrackEventId = function( obj ) {
       
  1051     return obj.data.history[ obj.data.history.length - 1 ];
       
  1052   };
       
  1053 
       
  1054   Popcorn.timeUpdate = function( obj, event ) {
       
  1055 
       
  1056     var currentTime = obj.media.currentTime,
       
  1057         previousTime = obj.data.trackEvents.previousUpdateTime,
       
  1058         tracks = obj.data.trackEvents,
       
  1059         animating = tracks.animating,
       
  1060         end = tracks.endIndex,
       
  1061         start = tracks.startIndex,
       
  1062         animIndex = 0,
       
  1063 
       
  1064         registryByName = Popcorn.registryByName,
       
  1065 
       
  1066         byEnd, byStart, byAnimate, natives, type;
       
  1067 
       
  1068     //  Playbar advancing
       
  1069     if ( previousTime <= currentTime ) {
       
  1070 
       
  1071       while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end <= currentTime ) {
       
  1072 
       
  1073         byEnd = tracks.byEnd[ end ];
       
  1074         natives = byEnd._natives;
       
  1075         type = natives && natives.type;
       
  1076 
       
  1077         //  If plugin does not exist on this instance, remove it
       
  1078         if ( !natives ||
       
  1079             ( !!registryByName[ type ] ||
       
  1080               !!obj[ type ] ) ) {
       
  1081 
       
  1082           if ( byEnd._running === true ) {
       
  1083             byEnd._running = false;
       
  1084             natives.end.call( obj, event, byEnd );
       
  1085           }
       
  1086 
       
  1087           end++;
       
  1088         } else {
       
  1089           // remove track event
       
  1090           Popcorn.removeTrackEvent( obj, byEnd._id );
       
  1091           return;
       
  1092         }
       
  1093       }
       
  1094 
       
  1095       while ( tracks.byStart[ start ] && tracks.byStart[ start ].start <= currentTime ) {
       
  1096 
       
  1097         byStart = tracks.byStart[ start ];
       
  1098         natives = byStart._natives;
       
  1099         type = natives && natives.type;
       
  1100 
       
  1101         //  If plugin does not exist on this instance, remove it
       
  1102         if ( !natives ||
       
  1103             ( !!registryByName[ type ] ||
       
  1104               !!obj[ type ] ) ) {
       
  1105 
       
  1106           if ( byStart.end > currentTime &&
       
  1107                 byStart._running === false &&
       
  1108                   obj.data.disabled.indexOf( type ) === -1 ) {
       
  1109 
       
  1110             byStart._running = true;
       
  1111             natives.start.call( obj, event, byStart );
       
  1112 
       
  1113             // If the `frameAnimation` option is used,
       
  1114             // push the current byStart object into the `animating` cue
       
  1115             if ( obj.options.frameAnimation &&
       
  1116                 ( byStart && byStart._running && byStart._natives.frame ) ) {
       
  1117 
       
  1118               animating.push( byStart );
       
  1119             }
       
  1120           }
       
  1121           start++;
       
  1122         } else {
       
  1123           // remove track event
       
  1124           Popcorn.removeTrackEvent( obj, byStart._id );
       
  1125           return;
       
  1126         }
       
  1127       }
       
  1128 
       
  1129       // If the `frameAnimation` option is used, iterate the animating track
       
  1130       // and execute the `frame` callback
       
  1131       if ( obj.options.frameAnimation ) {
       
  1132         while ( animIndex < animating.length ) {
       
  1133 
       
  1134           byAnimate = animating[ animIndex ];
       
  1135 
       
  1136           if ( !byAnimate._running ) {
       
  1137             animating.splice( animIndex, 1 );
       
  1138           } else {
       
  1139             byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
       
  1140             animIndex++;
       
  1141           }
       
  1142         }
       
  1143       }
       
  1144 
       
  1145     // Playbar receding
       
  1146     } else if ( previousTime > currentTime ) {
       
  1147 
       
  1148       while ( tracks.byStart[ start ] && tracks.byStart[ start ].start > currentTime ) {
       
  1149 
       
  1150         byStart = tracks.byStart[ start ];
       
  1151         natives = byStart._natives;
       
  1152         type = natives && natives.type;
       
  1153 
       
  1154         // if plugin does not exist on this instance, remove it
       
  1155         if ( !natives ||
       
  1156             ( !!registryByName[ type ] ||
       
  1157               !!obj[ type ] ) ) {
       
  1158 
       
  1159           if ( byStart._running === true ) {
       
  1160             byStart._running = false;
       
  1161             natives.end.call( obj, event, byStart );
       
  1162           }
       
  1163           start--;
       
  1164         } else {
       
  1165           // remove track event
       
  1166           Popcorn.removeTrackEvent( obj, byStart._id );
       
  1167           return;
       
  1168         }
       
  1169       }
       
  1170 
       
  1171       while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end > currentTime ) {
       
  1172 
       
  1173         byEnd = tracks.byEnd[ end ];
       
  1174         natives = byEnd._natives;
       
  1175         type = natives && natives.type;
       
  1176 
       
  1177         // if plugin does not exist on this instance, remove it
       
  1178         if ( !natives ||
       
  1179             ( !!registryByName[ type ] ||
       
  1180               !!obj[ type ] ) ) {
       
  1181 
       
  1182           if ( byEnd.start <= currentTime &&
       
  1183                 byEnd._running === false  &&
       
  1184                   obj.data.disabled.indexOf( type ) === -1 ) {
       
  1185 
       
  1186             byEnd._running = true;
       
  1187             natives.start.call( obj, event, byEnd );
       
  1188 
       
  1189             // If the `frameAnimation` option is used,
       
  1190             // push the current byEnd object into the `animating` cue
       
  1191             if ( obj.options.frameAnimation &&
       
  1192                   ( byEnd && byEnd._running && byEnd._natives.frame ) ) {
       
  1193 
       
  1194               animating.push( byEnd );
       
  1195             }
       
  1196           }
       
  1197           end--;
       
  1198         } else {
       
  1199           // remove track event
       
  1200           Popcorn.removeTrackEvent( obj, byEnd._id );
       
  1201           return;
       
  1202         }
       
  1203       }
       
  1204 
       
  1205       // If the `frameAnimation` option is used, iterate the animating track
       
  1206       // and execute the `frame` callback
       
  1207       if ( obj.options.frameAnimation ) {
       
  1208         while ( animIndex < animating.length ) {
       
  1209 
       
  1210           byAnimate = animating[ animIndex ];
       
  1211 
       
  1212           if ( !byAnimate._running ) {
       
  1213             animating.splice( animIndex, 1 );
       
  1214           } else {
       
  1215             byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
       
  1216             animIndex++;
       
  1217           }
       
  1218         }
       
  1219       }
       
  1220     // time bar is not moving ( video is paused )
       
  1221     }
       
  1222 
       
  1223     tracks.endIndex = end;
       
  1224     tracks.startIndex = start;
       
  1225     tracks.previousUpdateTime = currentTime;
       
  1226   };
       
  1227 
       
  1228   //  Map and Extend TrackEvent functions to all Popcorn instances
       
  1229   Popcorn.extend( Popcorn.p, {
       
  1230 
       
  1231     getTrackEvents: function() {
       
  1232       return Popcorn.getTrackEvents.call( null, this );
       
  1233     },
       
  1234 
       
  1235     getTrackEvent: function( id ) {
       
  1236       return Popcorn.getTrackEvent.call( null, this, id );
       
  1237     },
       
  1238 
       
  1239     getLastTrackEventId: function() {
       
  1240       return Popcorn.getLastTrackEventId.call( null, this );
       
  1241     },
       
  1242 
       
  1243     removeTrackEvent: function( id ) {
       
  1244 
       
  1245       Popcorn.removeTrackEvent.call( null, this, id );
       
  1246       return this;
       
  1247     },
       
  1248 
       
  1249     removePlugin: function( name ) {
       
  1250       Popcorn.removePlugin.call( null, this, name );
       
  1251       return this;
       
  1252     },
       
  1253 
       
  1254     timeUpdate: function( event ) {
       
  1255       Popcorn.timeUpdate.call( null, this, event );
       
  1256       return this;
       
  1257     },
       
  1258 
       
  1259     destroy: function() {
       
  1260       Popcorn.destroy.call( null, this );
       
  1261       return this;
       
  1262     }
       
  1263   });
       
  1264 
       
  1265   //  Plugin manifests
       
  1266   Popcorn.manifest = {};
       
  1267   //  Plugins are registered
       
  1268   Popcorn.registry = [];
       
  1269   Popcorn.registryByName = {};
       
  1270   //  An interface for extending Popcorn
       
  1271   //  with plugin functionality
       
  1272   Popcorn.plugin = function( name, definition, manifest ) {
       
  1273 
       
  1274     if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
       
  1275       Popcorn.error( "'" + name + "' is a protected function name" );
       
  1276       return;
       
  1277     }
       
  1278 
       
  1279     //  Provides some sugar, but ultimately extends
       
  1280     //  the definition into Popcorn.p
       
  1281     var reserved = [ "start", "end" ],
       
  1282         plugin = {},
       
  1283         setup,
       
  1284         isfn = typeof definition === "function",
       
  1285         methods = [ "_setup", "_teardown", "start", "end", "frame" ];
       
  1286 
       
  1287     // combines calls of two function calls into one
       
  1288     var combineFn = function( first, second ) {
       
  1289 
       
  1290       first = first || Popcorn.nop;
       
  1291       second = second || Popcorn.nop;
       
  1292 
       
  1293       return function() {
       
  1294         first.apply( this, arguments );
       
  1295         second.apply( this, arguments );
       
  1296       };
       
  1297     };
       
  1298 
       
  1299     //  If `manifest` arg is undefined, check for manifest within the `definition` object
       
  1300     //  If no `definition.manifest`, an empty object is a sufficient fallback
       
  1301     Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
       
  1302 
       
  1303     // apply safe, and empty default functions
       
  1304     methods.forEach(function( method ) {
       
  1305       definition[ method ] = safeTry( definition[ method ] || Popcorn.nop, name );
       
  1306     });
       
  1307 
       
  1308     var pluginFn = function( setup, options ) {
       
  1309 
       
  1310       if ( !options ) {
       
  1311         return this;
       
  1312       }
       
  1313 
       
  1314       //  Storing the plugin natives
       
  1315       var natives = options._natives = {},
       
  1316           compose = "",
       
  1317           defaults, originalOpts, manifestOpts, mergedSetupOpts;
       
  1318 
       
  1319       Popcorn.extend( natives, setup );
       
  1320 
       
  1321       options._natives.type = name;
       
  1322       options._running = false;
       
  1323 
       
  1324       natives.start = natives.start || natives[ "in" ];
       
  1325       natives.end = natives.end || natives[ "out" ];
       
  1326 
       
  1327       // extend teardown to always call end if running
       
  1328       natives._teardown = combineFn(function() {
       
  1329 
       
  1330         var args = slice.call( arguments );
       
  1331 
       
  1332         // end function signature is not the same as teardown,
       
  1333         // put null on the front of arguments for the event parameter
       
  1334         args.unshift( null );
       
  1335 
       
  1336         // only call end if event is running
       
  1337         args[ 1 ]._running && natives.end.apply( this, args );
       
  1338       }, natives._teardown );
       
  1339 
       
  1340       // Check for previously set default options
       
  1341       defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
       
  1342 
       
  1343       // default to an empty string if no effect exists
       
  1344       // split string into an array of effects
       
  1345       options.compose = options.compose && options.compose.split( " " ) || [];
       
  1346       options.effect = options.effect && options.effect.split( " " ) || [];
       
  1347 
       
  1348       // join the two arrays together
       
  1349       options.compose = options.compose.concat( options.effect );
       
  1350 
       
  1351       options.compose.forEach(function( composeOption ) {
       
  1352 
       
  1353         // if the requested compose is garbage, throw it away
       
  1354         compose = Popcorn.compositions[ composeOption ] || {};
       
  1355 
       
  1356         // extends previous functions with compose function
       
  1357         methods.forEach(function( method ) {
       
  1358           natives[ method ] = combineFn( natives[ method ], compose[ method ] );
       
  1359         });
       
  1360       });
       
  1361 
       
  1362       //  Ensure a manifest object, an empty object is a sufficient fallback
       
  1363       options._natives.manifest = manifest;
       
  1364 
       
  1365       //  Checks for expected properties
       
  1366       if ( !( "start" in options ) ) {
       
  1367         options.start = options[ "in" ] || 0;
       
  1368       }
       
  1369 
       
  1370       if ( !( "end" in options ) ) {
       
  1371         options.end = options[ "out" ] || this.duration() || Number.MAX_VALUE;
       
  1372       }
       
  1373 
       
  1374       // Merge with defaults if they exist, make sure per call is prioritized
       
  1375       mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) :
       
  1376                           options;
       
  1377 
       
  1378       // Resolves 239, 241, 242
       
  1379       if ( !mergedSetupOpts.target ) {
       
  1380 
       
  1381         //  Sometimes the manifest may be missing entirely
       
  1382         //  or it has an options object that doesn't have a `target` property
       
  1383         manifestOpts = "options" in manifest && manifest.options;
       
  1384 
       
  1385         mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
       
  1386       }
       
  1387 
       
  1388       // Trigger _setup method if exists
       
  1389       options._natives._setup && options._natives._setup.call( this, mergedSetupOpts );
       
  1390 
       
  1391       // Create new track event for this instance
       
  1392       Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) );
       
  1393 
       
  1394       //  Future support for plugin event definitions
       
  1395       //  for all of the native events
       
  1396       Popcorn.forEach( setup, function( callback, type ) {
       
  1397 
       
  1398         if ( type !== "type" ) {
       
  1399 
       
  1400           if ( reserved.indexOf( type ) === -1 ) {
       
  1401 
       
  1402             this.listen( type, callback );
       
  1403           }
       
  1404         }
       
  1405 
       
  1406       }, this );
       
  1407 
       
  1408       return this;
       
  1409     };
       
  1410 
       
  1411     //  Assign new named definition
       
  1412     plugin[ name ] = function( options ) {
       
  1413       return pluginFn.call( this, isfn ? definition.call( this, options ) : definition,
       
  1414                                   options );
       
  1415     };
       
  1416 
       
  1417     //  Extend Popcorn.p with new named definition
       
  1418     Popcorn.extend( Popcorn.p, plugin );
       
  1419 
       
  1420     //  Push into the registry
       
  1421     var entry = {
       
  1422       fn: plugin[ name ],
       
  1423       definition: definition,
       
  1424       base: definition,
       
  1425       parents: [],
       
  1426       name: name
       
  1427     };
       
  1428     Popcorn.registry.push(
       
  1429        Popcorn.extend( plugin, entry, {
       
  1430         type: name
       
  1431       })
       
  1432     );
       
  1433     Popcorn.registryByName[ name ] = entry;
       
  1434 
       
  1435     return plugin;
       
  1436   };
       
  1437 
       
  1438   // Storage for plugin function errors
       
  1439   Popcorn.plugin.errors = [];
       
  1440 
       
  1441   // Returns wrapped plugin function
       
  1442   function safeTry( fn, pluginName ) {
       
  1443     return function() {
       
  1444 
       
  1445       //  When Popcorn.plugin.debug is true, do not suppress errors
       
  1446       if ( Popcorn.plugin.debug ) {
       
  1447         return fn.apply( this, arguments );
       
  1448       }
       
  1449 
       
  1450       try {
       
  1451         return fn.apply( this, arguments );
       
  1452       } catch ( ex ) {
       
  1453 
       
  1454         // Push plugin function errors into logging queue
       
  1455         Popcorn.plugin.errors.push({
       
  1456           plugin: pluginName,
       
  1457           thrown: ex,
       
  1458           source: fn.toString()
       
  1459         });
       
  1460 
       
  1461         // Trigger an error that the instance can listen for
       
  1462         // and react to
       
  1463         this.trigger( "error", Popcorn.plugin.errors );
       
  1464       }
       
  1465     };
       
  1466   }
       
  1467 
       
  1468   // Debug-mode flag for plugin development
       
  1469   Popcorn.plugin.debug = false;
       
  1470 
       
  1471   //  removePlugin( type ) removes all tracks of that from all instances of popcorn
       
  1472   //  removePlugin( obj, type ) removes all tracks of type from obj, where obj is a single instance of popcorn
       
  1473   Popcorn.removePlugin = function( obj, name ) {
       
  1474 
       
  1475     //  Check if we are removing plugin from an instance or from all of Popcorn
       
  1476     if ( !name ) {
       
  1477 
       
  1478       //  Fix the order
       
  1479       name = obj;
       
  1480       obj = Popcorn.p;
       
  1481 
       
  1482       if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
       
  1483         Popcorn.error( "'" + name + "' is a protected function name" );
       
  1484         return;
       
  1485       }
       
  1486 
       
  1487       var registryLen = Popcorn.registry.length,
       
  1488           registryIdx;
       
  1489 
       
  1490       // remove plugin reference from registry
       
  1491       for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
       
  1492         if ( Popcorn.registry[ registryIdx ].name === name ) {
       
  1493           Popcorn.registry.splice( registryIdx, 1 );
       
  1494           delete Popcorn.registryByName[ name ];
       
  1495           delete Popcorn.manifest[ name ];
       
  1496 
       
  1497           // delete the plugin
       
  1498           delete obj[ name ];
       
  1499 
       
  1500           // plugin found and removed, stop checking, we are done
       
  1501           return;
       
  1502         }
       
  1503       }
       
  1504 
       
  1505     }
       
  1506 
       
  1507     var byStart = obj.data.trackEvents.byStart,
       
  1508         byEnd = obj.data.trackEvents.byEnd,
       
  1509         animating = obj.data.trackEvents.animating,
       
  1510         idx, sl;
       
  1511 
       
  1512     // remove all trackEvents
       
  1513     for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) {
       
  1514 
       
  1515       if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) &&
       
  1516                 ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) {
       
  1517 
       
  1518         byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] );
       
  1519 
       
  1520         byStart.splice( idx, 1 );
       
  1521         byEnd.splice( idx, 1 );
       
  1522 
       
  1523         // update for loop if something removed, but keep checking
       
  1524         idx--; sl--;
       
  1525         if ( obj.data.trackEvents.startIndex <= idx ) {
       
  1526           obj.data.trackEvents.startIndex--;
       
  1527           obj.data.trackEvents.endIndex--;
       
  1528         }
       
  1529       }
       
  1530     }
       
  1531 
       
  1532     //remove all animating events
       
  1533     for ( idx = 0, sl = animating.length; idx < sl; idx++ ) {
       
  1534 
       
  1535       if ( animating[ idx ] && animating[ idx ]._natives && animating[ idx ]._natives.type === name ) {
       
  1536 
       
  1537         animating.splice( idx, 1 );
       
  1538 
       
  1539         // update for loop if something removed, but keep checking
       
  1540         idx--; sl--;
       
  1541       }
       
  1542     }
       
  1543 
       
  1544   };
       
  1545 
       
  1546   Popcorn.compositions = {};
       
  1547 
       
  1548   //  Plugin inheritance
       
  1549   Popcorn.compose = function( name, definition, manifest ) {
       
  1550 
       
  1551     //  If `manifest` arg is undefined, check for manifest within the `definition` object
       
  1552     //  If no `definition.manifest`, an empty object is a sufficient fallback
       
  1553     Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
       
  1554 
       
  1555     // register the effect by name
       
  1556     Popcorn.compositions[ name ] = definition;
       
  1557   };
       
  1558 
       
  1559   Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose;
       
  1560 
       
  1561   // stores parsers keyed on filetype
       
  1562   Popcorn.parsers = {};
       
  1563 
       
  1564   // An interface for extending Popcorn
       
  1565   // with parser functionality
       
  1566   Popcorn.parser = function( name, type, definition ) {
       
  1567 
       
  1568     if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
       
  1569       Popcorn.error( "'" + name + "' is a protected function name" );
       
  1570       return;
       
  1571     }
       
  1572 
       
  1573     // fixes parameters for overloaded function call
       
  1574     if ( typeof type === "function" && !definition ) {
       
  1575       definition = type;
       
  1576       type = "";
       
  1577     }
       
  1578 
       
  1579     if ( typeof definition !== "function" || typeof type !== "string" ) {
       
  1580       return;
       
  1581     }
       
  1582 
       
  1583     // Provides some sugar, but ultimately extends
       
  1584     // the definition into Popcorn.p
       
  1585 
       
  1586     var natives = Popcorn.events.all,
       
  1587         parseFn,
       
  1588         parser = {};
       
  1589 
       
  1590     parseFn = function( filename, callback ) {
       
  1591 
       
  1592       if ( !filename ) {
       
  1593         return this;
       
  1594       }
       
  1595 
       
  1596       var that = this;
       
  1597 
       
  1598       Popcorn.xhr({
       
  1599         url: filename,
       
  1600         dataType: type,
       
  1601         success: function( data ) {
       
  1602 
       
  1603           var tracksObject = definition( data ),
       
  1604               tracksData,
       
  1605               tracksDataLen,
       
  1606               tracksDef,
       
  1607               idx = 0;
       
  1608 
       
  1609           tracksData = tracksObject.data || [];
       
  1610           tracksDataLen = tracksData.length;
       
  1611           tracksDef = null;
       
  1612 
       
  1613           //  If no tracks to process, return immediately
       
  1614           if ( !tracksDataLen ) {
       
  1615             return;
       
  1616           }
       
  1617 
       
  1618           //  Create tracks out of parsed object
       
  1619           for ( ; idx < tracksDataLen; idx++ ) {
       
  1620 
       
  1621             tracksDef = tracksData[ idx ];
       
  1622 
       
  1623             for ( var key in tracksDef ) {
       
  1624 
       
  1625               if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
       
  1626 
       
  1627                 that[ key ]( tracksDef[ key ] );
       
  1628               }
       
  1629             }
       
  1630           }
       
  1631           if ( callback ) {
       
  1632             callback();
       
  1633           }
       
  1634         }
       
  1635       });
       
  1636 
       
  1637       return this;
       
  1638     };
       
  1639 
       
  1640     // Assign new named definition
       
  1641     parser[ name ] = parseFn;
       
  1642 
       
  1643     // Extend Popcorn.p with new named definition
       
  1644     Popcorn.extend( Popcorn.p, parser );
       
  1645 
       
  1646     // keys the function name by filetype extension
       
  1647     //Popcorn.parsers[ name ] = true;
       
  1648 
       
  1649     return parser;
       
  1650   };
       
  1651 
       
  1652   Popcorn.player = function( name, player ) {
       
  1653 
       
  1654     player = player || {};
       
  1655 
       
  1656     var playerFn = function( target, src, options ) {
       
  1657 
       
  1658       options = options || {};
       
  1659 
       
  1660       // List of events
       
  1661       var date = new Date() / 1000,
       
  1662           baselineTime = date,
       
  1663           currentTime = 0,
       
  1664           volume = 1,
       
  1665           muted = false,
       
  1666           events = {},
       
  1667 
       
  1668           // The container div of the resource
       
  1669           container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) ||
       
  1670                         document.getElementById( target ) ||
       
  1671                           target,
       
  1672           basePlayer = {},
       
  1673           timeout,
       
  1674           popcorn;
       
  1675 
       
  1676       // copies a div into the media object
       
  1677       for( var val in container ) {
       
  1678 
       
  1679         if ( typeof container[ val ] === "object" ) {
       
  1680 
       
  1681           basePlayer[ val ] = container[ val ];
       
  1682         } else if ( typeof container[ val ] === "function" ) {
       
  1683 
       
  1684           basePlayer[ val ] = (function( value ) {
       
  1685 
       
  1686             // this is a stupid ugly kludgy hack in honour of Safari
       
  1687             // in Safari a NodeList is a function, not an object
       
  1688             if ( "length" in container[ value ] && !container[ value ].call ) {
       
  1689 
       
  1690               return container[ value ];
       
  1691             } else {
       
  1692 
       
  1693               return function() {
       
  1694 
       
  1695                 return container[ value ].apply( container, arguments );
       
  1696               };
       
  1697             }
       
  1698           }( val ));
       
  1699         } else {
       
  1700 
       
  1701           Popcorn.player.defineProperty( basePlayer, val, {
       
  1702             get: (function( value ) {
       
  1703 
       
  1704               return function() {
       
  1705 
       
  1706                 return container[ value ];
       
  1707               };
       
  1708             }( val )),
       
  1709             set: Popcorn.nop,
       
  1710             configurable: true
       
  1711           });
       
  1712         }
       
  1713       }
       
  1714 
       
  1715       var timeupdate = function() {
       
  1716 
       
  1717         date = new Date() / 1000;
       
  1718 
       
  1719         if ( !basePlayer.paused ) {
       
  1720 
       
  1721           basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
       
  1722           basePlayer.dispatchEvent( "timeupdate" );
       
  1723           timeout = setTimeout( timeupdate, 10 );
       
  1724         }
       
  1725 
       
  1726         baselineTime = date;
       
  1727       };
       
  1728 
       
  1729       basePlayer.play = function() {
       
  1730 
       
  1731         this.paused = false;
       
  1732 
       
  1733         if ( basePlayer.readyState >= 4 ) {
       
  1734 
       
  1735           baselineTime = new Date() / 1000;
       
  1736           basePlayer.dispatchEvent( "play" );
       
  1737           timeupdate();
       
  1738         }
       
  1739       };
       
  1740 
       
  1741       basePlayer.pause = function() {
       
  1742 
       
  1743         this.paused = true;
       
  1744         basePlayer.dispatchEvent( "pause" );
       
  1745       };
       
  1746 
       
  1747       Popcorn.player.defineProperty( basePlayer, "currentTime", {
       
  1748         get: function() {
       
  1749 
       
  1750           return currentTime;
       
  1751         },
       
  1752         set: function( val ) {
       
  1753 
       
  1754           // make sure val is a number
       
  1755           currentTime = +val;
       
  1756           basePlayer.dispatchEvent( "timeupdate" );
       
  1757           return currentTime;
       
  1758         },
       
  1759         configurable: true
       
  1760       });
       
  1761 
       
  1762       Popcorn.player.defineProperty( basePlayer, "volume", {
       
  1763         get: function() {
       
  1764 
       
  1765           return volume;
       
  1766         },
       
  1767         set: function( val ) {
       
  1768 
       
  1769           // make sure val is a number
       
  1770           volume = +val;
       
  1771           basePlayer.dispatchEvent( "volumechange" );
       
  1772           return volume;
       
  1773         },
       
  1774         configurable: true
       
  1775       });
       
  1776 
       
  1777       Popcorn.player.defineProperty( basePlayer, "muted", {
       
  1778         get: function() {
       
  1779 
       
  1780           return muted;
       
  1781         },
       
  1782         set: function( val ) {
       
  1783 
       
  1784           // make sure val is a number
       
  1785           muted = +val;
       
  1786           basePlayer.dispatchEvent( "volumechange" );
       
  1787           return muted;
       
  1788         },
       
  1789         configurable: true
       
  1790       });
       
  1791 
       
  1792       // Adds an event listener to the object
       
  1793       basePlayer.addEventListener = function( evtName, fn ) {
       
  1794 
       
  1795         if ( !events[ evtName ] ) {
       
  1796 
       
  1797           events[ evtName ] = [];
       
  1798         }
       
  1799 
       
  1800         events[ evtName ].push( fn );
       
  1801         return fn;
       
  1802       };
       
  1803 
       
  1804       // Can take event object or simple string
       
  1805       basePlayer.dispatchEvent = function( oEvent ) {
       
  1806 
       
  1807         var evt,
       
  1808             self = this,
       
  1809             eventInterface,
       
  1810             eventName = oEvent.type;
       
  1811 
       
  1812         // A string was passed, create event object
       
  1813         if ( !eventName ) {
       
  1814 
       
  1815           eventName = oEvent;
       
  1816           eventInterface  = Popcorn.events.getInterface( eventName );
       
  1817 
       
  1818           if ( eventInterface ) {
       
  1819 
       
  1820             evt = document.createEvent( eventInterface );
       
  1821             evt.initEvent( eventName, true, true, window, 1 );
       
  1822           }
       
  1823         }
       
  1824 
       
  1825         Popcorn.forEach( events[ eventName ], function( val ) {
       
  1826 
       
  1827           val.call( self, evt, self );
       
  1828         });
       
  1829       };
       
  1830 
       
  1831       // Attempt to get src from playerFn parameter
       
  1832       basePlayer.src = src || "";
       
  1833       basePlayer.readyState = 0;
       
  1834       basePlayer.duration = 0;
       
  1835       basePlayer.paused = true;
       
  1836       basePlayer.ended = 0;
       
  1837 
       
  1838       if ( player._setup ) {
       
  1839 
       
  1840         player._setup.call( basePlayer, options );
       
  1841       } else {
       
  1842 
       
  1843         // there is no setup, which means there is nothing to load
       
  1844         basePlayer.readyState = 4;
       
  1845         basePlayer.dispatchEvent( "load" );
       
  1846         basePlayer.dispatchEvent( "loadeddata" );
       
  1847       }
       
  1848 
       
  1849       // when a custom player is loaded, load basePlayer state into custom player
       
  1850       basePlayer.addEventListener( "load", function() {
       
  1851 
       
  1852         // if a player is not ready before currentTime is called, this will set it after it is ready
       
  1853         basePlayer.currentTime = currentTime;
       
  1854 
       
  1855         // same as above with volume and muted
       
  1856         basePlayer.volume = volume;
       
  1857         basePlayer.muted = muted;
       
  1858       });
       
  1859 
       
  1860       basePlayer.addEventListener( "loadeddata", function() {
       
  1861 
       
  1862         // if play was called before player ready, start playing video
       
  1863         !basePlayer.paused && basePlayer.play();
       
  1864       });
       
  1865 
       
  1866       popcorn = new Popcorn.p.init( basePlayer, options );
       
  1867 
       
  1868       return popcorn;
       
  1869     };
       
  1870 
       
  1871     Popcorn[ name ] = Popcorn[ name ] || playerFn;
       
  1872   };
       
  1873 
       
  1874   Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
       
  1875 
       
  1876     object.__defineGetter__( description, options.get || Popcorn.nop );
       
  1877     object.__defineSetter__( description, options.set || Popcorn.nop );
       
  1878   };
       
  1879 
       
  1880   //  Cache references to reused RegExps
       
  1881   var rparams = /\?/,
       
  1882   //  XHR Setup object
       
  1883   setup = {
       
  1884     url: "",
       
  1885     data: "",
       
  1886     dataType: "",
       
  1887     success: Popcorn.nop,
       
  1888     type: "GET",
       
  1889     async: true,
       
  1890     xhr: function() {
       
  1891       return new global.XMLHttpRequest();
       
  1892     }
       
  1893   };
       
  1894 
       
  1895   Popcorn.xhr = function( options ) {
       
  1896 
       
  1897     options.dataType = options.dataType && options.dataType.toLowerCase() || null;
       
  1898 
       
  1899     if ( options.dataType &&
       
  1900          ( options.dataType === "jsonp" || options.dataType === "script" ) ) {
       
  1901 
       
  1902       Popcorn.xhr.getJSONP(
       
  1903         options.url,
       
  1904         options.success,
       
  1905         options.dataType === "script"
       
  1906       );
       
  1907       return;
       
  1908     }
       
  1909 
       
  1910     var settings = Popcorn.extend( {}, setup, options );
       
  1911 
       
  1912     //  Create new XMLHttpRequest object
       
  1913     settings.ajax  = settings.xhr();
       
  1914 
       
  1915     if ( settings.ajax ) {
       
  1916 
       
  1917       if ( settings.type === "GET" && settings.data ) {
       
  1918 
       
  1919         //  append query string
       
  1920         settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
       
  1921 
       
  1922         //  Garbage collect and reset settings.data
       
  1923         settings.data = null;
       
  1924       }
       
  1925 
       
  1926 
       
  1927       settings.ajax.open( settings.type, settings.url, settings.async );
       
  1928       settings.ajax.send( settings.data || null );
       
  1929 
       
  1930       return Popcorn.xhr.httpData( settings );
       
  1931     }
       
  1932   };
       
  1933 
       
  1934 
       
  1935   Popcorn.xhr.httpData = function( settings ) {
       
  1936 
       
  1937     var data, json = null,
       
  1938         parser, xml = null;
       
  1939 
       
  1940     settings.ajax.onreadystatechange = function() {
       
  1941 
       
  1942       if ( settings.ajax.readyState === 4 ) {
       
  1943 
       
  1944         try {
       
  1945           json = JSON.parse( settings.ajax.responseText );
       
  1946         } catch( e ) {
       
  1947           //suppress
       
  1948         }
       
  1949 
       
  1950         data = {
       
  1951           xml: settings.ajax.responseXML,
       
  1952           text: settings.ajax.responseText,
       
  1953           json: json
       
  1954         };
       
  1955 
       
  1956         // Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
       
  1957         if ( !data.xml || !data.xml.documentElement ) {
       
  1958           data.xml = null;
       
  1959 
       
  1960           try {
       
  1961             parser = new DOMParser();
       
  1962             xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
       
  1963 
       
  1964             if ( !xml.getElementsByTagName( "parsererror" ).length ) {
       
  1965               data.xml = xml;
       
  1966             }
       
  1967           } catch ( e ) {
       
  1968             // data.xml remains null
       
  1969           }
       
  1970         }
       
  1971 
       
  1972         //  If a dataType was specified, return that type of data
       
  1973         if ( settings.dataType ) {
       
  1974           data = data[ settings.dataType ];
       
  1975         }
       
  1976 
       
  1977 
       
  1978         settings.success.call( settings.ajax, data );
       
  1979 
       
  1980       }
       
  1981     };
       
  1982     return data;
       
  1983   };
       
  1984 
       
  1985   Popcorn.xhr.getJSONP = function( url, success, isScript ) {
       
  1986 
       
  1987     var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
       
  1988       script = document.createElement( "script" ),
       
  1989       paramStr = url.split( "?" )[ 1 ],
       
  1990       isFired = false,
       
  1991       params = [],
       
  1992       callback, parts, callparam;
       
  1993 
       
  1994     if ( paramStr && !isScript ) {
       
  1995       params = paramStr.split( "&" );
       
  1996     }
       
  1997 
       
  1998     if ( params.length ) {
       
  1999       parts = params[ params.length - 1 ].split( "=" );
       
  2000     }
       
  2001 
       
  2002     callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ]  ) : "jsonp";
       
  2003 
       
  2004     if ( !paramStr && !isScript ) {
       
  2005       url += "?callback=" + callback;
       
  2006     }
       
  2007 
       
  2008     if ( callback && !isScript ) {
       
  2009 
       
  2010       //  If a callback name already exists
       
  2011       if ( !!window[ callback ] ) {
       
  2012         //  Create a new unique callback name
       
  2013         callback = Popcorn.guid( callback );
       
  2014       }
       
  2015 
       
  2016       //  Define the JSONP success callback globally
       
  2017       window[ callback ] = function( data ) {
       
  2018         // Fire success callbacks
       
  2019         success && success( data );
       
  2020         isFired = true;
       
  2021       };
       
  2022 
       
  2023       //  Replace callback param and callback name
       
  2024       url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
       
  2025     }
       
  2026 
       
  2027     script.onload = function() {
       
  2028 
       
  2029       //  Handling remote script loading callbacks
       
  2030       if ( isScript ) {
       
  2031         //  getScript
       
  2032         success && success();
       
  2033       }
       
  2034 
       
  2035       //  Executing for JSONP requests
       
  2036       if ( isFired ) {
       
  2037         //  Garbage collect the callback
       
  2038         delete window[ callback ];
       
  2039       }
       
  2040       //  Garbage collect the script resource
       
  2041       head.removeChild( script );
       
  2042     };
       
  2043 
       
  2044     script.src = url;
       
  2045 
       
  2046     head.insertBefore( script, head.firstChild );
       
  2047 
       
  2048     return;
       
  2049   };
       
  2050 
       
  2051   Popcorn.getJSONP = Popcorn.xhr.getJSONP;
       
  2052 
       
  2053   Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
       
  2054 
       
  2055     return Popcorn.xhr.getJSONP( url, success, true );
       
  2056   };
       
  2057 
       
  2058   Popcorn.util = {
       
  2059     // Simple function to parse a timestamp into seconds
       
  2060     // Acceptable formats are:
       
  2061     // HH:MM:SS.MMM
       
  2062     // HH:MM:SS;FF
       
  2063     // Hours and minutes are optional. They default to 0
       
  2064     toSeconds: function( timeStr, framerate ) {
       
  2065       // Hours and minutes are optional
       
  2066       // Seconds must be specified
       
  2067       // Seconds can be followed by milliseconds OR by the frame information
       
  2068       var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
       
  2069           errorMessage = "Invalid time format",
       
  2070           digitPairs, lastIndex, lastPair, firstPair,
       
  2071           frameInfo, frameTime;
       
  2072 
       
  2073       if ( typeof timeStr === "number" ) {
       
  2074         return timeStr;
       
  2075       }
       
  2076 
       
  2077       if ( typeof timeStr === "string" &&
       
  2078             !validTimeFormat.test( timeStr ) ) {
       
  2079         Popcorn.error( errorMessage );
       
  2080       }
       
  2081 
       
  2082       digitPairs = timeStr.split( ":" );
       
  2083       lastIndex = digitPairs.length - 1;
       
  2084       lastPair = digitPairs[ lastIndex ];
       
  2085 
       
  2086       // Fix last element:
       
  2087       if ( lastPair.indexOf( ";" ) > -1 ) {
       
  2088 
       
  2089         frameInfo = lastPair.split( ";" );
       
  2090         frameTime = 0;
       
  2091 
       
  2092         if ( framerate && ( typeof framerate === "number" ) ) {
       
  2093           frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
       
  2094         }
       
  2095 
       
  2096         digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime;
       
  2097       }
       
  2098 
       
  2099       firstPair = digitPairs[ 0 ];
       
  2100 
       
  2101       return {
       
  2102 
       
  2103         1: parseFloat( firstPair, 10 ),
       
  2104 
       
  2105         2: ( parseInt( firstPair, 10 ) * 60 ) +
       
  2106               parseFloat( digitPairs[ 1 ], 10 ),
       
  2107 
       
  2108         3: ( parseInt( firstPair, 10 ) * 3600 ) +
       
  2109             ( parseInt( digitPairs[ 1 ], 10 ) * 60 ) +
       
  2110               parseFloat( digitPairs[ 2 ], 10 )
       
  2111 
       
  2112       }[ digitPairs.length || 1 ];
       
  2113     }
       
  2114   };
       
  2115 
       
  2116 
       
  2117   // Initialize locale data
       
  2118   // Based on http://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes
       
  2119   function initLocale( arg ) {
       
  2120 
       
  2121     var locale = typeof arg === "string" ? arg : [ arg.language, arg.region ].join( "-" ),
       
  2122         parts = locale.split( "-" );
       
  2123 
       
  2124     // Setup locale data table
       
  2125     return {
       
  2126       iso6391: locale,
       
  2127       language: parts[ 0 ] || "",
       
  2128       region: parts[ 1 ] || ""
       
  2129     };
       
  2130   }
       
  2131 
       
  2132   // Declare locale data table
       
  2133   var localeData = initLocale( global.navigator.userLanguage || global.navigator.language );
       
  2134 
       
  2135   Popcorn.locale = {
       
  2136 
       
  2137     // Popcorn.locale.get()
       
  2138     // returns reference to privately
       
  2139     // defined localeData
       
  2140     get: function() {
       
  2141       return localeData;
       
  2142     },
       
  2143 
       
  2144     // Popcorn.locale.set( string|object );
       
  2145     set: function( arg ) {
       
  2146 
       
  2147       localeData = initLocale( arg );
       
  2148 
       
  2149       Popcorn.locale.broadcast();
       
  2150 
       
  2151       return localeData;
       
  2152     },
       
  2153 
       
  2154     // Popcorn.locale.broadcast( type )
       
  2155     // Sends events to all popcorn media instances that are
       
  2156     // listening for locale events
       
  2157     broadcast: function( type ) {
       
  2158 
       
  2159       var instances = Popcorn.instances,
       
  2160           length = instances.length,
       
  2161           idx = 0,
       
  2162           instance;
       
  2163 
       
  2164       type = type || "locale:changed";
       
  2165 
       
  2166       // Iterate all current instances
       
  2167       for ( ; idx < length; idx++ ) {
       
  2168         instance = instances[ idx ];
       
  2169 
       
  2170         // For those instances with locale event listeners,
       
  2171         // trigger a locale change event
       
  2172         if ( type in instance.data.events  ) {
       
  2173           instance.trigger( type );
       
  2174         }
       
  2175       }
       
  2176     }
       
  2177   };
       
  2178 
       
  2179   // alias for exec function
       
  2180   Popcorn.p.cue = Popcorn.p.exec;
       
  2181 
       
  2182   function getItems() {
       
  2183 
       
  2184     var item,
       
  2185         list = [];
       
  2186 
       
  2187     if ( Object.keys ) {
       
  2188       list = Object.keys( Popcorn.p );
       
  2189     } else {
       
  2190 
       
  2191       for ( item in Popcorn.p ) {
       
  2192         if ( hasOwn.call( Popcorn.p, item ) ) {
       
  2193           list.push( item );
       
  2194         }
       
  2195       }
       
  2196     }
       
  2197 
       
  2198     return list.join( "," ).toLowerCase().split( ",");
       
  2199   }
       
  2200 
       
  2201   //  Protected API methods
       
  2202   Popcorn.protect = {
       
  2203     natives: getItems()
       
  2204   };
       
  2205 
       
  2206   //  Exposes Popcorn to global context
       
  2207   global.Popcorn = Popcorn;
       
  2208 
       
  2209 })(window, window.document);
       
  2210 // A global callback for youtube... that makes me angry
       
  2211 var onYouTubePlayerReady = function( containerId ) {
       
  2212 
       
  2213   onYouTubePlayerReady[ containerId ] && onYouTubePlayerReady[ containerId ]();
       
  2214 };
       
  2215 onYouTubePlayerReady.stateChangeEventHandler = {};
       
  2216 
       
  2217 Popcorn.player( "youtube", {
       
  2218   _setup: function( options ) {
       
  2219 
       
  2220     var media = this,
       
  2221         youtubeObject,
       
  2222         container = document.createElement( "div" ),
       
  2223         currentTime = 0,
       
  2224         seekTime = 0,
       
  2225         seeking = false,
       
  2226 
       
  2227         // state code for volume changed polling
       
  2228         volumeChanged = false,
       
  2229         lastMuted = false,
       
  2230         lastVolume = 0;
       
  2231 
       
  2232     container.id = media.id + Popcorn.guid();
       
  2233 
       
  2234     media.appendChild( container );
       
  2235 
       
  2236     var youtubeInit = function() {
       
  2237 
       
  2238       var flashvars,
       
  2239           params,
       
  2240           attributes,
       
  2241           src;
       
  2242 
       
  2243       // expose a callback to this scope, that is called from the global callback youtube calls
       
  2244       onYouTubePlayerReady[ container.id ] = function() {
       
  2245 
       
  2246         youtubeObject = document.getElementById( container.id );
       
  2247 
       
  2248         // more youtube callback nonsense
       
  2249         onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) {
       
  2250 
       
  2251           // playing is state 1
       
  2252           // paused is state 2
       
  2253           if ( state === 1 ) {
       
  2254 
       
  2255             media.paused && media.play();
       
  2256           // youtube fires paused events while seeking
       
  2257           // this is the only way to get seeking events
       
  2258           } else if ( state === 2 ) {
       
  2259 
       
  2260             // silly logic forced on me by the youtube API
       
  2261             // calling youtube.seekTo triggers multiple events
       
  2262             // with the second events getCurrentTime being the old time
       
  2263             if ( seeking && seekTime === currentTime && seekTime !== youtubeObject.getCurrentTime() ) {
       
  2264 
       
  2265               seeking = false;
       
  2266               youtubeObject.seekTo( currentTime );
       
  2267               return;
       
  2268             }
       
  2269 
       
  2270             currentTime = youtubeObject.getCurrentTime();
       
  2271             media.dispatchEvent( "timeupdate" );
       
  2272             !media.paused && media.pause();
       
  2273           }
       
  2274         };
       
  2275 
       
  2276         // youtube requires callbacks to be a string to a function path from the global scope
       
  2277         youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
       
  2278 
       
  2279         var timeupdate = function() {
       
  2280 
       
  2281           if ( !media.paused ) {
       
  2282 
       
  2283             currentTime = youtubeObject.getCurrentTime();
       
  2284             media.dispatchEvent( "timeupdate" );
       
  2285             setTimeout( timeupdate, 10 );
       
  2286           }
       
  2287         };
       
  2288 
       
  2289         var volumeupdate = function() {
       
  2290 
       
  2291           if ( lastMuted !== youtubeObject.isMuted() ) {
       
  2292 
       
  2293             lastMuted = youtubeObject.isMuted();
       
  2294             media.dispatchEvent( "volumechange" );
       
  2295           }
       
  2296 
       
  2297           if ( lastVolume !== youtubeObject.getVolume() ) {
       
  2298 
       
  2299             lastVolume = youtubeObject.getVolume();
       
  2300             media.dispatchEvent( "volumechange" );
       
  2301           }
       
  2302 
       
  2303           setTimeout( volumeupdate, 250 );
       
  2304         };
       
  2305 
       
  2306         media.play = function() {
       
  2307 
       
  2308           media.paused = false;
       
  2309           media.dispatchEvent( "play" );
       
  2310 
       
  2311           media.dispatchEvent( "playing" );
       
  2312           timeupdate();
       
  2313           youtubeObject.playVideo();
       
  2314         };
       
  2315 
       
  2316         media.pause = function() {
       
  2317 
       
  2318           if ( !media.paused ) {
       
  2319 
       
  2320             media.paused = true;
       
  2321             media.dispatchEvent( "pause" );
       
  2322             youtubeObject.pauseVideo();
       
  2323           }
       
  2324         };
       
  2325 
       
  2326         Popcorn.player.defineProperty( media, "currentTime", {
       
  2327           set: function( val ) {
       
  2328 
       
  2329             // make sure val is a number
       
  2330             currentTime = seekTime = +val;
       
  2331             seeking = true;
       
  2332             media.dispatchEvent( "seeked" );
       
  2333             media.dispatchEvent( "timeupdate" );
       
  2334             youtubeObject.seekTo( currentTime );
       
  2335             return currentTime;
       
  2336           },
       
  2337           get: function() {
       
  2338 
       
  2339             return currentTime;
       
  2340           }
       
  2341         });
       
  2342 
       
  2343         Popcorn.player.defineProperty( media, "muted", {
       
  2344           set: function( val ) {
       
  2345 
       
  2346             if ( youtubeObject.isMuted() !== val ) {
       
  2347 
       
  2348               if ( val ) {
       
  2349 
       
  2350                 youtubeObject.mute();
       
  2351               } else {
       
  2352 
       
  2353                 youtubeObject.unMute();
       
  2354               }
       
  2355 
       
  2356               lastMuted = youtubeObject.isMuted();
       
  2357               media.dispatchEvent( "volumechange" );
       
  2358             }
       
  2359 
       
  2360             return youtubeObject.isMuted();
       
  2361           },
       
  2362           get: function() {
       
  2363 
       
  2364             return youtubeObject.isMuted();
       
  2365           }
       
  2366         });
       
  2367 
       
  2368         Popcorn.player.defineProperty( media, "volume", {
       
  2369           set: function( val ) {
       
  2370 
       
  2371             if ( youtubeObject.getVolume() !== val ) {
       
  2372 
       
  2373               youtubeObject.setVolume( val );
       
  2374               lastVolume = youtubeObject.getVolume();
       
  2375               media.dispatchEvent( "volumechange" );
       
  2376             }
       
  2377 
       
  2378             return youtubeObject.getVolume();
       
  2379           },
       
  2380           get: function() {
       
  2381 
       
  2382             return youtubeObject.getVolume();
       
  2383           }
       
  2384         });
       
  2385 
       
  2386         media.readyState = 4;
       
  2387         media.dispatchEvent( "load" );
       
  2388         media.duration = youtubeObject.getDuration();
       
  2389         media.dispatchEvent( "durationchange" );
       
  2390         volumeupdate();
       
  2391 
       
  2392         media.dispatchEvent( "loadeddata" );
       
  2393       };
       
  2394 
       
  2395       options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
       
  2396       options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
       
  2397 
       
  2398       flashvars = {
       
  2399         playerapiid: container.id,
       
  2400         controls: options.controls,
       
  2401         iv_load_policy: options.annotations
       
  2402       };
       
  2403 
       
  2404       params = {
       
  2405         wmode: "transparent",
       
  2406         allowScriptAccess: "always"
       
  2407       };
       
  2408 
       
  2409       attributes = {
       
  2410         id: container.id
       
  2411       };
       
  2412 
       
  2413       src = /^.*[\/=](.{11})/.exec( media.src )[ 1 ];
       
  2414 
       
  2415       swfobject.embedSWF( "http://www.youtube.com/e/" + src + "?enablejsapi=1&playerapiid=" + container.id + "&version=3",
       
  2416                           container.id, media.offsetWidth, media.offsetHeight, "8", null,
       
  2417                           flashvars, params, attributes );
       
  2418     };
       
  2419 
       
  2420     if ( !window.swfobject ) {
       
  2421 
       
  2422       Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", youtubeInit );
       
  2423     } else {
       
  2424 
       
  2425       youtubeInit();
       
  2426     }
       
  2427   }
       
  2428 });
       
  2429 
       
  2430 // PLUGIN: Code
       
  2431 
       
  2432 (function ( Popcorn ) {
       
  2433 
       
  2434   /**
       
  2435    * Code Popcorn Plug-in
       
  2436    *
       
  2437    * Adds the ability to run arbitrary code (JavaScript functions) according to video timing.
       
  2438    *
       
  2439    * @param {Object} options
       
  2440    *
       
  2441    * Required parameters: start, end, template, data, and target.
       
  2442    * Optional parameter: static.
       
  2443    *
       
  2444    *   start: the time in seconds when the mustache template should be rendered
       
  2445    *          in the target div.
       
  2446    *
       
  2447    *   end: the time in seconds when the rendered mustache template should be
       
  2448    *        removed from the target div.
       
  2449    *
       
  2450    *   onStart: the function to be run when the start time is reached.
       
  2451    *
       
  2452    *   onFrame: [optional] a function to be run on each paint call
       
  2453    *            (e.g., called ~60 times per second) between the start and end times.
       
  2454    *
       
  2455    *   onEnd: [optional] a function to be run when the end time is reached.
       
  2456    *
       
  2457    * Example:
       
  2458      var p = Popcorn('#video')
       
  2459 
       
  2460         // onStart function only
       
  2461         .code({
       
  2462           start: 1,
       
  2463           end: 4,
       
  2464           onStart: function( options ) {
       
  2465             // called on start
       
  2466           }
       
  2467         })
       
  2468 
       
  2469         // onStart + onEnd only
       
  2470         .code({
       
  2471           start: 6,
       
  2472           end: 8,
       
  2473           onStart: function( options ) {
       
  2474             // called on start
       
  2475           },
       
  2476           onEnd: function ( options ) {
       
  2477             // called on end
       
  2478           }
       
  2479         })
       
  2480 
       
  2481         // onStart, onEnd, onFrame
       
  2482         .code({
       
  2483           start: 10,
       
  2484           end: 14,
       
  2485           onStart: function( options ) {
       
  2486             // called on start
       
  2487           },
       
  2488           onFrame: function ( options ) {
       
  2489             // called on every paint frame between start and end.
       
  2490             // uses mozRequestAnimationFrame, webkitRequestAnimationFrame,
       
  2491             // or setTimeout with 16ms window.
       
  2492           },
       
  2493           onEnd: function ( options ) {
       
  2494             // called on end
       
  2495           }
       
  2496         });
       
  2497   *
       
  2498   */
       
  2499 
       
  2500   Popcorn.plugin( "code" , function( options ) {
       
  2501     var running = false;
       
  2502 
       
  2503     // Setup a proper frame interval function (60fps), favouring paint events.
       
  2504     var step = (function() {
       
  2505 
       
  2506       var buildFrameRunner = function( runner ) {
       
  2507         return function( f, options ) {
       
  2508 
       
  2509           var _f = function() {
       
  2510             running && f();
       
  2511             running && runner( _f );
       
  2512           };
       
  2513 
       
  2514           _f();
       
  2515         };
       
  2516       };
       
  2517 
       
  2518       // Figure out which level of browser support we have for this
       
  2519       if ( window.webkitRequestAnimationFrame ) {
       
  2520         return buildFrameRunner( window.webkitRequestAnimationFrame );
       
  2521       } else if ( window.mozRequestAnimationFrame ) {
       
  2522         return buildFrameRunner( window.mozRequestAnimationFrame );
       
  2523       } else {
       
  2524         return buildFrameRunner( function( f ) {
       
  2525           window.setTimeout( f, 16 );
       
  2526         });
       
  2527       }
       
  2528 
       
  2529     })();
       
  2530 
       
  2531     if ( !options.onStart || typeof options.onStart !== "function" ) {
       
  2532 
       
  2533       if ( Popcorn.plugin.debug ) {
       
  2534         throw new Error( "Popcorn Code Plugin Error: onStart must be a function." );
       
  2535       }
       
  2536       options.onStart = Popcorn.nop;
       
  2537     }
       
  2538 
       
  2539     if ( options.onEnd && typeof options.onEnd !== "function" ) {
       
  2540 
       
  2541       if ( Popcorn.plugin.debug ) {
       
  2542         throw new Error( "Popcorn Code Plugin Error: onEnd  must be a function." );
       
  2543       }
       
  2544       options.onEnd = undefined;
       
  2545     }
       
  2546 
       
  2547     if ( options.onFrame && typeof options.onFrame !== "function" ) {
       
  2548 
       
  2549       if ( Popcorn.plugin.debug ) {
       
  2550         throw new Error( "Popcorn Code Plugin Error: onFrame  must be a function." );
       
  2551       }
       
  2552       options.onFrame = undefined;
       
  2553     }
       
  2554 
       
  2555     return {
       
  2556       start: function( event, options ) {
       
  2557         options.onStart( options );
       
  2558 
       
  2559         if ( options.onFrame ) {
       
  2560           running = true;
       
  2561           step( options.onFrame, options );
       
  2562         }
       
  2563       },
       
  2564 
       
  2565       end: function( event, options ) {
       
  2566         if ( options.onFrame ) {
       
  2567           running = false;
       
  2568         }
       
  2569 
       
  2570         if ( options.onEnd ) {
       
  2571           options.onEnd( options );
       
  2572         }
       
  2573       }
       
  2574     };
       
  2575   },
       
  2576   {
       
  2577     about: {
       
  2578       name: "Popcorn Code Plugin",
       
  2579       version: "0.1",
       
  2580       author: "David Humphrey (@humphd)",
       
  2581       website: "http://vocamus.net/dave"
       
  2582     },
       
  2583     options: {
       
  2584       start: {
       
  2585        elem: "input",
       
  2586        type: "text",
       
  2587        label: "In"
       
  2588       },
       
  2589       end: {
       
  2590         elem: "input",
       
  2591         type: "text",
       
  2592         label: "Out"
       
  2593       },
       
  2594       onStart: {
       
  2595         elem: "input",
       
  2596         type: "function",
       
  2597         label: "onStart"
       
  2598       },
       
  2599       onFrame: {
       
  2600         elem: "input",
       
  2601         type: "function",
       
  2602         label: "onFrame"
       
  2603       },
       
  2604       onEnd: {
       
  2605         elem: "input",
       
  2606         type: "function",
       
  2607         label: "onEnd"
       
  2608       }
       
  2609     }
       
  2610   });
       
  2611 })( Popcorn );
       
  2612 var jwplayerObjects = {};
       
  2613 
       
  2614 Popcorn.player( "jwplayer", {
       
  2615   _setup: function( options ) {
       
  2616 
       
  2617     var media = this,
       
  2618         player = {},
       
  2619         container = document.createElement( "div" ),
       
  2620         currentTime = 0,
       
  2621         seekTime = 0,
       
  2622         seeking = false,
       
  2623         dataLoaded = false;
       
  2624     container.id = media.id + Popcorn.guid();
       
  2625 
       
  2626     media.appendChild( container );
       
  2627 
       
  2628   var initApi = function () {
       
  2629     jwplayer( container.id ).onTime(function() {
       
  2630         currentTime = jwplayer(container.id).getPosition();
       
  2631         media.dispatchEvent( "timeupdate" );
       
  2632        // timeout = setTimeout( timeupdate, 10 );
       
  2633     });
       
  2634     
       
  2635     media.play = function() {
       
  2636       media.paused = false;
       
  2637       media.dispatchEvent( "play" );
       
  2638 
       
  2639       media.dispatchEvent( "playing" );
       
  2640       jwplayer( container.id ).play();
       
  2641     };
       
  2642     
       
  2643     media.pause = function() {
       
  2644 
       
  2645       if ( !media.paused ) {
       
  2646         media.paused = true;
       
  2647         media.dispatchEvent( "pause" );
       
  2648         jwplayer( container.id ).pause();
       
  2649       }
       
  2650     };
       
  2651 
       
  2652     Popcorn.player.defineProperty( media, "currentTime", {
       
  2653           set: function( val ) {
       
  2654             // make sure val is a number
       
  2655             currentTime = seekTime = +val;
       
  2656             seeking = true;
       
  2657             media.dispatchEvent( "seeked" );
       
  2658             media.dispatchEvent( "timeupdate" );
       
  2659             jwplayer( container.id ).seek( currentTime );
       
  2660             return currentTime;
       
  2661           },
       
  2662           get: function() {
       
  2663             return jwplayer( container.id ).getPosition();            
       
  2664           }
       
  2665         });
       
  2666  
       
  2667     Popcorn.player.defineProperty( media, "muted", {   
       
  2668         set: function( val ) {
       
  2669           if ( jwplayer( container.id ).getMute() !== val ) {
       
  2670             if ( val ) {
       
  2671               jwplayer( container.id ).setMute(true);
       
  2672             } else {
       
  2673               jwplayer( container.id ).setMute(false);
       
  2674             }
       
  2675 
       
  2676             media.dispatchEvent( "volumechange" );
       
  2677           }
       
  2678           
       
  2679           return jwplayer( container.id ).getMute();
       
  2680         },
       
  2681         get: function() {
       
  2682           return jwplayer( container.id ).getMute();
       
  2683         }
       
  2684     });
       
  2685   
       
  2686     Popcorn.player.defineProperty( media, "volume", {
       
  2687     
       
  2688       set: function( val ) {
       
  2689 
       
  2690         if ( jwplayer( container.id ).getVolume() !== val *100 ) {
       
  2691           jwplayer( container.id ).setVolume( val * 100);
       
  2692           media.dispatchEvent( "volumechange" );
       
  2693         }
       
  2694         
       
  2695         return (jwplayer( container.id ).getVolume()) / 100;
       
  2696       },
       
  2697       
       
  2698       get: function() {
       
  2699         return jwplayer( container.id ).getVolume() / 100;
       
  2700       }
       
  2701     });
       
  2702 
       
  2703     media.readyState = 4;
       
  2704     media.dispatchEvent( 'load' );
       
  2705     dataLoaded = true;
       
  2706 
       
  2707     media.duration = options.duration;
       
  2708     media.dispatchEvent( 'durationchange' );
       
  2709 
       
  2710     media.paused && media.dispatchEvent( 'loadeddata' );
       
  2711 
       
  2712     };
       
  2713 
       
  2714     options.events = {
       
  2715         onReady: initApi
       
  2716       };
       
  2717       
       
  2718     jwplayer( container.id ).setup(options);
       
  2719 
       
  2720   }
       
  2721 });
       
  2722 
       
  2723 // PLUGIN: Mediafragment
       
  2724 
       
  2725 (function ( Popcorn ) {
       
  2726 
       
  2727   /**
       
  2728    * Mediafragment popcorn plug-in
       
  2729    * Adds (limited) support for mediafragment requests
       
  2730    * to a popcorn video.
       
  2731    * @param {Object} options
       
  2732    *
       
  2733   **/
       
  2734     Popcorn.plugin( "mediafragment" , {
       
  2735 
       
  2736       manifest: {
       
  2737         about: {
       
  2738           name: "Popcorn mediafragment plugin",
       
  2739           version: "0.1",
       
  2740           author: "Karim Hamidou",
       
  2741           website: "http://neyret.fr/~karim"
       
  2742         },
       
  2743         options: {
       
  2744         }
       
  2745       },
       
  2746 
       
  2747     _setup: function( options ) {
       
  2748         var advanceTime = function() {
       
  2749               var url = window.location.href;
       
  2750 
       
  2751               if ( url.split( "#" )[ 1 ] != null ) {
       
  2752                   pageoffset = url.split( "#" )[1];
       
  2753 
       
  2754                   if ( pageoffset.substring( 2 ) != null ) {
       
  2755                     var offsettime = pageoffset.substring( 2 );
       
  2756                     this.currentTime( parseFloat( offsettime ) );
       
  2757                   }
       
  2758               }
       
  2759         }
       
  2760 
       
  2761         var updateTime = function() {
       
  2762             var history = window.history;
       
  2763             if ( !history.pushState ) {
       
  2764               return false;
       
  2765             }
       
  2766             
       
  2767             splitArr = window.location.href.split( "#" )
       
  2768             history.replaceState( {}, "", splitArr[0] + "#t=" + this.currentTime().toFixed( 2 ) );
       
  2769         };
       
  2770 
       
  2771         this.listen( "loadedmetadata", advanceTime );
       
  2772         this.listen( "pause", updateTime );
       
  2773         this.listen( "seeked", updateTime );
       
  2774     },
       
  2775 
       
  2776     _teardown: function( options ) {
       
  2777       // FIXME: anything to implement here ?
       
  2778     }
       
  2779   });
       
  2780 })( Popcorn );
       
  2781 if(typeof jwplayer=="undefined"){var jwplayer=function(a){if(jwplayer.api){return jwplayer.api.selectPlayer(a)}};var $jw=jwplayer;jwplayer.version="5.7.1896";jwplayer.vid=document.createElement("video");jwplayer.audio=document.createElement("audio");jwplayer.source=document.createElement("source");(function(b){b.utils=function(){};b.utils.typeOf=function(d){var c=typeof d;if(c==="object"){if(d){if(d instanceof Array){c="array"}}else{c="null"}}return c};b.utils.extend=function(){var c=b.utils.extend["arguments"];if(c.length>1){for(var e=1;e<c.length;e++){for(var d in c[e]){c[0][d]=c[e][d]}}return c[0]}return null};b.utils.clone=function(f){var c;var d=b.utils.clone["arguments"];if(d.length==1){switch(b.utils.typeOf(d[0])){case"object":c={};for(var e in d[0]){c[e]=b.utils.clone(d[0][e])}break;case"array":c=[];for(var e in d[0]){c[e]=b.utils.clone(d[0][e])}break;default:return d[0];break}}return c};b.utils.extension=function(c){if(!c){return""}c=c.substring(c.lastIndexOf("/")+1,c.length);c=c.split("?")[0];if(c.lastIndexOf(".")>-1){return c.substr(c.lastIndexOf(".")+1,c.length).toLowerCase()}return};b.utils.html=function(c,d){c.innerHTML=d};b.utils.wrap=function(c,d){if(c.parentNode){c.parentNode.replaceChild(d,c)}d.appendChild(c)};b.utils.ajax=function(g,f,c){var e;if(window.XMLHttpRequest){e=new XMLHttpRequest()}else{e=new ActiveXObject("Microsoft.XMLHTTP")}e.onreadystatechange=function(){if(e.readyState===4){if(e.status===200){if(f){f(e)}}else{if(c){c(g)}}}};try{e.open("GET",g,true);e.send(null)}catch(d){if(c){c(g)}}return e};b.utils.load=function(d,e,c){d.onreadystatechange=function(){if(d.readyState===4){if(d.status===200){if(e){e()}}else{if(c){c()}}}}};b.utils.find=function(d,c){return d.getElementsByTagName(c)};b.utils.append=function(c,d){c.appendChild(d)};b.utils.isIE=function(){return((!+"\v1")||(typeof window.ActiveXObject!="undefined"))};b.utils.isLegacyAndroid=function(){var c=navigator.userAgent.toLowerCase();return(c.match(/android 2.[012]/i)!==null)};b.utils.isIOS=function(d){if(typeof d=="undefined"){d=/iP(hone|ad|od)/i}var c=navigator.userAgent.toLowerCase();return(c.match(d)!==null)};b.utils.isIPad=function(){return b.utils.isIOS(/iPad/i)};b.utils.isIPod=function(){return b.utils.isIOS(/iP(hone|od)/i)};b.utils.getFirstPlaylistItemFromConfig=function(c){var d={};var e;if(c.playlist&&c.playlist.length){e=c.playlist[0]}else{e=c}d.file=e.file;d.levels=e.levels;d.streamer=e.streamer;d.playlistfile=e.playlistfile;d.provider=e.provider;if(!d.provider){if(d.file&&(d.file.toLowerCase().indexOf("youtube.com")>-1||d.file.toLowerCase().indexOf("youtu.be")>-1)){d.provider="youtube"}if(d.streamer&&d.streamer.toLowerCase().indexOf("rtmp://")==0){d.provider="rtmp"}if(e.type){d.provider=e.type.toLowerCase()}}if(d.provider=="audio"){d.provider="sound"}return d};b.utils.getOuterHTML=function(c){if(c.outerHTML){return c.outerHTML}else{try{return new XMLSerializer().serializeToString(c)}catch(d){return""}}};b.utils.setOuterHTML=function(f,e){if(f.outerHTML){f.outerHTML=e}else{var g=document.createElement("div");g.innerHTML=e;var c=document.createRange();c.selectNodeContents(g);var d=c.extractContents();f.parentNode.insertBefore(d,f);f.parentNode.removeChild(f)}};b.utils.hasFlash=function(){if(typeof navigator.plugins!="undefined"&&typeof navigator.plugins["Shockwave Flash"]!="undefined"){return true}if(typeof window.ActiveXObject!="undefined"){try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash");return true}catch(c){}}return false};b.utils.getPluginName=function(c){if(c.lastIndexOf("/")>=0){c=c.substring(c.lastIndexOf("/")+1,c.length)}if(c.lastIndexOf("-")>=0){c=c.substring(0,c.lastIndexOf("-"))}if(c.lastIndexOf(".swf")>=0){c=c.substring(0,c.lastIndexOf(".swf"))}if(c.lastIndexOf(".js")>=0){c=c.substring(0,c.lastIndexOf(".js"))}return c};b.utils.getPluginVersion=function(c){if(c.lastIndexOf("-")>=0){if(c.lastIndexOf(".js")>=0){return c.substring(c.lastIndexOf("-")+1,c.lastIndexOf(".js"))}else{if(c.lastIndexOf(".swf")>=0){return c.substring(c.lastIndexOf("-")+1,c.lastIndexOf(".swf"))}else{return c.substring(c.lastIndexOf("-")+1)}}}return""};b.utils.getAbsolutePath=function(j,h){if(!b.utils.exists(h)){h=document.location.href}if(!b.utils.exists(j)){return undefined}if(a(j)){return j}var k=h.substring(0,h.indexOf("://")+3);var g=h.substring(k.length,h.indexOf("/",k.length+1));var d;if(j.indexOf("/")===0){d=j.split("/")}else{var e=h.split("?")[0];e=e.substring(k.length+g.length+1,e.lastIndexOf("/"));d=e.split("/").concat(j.split("/"))}var c=[];for(var f=0;f<d.length;f++){if(!d[f]||!b.utils.exists(d[f])||d[f]=="."){continue}else{if(d[f]==".."){c.pop()}else{c.push(d[f])}}}return k+g+"/"+c.join("/")};function a(d){if(!b.utils.exists(d)){return}var e=d.indexOf("://");var c=d.indexOf("?");return(e>0&&(c<0||(c>e)))}b.utils.pluginPathType={ABSOLUTE:"ABSOLUTE",RELATIVE:"RELATIVE",CDN:"CDN"};b.utils.getPluginPathType=function(d){if(typeof d!="string"){return}d=d.split("?")[0];var e=d.indexOf("://");if(e>0){return b.utils.pluginPathType.ABSOLUTE}var c=d.indexOf("/");var f=b.utils.extension(d);if(e<0&&c<0&&(!f||!isNaN(f))){return b.utils.pluginPathType.CDN}return b.utils.pluginPathType.RELATIVE};b.utils.mapEmpty=function(c){for(var d in c){return false}return true};b.utils.mapLength=function(d){var c=0;for(var e in d){c++}return c};b.utils.log=function(d,c){if(typeof console!="undefined"&&typeof console.log!="undefined"){if(c){console.log(d,c)}else{console.log(d)}}};b.utils.css=function(d,g,c){if(b.utils.exists(d)){for(var e in g){try{if(typeof g[e]==="undefined"){continue}else{if(typeof g[e]=="number"&&!(e=="zIndex"||e=="opacity")){if(isNaN(g[e])){continue}if(e.match(/color/i)){g[e]="#"+b.utils.strings.pad(g[e].toString(16),6)}else{g[e]=Math.ceil(g[e])+"px"}}}d.style[e]=g[e]}catch(f){}}}};b.utils.isYouTube=function(c){return(c.indexOf("youtube.com")>-1||c.indexOf("youtu.be")>-1)};b.utils.transform=function(c,d){c.style.webkitTransform=d;c.style.MozTransform=d;c.style.OTransform=d};b.utils.stretch=function(h,n,m,f,l,g){if(typeof m=="undefined"||typeof f=="undefined"||typeof l=="undefined"||typeof g=="undefined"){return}var d=m/l;var e=f/g;var k=0;var j=0;n.style.overflow="hidden";b.utils.transform(n,"");var c={};switch(h.toUpperCase()){case b.utils.stretching.NONE:c.width=l;c.height=g;break;case b.utils.stretching.UNIFORM:if(d>e){c.width=l*e;c.height=g*e}else{c.width=l*d;c.height=g*d}break;case b.utils.stretching.FILL:if(d>e){c.width=l*d;c.height=g*d}else{c.width=l*e;c.height=g*e}break;case b.utils.stretching.EXACTFIT:b.utils.transform(n,["scale(",d,",",e,")"," translate(0px,0px)"].join(""));c.width=l;c.height=g;break;default:break}c.top=(f-c.height)/2;c.left=(m-c.width)/2;b.utils.css(n,c)};b.utils.stretching={NONE:"NONE",FILL:"FILL",UNIFORM:"UNIFORM",EXACTFIT:"EXACTFIT"};b.utils.deepReplaceKeyName=function(h,e,c){switch(b.utils.typeOf(h)){case"array":for(var g=0;g<h.length;g++){h[g]=b.utils.deepReplaceKeyName(h[g],e,c)}break;case"object":for(var f in h){var d=f.replace(new RegExp(e,"g"),c);h[d]=b.utils.deepReplaceKeyName(h[f],e,c);if(f!=d){delete h[f]}}break}return h};b.utils.isInArray=function(e,d){if(!(e)||!(e instanceof Array)){return false}for(var c=0;c<e.length;c++){if(d===e[c]){return true}}return false};b.utils.exists=function(c){switch(typeof(c)){case"string":return(c.length>0);break;case"object":return(c!==null);case"undefined":return false}return true};b.utils.empty=function(c){if(typeof c.hasChildNodes=="function"){while(c.hasChildNodes()){c.removeChild(c.firstChild)}}};b.utils.parseDimension=function(c){if(typeof c=="string"){if(c===""){return 0}else{if(c.lastIndexOf("%")>-1){return c}else{return parseInt(c.replace("px",""),10)}}}return c};b.utils.getDimensions=function(c){if(c&&c.style){return{x:b.utils.parseDimension(c.style.left),y:b.utils.parseDimension(c.style.top),width:b.utils.parseDimension(c.style.width),height:b.utils.parseDimension(c.style.height)}}else{return{}}};b.utils.timeFormat=function(c){str="00:00";if(c>0){str=Math.floor(c/60)<10?"0"+Math.floor(c/60)+":":Math.floor(c/60)+":";str+=Math.floor(c%60)<10?"0"+Math.floor(c%60):Math.floor(c%60)}return str}})(jwplayer);(function(a){a.events=function(){};a.events.COMPLETE="COMPLETE";a.events.ERROR="ERROR"})(jwplayer);(function(jwplayer){jwplayer.events.eventdispatcher=function(debug){var _debug=debug;var _listeners;var _globallisteners;this.resetEventListeners=function(){_listeners={};_globallisteners=[]};this.resetEventListeners();this.addEventListener=function(type,listener,count){try{if(!jwplayer.utils.exists(_listeners[type])){_listeners[type]=[]}if(typeof(listener)=="string"){eval("listener = "+listener)}_listeners[type].push({listener:listener,count:count})}catch(err){jwplayer.utils.log("error",err)}return false};this.removeEventListener=function(type,listener){if(!_listeners[type]){return}try{for(var listenerIndex=0;listenerIndex<_listeners[type].length;listenerIndex++){if(_listeners[type][listenerIndex].listener.toString()==listener.toString()){_listeners[type].splice(listenerIndex,1);break}}}catch(err){jwplayer.utils.log("error",err)}return false};this.addGlobalListener=function(listener,count){try{if(typeof(listener)=="string"){eval("listener = "+listener)}_globallisteners.push({listener:listener,count:count})}catch(err){jwplayer.utils.log("error",err)}return false};this.removeGlobalListener=function(listener){if(!_globallisteners[type]){return}try{for(var globalListenerIndex=0;globalListenerIndex<_globallisteners.length;globalListenerIndex++){if(_globallisteners[globalListenerIndex].listener.toString()==listener.toString()){_globallisteners.splice(globalListenerIndex,1);break}}}catch(err){jwplayer.utils.log("error",err)}return false};this.sendEvent=function(type,data){if(!jwplayer.utils.exists(data)){data={}}if(_debug){jwplayer.utils.log(type,data)}if(typeof _listeners[type]!="undefined"){for(var listenerIndex=0;listenerIndex<_listeners[type].length;listenerIndex++){try{_listeners[type][listenerIndex].listener(data)}catch(err){jwplayer.utils.log("There was an error while handling a listener: "+err.toString(),_listeners[type][listenerIndex].listener)}if(_listeners[type][listenerIndex]){if(_listeners[type][listenerIndex].count===1){delete _listeners[type][listenerIndex]}else{if(_listeners[type][listenerIndex].count>0){_listeners[type][listenerIndex].count=_listeners[type][listenerIndex].count-1}}}}}for(var globalListenerIndex=0;globalListenerIndex<_globallisteners.length;globalListenerIndex++){try{_globallisteners[globalListenerIndex].listener(data)}catch(err){jwplayer.utils.log("There was an error while handling a listener: "+err.toString(),_globallisteners[globalListenerIndex].listener)}if(_globallisteners[globalListenerIndex]){if(_globallisteners[globalListenerIndex].count===1){delete _globallisteners[globalListenerIndex]}else{if(_globallisteners[globalListenerIndex].count>0){_globallisteners[globalListenerIndex].count=_globallisteners[globalListenerIndex].count-1}}}}}}})(jwplayer);(function(a){var b={};a.utils.animations=function(){};a.utils.animations.transform=function(c,d){c.style.webkitTransform=d;c.style.MozTransform=d;c.style.OTransform=d;c.style.msTransform=d};a.utils.animations.transformOrigin=function(c,d){c.style.webkitTransformOrigin=d;c.style.MozTransformOrigin=d;c.style.OTransformOrigin=d;c.style.msTransformOrigin=d};a.utils.animations.rotate=function(c,d){a.utils.animations.transform(c,["rotate(",d,"deg)"].join(""))};a.utils.cancelAnimation=function(c){delete b[c.id]};a.utils.fadeTo=function(m,f,e,j,h,d){if(b[m.id]!=d&&a.utils.exists(d)){return}if(m.style.opacity==f){return}var c=new Date().getTime();if(d>c){setTimeout(function(){a.utils.fadeTo(m,f,e,j,0,d)},d-c)}if(m.style.display=="none"){m.style.display="block"}if(!a.utils.exists(j)){j=m.style.opacity===""?1:m.style.opacity}if(m.style.opacity==f&&m.style.opacity!==""&&a.utils.exists(d)){if(f===0){m.style.display="none"}return}if(!a.utils.exists(d)){d=c;b[m.id]=d}if(!a.utils.exists(h)){h=0}var k=(e>0)?((c-d)/(e*1000)):0;k=k>1?1:k;var l=f-j;var g=j+(k*l);if(g>1){g=1}else{if(g<0){g=0}}m.style.opacity=g;if(h>0){b[m.id]=d+h*1000;a.utils.fadeTo(m,f,e,j,0,b[m.id]);return}setTimeout(function(){a.utils.fadeTo(m,f,e,j,0,d)},10)}})(jwplayer);(function(a){a.utils.arrays=function(){};a.utils.arrays.indexOf=function(c,d){for(var b=0;b<c.length;b++){if(c[b]==d){return b}}return -1};a.utils.arrays.remove=function(c,d){var b=a.utils.arrays.indexOf(c,d);if(b>-1){c.splice(b,1)}}})(jwplayer);(function(a){a.utils.extensionmap={"3gp":{html5:"video/3gpp",flash:"video"},"3gpp":{html5:"video/3gpp"},"3g2":{html5:"video/3gpp2",flash:"video"},"3gpp2":{html5:"video/3gpp2"},flv:{flash:"video"},f4a:{html5:"audio/mp4"},f4b:{html5:"audio/mp4",flash:"video"},f4v:{html5:"video/mp4",flash:"video"},mov:{html5:"video/quicktime",flash:"video"},m4a:{html5:"audio/mp4",flash:"video"},m4b:{html5:"audio/mp4"},m4p:{html5:"audio/mp4"},m4v:{html5:"video/mp4",flash:"video"},mp4:{html5:"video/mp4",flash:"video"},rbs:{flash:"sound"},aac:{html5:"audio/aac",flash:"video"},mp3:{html5:"audio/mp3",flash:"sound"},ogg:{html5:"audio/ogg"},oga:{html5:"audio/ogg"},ogv:{html5:"video/ogg"},webm:{html5:"video/webm"},m3u8:{html5:"audio/x-mpegurl"},gif:{flash:"image"},jpeg:{flash:"image"},jpg:{flash:"image"},swf:{flash:"image"},png:{flash:"image"},wav:{html5:"audio/x-wav"}}})(jwplayer);(function(e){e.utils.mediaparser=function(){};var g={element:{width:"width",height:"height",id:"id","class":"className",name:"name"},media:{src:"file",preload:"preload",autoplay:"autostart",loop:"repeat",controls:"controls"},source:{src:"file",type:"type",media:"media","data-jw-width":"width","data-jw-bitrate":"bitrate"},video:{poster:"image"}};var f={};e.utils.mediaparser.parseMedia=function(j){return d(j)};function c(k,j){if(!e.utils.exists(j)){j=g[k]}else{e.utils.extend(j,g[k])}return j}function d(n,j){if(f[n.tagName.toLowerCase()]&&!e.utils.exists(j)){return f[n.tagName.toLowerCase()](n)}else{j=c("element",j);var o={};for(var k in j){if(k!="length"){var m=n.getAttribute(k);if(e.utils.exists(m)){o[j[k]]=m}}}var l=n.style["#background-color"];if(l&&!(l=="transparent"||l=="rgba(0, 0, 0, 0)")){o.screencolor=l}return o}}function h(n,k){k=c("media",k);var l=[];var j=e.utils.selectors("source",n);for(var m in j){if(!isNaN(m)){l.push(a(j[m]))}}var o=d(n,k);if(e.utils.exists(o.file)){l[0]={file:o.file}}o.levels=l;return o}function a(l,k){k=c("source",k);var j=d(l,k);j.width=j.width?j.width:0;j.bitrate=j.bitrate?j.bitrate:0;return j}function b(l,k){k=c("video",k);var j=h(l,k);return j}f.media=h;f.audio=h;f.source=a;f.video=b})(jwplayer);(function(a){a.utils.loaderstatus={NEW:"NEW",LOADING:"LOADING",ERROR:"ERROR",COMPLETE:"COMPLETE"};a.utils.scriptloader=function(c){var d=a.utils.loaderstatus.NEW;var b=new a.events.eventdispatcher();a.utils.extend(this,b);this.load=function(){if(d==a.utils.loaderstatus.NEW){d=a.utils.loaderstatus.LOADING;var e=document.createElement("script");e.onload=function(f){d=a.utils.loaderstatus.COMPLETE;b.sendEvent(a.events.COMPLETE)};e.onerror=function(f){d=a.utils.loaderstatus.ERROR;b.sendEvent(a.events.ERROR)};e.onreadystatechange=function(){if(e.readyState=="loaded"||e.readyState=="complete"){d=a.utils.loaderstatus.COMPLETE;b.sendEvent(a.events.COMPLETE)}};document.getElementsByTagName("head")[0].appendChild(e);e.src=c}};this.getStatus=function(){return d}}})(jwplayer);(function(a){a.utils.selectors=function(b,e){if(!a.utils.exists(e)){e=document}b=a.utils.strings.trim(b);var c=b.charAt(0);if(c=="#"){return e.getElementById(b.substr(1))}else{if(c=="."){if(e.getElementsByClassName){return e.getElementsByClassName(b.substr(1))}else{return a.utils.selectors.getElementsByTagAndClass("*",b.substr(1))}}else{if(b.indexOf(".")>0){var d=b.split(".");return a.utils.selectors.getElementsByTagAndClass(d[0],d[1])}else{return e.getElementsByTagName(b)}}}return null};a.utils.selectors.getElementsByTagAndClass=function(e,h,g){var j=[];if(!a.utils.exists(g)){g=document}var f=g.getElementsByTagName(e);for(var d=0;d<f.length;d++){if(a.utils.exists(f[d].className)){var c=f[d].className.split(" ");for(var b=0;b<c.length;b++){if(c[b]==h){j.push(f[d])}}}}return j}})(jwplayer);(function(a){a.utils.strings=function(){};a.utils.strings.trim=function(b){return b.replace(/^\s*/,"").replace(/\s*$/,"")};a.utils.strings.pad=function(c,d,b){if(!b){b="0"}while(c.length<d){c=b+c}return c};a.utils.strings.serialize=function(b){if(b==null){return null}else{if(b=="true"){return true}else{if(b=="false"){return false}else{if(isNaN(Number(b))||b.length>5||b.length==0){return b}else{return Number(b)}}}}};a.utils.strings.seconds=function(d){d=d.replace(",",".");var b=d.split(":");var c=0;if(d.substr(-1)=="s"){c=Number(d.substr(0,d.length-1))}else{if(d.substr(-1)=="m"){c=Number(d.substr(0,d.length-1))*60}else{if(d.substr(-1)=="h"){c=Number(d.substr(0,d.length-1))*3600}else{if(b.length>1){c=Number(b[b.length-1]);c+=Number(b[b.length-2])*60;if(b.length==3){c+=Number(b[b.length-3])*3600}}else{c=Number(d)}}}}return c};a.utils.strings.xmlAttribute=function(b,c){for(var d=0;d<b.attributes.length;d++){if(b.attributes[d].name&&b.attributes[d].name.toLowerCase()==c.toLowerCase()){return b.attributes[d].value.toString()}}return""};a.utils.strings.jsonToString=function(f){var h=h||{};if(h&&h.stringify){return h.stringify(f)}var c=typeof(f);if(c!="object"||f===null){if(c=="string"){f='"'+f+'"'}else{return String(f)}}else{var g=[],b=(f&&f.constructor==Array);for(var d in f){var e=f[d];switch(typeof(e)){case"string":e='"'+e+'"';break;case"object":if(a.utils.exists(e)){e=a.utils.strings.jsonToString(e)}break}if(b){if(typeof(e)!="function"){g.push(String(e))}}else{if(typeof(e)!="function"){g.push('"'+d+'":'+String(e))}}}if(b){return"["+String(g)+"]"}else{return"{"+String(g)+"}"}}}})(jwplayer);(function(c){var d=new RegExp(/^(#|0x)[0-9a-fA-F]{3,6}/);c.utils.typechecker=function(g,f){f=!c.utils.exists(f)?b(g):f;return e(g,f)};function b(f){var g=["true","false","t","f"];if(g.toString().indexOf(f.toLowerCase().replace(" ",""))>=0){return"boolean"}else{if(d.test(f)){return"color"}else{if(!isNaN(parseInt(f,10))&&parseInt(f,10).toString().length==f.length){return"integer"}else{if(!isNaN(parseFloat(f))&&parseFloat(f).toString().length==f.length){return"float"}}}}return"string"}function e(g,f){if(!c.utils.exists(f)){return g}switch(f){case"color":if(g.length>0){return a(g)}return null;case"integer":return parseInt(g,10);case"float":return parseFloat(g);case"boolean":if(g.toLowerCase()=="true"){return true}else{if(g=="1"){return true}}return false}return g}function a(f){switch(f.toLowerCase()){case"blue":return parseInt("0000FF",16);case"green":return parseInt("00FF00",16);case"red":return parseInt("FF0000",16);case"cyan":return parseInt("00FFFF",16);case"magenta":return parseInt("FF00FF",16);case"yellow":return parseInt("FFFF00",16);case"black":return parseInt("000000",16);case"white":return parseInt("FFFFFF",16);default:f=f.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");if(f.length==3){f=f.charAt(0)+f.charAt(0)+f.charAt(1)+f.charAt(1)+f.charAt(2)+f.charAt(2)}return parseInt(f,16)}return parseInt("000000",16)}})(jwplayer);(function(a){a.utils.parsers=function(){};a.utils.parsers.localName=function(b){if(!b){return""}else{if(b.localName){return b.localName}else{if(b.baseName){return b.baseName}else{return""}}}};a.utils.parsers.textContent=function(b){if(!b){return""}else{if(b.textContent){return b.textContent}else{if(b.text){return b.text}else{return""}}}}})(jwplayer);(function(a){a.utils.parsers.jwparser=function(){};a.utils.parsers.jwparser.PREFIX="jwplayer";a.utils.parsers.jwparser.parseEntry=function(c,d){for(var b=0;b<c.childNodes.length;b++){if(c.childNodes[b].prefix==a.utils.parsers.jwparser.PREFIX){d[a.utils.parsers.localName(c.childNodes[b])]=a.utils.strings.serialize(a.utils.parsers.textContent(c.childNodes[b]))}if(!d.file&&String(d.link).toLowerCase().indexOf("youtube")>-1){d.file=d.link}}return d};a.utils.parsers.jwparser.getProvider=function(c){if(c.type){return c.type}else{if(c.file.indexOf("youtube.com/w")>-1||c.file.indexOf("youtube.com/v")>-1||c.file.indexOf("youtu.be/")>-1){return"youtube"}else{if(c.streamer&&c.streamer.indexOf("rtmp")==0){return"rtmp"}else{if(c.streamer&&c.streamer.indexOf("http")==0){return"http"}else{var b=a.utils.strings.extension(c.file);if(extensions.hasOwnProperty(b)){return extensions[b]}}}}}return""}})(jwplayer);(function(a){a.utils.parsers.mediaparser=function(){};a.utils.parsers.mediaparser.PREFIX="media";a.utils.parsers.mediaparser.parseGroup=function(d,f){var e=false;for(var c=0;c<d.childNodes.length;c++){if(d.childNodes[c].prefix==a.utils.parsers.mediaparser.PREFIX){if(!a.utils.parsers.localName(d.childNodes[c])){continue}switch(a.utils.parsers.localName(d.childNodes[c]).toLowerCase()){case"content":if(!e){f.file=a.utils.strings.xmlAttribute(d.childNodes[c],"url")}if(a.utils.strings.xmlAttribute(d.childNodes[c],"duration")){f.duration=a.utils.strings.seconds(a.utils.strings.xmlAttribute(d.childNodes[c],"duration"))}if(a.utils.strings.xmlAttribute(d.childNodes[c],"start")){f.start=a.utils.strings.seconds(a.utils.strings.xmlAttribute(d.childNodes[c],"start"))}if(d.childNodes[c].childNodes&&d.childNodes[c].childNodes.length>0){f=a.utils.parsers.mediaparser.parseGroup(d.childNodes[c],f)}if(a.utils.strings.xmlAttribute(d.childNodes[c],"width")||a.utils.strings.xmlAttribute(d.childNodes[c],"bitrate")||a.utils.strings.xmlAttribute(d.childNodes[c],"url")){if(!f.levels){f.levels=[]}f.levels.push({width:a.utils.strings.xmlAttribute(d.childNodes[c],"width"),bitrate:a.utils.strings.xmlAttribute(d.childNodes[c],"bitrate"),file:a.utils.strings.xmlAttribute(d.childNodes[c],"url")})}break;case"title":f.title=a.utils.parsers.textContent(d.childNodes[c]);break;case"description":f.description=a.utils.parsers.textContent(d.childNodes[c]);break;case"keywords":f.tags=a.utils.parsers.textContent(d.childNodes[c]);break;case"thumbnail":f.image=a.utils.strings.xmlAttribute(d.childNodes[c],"url");break;case"credit":f.author=a.utils.parsers.textContent(d.childNodes[c]);break;case"player":var b=d.childNodes[c].url;if(b.indexOf("youtube.com")>=0||b.indexOf("youtu.be")>=0){e=true;f.file=a.utils.strings.xmlAttribute(d.childNodes[c],"url")}break;case"group":a.utils.parsers.mediaparser.parseGroup(d.childNodes[c],f);break}}}return f}})(jwplayer);(function(b){b.utils.parsers.rssparser=function(){};b.utils.parsers.rssparser.parse=function(f){var c=[];for(var e=0;e<f.childNodes.length;e++){if(b.utils.parsers.localName(f.childNodes[e]).toLowerCase()=="channel"){for(var d=0;d<f.childNodes[e].childNodes.length;d++){if(b.utils.parsers.localName(f.childNodes[e].childNodes[d]).toLowerCase()=="item"){c.push(a(f.childNodes[e].childNodes[d]))}}}}return c};function a(d){var e={};for(var c=0;c<d.childNodes.length;c++){if(!b.utils.parsers.localName(d.childNodes[c])){continue}switch(b.utils.parsers.localName(d.childNodes[c]).toLowerCase()){case"enclosure":e.file=b.utils.strings.xmlAttribute(d.childNodes[c],"url");break;case"title":e.title=b.utils.parsers.textContent(d.childNodes[c]);break;case"pubdate":e.date=b.utils.parsers.textContent(d.childNodes[c]);break;case"description":e.description=b.utils.parsers.textContent(d.childNodes[c]);break;case"link":e.link=b.utils.parsers.textContent(d.childNodes[c]);break;case"category":if(e.tags){e.tags+=b.utils.parsers.textContent(d.childNodes[c])}else{e.tags=b.utils.parsers.textContent(d.childNodes[c])}break}}e=b.utils.parsers.mediaparser.parseGroup(d,e);e=b.utils.parsers.jwparser.parseEntry(d,e);return new b.html5.playlistitem(e)}})(jwplayer);(function(a){var c={};var b={};a.plugins=function(){};a.plugins.loadPlugins=function(e,d){b[e]=new a.plugins.pluginloader(new a.plugins.model(c),d);return b[e]};a.plugins.registerPlugin=function(h,f,e){var d=a.utils.getPluginName(h);if(c[d]){c[d].registerPlugin(h,f,e)}else{a.utils.log("A plugin ("+h+") was registered with the player that was not loaded. Please check your configuration.");for(var g in b){b[g].pluginFailed()}}}})(jwplayer);(function(a){a.plugins.model=function(b){this.addPlugin=function(c){var d=a.utils.getPluginName(c);if(!b[d]){b[d]=new a.plugins.plugin(c)}return b[d]}}})(jwplayer);(function(a){a.plugins.pluginmodes={FLASH:"FLASH",JAVASCRIPT:"JAVASCRIPT",HYBRID:"HYBRID"};a.plugins.plugin=function(b){var d="http://plugins.longtailvideo.com";var j=a.utils.loaderstatus.NEW;var k;var h;var l;var c=new a.events.eventdispatcher();a.utils.extend(this,c);function e(){switch(a.utils.getPluginPathType(b)){case a.utils.pluginPathType.ABSOLUTE:return b;case a.utils.pluginPathType.RELATIVE:return a.utils.getAbsolutePath(b,window.location.href);case a.utils.pluginPathType.CDN:var n=a.utils.getPluginName(b);var m=a.utils.getPluginVersion(b);return d+"/"+a.version.split(".")[0]+"/"+n+"/"+n+(m!==""?("-"+m):"")+".js"}}function g(m){l=setTimeout(function(){j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)},1000)}function f(m){j=a.utils.loaderstatus.ERROR;c.sendEvent(a.events.ERROR)}this.load=function(){if(j==a.utils.loaderstatus.NEW){if(b.lastIndexOf(".swf")>0){k=b;j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE);return}j=a.utils.loaderstatus.LOADING;var m=new a.utils.scriptloader(e());m.addEventListener(a.events.COMPLETE,g);m.addEventListener(a.events.ERROR,f);m.load()}};this.registerPlugin=function(o,n,m){if(l){clearTimeout(l);l=undefined}if(n&&m){k=m;h=n}else{if(typeof n=="string"){k=n}else{if(typeof n=="function"){h=n}else{if(!n&&!m){k=o}}}}j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)};this.getStatus=function(){return j};this.getPluginName=function(){return a.utils.getPluginName(b)};this.getFlashPath=function(){if(k){switch(a.utils.getPluginPathType(k)){case a.utils.pluginPathType.ABSOLUTE:return k;case a.utils.pluginPathType.RELATIVE:if(b.lastIndexOf(".swf")>0){return a.utils.getAbsolutePath(k,window.location.href)}return a.utils.getAbsolutePath(k,e());case a.utils.pluginPathType.CDN:if(k.indexOf("-")>-1){return k+"h"}return k+"-h"}}return null};this.getJS=function(){return h};this.getPluginmode=function(){if(typeof k!="undefined"&&typeof h!="undefined"){return a.plugins.pluginmodes.HYBRID}else{if(typeof k!="undefined"){return a.plugins.pluginmodes.FLASH}else{if(typeof h!="undefined"){return a.plugins.pluginmodes.JAVASCRIPT}}}};this.getNewInstance=function(n,m,o){return new h(n,m,o)};this.getURL=function(){return b}}})(jwplayer);(function(a){a.plugins.pluginloader=function(h,e){var g={};var k=a.utils.loaderstatus.NEW;var d=false;var b=false;var c=new a.events.eventdispatcher();a.utils.extend(this,c);function f(){if(!b){b=true;k=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)}}function j(){if(!b){var m=0;for(plugin in g){var l=g[plugin].getStatus();if(l==a.utils.loaderstatus.LOADING||l==a.utils.loaderstatus.NEW){m++}}if(m==0){f()}}}this.setupPlugins=function(n,l,s){var m={length:0,plugins:{}};var p={length:0,plugins:{}};for(var o in g){var q=g[o].getPluginName();if(g[o].getFlashPath()){m.plugins[g[o].getFlashPath()]=l.plugins[o];m.plugins[g[o].getFlashPath()].pluginmode=g[o].getPluginmode();m.length++}if(g[o].getJS()){var r=document.createElement("div");r.id=n.id+"_"+q;r.style.position="absolute";r.style.zIndex=p.length+10;p.plugins[q]=g[o].getNewInstance(n,l.plugins[o],r);p.length++;if(typeof p.plugins[q].resize!="undefined"){n.onReady(s(p.plugins[q],r,true));n.onResize(s(p.plugins[q],r))}}}n.plugins=p.plugins;return m};this.load=function(){k=a.utils.loaderstatus.LOADING;d=true;for(var l in e){if(a.utils.exists(l)){g[l]=h.addPlugin(l);g[l].addEventListener(a.events.COMPLETE,j);g[l].addEventListener(a.events.ERROR,j)}}for(l in g){g[l].load()}d=false;j()};this.pluginFailed=function(){f()};this.getStatus=function(){return k}}})(jwplayer);(function(b){var a=[];b.api=function(d){this.container=d;this.id=d.id;var n={};var s={};var q={};var c=[];var h=undefined;var l=false;var j=[];var p=b.utils.getOuterHTML(d);var r={};var k={};this.getBuffer=function(){return this.callInternal("jwGetBuffer")};this.getContainer=function(){return this.container};function e(u,t){return function(z,v,w,x){if(u.renderingMode=="flash"||u.renderingMode=="html5"){var y;if(v){k[z]=v;y="jwplayer('"+u.id+"').callback('"+z+"')"}else{if(!v&&k[z]){delete k[z]}}h.jwDockSetButton(z,y,w,x)}return t}}this.getPlugin=function(t){var v=this;var u={};if(t=="dock"){return b.utils.extend(u,{setButton:e(v,u),show:function(){v.callInternal("jwDockShow");return u},hide:function(){v.callInternal("jwDockHide");return u},onShow:function(w){v.componentListener("dock",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("dock",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{if(t=="controlbar"){return b.utils.extend(u,{show:function(){v.callInternal("jwControlbarShow");return u},hide:function(){v.callInternal("jwControlbarHide");return u},onShow:function(w){v.componentListener("controlbar",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("controlbar",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{if(t=="display"){return b.utils.extend(u,{show:function(){v.callInternal("jwDisplayShow");return u},hide:function(){v.callInternal("jwDisplayHide");return u},onShow:function(w){v.componentListener("display",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("display",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{return this.plugins[t]}}}};this.callback=function(t){if(k[t]){return k[t]()}};this.getDuration=function(){return this.callInternal("jwGetDuration")};this.getFullscreen=function(){return this.callInternal("jwGetFullscreen")};this.getHeight=function(){return this.callInternal("jwGetHeight")};this.getLockState=function(){return this.callInternal("jwGetLockState")};this.getMeta=function(){return this.getItemMeta()};this.getMute=function(){return this.callInternal("jwGetMute")};this.getPlaylist=function(){var u=this.callInternal("jwGetPlaylist");if(this.renderingMode=="flash"){b.utils.deepReplaceKeyName(u,"__dot__",".")}for(var t=0;t<u.length;t++){if(!b.utils.exists(u[t].index)){u[t].index=t}}return u};this.getPlaylistItem=function(t){if(!b.utils.exists(t)){t=this.getCurrentItem()}return this.getPlaylist()[t]};this.getPosition=function(){return this.callInternal("jwGetPosition")};this.getRenderingMode=function(){return this.renderingMode};this.getState=function(){return this.callInternal("jwGetState")};this.getVolume=function(){return this.callInternal("jwGetVolume")};this.getWidth=function(){return this.callInternal("jwGetWidth")};this.setFullscreen=function(t){if(!b.utils.exists(t)){this.callInternal("jwSetFullscreen",!this.callInternal("jwGetFullscreen"))}else{this.callInternal("jwSetFullscreen",t)}return this};this.setMute=function(t){if(!b.utils.exists(t)){this.callInternal("jwSetMute",!this.callInternal("jwGetMute"))}else{this.callInternal("jwSetMute",t)}return this};this.lock=function(){return this};this.unlock=function(){return this};this.load=function(t){this.callInternal("jwLoad",t);return this};this.playlistItem=function(t){this.callInternal("jwPlaylistItem",t);return this};this.playlistPrev=function(){this.callInternal("jwPlaylistPrev");return this};this.playlistNext=function(){this.callInternal("jwPlaylistNext");return this};this.resize=function(u,t){if(this.renderingMode=="html5"){h.jwResize(u,t)}else{this.container.width=u;this.container.height=t}return this};this.play=function(t){if(typeof t=="undefined"){t=this.getState();if(t==b.api.events.state.PLAYING||t==b.api.events.state.BUFFERING){this.callInternal("jwPause")}else{this.callInternal("jwPlay")}}else{this.callInternal("jwPlay",t)}return this};this.pause=function(t){if(typeof t=="undefined"){t=this.getState();if(t==b.api.events.state.PLAYING||t==b.api.events.state.BUFFERING){this.callInternal("jwPause")}else{this.callInternal("jwPlay")}}else{this.callInternal("jwPause",t)}return this};this.stop=function(){this.callInternal("jwStop");return this};this.seek=function(t){this.callInternal("jwSeek",t);return this};this.setVolume=function(t){this.callInternal("jwSetVolume",t);return this};this.onBufferChange=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_BUFFER,t)};this.onBufferFull=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_BUFFER_FULL,t)};this.onError=function(t){return this.eventListener(b.api.events.JWPLAYER_ERROR,t)};this.onFullscreen=function(t){return this.eventListener(b.api.events.JWPLAYER_FULLSCREEN,t)};this.onMeta=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_META,t)};this.onMute=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_MUTE,t)};this.onPlaylist=function(t){return this.eventListener(b.api.events.JWPLAYER_PLAYLIST_LOADED,t)};this.onPlaylistItem=function(t){return this.eventListener(b.api.events.JWPLAYER_PLAYLIST_ITEM,t)};this.onReady=function(t){return this.eventListener(b.api.events.API_READY,t)};this.onResize=function(t){return this.eventListener(b.api.events.JWPLAYER_RESIZE,t)};this.onComplete=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_COMPLETE,t)};this.onSeek=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_SEEK,t)};this.onTime=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_TIME,t)};this.onVolume=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_VOLUME,t)};this.onBuffer=function(t){return this.stateListener(b.api.events.state.BUFFERING,t)};this.onPause=function(t){return this.stateListener(b.api.events.state.PAUSED,t)};this.onPlay=function(t){return this.stateListener(b.api.events.state.PLAYING,t)};this.onIdle=function(t){return this.stateListener(b.api.events.state.IDLE,t)};this.remove=function(){n={};j=[];if(b.utils.getOuterHTML(this.container)!=p){b.api.destroyPlayer(this.id,p)}};this.setup=function(u){if(b.embed){var t=this.id;this.remove();var v=b(t);v.config=u;return new b.embed(v)}return this};this.registerPlugin=function(v,u,t){b.plugins.registerPlugin(v,u,t)};this.setPlayer=function(t,u){h=t;this.renderingMode=u};this.stateListener=function(t,u){if(!s[t]){s[t]=[];this.eventListener(b.api.events.JWPLAYER_PLAYER_STATE,g(t))}s[t].push(u);return this};function g(t){return function(v){var u=v.newstate,x=v.oldstate;if(u==t){var w=s[u];if(w){for(var y=0;y<w.length;y++){if(typeof w[y]=="function"){w[y].call(this,{oldstate:x,newstate:u})}}}}}}this.componentListener=function(t,u,v){if(!q[t]){q[t]={}}if(!q[t][u]){q[t][u]=[];this.eventListener(u,m(t,u))}q[t][u].push(v);return this};function m(t,u){return function(w){if(t==w.component){var v=q[t][u];if(v){for(var x=0;x<v.length;x++){if(typeof v[x]=="function"){v[x].call(this,w)}}}}}}this.addInternalListener=function(t,u){t.jwAddEventListener(u,'function(dat) { jwplayer("'+this.id+'").dispatchEvent("'+u+'", dat); }')};this.eventListener=function(t,u){if(!n[t]){n[t]=[];if(h&&l){this.addInternalListener(h,t)}}n[t].push(u);return this};this.dispatchEvent=function(v){if(n[v]){var u=f(v,arguments[1]);for(var t=0;t<n[v].length;t++){if(typeof n[v][t]=="function"){n[v][t].call(this,u)}}}};function f(v,t){var x=b.utils.extend({},t);if(v==b.api.events.JWPLAYER_FULLSCREEN&&!x.fullscreen){x.fullscreen=x.message=="true"?true:false;delete x.message}else{if(typeof x.data=="object"){x=b.utils.extend(x,x.data);delete x.data}}var u=["position","duration","offset"];for(var w in u){if(x[u[w]]){x[u[w]]=Math.round(x[u[w]]*1000)/1000}}return x}this.callInternal=function(u,t){if(l){if(typeof h!="undefined"&&typeof h[u]=="function"){if(b.utils.exists(t)){return(h[u])(t)}else{return(h[u])()}}return null}else{j.push({method:u,parameters:t})}};this.playerReady=function(v){l=true;if(!h){this.setPlayer(document.getElementById(v.id))}this.container=document.getElementById(this.id);for(var t in n){this.addInternalListener(h,t)}this.eventListener(b.api.events.JWPLAYER_PLAYLIST_ITEM,function(w){r={}});this.eventListener(b.api.events.JWPLAYER_MEDIA_META,function(w){b.utils.extend(r,w.metadata)});this.dispatchEvent(b.api.events.API_READY);while(j.length>0){var u=j.shift();this.callInternal(u.method,u.parameters)}};this.getItemMeta=function(){return r};this.getCurrentItem=function(){return this.callInternal("jwGetPlaylistIndex")};function o(v,x,w){var t=[];if(!x){x=0}if(!w){w=v.length-1}for(var u=x;u<=w;u++){t.push(v[u])}return t}return this};b.api.selectPlayer=function(d){var c;if(!b.utils.exists(d)){d=0}if(d.nodeType){c=d}else{if(typeof d=="string"){c=document.getElementById(d)}}if(c){var e=b.api.playerById(c.id);if(e){return e}else{return b.api.addPlayer(new b.api(c))}}else{if(typeof d=="number"){return b.getPlayers()[d]}}return null};b.api.events={API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem"};b.api.events.state={BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"};b.api.playerById=function(d){for(var c=0;c<a.length;c++){if(a[c].id==d){return a[c]}}return null};b.api.addPlayer=function(c){for(var d=0;d<a.length;d++){if(a[d]==c){return c}}a.push(c);return c};b.api.destroyPlayer=function(g,d){var f=-1;for(var j=0;j<a.length;j++){if(a[j].id==g){f=j;continue}}if(f>=0){var c=document.getElementById(a[f].id);if(document.getElementById(a[f].id+"_wrapper")){c=document.getElementById(a[f].id+"_wrapper")}if(c){if(d){b.utils.setOuterHTML(c,d)}else{var h=document.createElement("div");var e=c.id;if(c.id.indexOf("_wrapper")==c.id.length-8){newID=c.id.substring(0,c.id.length-8)}h.setAttribute("id",e);c.parentNode.replaceChild(h,c)}}a.splice(f,1)}return null};b.getPlayers=function(){return a.slice(0)}})(jwplayer);var _userPlayerReady=(typeof playerReady=="function")?playerReady:undefined;playerReady=function(b){var a=jwplayer.api.playerById(b.id);if(a){a.playerReady(b)}else{jwplayer.api.selectPlayer(b.id).playerReady(b)}if(_userPlayerReady){_userPlayerReady.call(this,b)}};(function(a){a.embed=function(g){var j={width:400,height:300,components:{controlbar:{position:"over"}}};var f=a.utils.mediaparser.parseMedia(g.container);var e=new a.embed.config(a.utils.extend(j,f,g.config),this);var h=a.plugins.loadPlugins(g.id,e.plugins);function c(m,l){for(var k in l){if(typeof m[k]=="function"){(m[k]).call(m,l[k])}}}function d(){if(h.getStatus()==a.utils.loaderstatus.COMPLETE){for(var m=0;m<e.modes.length;m++){if(e.modes[m].type&&a.embed[e.modes[m].type]){var k=e;if(e.modes[m].config){k=a.utils.extend(a.utils.clone(e),e.modes[m].config)}var l=new a.embed[e.modes[m].type](document.getElementById(g.id),e.modes[m],k,h,g);if(l.supportsConfig()){l.embed();c(g,e.events);return g}}}a.utils.log("No suitable players found");new a.embed.logo(a.utils.extend({hide:true},e.components.logo),"none",g.id)}}h.addEventListener(a.events.COMPLETE,d);h.addEventListener(a.events.ERROR,d);h.load();return g};function b(){if(!document.body){return setTimeout(b,15)}var c=a.utils.selectors.getElementsByTagAndClass("video","jwplayer");for(var d=0;d<c.length;d++){var e=c[d];a(e.id).setup({})}}b()})(jwplayer);(function(e){function h(){return[{type:"flash",src:"/jwplayer/player.swf"},{type:"html5"},{type:"download"}]}var a={players:"modes",autoplay:"autostart"};function b(n){var m=n.toLowerCase();var l=["left","right","top","bottom"];for(var k=0;k<l.length;k++){if(m==l[k]){return true}}return false}function c(l){var k=false;k=(l instanceof Array)||(typeof l=="object"&&!l.position&&!l.size);return k}function j(k){if(typeof k=="string"){if(parseInt(k).toString()==k||k.toLowerCase().indexOf("px")>-1){return parseInt(k)}}return k}var g=["playlist","dock","controlbar","logo","display"];function f(k){var n={};switch(e.utils.typeOf(k.plugins)){case"object":for(var m in k.plugins){n[e.utils.getPluginName(m)]=m}break;case"string":var o=k.plugins.split(",");for(var l=0;l<o.length;l++){n[e.utils.getPluginName(o[l])]=o[l]}break}return n}function d(o,n,m,k){if(e.utils.typeOf(o[n])!="object"){o[n]={}}var l=o[n][m];if(e.utils.typeOf(l)!="object"){o[n][m]=l={}}if(k){if(n=="plugins"){var p=e.utils.getPluginName(m);l[k]=o[p+"."+k];delete o[p+"."+k]}else{l[k]=o[m+"."+k];delete o[m+"."+k]}}}e.embed.deserialize=function(l){var m=f(l);for(var k in m){d(l,"plugins",m[k])}for(var p in l){if(p.indexOf(".")>-1){var o=p.split(".");var n=o[0];var p=o[1];if(e.utils.isInArray(g,n)){d(l,"components",n,p)}else{if(m[n]){d(l,"plugins",m[n],p)}}}}return l};e.embed.config=function(k,u){var t=e.utils.extend({},k);var r;if(c(t.playlist)){r=t.playlist;delete t.playlist}t=e.embed.deserialize(t);t.height=j(t.height);t.width=j(t.width);if(typeof t.plugins=="string"){var l=t.plugins.split(",");if(typeof t.plugins!="object"){t.plugins={}}for(var p=0;p<l.length;p++){var q=e.utils.getPluginName(l[p]);if(typeof t[q]=="object"){t.plugins[l[p]]=t[q];delete t[q]}else{t.plugins[l[p]]={}}}}for(var s=0;s<g.length;s++){var o=g[s];if(e.utils.exists(t[o])){if(typeof t[o]!="object"){if(!t.components[o]){t.components[o]={}}if(o=="logo"){t.components[o].file=t[o]}else{t.components[o].position=t[o]}delete t[o]}else{if(!t.components[o]){t.components[o]={}}e.utils.extend(t.components[o],t[o]);delete t[o]}}if(typeof t[o+"size"]!="undefined"){if(!t.components[o]){t.components[o]={}}t.components[o].size=t[o+"size"];delete t[o+"size"]}}if(typeof t.icons!="undefined"){if(!t.components.display){t.components.display={}}t.components.display.icons=t.icons;delete t.icons}for(var n in a){if(t[n]){if(!t[a[n]]){t[a[n]]=t[n]}delete t[n]}}var m;if(t.flashplayer&&!t.modes){m=h();m[0].src=t.flashplayer;delete t.flashplayer}else{if(t.modes){if(typeof t.modes=="string"){m=h();m[0].src=t.modes}else{if(t.modes instanceof Array){m=t.modes}else{if(typeof t.modes=="object"&&t.modes.type){m=[t.modes]}}}delete t.modes}else{m=h()}}t.modes=m;if(r){t.playlist=r}return t}})(jwplayer);(function(a){a.embed.download=function(c,g,b,d,f){this.embed=function(){var k=a.utils.extend({},b);var q={};var j=b.width?b.width:480;if(typeof j!="number"){j=parseInt(j,10)}var m=b.height?b.height:320;if(typeof m!="number"){m=parseInt(m,10)}var u,o,n;var s={};if(b.playlist&&b.playlist.length){s.file=b.playlist[0].file;o=b.playlist[0].image;s.levels=b.playlist[0].levels}else{s.file=b.file;o=b.image;s.levels=b.levels}if(s.file){u=s.file}else{if(s.levels&&s.levels.length){u=s.levels[0].file}}n=u?"pointer":"auto";var l={display:{style:{cursor:n,width:j,height:m,backgroundColor:"#000",position:"relative",textDecoration:"none",border:"none",display:"block"}},display_icon:{style:{cursor:n,position:"absolute",display:u?"block":"none",top:0,left:0,border:0,margin:0,padding:0,zIndex:3,width:50,height:50,backgroundImage:"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNrs18ENgjAYhmFouDOCcQJGcARHgE10BDcgTOIosAGwQOuPwaQeuFRi2p/3Sb6EC5L3QCxZBgAAAOCorLW1zMn65TrlkH4NcV7QNcUQt7Gn7KIhxA+qNIR81spOGkL8oFJDyLJRdosqKDDkK+iX5+d7huzwM40xptMQMkjIOeRGo+VkEVvIPfTGIpKASfYIfT9iCHkHrBEzf4gcUQ56aEzuGK/mw0rHpy4AAACAf3kJMACBxjAQNRckhwAAAABJRU5ErkJggg==)"}},display_iconBackground:{style:{cursor:n,position:"absolute",display:u?"block":"none",top:((m-50)/2),left:((j-50)/2),border:0,width:50,height:50,margin:0,padding:0,zIndex:2,backgroundImage:"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNrszwENADAIA7DhX8ENoBMZ5KR10EryckCJiIiIiIiIiIiIiIiIiIiIiIh8GmkRERERERERERERERERERERERGRHSPAAPlXH1phYpYaAAAAAElFTkSuQmCC)"}},display_image:{style:{width:j,height:m,display:o?"block":"none",position:"absolute",cursor:n,left:0,top:0,margin:0,padding:0,textDecoration:"none",zIndex:1,border:"none"}}};var h=function(v,x,y){var w=document.createElement(v);if(y){w.id=y}else{w.id=c.id+"_jwplayer_"+x}a.utils.css(w,l[x].style);return w};q.display=h("a","display",c.id);if(u){q.display.setAttribute("href",a.utils.getAbsolutePath(u))}q.display_image=h("img","display_image");q.display_image.setAttribute("alt","Click to download...");if(o){q.display_image.setAttribute("src",a.utils.getAbsolutePath(o))}if(true){q.display_icon=h("div","display_icon");q.display_iconBackground=h("div","display_iconBackground");q.display.appendChild(q.display_image);q.display_iconBackground.appendChild(q.display_icon);q.display.appendChild(q.display_iconBackground)}_css=a.utils.css;_hide=function(v){_css(v,{display:"none"})};function r(v){_imageWidth=q.display_image.naturalWidth;_imageHeight=q.display_image.naturalHeight;t()}function t(){a.utils.stretch(a.utils.stretching.UNIFORM,q.display_image,j,m,_imageWidth,_imageHeight)}q.display_image.onerror=function(v){_hide(q.display_image)};q.display_image.onload=r;c.parentNode.replaceChild(q.display,c);var p=(b.plugins&&b.plugins.logo)?b.plugins.logo:{};q.display.appendChild(new a.embed.logo(b.components.logo,"download",c.id));f.container=document.getElementById(f.id);f.setPlayer(q.display,"download")};this.supportsConfig=function(){if(b){var j=a.utils.getFirstPlaylistItemFromConfig(b);if(typeof j.file=="undefined"&&typeof j.levels=="undefined"){return true}else{if(j.file){return e(j.file,j.provider,j.playlistfile)}else{if(j.levels&&j.levels.length){for(var h=0;h<j.levels.length;h++){if(j.levels[h].file&&e(j.levels[h].file,j.provider,j.playlistfile)){return true}}}}}}else{return true}};function e(j,l,h){if(h){return false}var k=["image","sound","youtube","http"];if(l&&(k.toString().indexOf(l)>-1)){return true}if(!l||(l&&l=="video")){var m=a.utils.extension(j);if(m&&a.utils.extensionmap[m]){return true}}return false}}})(jwplayer);(function(a){a.embed.flash=function(f,g,l,e,j){function m(o,n,p){var q=document.createElement("param");q.setAttribute("name",n);q.setAttribute("value",p);o.appendChild(q)}function k(o,p,n){return function(q){if(n){document.getElementById(j.id+"_wrapper").appendChild(p)}var s=document.getElementById(j.id).getPluginConfig("display");o.resize(s.width,s.height);var r={left:s.x,top:s.y};a.utils.css(p,r)}}function d(p){if(!p){return{}}var r={};for(var o in p){var n=p[o];for(var q in n){r[o+"."+q]=n[q]}}return r}function h(q,p){if(q[p]){var s=q[p];for(var o in s){var n=s[o];if(typeof n=="string"){if(!q[o]){q[o]=n}}else{for(var r in n){if(!q[o+"."+r]){q[o+"."+r]=n[r]}}}}delete q[p]}}function b(q){if(!q){return{}}var t={},s=[];for(var n in q){var p=a.utils.getPluginName(n);var o=q[n];s.push(n);for(var r in o){t[p+"."+r]=o[r]}}t.plugins=s.join(",");return t}function c(p){var n=p.netstreambasepath?"":"netstreambasepath="+encodeURIComponent(window.location.href.split("#")[0])+"&";for(var o in p){if(typeof(p[o])=="object"){n+=o+"="+encodeURIComponent("[[JSON]]"+a.utils.strings.jsonToString(p[o]))+"&"}else{n+=o+"="+encodeURIComponent(p[o])+"&"}}return n.substring(0,n.length-1)}this.embed=function(){l.id=j.id;var y;var q=a.utils.extend({},l);var n=q.width;var w=q.height;if(f.id+"_wrapper"==f.parentNode.id){y=document.getElementById(f.id+"_wrapper")}else{y=document.createElement("div");y.id=f.id+"_wrapper";a.utils.wrap(f,y);a.utils.css(y,{position:"relative",width:n,height:w})}var o=e.setupPlugins(j,q,k);if(o.length>0){a.utils.extend(q,b(o.plugins))}else{delete q.plugins}var r=["height","width","modes","events"];for(var u=0;u<r.length;u++){delete q[r[u]]}var p="opaque";if(q.wmode){p=q.wmode}h(q,"components");h(q,"providers");if(typeof q["dock.position"]!="undefined"){if(q["dock.position"].toString().toLowerCase()=="false"){q.dock=q["dock.position"];delete q["dock.position"]}}var x="#000000";var t;if(a.utils.isIE()){var v='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" bgcolor="'+x+'" width="100%" height="100%" id="'+f.id+'" name="'+f.id+'" tabindex=0"">';v+='<param name="movie" value="'+g.src+'">';v+='<param name="allowfullscreen" value="true">';v+='<param name="allowscriptaccess" value="always">';v+='<param name="seamlesstabbing" value="true">';v+='<param name="wmode" value="'+p+'">';v+='<param name="flashvars" value="'+c(q)+'">';v+="</object>";a.utils.setOuterHTML(f,v);t=document.getElementById(f.id)}else{var s=document.createElement("object");s.setAttribute("type","application/x-shockwave-flash");s.setAttribute("data",g.src);s.setAttribute("width","100%");s.setAttribute("height","100%");s.setAttribute("bgcolor","#000000");s.setAttribute("id",f.id);s.setAttribute("name",f.id);s.setAttribute("tabindex",0);m(s,"allowfullscreen","true");m(s,"allowscriptaccess","always");m(s,"seamlesstabbing","true");m(s,"wmode",p);m(s,"flashvars",c(q));f.parentNode.replaceChild(s,f);t=s}j.container=t;j.setPlayer(t,"flash")};this.supportsConfig=function(){if(a.utils.hasFlash()){if(l){var o=a.utils.getFirstPlaylistItemFromConfig(l);if(typeof o.file=="undefined"&&typeof o.levels=="undefined"){return true}else{if(o.file){return flashCanPlay(o.file,o.provider)}else{if(o.levels&&o.levels.length){for(var n=0;n<o.levels.length;n++){if(o.levels[n].file&&flashCanPlay(o.levels[n].file,o.provider)){return true}}}}}}else{return true}}return false};flashCanPlay=function(n,p){var o=["video","http","sound","image"];if(p&&(o.toString().indexOf(p<0))){return true}var q=a.utils.extension(n);if(!q){return true}if(a.utils.exists(a.utils.extensionmap[q])&&!a.utils.exists(a.utils.extensionmap[q].flash)){return false}return true}}})(jwplayer);(function(a){a.embed.html5=function(c,g,b,d,f){function e(j,k,h){return function(l){var m=document.getElementById(c.id+"_displayarea");if(h){m.appendChild(k)}var n=m.style;j.resize(parseInt(n.width.replace("px","")),parseInt(n.height.replace("px","")));k.left=n.left;k.top=n.top}}this.embed=function(){if(a.html5){d.setupPlugins(f,b,e);c.innerHTML="";var j=a.utils.extend({screencolor:"0x000000"},b);var h=["plugins","modes","events"];for(var k=0;k<h.length;k++){delete j[h[k]]}if(j.levels&&!j.sources){j.sources=b.levels}if(j.skin&&j.skin.toLowerCase().indexOf(".zip")>0){j.skin=j.skin.replace(/\.zip/i,".xml")}var l=new (a.html5(c)).setup(j);f.container=document.getElementById(f.id);f.setPlayer(l,"html5")}else{return null}};this.supportsConfig=function(){if(!!a.vid.canPlayType){if(b){var j=a.utils.getFirstPlaylistItemFromConfig(b);if(typeof j.file=="undefined"&&typeof j.levels=="undefined"){return true}else{if(j.file){return html5CanPlay(a.vid,j.file,j.provider,j.playlistfile)}else{if(j.levels&&j.levels.length){for(var h=0;h<j.levels.length;h++){if(j.levels[h].file&&html5CanPlay(a.vid,j.levels[h].file,j.provider,j.playlistfile)){return true}}}}}}else{return true}}return false};html5CanPlay=function(k,j,l,h){if(h){return false}if(l&&l=="youtube"){return true}if(l&&l!="video"&&l!="http"&&l!="sound"){return false}var m=a.utils.extension(j);if(!a.utils.exists(m)||!a.utils.exists(a.utils.extensionmap[m])){return true}if(!a.utils.exists(a.utils.extensionmap[m].html5)){return false}if(a.utils.isLegacyAndroid()&&m.match(/m4v|mp4/)){return true}return browserCanPlay(k,a.utils.extensionmap[m].html5)};browserCanPlay=function(j,h){if(!h){return true}if(j.canPlayType(h)){return true}else{if(h=="audio/mp3"&&navigator.userAgent.match(/safari/i)){return j.canPlayType("audio/mpeg")}else{return false}}}}})(jwplayer);(function(a){a.embed.logo=function(m,l,d){var j={prefix:"http://l.longtailvideo.com/"+l+"/",file:"logo.png",link:"http://www.longtailvideo.com/players/jw-flv-player/",margin:8,out:0.5,over:1,timeout:5,hide:false,position:"bottom-left"};_css=a.utils.css;var b;var h;k();function k(){o();c();f()}function o(){if(j.prefix){var q=a.version.split(/\W/).splice(0,2).join("/");if(j.prefix.indexOf(q)<0){j.prefix+=q+"/"}}h=a.utils.extend({},j)}function p(){var s={border:"none",textDecoration:"none",position:"absolute",cursor:"pointer",zIndex:10};s.display=h.hide?"none":"block";var r=h.position.toLowerCase().split("-");for(var q in r){s[r[q]]=h.margin}return s}function c(){b=document.createElement("img");b.id=d+"_jwplayer_logo";b.style.display="none";b.onload=function(q){_css(b,p());e()};if(!h.file){return}if(h.file.indexOf("http://")===0){b.src=h.file}else{b.src=h.prefix+h.file}}if(!h.file){return}function f(){if(h.link){b.onmouseover=g;b.onmouseout=e;b.onclick=n}else{this.mouseEnabled=false}}function n(q){if(typeof q!="undefined"){q.preventDefault();q.stopPropagation()}if(h.link){window.open(h.link,"_blank")}return}function e(q){if(h.link){b.style.opacity=h.out}return}function g(q){if(h.hide){b.style.opacity=h.over}return}return b}})(jwplayer);(function(a){a.html5=function(b){var c=b;this.setup=function(d){a.utils.extend(this,new a.html5.api(c,d));return this};return this}})(jwplayer);(function(b){var d=b.utils;var c=d.css;b.html5.view=function(r,q,f){var u=r;var n=q;var x=f;var w;var g;var C;var s;var D;var p;var A;function z(){w=document.createElement("div");w.id=n.id;w.className=n.className;_videowrapper=document.createElement("div");_videowrapper.id=w.id+"_video_wrapper";n.id=w.id+"_video";c(w,{position:"relative",height:x.height,width:x.width,padding:0,backgroundColor:E(),zIndex:0});function E(){if(u.skin.getComponentSettings("display")&&u.skin.getComponentSettings("display").backgroundcolor){return u.skin.getComponentSettings("display").backgroundcolor}return parseInt("000000",16)}c(n,{width:x.width,height:x.height,top:0,left:0,zIndex:1,margin:"auto",display:"block"});c(_videowrapper,{overflow:"hidden",position:"absolute",top:0,left:0,bottom:0,right:0});d.wrap(n,w);d.wrap(n,_videowrapper);s=document.createElement("div");s.id=w.id+"_displayarea";w.appendChild(s)}function k(){for(var E=0;E<x.plugins.order.length;E++){var F=x.plugins.order[E];if(d.exists(x.plugins.object[F].getDisplayElement)){x.plugins.object[F].height=d.parseDimension(x.plugins.object[F].getDisplayElement().style.height);x.plugins.object[F].width=d.parseDimension(x.plugins.object[F].getDisplayElement().style.width);x.plugins.config[F].currentPosition=x.plugins.config[F].position}}v()}function m(E){c(s,{display:x.getMedia().hasChrome()?"none":"block"})}function v(F){var H=x.getMedia()?x.getMedia().getDisplayElement():null;if(d.exists(H)){if(A!=H){if(A&&A.parentNode){A.parentNode.replaceChild(H,A)}A=H}for(var E=0;E<x.plugins.order.length;E++){var G=x.plugins.order[E];if(d.exists(x.plugins.object[G].getDisplayElement)){x.plugins.config[G].currentPosition=x.plugins.config[G].position}}}j(x.width,x.height)}this.setup=function(){if(x&&x.getMedia()){n=x.getMedia().getDisplayElement()}z();k();u.jwAddEventListener(b.api.events.JWPLAYER_PLAYER_STATE,m);u.jwAddEventListener(b.api.events.JWPLAYER_MEDIA_LOADED,v);u.jwAddEventListener(b.api.events.JWPLAYER_MEDIA_META,function(){y()});var E;if(d.exists(window.onresize)){E=window.onresize}window.onresize=function(F){if(d.exists(E)){try{E(F)}catch(H){}}if(u.jwGetFullscreen()){var G=document.body.getBoundingClientRect();x.width=Math.abs(G.left)+Math.abs(G.right);x.height=window.innerHeight}j(x.width,x.height)}};function h(E){switch(E.keyCode){case 27:if(u.jwGetFullscreen()){u.jwSetFullscreen(false)}break;case 32:if(u.jwGetState()!=b.api.events.state.IDLE&&u.jwGetState()!=b.api.events.state.PAUSED){u.jwPause()}else{u.jwPlay()}break}}function j(H,E){if(w.style.display=="none"){return}var G=[].concat(x.plugins.order);G.reverse();D=G.length+2;if(!x.fullscreen){x.width=H;x.height=E;g=H;C=E;c(s,{top:0,bottom:0,left:0,right:0,width:H,height:E,position:"relative"});c(w,{height:C,width:g});var F=o(t,G);if(F.length>0){D+=F.length;var J=F.indexOf("playlist"),I=F.indexOf("controlbar");if(J>=0&&I>=0){F[J]=F.splice(I,1,F[J])[0]}o(l,F,true)}}else{if(!(navigator&&navigator.vendor&&navigator.vendor.indexOf("Apple")==0)){o(B,G,true)}}y()}function o(J,G,H){var F=[];for(var E=0;E<G.length;E++){var K=G[E];if(d.exists(x.plugins.object[K].getDisplayElement)){if(x.plugins.config[K].currentPosition!=b.html5.view.positions.NONE){var I=J(K,D--);if(!I){F.push(K)}else{x.plugins.object[K].resize(I.width,I.height);if(H){delete I.width;delete I.height}c(x.plugins.object[K].getDisplayElement(),I)}}else{c(x.plugins.object[K].getDisplayElement(),{display:"none"})}}}return F}function t(F,G){if(d.exists(x.plugins.object[F].getDisplayElement)){if(x.plugins.config[F].position&&a(x.plugins.config[F].position)){if(!d.exists(x.plugins.object[F].getDisplayElement().parentNode)){w.appendChild(x.plugins.object[F].getDisplayElement())}var E=e(F);E.zIndex=G;return E}}return false}function l(G,H){if(!d.exists(x.plugins.object[G].getDisplayElement().parentNode)){s.appendChild(x.plugins.object[G].getDisplayElement())}var E=x.width,F=x.height;if(typeof x.width=="string"&&x.width.lastIndexOf("%")>-1){percentage=parseFloat(x.width.substring(0,x.width.lastIndexOf("%")))/100;E=Math.round(window.innerWidth*percentage)}if(typeof x.height=="string"&&x.height.lastIndexOf("%")>-1){percentage=parseFloat(x.height.substring(0,x.height.lastIndexOf("%")))/100;F=Math.round(window.innerHeight*percentage)}return{position:"absolute",width:(E-d.parseDimension(s.style.left)-d.parseDimension(s.style.right)),height:(F-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom)),zIndex:H}}function B(E,F){return{position:"fixed",width:x.width,height:x.height,zIndex:F}}function y(){if(!d.exists(x.getMedia())){return}s.style.position="absolute";var H=x.getMedia().getDisplayElement();if(H&&H.tagName.toLowerCase()=="video"){H.style.position="absolute";var E,I;if(s.style.width.toString().lastIndexOf("%")>-1||s.style.width.toString().lastIndexOf("%")>-1){var F=s.getBoundingClientRect();E=Math.abs(F.left)+Math.abs(F.right);I=Math.abs(F.top)+Math.abs(F.bottom)}else{E=d.parseDimension(s.style.width);I=d.parseDimension(s.style.height)}if(H.parentNode){H.parentNode.style.left=s.style.left;H.parentNode.style.top=s.style.top}d.stretch(u.jwGetStretching(),H,E,I,H.videoWidth?H.videoWidth:400,H.videoHeight?H.videoHeight:300)}else{var G=x.plugins.object.display.getDisplayElement();if(G){x.getMedia().resize(d.parseDimension(G.style.width),d.parseDimension(G.style.height))}else{x.getMedia().resize(d.parseDimension(s.style.width),d.parseDimension(s.style.height))}}}function e(F){var G={position:"absolute",margin:0,padding:0,top:null};var E=x.plugins.config[F].currentPosition.toLowerCase();switch(E.toUpperCase()){case b.html5.view.positions.TOP:G.top=d.parseDimension(s.style.top);G.left=d.parseDimension(s.style.left);G.width=g-d.parseDimension(s.style.left)-d.parseDimension(s.style.right);G.height=x.plugins.object[F].height;s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].height+"px";s.style.height=d.parseDimension(s.style.height)-G.height+"px";break;case b.html5.view.positions.RIGHT:G.top=d.parseDimension(s.style.top);G.right=d.parseDimension(s.style.right);G.width=x.plugins.object[F].width;G.height=C-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom);s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].width+"px";s.style.width=d.parseDimension(s.style.width)-G.width+"px";break;case b.html5.view.positions.BOTTOM:G.bottom=d.parseDimension(s.style.bottom);G.left=d.parseDimension(s.style.left);G.width=g-d.parseDimension(s.style.left)-d.parseDimension(s.style.right);G.height=x.plugins.object[F].height;s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].height+"px";s.style.height=d.parseDimension(s.style.height)-G.height+"px";break;case b.html5.view.positions.LEFT:G.top=d.parseDimension(s.style.top);G.left=d.parseDimension(s.style.left);G.width=x.plugins.object[F].width;G.height=C-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom);s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].width+"px";s.style.width=d.parseDimension(s.style.width)-G.width+"px";break;default:break}return G}this.resize=j;this.fullscreen=function(H){if(navigator&&navigator.vendor&&navigator.vendor.indexOf("Apple")===0){if(x.getMedia().getDisplayElement().webkitSupportsFullscreen){if(H){try{x.getMedia().getDisplayElement().webkitEnterFullscreen()}catch(G){}}else{try{x.getMedia().getDisplayElement().webkitExitFullscreen()}catch(G){}}}}else{if(H){document.onkeydown=h;clearInterval(p);var F=document.body.getBoundingClientRect();x.width=Math.abs(F.left)+Math.abs(F.right);x.height=window.innerHeight;var E={position:"fixed",width:"100%",height:"100%",top:0,left:0,zIndex:2147483000};c(w,E);E.zIndex=1;if(x.getMedia()&&x.getMedia().getDisplayElement()){c(x.getMedia().getDisplayElement(),E)}E.zIndex=2;c(s,E)}else{document.onkeydown="";x.width=g;x.height=C;c(w,{position:"relative",height:x.height,width:x.width,zIndex:0})}j(x.width,x.height)}}};function a(e){return([b.html5.view.positions.TOP,b.html5.view.positions.RIGHT,b.html5.view.positions.BOTTOM,b.html5.view.positions.LEFT].toString().indexOf(e.toUpperCase())>-1)}b.html5.view.positions={TOP:"TOP",RIGHT:"RIGHT",BOTTOM:"BOTTOM",LEFT:"LEFT",OVER:"OVER",NONE:"NONE"}})(jwplayer);(function(a){var b={backgroundcolor:"",margin:10,font:"Arial,sans-serif",fontsize:10,fontcolor:parseInt("000000",16),fontstyle:"normal",fontweight:"bold",buttoncolor:parseInt("ffffff",16),position:a.html5.view.positions.BOTTOM,idlehide:false,layout:{left:{position:"left",elements:[{name:"play",type:"button"},{name:"divider",type:"divider"},{name:"prev",type:"button"},{name:"divider",type:"divider"},{name:"next",type:"button"},{name:"divider",type:"divider"},{name:"elapsed",type:"text"}]},center:{position:"center",elements:[{name:"time",type:"slider"}]},right:{position:"right",elements:[{name:"duration",type:"text"},{name:"blank",type:"button"},{name:"divider",type:"divider"},{name:"mute",type:"button"},{name:"volume",type:"slider"},{name:"divider",type:"divider"},{name:"fullscreen",type:"button"}]}}};_utils=a.utils;_css=_utils.css;_hide=function(c){_css(c,{display:"none"})};_show=function(c){_css(c,{display:"block"})};a.html5.controlbar=function(l,V){var k=l;var D=_utils.extend({},b,k.skin.getComponentSettings("controlbar"),V);if(D.position==a.html5.view.positions.NONE||typeof a.html5.view.positions[D.position]=="undefined"){return}if(_utils.mapLength(k.skin.getComponentLayout("controlbar"))>0){D.layout=k.skin.getComponentLayout("controlbar")}var ac;var P;var ab;var E;var v="none";var g;var j;var ad;var f;var e;var y;var Q={};var p=false;var c={};var Y;var h=false;var o;var d;var S=false;var G=false;var W=new a.html5.eventdispatcher();_utils.extend(this,W);function J(){if(!Y){Y=k.skin.getSkinElement("controlbar","background");if(!Y){Y={width:0,height:0,src:null}}}return Y}function N(){ab=0;E=0;P=0;if(!p){var ak={height:J().height,backgroundColor:D.backgroundcolor};ac=document.createElement("div");ac.id=k.id+"_jwplayer_controlbar";_css(ac,ak)}var aj=(k.skin.getSkinElement("controlbar","capLeft"));var ai=(k.skin.getSkinElement("controlbar","capRight"));if(aj){x("capLeft","left",false,ac)}var al={position:"absolute",height:J().height,left:(aj?aj.width:0),zIndex:0};Z("background",ac,al,"img");if(J().src){Q.background.src=J().src}al.zIndex=1;Z("elements",ac,al);if(ai){x("capRight","right",false,ac)}}this.getDisplayElement=function(){return ac};this.resize=function(ak,ai){_utils.cancelAnimation(ac);document.getElementById(k.id).onmousemove=A;e=ak;y=ai;if(G!=k.jwGetFullscreen()){G=k.jwGetFullscreen();d=undefined}var aj=w();A();I({id:k.id,duration:ad,position:j});u({id:k.id,bufferPercent:f});return aj};this.show=function(){if(h){h=false;_show(ac);T()}};this.hide=function(){if(!h){h=true;_hide(ac);aa()}};function q(){var aj=["timeSlider","volumeSlider","timeSliderRail","volumeSliderRail"];for(var ak in aj){var ai=aj[ak];if(typeof Q[ai]!="undefined"){c[ai]=Q[ai].getBoundingClientRect()}}}function A(ai){if(h){return}if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){clearTimeout(o);switch(k.jwGetState()){case a.api.events.state.PAUSED:case a.api.events.state.IDLE:if(!D.idlehide||_utils.exists(ai)){U()}if(D.idlehide){o=setTimeout(function(){z()},2000)}break;default:if(ai){U()}o=setTimeout(function(){z()},2000);break}}}function z(ai){aa();_utils.cancelAnimation(ac);_utils.fadeTo(ac,0,0.1,1,0)}function U(){T();_utils.cancelAnimation(ac);_utils.fadeTo(ac,1,0,1,0)}function H(ai){return function(){if(S&&d!=ai){d=ai;W.sendEvent(ai,{component:"controlbar",boundingRect:O()})}}}var T=H(a.api.events.JWPLAYER_COMPONENT_SHOW);var aa=H(a.api.events.JWPLAYER_COMPONENT_HIDE);function O(){if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){return _utils.getDimensions(ac)}else{return{x:0,y:0,width:0,height:0}}}function Z(am,al,ak,ai){var aj;if(!p){if(!ai){ai="div"}aj=document.createElement(ai);Q[am]=aj;aj.id=ac.id+"_"+am;al.appendChild(aj)}else{aj=document.getElementById(ac.id+"_"+am)}if(_utils.exists(ak)){_css(aj,ak)}return aj}function M(){ah(D.layout.left);ah(D.layout.right,-1);ah(D.layout.center)}function ah(al,ai){var am=al.position=="right"?"right":"left";var ak=_utils.extend([],al.elements);if(_utils.exists(ai)){ak.reverse()}for(var aj=0;aj<ak.length;aj++){C(ak[aj],am)}}function K(){return P++}function C(am,ao){var al,aj,ak,ai,aq;if(am.type=="divider"){x("divider"+K(),ao,true,undefined,undefined,am.width,am.element);return}switch(am.name){case"play":x("playButton",ao,false);x("pauseButton",ao,true);R("playButton","jwPlay");R("pauseButton","jwPause");break;case"prev":x("prevButton",ao,true);R("prevButton","jwPlaylistPrev");break;case"stop":x("stopButton",ao,true);R("stopButton","jwStop");break;case"next":x("nextButton",ao,true);R("nextButton","jwPlaylistNext");break;case"elapsed":x("elapsedText",ao,true);break;case"time":aj=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","timeSliderCapLeft").width;ak=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapRight"))?0:k.skin.getSkinElement("controlbar","timeSliderCapRight").width;al=ao=="left"?aj:ak;ai=k.skin.getSkinElement("controlbar","timeSliderRail").width+aj+ak;aq={height:J().height,position:"absolute",top:0,width:ai};aq[ao]=ao=="left"?ab:E;var an=Z("timeSlider",Q.elements,aq);x("timeSliderCapLeft",ao,true,an,ao=="left"?0:al);x("timeSliderRail",ao,false,an,al);x("timeSliderBuffer",ao,false,an,al);x("timeSliderProgress",ao,false,an,al);x("timeSliderThumb",ao,false,an,al);x("timeSliderCapRight",ao,true,an,ao=="right"?0:al);X("time");break;case"fullscreen":x("fullscreenButton",ao,false);x("normalscreenButton",ao,true);R("fullscreenButton","jwSetFullscreen",true);R("normalscreenButton","jwSetFullscreen",false);break;case"volume":aj=!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","volumeSliderCapLeft").width;ak=!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapRight"))?0:k.skin.getSkinElement("controlbar","volumeSliderCapRight").width;al=ao=="left"?aj:ak;ai=k.skin.getSkinElement("controlbar","volumeSliderRail").width+aj+ak;aq={height:J().height,position:"absolute",top:0,width:ai};aq[ao]=ao=="left"?ab:E;var ap=Z("volumeSlider",Q.elements,aq);x("volumeSliderCapLeft",ao,true,ap,ao=="left"?0:al);x("volumeSliderRail",ao,true,ap,al);x("volumeSliderProgress",ao,false,ap,al);x("volumeSliderCapRight",ao,true,ap,ao=="right"?0:al);X("volume");break;case"mute":x("muteButton",ao,false);x("unmuteButton",ao,true);R("muteButton","jwSetMute",true);R("unmuteButton","jwSetMute",false);break;case"duration":x("durationText",ao,true);break}}function x(al,ao,aj,ar,am,ai,ak){if(_utils.exists(k.skin.getSkinElement("controlbar",al))||al.indexOf("Text")>0||al.indexOf("divider")===0){var an={height:J().height,position:"absolute",display:"block",top:0};if((al.indexOf("next")===0||al.indexOf("prev")===0)&&k.jwGetPlaylist().length<2){aj=false;an.display="none"}var at;if(al.indexOf("Text")>0){al.innerhtml="00:00";an.font=D.fontsize+"px/"+(J().height+1)+"px "+D.font;an.color=D.fontcolor;an.textAlign="center";an.fontWeight=D.fontweight;an.fontStyle=D.fontstyle;an.cursor="default";at=14+3*D.fontsize}else{if(al.indexOf("divider")===0){if(ai){if(!isNaN(parseInt(ai))){at=parseInt(ai)}}else{if(ak){var ap=k.skin.getSkinElement("controlbar",ak);if(ap){an.background="url("+ap.src+") repeat-x center left";at=ap.width}}else{an.background="url("+k.skin.getSkinElement("controlbar","divider").src+") repeat-x center left";at=k.skin.getSkinElement("controlbar","divider").width}}}else{an.background="url("+k.skin.getSkinElement("controlbar",al).src+") repeat-x center left";at=k.skin.getSkinElement("controlbar",al).width}}if(ao=="left"){an.left=isNaN(am)?ab:am;if(aj){ab+=at}}else{if(ao=="right"){an.right=isNaN(am)?E:am;if(aj){E+=at}}}if(_utils.typeOf(ar)=="undefined"){ar=Q.elements}an.width=at;if(p){_css(Q[al],an)}else{var aq=Z(al,ar,an);if(_utils.exists(k.skin.getSkinElement("controlbar",al+"Over"))){aq.onmouseover=function(au){aq.style.backgroundImage=["url(",k.skin.getSkinElement("controlbar",al+"Over").src,")"].join("")};aq.onmouseout=function(au){aq.style.backgroundImage=["url(",k.skin.getSkinElement("controlbar",al).src,")"].join("")}}}}}function F(){k.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,B);k.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,s);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_BUFFER,u);k.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,r);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_TIME,I);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_MUTE,ag);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_VOLUME,m);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_COMPLETE,L)}function B(){N();M();w();ae()}function s(ai){ad=k.jwGetPlaylist()[ai.index].duration;I({id:k.id,duration:ad,position:0});u({id:k.id,bufferProgress:0})}function ae(){I({id:k.id,duration:k.jwGetDuration(),position:0});u({id:k.id,bufferProgress:0});ag({id:k.id,mute:k.jwGetMute()});r({id:k.id,newstate:a.api.events.state.IDLE});m({id:k.id,volume:k.jwGetVolume()})}function R(ak,al,aj){if(p){return}if(_utils.exists(k.skin.getSkinElement("controlbar",ak))){var ai=Q[ak];if(_utils.exists(ai)){_css(ai,{cursor:"pointer"});if(al=="fullscreen"){ai.onmouseup=function(am){am.stopPropagation();k.jwSetFullscreen(!k.jwGetFullscreen())}}else{ai.onmouseup=function(am){am.stopPropagation();if(_utils.exists(aj)){k[al](aj)}else{k[al]()}}}}}}function X(ai){if(p){return}var aj=Q[ai+"Slider"];_css(Q.elements,{cursor:"pointer"});_css(aj,{cursor:"pointer"});aj.onmousedown=function(ak){v=ai};aj.onmouseup=function(ak){ak.stopPropagation();af(ak.pageX)};aj.onmousemove=function(ak){if(v=="time"){g=true;var al=ak.pageX-c[ai+"Slider"].left-window.pageXOffset;_css(Q.timeSliderThumb,{left:al})}}}function af(aj){g=false;var ai;if(v=="time"){ai=aj-c.timeSliderRail.left+window.pageXOffset;var al=ai/c.timeSliderRail.width*ad;if(al<0){al=0}else{if(al>ad){al=ad-3}}if(k.jwGetState()==a.api.events.state.PAUSED||k.jwGetState()==a.api.events.state.IDLE){k.jwPlay()}k.jwSeek(al)}else{if(v=="volume"){ai=aj-c.volumeSliderRail.left-window.pageXOffset;var ak=Math.round(ai/c.volumeSliderRail.width*100);if(ak<0){ak=0}else{if(ak>100){ak=100}}if(k.jwGetMute()){k.jwSetMute(false)}k.jwSetVolume(ak)}}v="none"}function u(aj){if(_utils.exists(aj.bufferPercent)){f=aj.bufferPercent}if(c.timeSliderRail){var ak=c.timeSliderRail.width;var ai=isNaN(Math.round(ak*f/100))?0:Math.round(ak*f/100);_css(Q.timeSliderBuffer,{width:ai})}}function ag(ai){if(ai.mute){_hide(Q.muteButton);_show(Q.unmuteButton);_hide(Q.volumeSliderProgress)}else{_show(Q.muteButton);_hide(Q.unmuteButton);_show(Q.volumeSliderProgress)}}function r(ai){if(ai.newstate==a.api.events.state.BUFFERING||ai.newstate==a.api.events.state.PLAYING){_show(Q.pauseButton);_hide(Q.playButton)}else{_hide(Q.pauseButton);_show(Q.playButton)}A();if(ai.newstate==a.api.events.state.IDLE){_hide(Q.timeSliderBuffer);_hide(Q.timeSliderProgress);_hide(Q.timeSliderThumb);I({id:k.id,duration:k.jwGetDuration(),position:0})}else{_show(Q.timeSliderBuffer);if(ai.newstate!=a.api.events.state.BUFFERING){_show(Q.timeSliderProgress);_show(Q.timeSliderThumb)}}}function L(ai){u({bufferPercent:0});I(_utils.extend(ai,{position:0,duration:ad}))}function I(al){if(_utils.exists(al.position)){j=al.position}if(_utils.exists(al.duration)){ad=al.duration}var aj=(j===ad===0)?0:j/ad;var am=c.timeSliderRail;if(am){var ai=isNaN(Math.round(am.width*aj))?0:Math.round(am.width*aj);var ak=ai;if(Q.timeSliderProgress){Q.timeSliderProgress.style.width=ai+"px";if(!g){if(Q.timeSliderThumb){Q.timeSliderThumb.style.left=ak+"px"}}}}if(Q.durationText){Q.durationText.innerHTML=_utils.timeFormat(ad)}if(Q.elapsedText){Q.elapsedText.innerHTML=_utils.timeFormat(j)}}function n(){var am,aj;var ak=document.getElementById(ac.id+"_elements");if(!ak){return}var al=ak.childNodes;for(var ai in ak.childNodes){if(isNaN(parseInt(ai,10))){continue}if(al[ai].id.indexOf(ac.id+"_divider")===0&&aj&&aj.id.indexOf(ac.id+"_divider")===0&&al[ai].style.backgroundImage==aj.style.backgroundImage){al[ai].style.display="none"}else{if(al[ai].id.indexOf(ac.id+"_divider")===0&&am&&am.style.display!="none"){al[ai].style.display="block"}}if(al[ai].style.display!="none"){aj=al[ai]}am=al[ai]}}function w(){n();if(k.jwGetFullscreen()){_show(Q.normalscreenButton);_hide(Q.fullscreenButton)}else{_hide(Q.normalscreenButton);_show(Q.fullscreenButton)}var aj={width:e};var ai={};if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){aj.left=D.margin;aj.width-=2*D.margin;aj.top=y-J().height-D.margin;aj.height=J().height}var al=k.skin.getSkinElement("controlbar","capLeft");var ak=k.skin.getSkinElement("controlbar","capRight");ai.left=al?al.width:0;ai.width=aj.width-ai.left-(ak?ak.width:0);var am=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","timeSliderCapLeft").width;_css(Q.timeSliderRail,{width:(ai.width-ab-E),left:am});if(_utils.exists(Q.timeSliderCapRight)){_css(Q.timeSliderCapRight,{left:am+(ai.width-ab-E)})}_css(ac,aj);_css(Q.elements,ai);_css(Q.background,ai);q();return aj}function m(am){if(_utils.exists(Q.volumeSliderRail)){var ak=isNaN(am.volume/100)?1:am.volume/100;var al=_utils.parseDimension(Q.volumeSliderRail.style.width);var ai=isNaN(Math.round(al*ak))?0:Math.round(al*ak);var an=_utils.parseDimension(Q.volumeSliderRail.style.right);var aj=(!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapLeft")))?0:k.skin.getSkinElement("controlbar","volumeSliderCapLeft").width;_css(Q.volumeSliderProgress,{width:ai,left:aj});if(_utils.exists(Q.volumeSliderCapLeft)){_css(Q.volumeSliderCapLeft,{left:0})}}}function t(){N();M();q();p=true;F();D.idlehide=(D.idlehide.toString().toLowerCase()=="true");if(D.position==a.html5.view.positions.OVER&&D.idlehide){ac.style.opacity=0;S=true}else{setTimeout((function(){S=true;T()}),1)}ae()}t();return this}})(jwplayer);(function(b){var a=["width","height","state","playlist","item","position","buffer","duration","volume","mute","fullscreen"];var c=b.utils;b.html5.controller=function(z,w,h,v){var C=z;var G=h;var g=v;var o=w;var J=true;var e=-1;var A=c.exists(G.config.debug)&&(G.config.debug.toString().toLowerCase()=="console");var m=new b.html5.eventdispatcher(o.id,A);c.extend(this,m);var E=[];var d=false;function r(M){if(d){m.sendEvent(M.type,M)}else{E.push(M)}}function K(M){if(!d){m.sendEvent(b.api.events.JWPLAYER_READY,M);if(b.utils.exists(window.playerReady)){playerReady(M)}if(b.utils.exists(window[h.config.playerReady])){window[h.config.playerReady](M)}while(E.length>0){var O=E.shift();m.sendEvent(O.type,O)}if(h.config.autostart&&!b.utils.isIOS()){t(G.item)}while(p.length>0){var N=p.shift();x(N.method,N.arguments)}d=true}}G.addGlobalListener(r);G.addEventListener(b.api.events.JWPLAYER_MEDIA_BUFFER_FULL,function(){G.getMedia().play()});G.addEventListener(b.api.events.JWPLAYER_MEDIA_TIME,function(M){if(M.position>=G.playlist[G.item].start&&e>=0){G.playlist[G.item].start=e;e=-1}});G.addEventListener(b.api.events.JWPLAYER_MEDIA_COMPLETE,function(M){setTimeout(s,25)});function u(){try{f(G.item);if(G.playlist[G.item].levels[0].file.length>0){if(J||G.state==b.api.events.state.IDLE){G.getMedia().load(G.playlist[G.item]);J=false}else{if(G.state==b.api.events.state.PAUSED){G.getMedia().play()}}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function I(){try{if(G.playlist[G.item].levels[0].file.length>0){switch(G.state){case b.api.events.state.PLAYING:case b.api.events.state.BUFFERING:G.getMedia().pause();break}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function D(M){try{if(G.playlist[G.item].levels[0].file.length>0){if(typeof M!="number"){M=parseFloat(M)}switch(G.state){case b.api.events.state.IDLE:if(e<0){e=G.playlist[G.item].start;G.playlist[G.item].start=M}u();break;case b.api.events.state.PLAYING:case b.api.events.state.PAUSED:case b.api.events.state.BUFFERING:G.seek(M);break}}return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}function n(M){if(!c.exists(M)){M=true}try{G.getMedia().stop(M);return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}function k(){try{if(G.playlist[G.item].levels[0].file.length>0){if(G.config.shuffle){f(y())}else{if(G.item+1==G.playlist.length){f(0)}else{f(G.item+1)}}}if(G.state!=b.api.events.state.IDLE){var N=G.state;G.state=b.api.events.state.IDLE;m.sendEvent(b.api.events.JWPLAYER_PLAYER_STATE,{oldstate:N,newstate:b.api.events.state.IDLE})}u();return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function j(){try{if(G.playlist[G.item].levels[0].file.length>0){if(G.config.shuffle){f(y())}else{if(G.item===0){f(G.playlist.length-1)}else{f(G.item-1)}}}if(G.state!=b.api.events.state.IDLE){var N=G.state;G.state=b.api.events.state.IDLE;m.sendEvent(b.api.events.JWPLAYER_PLAYER_STATE,{oldstate:N,newstate:b.api.events.state.IDLE})}u();return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function y(){var M=null;if(G.playlist.length>1){while(!c.exists(M)){M=Math.floor(Math.random()*G.playlist.length);if(M==G.item){M=null}}}else{M=0}return M}function t(N){if(!G.playlist||!G.playlist[N]){return false}try{if(G.playlist[N].levels[0].file.length>0){var O=G.state;if(O!==b.api.events.state.IDLE){if(G.playlist[G.item].provider==G.playlist[N].provider){n(false)}else{n()}}f(N);u()}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function f(M){if(!G.playlist[M]){return}G.setActiveMediaProvider(G.playlist[M]);if(G.item!=M){G.item=M;J=true;m.sendEvent(b.api.events.JWPLAYER_PLAYLIST_ITEM,{index:M})}}function H(N){try{f(G.item);var O=G.getMedia();switch(typeof(N)){case"number":O.volume(N);break;case"string":O.volume(parseInt(N,10));break}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function q(N){try{f(G.item);var O=G.getMedia();if(typeof N=="undefined"){O.mute(!G.mute)}else{if(N.toString().toLowerCase()=="true"){O.mute(true)}else{O.mute(false)}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function l(N,M){try{G.width=N;G.height=M;g.resize(N,M);m.sendEvent(b.api.events.JWPLAYER_RESIZE,{width:G.width,height:G.height});return true}catch(O){m.sendEvent(b.api.events.JWPLAYER_ERROR,O)}return false}function B(N){try{if(typeof N=="undefined"){G.fullscreen=!G.fullscreen;g.fullscreen(!G.fullscreen)}else{if(N.toString().toLowerCase()=="true"){G.fullscreen=true;g.fullscreen(true)}else{G.fullscreen=false;g.fullscreen(false)}}m.sendEvent(b.api.events.JWPLAYER_RESIZE,{width:G.width,height:G.height});m.sendEvent(b.api.events.JWPLAYER_FULLSCREEN,{fullscreen:N});return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function L(M){try{n();G.loadPlaylist(M);f(G.item);return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}b.html5.controller.repeatoptions={LIST:"LIST",ALWAYS:"ALWAYS",SINGLE:"SINGLE",NONE:"NONE"};function s(){switch(G.config.repeat.toUpperCase()){case b.html5.controller.repeatoptions.SINGLE:u();break;case b.html5.controller.repeatoptions.ALWAYS:if(G.item==G.playlist.length-1&&!G.config.shuffle){t(0)}else{k()}break;case b.html5.controller.repeatoptions.LIST:if(G.item==G.playlist.length-1&&!G.config.shuffle){n();f(0)}else{k()}break;default:n();break}}var p=[];function F(M){return function(){if(d){x(M,arguments)}else{p.push({method:M,arguments:arguments})}}}function x(O,N){var M=[];for(i=0;i<N.length;i++){M.push(N[i])}O.apply(this,M)}this.play=F(u);this.pause=F(I);this.seek=F(D);this.stop=F(n);this.next=F(k);this.prev=F(j);this.item=F(t);this.setVolume=F(H);this.setMute=F(q);this.resize=F(l);this.setFullscreen=F(B);this.load=F(L);this.playerReady=K}})(jwplayer);(function(a){a.html5.defaultSkin=function(){this.text='<?xml version="1.0" ?><skin author="LongTail Video" name="Five" version="1.0"><settings><setting name="backcolor" value="0xFFFFFF"/><setting name="frontcolor" value="0x000000"/><setting name="lightcolor" value="0x000000"/><setting name="screencolor" value="0x000000"/></settings><components><component name="controlbar"><settings><setting name="margin" value="20"/><setting name="fontsize" value="11"/></settings><elements><element name="background" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFJJREFUeNrslLENwAAIwxLU/09j5AiOgD5hVQzNAVY8JK4qEfHMIKBnd2+BQlBINaiRtL/aV2rdzYBsM6CIONbI1NZENTr3RwdB2PlnJgJ6BRgA4hwu5Qg5iswAAAAASUVORK5CYII="/><element name="capLeft" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="capRight" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="divider" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="playButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEhJREFUeNpiYqABYBo1dNRQ+hr6H4jvA3E8NS39j4SpZvh/LJig4YxEGEqy3kET+w+AOGFQRhTJhrEQkGcczfujhg4CQwECDADpTRWU/B3wHQAAAABJRU5ErkJggg=="/><element name="pauseButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAChJREFUeNpiYBgFo2DwA0YC8v/R1P4nRu+ooaOGUtnQUTAKhgIACDAAFCwQCfAJ4gwAAAAASUVORK5CYII="/><element name="prevButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEtJREFUeNpiYBgFo2Dog/9QDAPyQHweTYwiQ/2B+D0Wi8g2tB+JTdBQRiIMJVkvEy0iglhDF9Aq9uOpHVEwoE+NJDUKRsFgAAABBgDe2hqZcNNL0AAAAABJRU5ErkJggg=="/><element name="nextButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAElJREFUeNpiYBgFo2Dog/9AfB6I5dHE/lNqKAi/B2J/ahsKw/3EGMpIhKEk66WJoaR6fz61IyqemhEFSlL61ExSo2AUDAYAEGAAiG4hj+5t7M8AAAAASUVORK5CYII="/><element name="timeSliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADxJREFUeNpiYBgFo2AU0Bwwzluw+D8tLWARFhKiqQ9YuLg4aWsBGxs7bS1gZ6e5BWyjSX0UjIKhDgACDABlYQOGh5pYywAAAABJRU5ErkJggg=="/><element name="timeSliderBuffer" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYBgFo2AU0Bww1jc0/aelBSz8/Pw09QELOzs7bS1gY2OjrQWsrKy09gHraFIfBaNgqAOAAAMAvy0DChXHsZMAAAAASUVORK5CYII="/><element name="timeSliderProgress" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpiYBgFo2AU0BwwAvF/WlrARGsfjFow8BaMglEwCugAAAIMAOHfAQunR+XzAAAAAElFTkSuQmCC"/><element name="timeSliderThumb" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAICAYAAAA870V8AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABZJREFUeNpiZICA/yCCiQEJUJcDEGAAY0gBD1/m7Q0AAAAASUVORK5CYII="/><element name="muteButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAYCAYAAADKx8xXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiYBgFIw3MB+L/5Gj8j6yRiRTFyICJXHfTXyMLAXlGati4YDRFDj8AEGAABk8GSqqS4CoAAAAASUVORK5CYII="/><element name="unmuteButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAYCAYAAADKx8xXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYBgFgxz8p7bm+cQa+h8LHy7GhEcjIz4bmAjYykiun/8j0fakGPIfTfPgiSr6aB4FVAcAAQYAWdwR1G1Wd2gAAAAASUVORK5CYII="/><element name="volumeSliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAYCAYAAADkgu3FAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGpJREFUeNpi/P//PwM9ABMDncCoRYPfIqqDZcuW1UPp/6AUDcNM1DQYKtRAlaAj1mCSLSLXYIIWUctgDItoZfDA5aOoqKhGEANIM9LVR7SymGDQUctikuOIXkFNdhHEOFrDjlpEd4sAAgwAriRMub95fu8AAAAASUVORK5CYII="/><element name="volumeSliderProgress" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAYCAYAAADkgu3FAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFtJREFUeNpi/P//PwM9ABMDncCoRYPfIlqAeij9H5SiYZiqBqPTlFqE02BKLSLaYFItIttgQhZRzWB8FjENiuRJ7aAbsMQwYMl7wDIsWUUQ42gNO2oR3S0CCDAAKhKq6MLLn8oAAAAASUVORK5CYII="/><element name="fullscreenButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE5JREFUeNpiYBgFo2DQA0YC8v/xqP1PjDlMRDrEgUgxkgHIlfZoriVGjmzLsLFHAW2D6D8eA/9Tw7L/BAwgJE90PvhPpNgoGAVDEQAEGAAMdhTyXcPKcAAAAABJRU5ErkJggg=="/><element name="normalscreenButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEZJREFUeNpiYBgFo2DIg/9UUkOUAf8JiFFsyX88fJyAkcQgYMQjNkzBoAgiezyRbE+tFGSPxQJ7auYBmma0UTAKBhgABBgAJAEY6zON61sAAAAASUVORK5CYII="/></elements></component><component name="display"><elements><element name="background" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNrszwENADAIA7DhX8ENoBMZ5KR10EryckCJiIiIiIiIiIiIiIiIiIiIiIh8GmkRERERERERERERERERERERERGRHSPAAPlXH1phYpYaAAAAAElFTkSuQmCC"/><element name="playIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNrs18ENgjAYhmFouDOCcQJGcARHgE10BDcgTOIosAGwQOuPwaQeuFRi2p/3Sb6EC5L3QCxZBgAAAOCorLW1zMn65TrlkH4NcV7QNcUQt7Gn7KIhxA+qNIR81spOGkL8oFJDyLJRdosqKDDkK+iX5+d7huzwM40xptMQMkjIOeRGo+VkEVvIPfTGIpKASfYIfT9iCHkHrBEzf4gcUQ56aEzuGK/mw0rHpy4AAACAf3kJMACBxjAQNRckhwAAAABJRU5ErkJggg=="/><element name="muteIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHJJREFUeNrs1jEOgCAMBVAg7t5/8qaoIy4uoobyXsLCxA+0NCUAAADGUWvdQoQ41x4ixNBB2hBvBskdD3w5ZCkl3+33VqI0kjBBlh9rp+uTcyOP33TnolfsU85XX3yIRpQph8ZQY3wTZtU5AACASA4BBgDHoVuY1/fvOQAAAABJRU5ErkJggg=="/><element name="errorIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWlJREFUeNrsl+1twjAQhsHq/7BBYQLYIBmBDcoGMAIjtBPQTcII2SDtBDBBwrU6pGsUO7YbO470PtKJkz9iH++d4ywWAAAAAABgljRNsyWr2bZzDuJG1rLdZhcMbTjrBCGDyUKsqQLFciJb9bSvuG/WagRVRUVUI6gqy5HVeKWfSgRyJruKIU//TrZTSn2nmlaXThrloi/v9F2STC1W4+Aw5cBzkquRc09bofFNc6YLxEON0VUZS5FPTftO49vMjRsIF3RhOGr7/D/pJw+FKU+q0vDyq8W42jCunDqI3LC5XxNj2wHLU1XjaRnb0Lhykhqhhd8MtSF5J9tbjCv4mXGvKJz/65FF/qJryyaaIvzP2QRxZTX2nTuXjvV/VPFSwyLnW7mpH99yTh1FEVro6JBSd40/pMrRdV8vPtcKl28T2pT8TnFZ4yNosct3Q0io6JfBiz1FlGdqVQH3VHnepAEAAAAAADDzEGAAcTwB10jWgxcAAAAASUVORK5CYII="/><element name="bufferIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuhJREFUeNrsWr9rU1EUznuNGqvFQh1ULOhiBx0KDtIuioO4pJuik3FxFfUPaAV1FTdx0Q5d2g4FFxehTnEpZHFoBy20tCIWtGq0TZP4HfkeHB5N8m6Sl/sa74XDybvv3vvOd8/Pe4lXrVZT3dD8VJc0B8QBcUAcEAfESktHGeR5XtMfqFQq/f92zPe/NbtGlKTdCY30kuxrpMGO94BlQCXs+rbh3ONgA6BlzP1p20d80gEI5hmA2A92Qua1Q2PtAFISM+bvjMG8U+Q7oA3rQGASwrYCU6WpNdLGYbA+Pq5jjXIiwi8EEa2UDbQSaKOIuV+SlkcCrfjY8XTI9EpKGwP0C2kru2hLtHqa4zoXtZRWyvi4CLwv9Opr6Hkn6A9HKgEANsQ1iqC3Ub/vRUk2JgmRkatK36kVrnt0qObunwUdUUMXMWYpakJsO5Am8tAw2GBIgwWA+G2S2dMpiw0gDioQRQJoKhRb1QiDwlHZUABYbaXWsm5ae6loTE4ZDxN4CZar8foVzOJ2iyZ2kWF3t7YIevffaMT5yJ70kQb2fQ1sE5SHr2wazs2wgMxgbsEKEAgxAvZUJbQLBGTSBMgNrncJbA6AljtS/eKDJ0Ez+DmrQEzXS2h1Ck25kAg0IZcUOaydCy4sYnN2fOA+2AP16gNoHALlQ+fwH7XO4CxLenUpgj4xr6ugY2roPMbMx+Xs18m/E8CVEIhxsNeg83XWOAN6grG3lGbk8uE5fr4B/WH3cJw+co/l9nTYsSGYCJ/lY5/qv0thn6nrIWmjeJcPSnWOeY++AkF8tpJHIMAUs/MaBBpj3znZfQo5psY+ZrG4gv5HickjEOymKjEeRpgyST6IuZcTcWbnjcgdPi5ghxciRKsl1lDSsgwA1i8fssonJgzmTSqfGUkCENndNdAL7PS6QQ7ZYISTo+1qq0LEWjTWcvY4isa4z+yfQB+7ooyHVg5RI7/i1Ijn/vnggDggDogD4oC00P4KMACd/juEHOrS4AAAAABJRU5ErkJggg=="/></elements></component><component name="dock"><elements><element name="button" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFBJREFUeNrs0cEJACAQA8Eofu0fu/W6EM5ZSAFDRpKTBs00CQQEBAQEBAQEBAQEBAQEBATkK8iqbY+AgICAgICAgICAgICAgICAgIC86QowAG5PAQzEJ0lKAAAAAElFTkSuQmCC"/></elements></component><component name="playlist"><elements><element name="item" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAIAAAC1nk4lAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHhJREFUeNrs2NEJwCAMBcBYuv/CFuIE9VN47WWCR7iocXR3pdWdGPqqwIoMjYfQeAiNh9B4JHc6MHQVHnjggQceeOCBBx77TifyeOY0iHi8DqIdEY8dD5cL094eePzINB5CO/LwcOTptNB4CP25L4TIbZzpU7UEGAA5wz1uF5rF9AAAAABJRU5ErkJggg=="/><element name="sliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAA8CAIAAADpFA0BAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADhJREFUeNrsy6ENACAMAMHClp2wYxZLAg5Fcu9e3OjuOKqqfTMzbs14CIZhGIZhGIZhGP4VLwEGAK/BBnVFpB0oAAAAAElFTkSuQmCC"/><element name="sliderThumb" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAA8CAIAAADpFA0BAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrsy7ENACAMBLE8++8caFFKKiRffU53112SGs3ttOohGIZhGIZhGIZh+Fe8BRgAiaUGde6NOSEAAAAASUVORK5CYII="/></elements></component></components></skin>';this.xml=null;if(window.DOMParser){parser=new DOMParser();this.xml=parser.parseFromString(this.text,"text/xml")}else{this.xml=new ActiveXObject("Microsoft.XMLDOM");this.xml.async="false";this.xml.loadXML(this.text)}return this}})(jwplayer);(function(a){_utils=a.utils;_css=_utils.css;_hide=function(b){_css(b,{display:"none"})};_show=function(b){_css(b,{display:"block"})};a.html5.display=function(k,G){var j={icons:true,showmute:false};var Q=_utils.extend({},j,G);var h=k;var P={};var e;var u;var w;var N;var s;var I;var A;var J=!_utils.exists(h.skin.getComponentSettings("display").bufferrotation)?15:parseInt(h.skin.getComponentSettings("display").bufferrotation,10);var q=!_utils.exists(h.skin.getComponentSettings("display").bufferinterval)?100:parseInt(h.skin.getComponentSettings("display").bufferinterval,10);var z=-1;var t="";var K=true;var d;var g=false;var n=false;var H=new a.html5.eventdispatcher();_utils.extend(this,H);var D={display:{style:{cursor:"pointer",top:0,left:0,overflow:"hidden"},click:m},display_icon:{style:{cursor:"pointer",position:"absolute",top:((h.skin.getSkinElement("display","background").height-h.skin.getSkinElement("display","playIcon").height)/2),left:((h.skin.getSkinElement("display","background").width-h.skin.getSkinElement("display","playIcon").width)/2),border:0,margin:0,padding:0,zIndex:3,display:"none"}},display_iconBackground:{style:{cursor:"pointer",position:"absolute",top:((u-h.skin.getSkinElement("display","background").height)/2),left:((e-h.skin.getSkinElement("display","background").width)/2),border:0,backgroundImage:(["url(",h.skin.getSkinElement("display","background").src,")"]).join(""),width:h.skin.getSkinElement("display","background").width,height:h.skin.getSkinElement("display","background").height,margin:0,padding:0,zIndex:2,display:"none"}},display_image:{style:{display:"none",width:e,height:u,position:"absolute",cursor:"pointer",left:0,top:0,margin:0,padding:0,textDecoration:"none",zIndex:1}},display_text:{style:{zIndex:4,position:"relative",opacity:0.8,backgroundColor:parseInt("000000",16),color:parseInt("ffffff",16),textAlign:"center",fontFamily:"Arial,sans-serif",padding:"0 5px",fontSize:14}}};h.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,p);h.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_MUTE,p);h.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,p);h.jwAddEventListener(a.api.events.JWPLAYER_ERROR,o);L();function L(){P.display=C("div","display");P.display_text=C("div","display_text");P.display.appendChild(P.display_text);P.display_image=C("img","display_image");P.display_image.onerror=function(R){_hide(P.display_image)};P.display_image.onload=y;P.display_icon=C("div","display_icon");P.display_iconBackground=C("div","display_iconBackground");P.display.appendChild(P.display_image);P.display_iconBackground.appendChild(P.display_icon);P.display.appendChild(P.display_iconBackground);f();setTimeout((function(){n=true;if(Q.icons.toString()=="true"){F()}}),1)}this.getDisplayElement=function(){return P.display};this.resize=function(S,R){_css(P.display,{width:S,height:R});_css(P.display_text,{width:(S-10),top:((R-P.display_text.getBoundingClientRect().height)/2)});_css(P.display_iconBackground,{top:((R-h.skin.getSkinElement("display","background").height)/2),left:((S-h.skin.getSkinElement("display","background").width)/2)});if(e!=S||u!=R){e=S;u=R;d=undefined;F()}c();p({})};this.show=function(){if(g){g=false;r(h.jwGetState())}};this.hide=function(){if(!g){B();g=true}};function y(R){w=P.display_image.naturalWidth;N=P.display_image.naturalHeight;c()}function c(){_utils.stretch(h.jwGetStretching(),P.display_image,e,u,w,N)}function C(R,T){var S=document.createElement(R);S.id=h.id+"_jwplayer_"+T;_css(S,D[T].style);return S}function f(){for(var R in P){if(_utils.exists(D[R].click)){P[R].onclick=D[R].click}}}function m(R){if(typeof R.preventDefault!="undefined"){R.preventDefault()}else{R.returnValue=false}if(h.jwGetState()!=a.api.events.state.PLAYING){h.jwPlay()}else{h.jwPause()}}function O(R){if(A){B();return}P.display_icon.style.backgroundImage=(["url(",h.skin.getSkinElement("display",R).src,")"]).join("");_css(P.display_icon,{width:h.skin.getSkinElement("display",R).width,height:h.skin.getSkinElement("display",R).height,top:(h.skin.getSkinElement("display","background").height-h.skin.getSkinElement("display",R).height)/2,left:(h.skin.getSkinElement("display","background").width-h.skin.getSkinElement("display",R).width)/2});b();if(_utils.exists(h.skin.getSkinElement("display",R+"Over"))){P.display_icon.onmouseover=function(S){P.display_icon.style.backgroundImage=["url(",h.skin.getSkinElement("display",R+"Over").src,")"].join("")};P.display_icon.onmouseout=function(S){P.display_icon.style.backgroundImage=["url(",h.skin.getSkinElement("display",R).src,")"].join("")}}else{P.display_icon.onmouseover=null;P.display_icon.onmouseout=null}}function B(){if(Q.icons.toString()=="true"){_hide(P.display_icon);_hide(P.display_iconBackground);M()}}function b(){if(!g&&Q.icons.toString()=="true"){_show(P.display_icon);_show(P.display_iconBackground);F()}}function o(R){A=true;B();P.display_text.innerHTML=R.error;_show(P.display_text);P.display_text.style.top=((u-P.display_text.getBoundingClientRect().height)/2)+"px"}function E(){P.display_image.style.display="none"}function p(R){if((R.type==a.api.events.JWPLAYER_PLAYER_STATE||R.type==a.api.events.JWPLAYER_PLAYLIST_ITEM)&&A){A=false;_hide(P.display_text)}var S=h.jwGetState();if(S==t){return}t=S;if(z>=0){clearTimeout(z)}if(K||h.jwGetState()==a.api.events.state.PLAYING||h.jwGetState()==a.api.events.state.PAUSED){r(h.jwGetState())}else{z=setTimeout(l(h.jwGetState()),500)}}function l(R){return(function(){r(R)})}function r(R){if(_utils.exists(I)){clearInterval(I);I=null;_utils.animations.rotate(P.display_icon,0)}switch(R){case a.api.events.state.BUFFERING:if(_utils.isIOS()){E();B()}else{if(h.jwGetPlaylist()[h.jwGetItem()].provider=="sound"){v()}s=0;I=setInterval(function(){s+=J;_utils.animations.rotate(P.display_icon,s%360)},q);O("bufferIcon");K=true}break;case a.api.events.state.PAUSED:if(!_utils.isIOS()){if(h.jwGetPlaylist()[h.jwGetItem()].provider!="sound"){_css(P.display_image,{background:"transparent no-repeat center center"})}O("playIcon");K=true}break;case a.api.events.state.IDLE:if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].image){v()}else{E()}O("playIcon");K=true;break;default:if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].provider=="sound"){if(_utils.isIOS()){E();K=false}else{v()}}else{E();K=false}if(h.jwGetMute()&&Q.showmute){O("muteIcon")}else{B()}break}z=-1}function v(){if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].image){_css(P.display_image,{display:"block"});P.display_image.src=_utils.getAbsolutePath(h.jwGetPlaylist()[h.jwGetItem()].image)}}function x(R){return function(){if(!n){return}if(!g&&d!=R){d=R;H.sendEvent(R,{component:"display",boundingRect:_utils.getDimensions(P.display_iconBackground)})}}}var F=x(a.api.events.JWPLAYER_COMPONENT_SHOW);var M=x(a.api.events.JWPLAYER_COMPONENT_HIDE);return this}})(jwplayer);(function(a){_css=a.utils.css;a.html5.dock=function(p,u){function q(){return{align:a.html5.view.positions.RIGHT}}var k=a.utils.extend({},q(),u);if(k.align=="FALSE"){return}var f={};var s=[];var g;var v;var d=false;var t=false;var e={x:0,y:0,width:0,height:0};var r;var j=new a.html5.eventdispatcher();_utils.extend(this,j);var m=document.createElement("div");m.id=p.id+"_jwplayer_dock";p.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,l);this.getDisplayElement=function(){return m};this.setButton=function(A,x,y,z){if(!x&&f[A]){a.utils.arrays.remove(s,A);m.removeChild(f[A].div);delete f[A]}else{if(x){if(!f[A]){f[A]={}}f[A].handler=x;f[A].outGraphic=y;f[A].overGraphic=z;if(!f[A].div){s.push(A);f[A].div=document.createElement("div");f[A].div.style.position="relative";m.appendChild(f[A].div);f[A].div.appendChild(document.createElement("img"));f[A].div.childNodes[0].style.position="absolute";f[A].div.childNodes[0].style.left=0;f[A].div.childNodes[0].style.top=0;f[A].div.childNodes[0].style.zIndex=10;f[A].div.childNodes[0].style.cursor="pointer";f[A].div.appendChild(document.createElement("img"));f[A].div.childNodes[1].style.position="absolute";f[A].div.childNodes[1].style.left=0;f[A].div.childNodes[1].style.top=0;if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}f[A].div.childNodes[1].style.zIndex=9;f[A].div.childNodes[1].style.cursor="pointer";f[A].div.onmouseover=function(){if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(p.skin.getSkinElement("dock","buttonOver")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","buttonOver").src}};f[A].div.onmouseout=function(){if(f[A].outGraphic){f[A].div.childNodes[0].src=f[A].outGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}};if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(f[A].outGraphic){f[A].div.childNodes[0].src=f[A].outGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}}if(x){f[A].div.onclick=function(B){B.preventDefault();a(p.id).callback(A);if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}}}}}h(g,v)};function h(x,J){if(s.length>0){var y=10;var I=y;var F=-1;var G=p.skin.getSkinElement("dock","button").height;var E=p.skin.getSkinElement("dock","button").width;var C=x-E-y;var H,B;if(k.align==a.html5.view.positions.LEFT){F=1;C=y}for(var z=0;z<s.length;z++){var K=Math.floor(I/J);if((I+G+y)>((K+1)*J)){I=((K+1)*J)+y;K=Math.floor(I/J)}var A=f[s[z]].div;A.style.top=(I%J)+"px";A.style.left=(C+(p.skin.getSkinElement("dock","button").width+y)*K*F)+"px";var D={x:a.utils.parseDimension(A.style.left),y:a.utils.parseDimension(A.style.top),width:E,height:G};if(!H||(D.x<=H.x&&D.y<=H.y)){H=D}if(!B||(D.x>=B.x&&D.y>=B.y)){B=D}I+=p.skin.getSkinElement("dock","button").height+y}e={x:H.x,y:H.y,width:B.x-H.x+B.width,height:H.y-B.y+B.height}}if(t!=p.jwGetFullscreen()||g!=x||v!=J){g=x;v=J;t=p.jwGetFullscreen();r=undefined;setTimeout(n,1)}}function b(x){return function(){if(!d&&r!=x&&s.length>0){r=x;j.sendEvent(x,{component:"dock",boundingRect:e})}}}function l(x){if(a.utils.isIOS()){switch(x.newstate){case a.api.events.state.IDLE:o();break;default:c();break}}}var n=b(a.api.events.JWPLAYER_COMPONENT_SHOW);var w=b(a.api.events.JWPLAYER_COMPONENT_HIDE);this.resize=h;var o=function(){_css(m,{display:"block"});if(d){d=false;n()}};var c=function(){_css(m,{display:"none"});if(!d){w();d=true}};this.hide=c;this.show=o;return this}})(jwplayer);(function(a){a.html5.eventdispatcher=function(d,b){var c=new a.events.eventdispatcher(b);a.utils.extend(this,c);this.sendEvent=function(e,f){if(!a.utils.exists(f)){f={}}a.utils.extend(f,{id:d,version:a.version,type:e});c.sendEvent(e,f)}}})(jwplayer);(function(a){var b={prefix:"http://l.longtailvideo.com/html5/",file:"logo.png",link:"http://www.longtailvideo.com/players/jw-flv-player/",margin:8,out:0.5,over:1,timeout:5,hide:true,position:"bottom-left"};_css=a.utils.css;a.html5.logo=function(n,r){var q=n;var u;var d;var t;var h=false;g();function g(){o();c();l()}function o(){if(b.prefix){var v=n.version.split(/\W/).splice(0,2).join("/");if(b.prefix.indexOf(v)<0){b.prefix+=v+"/"}}if(r.position==a.html5.view.positions.OVER){r.position=b.position}d=a.utils.extend({},b)}function c(){t=document.createElement("img");t.id=q.id+"_jwplayer_logo";t.style.display="none";t.onload=function(v){_css(t,k());q.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,j);p()};if(!d.file){return}if(d.file.indexOf("http://")===0){t.src=d.file}else{t.src=d.prefix+d.file}}if(!d.file){return}this.resize=function(w,v){};this.getDisplayElement=function(){return t};function l(){if(d.link){t.onmouseover=f;t.onmouseout=p;t.onclick=s}else{this.mouseEnabled=false}}function s(v){if(typeof v!="undefined"){v.stopPropagation()}if(!h){return}q.jwPause();q.jwSetFullscreen(false);if(d.link){window.open(d.link,"_top")}return}function p(v){if(d.link&&h){t.style.opacity=d.out}return}function f(v){if(d.hide.toString()=="true"&&h){t.style.opacity=d.over}return}function k(){var x={textDecoration:"none",position:"absolute",cursor:"pointer"};x.display=(d.hide.toString()=="true")?"none":"block";var w=d.position.toLowerCase().split("-");for(var v in w){x[w[v]]=d.margin}return x}function m(){if(d.hide.toString()=="true"){t.style.display="block";t.style.opacity=0;a.utils.fadeTo(t,d.out,0.1,parseFloat(t.style.opacity));u=setTimeout(function(){e()},d.timeout*1000)}h=true}function e(){h=false;if(d.hide.toString()=="true"){a.utils.fadeTo(t,0,0.1,parseFloat(t.style.opacity))}}function j(v){if(v.newstate==a.api.events.state.BUFFERING){clearTimeout(u);m()}}return this}})(jwplayer);(function(a){var c={ended:a.api.events.state.IDLE,playing:a.api.events.state.PLAYING,pause:a.api.events.state.PAUSED,buffering:a.api.events.state.BUFFERING};var e=a.utils;var b=e.css;var d=e.isIOS();a.html5.mediavideo=function(h,s){var r={abort:n,canplay:k,canplaythrough:k,durationchange:G,emptied:n,ended:k,error:u,loadeddata:G,loadedmetadata:G,loadstart:k,pause:k,play:n,playing:k,progress:v,ratechange:n,seeked:k,seeking:k,stalled:k,suspend:k,timeupdate:D,volumechange:n,waiting:k,canshowcurrentframe:n,dataunavailable:n,empty:n,load:z,loadedfirstframe:n};var j=new a.html5.eventdispatcher();e.extend(this,j);var y=h,l=s,m,B,A,x,f,H=false,C,p,q;o();this.load=function(J,K){if(typeof K=="undefined"){K=true}x=J;e.empty(m);q=0;if(J.levels&&J.levels.length>0){if(J.levels.length==1){m.src=J.levels[0].file}else{if(m.src){m.removeAttribute("src")}for(var I=0;I<J.levels.length;I++){var L=m.ownerDocument.createElement("source");L.src=J.levels[I].file;m.appendChild(L);q++}}}else{m.src=J.file}if(d){if(J.image){m.poster=J.image}m.controls="controls";m.style.display="block"}C=p=A=false;y.buffer=0;if(!e.exists(J.start)){J.start=0}y.duration=J.duration;j.sendEvent(a.api.events.JWPLAYER_MEDIA_LOADED);if((!d&&J.levels.length==1)||!H){m.load()}H=false;if(K){E(a.api.events.state.BUFFERING);j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:0});this.play()}};this.play=function(){if(B!=a.api.events.state.PLAYING){t();if(p){E(a.api.events.state.PLAYING)}else{E(a.api.events.state.BUFFERING)}m.play()}};this.pause=function(){m.pause();E(a.api.events.state.PAUSED)};this.seek=function(I){if(!(y.duration<=0||isNaN(y.duration))&&!(y.position<=0||isNaN(y.position))){m.currentTime=I;m.play()}};_stop=this.stop=function(I){if(!e.exists(I)){I=true}g();if(I){m.style.display="none";p=false;var J=navigator.userAgent;if(J.match(/chrome/i)){m.src=undefined}else{if(J.match(/safari/i)){m.removeAttribute("src")}else{m.src=""}}m.removeAttribute("controls");m.removeAttribute("poster");e.empty(m);m.load();H=true;if(m.webkitSupportsFullscreen){try{m.webkitExitFullscreen()}catch(K){}}}E(a.api.events.state.IDLE)};this.fullscreen=function(I){if(I===true){this.resize("100%","100%")}else{this.resize(y.config.width,y.config.height)}};this.resize=function(J,I){if(false){b(l,{width:J,height:I})}j.sendEvent(a.api.events.JWPLAYER_MEDIA_RESIZE,{fullscreen:y.fullscreen,width:J,hieght:I})};this.volume=function(I){if(!d){m.volume=I/100;y.volume=I;j.sendEvent(a.api.events.JWPLAYER_MEDIA_VOLUME,{volume:Math.round(I)})}};this.mute=function(I){if(!d){m.muted=I;y.mute=I;j.sendEvent(a.api.events.JWPLAYER_MEDIA_MUTE,{mute:I})}};this.getDisplayElement=function(){return m};this.hasChrome=function(){return false};function o(){m=document.createElement("video");B=a.api.events.state.IDLE;for(var I in r){m.addEventListener(I,function(J){if(e.exists(J.target.parentNode)){r[J.type](J)}},true)}m.setAttribute("x-webkit-airplay","allow");if(l.parentNode){l.parentNode.replaceChild(m,l)}if(!m.id){m.id=l.id}}function E(I){if(I==a.api.events.state.PAUSED&&B==a.api.events.state.IDLE){return}if(B!=I){var J=B;y.state=B=I;j.sendEvent(a.api.events.JWPLAYER_PLAYER_STATE,{oldstate:J,newstate:I})}}function n(I){}function v(K){var J;if(e.exists(K)&&K.lengthComputable&&K.total){J=K.loaded/K.total*100}else{if(e.exists(m.buffered)&&(m.buffered.length>0)){var I=m.buffered.length-1;if(I>=0){J=m.buffered.end(I)/m.duration*100}}}if(p===false&&B==a.api.events.state.BUFFERING){j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER_FULL);p=true}if(!C){if(J==100){C=true}if(e.exists(J)&&(J>y.buffer)){y.buffer=Math.round(J);j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:Math.round(J)})}}}function D(J){if(e.exists(J)&&e.exists(J.target)){if(!isNaN(J.target.duration)&&(isNaN(y.duration)||y.duration<1)){if(J.target.duration==Infinity){y.duration=0}else{y.duration=Math.round(J.target.duration*10)/10}}if(!A&&m.readyState>0){m.style.display="block";E(a.api.events.state.PLAYING)}if(B==a.api.events.state.PLAYING){if(!A&&m.readyState>0){A=true;try{if(m.currentTime<x.start){m.currentTime=x.start}}catch(I){}m.volume=y.volume/100;m.muted=y.mute}y.position=y.duration>0?(Math.round(J.target.currentTime*10)/10):0;j.sendEvent(a.api.events.JWPLAYER_MEDIA_TIME,{position:y.position,duration:y.duration});if(y.position>=y.duration&&(y.position>0||y.duration>0)){w()}}}v(J)}function z(I){}function k(I){if(c[I.type]){if(I.type=="ended"){w()}else{E(c[I.type])}}}function G(I){var J={height:I.target.videoHeight,width:I.target.videoWidth,duration:Math.round(I.target.duration*10)/10};if((y.duration===0||isNaN(y.duration))&&I.target.duration!=Infinity){y.duration=Math.round(I.target.duration*10)/10}j.sendEvent(a.api.events.JWPLAYER_MEDIA_META,{metadata:J})}function u(K){if(B==a.api.events.state.IDLE){return}var J="There was an error: ";if((K.target.error&&K.target.tagName.toLowerCase()=="video")||K.target.parentNode.error&&K.target.parentNode.tagName.toLowerCase()=="video"){var I=!e.exists(K.target.error)?K.target.parentNode.error:K.target.error;switch(I.code){case I.MEDIA_ERR_ABORTED:J="You aborted the video playback: ";break;case I.MEDIA_ERR_NETWORK:J="A network error caused the video download to fail part-way: ";break;case I.MEDIA_ERR_DECODE:J="The video playback was aborted due to a corruption problem or because the video used features your browser did not support: ";break;case I.MEDIA_ERR_SRC_NOT_SUPPORTED:J="The video could not be loaded, either because the server or network failed or because the format is not supported: ";break;default:J="An unknown error occurred: ";break}}else{if(K.target.tagName.toLowerCase()=="source"){q--;if(q>0){return}J="The video could not be loaded, either because the server or network failed or because the format is not supported: "}else{e.log("An unknown error occurred.  Continuing...");return}}_stop(false);J+=F();_error=true;j.sendEvent(a.api.events.JWPLAYER_ERROR,{error:J});return}function F(){var K="";for(var J in x.levels){var I=x.levels[J];var L=l.ownerDocument.createElement("source");K+=a.utils.getAbsolutePath(I.file);if(J<(x.levels.length-1)){K+=", "}}return K}function t(){if(!e.exists(f)){f=setInterval(function(){v()},100)}}function g(){clearInterval(f);f=null}function w(){if(B!=a.api.events.state.IDLE){_stop(false);j.sendEvent(a.api.events.JWPLAYER_MEDIA_COMPLETE)}}}})(jwplayer);(function(a){var c={ended:a.api.events.state.IDLE,playing:a.api.events.state.PLAYING,pause:a.api.events.state.PAUSED,buffering:a.api.events.state.BUFFERING};var b=a.utils.css;a.html5.mediayoutube=function(j,e){var f=new a.html5.eventdispatcher();a.utils.extend(this,f);var l=j;var h=document.getElementById(e.id);var g=a.api.events.state.IDLE;var n,m;function k(p){if(g!=p){var q=g;l.state=p;g=p;f.sendEvent(a.api.events.JWPLAYER_PLAYER_STATE,{oldstate:q,newstate:p})}}this.getDisplayElement=function(){return h};this.play=function(){if(g==a.api.events.state.IDLE){f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:100});f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER_FULL);k(a.api.events.state.PLAYING)}else{if(g==a.api.events.state.PAUSED){k(a.api.events.state.PLAYING)}}};this.pause=function(){k(a.api.events.state.PAUSED)};this.seek=function(p){};this.stop=function(p){if(!_utils.exists(p)){p=true}l.position=0;k(a.api.events.state.IDLE);if(p){b(h,{display:"none"})}};this.volume=function(p){l.volume=p;f.sendEvent(a.api.events.JWPLAYER_MEDIA_VOLUME,{volume:Math.round(p)})};this.mute=function(p){h.muted=p;l.mute=p;f.sendEvent(a.api.events.JWPLAYER_MEDIA_MUTE,{mute:p})};this.resize=function(q,p){if(q*p>0&&n){n.width=m.width=q;n.height=m.height=p}f.sendEvent(a.api.events.JWPLAYER_MEDIA_RESIZE,{fullscreen:l.fullscreen,width:q,height:p})};this.fullscreen=function(p){if(p===true){this.resize("100%","100%")}else{this.resize(l.config.width,l.config.height)}};this.load=function(p){o(p);b(n,{display:"block"});k(a.api.events.state.BUFFERING);f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:0});f.sendEvent(a.api.events.JWPLAYER_MEDIA_LOADED);this.play()};this.hasChrome=function(){return(g!=a.api.events.state.IDLE)};function o(v){var s=v.levels[0].file;s=["http://www.youtube.com/v/",d(s),"&amp;hl=en_US&amp;fs=1&autoplay=1"].join("");n=document.createElement("object");n.id=h.id;n.style.position="absolute";var u={movie:s,allowfullscreen:"true",allowscriptaccess:"always"};for(var p in u){var t=document.createElement("param");t.name=p;t.value=u[p];n.appendChild(t)}m=document.createElement("embed");n.appendChild(m);var q={src:s,type:"application/x-shockwave-flash",allowfullscreen:"true",allowscriptaccess:"always",width:n.width,height:n.height};for(var r in q){m.setAttribute(r,q[r])}n.appendChild(m);n.style.zIndex=2147483000;if(h!=n&&h.parentNode){h.parentNode.replaceChild(n,h)}h=n}function d(q){var p=q.split(/\?|\#\!/);var s="";for(var r=0;r<p.length;r++){if(p[r].substr(0,2)=="v="){s=p[r].substr(2)}}if(s==""){if(q.indexOf("/v/")>=0){s=q.substr(q.indexOf("/v/")+3)}else{if(q.indexOf("youtu.be")>=0){s=q.substr(q.indexOf("youtu.be/")+9)}else{s=q}}}if(s.indexOf("?")>-1){s=s.substr(0,s.indexOf("?"))}if(s.indexOf("&")>-1){s=s.substr(0,s.indexOf("&"))}return s}this.embed=m;return this}})(jwplayer);(function(jwplayer){var _configurableStateVariables=["width","height","start","duration","volume","mute","fullscreen","item","plugins","stretching"];jwplayer.html5.model=function(api,container,options){var _api=api;var _container=container;var _model={id:_container.id,playlist:[],state:jwplayer.api.events.state.IDLE,position:0,buffer:0,config:{width:480,height:320,item:-1,skin:undefined,file:undefined,image:undefined,start:0,duration:0,bufferlength:5,volume:90,mute:false,fullscreen:false,repeat:"",stretching:jwplayer.utils.stretching.UNIFORM,autostart:false,debug:undefined,screencolor:undefined}};var _media;var _eventDispatcher=new jwplayer.html5.eventdispatcher();var _components=["display","logo","controlbar","playlist","dock"];jwplayer.utils.extend(_model,_eventDispatcher);for(var option in options){if(typeof options[option]=="string"){var type=/color$/.test(option)?"color":null;options[option]=jwplayer.utils.typechecker(options[option],type)}var config=_model.config;var path=option.split(".");for(var edge in path){if(edge==path.length-1){config[path[edge]]=options[option]}else{if(!jwplayer.utils.exists(config[path[edge]])){config[path[edge]]={}}config=config[path[edge]]}}}for(var index in _configurableStateVariables){var configurableStateVariable=_configurableStateVariables[index];_model[configurableStateVariable]=_model.config[configurableStateVariable]}var pluginorder=_components.concat([]);if(jwplayer.utils.exists(_model.plugins)){if(typeof _model.plugins=="string"){var userplugins=_model.plugins.split(",");for(var userplugin in userplugins){if(typeof userplugins[userplugin]=="string"){pluginorder.push(userplugins[userplugin].replace(/^\s+|\s+$/g,""))}}}}if(jwplayer.utils.isIOS()){pluginorder=["display","logo","dock","playlist"];if(!jwplayer.utils.exists(_model.config.repeat)){_model.config.repeat="list"}}else{if(_model.config.chromeless){pluginorder=["logo","dock","playlist"];if(!jwplayer.utils.exists(_model.config.repeat)){_model.config.repeat="list"}}}_model.plugins={order:pluginorder,config:{},object:{}};if(typeof _model.config.components!="undefined"){for(var component in _model.config.components){_model.plugins.config[component]=_model.config.components[component]}}for(var pluginIndex in _model.plugins.order){var pluginName=_model.plugins.order[pluginIndex];var pluginConfig=!jwplayer.utils.exists(_model.plugins.config[pluginName])?{}:_model.plugins.config[pluginName];_model.plugins.config[pluginName]=!jwplayer.utils.exists(_model.plugins.config[pluginName])?pluginConfig:jwplayer.utils.extend(_model.plugins.config[pluginName],pluginConfig);if(!jwplayer.utils.exists(_model.plugins.config[pluginName].position)){if(pluginName=="playlist"){_model.plugins.config[pluginName].position=jwplayer.html5.view.positions.NONE}else{_model.plugins.config[pluginName].position=jwplayer.html5.view.positions.OVER}}else{_model.plugins.config[pluginName].position=_model.plugins.config[pluginName].position.toString().toUpperCase()}}if(typeof _model.plugins.config.dock!="undefined"){if(typeof _model.plugins.config.dock!="object"){var position=_model.plugins.config.dock.toString().toUpperCase();_model.plugins.config.dock={position:position}}if(typeof _model.plugins.config.dock.position!="undefined"){_model.plugins.config.dock.align=_model.plugins.config.dock.position;_model.plugins.config.dock.position=jwplayer.html5.view.positions.OVER}}function _loadExternal(playlistfile){var loader=new jwplayer.html5.playlistloader();loader.addEventListener(jwplayer.api.events.JWPLAYER_PLAYLIST_LOADED,function(evt){_model.playlist=new jwplayer.html5.playlist(evt);_loadComplete(true)});loader.addEventListener(jwplayer.api.events.JWPLAYER_ERROR,function(evt){_model.playlist=new jwplayer.html5.playlist({playlist:[]});_loadComplete(false)});loader.load(playlistfile)}function _loadComplete(){if(_model.config.shuffle){_model.item=_getShuffleItem()}else{if(_model.config.item>=_model.playlist.length){_model.config.item=_model.playlist.length-1}else{if(_model.config.item<0){_model.config.item=0}}_model.item=_model.config.item}_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_PLAYLIST_LOADED,{playlist:_model.playlist});_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_PLAYLIST_ITEM,{index:_model.item})}_model.loadPlaylist=function(arg){var input;if(typeof arg=="string"){if(arg.indexOf("[")==0||arg.indexOf("{")=="0"){try{input=eval(arg)}catch(err){input=arg}}else{input=arg}}else{input=arg}var config;switch(jwplayer.utils.typeOf(input)){case"object":config=input;break;case"array":config={playlist:input};break;default:_loadExternal(input);return;break}_model.playlist=new jwplayer.html5.playlist(config);if(jwplayer.utils.extension(_model.playlist[0].file)=="xml"){_loadExternal(_model.playlist[0].file)}else{_loadComplete()}};function _getShuffleItem(){var result=null;if(_model.playlist.length>1){while(!jwplayer.utils.exists(result)){result=Math.floor(Math.random()*_model.playlist.length);if(result==_model.item){result=null}}}else{result=0}return result}function forward(evt){if(evt.type==jwplayer.api.events.JWPLAYER_MEDIA_LOADED){_container=_media.getDisplayElement()}_eventDispatcher.sendEvent(evt.type,evt)}var _mediaProviders={};_model.setActiveMediaProvider=function(playlistItem){if(playlistItem.provider=="audio"){playlistItem.provider="sound"}var provider=playlistItem.provider;var current=_media?_media.getDisplayElement():null;if(provider=="sound"||provider=="http"||provider==""){provider="video"}if(!jwplayer.utils.exists(_mediaProviders[provider])){switch(provider){case"video":_media=new jwplayer.html5.mediavideo(_model,current?current:_container);break;case"youtube":_media=new jwplayer.html5.mediayoutube(_model,current?current:_container);break}if(!jwplayer.utils.exists(_media)){return false}_media.addGlobalListener(forward);_mediaProviders[provider]=_media}else{if(_media!=_mediaProviders[provider]){if(_media){_media.stop()}_media=_mediaProviders[provider]}}return true};_model.getMedia=function(){return _media};_model.seek=function(pos){_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_MEDIA_SEEK,{position:_model.position,offset:pos});return _media.seek(pos)};_model.setupPlugins=function(){if(!jwplayer.utils.exists(_model.plugins)||!jwplayer.utils.exists(_model.plugins.order)||_model.plugins.order.length==0){jwplayer.utils.log("No plugins to set up");return _model}for(var i=0;i<_model.plugins.order.length;i++){try{var pluginName=_model.plugins.order[i];if(jwplayer.utils.exists(jwplayer.html5[pluginName])){if(pluginName=="playlist"){_model.plugins.object[pluginName]=new jwplayer.html5.playlistcomponent(_api,_model.plugins.config[pluginName])}else{_model.plugins.object[pluginName]=new jwplayer.html5[pluginName](_api,_model.plugins.config[pluginName])}}else{_model.plugins.order.splice(plugin,plugin+1)}if(typeof _model.plugins.object[pluginName].addGlobalListener=="function"){_model.plugins.object[pluginName].addGlobalListener(forward)}}catch(err){jwplayer.utils.log("Could not setup "+pluginName)}}};return _model}})(jwplayer);(function(a){a.html5.playlist=function(b){var d=[];if(b.playlist&&b.playlist instanceof Array&&b.playlist.length>0){for(var c in b.playlist){if(!isNaN(parseInt(c))){d.push(new a.html5.playlistitem(b.playlist[c]))}}}else{d.push(new a.html5.playlistitem(b))}return d}})(jwplayer);(function(a){var c={size:180,position:a.html5.view.positions.NONE,itemheight:60,thumbs:true,fontcolor:"#000000",overcolor:"",activecolor:"",backgroundcolor:"#f8f8f8",font:"_sans",fontsize:"",fontstyle:"",fontweight:""};var b={_sans:"Arial, Helvetica, sans-serif",_serif:"Times, Times New Roman, serif",_typewriter:"Courier New, Courier, monospace"};_utils=a.utils;_css=_utils.css;_hide=function(d){_css(d,{display:"none"})};_show=function(d){_css(d,{display:"block"})};a.html5.playlistcomponent=function(r,B){var w=r;var e=a.utils.extend({},c,w.skin.getComponentSettings("playlist"),B);if(e.position==a.html5.view.positions.NONE||typeof a.html5.view.positions[e.position]=="undefined"){return}var x;var l;var C;var d;var g;var f;var k=-1;var h={background:undefined,item:undefined,itemOver:undefined,itemImage:undefined,itemActive:undefined};this.getDisplayElement=function(){return x};this.resize=function(F,D){l=F;C=D;if(w.jwGetFullscreen()){_hide(x)}else{var E={display:"block",width:l,height:C};_css(x,E)}};this.show=function(){_show(x)};this.hide=function(){_hide(x)};function j(){x=document.createElement("div");x.id=w.id+"_jwplayer_playlistcomponent";switch(e.position){case a.html5.view.positions.RIGHT:case a.html5.view.positions.LEFT:x.style.width=e.size+"px";break;case a.html5.view.positions.TOP:case a.html5.view.positions.BOTTOM:x.style.height=e.size+"px";break}A();if(h.item){e.itemheight=h.item.height}x.style.backgroundColor="#C6C6C6";w.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,s);w.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,u);w.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,m)}function p(){var D=document.createElement("ul");_css(D,{width:x.style.width,minWidth:x.style.width,height:x.style.height,backgroundColor:e.backgroundcolor,backgroundImage:h.background?"url("+h.background.src+")":"",color:e.fontcolor,listStyle:"none",margin:0,padding:0,fontFamily:b[e.font]?b[e.font]:b._sans,fontSize:(e.fontsize?e.fontsize:11)+"px",fontStyle:e.fontstyle,fontWeight:e.fontweight,overflowY:"auto"});return D}function y(D){return function(){var E=f.getElementsByClassName("item")[D];var F=e.fontcolor;var G=h.item?"url("+h.item.src+")":"";if(D==w.jwGetPlaylistIndex()){if(e.activecolor!==""){F=e.activecolor}if(h.itemActive){G="url("+h.itemActive.src+")"}}_css(E,{color:e.overcolor!==""?e.overcolor:F,backgroundImage:h.itemOver?"url("+h.itemOver.src+")":G})}}function o(D){return function(){var E=f.getElementsByClassName("item")[D];var F=e.fontcolor;var G=h.item?"url("+h.item.src+")":"";if(D==w.jwGetPlaylistIndex()){if(e.activecolor!==""){F=e.activecolor}if(h.itemActive){G="url("+h.itemActive.src+")"}}_css(E,{color:F,backgroundImage:G})}}function q(I){var P=d[I];var O=document.createElement("li");O.className="item";_css(O,{height:e.itemheight,display:"block",cursor:"pointer",backgroundImage:h.item?"url("+h.item.src+")":"",backgroundSize:"100% "+e.itemheight+"px"});O.onmouseover=y(I);O.onmouseout=o(I);var J=document.createElement("div");var F=new Image();var K=0;var L=0;var M=0;if(v()&&(P.image||P["playlist.image"]||h.itemImage)){F.className="image";if(h.itemImage){K=(e.itemheight-h.itemImage.height)/2;L=h.itemImage.width;M=h.itemImage.height}else{L=e.itemheight*4/3;M=e.itemheight}_css(J,{height:M,width:L,"float":"left",styleFloat:"left",cssFloat:"left",margin:"0 5px 0 0",background:"black",overflow:"hidden",margin:K+"px",position:"relative"});_css(F,{position:"relative"});J.appendChild(F);F.onload=function(){a.utils.stretch(a.utils.stretching.FILL,F,L,M,this.naturalWidth,this.naturalHeight)};if(P["playlist.image"]){F.src=P["playlist.image"]}else{if(P.image){F.src=P.image}else{if(h.itemImage){F.src=h.itemImage.src}}}O.appendChild(J)}var E=l-L-K*2;if(C<e.itemheight*d.length){E-=15}var D=document.createElement("div");_css(D,{position:"relative",height:"100%",overflow:"hidden"});var G=document.createElement("span");if(P.duration>0){G.className="duration";_css(G,{fontSize:(e.fontsize?e.fontsize:11)+"px",fontWeight:(e.fontweight?e.fontweight:"bold"),width:"40px",height:e.fontsize?e.fontsize+10:20,lineHeight:24,"float":"right",styleFloat:"right",cssFloat:"right"});G.innerHTML=_utils.timeFormat(P.duration);D.appendChild(G)}var N=document.createElement("span");N.className="title";_css(N,{padding:"5px 5px 0 "+(K?0:"5px"),height:e.fontsize?e.fontsize+10:20,lineHeight:e.fontsize?e.fontsize+10:20,overflow:"hidden","float":"left",styleFloat:"left",cssFloat:"left",width:((P.duration>0)?E-50:E)-10+"px",fontSize:(e.fontsize?e.fontsize:13)+"px",fontWeight:(e.fontweight?e.fontweight:"bold")});N.innerHTML=P?P.title:"";D.appendChild(N);if(P.description){var H=document.createElement("span");H.className="description";_css(H,{display:"block","float":"left",styleFloat:"left",cssFloat:"left",margin:0,paddingLeft:N.style.paddingLeft,paddingRight:N.style.paddingRight,lineHeight:(e.fontsize?e.fontsize+4:16)+"px",overflow:"hidden",position:"relative"});H.innerHTML=P.description;D.appendChild(H)}O.appendChild(D);return O}function s(E){x.innerHTML="";d=w.jwGetPlaylist();if(!d){return}items=[];f=p();for(var F=0;F<d.length;F++){var D=q(F);D.onclick=z(F);f.appendChild(D);items.push(D)}k=w.jwGetPlaylistIndex();o(k)();x.appendChild(f);if(_utils.isIOS()&&window.iScroll){f.style.height=e.itemheight*d.length+"px";var G=new iScroll(x.id)}}function z(D){return function(){w.jwPlaylistItem(D);w.jwPlay(true)}}function n(){f.scrollTop=w.jwGetPlaylistIndex()*e.itemheight}function v(){return e.thumbs.toString().toLowerCase()=="true"}function u(D){if(k>=0){o(k)();k=D.index}o(D.index)();n()}function m(){if(e.position==a.html5.view.positions.OVER){switch(w.jwGetState()){case a.api.events.state.IDLE:_show(x);break;default:_hide(x);break}}}function A(){for(var D in h){h[D]=t(D)}}function t(D){return w.skin.getSkinElement("playlist",D)}j();return this}})(jwplayer);(function(b){b.html5.playlistitem=function(d){var e={author:"",date:"",description:"",image:"",link:"",mediaid:"",tags:"",title:"",provider:"",file:"",streamer:"",duration:-1,start:0,currentLevel:-1,levels:[]};var c=b.utils.extend({},e,d);if(c.type){c.provider=c.type;delete c.type}if(c.levels.length===0){c.levels[0]=new b.html5.playlistitemlevel(c)}if(!c.provider){c.provider=a(c.levels[0])}else{c.provider=c.provider.toLowerCase()}return c};function a(e){if(b.utils.isYouTube(e.file)){return"youtube"}else{var f=b.utils.extension(e.file);var c;if(f&&b.utils.extensionmap[f]){if(f=="m3u8"){return"video"}c=b.utils.extensionmap[f].html5}else{if(e.type){c=e.type}}if(c){var d=c.split("/")[0];if(d=="audio"){return"sound"}else{if(d=="video"){return d}}}}return""}})(jwplayer);(function(a){a.html5.playlistitemlevel=function(b){var d={file:"",streamer:"",bitrate:0,width:0};for(var c in d){if(a.utils.exists(b[c])){d[c]=b[c]}}return d}})(jwplayer);(function(a){a.html5.playlistloader=function(){var c=new a.html5.eventdispatcher();a.utils.extend(this,c);this.load=function(e){a.utils.ajax(e,d,b)};function d(g){var f=[];try{var f=a.utils.parsers.rssparser.parse(g.responseXML.firstChild);c.sendEvent(a.api.events.JWPLAYER_PLAYLIST_LOADED,{playlist:new a.html5.playlist({playlist:f})})}catch(h){b("Could not parse the playlist")}}function b(e){c.sendEvent(a.api.events.JWPLAYER_ERROR,{error:e?e:"could not load playlist for whatever reason.  too bad"})}}})(jwplayer);(function(a){a.html5.skin=function(){var b={};var c=false;this.load=function(d,e){new a.html5.skinloader(d,function(f){c=true;b=f;e()},function(){new a.html5.skinloader("",function(f){c=true;b=f;e()})})};this.getSkinElement=function(d,e){if(c){try{return b[d].elements[e]}catch(f){a.utils.log("No such skin component / element: ",[d,e])}}return null};this.getComponentSettings=function(d){if(c){return b[d].settings}return null};this.getComponentLayout=function(d){if(c){return b[d].layout}return null}}})(jwplayer);(function(a){a.html5.skinloader=function(f,p,k){var o={};var c=p;var l=k;var e=true;var j;var n=f;var s=false;function m(){if(typeof n!="string"||n===""){d(a.html5.defaultSkin().xml)}else{a.utils.ajax(a.utils.getAbsolutePath(n),function(t){try{if(a.utils.exists(t.responseXML)){d(t.responseXML);return}}catch(u){h()}d(a.html5.defaultSkin().xml)},function(t){d(a.html5.defaultSkin().xml)})}}function d(y){var E=y.getElementsByTagName("component");if(E.length===0){return}for(var H=0;H<E.length;H++){var C=E[H].getAttribute("name");var B={settings:{},elements:{},layout:{}};o[C]=B;var G=E[H].getElementsByTagName("elements")[0].getElementsByTagName("element");for(var F=0;F<G.length;F++){b(G[F],C)}var z=E[H].getElementsByTagName("settings")[0];if(z&&z.childNodes.length>0){var K=z.getElementsByTagName("setting");for(var P=0;P<K.length;P++){var Q=K[P].getAttribute("name");var I=K[P].getAttribute("value");var x=/color$/.test(Q)?"color":null;o[C].settings[Q]=a.utils.typechecker(I,x)}}var L=E[H].getElementsByTagName("layout")[0];if(L&&L.childNodes.length>0){var M=L.getElementsByTagName("group");for(var w=0;w<M.length;w++){var A=M[w];o[C].layout[A.getAttribute("position")]={elements:[]};for(var O=0;O<A.attributes.length;O++){var D=A.attributes[O];o[C].layout[A.getAttribute("position")][D.name]=D.value}var N=A.getElementsByTagName("*");for(var v=0;v<N.length;v++){var t=N[v];o[C].layout[A.getAttribute("position")].elements.push({type:t.tagName});for(var u=0;u<t.attributes.length;u++){var J=t.attributes[u];o[C].layout[A.getAttribute("position")].elements[v][J.name]=J.value}if(!a.utils.exists(o[C].layout[A.getAttribute("position")].elements[v].name)){o[C].layout[A.getAttribute("position")].elements[v].name=t.tagName}}}}e=false;r()}}function r(){clearInterval(j);if(!s){j=setInterval(function(){q()},100)}}function b(y,x){var w=new Image();var t=y.getAttribute("name");var v=y.getAttribute("src");var A;if(v.indexOf("data:image/png;base64,")===0){A=v}else{var u=a.utils.getAbsolutePath(n);var z=u.substr(0,u.lastIndexOf("/"));A=[z,x,v].join("/")}o[x].elements[t]={height:0,width:0,src:"",ready:false,image:w};w.onload=function(B){g(w,t,x)};w.onerror=function(B){s=true;r();l()};w.src=A}function h(){for(var u in o){var w=o[u];for(var t in w.elements){var x=w.elements[t];var v=x.image;v.onload=null;v.onerror=null;delete x.image;delete w.elements[t]}delete o[u]}}function q(){for(var t in o){if(t!="properties"){for(var u in o[t].elements){if(!o[t].elements[u].ready){return}}}}if(e===false){clearInterval(j);c(o)}}function g(t,v,u){if(o[u]&&o[u].elements[v]){o[u].elements[v].height=t.height;o[u].elements[v].width=t.width;o[u].elements[v].src=t.src;o[u].elements[v].ready=true;r()}else{a.utils.log("Loaded an image for a missing element: "+u+"."+v)}}m()}})(jwplayer);(function(a){a.html5.api=function(c,n){var m={};var f=document.createElement("div");c.parentNode.replaceChild(f,c);f.id=c.id;m.version=a.version;m.id=f.id;var l=new a.html5.model(m,f,n);var j=new a.html5.view(m,f,l);var k=new a.html5.controller(m,f,l,j);m.skin=new a.html5.skin();m.jwPlay=function(o){if(typeof o=="undefined"){e()}else{if(o.toString().toLowerCase()=="true"){k.play()}else{k.pause()}}};m.jwPause=function(o){if(typeof o=="undefined"){e()}else{if(o.toString().toLowerCase()=="true"){k.pause()}else{k.play()}}};function e(){if(l.state==a.api.events.state.PLAYING||l.state==a.api.events.state.BUFFERING){k.pause()}else{k.play()}}m.jwStop=k.stop;m.jwSeek=k.seek;m.jwPlaylistItem=k.item;m.jwPlaylistNext=k.next;m.jwPlaylistPrev=k.prev;m.jwResize=k.resize;m.jwLoad=k.load;function h(o){return function(){return l[o]}}function d(o,q,p){return function(){var r=l.plugins.object[o];if(r&&r[q]&&typeof r[q]=="function"){r[q].apply(r,p)}}}m.jwGetItem=h("item");m.jwGetPosition=h("position");m.jwGetDuration=h("duration");m.jwGetBuffer=h("buffer");m.jwGetWidth=h("width");m.jwGetHeight=h("height");m.jwGetFullscreen=h("fullscreen");m.jwSetFullscreen=k.setFullscreen;m.jwGetVolume=h("volume");m.jwSetVolume=k.setVolume;m.jwGetMute=h("mute");m.jwSetMute=k.setMute;m.jwGetStretching=h("stretching");m.jwGetState=h("state");m.jwGetVersion=function(){return m.version};m.jwGetPlaylist=function(){return l.playlist};m.jwGetPlaylistIndex=m.jwGetItem;m.jwAddEventListener=k.addEventListener;m.jwRemoveEventListener=k.removeEventListener;m.jwSendEvent=k.sendEvent;m.jwDockSetButton=function(r,o,p,q){if(l.plugins.object.dock&&l.plugins.object.dock.setButton){l.plugins.object.dock.setButton(r,o,p,q)}};m.jwControlbarShow=d("controlbar","show");m.jwControlbarHide=d("controlbar","hide");m.jwDockShow=d("dock","show");m.jwDockHide=d("dock","hide");m.jwDisplayShow=d("display","show");m.jwDisplayHide=d("display","hide");m.jwGetLevel=function(){};m.jwGetBandwidth=function(){};m.jwGetLockState=function(){};m.jwLock=function(){};m.jwUnlock=function(){};function b(){if(l.config.playlistfile){l.addEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.loadPlaylist(l.config.playlistfile)}else{if(typeof l.config.playlist=="string"){l.addEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.loadPlaylist(l.config.playlist)}else{l.loadPlaylist(l.config);setTimeout(g,25)}}}function g(o){l.removeEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.setupPlugins();j.setup();var o={id:m.id,version:m.version};k.playerReady(o)}if(l.config.chromeless&&!a.utils.isIOS()){b()}else{m.skin.load(l.config.skin,b)}return m}})(jwplayer)};/*
       
  2782   mustache.js — Logic-less templates in JavaScript
       
  2783 
       
  2784   See http://mustache.github.com/ for more info.
       
  2785 */
       
  2786 
       
  2787 var Mustache = function() {
       
  2788   var Renderer = function() {};
       
  2789 
       
  2790   Renderer.prototype = {
       
  2791     otag: "{{",
       
  2792     ctag: "}}",
       
  2793     pragmas: {},
       
  2794     buffer: [],
       
  2795     pragmas_implemented: {
       
  2796       "IMPLICIT-ITERATOR": true
       
  2797     },
       
  2798     context: {},
       
  2799 
       
  2800     render: function(template, context, partials, in_recursion) {
       
  2801       // reset buffer & set context
       
  2802       if(!in_recursion) {
       
  2803         this.context = context;
       
  2804         this.buffer = []; // TODO: make this non-lazy
       
  2805       }
       
  2806 
       
  2807       // fail fast
       
  2808       if(!this.includes("", template)) {
       
  2809         if(in_recursion) {
       
  2810           return template;
       
  2811         } else {
       
  2812           this.send(template);
       
  2813           return;
       
  2814         }
       
  2815       }
       
  2816 
       
  2817       template = this.render_pragmas(template);
       
  2818       var html = this.render_section(template, context, partials);
       
  2819       if(in_recursion) {
       
  2820         return this.render_tags(html, context, partials, in_recursion);
       
  2821       }
       
  2822 
       
  2823       this.render_tags(html, context, partials, in_recursion);
       
  2824     },
       
  2825 
       
  2826     /*
       
  2827       Sends parsed lines
       
  2828     */
       
  2829     send: function(line) {
       
  2830       if(line !== "") {
       
  2831         this.buffer.push(line);
       
  2832       }
       
  2833     },
       
  2834 
       
  2835     /*
       
  2836       Looks for %PRAGMAS
       
  2837     */
       
  2838     render_pragmas: function(template) {
       
  2839       // no pragmas
       
  2840       if(!this.includes("%", template)) {
       
  2841         return template;
       
  2842       }
       
  2843 
       
  2844       var that = this;
       
  2845       var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
       
  2846             this.ctag, "g");
       
  2847       return template.replace(regex, function(match, pragma, options) {
       
  2848         if(!that.pragmas_implemented[pragma]) {
       
  2849           throw({message: 
       
  2850             "This implementation of mustache doesn't understand the '" +
       
  2851             pragma + "' pragma"});
       
  2852         }
       
  2853         that.pragmas[pragma] = {};
       
  2854         if(options) {
       
  2855           var opts = options.split("=");
       
  2856           that.pragmas[pragma][opts[0]] = opts[1];
       
  2857         }
       
  2858         return "";
       
  2859         // ignore unknown pragmas silently
       
  2860       });
       
  2861     },
       
  2862 
       
  2863     /*
       
  2864       Tries to find a partial in the curent scope and render it
       
  2865     */
       
  2866     render_partial: function(name, context, partials) {
       
  2867       name = this.trim(name);
       
  2868       if(!partials || partials[name] === undefined) {
       
  2869         throw({message: "unknown_partial '" + name + "'"});
       
  2870       }
       
  2871       if(typeof(context[name]) != "object") {
       
  2872         return this.render(partials[name], context, partials, true);
       
  2873       }
       
  2874       return this.render(partials[name], context[name], partials, true);
       
  2875     },
       
  2876 
       
  2877     /*
       
  2878       Renders inverted (^) and normal (#) sections
       
  2879     */
       
  2880     render_section: function(template, context, partials) {
       
  2881       if(!this.includes("#", template) && !this.includes("^", template)) {
       
  2882         return template;
       
  2883       }
       
  2884 
       
  2885       var that = this;
       
  2886       // CSW - Added "+?" so it finds the tighest bound, not the widest
       
  2887       var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
       
  2888               "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
       
  2889               "\\s*", "mg");
       
  2890 
       
  2891       // for each {{#foo}}{{/foo}} section do...
       
  2892       return template.replace(regex, function(match, type, name, content) {
       
  2893         var value = that.find(name, context);
       
  2894         if(type == "^") { // inverted section
       
  2895           if(!value || that.is_array(value) && value.length === 0) {
       
  2896             // false or empty list, render it
       
  2897             return that.render(content, context, partials, true);
       
  2898           } else {
       
  2899             return "";
       
  2900           }
       
  2901         } else if(type == "#") { // normal section
       
  2902           if(that.is_array(value)) { // Enumerable, Let's loop!
       
  2903             return that.map(value, function(row) {
       
  2904               return that.render(content, that.create_context(row),
       
  2905                 partials, true);
       
  2906             }).join("");
       
  2907           } else if(that.is_object(value)) { // Object, Use it as subcontext!
       
  2908             return that.render(content, that.create_context(value),
       
  2909               partials, true);
       
  2910           } else if(typeof value === "function") {
       
  2911             // higher order section
       
  2912             return value.call(context, content, function(text) {
       
  2913               return that.render(text, context, partials, true);
       
  2914             });
       
  2915           } else if(value) { // boolean section
       
  2916             return that.render(content, context, partials, true);
       
  2917           } else {
       
  2918             return "";
       
  2919           }
       
  2920         }
       
  2921       });
       
  2922     },
       
  2923 
       
  2924     /*
       
  2925       Replace {{foo}} and friends with values from our view
       
  2926     */
       
  2927     render_tags: function(template, context, partials, in_recursion) {
       
  2928       // tit for tat
       
  2929       var that = this;
       
  2930 
       
  2931       var new_regex = function() {
       
  2932         return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
       
  2933           that.ctag + "+", "g");
       
  2934       };
       
  2935 
       
  2936       var regex = new_regex();
       
  2937       var tag_replace_callback = function(match, operator, name) {
       
  2938         switch(operator) {
       
  2939         case "!": // ignore comments
       
  2940           return "";
       
  2941         case "=": // set new delimiters, rebuild the replace regexp
       
  2942           that.set_delimiters(name);
       
  2943           regex = new_regex();
       
  2944           return "";
       
  2945         case ">": // render partial
       
  2946           return that.render_partial(name, context, partials);
       
  2947         case "{": // the triple mustache is unescaped
       
  2948           return that.find(name, context);
       
  2949         default: // escape the value
       
  2950           return that.escape(that.find(name, context));
       
  2951         }
       
  2952       };
       
  2953       var lines = template.split("\n");
       
  2954       for(var i = 0; i < lines.length; i++) {
       
  2955         lines[i] = lines[i].replace(regex, tag_replace_callback, this);
       
  2956         if(!in_recursion) {
       
  2957           this.send(lines[i]);
       
  2958         }
       
  2959       }
       
  2960 
       
  2961       if(in_recursion) {
       
  2962         return lines.join("\n");
       
  2963       }
       
  2964     },
       
  2965 
       
  2966     set_delimiters: function(delimiters) {
       
  2967       var dels = delimiters.split(" ");
       
  2968       this.otag = this.escape_regex(dels[0]);
       
  2969       this.ctag = this.escape_regex(dels[1]);
       
  2970     },
       
  2971 
       
  2972     escape_regex: function(text) {
       
  2973       // thank you Simon Willison
       
  2974       if(!arguments.callee.sRE) {
       
  2975         var specials = [
       
  2976           '/', '.', '*', '+', '?', '|',
       
  2977           '(', ')', '[', ']', '{', '}', '\\'
       
  2978         ];
       
  2979         arguments.callee.sRE = new RegExp(
       
  2980           '(\\' + specials.join('|\\') + ')', 'g'
       
  2981         );
       
  2982       }
       
  2983       return text.replace(arguments.callee.sRE, '\\$1');
       
  2984     },
       
  2985 
       
  2986     /*
       
  2987       find `name` in current `context`. That is find me a value
       
  2988       from the view object
       
  2989     */
       
  2990     find: function(name, context) {
       
  2991       name = this.trim(name);
       
  2992 
       
  2993       // Checks whether a value is thruthy or false or 0
       
  2994       function is_kinda_truthy(bool) {
       
  2995         return bool === false || bool === 0 || bool;
       
  2996       }
       
  2997 
       
  2998       var value;
       
  2999       if(is_kinda_truthy(context[name])) {
       
  3000         value = context[name];
       
  3001       } else if(is_kinda_truthy(this.context[name])) {
       
  3002         value = this.context[name];
       
  3003       }
       
  3004 
       
  3005       if(typeof value === "function") {
       
  3006         return value.apply(context);
       
  3007       }
       
  3008       if(value !== undefined) {
       
  3009         return value;
       
  3010       }
       
  3011       // silently ignore unkown variables
       
  3012       return "";
       
  3013     },
       
  3014 
       
  3015     // Utility methods
       
  3016 
       
  3017     /* includes tag */
       
  3018     includes: function(needle, haystack) {
       
  3019       return haystack.indexOf(this.otag + needle) != -1;
       
  3020     },
       
  3021 
       
  3022     /*
       
  3023       Does away with nasty characters
       
  3024     */
       
  3025     escape: function(s) {
       
  3026       s = String(s === null ? "" : s);
       
  3027       return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
       
  3028         switch(s) {
       
  3029         case "&": return "&amp;";
       
  3030         case "\\": return "\\\\";
       
  3031         case '"': return '&quot;';
       
  3032         case "'": return '&#39;';
       
  3033         case "<": return "&lt;";
       
  3034         case ">": return "&gt;";
       
  3035         default: return s;
       
  3036         }
       
  3037       });
       
  3038     },
       
  3039 
       
  3040     // by @langalex, support for arrays of strings
       
  3041     create_context: function(_context) {
       
  3042       if(this.is_object(_context)) {
       
  3043         return _context;
       
  3044       } else {
       
  3045         var iterator = ".";
       
  3046         if(this.pragmas["IMPLICIT-ITERATOR"]) {
       
  3047           iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
       
  3048         }
       
  3049         var ctx = {};
       
  3050         ctx[iterator] = _context;
       
  3051         return ctx;
       
  3052       }
       
  3053     },
       
  3054 
       
  3055     is_object: function(a) {
       
  3056       return a && typeof a == "object";
       
  3057     },
       
  3058 
       
  3059     is_array: function(a) {
       
  3060       return Object.prototype.toString.call(a) === '[object Array]';
       
  3061     },
       
  3062 
       
  3063     /*
       
  3064       Gets rid of leading and trailing whitespace
       
  3065     */
       
  3066     trim: function(s) {
       
  3067       return s.replace(/^\s*|\s*$/g, "");
       
  3068     },
       
  3069 
       
  3070     /*
       
  3071       Why, why, why? Because IE. Cry, cry cry.
       
  3072     */
       
  3073     map: function(array, fn) {
       
  3074       if (typeof array.map == "function") {
       
  3075         return array.map(fn);
       
  3076       } else {
       
  3077         var r = [];
       
  3078         var l = array.length;
       
  3079         for(var i = 0; i < l; i++) {
       
  3080           r.push(fn(array[i]));
       
  3081         }
       
  3082         return r;
       
  3083       }
       
  3084     }
       
  3085   };
       
  3086 
       
  3087   return({
       
  3088     name: "mustache.js",
       
  3089     version: "0.3.1-dev",
       
  3090 
       
  3091     /*
       
  3092       Turns a template and view into HTML
       
  3093     */
       
  3094     to_html: function(template, view, partials, send_fun) {
       
  3095       var renderer = new Renderer();
       
  3096       if(send_fun) {
       
  3097         renderer.send = send_fun;
       
  3098       }
       
  3099       renderer.render(template, view, partials);
       
  3100       if(!send_fun) {
       
  3101         return renderer.buffer.join("\n");
       
  3102       }
       
  3103     }
       
  3104   });
       
  3105 }();
       
  3106 // ┌─────────────────────────────────────────────────────────────────────┐ \\
       
  3107 // │ Raphaël 2.0 - JavaScript Vector Library                             │ \\
       
  3108 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  3109 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
       
  3110 // │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
       
  3111 // │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
       
  3112 // └─────────────────────────────────────────────────────────────────────┘ \\
       
  3113 
       
  3114 // ┌──────────────────────────────────────────────────────────────────────────────────────┐ \\
       
  3115 // │ Eve 0.3.2 - JavaScript Events Library                                                │ \\
       
  3116 // ├──────────────────────────────────────────────────────────────────────────────────────┤ \\
       
  3117 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/)          │ \\
       
  3118 // │ Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. │ \\
       
  3119 // └──────────────────────────────────────────────────────────────────────────────────────┘ \\
       
  3120 
       
  3121 (function (glob) {
       
  3122     var version = "0.3.2",
       
  3123         has = "hasOwnProperty",
       
  3124         separator = /[\.\/]/,
       
  3125         wildcard = "*",
       
  3126         fun = function () {},
       
  3127         numsort = function (a, b) {
       
  3128             return a - b;
       
  3129         },
       
  3130         current_event,
       
  3131         stop,
       
  3132         events = {n: {}},
       
  3133     
       
  3134         eve = function (name, scope) {
       
  3135             var e = events,
       
  3136                 oldstop = stop,
       
  3137                 args = Array.prototype.slice.call(arguments, 2),
       
  3138                 listeners = eve.listeners(name),
       
  3139                 z = 0,
       
  3140                 f = false,
       
  3141                 l,
       
  3142                 indexed = [],
       
  3143                 queue = {},
       
  3144                 out = [],
       
  3145                 errors = [];
       
  3146             current_event = name;
       
  3147             stop = 0;
       
  3148             for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
       
  3149                 indexed.push(listeners[i].zIndex);
       
  3150                 if (listeners[i].zIndex < 0) {
       
  3151                     queue[listeners[i].zIndex] = listeners[i];
       
  3152                 }
       
  3153             }
       
  3154             indexed.sort(numsort);
       
  3155             while (indexed[z] < 0) {
       
  3156                 l = queue[indexed[z++]];
       
  3157                 out.push(l.apply(scope, args));
       
  3158                 if (stop) {
       
  3159                     stop = oldstop;
       
  3160                     return out;
       
  3161                 }
       
  3162             }
       
  3163             for (i = 0; i < ii; i++) {
       
  3164                 l = listeners[i];
       
  3165                 if ("zIndex" in l) {
       
  3166                     if (l.zIndex == indexed[z]) {
       
  3167                         out.push(l.apply(scope, args));
       
  3168                         if (stop) {
       
  3169                             stop = oldstop;
       
  3170                             return out;
       
  3171                         }
       
  3172                         do {
       
  3173                             z++;
       
  3174                             l = queue[indexed[z]];
       
  3175                             l && out.push(l.apply(scope, args));
       
  3176                             if (stop) {
       
  3177                                 stop = oldstop;
       
  3178                                 return out;
       
  3179                             }
       
  3180                         } while (l)
       
  3181                     } else {
       
  3182                         queue[l.zIndex] = l;
       
  3183                     }
       
  3184                 } else {
       
  3185                     out.push(l.apply(scope, args));
       
  3186                     if (stop) {
       
  3187                         stop = oldstop;
       
  3188                         return out;
       
  3189                     }
       
  3190                 }
       
  3191             }
       
  3192             stop = oldstop;
       
  3193             return out.length ? out : null;
       
  3194         };
       
  3195     
       
  3196     eve.listeners = function (name) {
       
  3197         var names = name.split(separator),
       
  3198             e = events,
       
  3199             item,
       
  3200             items,
       
  3201             k,
       
  3202             i,
       
  3203             ii,
       
  3204             j,
       
  3205             jj,
       
  3206             nes,
       
  3207             es = [e],
       
  3208             out = [];
       
  3209         for (i = 0, ii = names.length; i < ii; i++) {
       
  3210             nes = [];
       
  3211             for (j = 0, jj = es.length; j < jj; j++) {
       
  3212                 e = es[j].n;
       
  3213                 items = [e[names[i]], e[wildcard]];
       
  3214                 k = 2;
       
  3215                 while (k--) {
       
  3216                     item = items[k];
       
  3217                     if (item) {
       
  3218                         nes.push(item);
       
  3219                         out = out.concat(item.f || []);
       
  3220                     }
       
  3221                 }
       
  3222             }
       
  3223             es = nes;
       
  3224         }
       
  3225         return out;
       
  3226     };
       
  3227     
       
  3228     
       
  3229     eve.on = function (name, f) {
       
  3230         var names = name.split(separator),
       
  3231             e = events;
       
  3232         for (var i = 0, ii = names.length; i < ii; i++) {
       
  3233             e = e.n;
       
  3234             !e[names[i]] && (e[names[i]] = {n: {}});
       
  3235             e = e[names[i]];
       
  3236         }
       
  3237         e.f = e.f || [];
       
  3238         for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
       
  3239             return fun;
       
  3240         }
       
  3241         e.f.push(f);
       
  3242         return function (zIndex) {
       
  3243             if (+zIndex == +zIndex) {
       
  3244                 f.zIndex = +zIndex;
       
  3245             }
       
  3246         };
       
  3247     };
       
  3248     
       
  3249     eve.stop = function () {
       
  3250         stop = 1;
       
  3251     };
       
  3252     
       
  3253     eve.nt = function (subname) {
       
  3254         if (subname) {
       
  3255             return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
       
  3256         }
       
  3257         return current_event;
       
  3258     };
       
  3259     
       
  3260     eve.unbind = function (name, f) {
       
  3261         var names = name.split(separator),
       
  3262             e,
       
  3263             key,
       
  3264             splice,
       
  3265             cur = [events];
       
  3266         for (var i = 0, ii = names.length; i < ii; i++) {
       
  3267             for (var j = 0; j < cur.length; j += splice.length - 2) {
       
  3268                 splice = [j, 1];
       
  3269                 e = cur[j].n;
       
  3270                 if (names[i] != wildcard) {
       
  3271                     if (e[names[i]]) {
       
  3272                         splice.push(e[names[i]]);
       
  3273                     }
       
  3274                 } else {
       
  3275                     for (key in e) if (e[has](key)) {
       
  3276                         splice.push(e[key]);
       
  3277                     }
       
  3278                 }
       
  3279                 cur.splice.apply(cur, splice);
       
  3280             }
       
  3281         }
       
  3282         for (i = 0, ii = cur.length; i < ii; i++) {
       
  3283             e = cur[i];
       
  3284             while (e.n) {
       
  3285                 if (f) {
       
  3286                     if (e.f) {
       
  3287                         for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
       
  3288                             e.f.splice(j, 1);
       
  3289                             break;
       
  3290                         }
       
  3291                         !e.f.length && delete e.f;
       
  3292                     }
       
  3293                     for (key in e.n) if (e.n[has](key) && e.n[key].f) {
       
  3294                         var funcs = e.n[key].f;
       
  3295                         for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
       
  3296                             funcs.splice(j, 1);
       
  3297                             break;
       
  3298                         }
       
  3299                         !funcs.length && delete e.n[key].f;
       
  3300                     }
       
  3301                 } else {
       
  3302                     delete e.f;
       
  3303                     for (key in e.n) if (e.n[has](key) && e.n[key].f) {
       
  3304                         delete e.n[key].f;
       
  3305                     }
       
  3306                 }
       
  3307                 e = e.n;
       
  3308             }
       
  3309         }
       
  3310     };
       
  3311     
       
  3312     eve.version = version;
       
  3313     eve.toString = function () {
       
  3314         return "You are running Eve " + version;
       
  3315     };
       
  3316     (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (glob.eve = eve);
       
  3317 })(this);
       
  3318 
       
  3319 // ┌─────────────────────────────────────────────────────────────────────┐ \\
       
  3320 // │ "Raphaël 2.0" - JavaScript Vector Library                           │ \\
       
  3321 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  3322 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
       
  3323 // │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
       
  3324 // │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
       
  3325 // └─────────────────────────────────────────────────────────────────────┘ \\
       
  3326 (function () {
       
  3327     
       
  3328     function R(first) {
       
  3329         if (R.is(first, "function")) {
       
  3330             return loaded ? first() : eve.on("DOMload", first);
       
  3331         } else if (R.is(first, array)) {
       
  3332             var a = first,
       
  3333                 cnv = R._engine.create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
       
  3334                 res = cnv.set(),
       
  3335                 i = 0,
       
  3336                 ii = a.length,
       
  3337                 j;
       
  3338             for (; i < ii; i++) {
       
  3339                 j = a[i] || {};
       
  3340                 elements[has](j.type) && res.push(cnv[j.type]().attr(j));
       
  3341             }
       
  3342             return res;
       
  3343         } else {
       
  3344             var args = Array.prototype.slice.call(arguments, 0);
       
  3345             if (R.is(args[args.length - 1], "function")) {
       
  3346                 var f = args.pop();
       
  3347                 return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("DOMload", function () {
       
  3348                     f.call(R._engine.create[apply](R, args));
       
  3349                 });
       
  3350             } else {
       
  3351                 return R._engine.create[apply](R, arguments);
       
  3352             }
       
  3353         }
       
  3354     }
       
  3355     R.version = "2.0.0";
       
  3356     R.eve = eve;
       
  3357     var loaded,
       
  3358         separator = /[, ]+/,
       
  3359         elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
       
  3360         formatrg = /\{(\d+)\}/g,
       
  3361         proto = "prototype",
       
  3362         has = "hasOwnProperty",
       
  3363         g = {
       
  3364             doc: document,
       
  3365             win: window
       
  3366         },
       
  3367         oldRaphael = {
       
  3368             was: Object.prototype[has].call(g.win, "Raphael"),
       
  3369             is: g.win.Raphael
       
  3370         },
       
  3371         Paper = function () {
       
  3372             
       
  3373             
       
  3374             this.ca = this.customAttributes = {};
       
  3375         },
       
  3376         paperproto,
       
  3377         appendChild = "appendChild",
       
  3378         apply = "apply",
       
  3379         concat = "concat",
       
  3380         supportsTouch = "createTouch" in g.doc,
       
  3381         E = "",
       
  3382         S = " ",
       
  3383         Str = String,
       
  3384         split = "split",
       
  3385         events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
       
  3386         touchMap = {
       
  3387             mousedown: "touchstart",
       
  3388             mousemove: "touchmove",
       
  3389             mouseup: "touchend"
       
  3390         },
       
  3391         lowerCase = Str.prototype.toLowerCase,
       
  3392         math = Math,
       
  3393         mmax = math.max,
       
  3394         mmin = math.min,
       
  3395         abs = math.abs,
       
  3396         pow = math.pow,
       
  3397         PI = math.PI,
       
  3398         nu = "number",
       
  3399         string = "string",
       
  3400         array = "array",
       
  3401         toString = "toString",
       
  3402         fillString = "fill",
       
  3403         objectToString = Object.prototype.toString,
       
  3404         paper = {},
       
  3405         push = "push",
       
  3406         ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
       
  3407         colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
       
  3408         isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
       
  3409         bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
       
  3410         round = math.round,
       
  3411         setAttribute = "setAttribute",
       
  3412         toFloat = parseFloat,
       
  3413         toInt = parseInt,
       
  3414         upperCase = Str.prototype.toUpperCase,
       
  3415         availableAttrs = R._availableAttrs = {
       
  3416             "arrow-end": "none",
       
  3417             "arrow-start": "none",
       
  3418             blur: 0,
       
  3419             "clip-rect": "0 0 1e9 1e9",
       
  3420             cursor: "default",
       
  3421             cx: 0,
       
  3422             cy: 0,
       
  3423             fill: "#fff",
       
  3424             "fill-opacity": 1,
       
  3425             font: '10px "Arial"',
       
  3426             "font-family": '"Arial"',
       
  3427             "font-size": "10",
       
  3428             "font-style": "normal",
       
  3429             "font-weight": 400,
       
  3430             gradient: 0,
       
  3431             height: 0,
       
  3432             href: "http://raphaeljs.com/",
       
  3433             opacity: 1,
       
  3434             path: "M0,0",
       
  3435             r: 0,
       
  3436             rx: 0,
       
  3437             ry: 0,
       
  3438             src: "",
       
  3439             stroke: "#000",
       
  3440             "stroke-dasharray": "",
       
  3441             "stroke-linecap": "butt",
       
  3442             "stroke-linejoin": "butt",
       
  3443             "stroke-miterlimit": 0,
       
  3444             "stroke-opacity": 1,
       
  3445             "stroke-width": 1,
       
  3446             target: "_blank",
       
  3447             "text-anchor": "middle",
       
  3448             title: "Raphael",
       
  3449             transform: "",
       
  3450             width: 0,
       
  3451             x: 0,
       
  3452             y: 0
       
  3453         },
       
  3454         availableAnimAttrs = R._availableAnimAttrs = {
       
  3455             blur: nu,
       
  3456             "clip-rect": "csv",
       
  3457             cx: nu,
       
  3458             cy: nu,
       
  3459             fill: "colour",
       
  3460             "fill-opacity": nu,
       
  3461             "font-size": nu,
       
  3462             height: nu,
       
  3463             opacity: nu,
       
  3464             path: "path",
       
  3465             r: nu,
       
  3466             rx: nu,
       
  3467             ry: nu,
       
  3468             stroke: "colour",
       
  3469             "stroke-opacity": nu,
       
  3470             "stroke-width": nu,
       
  3471             transform: "transform",
       
  3472             width: nu,
       
  3473             x: nu,
       
  3474             y: nu
       
  3475         },
       
  3476         commaSpaces = /\s*,\s*/,
       
  3477         hsrg = {hs: 1, rg: 1},
       
  3478         p2s = /,?([achlmqrstvxz]),?/gi,
       
  3479         pathCommand = /([achlmrqstvz])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
       
  3480         tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
       
  3481         pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)\s*,?\s*/ig,
       
  3482         radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
       
  3483         eldata = {},
       
  3484         sortByKey = function (a, b) {
       
  3485             return a.key - b.key;
       
  3486         },
       
  3487         sortByNumber = function (a, b) {
       
  3488             return toFloat(a) - toFloat(b);
       
  3489         },
       
  3490         fun = function () {},
       
  3491         pipe = function (x) {
       
  3492             return x;
       
  3493         },
       
  3494         rectPath = R._rectPath = function (x, y, w, h, r) {
       
  3495             if (r) {
       
  3496                 return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
       
  3497             }
       
  3498             return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
       
  3499         },
       
  3500         ellipsePath = function (x, y, rx, ry) {
       
  3501             if (ry == null) {
       
  3502                 ry = rx;
       
  3503             }
       
  3504             return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
       
  3505         },
       
  3506         getPath = R._getPath = {
       
  3507             path: function (el) {
       
  3508                 return el.attr("path");
       
  3509             },
       
  3510             circle: function (el) {
       
  3511                 var a = el.attrs;
       
  3512                 return ellipsePath(a.cx, a.cy, a.r);
       
  3513             },
       
  3514             ellipse: function (el) {
       
  3515                 var a = el.attrs;
       
  3516                 return ellipsePath(a.cx, a.cy, a.rx, a.ry);
       
  3517             },
       
  3518             rect: function (el) {
       
  3519                 var a = el.attrs;
       
  3520                 return rectPath(a.x, a.y, a.width, a.height, a.r);
       
  3521             },
       
  3522             image: function (el) {
       
  3523                 var a = el.attrs;
       
  3524                 return rectPath(a.x, a.y, a.width, a.height);
       
  3525             },
       
  3526             text: function (el) {
       
  3527                 var bbox = el._getBBox();
       
  3528                 return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
       
  3529             }
       
  3530         },
       
  3531         mapPath = R.mapPath = function (path, matrix) {
       
  3532             if (!matrix) {
       
  3533                 return path;
       
  3534             }
       
  3535             var x, y, i, j, pathi;
       
  3536             path = path2curve(path);
       
  3537             for (i = 0, ii = path.length; i < ii; i++) {
       
  3538                 pathi = path[i];
       
  3539                 for (j = 1, jj = pathi.length; j < jj; j += 2) {
       
  3540                     x = matrix.x(pathi[j], pathi[j + 1]);
       
  3541                     y = matrix.y(pathi[j], pathi[j + 1]);
       
  3542                     pathi[j] = x;
       
  3543                     pathi[j + 1] = y;
       
  3544                 }
       
  3545             }
       
  3546             return path;
       
  3547         };
       
  3548 
       
  3549     R._g = g;
       
  3550     
       
  3551     R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
       
  3552     if (R.type == "VML") {
       
  3553         var d = g.doc.createElement("div"),
       
  3554             b;
       
  3555         d.innerHTML = '<v:shape adj="1"/>';
       
  3556         b = d.firstChild;
       
  3557         b.style.behavior = "url(#default#VML)";
       
  3558         if (!(b && typeof b.adj == "object")) {
       
  3559             return (R.type = E);
       
  3560         }
       
  3561         d = null;
       
  3562     }
       
  3563     
       
  3564     
       
  3565     R.svg = !(R.vml = R.type == "VML");
       
  3566     R._Paper = Paper;
       
  3567     
       
  3568     R.fn = paperproto = Paper.prototype = R.prototype;
       
  3569     R._id = 0;
       
  3570     R._oid = 0;
       
  3571     
       
  3572     R.is = function (o, type) {
       
  3573         type = lowerCase.call(type);
       
  3574         if (type == "finite") {
       
  3575             return !isnan[has](+o);
       
  3576         }
       
  3577         if (type == "array") {
       
  3578             return o instanceof Array;
       
  3579         }
       
  3580         return  (type == "null" && o === null) ||
       
  3581                 (type == typeof o && o !== null) ||
       
  3582                 (type == "object" && o === Object(o)) ||
       
  3583                 (type == "array" && Array.isArray && Array.isArray(o)) ||
       
  3584                 objectToString.call(o).slice(8, -1).toLowerCase() == type;
       
  3585     };
       
  3586     
       
  3587     R.angle = function (x1, y1, x2, y2, x3, y3) {
       
  3588         if (x3 == null) {
       
  3589             var x = x1 - x2,
       
  3590                 y = y1 - y2;
       
  3591             if (!x && !y) {
       
  3592                 return 0;
       
  3593             }
       
  3594             return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
       
  3595         } else {
       
  3596             return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
       
  3597         }
       
  3598     };
       
  3599     
       
  3600     R.rad = function (deg) {
       
  3601         return deg % 360 * PI / 180;
       
  3602     };
       
  3603     
       
  3604     R.deg = function (rad) {
       
  3605         return rad * 180 / PI % 360;
       
  3606     };
       
  3607     
       
  3608     R.snapTo = function (values, value, tolerance) {
       
  3609         tolerance = R.is(tolerance, "finite") ? tolerance : 10;
       
  3610         if (R.is(values, array)) {
       
  3611             var i = values.length;
       
  3612             while (i--) if (abs(values[i] - value) <= tolerance) {
       
  3613                 return values[i];
       
  3614             }
       
  3615         } else {
       
  3616             values = +values;
       
  3617             var rem = value % values;
       
  3618             if (rem < tolerance) {
       
  3619                 return value - rem;
       
  3620             }
       
  3621             if (rem > values - tolerance) {
       
  3622                 return value - rem + values;
       
  3623             }
       
  3624         }
       
  3625         return value;
       
  3626     };
       
  3627     
       
  3628     
       
  3629     var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
       
  3630         return function () {
       
  3631             return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
       
  3632         };
       
  3633     })(/[xy]/g, function (c) {
       
  3634         var r = math.random() * 16 | 0,
       
  3635             v = c == "x" ? r : (r & 3 | 8);
       
  3636         return v.toString(16);
       
  3637     });
       
  3638 
       
  3639     
       
  3640     R.setWindow = function (newwin) {
       
  3641         eve("setWindow", R, g.win, newwin);
       
  3642         g.win = newwin;
       
  3643         g.doc = g.win.document;
       
  3644         if (initWin) {
       
  3645             initWin(g.win);
       
  3646         }
       
  3647     };
       
  3648     var toHex = function (color) {
       
  3649         if (R.vml) {
       
  3650             // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
       
  3651             var trim = /^\s+|\s+$/g;
       
  3652             var bod;
       
  3653             try {
       
  3654                 var docum = new ActiveXObject("htmlfile");
       
  3655                 docum.write("<body>");
       
  3656                 docum.close();
       
  3657                 bod = docum.body;
       
  3658             } catch(e) {
       
  3659                 bod = createPopup().document.body;
       
  3660             }
       
  3661             var range = bod.createTextRange();
       
  3662             toHex = cacher(function (color) {
       
  3663                 try {
       
  3664                     bod.style.color = Str(color).replace(trim, E);
       
  3665                     var value = range.queryCommandValue("ForeColor");
       
  3666                     value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
       
  3667                     return "#" + ("000000" + value.toString(16)).slice(-6);
       
  3668                 } catch(e) {
       
  3669                     return "none";
       
  3670                 }
       
  3671             });
       
  3672         } else {
       
  3673             var i = g.doc.createElement("i");
       
  3674             i.title = "Rapha\xebl Colour Picker";
       
  3675             i.style.display = "none";
       
  3676             g.doc.body.appendChild(i);
       
  3677             toHex = cacher(function (color) {
       
  3678                 i.style.color = color;
       
  3679                 return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
       
  3680             });
       
  3681         }
       
  3682         return toHex(color);
       
  3683     },
       
  3684     hsbtoString = function () {
       
  3685         return "hsb(" + [this.h, this.s, this.b] + ")";
       
  3686     },
       
  3687     hsltoString = function () {
       
  3688         return "hsl(" + [this.h, this.s, this.l] + ")";
       
  3689     },
       
  3690     rgbtoString = function () {
       
  3691         return this.hex;
       
  3692     },
       
  3693     prepareRGB = function (r, g, b) {
       
  3694         if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
       
  3695             b = r.b;
       
  3696             g = r.g;
       
  3697             r = r.r;
       
  3698         }
       
  3699         if (g == null && R.is(r, string)) {
       
  3700             var clr = R.getRGB(r);
       
  3701             r = clr.r;
       
  3702             g = clr.g;
       
  3703             b = clr.b;
       
  3704         }
       
  3705         if (r > 1 || g > 1 || b > 1) {
       
  3706             r /= 255;
       
  3707             g /= 255;
       
  3708             b /= 255;
       
  3709         }
       
  3710         
       
  3711         return [r, g, b];
       
  3712     },
       
  3713     packageRGB = function (r, g, b, o) {
       
  3714         r *= 255;
       
  3715         g *= 255;
       
  3716         b *= 255;
       
  3717         var rgb = {
       
  3718             r: r,
       
  3719             g: g,
       
  3720             b: b,
       
  3721             hex: R.rgb(r, g, b),
       
  3722             toString: rgbtoString
       
  3723         };
       
  3724         R.is(o, "finite") && (rgb.opacity = o);
       
  3725         return rgb;
       
  3726     };
       
  3727     
       
  3728     
       
  3729     R.color = function (clr) {
       
  3730         var rgb;
       
  3731         if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
       
  3732             rgb = R.hsb2rgb(clr);
       
  3733             clr.r = rgb.r;
       
  3734             clr.g = rgb.g;
       
  3735             clr.b = rgb.b;
       
  3736             clr.hex = rgb.hex;
       
  3737         } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
       
  3738             rgb = R.hsl2rgb(clr);
       
  3739             clr.r = rgb.r;
       
  3740             clr.g = rgb.g;
       
  3741             clr.b = rgb.b;
       
  3742             clr.hex = rgb.hex;
       
  3743         } else {
       
  3744             if (R.is(clr, "string")) {
       
  3745                 clr = R.getRGB(clr);
       
  3746             }
       
  3747             if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
       
  3748                 rgb = R.rgb2hsl(clr);
       
  3749                 clr.h = rgb.h;
       
  3750                 clr.s = rgb.s;
       
  3751                 clr.l = rgb.l;
       
  3752                 rgb = R.rgb2hsb(clr);
       
  3753                 clr.v = rgb.b;
       
  3754             } else {
       
  3755                 clr = {hex: "none"};
       
  3756                 crl.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
       
  3757             }
       
  3758         }
       
  3759         clr.toString = rgbtoString;
       
  3760         return clr;
       
  3761     };
       
  3762     
       
  3763     R.hsb2rgb = function (h, s, v, o) {
       
  3764         if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
       
  3765             v = h.b;
       
  3766             s = h.s;
       
  3767             h = h.h;
       
  3768             o = h.o;
       
  3769         }
       
  3770         h *= 360;
       
  3771         var R, G, B, X, C;
       
  3772         h = (h % 360) / 60;
       
  3773         C = v * s;
       
  3774         X = C * (1 - abs(h % 2 - 1));
       
  3775         R = G = B = v - C;
       
  3776 
       
  3777         h = ~~h;
       
  3778         R += [C, X, 0, 0, X, C][h];
       
  3779         G += [X, C, C, X, 0, 0][h];
       
  3780         B += [0, 0, X, C, C, X][h];
       
  3781         return packageRGB(R, G, B, o);
       
  3782     };
       
  3783     
       
  3784     R.hsl2rgb = function (h, s, l, o) {
       
  3785         if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
       
  3786             l = h.l;
       
  3787             s = h.s;
       
  3788             h = h.h;
       
  3789         }
       
  3790         if (h > 1 || s > 1 || l > 1) {
       
  3791             h /= 360;
       
  3792             s /= 100;
       
  3793             l /= 100;
       
  3794         }
       
  3795         h *= 360;
       
  3796         var R, G, B, X, C;
       
  3797         h = (h % 360) / 60;
       
  3798         C = 2 * s * (l < .5 ? l : 1 - l);
       
  3799         X = C * (1 - abs(h % 2 - 1));
       
  3800         R = G = B = l - C / 2;
       
  3801 
       
  3802         h = ~~h;
       
  3803         R += [C, X, 0, 0, X, C][h];
       
  3804         G += [X, C, C, X, 0, 0][h];
       
  3805         B += [0, 0, X, C, C, X][h];
       
  3806         return packageRGB(R, G, B, o);
       
  3807     };
       
  3808     
       
  3809     R.rgb2hsb = function (r, g, b) {
       
  3810         b = prepareRGB(r, g, b);
       
  3811         r = b[0];
       
  3812         g = b[1];
       
  3813         b = b[2];
       
  3814 
       
  3815         var H, S, V, C;
       
  3816         V = mmax(r, g, b);
       
  3817         C = V - mmin(r, g, b);
       
  3818         H = (C == 0 ? null :
       
  3819              V == r ? (g - b) / C :
       
  3820              V == g ? (b - r) / C + 2 :
       
  3821                       (r - g) / C + 4
       
  3822             );
       
  3823         H = ((H + 360) % 6) * 60 / 360;
       
  3824         S = C == 0 ? 0 : C / V;
       
  3825         return {h: H, s: S, b: V, toString: hsbtoString};
       
  3826     };
       
  3827     
       
  3828     R.rgb2hsl = function (r, g, b) {
       
  3829         b = prepareRGB(r, g, b);
       
  3830         r = b[0];
       
  3831         g = b[1];
       
  3832         b = b[2];
       
  3833 
       
  3834         var H, S, L, M, m, C;
       
  3835         M = mmax(r, g, b);
       
  3836         m = mmin(r, g, b);
       
  3837         C = M - m;
       
  3838         H = (C == 0 ? null :
       
  3839              M == r ? (g - b) / C :
       
  3840              M == g ? (b - r) / C + 2 :
       
  3841                       (r - g) / C + 4);
       
  3842         H = ((H + 360) % 6) * 60 / 360;
       
  3843         L = (M + m) / 2;
       
  3844         S = (C == 0 ? 0 :
       
  3845              L < .5 ? C / (2 * L) :
       
  3846                       C / (2 - 2 * L));
       
  3847         return {h: H, s: S, l: L, toString: hsltoString};
       
  3848     };
       
  3849     R._path2string = function () {
       
  3850         return this.join(",").replace(p2s, "$1");
       
  3851     };
       
  3852     function repush(array, item) {
       
  3853         for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
       
  3854             return array.push(array.splice(i, 1)[0]);
       
  3855         }
       
  3856     }
       
  3857     function cacher(f, scope, postprocessor) {
       
  3858         function newf() {
       
  3859             var arg = Array.prototype.slice.call(arguments, 0),
       
  3860                 args = arg.join("\u2400"),
       
  3861                 cache = newf.cache = newf.cache || {},
       
  3862                 count = newf.count = newf.count || [];
       
  3863             if (cache[has](args)) {
       
  3864                 repush(count, args);
       
  3865                 return postprocessor ? postprocessor(cache[args]) : cache[args];
       
  3866             }
       
  3867             count.length >= 1e3 && delete cache[count.shift()];
       
  3868             count.push(args);
       
  3869             cache[args] = f[apply](scope, arg);
       
  3870             return postprocessor ? postprocessor(cache[args]) : cache[args];
       
  3871         }
       
  3872         return newf;
       
  3873     }
       
  3874 
       
  3875     var preload = R._preload = function (src, f) {
       
  3876         var img = g.doc.createElement("img");
       
  3877         img.style.cssText = "position:absolute;left:-9999em;top-9999em";
       
  3878         img.onload = function () {
       
  3879             f.call(this);
       
  3880             this.onload = null;
       
  3881             g.doc.body.removeChild(this);
       
  3882         };
       
  3883         img.onerror = function () {
       
  3884             g.doc.body.removeChild(this);
       
  3885         };
       
  3886         g.doc.body.appendChild(img);
       
  3887         img.src = src;
       
  3888     };
       
  3889     
       
  3890     function clrToString() {
       
  3891         return this.hex;
       
  3892     }
       
  3893 
       
  3894     
       
  3895     R.getRGB = cacher(function (colour) {
       
  3896         if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
       
  3897             return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
       
  3898         }
       
  3899         if (colour == "none") {
       
  3900             return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
       
  3901         }
       
  3902         !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
       
  3903         var res,
       
  3904             red,
       
  3905             green,
       
  3906             blue,
       
  3907             opacity,
       
  3908             t,
       
  3909             values,
       
  3910             rgb = colour.match(colourRegExp);
       
  3911         if (rgb) {
       
  3912             if (rgb[2]) {
       
  3913                 blue = toInt(rgb[2].substring(5), 16);
       
  3914                 green = toInt(rgb[2].substring(3, 5), 16);
       
  3915                 red = toInt(rgb[2].substring(1, 3), 16);
       
  3916             }
       
  3917             if (rgb[3]) {
       
  3918                 blue = toInt((t = rgb[3].charAt(3)) + t, 16);
       
  3919                 green = toInt((t = rgb[3].charAt(2)) + t, 16);
       
  3920                 red = toInt((t = rgb[3].charAt(1)) + t, 16);
       
  3921             }
       
  3922             if (rgb[4]) {
       
  3923                 values = rgb[4][split](commaSpaces);
       
  3924                 red = toFloat(values[0]);
       
  3925                 values[0].slice(-1) == "%" && (red *= 2.55);
       
  3926                 green = toFloat(values[1]);
       
  3927                 values[1].slice(-1) == "%" && (green *= 2.55);
       
  3928                 blue = toFloat(values[2]);
       
  3929                 values[2].slice(-1) == "%" && (blue *= 2.55);
       
  3930                 rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
       
  3931                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
       
  3932             }
       
  3933             if (rgb[5]) {
       
  3934                 values = rgb[5][split](commaSpaces);
       
  3935                 red = toFloat(values[0]);
       
  3936                 values[0].slice(-1) == "%" && (red *= 2.55);
       
  3937                 green = toFloat(values[1]);
       
  3938                 values[1].slice(-1) == "%" && (green *= 2.55);
       
  3939                 blue = toFloat(values[2]);
       
  3940                 values[2].slice(-1) == "%" && (blue *= 2.55);
       
  3941                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
       
  3942                 rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
       
  3943                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
       
  3944                 return R.hsb2rgb(red, green, blue, opacity);
       
  3945             }
       
  3946             if (rgb[6]) {
       
  3947                 values = rgb[6][split](commaSpaces);
       
  3948                 red = toFloat(values[0]);
       
  3949                 values[0].slice(-1) == "%" && (red *= 2.55);
       
  3950                 green = toFloat(values[1]);
       
  3951                 values[1].slice(-1) == "%" && (green *= 2.55);
       
  3952                 blue = toFloat(values[2]);
       
  3953                 values[2].slice(-1) == "%" && (blue *= 2.55);
       
  3954                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
       
  3955                 rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
       
  3956                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
       
  3957                 return R.hsl2rgb(red, green, blue, opacity);
       
  3958             }
       
  3959             rgb = {r: red, g: green, b: blue, toString: clrToString};
       
  3960             rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
       
  3961             R.is(opacity, "finite") && (rgb.opacity = opacity);
       
  3962             return rgb;
       
  3963         }
       
  3964         return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
       
  3965     }, R);
       
  3966     
       
  3967     R.hsb = cacher(function (h, s, b) {
       
  3968         return R.hsb2rgb(h, s, b).hex;
       
  3969     });
       
  3970     
       
  3971     R.hsl = cacher(function (h, s, l) {
       
  3972         return R.hsl2rgb(h, s, l).hex;
       
  3973     });
       
  3974     
       
  3975     R.rgb = cacher(function (r, g, b) {
       
  3976         return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
       
  3977     });
       
  3978     
       
  3979     R.getColor = function (value) {
       
  3980         var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
       
  3981             rgb = this.hsb2rgb(start.h, start.s, start.b);
       
  3982         start.h += .075;
       
  3983         if (start.h > 1) {
       
  3984             start.h = 0;
       
  3985             start.s -= .2;
       
  3986             start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
       
  3987         }
       
  3988         return rgb.hex;
       
  3989     };
       
  3990     
       
  3991     R.getColor.reset = function () {
       
  3992         delete this.start;
       
  3993     };
       
  3994 
       
  3995     // http://schepers.cc/getting-to-the-point
       
  3996     function catmullRom2bezier(crp) {
       
  3997         var d = [];
       
  3998         for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
       
  3999             var p = [{x: +crp[i],     y: +crp[i + 1]},
       
  4000                      {x: +crp[i],     y: +crp[i + 1]},
       
  4001                      {x: +crp[i + 2], y: +crp[i + 3]},
       
  4002                      {x: +crp[i + 4], y: +crp[i + 5]}];
       
  4003             if (iLen - 4 == i) {
       
  4004                 p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
       
  4005                 p[3] = p[2];
       
  4006             } else if (i) {
       
  4007                 p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
       
  4008             }
       
  4009             d.push(["C",
       
  4010                 (-p[0].x + 6 * p[1].x + p[2].x) / 6,
       
  4011                 (-p[0].y + 6 * p[1].y + p[2].y) / 6,
       
  4012                 (p[1].x + 6 * p[2].x - p[3].x) / 6,
       
  4013                 (p[1].y + 6*p[2].y - p[3].y) / 6,
       
  4014                 p[2].x,
       
  4015                 p[2].y
       
  4016             ]);
       
  4017         }
       
  4018 
       
  4019         return d;
       
  4020     }
       
  4021     
       
  4022     R.parsePathString = cacher(function (pathString) {
       
  4023         if (!pathString) {
       
  4024             return null;
       
  4025         }
       
  4026         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
       
  4027             data = [];
       
  4028         if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
       
  4029             data = pathClone(pathString);
       
  4030         }
       
  4031         if (!data.length) {
       
  4032             Str(pathString).replace(pathCommand, function (a, b, c) {
       
  4033                 var params = [],
       
  4034                     name = b.toLowerCase();
       
  4035                 c.replace(pathValues, function (a, b) {
       
  4036                     b && params.push(+b);
       
  4037                 });
       
  4038                 if (name == "m" && params.length > 2) {
       
  4039                     data.push([b][concat](params.splice(0, 2)));
       
  4040                     name = "l";
       
  4041                     b = b == "m" ? "l" : "L";
       
  4042                 }
       
  4043                 if (name == "r") {
       
  4044                     data.push([b][concat](params));
       
  4045                 } else while (params.length >= paramCounts[name]) {
       
  4046                     data.push([b][concat](params.splice(0, paramCounts[name])));
       
  4047                     if (!paramCounts[name]) {
       
  4048                         break;
       
  4049                     }
       
  4050                 }
       
  4051             });
       
  4052         }
       
  4053         data.toString = R._path2string;
       
  4054         return data;
       
  4055     });
       
  4056     
       
  4057     R.parseTransformString = cacher(function (TString) {
       
  4058         if (!TString) {
       
  4059             return null;
       
  4060         }
       
  4061         var paramCounts = {r: 3, s: 4, t: 2, m: 6},
       
  4062             data = [];
       
  4063         if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
       
  4064             data = pathClone(TString);
       
  4065         }
       
  4066         if (!data.length) {
       
  4067             Str(TString).replace(tCommand, function (a, b, c) {
       
  4068                 var params = [],
       
  4069                     name = lowerCase.call(b);
       
  4070                 c.replace(pathValues, function (a, b) {
       
  4071                     b && params.push(+b);
       
  4072                 });
       
  4073                 data.push([b][concat](params));
       
  4074             });
       
  4075         }
       
  4076         data.toString = R._path2string;
       
  4077         return data;
       
  4078     });
       
  4079     
       
  4080     R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
       
  4081         var t1 = 1 - t,
       
  4082             t13 = pow(t1, 3),
       
  4083             t12 = pow(t1, 2),
       
  4084             t2 = t * t,
       
  4085             t3 = t2 * t,
       
  4086             x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
       
  4087             y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
       
  4088             mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
       
  4089             my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
       
  4090             nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
       
  4091             ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
       
  4092             ax = t1 * p1x + t * c1x,
       
  4093             ay = t1 * p1y + t * c1y,
       
  4094             cx = t1 * c2x + t * p2x,
       
  4095             cy = t1 * c2y + t * p2y,
       
  4096             alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
       
  4097         (mx > nx || my < ny) && (alpha += 180);
       
  4098         return {
       
  4099             x: x,
       
  4100             y: y,
       
  4101             m: {x: mx, y: my},
       
  4102             n: {x: nx, y: ny},
       
  4103             start: {x: ax, y: ay},
       
  4104             end: {x: cx, y: cy},
       
  4105             alpha: alpha
       
  4106         };
       
  4107     };
       
  4108     var pathDimensions = cacher(function (path) {
       
  4109         if (!path) {
       
  4110             return {x: 0, y: 0, width: 0, height: 0};
       
  4111         }
       
  4112         path = path2curve(path);
       
  4113         var x = 0, 
       
  4114             y = 0,
       
  4115             X = [],
       
  4116             Y = [],
       
  4117             p;
       
  4118         for (var i = 0, ii = path.length; i < ii; i++) {
       
  4119             p = path[i];
       
  4120             if (p[0] == "M") {
       
  4121                 x = p[1];
       
  4122                 y = p[2];
       
  4123                 X.push(x);
       
  4124                 Y.push(y);
       
  4125             } else {
       
  4126                 var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
       
  4127                 X = X[concat](dim.min.x, dim.max.x);
       
  4128                 Y = Y[concat](dim.min.y, dim.max.y);
       
  4129                 x = p[5];
       
  4130                 y = p[6];
       
  4131             }
       
  4132         }
       
  4133         var xmin = mmin[apply](0, X),
       
  4134             ymin = mmin[apply](0, Y);
       
  4135         return {
       
  4136             x: xmin,
       
  4137             y: ymin,
       
  4138             width: mmax[apply](0, X) - xmin,
       
  4139             height: mmax[apply](0, Y) - ymin
       
  4140         };
       
  4141     }, null, function (o) {
       
  4142         return {
       
  4143             x: o.x,
       
  4144             y: o.y,
       
  4145             width: o.width,
       
  4146             height: o.height
       
  4147         };
       
  4148     }),
       
  4149         pathClone = function (pathArray) {
       
  4150             var res = [];
       
  4151             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
       
  4152                 pathArray = R.parsePathString(pathArray);
       
  4153             }
       
  4154             for (var i = 0, ii = pathArray.length; i < ii; i++) {
       
  4155                 res[i] = [];
       
  4156                 for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
       
  4157                     res[i][j] = pathArray[i][j];
       
  4158                 }
       
  4159             }
       
  4160             res.toString = R._path2string;
       
  4161             return res;
       
  4162         },
       
  4163         pathToRelative = R._pathToRelative = cacher(function (pathArray) {
       
  4164             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
       
  4165                 pathArray = R.parsePathString(pathArray);
       
  4166             }
       
  4167             var res = [],
       
  4168                 x = 0,
       
  4169                 y = 0,
       
  4170                 mx = 0,
       
  4171                 my = 0,
       
  4172                 start = 0;
       
  4173             if (pathArray[0][0] == "M") {
       
  4174                 x = pathArray[0][1];
       
  4175                 y = pathArray[0][2];
       
  4176                 mx = x;
       
  4177                 my = y;
       
  4178                 start++;
       
  4179                 res.push(["M", x, y]);
       
  4180             }
       
  4181             for (var i = start, ii = pathArray.length; i < ii; i++) {
       
  4182                 var r = res[i] = [],
       
  4183                     pa = pathArray[i];
       
  4184                 if (pa[0] != lowerCase.call(pa[0])) {
       
  4185                     r[0] = lowerCase.call(pa[0]);
       
  4186                     switch (r[0]) {
       
  4187                         case "a":
       
  4188                             r[1] = pa[1];
       
  4189                             r[2] = pa[2];
       
  4190                             r[3] = pa[3];
       
  4191                             r[4] = pa[4];
       
  4192                             r[5] = pa[5];
       
  4193                             r[6] = +(pa[6] - x).toFixed(3);
       
  4194                             r[7] = +(pa[7] - y).toFixed(3);
       
  4195                             break;
       
  4196                         case "v":
       
  4197                             r[1] = +(pa[1] - y).toFixed(3);
       
  4198                             break;
       
  4199                         case "m":
       
  4200                             mx = pa[1];
       
  4201                             my = pa[2];
       
  4202                         default:
       
  4203                             for (var j = 1, jj = pa.length; j < jj; j++) {
       
  4204                                 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
       
  4205                             }
       
  4206                     }
       
  4207                 } else {
       
  4208                     r = res[i] = [];
       
  4209                     if (pa[0] == "m") {
       
  4210                         mx = pa[1] + x;
       
  4211                         my = pa[2] + y;
       
  4212                     }
       
  4213                     for (var k = 0, kk = pa.length; k < kk; k++) {
       
  4214                         res[i][k] = pa[k];
       
  4215                     }
       
  4216                 }
       
  4217                 var len = res[i].length;
       
  4218                 switch (res[i][0]) {
       
  4219                     case "z":
       
  4220                         x = mx;
       
  4221                         y = my;
       
  4222                         break;
       
  4223                     case "h":
       
  4224                         x += +res[i][len - 1];
       
  4225                         break;
       
  4226                     case "v":
       
  4227                         y += +res[i][len - 1];
       
  4228                         break;
       
  4229                     default:
       
  4230                         x += +res[i][len - 2];
       
  4231                         y += +res[i][len - 1];
       
  4232                 }
       
  4233             }
       
  4234             res.toString = R._path2string;
       
  4235             return res;
       
  4236         }, 0, pathClone),
       
  4237         pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) {
       
  4238             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
       
  4239                 pathArray = R.parsePathString(pathArray);
       
  4240             }
       
  4241             if (!pathArray || !pathArray.length) {
       
  4242                 return [["M", 0, 0]];
       
  4243             }
       
  4244             var res = [],
       
  4245                 x = 0,
       
  4246                 y = 0,
       
  4247                 mx = 0,
       
  4248                 my = 0,
       
  4249                 start = 0;
       
  4250             if (pathArray[0][0] == "M") {
       
  4251                 x = +pathArray[0][1];
       
  4252                 y = +pathArray[0][2];
       
  4253                 mx = x;
       
  4254                 my = y;
       
  4255                 start++;
       
  4256                 res[0] = ["M", x, y];
       
  4257             }
       
  4258             for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
       
  4259                 res.push(r = []);
       
  4260                 pa = pathArray[i];
       
  4261                 if (pa[0] != upperCase.call(pa[0])) {
       
  4262                     r[0] = upperCase.call(pa[0]);
       
  4263                     switch (r[0]) {
       
  4264                         case "A":
       
  4265                             r[1] = pa[1];
       
  4266                             r[2] = pa[2];
       
  4267                             r[3] = pa[3];
       
  4268                             r[4] = pa[4];
       
  4269                             r[5] = pa[5];
       
  4270                             r[6] = +(pa[6] + x);
       
  4271                             r[7] = +(pa[7] + y);
       
  4272                             break;
       
  4273                         case "V":
       
  4274                             r[1] = +pa[1] + y;
       
  4275                             break;
       
  4276                         case "H":
       
  4277                             r[1] = +pa[1] + x;
       
  4278                             break;
       
  4279                         case "R":
       
  4280                             var dots = [x, y][concat](pa.slice(1));
       
  4281                             for (var j = 2, jj = dots.length; j < jj; j++) {
       
  4282                                 dots[j] = +dots[j] + x;
       
  4283                                 dots[++j] = +dots[j] + y;
       
  4284                             }
       
  4285                             res.pop();
       
  4286                             res = res[concat](catmullRom2bezier(dots));
       
  4287                             break;
       
  4288                         case "M":
       
  4289                             mx = +pa[1] + x;
       
  4290                             my = +pa[2] + y;
       
  4291                         default:
       
  4292                             for (j = 1, jj = pa.length; j < jj; j++) {
       
  4293                                 r[j] = +pa[j] + ((j % 2) ? x : y);
       
  4294                             }
       
  4295                     }
       
  4296                 } else if (pa[0] == "R") {
       
  4297                     dots = [x, y][concat](pa.slice(1));
       
  4298                     res.pop();
       
  4299                     res = res[concat](catmullRom2bezier(dots));
       
  4300                     r = ["R"][concat](pa.slice(-2));
       
  4301                 } else {
       
  4302                     for (var k = 0, kk = pa.length; k < kk; k++) {
       
  4303                         r[k] = pa[k];
       
  4304                     }
       
  4305                 }
       
  4306                 switch (r[0]) {
       
  4307                     case "Z":
       
  4308                         x = mx;
       
  4309                         y = my;
       
  4310                         break;
       
  4311                     case "H":
       
  4312                         x = r[1];
       
  4313                         break;
       
  4314                     case "V":
       
  4315                         y = r[1];
       
  4316                         break;
       
  4317                     case "M":
       
  4318                         mx = r[r.length - 2];
       
  4319                         my = r[r.length - 1];
       
  4320                     default:
       
  4321                         x = r[r.length - 2];
       
  4322                         y = r[r.length - 1];
       
  4323                 }
       
  4324             }
       
  4325             res.toString = R._path2string;
       
  4326             return res;
       
  4327         }, null, pathClone),
       
  4328         l2c = function (x1, y1, x2, y2) {
       
  4329             return [x1, y1, x2, y2, x2, y2];
       
  4330         },
       
  4331         q2c = function (x1, y1, ax, ay, x2, y2) {
       
  4332             var _13 = 1 / 3,
       
  4333                 _23 = 2 / 3;
       
  4334             return [
       
  4335                     _13 * x1 + _23 * ax,
       
  4336                     _13 * y1 + _23 * ay,
       
  4337                     _13 * x2 + _23 * ax,
       
  4338                     _13 * y2 + _23 * ay,
       
  4339                     x2,
       
  4340                     y2
       
  4341                 ];
       
  4342         },
       
  4343         a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
       
  4344             // for more information of where this math came from visit:
       
  4345             // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
       
  4346             var _120 = PI * 120 / 180,
       
  4347                 rad = PI / 180 * (+angle || 0),
       
  4348                 res = [],
       
  4349                 xy,
       
  4350                 rotate = cacher(function (x, y, rad) {
       
  4351                     var X = x * math.cos(rad) - y * math.sin(rad),
       
  4352                         Y = x * math.sin(rad) + y * math.cos(rad);
       
  4353                     return {x: X, y: Y};
       
  4354                 });
       
  4355             if (!recursive) {
       
  4356                 xy = rotate(x1, y1, -rad);
       
  4357                 x1 = xy.x;
       
  4358                 y1 = xy.y;
       
  4359                 xy = rotate(x2, y2, -rad);
       
  4360                 x2 = xy.x;
       
  4361                 y2 = xy.y;
       
  4362                 var cos = math.cos(PI / 180 * angle),
       
  4363                     sin = math.sin(PI / 180 * angle),
       
  4364                     x = (x1 - x2) / 2,
       
  4365                     y = (y1 - y2) / 2;
       
  4366                 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
       
  4367                 if (h > 1) {
       
  4368                     h = math.sqrt(h);
       
  4369                     rx = h * rx;
       
  4370                     ry = h * ry;
       
  4371                 }
       
  4372                 var rx2 = rx * rx,
       
  4373                     ry2 = ry * ry,
       
  4374                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
       
  4375                         math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
       
  4376                     cx = k * rx * y / ry + (x1 + x2) / 2,
       
  4377                     cy = k * -ry * x / rx + (y1 + y2) / 2,
       
  4378                     f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
       
  4379                     f2 = math.asin(((y2 - cy) / ry).toFixed(9));
       
  4380 
       
  4381                 f1 = x1 < cx ? PI - f1 : f1;
       
  4382                 f2 = x2 < cx ? PI - f2 : f2;
       
  4383                 f1 < 0 && (f1 = PI * 2 + f1);
       
  4384                 f2 < 0 && (f2 = PI * 2 + f2);
       
  4385                 if (sweep_flag && f1 > f2) {
       
  4386                     f1 = f1 - PI * 2;
       
  4387                 }
       
  4388                 if (!sweep_flag && f2 > f1) {
       
  4389                     f2 = f2 - PI * 2;
       
  4390                 }
       
  4391             } else {
       
  4392                 f1 = recursive[0];
       
  4393                 f2 = recursive[1];
       
  4394                 cx = recursive[2];
       
  4395                 cy = recursive[3];
       
  4396             }
       
  4397             var df = f2 - f1;
       
  4398             if (abs(df) > _120) {
       
  4399                 var f2old = f2,
       
  4400                     x2old = x2,
       
  4401                     y2old = y2;
       
  4402                 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
       
  4403                 x2 = cx + rx * math.cos(f2);
       
  4404                 y2 = cy + ry * math.sin(f2);
       
  4405                 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
       
  4406             }
       
  4407             df = f2 - f1;
       
  4408             var c1 = math.cos(f1),
       
  4409                 s1 = math.sin(f1),
       
  4410                 c2 = math.cos(f2),
       
  4411                 s2 = math.sin(f2),
       
  4412                 t = math.tan(df / 4),
       
  4413                 hx = 4 / 3 * rx * t,
       
  4414                 hy = 4 / 3 * ry * t,
       
  4415                 m1 = [x1, y1],
       
  4416                 m2 = [x1 + hx * s1, y1 - hy * c1],
       
  4417                 m3 = [x2 + hx * s2, y2 - hy * c2],
       
  4418                 m4 = [x2, y2];
       
  4419             m2[0] = 2 * m1[0] - m2[0];
       
  4420             m2[1] = 2 * m1[1] - m2[1];
       
  4421             if (recursive) {
       
  4422                 return [m2, m3, m4][concat](res);
       
  4423             } else {
       
  4424                 res = [m2, m3, m4][concat](res).join()[split](",");
       
  4425                 var newres = [];
       
  4426                 for (var i = 0, ii = res.length; i < ii; i++) {
       
  4427                     newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
       
  4428                 }
       
  4429                 return newres;
       
  4430             }
       
  4431         },
       
  4432         findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
       
  4433             var t1 = 1 - t;
       
  4434             return {
       
  4435                 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
       
  4436                 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
       
  4437             };
       
  4438         },
       
  4439         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
       
  4440             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
       
  4441                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
       
  4442                 c = p1x - c1x,
       
  4443                 t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
       
  4444                 t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
       
  4445                 y = [p1y, p2y],
       
  4446                 x = [p1x, p2x],
       
  4447                 dot;
       
  4448             abs(t1) > "1e12" && (t1 = .5);
       
  4449             abs(t2) > "1e12" && (t2 = .5);
       
  4450             if (t1 > 0 && t1 < 1) {
       
  4451                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
       
  4452                 x.push(dot.x);
       
  4453                 y.push(dot.y);
       
  4454             }
       
  4455             if (t2 > 0 && t2 < 1) {
       
  4456                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
       
  4457                 x.push(dot.x);
       
  4458                 y.push(dot.y);
       
  4459             }
       
  4460             a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
       
  4461             b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
       
  4462             c = p1y - c1y;
       
  4463             t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
       
  4464             t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
       
  4465             abs(t1) > "1e12" && (t1 = .5);
       
  4466             abs(t2) > "1e12" && (t2 = .5);
       
  4467             if (t1 > 0 && t1 < 1) {
       
  4468                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
       
  4469                 x.push(dot.x);
       
  4470                 y.push(dot.y);
       
  4471             }
       
  4472             if (t2 > 0 && t2 < 1) {
       
  4473                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
       
  4474                 x.push(dot.x);
       
  4475                 y.push(dot.y);
       
  4476             }
       
  4477             return {
       
  4478                 min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
       
  4479                 max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
       
  4480             };
       
  4481         }),
       
  4482         path2curve = R._path2curve = cacher(function (path, path2) {
       
  4483             var p = pathToAbsolute(path),
       
  4484                 p2 = path2 && pathToAbsolute(path2),
       
  4485                 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
       
  4486                 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
       
  4487                 processPath = function (path, d) {
       
  4488                     var nx, ny;
       
  4489                     if (!path) {
       
  4490                         return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
       
  4491                     }
       
  4492                     !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
       
  4493                     switch (path[0]) {
       
  4494                         case "M":
       
  4495                             d.X = path[1];
       
  4496                             d.Y = path[2];
       
  4497                             break;
       
  4498                         case "A":
       
  4499                             path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
       
  4500                             break;
       
  4501                         case "S":
       
  4502                             nx = d.x + (d.x - (d.bx || d.x));
       
  4503                             ny = d.y + (d.y - (d.by || d.y));
       
  4504                             path = ["C", nx, ny][concat](path.slice(1));
       
  4505                             break;
       
  4506                         case "T":
       
  4507                             d.qx = d.x + (d.x - (d.qx || d.x));
       
  4508                             d.qy = d.y + (d.y - (d.qy || d.y));
       
  4509                             path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
       
  4510                             break;
       
  4511                         case "Q":
       
  4512                             d.qx = path[1];
       
  4513                             d.qy = path[2];
       
  4514                             path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
       
  4515                             break;
       
  4516                         case "L":
       
  4517                             path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
       
  4518                             break;
       
  4519                         case "H":
       
  4520                             path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
       
  4521                             break;
       
  4522                         case "V":
       
  4523                             path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
       
  4524                             break;
       
  4525                         case "Z":
       
  4526                             path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
       
  4527                             break;
       
  4528                     }
       
  4529                     return path;
       
  4530                 },
       
  4531                 fixArc = function (pp, i) {
       
  4532                     if (pp[i].length > 7) {
       
  4533                         pp[i].shift();
       
  4534                         var pi = pp[i];
       
  4535                         while (pi.length) {
       
  4536                             pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
       
  4537                         }
       
  4538                         pp.splice(i, 1);
       
  4539                         ii = mmax(p.length, p2 && p2.length || 0);
       
  4540                     }
       
  4541                 },
       
  4542                 fixM = function (path1, path2, a1, a2, i) {
       
  4543                     if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
       
  4544                         path2.splice(i, 0, ["M", a2.x, a2.y]);
       
  4545                         a1.bx = 0;
       
  4546                         a1.by = 0;
       
  4547                         a1.x = path1[i][1];
       
  4548                         a1.y = path1[i][2];
       
  4549                         ii = mmax(p.length, p2 && p2.length || 0);
       
  4550                     }
       
  4551                 };
       
  4552             for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
       
  4553                 p[i] = processPath(p[i], attrs);
       
  4554                 fixArc(p, i);
       
  4555                 p2 && (p2[i] = processPath(p2[i], attrs2));
       
  4556                 p2 && fixArc(p2, i);
       
  4557                 fixM(p, p2, attrs, attrs2, i);
       
  4558                 fixM(p2, p, attrs2, attrs, i);
       
  4559                 var seg = p[i],
       
  4560                     seg2 = p2 && p2[i],
       
  4561                     seglen = seg.length,
       
  4562                     seg2len = p2 && seg2.length;
       
  4563                 attrs.x = seg[seglen - 2];
       
  4564                 attrs.y = seg[seglen - 1];
       
  4565                 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
       
  4566                 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
       
  4567                 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
       
  4568                 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
       
  4569                 attrs2.x = p2 && seg2[seg2len - 2];
       
  4570                 attrs2.y = p2 && seg2[seg2len - 1];
       
  4571             }
       
  4572             return p2 ? [p, p2] : p;
       
  4573         }, null, pathClone),
       
  4574         parseDots = R._parseDots = cacher(function (gradient) {
       
  4575             var dots = [];
       
  4576             for (var i = 0, ii = gradient.length; i < ii; i++) {
       
  4577                 var dot = {},
       
  4578                     par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
       
  4579                 dot.color = R.getRGB(par[1]);
       
  4580                 if (dot.color.error) {
       
  4581                     return null;
       
  4582                 }
       
  4583                 dot.color = dot.color.hex;
       
  4584                 par[2] && (dot.offset = par[2] + "%");
       
  4585                 dots.push(dot);
       
  4586             }
       
  4587             for (i = 1, ii = dots.length - 1; i < ii; i++) {
       
  4588                 if (!dots[i].offset) {
       
  4589                     var start = toFloat(dots[i - 1].offset || 0),
       
  4590                         end = 0;
       
  4591                     for (var j = i + 1; j < ii; j++) {
       
  4592                         if (dots[j].offset) {
       
  4593                             end = dots[j].offset;
       
  4594                             break;
       
  4595                         }
       
  4596                     }
       
  4597                     if (!end) {
       
  4598                         end = 100;
       
  4599                         j = ii;
       
  4600                     }
       
  4601                     end = toFloat(end);
       
  4602                     var d = (end - start) / (j - i + 1);
       
  4603                     for (; i < j; i++) {
       
  4604                         start += d;
       
  4605                         dots[i].offset = start + "%";
       
  4606                     }
       
  4607                 }
       
  4608             }
       
  4609             return dots;
       
  4610         }),
       
  4611         tear = R._tear = function (el, paper) {
       
  4612             el == paper.top && (paper.top = el.prev);
       
  4613             el == paper.bottom && (paper.bottom = el.next);
       
  4614             el.next && (el.next.prev = el.prev);
       
  4615             el.prev && (el.prev.next = el.next);
       
  4616         },
       
  4617         tofront = R._tofront = function (el, paper) {
       
  4618             if (paper.top === el) {
       
  4619                 return;
       
  4620             }
       
  4621             tear(el, paper);
       
  4622             el.next = null;
       
  4623             el.prev = paper.top;
       
  4624             paper.top.next = el;
       
  4625             paper.top = el;
       
  4626         },
       
  4627         toback = R._toback = function (el, paper) {
       
  4628             if (paper.bottom === el) {
       
  4629                 return;
       
  4630             }
       
  4631             tear(el, paper);
       
  4632             el.next = paper.bottom;
       
  4633             el.prev = null;
       
  4634             paper.bottom.prev = el;
       
  4635             paper.bottom = el;
       
  4636         },
       
  4637         insertafter = R._insertafter = function (el, el2, paper) {
       
  4638             tear(el, paper);
       
  4639             el2 == paper.top && (paper.top = el);
       
  4640             el2.next && (el2.next.prev = el);
       
  4641             el.next = el2.next;
       
  4642             el.prev = el2;
       
  4643             el2.next = el;
       
  4644         },
       
  4645         insertbefore = R._insertbefore = function (el, el2, paper) {
       
  4646             tear(el, paper);
       
  4647             el2 == paper.bottom && (paper.bottom = el);
       
  4648             el2.prev && (el2.prev.next = el);
       
  4649             el.prev = el2.prev;
       
  4650             el2.prev = el;
       
  4651             el.next = el2;
       
  4652         },
       
  4653         removed = function (methodname) {
       
  4654             return function () {
       
  4655                 throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
       
  4656             };
       
  4657         },
       
  4658         extractTransform = R._extractTransform = function (el, tstr) {
       
  4659             if (tstr == null) {
       
  4660                 return el._.transform;
       
  4661             }
       
  4662             tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
       
  4663             var tdata = R.parseTransformString(tstr),
       
  4664                 deg = 0,
       
  4665                 dx = 0,
       
  4666                 dy = 0,
       
  4667                 sx = 1,
       
  4668                 sy = 1,
       
  4669                 _ = el._,
       
  4670                 m = new Matrix;
       
  4671             _.transform = tdata || [];
       
  4672             if (tdata) {
       
  4673                 for (var i = 0, ii = tdata.length; i < ii; i++) {
       
  4674                     var t = tdata[i],
       
  4675                         tlen = t.length,
       
  4676                         command = Str(t[0]).toLowerCase(),
       
  4677                         absolute = t[0] != command,
       
  4678                         inver = absolute ? m.invert() : 0,
       
  4679                         x1,
       
  4680                         y1,
       
  4681                         x2,
       
  4682                         y2,
       
  4683                         bb;
       
  4684                     if (command == "t" && tlen == 3) {
       
  4685                         if (absolute) {
       
  4686                             x1 = inver.x(0, 0);
       
  4687                             y1 = inver.y(0, 0);
       
  4688                             x2 = inver.x(t[1], t[2]);
       
  4689                             y2 = inver.y(t[1], t[2]);
       
  4690                             m.translate(x2 - x1, y2 - y1);
       
  4691                         } else {
       
  4692                             m.translate(t[1], t[2]);
       
  4693                         }
       
  4694                     } else if (command == "r") {
       
  4695                         if (tlen == 2) {
       
  4696                             bb = bb || el.getBBox(1);
       
  4697                             m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
       
  4698                             deg += t[1];
       
  4699                         } else if (tlen == 4) {
       
  4700                             if (absolute) {
       
  4701                                 x2 = inver.x(t[2], t[3]);
       
  4702                                 y2 = inver.y(t[2], t[3]);
       
  4703                                 m.rotate(t[1], x2, y2);
       
  4704                             } else {
       
  4705                                 m.rotate(t[1], t[2], t[3]);
       
  4706                             }
       
  4707                             deg += t[1];
       
  4708                         }
       
  4709                     } else if (command == "s") {
       
  4710                         if (tlen == 2 || tlen == 3) {
       
  4711                             bb = bb || el.getBBox(1);
       
  4712                             m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
       
  4713                             sx *= t[1];
       
  4714                             sy *= t[tlen - 1];
       
  4715                         } else if (tlen == 5) {
       
  4716                             if (absolute) {
       
  4717                                 x2 = inver.x(t[3], t[4]);
       
  4718                                 y2 = inver.y(t[3], t[4]);
       
  4719                                 m.scale(t[1], t[2], x2, y2);
       
  4720                             } else {
       
  4721                                 m.scale(t[1], t[2], t[3], t[4]);
       
  4722                             }
       
  4723                             sx *= t[1];
       
  4724                             sy *= t[2];
       
  4725                         }
       
  4726                     } else if (command == "m" && tlen == 7) {
       
  4727                         m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
       
  4728                     }
       
  4729                     _.dirtyT = 1;
       
  4730                     el.matrix = m;
       
  4731                 }
       
  4732             }
       
  4733 
       
  4734             el.matrix = m;
       
  4735 
       
  4736             _.sx = sx;
       
  4737             _.sy = sy;
       
  4738             _.deg = deg;
       
  4739             _.dx = dx = m.e;
       
  4740             _.dy = dy = m.f;
       
  4741 
       
  4742             if (sx == 1 && sy == 1 && !deg && _.bbox) {
       
  4743                 _.bbox.x += +dx;
       
  4744                 _.bbox.y += +dy;
       
  4745             } else {
       
  4746                 _.dirtyT = 1;
       
  4747             }
       
  4748         },
       
  4749         getEmpty = function (item) {
       
  4750             var l = item[0];
       
  4751             switch (l.toLowerCase()) {
       
  4752                 case "t": return [l, 0, 0];
       
  4753                 case "m": return [l, 1, 0, 0, 1, 0, 0];
       
  4754                 case "r": if (item.length == 4) {
       
  4755                     return [l, 0, item[2], item[3]];
       
  4756                 } else {
       
  4757                     return [l, 0];
       
  4758                 }
       
  4759                 case "s": if (item.length == 5) {
       
  4760                     return [l, 1, 1, item[3], item[4]];
       
  4761                 } else if (item.length == 3) {
       
  4762                     return [l, 1, 1];
       
  4763                 } else {
       
  4764                     return [l, 1];
       
  4765                 }
       
  4766             }
       
  4767         },
       
  4768         equaliseTransform = R._equaliseTransform = function (t1, t2) {
       
  4769             t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
       
  4770             t1 = R.parseTransformString(t1) || [];
       
  4771             t2 = R.parseTransformString(t2) || [];
       
  4772             var maxlength = mmax(t1.length, t2.length),
       
  4773                 from = [],
       
  4774                 to = [],
       
  4775                 i = 0, j, jj,
       
  4776                 tt1, tt2;
       
  4777             for (; i < maxlength; i++) {
       
  4778                 tt1 = t1[i] || getEmpty(t2[i]);
       
  4779                 tt2 = t2[i] || getEmpty(tt1);
       
  4780                 if ((tt1[0] != tt2[0]) ||
       
  4781                     (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
       
  4782                     (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
       
  4783                     ) {
       
  4784                     return;
       
  4785                 }
       
  4786                 from[i] = [];
       
  4787                 to[i] = [];
       
  4788                 for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
       
  4789                     j in tt1 && (from[i][j] = tt1[j]);
       
  4790                     j in tt2 && (to[i][j] = tt2[j]);
       
  4791                 }
       
  4792             }
       
  4793             return {
       
  4794                 from: from,
       
  4795                 to: to
       
  4796             };
       
  4797         };
       
  4798     R._getContainer = function (x, y, w, h) {
       
  4799         var container;
       
  4800         container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
       
  4801         if (container == null) {
       
  4802             return;
       
  4803         }
       
  4804         if (container.tagName) {
       
  4805             if (y == null) {
       
  4806                 return {
       
  4807                     container: container,
       
  4808                     width: container.style.pixelWidth || container.offsetWidth,
       
  4809                     height: container.style.pixelHeight || container.offsetHeight
       
  4810                 };
       
  4811             } else {
       
  4812                 return {
       
  4813                     container: container,
       
  4814                     width: y,
       
  4815                     height: w
       
  4816                 };
       
  4817             }
       
  4818         }
       
  4819         return {
       
  4820             container: 1,
       
  4821             x: x,
       
  4822             y: y,
       
  4823             width: w,
       
  4824             height: h
       
  4825         };
       
  4826     };
       
  4827     
       
  4828     R.pathToRelative = pathToRelative;
       
  4829     R._engine = {};
       
  4830     
       
  4831     R.path2curve = path2curve;
       
  4832     
       
  4833     R.matrix = function (a, b, c, d, e, f) {
       
  4834         return new Matrix(a, b, c, d, e, f);
       
  4835     };
       
  4836     function Matrix(a, b, c, d, e, f) {
       
  4837         if (a != null) {
       
  4838             this.a = +a;
       
  4839             this.b = +b;
       
  4840             this.c = +c;
       
  4841             this.d = +d;
       
  4842             this.e = +e;
       
  4843             this.f = +f;
       
  4844         } else {
       
  4845             this.a = 1;
       
  4846             this.b = 0;
       
  4847             this.c = 0;
       
  4848             this.d = 1;
       
  4849             this.e = 0;
       
  4850             this.f = 0;
       
  4851         }
       
  4852     }
       
  4853     (function (matrixproto) {
       
  4854         
       
  4855         matrixproto.add = function (a, b, c, d, e, f) {
       
  4856             var out = [[], [], []],
       
  4857                 m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
       
  4858                 matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
       
  4859                 x, y, z, res;
       
  4860 
       
  4861             if (a && a instanceof Matrix) {
       
  4862                 matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
       
  4863             }
       
  4864 
       
  4865             for (x = 0; x < 3; x++) {
       
  4866                 for (y = 0; y < 3; y++) {
       
  4867                     res = 0;
       
  4868                     for (z = 0; z < 3; z++) {
       
  4869                         res += m[x][z] * matrix[z][y];
       
  4870                     }
       
  4871                     out[x][y] = res;
       
  4872                 }
       
  4873             }
       
  4874             this.a = out[0][0];
       
  4875             this.b = out[1][0];
       
  4876             this.c = out[0][1];
       
  4877             this.d = out[1][1];
       
  4878             this.e = out[0][2];
       
  4879             this.f = out[1][2];
       
  4880         };
       
  4881         
       
  4882         matrixproto.invert = function () {
       
  4883             var me = this,
       
  4884                 x = me.a * me.d - me.b * me.c;
       
  4885             return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
       
  4886         };
       
  4887         
       
  4888         matrixproto.clone = function () {
       
  4889             return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
       
  4890         };
       
  4891         
       
  4892         matrixproto.translate = function (x, y) {
       
  4893             this.add(1, 0, 0, 1, x, y);
       
  4894         };
       
  4895         
       
  4896         matrixproto.scale = function (x, y, cx, cy) {
       
  4897             y == null && (y = x);
       
  4898             (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
       
  4899             this.add(x, 0, 0, y, 0, 0);
       
  4900             (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
       
  4901         };
       
  4902         
       
  4903         matrixproto.rotate = function (a, x, y) {
       
  4904             a = R.rad(a);
       
  4905             x = x || 0;
       
  4906             y = y || 0;
       
  4907             var cos = +math.cos(a).toFixed(9),
       
  4908                 sin = +math.sin(a).toFixed(9);
       
  4909             this.add(cos, sin, -sin, cos, x, y);
       
  4910             this.add(1, 0, 0, 1, -x, -y);
       
  4911         };
       
  4912         
       
  4913         matrixproto.x = function (x, y) {
       
  4914             return x * this.a + y * this.c + this.e;
       
  4915         };
       
  4916         
       
  4917         matrixproto.y = function (x, y) {
       
  4918             return x * this.b + y * this.d + this.f;
       
  4919         };
       
  4920         matrixproto.get = function (i) {
       
  4921             return +this[Str.fromCharCode(97 + i)].toFixed(4);
       
  4922         };
       
  4923         matrixproto.toString = function () {
       
  4924             return R.svg ?
       
  4925                 "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
       
  4926                 [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
       
  4927         };
       
  4928         matrixproto.toFilter = function () {
       
  4929             return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
       
  4930                 ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
       
  4931                 ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
       
  4932         };
       
  4933         matrixproto.offset = function () {
       
  4934             return [this.e.toFixed(4), this.f.toFixed(4)];
       
  4935         };
       
  4936         function norm(a) {
       
  4937             return a[0] * a[0] + a[1] * a[1];
       
  4938         }
       
  4939         function normalize(a) {
       
  4940             var mag = math.sqrt(norm(a));
       
  4941             a[0] && (a[0] /= mag);
       
  4942             a[1] && (a[1] /= mag);
       
  4943         }
       
  4944         
       
  4945         matrixproto.split = function () {
       
  4946             var out = {};
       
  4947             // translation
       
  4948             out.dx = this.e;
       
  4949             out.dy = this.f;
       
  4950 
       
  4951             // scale and shear
       
  4952             var row = [[this.a, this.c], [this.b, this.d]];
       
  4953             out.scalex = math.sqrt(norm(row[0]));
       
  4954             normalize(row[0]);
       
  4955 
       
  4956             out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
       
  4957             row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
       
  4958 
       
  4959             out.scaley = math.sqrt(norm(row[1]));
       
  4960             normalize(row[1]);
       
  4961             out.shear /= out.scaley;
       
  4962 
       
  4963             // rotation
       
  4964             var sin = -row[0][1],
       
  4965                 cos = row[1][1];
       
  4966             if (cos < 0) {
       
  4967                 out.rotate = R.deg(math.acos(cos));
       
  4968                 if (sin < 0) {
       
  4969                     out.rotate = 360 - out.rotate;
       
  4970                 }
       
  4971             } else {
       
  4972                 out.rotate = R.deg(math.asin(sin));
       
  4973             }
       
  4974 
       
  4975             out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
       
  4976             out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
       
  4977             out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
       
  4978             return out;
       
  4979         };
       
  4980         
       
  4981         matrixproto.toTransformString = function (shorter) {
       
  4982             var s = shorter || this[split]();
       
  4983             if (s.isSimple) {
       
  4984                 return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0];
       
  4985             } else {
       
  4986                 return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
       
  4987             }
       
  4988         };
       
  4989     })(Matrix.prototype);
       
  4990 
       
  4991     // WebKit rendering bug workaround method
       
  4992     var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
       
  4993     if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
       
  4994         (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
       
  4995         
       
  4996         paperproto.safari = function () {
       
  4997             var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
       
  4998             setTimeout(function () {rect.remove();});
       
  4999         };
       
  5000     } else {
       
  5001         paperproto.safari = fun;
       
  5002     }
       
  5003  
       
  5004     var preventDefault = function () {
       
  5005         this.returnValue = false;
       
  5006     },
       
  5007     preventTouch = function () {
       
  5008         return this.originalEvent.preventDefault();
       
  5009     },
       
  5010     stopPropagation = function () {
       
  5011         this.cancelBubble = true;
       
  5012     },
       
  5013     stopTouch = function () {
       
  5014         return this.originalEvent.stopPropagation();
       
  5015     },
       
  5016     addEvent = (function () {
       
  5017         if (g.doc.addEventListener) {
       
  5018             return function (obj, type, fn, element) {
       
  5019                 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
       
  5020                     f = function (e) {
       
  5021                         var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
       
  5022                             scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
       
  5023                             x = e.clientX + scrollX,
       
  5024                             y = e.clientY + scrollY;
       
  5025                     if (supportsTouch && touchMap[has](type)) {
       
  5026                         for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
       
  5027                             if (e.targetTouches[i].target == obj) {
       
  5028                                 var olde = e;
       
  5029                                 e = e.targetTouches[i];
       
  5030                                 e.originalEvent = olde;
       
  5031                                 e.preventDefault = preventTouch;
       
  5032                                 e.stopPropagation = stopTouch;
       
  5033                                 break;
       
  5034                             }
       
  5035                         }
       
  5036                     }
       
  5037                     return fn.call(element, e, x, y);
       
  5038                 };
       
  5039                 obj.addEventListener(realName, f, false);
       
  5040                 return function () {
       
  5041                     obj.removeEventListener(realName, f, false);
       
  5042                     return true;
       
  5043                 };
       
  5044             };
       
  5045         } else if (g.doc.attachEvent) {
       
  5046             return function (obj, type, fn, element) {
       
  5047                 var f = function (e) {
       
  5048                     e = e || g.win.event;
       
  5049                     var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
       
  5050                         scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
       
  5051                         x = e.clientX + scrollX,
       
  5052                         y = e.clientY + scrollY;
       
  5053                     e.preventDefault = e.preventDefault || preventDefault;
       
  5054                     e.stopPropagation = e.stopPropagation || stopPropagation;
       
  5055                     return fn.call(element, e, x, y);
       
  5056                 };
       
  5057                 obj.attachEvent("on" + type, f);
       
  5058                 var detacher = function () {
       
  5059                     obj.detachEvent("on" + type, f);
       
  5060                     return true;
       
  5061                 };
       
  5062                 return detacher;
       
  5063             };
       
  5064         }
       
  5065     })(),
       
  5066     drag = [],
       
  5067     dragMove = function (e) {
       
  5068         var x = e.clientX,
       
  5069             y = e.clientY,
       
  5070             scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
       
  5071             scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
       
  5072             dragi,
       
  5073             j = drag.length;
       
  5074         while (j--) {
       
  5075             dragi = drag[j];
       
  5076             if (supportsTouch) {
       
  5077                 var i = e.touches.length,
       
  5078                     touch;
       
  5079                 while (i--) {
       
  5080                     touch = e.touches[i];
       
  5081                     if (touch.identifier == dragi.el._drag.id) {
       
  5082                         x = touch.clientX;
       
  5083                         y = touch.clientY;
       
  5084                         (e.originalEvent ? e.originalEvent : e).preventDefault();
       
  5085                         break;
       
  5086                     }
       
  5087                 }
       
  5088             } else {
       
  5089                 e.preventDefault();
       
  5090             }
       
  5091             var node = dragi.el.node,
       
  5092                 o,
       
  5093                 next = node.nextSibling,
       
  5094                 parent = node.parentNode,
       
  5095                 display = node.style.display;
       
  5096             g.win.opera && parent.removeChild(node);
       
  5097             node.style.display = "none";
       
  5098             o = dragi.el.paper.getElementByPoint(x, y);
       
  5099             node.style.display = display;
       
  5100             g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
       
  5101             o && eve("drag.over." + dragi.el.id, dragi.el, o);
       
  5102             x += scrollX;
       
  5103             y += scrollY;
       
  5104             eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
       
  5105         }
       
  5106     },
       
  5107     dragUp = function (e) {
       
  5108         R.unmousemove(dragMove).unmouseup(dragUp);
       
  5109         var i = drag.length,
       
  5110             dragi;
       
  5111         while (i--) {
       
  5112             dragi = drag[i];
       
  5113             dragi.el._drag = {};
       
  5114             eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
       
  5115         }
       
  5116         drag = [];
       
  5117     },
       
  5118     
       
  5119     elproto = R.el = {};
       
  5120     
       
  5121     
       
  5122     
       
  5123     
       
  5124     
       
  5125     
       
  5126     
       
  5127     
       
  5128     
       
  5129     
       
  5130     
       
  5131     
       
  5132     
       
  5133     
       
  5134     
       
  5135     
       
  5136     
       
  5137     
       
  5138     
       
  5139     
       
  5140     
       
  5141     
       
  5142     
       
  5143     
       
  5144     
       
  5145     
       
  5146     
       
  5147     
       
  5148     
       
  5149     
       
  5150     
       
  5151     
       
  5152     for (var i = events.length; i--;) {
       
  5153         (function (eventName) {
       
  5154             R[eventName] = elproto[eventName] = function (fn, scope) {
       
  5155                 if (R.is(fn, "function")) {
       
  5156                     this.events = this.events || [];
       
  5157                     this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
       
  5158                 }
       
  5159                 return this;
       
  5160             };
       
  5161             R["un" + eventName] = elproto["un" + eventName] = function (fn) {
       
  5162                 var events = this.events,
       
  5163                     l = events.length;
       
  5164                 while (l--) if (events[l].name == eventName && events[l].f == fn) {
       
  5165                     events[l].unbind();
       
  5166                     events.splice(l, 1);
       
  5167                     !events.length && delete this.events;
       
  5168                     return this;
       
  5169                 }
       
  5170                 return this;
       
  5171             };
       
  5172         })(events[i]);
       
  5173     }
       
  5174     
       
  5175     
       
  5176     elproto.data = function (key, value) {
       
  5177         var data = eldata[this.id] = eldata[this.id] || {};
       
  5178         if (arguments.length == 1) {
       
  5179             if (R.is(key, "object")) {
       
  5180                 for (var i in key) if (key[has](i)) {
       
  5181                     this.data(i, key[i]);
       
  5182                 }
       
  5183                 return this;
       
  5184             }
       
  5185             eve("data.get." + this.id, this, data[key], key);
       
  5186             return data[key];
       
  5187         }
       
  5188         data[key] = value;
       
  5189         eve("data.set." + this.id, this, value, key);
       
  5190         return this;
       
  5191     };
       
  5192     
       
  5193     elproto.removeData = function (key) {
       
  5194         if (key == null) {
       
  5195             eldata[this.id] = {};
       
  5196         } else {
       
  5197             eldata[this.id] && delete eldata[this.id][key];
       
  5198         }
       
  5199         return this;
       
  5200     };
       
  5201     
       
  5202     elproto.hover = function (f_in, f_out, scope_in, scope_out) {
       
  5203         return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
       
  5204     };
       
  5205     
       
  5206     elproto.unhover = function (f_in, f_out) {
       
  5207         return this.unmouseover(f_in).unmouseout(f_out);
       
  5208     };
       
  5209     
       
  5210     elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
       
  5211         function start(e) {
       
  5212             (e.originalEvent || e).preventDefault();
       
  5213             var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
       
  5214                 scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
       
  5215             this._drag.x = e.clientX + scrollX;
       
  5216             this._drag.y = e.clientY + scrollY;
       
  5217             this._drag.id = e.identifier;
       
  5218             !drag.length && R.mousemove(dragMove).mouseup(dragUp);
       
  5219             drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
       
  5220             onstart && eve.on("drag.start." + this.id, onstart);
       
  5221             onmove && eve.on("drag.move." + this.id, onmove);
       
  5222             onend && eve.on("drag.end." + this.id, onend);
       
  5223             eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
       
  5224         }
       
  5225         this._drag = {};
       
  5226         this.mousedown(start);
       
  5227         return this;
       
  5228     };
       
  5229     
       
  5230     elproto.onDragOver = function (f) {
       
  5231         f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
       
  5232     };
       
  5233     
       
  5234     elproto.undrag = function () {
       
  5235         var i = drag.length;
       
  5236         while (i--) if (drag[i].el == this) {
       
  5237             R.unmousedown(drag[i].start);
       
  5238             drag.splice(i++, 1);
       
  5239             eve.unbind("drag.*." + this.id);
       
  5240         }
       
  5241         !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
       
  5242     };
       
  5243     
       
  5244     paperproto.circle = function (x, y, r) {
       
  5245         var out = R._engine.circle(this, x || 0, y || 0, r || 0);
       
  5246         this.__set__ && this.__set__.push(out);
       
  5247         return out;
       
  5248     };
       
  5249     
       
  5250     paperproto.rect = function (x, y, w, h, r) {
       
  5251         var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
       
  5252         this.__set__ && this.__set__.push(out);
       
  5253         return out;
       
  5254     };
       
  5255     
       
  5256     paperproto.ellipse = function (x, y, rx, ry) {
       
  5257         var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
       
  5258         this.__set__ && this.__set__.push(out);
       
  5259         return out;
       
  5260     };
       
  5261     
       
  5262     paperproto.path = function (pathString) {
       
  5263         pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
       
  5264         var out = R._engine.path(R.format[apply](R, arguments), this);
       
  5265         this.__set__ && this.__set__.push(out);
       
  5266         return out;
       
  5267     };
       
  5268     
       
  5269     paperproto.image = function (src, x, y, w, h) {
       
  5270         var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
       
  5271         this.__set__ && this.__set__.push(out);
       
  5272         return out;
       
  5273     };
       
  5274     
       
  5275     paperproto.text = function (x, y, text) {
       
  5276         var out = R._engine.text(this, x || 0, y || 0, Str(text));
       
  5277         this.__set__ && this.__set__.push(out);
       
  5278         return out;
       
  5279     };
       
  5280     
       
  5281     paperproto.set = function (itemsArray) {
       
  5282         !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
       
  5283         var out = new Set(itemsArray);
       
  5284         this.__set__ && this.__set__.push(out);
       
  5285         return out;
       
  5286     };
       
  5287     
       
  5288     paperproto.setStart = function (set) {
       
  5289         this.__set__ = set || this.set();
       
  5290     };
       
  5291     
       
  5292     paperproto.setFinish = function (set) {
       
  5293         var out = this.__set__;
       
  5294         delete this.__set__;
       
  5295         return out;
       
  5296     };
       
  5297     
       
  5298     paperproto.setSize = function (width, height) {
       
  5299         return R._engine.setSize.call(this, width, height);
       
  5300     };
       
  5301     
       
  5302     paperproto.setViewBox = function (x, y, w, h, fit) {
       
  5303         return R._engine.setViewBox.call(this, x, y, w, h, fit);
       
  5304     };
       
  5305     
       
  5306     
       
  5307     paperproto.top = paperproto.bottom = null;
       
  5308     
       
  5309     paperproto.raphael = R;
       
  5310     var getOffset = function (elem) {
       
  5311         var box = elem.getBoundingClientRect(),
       
  5312             doc = elem.ownerDocument,
       
  5313             body = doc.body,
       
  5314             docElem = doc.documentElement,
       
  5315             clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
       
  5316             top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
       
  5317             left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
       
  5318         return {
       
  5319             y: top,
       
  5320             x: left
       
  5321         };
       
  5322     };
       
  5323     
       
  5324     paperproto.getElementByPoint = function (x, y) {
       
  5325         var paper = this,
       
  5326             svg = paper.canvas,
       
  5327             target = g.doc.elementFromPoint(x, y);
       
  5328         if (g.win.opera && target.tagName == "svg") {
       
  5329             var so = getOffset(svg),
       
  5330                 sr = svg.createSVGRect();
       
  5331             sr.x = x - so.x;
       
  5332             sr.y = y - so.y;
       
  5333             sr.width = sr.height = 1;
       
  5334             var hits = svg.getIntersectionList(sr, null);
       
  5335             if (hits.length) {
       
  5336                 target = hits[hits.length - 1];
       
  5337             }
       
  5338         }
       
  5339         if (!target) {
       
  5340             return null;
       
  5341         }
       
  5342         while (target.parentNode && target != svg.parentNode && !target.raphael) {
       
  5343             target = target.parentNode;
       
  5344         }
       
  5345         target == paper.canvas.parentNode && (target = svg);
       
  5346         target = target && target.raphael ? paper.getById(target.raphaelid) : null;
       
  5347         return target;
       
  5348     };
       
  5349     
       
  5350     paperproto.getById = function (id) {
       
  5351         var bot = this.bottom;
       
  5352         while (bot) {
       
  5353             if (bot.id == id) {
       
  5354                 return bot;
       
  5355             }
       
  5356             bot = bot.next;
       
  5357         }
       
  5358         return null;
       
  5359     };
       
  5360     
       
  5361     paperproto.forEach = function (callback, thisArg) {
       
  5362         var bot = this.bottom;
       
  5363         while (bot) {
       
  5364             if (callback.call(thisArg, bot) === false) {
       
  5365                 return this;
       
  5366             }
       
  5367             bot = bot.next;
       
  5368         }
       
  5369         return this;
       
  5370     };
       
  5371     function x_y() {
       
  5372         return this.x + S + this.y;
       
  5373     }
       
  5374     function x_y_w_h() {
       
  5375         return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
       
  5376     }
       
  5377     
       
  5378     elproto.getBBox = function (isWithoutTransform) {
       
  5379         if (this.removed) {
       
  5380             return {};
       
  5381         }
       
  5382         var _ = this._;
       
  5383         if (isWithoutTransform) {
       
  5384             if (_.dirty || !_.bboxwt) {
       
  5385                 this.realPath = getPath[this.type](this);
       
  5386                 _.bboxwt = pathDimensions(this.realPath);
       
  5387                 _.bboxwt.toString = x_y_w_h;
       
  5388                 _.dirty = 0;
       
  5389             }
       
  5390             return _.bboxwt;
       
  5391         }
       
  5392         if (_.dirty || _.dirtyT || !_.bbox) {
       
  5393             if (_.dirty || !this.realPath) {
       
  5394                 _.bboxwt = 0;
       
  5395                 this.realPath = getPath[this.type](this);
       
  5396             }
       
  5397             _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
       
  5398             _.bbox.toString = x_y_w_h;
       
  5399             _.dirty = _.dirtyT = 0;
       
  5400         }
       
  5401         return _.bbox;
       
  5402     };
       
  5403     
       
  5404     elproto.clone = function () {
       
  5405         if (this.removed) {
       
  5406             return null;
       
  5407         }
       
  5408         var out = this.paper[this.type]().attr(this.attr());
       
  5409         this.__set__ && this.__set__.push(out);
       
  5410         return out;
       
  5411     };
       
  5412     
       
  5413     elproto.glow = function (glow) {
       
  5414         if (this.type == "text") {
       
  5415             return null;
       
  5416         }
       
  5417         glow = glow || {};
       
  5418         var s = {
       
  5419             width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
       
  5420             fill: glow.fill || false,
       
  5421             opacity: glow.opacity || .5,
       
  5422             offsetx: glow.offsetx || 0,
       
  5423             offsety: glow.offsety || 0,
       
  5424             color: glow.color || "#000"
       
  5425         },
       
  5426             c = s.width / 2,
       
  5427             r = this.paper,
       
  5428             out = r.set(),
       
  5429             path = this.realPath || getPath[this.type](this);
       
  5430         path = this.matrix ? mapPath(path, this.matrix) : path;
       
  5431         for (var i = 1; i < c + 1; i++) {
       
  5432             out.push(r.path(path).attr({
       
  5433                 stroke: s.color,
       
  5434                 fill: s.fill ? s.color : "none",
       
  5435                 "stroke-linejoin": "round",
       
  5436                 "stroke-linecap": "round",
       
  5437                 "stroke-width": +(s.width / c * i).toFixed(3),
       
  5438                 opacity: +(s.opacity / c).toFixed(3)
       
  5439             }));
       
  5440         }
       
  5441         return out.insertBefore(this).translate(s.offsetx, s.offsety);
       
  5442     };
       
  5443     var curveslengths = {},
       
  5444     getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
       
  5445         var len = 0,
       
  5446             precision = 100,
       
  5447             name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
       
  5448             cache = curveslengths[name],
       
  5449             old, dot;
       
  5450         !cache && (curveslengths[name] = cache = {data: []});
       
  5451         cache.timer && clearTimeout(cache.timer);
       
  5452         cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3);
       
  5453         if (length != null && !cache.precision) {
       
  5454             var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
       
  5455             cache.precision = ~~total * 10;
       
  5456             cache.data = [];
       
  5457         }
       
  5458         precision = cache.precision || precision;
       
  5459         for (var i = 0; i < precision + 1; i++) {
       
  5460             if (cache.data[i * precision]) {
       
  5461                 dot = cache.data[i * precision];
       
  5462             } else {
       
  5463                 dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
       
  5464                 cache.data[i * precision] = dot;
       
  5465             }
       
  5466             i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
       
  5467             if (length != null && len >= length) {
       
  5468                 return dot;
       
  5469             }
       
  5470             old = dot;
       
  5471         }
       
  5472         if (length == null) {
       
  5473             return len;
       
  5474         }
       
  5475     },
       
  5476     getLengthFactory = function (istotal, subpath) {
       
  5477         return function (path, length, onlystart) {
       
  5478             path = path2curve(path);
       
  5479             var x, y, p, l, sp = "", subpaths = {}, point,
       
  5480                 len = 0;
       
  5481             for (var i = 0, ii = path.length; i < ii; i++) {
       
  5482                 p = path[i];
       
  5483                 if (p[0] == "M") {
       
  5484                     x = +p[1];
       
  5485                     y = +p[2];
       
  5486                 } else {
       
  5487                     l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
       
  5488                     if (len + l > length) {
       
  5489                         if (subpath && !subpaths.start) {
       
  5490                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
       
  5491                             sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
       
  5492                             if (onlystart) {return sp;}
       
  5493                             subpaths.start = sp;
       
  5494                             sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
       
  5495                             len += l;
       
  5496                             x = +p[5];
       
  5497                             y = +p[6];
       
  5498                             continue;
       
  5499                         }
       
  5500                         if (!istotal && !subpath) {
       
  5501                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
       
  5502                             return {x: point.x, y: point.y, alpha: point.alpha};
       
  5503                         }
       
  5504                     }
       
  5505                     len += l;
       
  5506                     x = +p[5];
       
  5507                     y = +p[6];
       
  5508                 }
       
  5509                 sp += p.shift() + p;
       
  5510             }
       
  5511             subpaths.end = sp;
       
  5512             point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
       
  5513             point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
       
  5514             return point;
       
  5515         };
       
  5516     };
       
  5517     var getTotalLength = getLengthFactory(1),
       
  5518         getPointAtLength = getLengthFactory(),
       
  5519         getSubpathsAtLength = getLengthFactory(0, 1);
       
  5520     
       
  5521     R.getTotalLength = getTotalLength;
       
  5522     
       
  5523     R.getPointAtLength = getPointAtLength;
       
  5524     
       
  5525     R.getSubpath = function (path, from, to) {
       
  5526         if (this.getTotalLength(path) - to < 1e-6) {
       
  5527             return getSubpathsAtLength(path, from).end;
       
  5528         }
       
  5529         var a = getSubpathsAtLength(path, to, 1);
       
  5530         return from ? getSubpathsAtLength(a, from).end : a;
       
  5531     };
       
  5532     
       
  5533     elproto.getTotalLength = function () {
       
  5534         if (this.type != "path") {return;}
       
  5535         if (this.node.getTotalLength) {
       
  5536             return this.node.getTotalLength();
       
  5537         }
       
  5538         return getTotalLength(this.attrs.path);
       
  5539     };
       
  5540     
       
  5541     elproto.getPointAtLength = function (length) {
       
  5542         if (this.type != "path") {return;}
       
  5543         return getPointAtLength(this.attrs.path, length);
       
  5544     };
       
  5545     
       
  5546     elproto.getSubpath = function (from, to) {
       
  5547         if (this.type != "path") {return;}
       
  5548         return R.getSubpath(this.attrs.path, from, to);
       
  5549     };
       
  5550     
       
  5551     var ef = R.easing_formulas = {
       
  5552         linear: function (n) {
       
  5553             return n;
       
  5554         },
       
  5555         "<": function (n) {
       
  5556             return pow(n, 1.7);
       
  5557         },
       
  5558         ">": function (n) {
       
  5559             return pow(n, .48);
       
  5560         },
       
  5561         "<>": function (n) {
       
  5562             var q = .48 - n / 1.04,
       
  5563                 Q = math.sqrt(.1734 + q * q),
       
  5564                 x = Q - q,
       
  5565                 X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
       
  5566                 y = -Q - q,
       
  5567                 Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
       
  5568                 t = X + Y + .5;
       
  5569             return (1 - t) * 3 * t * t + t * t * t;
       
  5570         },
       
  5571         backIn: function (n) {
       
  5572             var s = 1.70158;
       
  5573             return n * n * ((s + 1) * n - s);
       
  5574         },
       
  5575         backOut: function (n) {
       
  5576             n = n - 1;
       
  5577             var s = 1.70158;
       
  5578             return n * n * ((s + 1) * n + s) + 1;
       
  5579         },
       
  5580         elastic: function (n) {
       
  5581             if (n == !!n) {
       
  5582                 return n;
       
  5583             }
       
  5584             return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
       
  5585         },
       
  5586         bounce: function (n) {
       
  5587             var s = 7.5625,
       
  5588                 p = 2.75,
       
  5589                 l;
       
  5590             if (n < (1 / p)) {
       
  5591                 l = s * n * n;
       
  5592             } else {
       
  5593                 if (n < (2 / p)) {
       
  5594                     n -= (1.5 / p);
       
  5595                     l = s * n * n + .75;
       
  5596                 } else {
       
  5597                     if (n < (2.5 / p)) {
       
  5598                         n -= (2.25 / p);
       
  5599                         l = s * n * n + .9375;
       
  5600                     } else {
       
  5601                         n -= (2.625 / p);
       
  5602                         l = s * n * n + .984375;
       
  5603                     }
       
  5604                 }
       
  5605             }
       
  5606             return l;
       
  5607         }
       
  5608     };
       
  5609     ef.easeIn = ef["ease-in"] = ef["<"];
       
  5610     ef.easeOut = ef["ease-out"] = ef[">"];
       
  5611     ef.easeInOut = ef["ease-in-out"] = ef["<>"];
       
  5612     ef["back-in"] = ef.backIn;
       
  5613     ef["back-out"] = ef.backOut;
       
  5614 
       
  5615     var animationElements = [],
       
  5616         requestAnimFrame = window.requestAnimationFrame       ||
       
  5617                            window.webkitRequestAnimationFrame ||
       
  5618                            window.mozRequestAnimationFrame    ||
       
  5619                            window.oRequestAnimationFrame      ||
       
  5620                            window.msRequestAnimationFrame     ||
       
  5621                            function (callback) {
       
  5622                                setTimeout(callback, 16);
       
  5623                            },
       
  5624         animation = function () {
       
  5625             var Now = +new Date,
       
  5626                 l = 0;
       
  5627             for (; l < animationElements.length; l++) {
       
  5628                 var e = animationElements[l];
       
  5629                 if (e.el.removed || e.paused) {
       
  5630                     continue;
       
  5631                 }
       
  5632                 var time = Now - e.start,
       
  5633                     ms = e.ms,
       
  5634                     easing = e.easing,
       
  5635                     from = e.from,
       
  5636                     diff = e.diff,
       
  5637                     to = e.to,
       
  5638                     t = e.t,
       
  5639                     that = e.el,
       
  5640                     set = {},
       
  5641                     now,
       
  5642                     init = {},
       
  5643                     key;
       
  5644                 if (e.initstatus) {
       
  5645                     time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
       
  5646                     e.status = e.initstatus;
       
  5647                     delete e.initstatus;
       
  5648                     e.stop && animationElements.splice(l--, 1);
       
  5649                 } else {
       
  5650                     e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
       
  5651                 }
       
  5652                 if (time < 0) {
       
  5653                     continue;
       
  5654                 }
       
  5655                 if (time < ms) {
       
  5656                     var pos = easing(time / ms);
       
  5657                     for (var attr in from) if (from[has](attr)) {
       
  5658                         switch (availableAnimAttrs[attr]) {
       
  5659                             case nu:
       
  5660                                 now = +from[attr] + pos * ms * diff[attr];
       
  5661                                 break;
       
  5662                             case "colour":
       
  5663                                 now = "rgb(" + [
       
  5664                                     upto255(round(from[attr].r + pos * ms * diff[attr].r)),
       
  5665                                     upto255(round(from[attr].g + pos * ms * diff[attr].g)),
       
  5666                                     upto255(round(from[attr].b + pos * ms * diff[attr].b))
       
  5667                                 ].join(",") + ")";
       
  5668                                 break;
       
  5669                             case "path":
       
  5670                                 now = [];
       
  5671                                 for (var i = 0, ii = from[attr].length; i < ii; i++) {
       
  5672                                     now[i] = [from[attr][i][0]];
       
  5673                                     for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
       
  5674                                         now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
       
  5675                                     }
       
  5676                                     now[i] = now[i].join(S);
       
  5677                                 }
       
  5678                                 now = now.join(S);
       
  5679                                 break;
       
  5680                             case "transform":
       
  5681                                 if (diff[attr].real) {
       
  5682                                     now = [];
       
  5683                                     for (i = 0, ii = from[attr].length; i < ii; i++) {
       
  5684                                         now[i] = [from[attr][i][0]];
       
  5685                                         for (j = 1, jj = from[attr][i].length; j < jj; j++) {
       
  5686                                             now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
       
  5687                                         }
       
  5688                                     }
       
  5689                                 } else {
       
  5690                                     var get = function (i) {
       
  5691                                         return +from[attr][i] + pos * ms * diff[attr][i];
       
  5692                                     };
       
  5693                                     // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
       
  5694                                     now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
       
  5695                                 }
       
  5696                                 break;
       
  5697                             case "csv":
       
  5698                                 if (attr == "clip-rect") {
       
  5699                                     now = [];
       
  5700                                     i = 4;
       
  5701                                     while (i--) {
       
  5702                                         now[i] = +from[attr][i] + pos * ms * diff[attr][i];
       
  5703                                     }
       
  5704                                 }
       
  5705                                 break;
       
  5706                             default:
       
  5707                                 var from2 = [][concat](from[attr]);
       
  5708                                 now = [];
       
  5709                                 i = that.paper.customAttributes[attr].length;
       
  5710                                 while (i--) {
       
  5711                                     now[i] = +from2[i] + pos * ms * diff[attr][i];
       
  5712                                 }
       
  5713                                 break;
       
  5714                         }
       
  5715                         set[attr] = now;
       
  5716                     }
       
  5717                     that.attr(set);
       
  5718                     (function (id, that, anim) {
       
  5719                         setTimeout(function () {
       
  5720                             eve("anim.frame." + id, that, anim);
       
  5721                         });
       
  5722                     })(that.id, that, e.anim);
       
  5723                 } else {
       
  5724                     (function(f, el, a) {
       
  5725                         setTimeout(function() {
       
  5726                             eve("anim.frame." + el.id, el, a);
       
  5727                             eve("anim.finish." + el.id, el, a);
       
  5728                             R.is(f, "function") && f.call(el);
       
  5729                         });
       
  5730                     })(e.callback, that, e.anim);
       
  5731                     that.attr(to);
       
  5732                     animationElements.splice(l--, 1);
       
  5733                     if (e.repeat > 1 && !e.next) {
       
  5734                         for (key in to) if (to[has](key)) {
       
  5735                             init[key] = e.totalOrigin[key];
       
  5736                         }
       
  5737                         e.el.attr(init);
       
  5738                         runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
       
  5739                     }
       
  5740                     if (e.next && !e.stop) {
       
  5741                         runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
       
  5742                     }
       
  5743                 }
       
  5744             }
       
  5745             R.svg && that && that.paper && that.paper.safari();
       
  5746             animationElements.length && requestAnimFrame(animation);
       
  5747         },
       
  5748         upto255 = function (color) {
       
  5749             return color > 255 ? 255 : color < 0 ? 0 : color;
       
  5750         };
       
  5751     
       
  5752     elproto.animateWith = function (element, anim, params, ms, easing, callback) {
       
  5753         var a = params ? R.animation(params, ms, easing, callback) : anim;
       
  5754             status = element.status(anim);
       
  5755         return this.animate(a).status(a, status * anim.ms / a.ms);
       
  5756     };
       
  5757     function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
       
  5758         var cx = 3 * p1x,
       
  5759             bx = 3 * (p2x - p1x) - cx,
       
  5760             ax = 1 - cx - bx,
       
  5761             cy = 3 * p1y,
       
  5762             by = 3 * (p2y - p1y) - cy,
       
  5763             ay = 1 - cy - by;
       
  5764         function sampleCurveX(t) {
       
  5765             return ((ax * t + bx) * t + cx) * t;
       
  5766         }
       
  5767         function solve(x, epsilon) {
       
  5768             var t = solveCurveX(x, epsilon);
       
  5769             return ((ay * t + by) * t + cy) * t;
       
  5770         }
       
  5771         function solveCurveX(x, epsilon) {
       
  5772             var t0, t1, t2, x2, d2, i;
       
  5773             for(t2 = x, i = 0; i < 8; i++) {
       
  5774                 x2 = sampleCurveX(t2) - x;
       
  5775                 if (abs(x2) < epsilon) {
       
  5776                     return t2;
       
  5777                 }
       
  5778                 d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
       
  5779                 if (abs(d2) < 1e-6) {
       
  5780                     break;
       
  5781                 }
       
  5782                 t2 = t2 - x2 / d2;
       
  5783             }
       
  5784             t0 = 0;
       
  5785             t1 = 1;
       
  5786             t2 = x;
       
  5787             if (t2 < t0) {
       
  5788                 return t0;
       
  5789             }
       
  5790             if (t2 > t1) {
       
  5791                 return t1;
       
  5792             }
       
  5793             while (t0 < t1) {
       
  5794                 x2 = sampleCurveX(t2);
       
  5795                 if (abs(x2 - x) < epsilon) {
       
  5796                     return t2;
       
  5797                 }
       
  5798                 if (x > x2) {
       
  5799                     t0 = t2;
       
  5800                 } else {
       
  5801                     t1 = t2;
       
  5802                 }
       
  5803                 t2 = (t1 - t0) / 2 + t0;
       
  5804             }
       
  5805             return t2;
       
  5806         }
       
  5807         return solve(t, 1 / (200 * duration));
       
  5808     }
       
  5809     elproto.onAnimation = function (f) {
       
  5810         f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
       
  5811         return this;
       
  5812     };
       
  5813     function Animation(anim, ms) {
       
  5814         var percents = [],
       
  5815             newAnim = {};
       
  5816         this.ms = ms;
       
  5817         this.times = 1;
       
  5818         if (anim) {
       
  5819             for (var attr in anim) if (anim[has](attr)) {
       
  5820                 newAnim[toFloat(attr)] = anim[attr];
       
  5821                 percents.push(toFloat(attr));
       
  5822             }
       
  5823             percents.sort(sortByNumber);
       
  5824         }
       
  5825         this.anim = newAnim;
       
  5826         this.top = percents[percents.length - 1];
       
  5827         this.percents = percents;
       
  5828     }
       
  5829     
       
  5830     Animation.prototype.delay = function (delay) {
       
  5831         var a = new Animation(this.anim, this.ms);
       
  5832         a.times = this.times;
       
  5833         a.del = +delay || 0;
       
  5834         return a;
       
  5835     };
       
  5836     
       
  5837     Animation.prototype.repeat = function (times) { 
       
  5838         var a = new Animation(this.anim, this.ms);
       
  5839         a.del = this.del;
       
  5840         a.times = math.floor(mmax(times, 0)) || 1;
       
  5841         return a;
       
  5842     };
       
  5843     function runAnimation(anim, element, percent, status, totalOrigin, times) {
       
  5844         percent = toFloat(percent);
       
  5845         var params,
       
  5846             isInAnim,
       
  5847             isInAnimSet,
       
  5848             percents = [],
       
  5849             next,
       
  5850             prev,
       
  5851             timestamp,
       
  5852             ms = anim.ms,
       
  5853             from = {},
       
  5854             to = {},
       
  5855             diff = {};
       
  5856         if (status) {
       
  5857             for (i = 0, ii = animationElements.length; i < ii; i++) {
       
  5858                 var e = animationElements[i];
       
  5859                 if (e.el.id == element.id && e.anim == anim) {
       
  5860                     if (e.percent != percent) {
       
  5861                         animationElements.splice(i, 1);
       
  5862                         isInAnimSet = 1;
       
  5863                     } else {
       
  5864                         isInAnim = e;
       
  5865                     }
       
  5866                     element.attr(e.totalOrigin);
       
  5867                     break;
       
  5868                 }
       
  5869             }
       
  5870         } else {
       
  5871             status = +to; // NaN
       
  5872         }
       
  5873         for (var i = 0, ii = anim.percents.length; i < ii; i++) {
       
  5874             if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
       
  5875                 percent = anim.percents[i];
       
  5876                 prev = anim.percents[i - 1] || 0;
       
  5877                 ms = ms / anim.top * (percent - prev);
       
  5878                 next = anim.percents[i + 1];
       
  5879                 params = anim.anim[percent];
       
  5880                 break;
       
  5881             } else if (status) {
       
  5882                 element.attr(anim.anim[anim.percents[i]]);
       
  5883             }
       
  5884         }
       
  5885         if (!params) {
       
  5886             return;
       
  5887         }
       
  5888         if (!isInAnim) {
       
  5889             for (attr in params) if (params[has](attr)) {
       
  5890                 if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
       
  5891                     from[attr] = element.attr(attr);
       
  5892                     (from[attr] == null) && (from[attr] = availableAttrs[attr]);
       
  5893                     to[attr] = params[attr];
       
  5894                     switch (availableAnimAttrs[attr]) {
       
  5895                         case nu:
       
  5896                             diff[attr] = (to[attr] - from[attr]) / ms;
       
  5897                             break;
       
  5898                         case "colour":
       
  5899                             from[attr] = R.getRGB(from[attr]);
       
  5900                             var toColour = R.getRGB(to[attr]);
       
  5901                             diff[attr] = {
       
  5902                                 r: (toColour.r - from[attr].r) / ms,
       
  5903                                 g: (toColour.g - from[attr].g) / ms,
       
  5904                                 b: (toColour.b - from[attr].b) / ms
       
  5905                             };
       
  5906                             break;
       
  5907                         case "path":
       
  5908                             var pathes = path2curve(from[attr], to[attr]),
       
  5909                                 toPath = pathes[1];
       
  5910                             from[attr] = pathes[0];
       
  5911                             diff[attr] = [];
       
  5912                             for (i = 0, ii = from[attr].length; i < ii; i++) {
       
  5913                                 diff[attr][i] = [0];
       
  5914                                 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
       
  5915                                     diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
       
  5916                                 }
       
  5917                             }
       
  5918                             break;
       
  5919                         case "transform":
       
  5920                             var _ = element._,
       
  5921                                 eq = equaliseTransform(_[attr], to[attr]);
       
  5922                             if (eq) {
       
  5923                                 from[attr] = eq.from;
       
  5924                                 to[attr] = eq.to;
       
  5925                                 diff[attr] = [];
       
  5926                                 diff[attr].real = true;
       
  5927                                 for (i = 0, ii = from[attr].length; i < ii; i++) {
       
  5928                                     diff[attr][i] = [from[attr][i][0]];
       
  5929                                     for (j = 1, jj = from[attr][i].length; j < jj; j++) {
       
  5930                                         diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
       
  5931                                     }
       
  5932                                 }
       
  5933                             } else {
       
  5934                                 var m = (element.matrix || new Matrix),
       
  5935                                     to2 = {
       
  5936                                         _: {transform: _.transform},
       
  5937                                         getBBox: function () {
       
  5938                                             return element.getBBox(1);
       
  5939                                         }
       
  5940                                     };
       
  5941                                 from[attr] = [
       
  5942                                     m.a,
       
  5943                                     m.b,
       
  5944                                     m.c,
       
  5945                                     m.d,
       
  5946                                     m.e,
       
  5947                                     m.f
       
  5948                                 ];
       
  5949                                 extractTransform(to2, to[attr]);
       
  5950                                 to[attr] = to2._.transform;
       
  5951                                 diff[attr] = [
       
  5952                                     (to2.matrix.a - m.a) / ms,
       
  5953                                     (to2.matrix.b - m.b) / ms,
       
  5954                                     (to2.matrix.c - m.c) / ms,
       
  5955                                     (to2.matrix.d - m.d) / ms,
       
  5956                                     (to2.matrix.e - m.e) / ms,
       
  5957                                     (to2.matrix.e - m.f) / ms
       
  5958                                 ];
       
  5959                                 // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
       
  5960                                 // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
       
  5961                                 // extractTransform(to2, to[attr]);
       
  5962                                 // diff[attr] = [
       
  5963                                 //     (to2._.sx - _.sx) / ms,
       
  5964                                 //     (to2._.sy - _.sy) / ms,
       
  5965                                 //     (to2._.deg - _.deg) / ms,
       
  5966                                 //     (to2._.dx - _.dx) / ms,
       
  5967                                 //     (to2._.dy - _.dy) / ms
       
  5968                                 // ];
       
  5969                             }
       
  5970                             break;
       
  5971                         case "csv":
       
  5972                             var values = Str(params[attr])[split](separator),
       
  5973                                 from2 = Str(from[attr])[split](separator);
       
  5974                             if (attr == "clip-rect") {
       
  5975                                 from[attr] = from2;
       
  5976                                 diff[attr] = [];
       
  5977                                 i = from2.length;
       
  5978                                 while (i--) {
       
  5979                                     diff[attr][i] = (values[i] - from[attr][i]) / ms;
       
  5980                                 }
       
  5981                             }
       
  5982                             to[attr] = values;
       
  5983                             break;
       
  5984                         default:
       
  5985                             values = [][concat](params[attr]);
       
  5986                             from2 = [][concat](from[attr]);
       
  5987                             diff[attr] = [];
       
  5988                             i = element.paper.customAttributes[attr].length;
       
  5989                             while (i--) {
       
  5990                                 diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
       
  5991                             }
       
  5992                             break;
       
  5993                     }
       
  5994                 }
       
  5995             }
       
  5996             var easing = params.easing,
       
  5997                 easyeasy = R.easing_formulas[easing];
       
  5998             if (!easyeasy) {
       
  5999                 easyeasy = Str(easing).match(bezierrg);
       
  6000                 if (easyeasy && easyeasy.length == 5) {
       
  6001                     var curve = easyeasy;
       
  6002                     easyeasy = function (t) {
       
  6003                         return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
       
  6004                     };
       
  6005                 } else {
       
  6006                     easyeasy = pipe;
       
  6007                 }
       
  6008             }
       
  6009             timestamp = params.start || anim.start || +new Date;
       
  6010             e = {
       
  6011                 anim: anim,
       
  6012                 percent: percent,
       
  6013                 timestamp: timestamp,
       
  6014                 start: timestamp + (anim.del || 0),
       
  6015                 status: 0,
       
  6016                 initstatus: status || 0,
       
  6017                 stop: false,
       
  6018                 ms: ms,
       
  6019                 easing: easyeasy,
       
  6020                 from: from,
       
  6021                 diff: diff,
       
  6022                 to: to,
       
  6023                 el: element,
       
  6024                 callback: params.callback,
       
  6025                 prev: prev,
       
  6026                 next: next,
       
  6027                 repeat: times || anim.times,
       
  6028                 origin: element.attr(),
       
  6029                 totalOrigin: totalOrigin
       
  6030             };
       
  6031             animationElements.push(e);
       
  6032             if (status && !isInAnim && !isInAnimSet) {
       
  6033                 e.stop = true;
       
  6034                 e.start = new Date - ms * status;
       
  6035                 if (animationElements.length == 1) {
       
  6036                     return animation();
       
  6037                 }
       
  6038             }
       
  6039             if (isInAnimSet) {
       
  6040                 e.start = new Date - e.ms * status;
       
  6041             }
       
  6042             animationElements.length == 1 && requestAnimFrame(animation);
       
  6043         } else {
       
  6044             isInAnim.initstatus = status;
       
  6045             isInAnim.start = new Date - isInAnim.ms * status;
       
  6046         }
       
  6047         eve("anim.start." + element.id, element, anim);
       
  6048     }
       
  6049     
       
  6050     R.animation = function (params, ms, easing, callback) {
       
  6051         if (params instanceof Animation) {
       
  6052             return params;
       
  6053         }
       
  6054         if (R.is(easing, "function") || !easing) {
       
  6055             callback = callback || easing || null;
       
  6056             easing = null;
       
  6057         }
       
  6058         params = Object(params);
       
  6059         ms = +ms || 0;
       
  6060         var p = {},
       
  6061             json,
       
  6062             attr;
       
  6063         for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
       
  6064             json = true;
       
  6065             p[attr] = params[attr];
       
  6066         }
       
  6067         if (!json) {
       
  6068             return new Animation(params, ms);
       
  6069         } else {
       
  6070             easing && (p.easing = easing);
       
  6071             callback && (p.callback = callback);
       
  6072             return new Animation({100: p}, ms);
       
  6073         }
       
  6074     };
       
  6075     
       
  6076     elproto.animate = function (params, ms, easing, callback) {
       
  6077         var element = this;
       
  6078         if (element.removed) {
       
  6079             callback && callback.call(element);
       
  6080             return element;
       
  6081         }
       
  6082         var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
       
  6083         runAnimation(anim, element, anim.percents[0], null, element.attr());
       
  6084         return element;
       
  6085     };
       
  6086     
       
  6087     elproto.setTime = function (anim, value) {
       
  6088         if (anim && value != null) {
       
  6089             this.status(anim, mmin(value, anim.ms) / anim.ms);
       
  6090         }
       
  6091         return this;
       
  6092     };
       
  6093     
       
  6094     elproto.status = function (anim, value) {
       
  6095         var out = [],
       
  6096             i = 0,
       
  6097             len,
       
  6098             e;
       
  6099         if (value != null) {
       
  6100             runAnimation(anim, this, -1, mmin(value, 1));
       
  6101             return this;
       
  6102         } else {
       
  6103             len = animationElements.length;
       
  6104             for (; i < len; i++) {
       
  6105                 e = animationElements[i];
       
  6106                 if (e.el.id == this.id && (!anim || e.anim == anim)) {
       
  6107                     if (anim) {
       
  6108                         return e.status;
       
  6109                     }
       
  6110                     out.push({
       
  6111                         anim: e.anim,
       
  6112                         status: e.status
       
  6113                     });
       
  6114                 }
       
  6115             }
       
  6116             if (anim) {
       
  6117                 return 0;
       
  6118             }
       
  6119             return out;
       
  6120         }
       
  6121     };
       
  6122     
       
  6123     elproto.pause = function (anim) {
       
  6124         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
       
  6125             if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
       
  6126                 animationElements[i].paused = true;
       
  6127             }
       
  6128         }
       
  6129         return this;
       
  6130     };
       
  6131     
       
  6132     elproto.resume = function (anim) {
       
  6133         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
       
  6134             var e = animationElements[i];
       
  6135             if (eve("anim.resume." + this.id, this, e.anim) !== false) {
       
  6136                 delete e.paused;
       
  6137                 this.status(e.anim, e.status);
       
  6138             }
       
  6139         }
       
  6140         return this;
       
  6141     };
       
  6142     
       
  6143     elproto.stop = function (anim) {
       
  6144         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
       
  6145             if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
       
  6146                 animationElements.splice(i--, 1);
       
  6147             }
       
  6148         }
       
  6149         return this;
       
  6150     };
       
  6151     elproto.toString = function () {
       
  6152         return "Rapha\xebl\u2019s object";
       
  6153     };
       
  6154 
       
  6155     // Set
       
  6156     var Set = function (items) {
       
  6157         this.items = [];
       
  6158         this.length = 0;
       
  6159         this.type = "set";
       
  6160         if (items) {
       
  6161             for (var i = 0, ii = items.length; i < ii; i++) {
       
  6162                 if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
       
  6163                     this[this.items.length] = this.items[this.items.length] = items[i];
       
  6164                     this.length++;
       
  6165                 }
       
  6166             }
       
  6167         }
       
  6168     },
       
  6169     setproto = Set.prototype;
       
  6170     
       
  6171     setproto.push = function () {
       
  6172         var item,
       
  6173             len;
       
  6174         for (var i = 0, ii = arguments.length; i < ii; i++) {
       
  6175             item = arguments[i];
       
  6176             if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
       
  6177                 len = this.items.length;
       
  6178                 this[len] = this.items[len] = item;
       
  6179                 this.length++;
       
  6180             }
       
  6181         }
       
  6182         return this;
       
  6183     };
       
  6184     
       
  6185     setproto.pop = function () {
       
  6186         this.length && delete this[this.length--];
       
  6187         return this.items.pop();
       
  6188     };
       
  6189     
       
  6190     setproto.forEach = function (callback, thisArg) {
       
  6191         for (var i = 0, ii = this.items.length; i < ii; i++) {
       
  6192             if (callback.call(thisArg, this.items[i], i) === false) {
       
  6193                 return this;
       
  6194             }
       
  6195         }
       
  6196         return this;
       
  6197     };
       
  6198     for (var method in elproto) if (elproto[has](method)) {
       
  6199         setproto[method] = (function (methodname) {
       
  6200             return function () {
       
  6201                 var arg = arguments;
       
  6202                 return this.forEach(function (el) {
       
  6203                     el[methodname][apply](el, arg);
       
  6204                 });
       
  6205             };
       
  6206         })(method);
       
  6207     }
       
  6208     setproto.attr = function (name, value) {
       
  6209         if (name && R.is(name, array) && R.is(name[0], "object")) {
       
  6210             for (var j = 0, jj = name.length; j < jj; j++) {
       
  6211                 this.items[j].attr(name[j]);
       
  6212             }
       
  6213         } else {
       
  6214             for (var i = 0, ii = this.items.length; i < ii; i++) {
       
  6215                 this.items[i].attr(name, value);
       
  6216             }
       
  6217         }
       
  6218         return this;
       
  6219     };
       
  6220     
       
  6221     setproto.clear = function () {
       
  6222         while (this.length) {
       
  6223             this.pop();
       
  6224         }
       
  6225     };
       
  6226     
       
  6227     setproto.splice = function (index, count, insertion) {
       
  6228         index = index < 0 ? mmax(this.length + index, 0) : index;
       
  6229         count = mmax(0, mmin(this.length - index, count));
       
  6230         var tail = [],
       
  6231             todel = [],
       
  6232             args = [],
       
  6233             i;
       
  6234         for (i = 2; i < arguments.length; i++) {
       
  6235             args.push(arguments[i]);
       
  6236         }
       
  6237         for (i = 0; i < count; i++) {
       
  6238             todel.push(this[index + i]);
       
  6239         }
       
  6240         for (; i < this.length - index; i++) {
       
  6241             tail.push(this[index + i]);
       
  6242         }
       
  6243         var arglen = args.length;
       
  6244         for (i = 0; i < arglen + tail.length; i++) {
       
  6245             this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
       
  6246         }
       
  6247         i = this.items.length = this.length -= count - arglen;
       
  6248         while (this[i]) {
       
  6249             delete this[i++];
       
  6250         }
       
  6251         return new Set(todel);
       
  6252     };
       
  6253     
       
  6254     setproto.exclude = function (el) {
       
  6255         for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
       
  6256             this.splice(i, 1);
       
  6257             return true;
       
  6258         }
       
  6259     };
       
  6260     setproto.animate = function (params, ms, easing, callback) {
       
  6261         (R.is(easing, "function") || !easing) && (callback = easing || null);
       
  6262         var len = this.items.length,
       
  6263             i = len,
       
  6264             item,
       
  6265             set = this,
       
  6266             collector;
       
  6267         if (!len) {
       
  6268             return this;
       
  6269         }
       
  6270         callback && (collector = function () {
       
  6271             !--len && callback.call(set);
       
  6272         });
       
  6273         easing = R.is(easing, string) ? easing : collector;
       
  6274         var anim = R.animation(params, ms, easing, collector);
       
  6275         item = this.items[--i].animate(anim);
       
  6276         while (i--) {
       
  6277             this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim);
       
  6278         }
       
  6279         return this;
       
  6280     };
       
  6281     setproto.insertAfter = function (el) {
       
  6282         var i = this.items.length;
       
  6283         while (i--) {
       
  6284             this.items[i].insertAfter(el);
       
  6285         }
       
  6286         return this;
       
  6287     };
       
  6288     setproto.getBBox = function () {
       
  6289         var x = [],
       
  6290             y = [],
       
  6291             w = [],
       
  6292             h = [];
       
  6293         for (var i = this.items.length; i--;) if (!this.items[i].removed) {
       
  6294             var box = this.items[i].getBBox();
       
  6295             x.push(box.x);
       
  6296             y.push(box.y);
       
  6297             w.push(box.x + box.width);
       
  6298             h.push(box.y + box.height);
       
  6299         }
       
  6300         x = mmin[apply](0, x);
       
  6301         y = mmin[apply](0, y);
       
  6302         return {
       
  6303             x: x,
       
  6304             y: y,
       
  6305             width: mmax[apply](0, w) - x,
       
  6306             height: mmax[apply](0, h) - y
       
  6307         };
       
  6308     };
       
  6309     setproto.clone = function (s) {
       
  6310         s = new Set;
       
  6311         for (var i = 0, ii = this.items.length; i < ii; i++) {
       
  6312             s.push(this.items[i].clone());
       
  6313         }
       
  6314         return s;
       
  6315     };
       
  6316     setproto.toString = function () {
       
  6317         return "Rapha\xebl\u2018s set";
       
  6318     };
       
  6319 
       
  6320     
       
  6321     R.registerFont = function (font) {
       
  6322         if (!font.face) {
       
  6323             return font;
       
  6324         }
       
  6325         this.fonts = this.fonts || {};
       
  6326         var fontcopy = {
       
  6327                 w: font.w,
       
  6328                 face: {},
       
  6329                 glyphs: {}
       
  6330             },
       
  6331             family = font.face["font-family"];
       
  6332         for (var prop in font.face) if (font.face[has](prop)) {
       
  6333             fontcopy.face[prop] = font.face[prop];
       
  6334         }
       
  6335         if (this.fonts[family]) {
       
  6336             this.fonts[family].push(fontcopy);
       
  6337         } else {
       
  6338             this.fonts[family] = [fontcopy];
       
  6339         }
       
  6340         if (!font.svg) {
       
  6341             fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
       
  6342             for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
       
  6343                 var path = font.glyphs[glyph];
       
  6344                 fontcopy.glyphs[glyph] = {
       
  6345                     w: path.w,
       
  6346                     k: {},
       
  6347                     d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
       
  6348                             return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
       
  6349                         }) + "z"
       
  6350                 };
       
  6351                 if (path.k) {
       
  6352                     for (var k in path.k) if (path[has](k)) {
       
  6353                         fontcopy.glyphs[glyph].k[k] = path.k[k];
       
  6354                     }
       
  6355                 }
       
  6356             }
       
  6357         }
       
  6358         return font;
       
  6359     };
       
  6360     
       
  6361     paperproto.getFont = function (family, weight, style, stretch) {
       
  6362         stretch = stretch || "normal";
       
  6363         style = style || "normal";
       
  6364         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
       
  6365         if (!R.fonts) {
       
  6366             return;
       
  6367         }
       
  6368         var font = R.fonts[family];
       
  6369         if (!font) {
       
  6370             var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
       
  6371             for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
       
  6372                 if (name.test(fontName)) {
       
  6373                     font = R.fonts[fontName];
       
  6374                     break;
       
  6375                 }
       
  6376             }
       
  6377         }
       
  6378         var thefont;
       
  6379         if (font) {
       
  6380             for (var i = 0, ii = font.length; i < ii; i++) {
       
  6381                 thefont = font[i];
       
  6382                 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
       
  6383                     break;
       
  6384                 }
       
  6385             }
       
  6386         }
       
  6387         return thefont;
       
  6388     };
       
  6389     
       
  6390     paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
       
  6391         origin = origin || "middle"; // baseline|middle
       
  6392         letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
       
  6393         var out = this.set(),
       
  6394             letters = Str(string)[split](E),
       
  6395             shift = 0,
       
  6396             path = E,
       
  6397             scale;
       
  6398         R.is(font, string) && (font = this.getFont(font));
       
  6399         if (font) {
       
  6400             scale = (size || 16) / font.face["units-per-em"];
       
  6401             var bb = font.face.bbox[split](separator),
       
  6402                 top = +bb[0],
       
  6403                 height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
       
  6404             for (var i = 0, ii = letters.length; i < ii; i++) {
       
  6405                 var prev = i && font.glyphs[letters[i - 1]] || {},
       
  6406                     curr = font.glyphs[letters[i]];
       
  6407                 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
       
  6408                 curr && curr.d && out.push(this.path(curr.d).attr({
       
  6409                     fill: "#000",
       
  6410                     stroke: "none",
       
  6411                     transform: [["t", shift * scale, 0]]
       
  6412                 }));
       
  6413             }
       
  6414             out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
       
  6415         }
       
  6416         return out;
       
  6417     };
       
  6418 
       
  6419     
       
  6420     R.format = function (token, params) {
       
  6421         var args = R.is(params, array) ? [0][concat](params) : arguments;
       
  6422         token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
       
  6423             return args[++i] == null ? E : args[i];
       
  6424         }));
       
  6425         return token || E;
       
  6426     };
       
  6427     
       
  6428     R.fullfill = (function () {
       
  6429         var tokenRegex = /\{([^\}]+)\}/g,
       
  6430             objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
       
  6431             replacer = function (all, key, obj) {
       
  6432                 var res = obj;
       
  6433                 key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
       
  6434                     name = name || quotedName;
       
  6435                     if (res) {
       
  6436                         if (name in res) {
       
  6437                             res = res[name];
       
  6438                         }
       
  6439                         typeof res == "function" && isFunc && (res = res());
       
  6440                     }
       
  6441                 });
       
  6442                 res = (res == null || res == obj ? all : res) + "";
       
  6443                 return res;
       
  6444             };
       
  6445         return function (str, obj) {
       
  6446             return String(str).replace(tokenRegex, function (all, key) {
       
  6447                 return replacer(all, key, obj);
       
  6448             });
       
  6449         };
       
  6450     })();
       
  6451     
       
  6452     R.ninja = function () {
       
  6453         oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
       
  6454         return R;
       
  6455     };
       
  6456     
       
  6457     R.st = setproto;
       
  6458     // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
       
  6459     (function (doc, loaded, f) {
       
  6460         if (doc.readyState == null && doc.addEventListener){
       
  6461             doc.addEventListener(loaded, f = function () {
       
  6462                 doc.removeEventListener(loaded, f, false);
       
  6463                 doc.readyState = "complete";
       
  6464             }, false);
       
  6465             doc.readyState = "loading";
       
  6466         }
       
  6467         function isLoaded() {
       
  6468             (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload");
       
  6469         }
       
  6470         isLoaded();
       
  6471     })(document, "DOMContentLoaded");
       
  6472 
       
  6473     oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
       
  6474     
       
  6475     eve.on("DOMload", function () {
       
  6476         loaded = true;
       
  6477     });
       
  6478 })();
       
  6479 
       
  6480 // ┌─────────────────────────────────────────────────────────────────────┐ \\
       
  6481 // │ Raphaël 2 - JavaScript Vector Library                               │ \\
       
  6482 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  6483 // │ SVG Module                                                          │ \\
       
  6484 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  6485 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
       
  6486 // │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
       
  6487 // │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
       
  6488 // └─────────────────────────────────────────────────────────────────────┘ \\
       
  6489 window.Raphael.svg && function (R) {
       
  6490     var has = "hasOwnProperty",
       
  6491         Str = String,
       
  6492         toFloat = parseFloat,
       
  6493         toInt = parseInt,
       
  6494         math = Math,
       
  6495         mmax = math.max,
       
  6496         abs = math.abs,
       
  6497         pow = math.pow,
       
  6498         separator = /[, ]+/,
       
  6499         eve = R.eve,
       
  6500         E = "",
       
  6501         S = " ";
       
  6502     var xlink = "http://www.w3.org/1999/xlink",
       
  6503         markers = {
       
  6504             block: "M5,0 0,2.5 5,5z",
       
  6505             classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
       
  6506             diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
       
  6507             open: "M6,1 1,3.5 6,6",
       
  6508             oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
       
  6509         },
       
  6510         markerCounter = {};
       
  6511     R.toString = function () {
       
  6512         return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
       
  6513     };
       
  6514     var $ = function (el, attr) {
       
  6515         if (attr) {
       
  6516             if (typeof el == "string") {
       
  6517                 el = $(el);
       
  6518             }
       
  6519             for (var key in attr) if (attr[has](key)) {
       
  6520                 if (key.substring(0, 6) == "xlink:") {
       
  6521                     el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
       
  6522                 } else {
       
  6523                     el.setAttribute(key, Str(attr[key]));
       
  6524                 }
       
  6525             }
       
  6526         } else {
       
  6527             el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
       
  6528             el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
       
  6529         }
       
  6530         return el;
       
  6531     },
       
  6532     gradients = {},
       
  6533     rgGrad = /^url\(#(.*)\)$/,
       
  6534     removeGradientFill = function (node, paper) {
       
  6535         var oid = node.getAttribute("fill");
       
  6536         oid = oid && oid.match(rgGrad);
       
  6537         if (oid && !--gradients[oid[1]]) {
       
  6538             delete gradients[oid[1]];
       
  6539             paper.defs.removeChild(R._g.doc.getElementById(oid[1]));
       
  6540         }
       
  6541     },
       
  6542     addGradientFill = function (element, gradient) {
       
  6543         var type = "linear",
       
  6544             id = element.id + gradient,
       
  6545             fx = .5, fy = .5,
       
  6546             o = element.node,
       
  6547             SVG = element.paper,
       
  6548             s = o.style,
       
  6549             el = R._g.doc.getElementById(id);
       
  6550         if (!el) {
       
  6551             gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
       
  6552                 type = "radial";
       
  6553                 if (_fx && _fy) {
       
  6554                     fx = toFloat(_fx);
       
  6555                     fy = toFloat(_fy);
       
  6556                     var dir = ((fy > .5) * 2 - 1);
       
  6557                     pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
       
  6558                         (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
       
  6559                         fy != .5 &&
       
  6560                         (fy = fy.toFixed(5) - 1e-5 * dir);
       
  6561                 }
       
  6562                 return E;
       
  6563             });
       
  6564             gradient = gradient.split(/\s*\-\s*/);
       
  6565             if (type == "linear") {
       
  6566                 var angle = gradient.shift();
       
  6567                 angle = -toFloat(angle);
       
  6568                 if (isNaN(angle)) {
       
  6569                     return null;
       
  6570                 }
       
  6571                 var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
       
  6572                     max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
       
  6573                 vector[2] *= max;
       
  6574                 vector[3] *= max;
       
  6575                 if (vector[2] < 0) {
       
  6576                     vector[0] = -vector[2];
       
  6577                     vector[2] = 0;
       
  6578                 }
       
  6579                 if (vector[3] < 0) {
       
  6580                     vector[1] = -vector[3];
       
  6581                     vector[3] = 0;
       
  6582                 }
       
  6583             }
       
  6584             var dots = R._parseDots(gradient);
       
  6585             if (!dots) {
       
  6586                 return null;
       
  6587             }
       
  6588             if (element.gradient) {
       
  6589                 SVG.defs.removeChild(element.gradient);
       
  6590                 delete element.gradient;
       
  6591             }
       
  6592 
       
  6593             id = id.replace(/[\(\)\s,\xb0#]/g, "-");
       
  6594             el = $(type + "Gradient", {id: id});
       
  6595             element.gradient = el;
       
  6596             $(el, type == "radial" ? {
       
  6597                 fx: fx,
       
  6598                 fy: fy
       
  6599             } : {
       
  6600                 x1: vector[0],
       
  6601                 y1: vector[1],
       
  6602                 x2: vector[2],
       
  6603                 y2: vector[3],
       
  6604                 gradientTransform: element.matrix.invert()
       
  6605             });
       
  6606             SVG.defs.appendChild(el);
       
  6607             for (var i = 0, ii = dots.length; i < ii; i++) {
       
  6608                 el.appendChild($("stop", {
       
  6609                     offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
       
  6610                     "stop-color": dots[i].color || "#fff"
       
  6611                 }));
       
  6612             }
       
  6613         }
       
  6614         $(o, {
       
  6615             fill: "url(#" + id + ")",
       
  6616             opacity: 1,
       
  6617             "fill-opacity": 1
       
  6618         });
       
  6619         s.fill = E;
       
  6620         s.opacity = 1;
       
  6621         s.fillOpacity = 1;
       
  6622         return 1;
       
  6623     },
       
  6624     updatePosition = function (o) {
       
  6625         var bbox = o.getBBox(1);
       
  6626         $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
       
  6627     },
       
  6628     addArrow = function (o, value, isEnd) {
       
  6629         if (o.type == "path") {
       
  6630             var values = Str(value).toLowerCase().split("-"),
       
  6631                 p = o.paper,
       
  6632                 se = isEnd ? "end" : "start",
       
  6633                 node = o.node,
       
  6634                 attrs = o.attrs,
       
  6635                 stroke = attrs["stroke-width"],
       
  6636                 i = values.length,
       
  6637                 type = "classic",
       
  6638                 from,
       
  6639                 to,
       
  6640                 dx,
       
  6641                 refX,
       
  6642                 attr,
       
  6643                 w = 3,
       
  6644                 h = 3,
       
  6645                 t = 5;
       
  6646             while (i--) {
       
  6647                 switch (values[i]) {
       
  6648                     case "block":
       
  6649                     case "classic":
       
  6650                     case "oval":
       
  6651                     case "diamond":
       
  6652                     case "open":
       
  6653                     case "none":
       
  6654                         type = values[i];
       
  6655                         break;
       
  6656                     case "wide": h = 5; break;
       
  6657                     case "narrow": h = 2; break;
       
  6658                     case "long": w = 5; break;
       
  6659                     case "short": w = 2; break;
       
  6660                 }
       
  6661             }
       
  6662             if (type == "open") {
       
  6663                 w += 2;
       
  6664                 h += 2;
       
  6665                 t += 2;
       
  6666                 dx = 1;
       
  6667                 refX = isEnd ? 4 : 1;
       
  6668                 attr = {
       
  6669                     fill: "none",
       
  6670                     stroke: attrs.stroke
       
  6671                 };
       
  6672             } else {
       
  6673                 refX = dx = w / 2;
       
  6674                 attr = {
       
  6675                     fill: attrs.stroke,
       
  6676                     stroke: "none"
       
  6677                 };
       
  6678             }
       
  6679             if (o._.arrows) {
       
  6680                 if (isEnd) {
       
  6681                     o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
       
  6682                     o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
       
  6683                 } else {
       
  6684                     o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
       
  6685                     o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
       
  6686                 }
       
  6687             } else {
       
  6688                 o._.arrows = {};
       
  6689             }
       
  6690             if (type != "none") {
       
  6691                 var pathId = "raphael-marker-" + type,
       
  6692                     markerId = "raphael-marker-" + se + type + w + h;
       
  6693                 if (!R._g.doc.getElementById(pathId)) {
       
  6694                     p.defs.appendChild($($("path"), {
       
  6695                         "stroke-linecap": "round",
       
  6696                         d: markers[type],
       
  6697                         id: pathId
       
  6698                     }));
       
  6699                     markerCounter[pathId] = 1;
       
  6700                 } else {
       
  6701                     markerCounter[pathId]++;
       
  6702                 }
       
  6703                 var marker = R._g.doc.getElementById(markerId),
       
  6704                     use;
       
  6705                 if (!marker) {
       
  6706                     marker = $($("marker"), {
       
  6707                         id: markerId,
       
  6708                         markerHeight: h,
       
  6709                         markerWidth: w,
       
  6710                         orient: "auto",
       
  6711                         refX: refX,
       
  6712                         refY: h / 2
       
  6713                     });
       
  6714                     use = $($("use"), {
       
  6715                         "xlink:href": "#" + pathId,
       
  6716                         transform: (isEnd ? " rotate(180 " + w / 2 + " " + h / 2 + ") " : S) + "scale(" + w / t + "," + h / t + ")",
       
  6717                         "stroke-width": 1 / ((w / t + h / t) / 2)
       
  6718                     });
       
  6719                     marker.appendChild(use);
       
  6720                     p.defs.appendChild(marker);
       
  6721                     markerCounter[markerId] = 1;
       
  6722                 } else {
       
  6723                     markerCounter[markerId]++;
       
  6724                     use = marker.getElementsByTagName("use")[0];
       
  6725                 }
       
  6726                 $(use, attr);
       
  6727                 var delta = dx * (type != "diamond" && type != "oval");
       
  6728                 if (isEnd) {
       
  6729                     from = o._.arrows.startdx * stroke || 0;
       
  6730                     to = R.getTotalLength(attrs.path) - delta * stroke;
       
  6731                 } else {
       
  6732                     from = delta * stroke;
       
  6733                     to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
       
  6734                 }
       
  6735                 attr = {};
       
  6736                 attr["marker-" + se] = "url(#" + markerId + ")";
       
  6737                 if (to || from) {
       
  6738                     attr.d = Raphael.getSubpath(attrs.path, from, to);
       
  6739                 }
       
  6740                 $(node, attr);
       
  6741                 o._.arrows[se + "Path"] = pathId;
       
  6742                 o._.arrows[se + "Marker"] = markerId;
       
  6743                 o._.arrows[se + "dx"] = delta;
       
  6744                 o._.arrows[se + "Type"] = type;
       
  6745                 o._.arrows[se + "String"] = value;
       
  6746             } else {
       
  6747                 if (isEnd) {
       
  6748                     from = o._.arrows.startdx * stroke || 0;
       
  6749                     to = R.getTotalLength(attrs.path) - from;
       
  6750                 } else {
       
  6751                     from = 0;
       
  6752                     to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
       
  6753                 }
       
  6754                 o._.arrows[se + "Path"] && $(node, {d: Raphael.getSubpath(attrs.path, from, to)});
       
  6755                 delete o._.arrows[se + "Path"];
       
  6756                 delete o._.arrows[se + "Marker"];
       
  6757                 delete o._.arrows[se + "dx"];
       
  6758                 delete o._.arrows[se + "Type"];
       
  6759                 delete o._.arrows[se + "String"];
       
  6760             }
       
  6761             for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
       
  6762                 var item = R._g.doc.getElementById(attr);
       
  6763                 item && item.parentNode.removeChild(item);
       
  6764             }
       
  6765         }
       
  6766     },
       
  6767     dasharray = {
       
  6768         "": [0],
       
  6769         "none": [0],
       
  6770         "-": [3, 1],
       
  6771         ".": [1, 1],
       
  6772         "-.": [3, 1, 1, 1],
       
  6773         "-..": [3, 1, 1, 1, 1, 1],
       
  6774         ". ": [1, 3],
       
  6775         "- ": [4, 3],
       
  6776         "--": [8, 3],
       
  6777         "- .": [4, 3, 1, 3],
       
  6778         "--.": [8, 3, 1, 3],
       
  6779         "--..": [8, 3, 1, 3, 1, 3]
       
  6780     },
       
  6781     addDashes = function (o, value, params) {
       
  6782         value = dasharray[Str(value).toLowerCase()];
       
  6783         if (value) {
       
  6784             var width = o.attrs["stroke-width"] || "1",
       
  6785                 butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
       
  6786                 dashes = [],
       
  6787                 i = value.length;
       
  6788             while (i--) {
       
  6789                 dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
       
  6790             }
       
  6791             $(o.node, {"stroke-dasharray": dashes.join(",")});
       
  6792         }
       
  6793     },
       
  6794     setFillAndStroke = function (o, params) {
       
  6795         var node = o.node,
       
  6796             attrs = o.attrs,
       
  6797             vis = node.style.visibility;
       
  6798         node.style.visibility = "hidden";
       
  6799         for (var att in params) {
       
  6800             if (params[has](att)) {
       
  6801                 if (!R._availableAttrs[has](att)) {
       
  6802                     continue;
       
  6803                 }
       
  6804                 var value = params[att];
       
  6805                 attrs[att] = value;
       
  6806                 switch (att) {
       
  6807                     case "blur":
       
  6808                         o.blur(value);
       
  6809                         break;
       
  6810                     case "href":
       
  6811                     case "title":
       
  6812                     case "target":
       
  6813                         var pn = node.parentNode;
       
  6814                         if (pn.tagName.toLowerCase() != "a") {
       
  6815                             var hl = $("a");
       
  6816                             pn.insertBefore(hl, node);
       
  6817                             hl.appendChild(node);
       
  6818                             pn = hl;
       
  6819                         }
       
  6820                         if (att == "target" && value == "blank") {
       
  6821                             pn.setAttributeNS(xlink, "show", "new");
       
  6822                         } else {
       
  6823                             pn.setAttributeNS(xlink, att, value);
       
  6824                         }
       
  6825                         break;
       
  6826                     case "cursor":
       
  6827                         node.style.cursor = value;
       
  6828                         break;
       
  6829                     case "transform":
       
  6830                         o.transform(value);
       
  6831                         break;
       
  6832                     case "arrow-start":
       
  6833                         addArrow(o, value);
       
  6834                         break;
       
  6835                     case "arrow-end":
       
  6836                         addArrow(o, value, 1);
       
  6837                         break;
       
  6838                     case "clip-rect":
       
  6839                         var rect = Str(value).split(separator);
       
  6840                         if (rect.length == 4) {
       
  6841                             o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
       
  6842                             var el = $("clipPath"),
       
  6843                                 rc = $("rect");
       
  6844                             el.id = R.createUUID();
       
  6845                             $(rc, {
       
  6846                                 x: rect[0],
       
  6847                                 y: rect[1],
       
  6848                                 width: rect[2],
       
  6849                                 height: rect[3]
       
  6850                             });
       
  6851                             el.appendChild(rc);
       
  6852                             o.paper.defs.appendChild(el);
       
  6853                             $(node, {"clip-path": "url(#" + el.id + ")"});
       
  6854                             o.clip = rc;
       
  6855                         }
       
  6856                         if (!value) {
       
  6857                             var clip = R._g.doc.getElementById(node.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, E));
       
  6858                             clip && clip.parentNode.removeChild(clip);
       
  6859                             $(node, {"clip-path": E});
       
  6860                             delete o.clip;
       
  6861                         }
       
  6862                     break;
       
  6863                     case "path":
       
  6864                         if (o.type == "path") {
       
  6865                             $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
       
  6866                             o._.dirty = 1;
       
  6867                             if (o._.arrows) {
       
  6868                                 "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
       
  6869                                 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
       
  6870                             }
       
  6871                         }
       
  6872                         break;
       
  6873                     case "width":
       
  6874                         node.setAttribute(att, value);
       
  6875                         o._.dirty = 1;
       
  6876                         if (attrs.fx) {
       
  6877                             att = "x";
       
  6878                             value = attrs.x;
       
  6879                         } else {
       
  6880                             break;
       
  6881                         }
       
  6882                     case "x":
       
  6883                         if (attrs.fx) {
       
  6884                             value = -attrs.x - (attrs.width || 0);
       
  6885                         }
       
  6886                     case "rx":
       
  6887                         if (att == "rx" && o.type == "rect") {
       
  6888                             break;
       
  6889                         }
       
  6890                     case "cx":
       
  6891                         node.setAttribute(att, value);
       
  6892                         o.pattern && updatePosition(o);
       
  6893                         o._.dirty = 1;
       
  6894                         break;
       
  6895                     case "height":
       
  6896                         node.setAttribute(att, value);
       
  6897                         o._.dirty = 1;
       
  6898                         if (attrs.fy) {
       
  6899                             att = "y";
       
  6900                             value = attrs.y;
       
  6901                         } else {
       
  6902                             break;
       
  6903                         }
       
  6904                     case "y":
       
  6905                         if (attrs.fy) {
       
  6906                             value = -attrs.y - (attrs.height || 0);
       
  6907                         }
       
  6908                     case "ry":
       
  6909                         if (att == "ry" && o.type == "rect") {
       
  6910                             break;
       
  6911                         }
       
  6912                     case "cy":
       
  6913                         node.setAttribute(att, value);
       
  6914                         o.pattern && updatePosition(o);
       
  6915                         o._.dirty = 1;
       
  6916                         break;
       
  6917                     case "r":
       
  6918                         if (o.type == "rect") {
       
  6919                             $(node, {rx: value, ry: value});
       
  6920                         } else {
       
  6921                             node.setAttribute(att, value);
       
  6922                         }
       
  6923                         o._.dirty = 1;
       
  6924                         break;
       
  6925                     case "src":
       
  6926                         if (o.type == "image") {
       
  6927                             node.setAttributeNS(xlink, "href", value);
       
  6928                         }
       
  6929                         break;
       
  6930                     case "stroke-width":
       
  6931                         if (o._.sx != 1 || o._.sy != 1) {
       
  6932                             value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
       
  6933                         }
       
  6934                         if (o.paper._vbSize) {
       
  6935                             value *= o.paper._vbSize;
       
  6936                         }
       
  6937                         node.setAttribute(att, value);
       
  6938                         if (attrs["stroke-dasharray"]) {
       
  6939                             addDashes(o, attrs["stroke-dasharray"], params);
       
  6940                         }
       
  6941                         if (o._.arrows) {
       
  6942                             "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
       
  6943                             "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
       
  6944                         }
       
  6945                         break;
       
  6946                     case "stroke-dasharray":
       
  6947                         addDashes(o, value, params);
       
  6948                         break;
       
  6949                     case "fill":
       
  6950                         var isURL = Str(value).match(R._ISURL);
       
  6951                         if (isURL) {
       
  6952                             el = $("pattern");
       
  6953                             var ig = $("image");
       
  6954                             el.id = R.createUUID();
       
  6955                             $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
       
  6956                             $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
       
  6957                             el.appendChild(ig);
       
  6958 
       
  6959                             (function (el) {
       
  6960                                 R._preload(isURL[1], function () {
       
  6961                                     var w = this.offsetWidth,
       
  6962                                         h = this.offsetHeight;
       
  6963                                     $(el, {width: w, height: h});
       
  6964                                     $(ig, {width: w, height: h});
       
  6965                                     o.paper.safari();
       
  6966                                 });
       
  6967                             })(el);
       
  6968                             o.paper.defs.appendChild(el);
       
  6969                             node.style.fill = "url(#" + el.id + ")";
       
  6970                             $(node, {fill: "url(#" + el.id + ")"});
       
  6971                             o.pattern = el;
       
  6972                             o.pattern && updatePosition(o);
       
  6973                             break;
       
  6974                         }
       
  6975                         var clr = R.getRGB(value);
       
  6976                         if (!clr.error) {
       
  6977                             delete params.gradient;
       
  6978                             delete attrs.gradient;
       
  6979                             !R.is(attrs.opacity, "undefined") &&
       
  6980                                 R.is(params.opacity, "undefined") &&
       
  6981                                 $(node, {opacity: attrs.opacity});
       
  6982                             !R.is(attrs["fill-opacity"], "undefined") &&
       
  6983                                 R.is(params["fill-opacity"], "undefined") &&
       
  6984                                 $(node, {"fill-opacity": attrs["fill-opacity"]});
       
  6985                         } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
       
  6986                             if ("opacity" in attrs || "fill-opacity" in attrs) {
       
  6987                                 var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
       
  6988                                 if (gradient) {
       
  6989                                     var stops = gradient.getElementsByTagName("stop");
       
  6990                                     $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
       
  6991                                 }
       
  6992                             }
       
  6993                             attrs.gradient = value;
       
  6994                             attrs.fill = "none";
       
  6995                             break;
       
  6996                         }
       
  6997                         clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
       
  6998                     case "stroke":
       
  6999                         clr = R.getRGB(value);
       
  7000                         node.setAttribute(att, clr.hex);
       
  7001                         att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
       
  7002                         if (att == "stroke" && o._.arrows) {
       
  7003                             "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
       
  7004                             "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
       
  7005                         }
       
  7006                         break;
       
  7007                     case "gradient":
       
  7008                         (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
       
  7009                         break;
       
  7010                     case "opacity":
       
  7011                         if (attrs.gradient && !attrs[has]("stroke-opacity")) {
       
  7012                             $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
       
  7013                         }
       
  7014                         // fall
       
  7015                     case "fill-opacity":
       
  7016                         if (attrs.gradient) {
       
  7017                             gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
       
  7018                             if (gradient) {
       
  7019                                 stops = gradient.getElementsByTagName("stop");
       
  7020                                 $(stops[stops.length - 1], {"stop-opacity": value});
       
  7021                             }
       
  7022                             break;
       
  7023                         }
       
  7024                     default:
       
  7025                         att == "font-size" && (value = toInt(value, 10) + "px");
       
  7026                         var cssrule = att.replace(/(\-.)/g, function (w) {
       
  7027                             return w.substring(1).toUpperCase();
       
  7028                         });
       
  7029                         node.style[cssrule] = value;
       
  7030                         o._.dirty = 1;
       
  7031                         node.setAttribute(att, value);
       
  7032                         break;
       
  7033                 }
       
  7034             }
       
  7035         }
       
  7036 
       
  7037         tuneText(o, params);
       
  7038         node.style.visibility = vis;
       
  7039     },
       
  7040     leading = 1.2,
       
  7041     tuneText = function (el, params) {
       
  7042         if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
       
  7043             return;
       
  7044         }
       
  7045         var a = el.attrs,
       
  7046             node = el.node,
       
  7047             fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
       
  7048 
       
  7049         if (params[has]("text")) {
       
  7050             a.text = params.text;
       
  7051             while (node.firstChild) {
       
  7052                 node.removeChild(node.firstChild);
       
  7053             }
       
  7054             var texts = Str(params.text).split("\n"),
       
  7055                 tspans = [],
       
  7056                 tspan;
       
  7057             for (var i = 0, ii = texts.length; i < ii; i++) {
       
  7058                 tspan = $("tspan");
       
  7059                 i && $(tspan, {dy: fontSize * leading, x: a.x});
       
  7060                 tspan.appendChild(R._g.doc.createTextNode(texts[i]));
       
  7061                 node.appendChild(tspan);
       
  7062                 tspans[i] = tspan;
       
  7063             }
       
  7064         } else {
       
  7065             tspans = node.getElementsByTagName("tspan");
       
  7066             for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
       
  7067                 $(tspans[i], {dy: fontSize * leading, x: a.x});
       
  7068             } else {
       
  7069                 $(tspans[0], {dy: 0});
       
  7070             }
       
  7071         }
       
  7072         $(node, {x: a.x, y: a.y});
       
  7073         el._.dirty = 1;
       
  7074         var bb = el._getBBox(),
       
  7075             dif = a.y - (bb.y + bb.height / 2);
       
  7076         dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
       
  7077     },
       
  7078     Element = function (node, svg) {
       
  7079         var X = 0,
       
  7080             Y = 0;
       
  7081         
       
  7082         this[0] = this.node = node;
       
  7083         
       
  7084         node.raphael = true;
       
  7085         
       
  7086         this.id = R._oid++;
       
  7087         node.raphaelid = this.id;
       
  7088         this.matrix = R.matrix();
       
  7089         this.realPath = null;
       
  7090         
       
  7091         this.paper = svg;
       
  7092         this.attrs = this.attrs || {};
       
  7093         this._ = {
       
  7094             transform: [],
       
  7095             sx: 1,
       
  7096             sy: 1,
       
  7097             deg: 0,
       
  7098             dx: 0,
       
  7099             dy: 0,
       
  7100             dirty: 1
       
  7101         };
       
  7102         !svg.bottom && (svg.bottom = this);
       
  7103         
       
  7104         this.prev = svg.top;
       
  7105         svg.top && (svg.top.next = this);
       
  7106         svg.top = this;
       
  7107         
       
  7108         this.next = null;
       
  7109     },
       
  7110     elproto = R.el;
       
  7111 
       
  7112     Element.prototype = elproto;
       
  7113     elproto.constructor = Element;
       
  7114 
       
  7115     R._engine.path = function (pathString, SVG) {
       
  7116         var el = $("path");
       
  7117         SVG.canvas && SVG.canvas.appendChild(el);
       
  7118         var p = new Element(el, SVG);
       
  7119         p.type = "path";
       
  7120         setFillAndStroke(p, {
       
  7121             fill: "none",
       
  7122             stroke: "#000",
       
  7123             path: pathString
       
  7124         });
       
  7125         return p;
       
  7126     };
       
  7127     
       
  7128     elproto.rotate = function (deg, cx, cy) {
       
  7129         if (this.removed) {
       
  7130             return this;
       
  7131         }
       
  7132         deg = Str(deg).split(separator);
       
  7133         if (deg.length - 1) {
       
  7134             cx = toFloat(deg[1]);
       
  7135             cy = toFloat(deg[2]);
       
  7136         }
       
  7137         deg = toFloat(deg[0]);
       
  7138         (cy == null) && (cx = cy);
       
  7139         if (cx == null || cy == null) {
       
  7140             var bbox = this.getBBox(1);
       
  7141             cx = bbox.x + bbox.width / 2;
       
  7142             cy = bbox.y + bbox.height / 2;
       
  7143         }
       
  7144         this.transform(this._.transform.concat([["r", deg, cx, cy]]));
       
  7145         return this;
       
  7146     };
       
  7147     
       
  7148     elproto.scale = function (sx, sy, cx, cy) {
       
  7149         if (this.removed) {
       
  7150             return this;
       
  7151         }
       
  7152         sx = Str(sx).split(separator);
       
  7153         if (sx.length - 1) {
       
  7154             sy = toFloat(sx[1]);
       
  7155             cx = toFloat(sx[2]);
       
  7156             cy = toFloat(sx[3]);
       
  7157         }
       
  7158         sx = toFloat(sx[0]);
       
  7159         (sy == null) && (sy = sx);
       
  7160         (cy == null) && (cx = cy);
       
  7161         if (cx == null || cy == null) {
       
  7162             var bbox = this.getBBox(1);
       
  7163         }
       
  7164         cx = cx == null ? bbox.x + bbox.width / 2 : cx;
       
  7165         cy = cy == null ? bbox.y + bbox.height / 2 : cy;
       
  7166         this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
       
  7167         return this;
       
  7168     };
       
  7169     
       
  7170     elproto.translate = function (dx, dy) {
       
  7171         if (this.removed) {
       
  7172             return this;
       
  7173         }
       
  7174         dx = Str(dx).split(separator);
       
  7175         if (dx.length - 1) {
       
  7176             dy = toFloat(dx[1]);
       
  7177         }
       
  7178         dx = toFloat(dx[0]) || 0;
       
  7179         dy = +dy || 0;
       
  7180         this.transform(this._.transform.concat([["t", dx, dy]]));
       
  7181         return this;
       
  7182     };
       
  7183     
       
  7184     elproto.transform = function (tstr) {
       
  7185         var _ = this._;
       
  7186         if (tstr == null) {
       
  7187             return _.transform;
       
  7188         }
       
  7189         R._extractTransform(this, tstr);
       
  7190 
       
  7191         this.clip && $(this.clip, {transform: this.matrix.invert()});
       
  7192         this.pattern && updatePosition(this);
       
  7193         this.node && $(this.node, {transform: this.matrix});
       
  7194     
       
  7195         if (_.sx != 1 || _.sy != 1) {
       
  7196             var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
       
  7197             this.attr({"stroke-width": sw});
       
  7198         }
       
  7199 
       
  7200         return this;
       
  7201     };
       
  7202     
       
  7203     elproto.hide = function () {
       
  7204         !this.removed && this.paper.safari(this.node.style.display = "none");
       
  7205         return this;
       
  7206     };
       
  7207     
       
  7208     elproto.show = function () {
       
  7209         !this.removed && this.paper.safari(this.node.style.display = "");
       
  7210         return this;
       
  7211     };
       
  7212     
       
  7213     elproto.remove = function () {
       
  7214         if (this.removed) {
       
  7215             return;
       
  7216         }
       
  7217         this.paper.__set__ && this.paper.__set__.exclude(this);
       
  7218         eve.unbind("*.*." + this.id);
       
  7219         R._tear(this, this.paper);
       
  7220         this.node.parentNode.removeChild(this.node);
       
  7221         for (var i in this) {
       
  7222             delete this[i];
       
  7223         }
       
  7224         this.removed = true;
       
  7225     };
       
  7226     elproto._getBBox = function () {
       
  7227         if (this.node.style.display == "none") {
       
  7228             this.show();
       
  7229             var hide = true;
       
  7230         }
       
  7231         var bbox = {};
       
  7232         try {
       
  7233             bbox = this.node.getBBox();
       
  7234         } catch(e) {
       
  7235             // Firefox 3.0.x plays badly here
       
  7236         } finally {
       
  7237             bbox = bbox || {};
       
  7238         }
       
  7239         hide && this.hide();
       
  7240         return bbox;
       
  7241     };
       
  7242     
       
  7243     elproto.attr = function (name, value) {
       
  7244         if (this.removed) {
       
  7245             return this;
       
  7246         }
       
  7247         if (name == null) {
       
  7248             var res = {};
       
  7249             for (var a in this.attrs) if (this.attrs[has](a)) {
       
  7250                 res[a] = this.attrs[a];
       
  7251             }
       
  7252             res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
       
  7253             res.transform = this._.transform;
       
  7254             return res;
       
  7255         }
       
  7256         if (value == null && R.is(name, "string")) {
       
  7257             if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
       
  7258                 return this.attrs.gradient;
       
  7259             }
       
  7260             if (name == "transform") {
       
  7261                 return this._.transform;
       
  7262             }
       
  7263             var names = name.split(separator),
       
  7264                 out = {};
       
  7265             for (var i = 0, ii = names.length; i < ii; i++) {
       
  7266                 name = names[i];
       
  7267                 if (name in this.attrs) {
       
  7268                     out[name] = this.attrs[name];
       
  7269                 } else if (R.is(this.paper.customAttributes[name], "function")) {
       
  7270                     out[name] = this.paper.customAttributes[name].def;
       
  7271                 } else {
       
  7272                     out[name] = R._availableAttrs[name];
       
  7273                 }
       
  7274             }
       
  7275             return ii - 1 ? out : out[names[0]];
       
  7276         }
       
  7277         if (value == null && R.is(name, "array")) {
       
  7278             out = {};
       
  7279             for (i = 0, ii = name.length; i < ii; i++) {
       
  7280                 out[name[i]] = this.attr(name[i]);
       
  7281             }
       
  7282             return out;
       
  7283         }
       
  7284         if (value != null) {
       
  7285             var params = {};
       
  7286             params[name] = value;
       
  7287         } else if (name != null && R.is(name, "object")) {
       
  7288             params = name;
       
  7289         }
       
  7290         for (var key in params) {
       
  7291             eve("attr." + key + "." + this.id, this, params[key]);
       
  7292         }
       
  7293         for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
       
  7294             var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
       
  7295             this.attrs[key] = params[key];
       
  7296             for (var subkey in par) if (par[has](subkey)) {
       
  7297                 params[subkey] = par[subkey];
       
  7298             }
       
  7299         }
       
  7300         setFillAndStroke(this, params);
       
  7301         return this;
       
  7302     };
       
  7303     
       
  7304     elproto.toFront = function () {
       
  7305         if (this.removed) {
       
  7306             return this;
       
  7307         }
       
  7308         this.node.parentNode.appendChild(this.node);
       
  7309         var svg = this.paper;
       
  7310         svg.top != this && R._tofront(this, svg);
       
  7311         return this;
       
  7312     };
       
  7313     
       
  7314     elproto.toBack = function () {
       
  7315         if (this.removed) {
       
  7316             return this;
       
  7317         }
       
  7318         if (this.node.parentNode.firstChild != this.node) {
       
  7319             this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
       
  7320             R._toback(this, this.paper);
       
  7321             var svg = this.paper;
       
  7322         }
       
  7323         return this;
       
  7324     };
       
  7325     
       
  7326     elproto.insertAfter = function (element) {
       
  7327         if (this.removed) {
       
  7328             return this;
       
  7329         }
       
  7330         var node = element.node || element[element.length - 1].node;
       
  7331         if (node.nextSibling) {
       
  7332             node.parentNode.insertBefore(this.node, node.nextSibling);
       
  7333         } else {
       
  7334             node.parentNode.appendChild(this.node);
       
  7335         }
       
  7336         R._insertafter(this, element, this.paper);
       
  7337         return this;
       
  7338     };
       
  7339     
       
  7340     elproto.insertBefore = function (element) {
       
  7341         if (this.removed) {
       
  7342             return this;
       
  7343         }
       
  7344         var node = element.node || element[0].node;
       
  7345         node.parentNode.insertBefore(this.node, node);
       
  7346         R._insertbefore(this, element, this.paper);
       
  7347         return this;
       
  7348     };
       
  7349     elproto.blur = function (size) {
       
  7350         // Experimental. No Safari support. Use it on your own risk.
       
  7351         var t = this;
       
  7352         if (+size !== 0) {
       
  7353             var fltr = $("filter"),
       
  7354                 blur = $("feGaussianBlur");
       
  7355             t.attrs.blur = size;
       
  7356             fltr.id = R.createUUID();
       
  7357             $(blur, {stdDeviation: +size || 1.5});
       
  7358             fltr.appendChild(blur);
       
  7359             t.paper.defs.appendChild(fltr);
       
  7360             t._blur = fltr;
       
  7361             $(t.node, {filter: "url(#" + fltr.id + ")"});
       
  7362         } else {
       
  7363             if (t._blur) {
       
  7364                 t._blur.parentNode.removeChild(t._blur);
       
  7365                 delete t._blur;
       
  7366                 delete t.attrs.blur;
       
  7367             }
       
  7368             t.node.removeAttribute("filter");
       
  7369         }
       
  7370     };
       
  7371     R._engine.circle = function (svg, x, y, r) {
       
  7372         var el = $("circle");
       
  7373         svg.canvas && svg.canvas.appendChild(el);
       
  7374         var res = new Element(el, svg);
       
  7375         res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
       
  7376         res.type = "circle";
       
  7377         $(el, res.attrs);
       
  7378         return res;
       
  7379     };
       
  7380     R._engine.rect = function (svg, x, y, w, h, r) {
       
  7381         var el = $("rect");
       
  7382         svg.canvas && svg.canvas.appendChild(el);
       
  7383         var res = new Element(el, svg);
       
  7384         res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
       
  7385         res.type = "rect";
       
  7386         $(el, res.attrs);
       
  7387         return res;
       
  7388     };
       
  7389     R._engine.ellipse = function (svg, x, y, rx, ry) {
       
  7390         var el = $("ellipse");
       
  7391         svg.canvas && svg.canvas.appendChild(el);
       
  7392         var res = new Element(el, svg);
       
  7393         res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
       
  7394         res.type = "ellipse";
       
  7395         $(el, res.attrs);
       
  7396         return res;
       
  7397     };
       
  7398     R._engine.image = function (svg, src, x, y, w, h) {
       
  7399         var el = $("image");
       
  7400         $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
       
  7401         el.setAttributeNS(xlink, "href", src);
       
  7402         svg.canvas && svg.canvas.appendChild(el);
       
  7403         var res = new Element(el, svg);
       
  7404         res.attrs = {x: x, y: y, width: w, height: h, src: src};
       
  7405         res.type = "image";
       
  7406         return res;
       
  7407     };
       
  7408     R._engine.text = function (svg, x, y, text) {
       
  7409         var el = $("text");
       
  7410         // $(el, {x: x, y: y, "text-anchor": "middle"});
       
  7411         svg.canvas && svg.canvas.appendChild(el);
       
  7412         var res = new Element(el, svg);
       
  7413         res.attrs = {
       
  7414             x: x,
       
  7415             y: y,
       
  7416             "text-anchor": "middle",
       
  7417             text: text,
       
  7418             font: R._availableAttrs.font,
       
  7419             stroke: "none",
       
  7420             fill: "#000"
       
  7421         };
       
  7422         res.type = "text";
       
  7423         setFillAndStroke(res, res.attrs);
       
  7424         return res;
       
  7425     };
       
  7426     R._engine.setSize = function (width, height) {
       
  7427         this.width = width || this.width;
       
  7428         this.height = height || this.height;
       
  7429         this.canvas.setAttribute("width", this.width);
       
  7430         this.canvas.setAttribute("height", this.height);
       
  7431         if (this._viewBox) {
       
  7432             this.setViewBox.apply(this, this._viewBox);
       
  7433         }
       
  7434         return this;
       
  7435     };
       
  7436     R._engine.create = function () {
       
  7437         var con = R._getContainer.apply(0, arguments),
       
  7438             container = con && con.container,
       
  7439             x = con.x,
       
  7440             y = con.y,
       
  7441             width = con.width,
       
  7442             height = con.height;
       
  7443         if (!container) {
       
  7444             throw new Error("SVG container not found.");
       
  7445         }
       
  7446         var cnvs = $("svg"),
       
  7447             css = "overflow:hidden;",
       
  7448             isFloating;
       
  7449         x = x || 0;
       
  7450         y = y || 0;
       
  7451         width = width || 512;
       
  7452         height = height || 342;
       
  7453         $(cnvs, {
       
  7454             height: height,
       
  7455             version: 1.1,
       
  7456             width: width,
       
  7457             xmlns: "http://www.w3.org/2000/svg"
       
  7458         });
       
  7459         if (container == 1) {
       
  7460             cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
       
  7461             R._g.doc.body.appendChild(cnvs);
       
  7462             isFloating = 1;
       
  7463         } else {
       
  7464             cnvs.style.cssText = css + "position:relative";
       
  7465             if (container.firstChild) {
       
  7466                 container.insertBefore(cnvs, container.firstChild);
       
  7467             } else {
       
  7468                 container.appendChild(cnvs);
       
  7469             }
       
  7470         }
       
  7471         container = new R._Paper;
       
  7472         container.width = width;
       
  7473         container.height = height;
       
  7474         container.canvas = cnvs;
       
  7475         // plugins.call(container, container, R.fn);
       
  7476         container.clear();
       
  7477         container._left = container._top = 0;
       
  7478         isFloating && (container.renderfix = function () {});
       
  7479         container.renderfix();
       
  7480         return container;
       
  7481     };
       
  7482     R._engine.setViewBox = function (x, y, w, h, fit) {
       
  7483         eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
       
  7484         var size = mmax(w / this.width, h / this.height),
       
  7485             top = this.top,
       
  7486             aspectRatio = fit ? "meet" : "xMinYMin",
       
  7487             vb,
       
  7488             sw;
       
  7489         if (x == null) {
       
  7490             if (this._vbSize) {
       
  7491                 size = 1;
       
  7492             }
       
  7493             delete this._vbSize;
       
  7494             vb = "0 0 " + this.width + S + this.height;
       
  7495         } else {
       
  7496             this._vbSize = size;
       
  7497             vb = x + S + y + S + w + S + h;
       
  7498         }
       
  7499         $(this.canvas, {
       
  7500             viewBox: vb,
       
  7501             preserveAspectRatio: aspectRatio
       
  7502         });
       
  7503         while (size && top) {
       
  7504             sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
       
  7505             top.attr({"stroke-width": sw});
       
  7506             top._.dirty = 1;
       
  7507             top._.dirtyT = 1;
       
  7508             top = top.prev;
       
  7509         }
       
  7510         this._viewBox = [x, y, w, h, !!fit];
       
  7511         return this;
       
  7512     };
       
  7513     
       
  7514     R.prototype.renderfix = function () {
       
  7515         var cnvs = this.canvas,
       
  7516             s = cnvs.style,
       
  7517             pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix(),
       
  7518             left = -pos.e % 1,
       
  7519             top = -pos.f % 1;
       
  7520         if (left || top) {
       
  7521             if (left) {
       
  7522                 this._left = (this._left + left) % 1;
       
  7523                 s.left = this._left + "px";
       
  7524             }
       
  7525             if (top) {
       
  7526                 this._top = (this._top + top) % 1;
       
  7527                 s.top = this._top + "px";
       
  7528             }
       
  7529         }
       
  7530     };
       
  7531     
       
  7532     R.prototype.clear = function () {
       
  7533         R.eve("clear", this);
       
  7534         var c = this.canvas;
       
  7535         while (c.firstChild) {
       
  7536             c.removeChild(c.firstChild);
       
  7537         }
       
  7538         this.bottom = this.top = null;
       
  7539         (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
       
  7540         c.appendChild(this.desc);
       
  7541         c.appendChild(this.defs = $("defs"));
       
  7542     };
       
  7543     
       
  7544     R.prototype.remove = function () {
       
  7545         eve("remove", this);
       
  7546         this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
       
  7547         for (var i in this) {
       
  7548             this[i] = removed(i);
       
  7549         }
       
  7550     };
       
  7551     var setproto = R.st;
       
  7552     for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
       
  7553         setproto[method] = (function (methodname) {
       
  7554             return function () {
       
  7555                 var arg = arguments;
       
  7556                 return this.forEach(function (el) {
       
  7557                     el[methodname].apply(el, arg);
       
  7558                 });
       
  7559             };
       
  7560         })(method);
       
  7561     }
       
  7562 }(window.Raphael);
       
  7563 
       
  7564 // ┌─────────────────────────────────────────────────────────────────────┐ \\
       
  7565 // │ Raphaël 2 - JavaScript Vector Library                               │ \\
       
  7566 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  7567 // │ VML Module                                                          │ \\
       
  7568 // ├─────────────────────────────────────────────────────────────────────┤ \\
       
  7569 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
       
  7570 // │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
       
  7571 // │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
       
  7572 // └─────────────────────────────────────────────────────────────────────┘ \\
       
  7573 window.Raphael.vml && function (R) {
       
  7574     var has = "hasOwnProperty",
       
  7575         Str = String,
       
  7576         toFloat = parseFloat,
       
  7577         math = Math,
       
  7578         round = math.round,
       
  7579         mmax = math.max,
       
  7580         mmin = math.min,
       
  7581         abs = math.abs,
       
  7582         fillString = "fill",
       
  7583         separator = /[, ]+/,
       
  7584         eve = R.eve,
       
  7585         ms = " progid:DXImageTransform.Microsoft",
       
  7586         S = " ",
       
  7587         E = "",
       
  7588         map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
       
  7589         bites = /([clmz]),?([^clmz]*)/gi,
       
  7590         blurregexp = / progid:\S+Blur\([^\)]+\)/g,
       
  7591         val = /-?[^,\s-]+/g,
       
  7592         cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
       
  7593         zoom = 21600,
       
  7594         pathTypes = {path: 1, rect: 1, image: 1},
       
  7595         ovalTypes = {circle: 1, ellipse: 1},
       
  7596         path2vml = function (path) {
       
  7597             var total =  /[ahqstv]/ig,
       
  7598                 command = R._pathToAbsolute;
       
  7599             Str(path).match(total) && (command = R._path2curve);
       
  7600             total = /[clmz]/g;
       
  7601             if (command == R._pathToAbsolute && !Str(path).match(total)) {
       
  7602                 var res = Str(path).replace(bites, function (all, command, args) {
       
  7603                     var vals = [],
       
  7604                         isMove = command.toLowerCase() == "m",
       
  7605                         res = map[command];
       
  7606                     args.replace(val, function (value) {
       
  7607                         if (isMove && vals.length == 2) {
       
  7608                             res += vals + map[command == "m" ? "l" : "L"];
       
  7609                             vals = [];
       
  7610                         }
       
  7611                         vals.push(round(value * zoom));
       
  7612                     });
       
  7613                     return res + vals;
       
  7614                 });
       
  7615                 return res;
       
  7616             }
       
  7617             var pa = command(path), p, r;
       
  7618             res = [];
       
  7619             for (var i = 0, ii = pa.length; i < ii; i++) {
       
  7620                 p = pa[i];
       
  7621                 r = pa[i][0].toLowerCase();
       
  7622                 r == "z" && (r = "x");
       
  7623                 for (var j = 1, jj = p.length; j < jj; j++) {
       
  7624                     r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
       
  7625                 }
       
  7626                 res.push(r);
       
  7627             }
       
  7628             return res.join(S);
       
  7629         },
       
  7630         compensation = function (deg, dx, dy) {
       
  7631             var m = R.matrix();
       
  7632             m.rotate(-deg, .5, .5);
       
  7633             return {
       
  7634                 dx: m.x(dx, dy),
       
  7635                 dy: m.y(dx, dy)
       
  7636             };
       
  7637         },
       
  7638         setCoords = function (p, sx, sy, dx, dy, deg) {
       
  7639             var _ = p._,
       
  7640                 m = p.matrix,
       
  7641                 fillpos = _.fillpos,
       
  7642                 o = p.node,
       
  7643                 s = o.style,
       
  7644                 y = 1,
       
  7645                 flip = "",
       
  7646                 dxdy,
       
  7647                 kx = zoom / sx,
       
  7648                 ky = zoom / sy;
       
  7649             s.visibility = "hidden";
       
  7650             if (!sx || !sy) {
       
  7651                 return;
       
  7652             }
       
  7653             o.coordsize = abs(kx) + S + abs(ky);
       
  7654             s.rotation = deg * (sx * sy < 0 ? -1 : 1);
       
  7655             if (deg) {
       
  7656                 var c = compensation(deg, dx, dy);
       
  7657                 dx = c.dx;
       
  7658                 dy = c.dy;
       
  7659             }
       
  7660             sx < 0 && (flip += "x");
       
  7661             sy < 0 && (flip += " y") && (y = -1);
       
  7662             s.flip = flip;
       
  7663             o.coordorigin = (dx * -kx) + S + (dy * -ky);
       
  7664             if (fillpos || _.fillsize) {
       
  7665                 var fill = o.getElementsByTagName(fillString);
       
  7666                 fill = fill && fill[0];
       
  7667                 o.removeChild(fill);
       
  7668                 if (fillpos) {
       
  7669                     c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
       
  7670                     fill.position = c.dx * y + S + c.dy * y;
       
  7671                 }
       
  7672                 if (_.fillsize) {
       
  7673                     fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
       
  7674                 }
       
  7675                 o.appendChild(fill);
       
  7676             }
       
  7677             s.visibility = "visible";
       
  7678         };
       
  7679     R.toString = function () {
       
  7680         return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
       
  7681     };
       
  7682     addArrow = function (o, value, isEnd) {
       
  7683         var values = Str(value).toLowerCase().split("-"),
       
  7684             se = isEnd ? "end" : "start",
       
  7685             i = values.length,
       
  7686             type = "classic",
       
  7687             w = "medium",
       
  7688             h = "medium";
       
  7689         while (i--) {
       
  7690             switch (values[i]) {
       
  7691                 case "block":
       
  7692                 case "classic":
       
  7693                 case "oval":
       
  7694                 case "diamond":
       
  7695                 case "open":
       
  7696                 case "none":
       
  7697                     type = values[i];
       
  7698                     break;
       
  7699                 case "wide":
       
  7700                 case "narrow": h = values[i]; break;
       
  7701                 case "long":
       
  7702                 case "short": w = values[i]; break;
       
  7703             }
       
  7704         }
       
  7705         var stroke = o.node.getElementsByTagName("stroke")[0];
       
  7706         stroke[se + "arrow"] = type;
       
  7707         stroke[se + "arrowlength"] = w;
       
  7708         stroke[se + "arrowwidth"] = h;
       
  7709     };
       
  7710     setFillAndStroke = function (o, params) {
       
  7711         // o.paper.canvas.style.display = "none";
       
  7712         o.attrs = o.attrs || {};
       
  7713         var node = o.node,
       
  7714             a = o.attrs,
       
  7715             s = node.style,
       
  7716             xy,
       
  7717             newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
       
  7718             isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
       
  7719             res = o;
       
  7720 
       
  7721 
       
  7722         for (var par in params) if (params[has](par)) {
       
  7723             a[par] = params[par];
       
  7724         }
       
  7725         if (newpath) {
       
  7726             a.path = R._getPath[o.type](o);
       
  7727             o._.dirty = 1;
       
  7728         }
       
  7729         params.href && (node.href = params.href);
       
  7730         params.title && (node.title = params.title);
       
  7731         params.target && (node.target = params.target);
       
  7732         params.cursor && (s.cursor = params.cursor);
       
  7733         "blur" in params && o.blur(params.blur);
       
  7734         if (params.path && o.type == "path" || newpath) {
       
  7735             node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
       
  7736             if (o.type == "image") {
       
  7737                 o._.fillpos = [a.x, a.y];
       
  7738                 o._.fillsize = [a.width, a.height];
       
  7739                 setCoords(o, 1, 1, 0, 0, 0);
       
  7740             }
       
  7741         }
       
  7742         "transform" in params && o.transform(params.transform);
       
  7743         if (isOval) {
       
  7744             var cx = +a.cx,
       
  7745                 cy = +a.cy,
       
  7746                 rx = +a.rx || +a.r || 0,
       
  7747                 ry = +a.ry || +a.r || 0;
       
  7748             node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
       
  7749         }
       
  7750         if ("clip-rect" in params) {
       
  7751             var rect = Str(params["clip-rect"]).split(separator);
       
  7752             if (rect.length == 4) {
       
  7753                 rect[2] = +rect[2] + (+rect[0]);
       
  7754                 rect[3] = +rect[3] + (+rect[1]);
       
  7755                 var div = node.clipRect || R._g.doc.createElement("div"),
       
  7756                     dstyle = div.style;
       
  7757                 dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
       
  7758                 if (!node.clipRect) {
       
  7759                     dstyle.position = "absolute";
       
  7760                     dstyle.top = 0;
       
  7761                     dstyle.left = 0;
       
  7762                     dstyle.width = o.paper.width + "px";
       
  7763                     dstyle.height = o.paper.height + "px";
       
  7764                     node.parentNode.insertBefore(div, node);
       
  7765                     div.appendChild(node);
       
  7766                     node.clipRect = div;
       
  7767                 }
       
  7768             }
       
  7769             if (!params["clip-rect"]) {
       
  7770                 node.clipRect && (node.clipRect.style.clip = E);
       
  7771             }
       
  7772         }
       
  7773         if (o.textpath) {
       
  7774             var textpathStyle = o.textpath.style;
       
  7775             params.font && (textpathStyle.font = params.font);
       
  7776             params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
       
  7777             params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
       
  7778             params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
       
  7779             params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
       
  7780         }
       
  7781         if ("arrow-start" in params) {
       
  7782             addArrow(res, params["arrow-start"]);
       
  7783         }
       
  7784         if ("arrow-end" in params) {
       
  7785             addArrow(res, params["arrow-end"], 1);
       
  7786         }
       
  7787         if (params.opacity != null || 
       
  7788             params["stroke-width"] != null ||
       
  7789             params.fill != null ||
       
  7790             params.src != null ||
       
  7791             params.stroke != null ||
       
  7792             params["stroke-width"] != null ||
       
  7793             params["stroke-opacity"] != null ||
       
  7794             params["fill-opacity"] != null ||
       
  7795             params["stroke-dasharray"] != null ||
       
  7796             params["stroke-miterlimit"] != null ||
       
  7797             params["stroke-linejoin"] != null ||
       
  7798             params["stroke-linecap"] != null) {
       
  7799             var fill = node.getElementsByTagName(fillString),
       
  7800                 newfill = false;
       
  7801             fill = fill && fill[0];
       
  7802             !fill && (newfill = fill = createNode(fillString));
       
  7803             if (o.type == "image" && params.src) {
       
  7804                 fill.src = params.src;
       
  7805             }
       
  7806             params.fill && (fill.on = true);
       
  7807             if (fill.on == null || params.fill == "none" || params.fill === null) {
       
  7808                 fill.on = false;
       
  7809             }
       
  7810             if (fill.on && params.fill) {
       
  7811                 var isURL = Str(params.fill).match(R._ISURL);
       
  7812                 if (isURL) {
       
  7813                     fill.parentNode == node && node.removeChild(fill);
       
  7814                     fill.rotate = true;
       
  7815                     fill.src = isURL[1];
       
  7816                     fill.type = "tile";
       
  7817                     var bbox = o.getBBox(1);
       
  7818                     fill.position = bbox.x + S + bbox.y;
       
  7819                     o._.fillpos = [bbox.x, bbox.y];
       
  7820 
       
  7821                     R._preload(isURL[1], function () {
       
  7822                         o._.fillsize = [this.offsetWidth, this.offsetHeight];
       
  7823                     });
       
  7824                 } else {
       
  7825                     fill.color = R.getRGB(params.fill).hex;
       
  7826                     fill.src = E;
       
  7827                     fill.type = "solid";
       
  7828                     if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
       
  7829                         a.fill = "none";
       
  7830                         a.gradient = params.fill;
       
  7831                         fill.rotate = false;
       
  7832                     }
       
  7833                 }
       
  7834             }
       
  7835             if ("fill-opacity" in params || "opacity" in params) {
       
  7836                 var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
       
  7837                 opacity = mmin(mmax(opacity, 0), 1);
       
  7838                 fill.opacity = opacity;
       
  7839                 if (fill.src) {
       
  7840                     fill.color = "none";
       
  7841                 }
       
  7842             }
       
  7843             node.appendChild(fill);
       
  7844             var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
       
  7845             newstroke = false;
       
  7846             !stroke && (newstroke = stroke = createNode("stroke"));
       
  7847             if ((params.stroke && params.stroke != "none") ||
       
  7848                 params["stroke-width"] ||
       
  7849                 params["stroke-opacity"] != null ||
       
  7850                 params["stroke-dasharray"] ||
       
  7851                 params["stroke-miterlimit"] ||
       
  7852                 params["stroke-linejoin"] ||
       
  7853                 params["stroke-linecap"]) {
       
  7854                 stroke.on = true;
       
  7855             }
       
  7856             (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
       
  7857             var strokeColor = R.getRGB(params.stroke);
       
  7858             stroke.on && params.stroke && (stroke.color = strokeColor.hex);
       
  7859             opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
       
  7860             var width = (toFloat(params["stroke-width"]) || 1) * .75;
       
  7861             opacity = mmin(mmax(opacity, 0), 1);
       
  7862             params["stroke-width"] == null && (width = a["stroke-width"]);
       
  7863             params["stroke-width"] && (stroke.weight = width);
       
  7864             width && width < 1 && (opacity *= width) && (stroke.weight = 1);
       
  7865             stroke.opacity = opacity;
       
  7866         
       
  7867             params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
       
  7868             stroke.miterlimit = params["stroke-miterlimit"] || 8;
       
  7869             params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
       
  7870             if (params["stroke-dasharray"]) {
       
  7871                 var dasharray = {
       
  7872                     "-": "shortdash",
       
  7873                     ".": "shortdot",
       
  7874                     "-.": "shortdashdot",
       
  7875                     "-..": "shortdashdotdot",
       
  7876                     ". ": "dot",
       
  7877                     "- ": "dash",
       
  7878                     "--": "longdash",
       
  7879                     "- .": "dashdot",
       
  7880                     "--.": "longdashdot",
       
  7881                     "--..": "longdashdotdot"
       
  7882                 };
       
  7883                 stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
       
  7884             }
       
  7885             newstroke && node.appendChild(stroke);
       
  7886         }
       
  7887         if (res.type == "text") {
       
  7888             res.paper.canvas.style.display = E;
       
  7889             var span = res.paper.span,
       
  7890                 m = 100,
       
  7891                 fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
       
  7892             s = span.style;
       
  7893             a.font && (s.font = a.font);
       
  7894             a["font-family"] && (s.fontFamily = a["font-family"]);
       
  7895             a["font-weight"] && (s.fontWeight = a["font-weight"]);
       
  7896             a["font-style"] && (s.fontStyle = a["font-style"]);
       
  7897             fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]);
       
  7898             s.fontSize = fontSize * m + "px";
       
  7899             res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
       
  7900             var brect = span.getBoundingClientRect();
       
  7901             res.W = a.w = (brect.right - brect.left) / m;
       
  7902             res.H = a.h = (brect.bottom - brect.top) / m;
       
  7903             // res.paper.canvas.style.display = "none";
       
  7904             res.X = a.x;
       
  7905             res.Y = a.y + res.H / 2;
       
  7906 
       
  7907             ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
       
  7908             var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
       
  7909             for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
       
  7910                 res._.dirty = 1;
       
  7911                 break;
       
  7912             }
       
  7913         
       
  7914             // text-anchor emulation
       
  7915             switch (a["text-anchor"]) {
       
  7916                 case "start":
       
  7917                     res.textpath.style["v-text-align"] = "left";
       
  7918                     res.bbx = res.W / 2;
       
  7919                 break;
       
  7920                 case "end":
       
  7921                     res.textpath.style["v-text-align"] = "right";
       
  7922                     res.bbx = -res.W / 2;
       
  7923                 break;
       
  7924                 default:
       
  7925                     res.textpath.style["v-text-align"] = "center";
       
  7926                     res.bbx = 0;
       
  7927                 break;
       
  7928             }
       
  7929             res.textpath.style["v-text-kern"] = true;
       
  7930         }
       
  7931         // res.paper.canvas.style.display = E;
       
  7932     };
       
  7933     addGradientFill = function (o, gradient, fill) {
       
  7934         o.attrs = o.attrs || {};
       
  7935         var attrs = o.attrs,
       
  7936             pow = Math.pow,
       
  7937             opacity,
       
  7938             oindex,
       
  7939             type = "linear",
       
  7940             fxfy = ".5 .5";
       
  7941         o.attrs.gradient = gradient;
       
  7942         gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
       
  7943             type = "radial";
       
  7944             if (fx && fy) {
       
  7945                 fx = toFloat(fx);
       
  7946                 fy = toFloat(fy);
       
  7947                 pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
       
  7948                 fxfy = fx + S + fy;
       
  7949             }
       
  7950             return E;
       
  7951         });
       
  7952         gradient = gradient.split(/\s*\-\s*/);
       
  7953         if (type == "linear") {
       
  7954             var angle = gradient.shift();
       
  7955             angle = -toFloat(angle);
       
  7956             if (isNaN(angle)) {
       
  7957                 return null;
       
  7958             }
       
  7959         }
       
  7960         var dots = R._parseDots(gradient);
       
  7961         if (!dots) {
       
  7962             return null;
       
  7963         }
       
  7964         o = o.shape || o.node;
       
  7965         if (dots.length) {
       
  7966             o.removeChild(fill);
       
  7967             fill.on = true;
       
  7968             fill.method = "none";
       
  7969             fill.color = dots[0].color;
       
  7970             fill.color2 = dots[dots.length - 1].color;
       
  7971             var clrs = [];
       
  7972             for (var i = 0, ii = dots.length; i < ii; i++) {
       
  7973                 dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
       
  7974             }
       
  7975             fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
       
  7976             if (type == "radial") {
       
  7977                 fill.type = "gradientTitle";
       
  7978                 fill.focus = "100%";
       
  7979                 fill.focussize = "0 0";
       
  7980                 fill.focusposition = fxfy;
       
  7981                 fill.angle = 0;
       
  7982             } else {
       
  7983                 // fill.rotate= true;
       
  7984                 fill.type = "gradient";
       
  7985                 fill.angle = (270 - angle) % 360;
       
  7986             }
       
  7987             o.appendChild(fill);
       
  7988         }
       
  7989         return 1;
       
  7990     };
       
  7991     Element = function (node, vml) {
       
  7992         this[0] = this.node = node;
       
  7993         node.raphael = true;
       
  7994         this.id = R._oid++;
       
  7995         node.raphaelid = this.id;
       
  7996         this.X = 0;
       
  7997         this.Y = 0;
       
  7998         this.attrs = {};
       
  7999         this.paper = vml;
       
  8000         this.matrix = R.matrix();
       
  8001         this._ = {
       
  8002             transform: [],
       
  8003             sx: 1,
       
  8004             sy: 1,
       
  8005             dx: 0,
       
  8006             dy: 0,
       
  8007             deg: 0,
       
  8008             dirty: 1,
       
  8009             dirtyT: 1
       
  8010         };
       
  8011         !vml.bottom && (vml.bottom = this);
       
  8012         this.prev = vml.top;
       
  8013         vml.top && (vml.top.next = this);
       
  8014         vml.top = this;
       
  8015         this.next = null;
       
  8016     };
       
  8017     var elproto = R.el;
       
  8018 
       
  8019     Element.prototype = elproto;
       
  8020     elproto.constructor = Element;
       
  8021     elproto.transform = function (tstr) {
       
  8022         if (tstr == null) {
       
  8023             return this._.transform;
       
  8024         }
       
  8025         var vbs = this.paper._viewBoxShift,
       
  8026             vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
       
  8027             oldt;
       
  8028         if (vbs) {
       
  8029             oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
       
  8030         }
       
  8031         R._extractTransform(this, vbt + tstr);
       
  8032         var matrix = this.matrix.clone(),
       
  8033             skew = this.skew,
       
  8034             o = this.node,
       
  8035             split,
       
  8036             isGrad = ~Str(this.attrs.fill).indexOf("-"),
       
  8037             isPatt = !Str(this.attrs.fill).indexOf("url(");
       
  8038         matrix.translate(-.5, -.5);
       
  8039         if (isPatt || isGrad || this.type == "image") {
       
  8040             skew.matrix = "1 0 0 1";
       
  8041             skew.offset = "0 0";
       
  8042             split = matrix.split();
       
  8043             if ((isGrad && split.noRotation) || !split.isSimple) {
       
  8044                 o.style.filter = matrix.toFilter();
       
  8045                 var bb = this.getBBox(),
       
  8046                     bbt = this.getBBox(1),
       
  8047                     dx = bb.x - bbt.x,
       
  8048                     dy = bb.y - bbt.y;
       
  8049                 o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
       
  8050                 setCoords(this, 1, 1, dx, dy, 0);
       
  8051             } else {
       
  8052                 o.style.filter = E;
       
  8053                 setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
       
  8054             }
       
  8055         } else {
       
  8056             o.style.filter = E;
       
  8057             skew.matrix = Str(matrix);
       
  8058             skew.offset = matrix.offset();
       
  8059         }
       
  8060         oldt && (this._.transform = oldt);
       
  8061         return this;
       
  8062     };
       
  8063     elproto.rotate = function (deg, cx, cy) {
       
  8064         if (this.removed) {
       
  8065             return this;
       
  8066         }
       
  8067         if (deg == null) {
       
  8068             return;
       
  8069         }
       
  8070         deg = Str(deg).split(separator);
       
  8071         if (deg.length - 1) {
       
  8072             cx = toFloat(deg[1]);
       
  8073             cy = toFloat(deg[2]);
       
  8074         }
       
  8075         deg = toFloat(deg[0]);
       
  8076         (cy == null) && (cx = cy);
       
  8077         if (cx == null || cy == null) {
       
  8078             var bbox = this.getBBox(1);
       
  8079             cx = bbox.x + bbox.width / 2;
       
  8080             cy = bbox.y + bbox.height / 2;
       
  8081         }
       
  8082         this._.dirtyT = 1;
       
  8083         this.transform(this._.transform.concat([["r", deg, cx, cy]]));
       
  8084         return this;
       
  8085     };
       
  8086     elproto.translate = function (dx, dy) {
       
  8087         if (this.removed) {
       
  8088             return this;
       
  8089         }
       
  8090         dx = Str(dx).split(separator);
       
  8091         if (dx.length - 1) {
       
  8092             dy = toFloat(dx[1]);
       
  8093         }
       
  8094         dx = toFloat(dx[0]) || 0;
       
  8095         dy = +dy || 0;
       
  8096         if (this._.bbox) {
       
  8097             this._.bbox.x += dx;
       
  8098             this._.bbox.y += dy;
       
  8099         }
       
  8100         this.transform(this._.transform.concat([["t", dx, dy]]));
       
  8101         return this;
       
  8102     };
       
  8103     elproto.scale = function (sx, sy, cx, cy) {
       
  8104         if (this.removed) {
       
  8105             return this;
       
  8106         }
       
  8107         sx = Str(sx).split(separator);
       
  8108         if (sx.length - 1) {
       
  8109             sy = toFloat(sx[1]);
       
  8110             cx = toFloat(sx[2]);
       
  8111             cy = toFloat(sx[3]);
       
  8112             isNaN(cx) && (cx = null);
       
  8113             isNaN(cy) && (cy = null);
       
  8114         }
       
  8115         sx = toFloat(sx[0]);
       
  8116         (sy == null) && (sy = sx);
       
  8117         (cy == null) && (cx = cy);
       
  8118         if (cx == null || cy == null) {
       
  8119             var bbox = this.getBBox(1);
       
  8120         }
       
  8121         cx = cx == null ? bbox.x + bbox.width / 2 : cx;
       
  8122         cy = cy == null ? bbox.y + bbox.height / 2 : cy;
       
  8123     
       
  8124         this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
       
  8125         this._.dirtyT = 1;
       
  8126         return this;
       
  8127     };
       
  8128     elproto.hide = function () {
       
  8129         !this.removed && (this.node.style.display = "none");
       
  8130         return this;
       
  8131     };
       
  8132     elproto.show = function () {
       
  8133         !this.removed && (this.node.style.display = E);
       
  8134         return this;
       
  8135     };
       
  8136     elproto._getBBox = function () {
       
  8137         if (this.removed) {
       
  8138             return {};
       
  8139         }
       
  8140         if (this.type == "text") {
       
  8141             return {
       
  8142                 x: this.X + (this.bbx || 0) - this.W / 2,
       
  8143                 y: this.Y - this.H,
       
  8144                 width: this.W,
       
  8145                 height: this.H
       
  8146             };
       
  8147         } else {
       
  8148             return pathDimensions(this.attrs.path);
       
  8149         }
       
  8150     };
       
  8151     elproto.remove = function () {
       
  8152         if (this.removed) {
       
  8153             return;
       
  8154         }
       
  8155         this.paper.__set__ && this.paper.__set__.exclude(this);
       
  8156         R.eve.unbind("*.*." + this.id);
       
  8157         R._tear(this, this.paper);
       
  8158         this.node.parentNode.removeChild(this.node);
       
  8159         this.shape && this.shape.parentNode.removeChild(this.shape);
       
  8160         for (var i in this) {
       
  8161             delete this[i];
       
  8162         }
       
  8163         this.removed = true;
       
  8164     };
       
  8165     elproto.attr = function (name, value) {
       
  8166         if (this.removed) {
       
  8167             return this;
       
  8168         }
       
  8169         if (name == null) {
       
  8170             var res = {};
       
  8171             for (var a in this.attrs) if (this.attrs[has](a)) {
       
  8172                 res[a] = this.attrs[a];
       
  8173             }
       
  8174             res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
       
  8175             res.transform = this._.transform;
       
  8176             return res;
       
  8177         }
       
  8178         if (value == null && R.is(name, "string")) {
       
  8179             if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
       
  8180                 return this.attrs.gradient;
       
  8181             }
       
  8182             var names = name.split(separator),
       
  8183                 out = {};
       
  8184             for (var i = 0, ii = names.length; i < ii; i++) {
       
  8185                 name = names[i];
       
  8186                 if (name in this.attrs) {
       
  8187                     out[name] = this.attrs[name];
       
  8188                 } else if (R.is(this.paper.customAttributes[name], "function")) {
       
  8189                     out[name] = this.paper.customAttributes[name].def;
       
  8190                 } else {
       
  8191                     out[name] = R._availableAttrs[name];
       
  8192                 }
       
  8193             }
       
  8194             return ii - 1 ? out : out[names[0]];
       
  8195         }
       
  8196         if (this.attrs && value == null && R.is(name, "array")) {
       
  8197             out = {};
       
  8198             for (i = 0, ii = name.length; i < ii; i++) {
       
  8199                 out[name[i]] = this.attr(name[i]);
       
  8200             }
       
  8201             return out;
       
  8202         }
       
  8203         var params;
       
  8204         if (value != null) {
       
  8205             params = {};
       
  8206             params[name] = value;
       
  8207         }
       
  8208         value == null && R.is(name, "object") && (params = name);
       
  8209         for (var key in params) {
       
  8210             eve("attr." + key + "." + this.id, this, params[key]);
       
  8211         }
       
  8212         if (params) {
       
  8213             for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
       
  8214                 var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
       
  8215                 this.attrs[key] = params[key];
       
  8216                 for (var subkey in par) if (par[has](subkey)) {
       
  8217                     params[subkey] = par[subkey];
       
  8218                 }
       
  8219             }
       
  8220             // this.paper.canvas.style.display = "none";
       
  8221             if (params.text && this.type == "text") {
       
  8222                 this.textpath.string = params.text;
       
  8223             }
       
  8224             setFillAndStroke(this, params);
       
  8225             // this.paper.canvas.style.display = E;
       
  8226         }
       
  8227         return this;
       
  8228     };
       
  8229     elproto.toFront = function () {
       
  8230         !this.removed && this.node.parentNode.appendChild(this.node);
       
  8231         this.paper && this.paper.top != this && R._tofront(this, this.paper);
       
  8232         return this;
       
  8233     };
       
  8234     elproto.toBack = function () {
       
  8235         if (this.removed) {
       
  8236             return this;
       
  8237         }
       
  8238         if (this.node.parentNode.firstChild != this.node) {
       
  8239             this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
       
  8240             R._toback(this, this.paper);
       
  8241         }
       
  8242         return this;
       
  8243     };
       
  8244     elproto.insertAfter = function (element) {
       
  8245         if (this.removed) {
       
  8246             return this;
       
  8247         }
       
  8248         if (element.constructor == R.st.constructor) {
       
  8249             element = element[element.length - 1];
       
  8250         }
       
  8251         if (element.node.nextSibling) {
       
  8252             element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
       
  8253         } else {
       
  8254             element.node.parentNode.appendChild(this.node);
       
  8255         }
       
  8256         R._insertafter(this, element, this.paper);
       
  8257         return this;
       
  8258     };
       
  8259     elproto.insertBefore = function (element) {
       
  8260         if (this.removed) {
       
  8261             return this;
       
  8262         }
       
  8263         if (element.constructor == R.st.constructor) {
       
  8264             element = element[0];
       
  8265         }
       
  8266         element.node.parentNode.insertBefore(this.node, element.node);
       
  8267         R._insertbefore(this, element, this.paper);
       
  8268         return this;
       
  8269     };
       
  8270     elproto.blur = function (size) {
       
  8271         var s = this.node.runtimeStyle,
       
  8272             f = s.filter;
       
  8273         f = f.replace(blurregexp, E);
       
  8274         if (+size !== 0) {
       
  8275             this.attrs.blur = size;
       
  8276             s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
       
  8277             s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
       
  8278         } else {
       
  8279             s.filter = f;
       
  8280             s.margin = 0;
       
  8281             delete this.attrs.blur;
       
  8282         }
       
  8283     };
       
  8284 
       
  8285     R._engine.path = function (pathString, vml) {
       
  8286         var el = createNode("shape");
       
  8287         el.style.cssText = cssDot;
       
  8288         el.coordsize = zoom + S + zoom;
       
  8289         el.coordorigin = vml.coordorigin;
       
  8290         var p = new Element(el, vml),
       
  8291             attr = {fill: "none", stroke: "#000"};
       
  8292         pathString && (attr.path = pathString);
       
  8293         p.type = "path";
       
  8294         p.path = [];
       
  8295         p.Path = E;
       
  8296         setFillAndStroke(p, attr);
       
  8297         vml.canvas.appendChild(el);
       
  8298         var skew = createNode("skew");
       
  8299         skew.on = true;
       
  8300         el.appendChild(skew);
       
  8301         p.skew = skew;
       
  8302         p.transform(E);
       
  8303         return p;
       
  8304     };
       
  8305     R._engine.rect = function (vml, x, y, w, h, r) {
       
  8306         var path = R._rectPath(x, y, w, h, r),
       
  8307             res = vml.path(path),
       
  8308             a = res.attrs;
       
  8309         res.X = a.x = x;
       
  8310         res.Y = a.y = y;
       
  8311         res.W = a.width = w;
       
  8312         res.H = a.height = h;
       
  8313         a.r = r;
       
  8314         a.path = path;
       
  8315         res.type = "rect";
       
  8316         return res;
       
  8317     };
       
  8318     R._engine.ellipse = function (vml, x, y, rx, ry) {
       
  8319         var res = vml.path(),
       
  8320             a = res.attrs;
       
  8321         res.X = x - rx;
       
  8322         res.Y = y - ry;
       
  8323         res.W = rx * 2;
       
  8324         res.H = ry * 2;
       
  8325         res.type = "ellipse";
       
  8326         setFillAndStroke(res, {
       
  8327             cx: x,
       
  8328             cy: y,
       
  8329             rx: rx,
       
  8330             ry: ry
       
  8331         });
       
  8332         return res;
       
  8333     };
       
  8334     R._engine.circle = function (vml, x, y, r) {
       
  8335         var res = vml.path(),
       
  8336             a = res.attrs;
       
  8337         res.X = x - r;
       
  8338         res.Y = y - r;
       
  8339         res.W = res.H = r * 2;
       
  8340         res.type = "circle";
       
  8341         setFillAndStroke(res, {
       
  8342             cx: x,
       
  8343             cy: y,
       
  8344             r: r
       
  8345         });
       
  8346         return res;
       
  8347     };
       
  8348     R._engine.image = function (vml, src, x, y, w, h) {
       
  8349         var path = R._rectPath(x, y, w, h),
       
  8350             res = vml.path(path).attr({stroke: "none"}),
       
  8351             a = res.attrs,
       
  8352             node = res.node,
       
  8353             fill = node.getElementsByTagName(fillString)[0];
       
  8354         a.src = src;
       
  8355         res.X = a.x = x;
       
  8356         res.Y = a.y = y;
       
  8357         res.W = a.width = w;
       
  8358         res.H = a.height = h;
       
  8359         a.path = path;
       
  8360         res.type = "image";
       
  8361         fill.parentNode == node && node.removeChild(fill);
       
  8362         fill.rotate = true;
       
  8363         fill.src = src;
       
  8364         fill.type = "tile";
       
  8365         res._.fillpos = [x, y];
       
  8366         res._.fillsize = [w, h];
       
  8367         node.appendChild(fill);
       
  8368         setCoords(res, 1, 1, 0, 0, 0);
       
  8369         return res;
       
  8370     };
       
  8371     R._engine.text = function (vml, x, y, text) {
       
  8372         var el = createNode("shape"),
       
  8373             path = createNode("path"),
       
  8374             o = createNode("textpath");
       
  8375         x = x || 0;
       
  8376         y = y || 0;
       
  8377         text = text || "";
       
  8378         path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
       
  8379         path.textpathok = true;
       
  8380         o.string = Str(text);
       
  8381         o.on = true;
       
  8382         el.style.cssText = cssDot;
       
  8383         el.coordsize = zoom + S + zoom;
       
  8384         el.coordorigin = "0 0";
       
  8385         var p = new Element(el, vml),
       
  8386             attr = {
       
  8387                 fill: "#000",
       
  8388                 stroke: "none",
       
  8389                 font: R._availableAttrs.font,
       
  8390                 text: text
       
  8391             };
       
  8392         p.shape = el;
       
  8393         p.path = path;
       
  8394         p.textpath = o;
       
  8395         p.type = "text";
       
  8396         p.attrs.text = Str(text);
       
  8397         p.attrs.x = x;
       
  8398         p.attrs.y = y;
       
  8399         p.attrs.w = 1;
       
  8400         p.attrs.h = 1;
       
  8401         setFillAndStroke(p, attr);
       
  8402         el.appendChild(o);
       
  8403         el.appendChild(path);
       
  8404         vml.canvas.appendChild(el);
       
  8405         var skew = createNode("skew");
       
  8406         skew.on = true;
       
  8407         el.appendChild(skew);
       
  8408         p.skew = skew;
       
  8409         p.transform(E);
       
  8410         return p;
       
  8411     };
       
  8412     R._engine.setSize = function (width, height) {
       
  8413         var cs = this.canvas.style;
       
  8414         this.width = width;
       
  8415         this.height = height;
       
  8416         width == +width && (width += "px");
       
  8417         height == +height && (height += "px");
       
  8418         cs.width = width;
       
  8419         cs.height = height;
       
  8420         cs.clip = "rect(0 " + width + " " + height + " 0)";
       
  8421         if (this._viewBox) {
       
  8422             setViewBox.apply(this, this._viewBox);
       
  8423         }
       
  8424         return this;
       
  8425     };
       
  8426     R._engine.setViewBox = function (x, y, w, h, fit) {
       
  8427         R.eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
       
  8428         var width = this.width,
       
  8429             height = this.height,
       
  8430             size = 1 / mmax(w / width, h / height),
       
  8431             H, W;
       
  8432         if (fit) {
       
  8433             H = height / h;
       
  8434             W = width / w;
       
  8435             if (w * H < width) {
       
  8436                 x -= (width - w * H) / 2 / H;
       
  8437             }
       
  8438             if (h * W < height) {
       
  8439                 y -= (height - h * W) / 2 / W;
       
  8440             }
       
  8441         }
       
  8442         this._viewBox = [x, y, w, h, !!fit];
       
  8443         this._viewBoxShift = {
       
  8444             dx: -x,
       
  8445             dy: -y,
       
  8446             scale: size
       
  8447         };
       
  8448         this.forEach(function (el) {
       
  8449             el.transform("...");
       
  8450         });
       
  8451         return this;
       
  8452     };
       
  8453     var createNode,
       
  8454         initWin = function (win) {
       
  8455             var doc = win.document;
       
  8456             doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
       
  8457             try {
       
  8458                 !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
       
  8459                 createNode = function (tagName) {
       
  8460                     return doc.createElement('<rvml:' + tagName + ' class="rvml">');
       
  8461                 };
       
  8462             } catch (e) {
       
  8463                 createNode = function (tagName) {
       
  8464                     return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
       
  8465                 };
       
  8466             }
       
  8467         };
       
  8468     initWin(R._g.win);
       
  8469     R._engine.create = function () {
       
  8470         var con = R._getContainer.apply(0, arguments),
       
  8471             container = con.container,
       
  8472             height = con.height,
       
  8473             s,
       
  8474             width = con.width,
       
  8475             x = con.x,
       
  8476             y = con.y;
       
  8477         if (!container) {
       
  8478             throw new Error("VML container not found.");
       
  8479         }
       
  8480         var res = new R._Paper,
       
  8481             c = res.canvas = R._g.doc.createElement("div"),
       
  8482             cs = c.style;
       
  8483         x = x || 0;
       
  8484         y = y || 0;
       
  8485         width = width || 512;
       
  8486         height = height || 342;
       
  8487         res.width = width;
       
  8488         res.height = height;
       
  8489         width == +width && (width += "px");
       
  8490         height == +height && (height += "px");
       
  8491         res.coordsize = zoom * 1e3 + S + zoom * 1e3;
       
  8492         res.coordorigin = "0 0";
       
  8493         res.span = R._g.doc.createElement("span");
       
  8494         res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
       
  8495         c.appendChild(res.span);
       
  8496         cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
       
  8497         if (container == 1) {
       
  8498             R._g.doc.body.appendChild(c);
       
  8499             cs.left = x + "px";
       
  8500             cs.top = y + "px";
       
  8501             cs.position = "absolute";
       
  8502         } else {
       
  8503             if (container.firstChild) {
       
  8504                 container.insertBefore(c, container.firstChild);
       
  8505             } else {
       
  8506                 container.appendChild(c);
       
  8507             }
       
  8508         }
       
  8509         // plugins.call(res, res, R.fn);
       
  8510         res.renderfix = function () {};
       
  8511         return res;
       
  8512     };
       
  8513     R.prototype.clear = function () {
       
  8514         R.eve("clear", this);
       
  8515         this.canvas.innerHTML = E;
       
  8516         this.span = R._g.doc.createElement("span");
       
  8517         this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
       
  8518         this.canvas.appendChild(this.span);
       
  8519         this.bottom = this.top = null;
       
  8520     };
       
  8521     R.prototype.remove = function () {
       
  8522         R.eve("remove", this);
       
  8523         this.canvas.parentNode.removeChild(this.canvas);
       
  8524         for (var i in this) {
       
  8525             this[i] = removed(i);
       
  8526         }
       
  8527         return true;
       
  8528     };
       
  8529 
       
  8530     var setproto = R.st;
       
  8531     for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
       
  8532         setproto[method] = (function (methodname) {
       
  8533             return function () {
       
  8534                 var arg = arguments;
       
  8535                 return this.forEach(function (el) {
       
  8536                     el[methodname].apply(el, arg);
       
  8537                 });
       
  8538             };
       
  8539         })(method);
       
  8540     }
       
  8541 }(window.Raphael);/* main file */
       
  8542 
       
  8543 if ( window.IriSP === undefined && window.__IriSP === undefined ) { 
       
  8544 	var IriSP = {}; 
       
  8545 	var __IriSP = IriSP; /* for backward compatibility */
       
  8546 }
       
  8547 
       
  8548 /* crap code will be the first against the wall when the
       
  8549    revolution comes */
       
  8550 IriSP.loadLibs = function( libs, customCssPath, metadata_url, callback ) {
       
  8551 		// Localize jQuery variable
       
  8552 		IriSP.jQuery = null;
       
  8553 
       
  8554 		/* FIXME : to refactor using popcorn.getscript ? */
       
  8555 		/******** Load jQuery if not present *********/
       
  8556 		if ( window.jQuery === undefined || window.jQuery.fn.jquery !== '1.4.2' ) {
       
  8557 			
       
  8558 			var script_tag = document.createElement( 'script' );
       
  8559 			script_tag.setAttribute( "type", "text/javascript" );
       
  8560 			script_tag.setAttribute( "src", libs.jQuery );
       
  8561 			
       
  8562 			script_tag.onload = scriptLibHandler;
       
  8563 			script_tag.onreadystatechange = function () { // Same thing but for IE
       
  8564 				if ( this.readyState == 'complete' || this.readyState == 'loaded' ) {
       
  8565 					scriptLibHandler();					
       
  8566 				}
       
  8567 			};
       
  8568 			
       
  8569 			// Try to find the head, otherwise default to the documentElement
       
  8570 			( document.getElementsByTagName("head")[0] || document.documentElement ).appendChild( script_tag );
       
  8571 		} else {
       
  8572 			// The jQuery version on the window is the one we want to use
       
  8573 			 IriSP.jQuery = window.jQuery;
       
  8574 			 scriptLibHandler();
       
  8575 		}
       
  8576 
       
  8577 		/******** Called once jQuery has loaded ******/
       
  8578 		function scriptLibHandler() {
       
  8579 			
       
  8580 			var script_jqUi_tooltip = document.createElement( 'script' );
       
  8581 			script_jqUi_tooltip.setAttribute( "type", "text/javascript" );
       
  8582 			script_jqUi_tooltip.setAttribute( "src", libs.jQueryToolTip );
       
  8583 			script_jqUi_tooltip.onload = scriptLoadHandler;
       
  8584 			script_jqUi_tooltip.onreadystatechange = function () { // Same thing but for IE
       
  8585 				if ( this.readyState == 'complete' || this.readyState == 'loaded' ) {
       
  8586 					scriptLoadHandler( "jquery.tools.min.js loded" );
       
  8587 				}
       
  8588 			};
       
  8589 			
       
  8590 			var script_swfObj = document.createElement('script');
       
  8591 			script_swfObj.setAttribute( "type","text/javascript" );
       
  8592 			script_swfObj.setAttribute( "src",libs.swfObject );
       
  8593 			script_swfObj.onload = scriptLoadHandler;
       
  8594 			script_swfObj.onreadystatechange = function () { // Same thing but for IE
       
  8595 				if ( this.readyState == 'complete' || this.readyState == 'loaded' ) {
       
  8596 					scriptLoadHandler( "swfobject.js loded" );
       
  8597 				}
       
  8598 			};
       
  8599 		
       
  8600 			var script_jqUi = document.createElement( 'script' );
       
  8601 			script_jqUi.setAttribute( "type","text/javascript" );
       
  8602 			script_jqUi.setAttribute( "src",libs.jQueryUI );
       
  8603 			script_jqUi.onload = scriptLoadHandler;
       
  8604 			script_jqUi.onreadystatechange = function () { // Same thing but for IE
       
  8605 				if ( this.readyState == 'complete' || this.readyState == 'loaded' ) {
       
  8606 					scriptLoadHandler( "jquery-ui.min.js loded" );
       
  8607 				}
       
  8608 			};
       
  8609 		
       
  8610 
       
  8611 			( document.getElementsByTagName("head")[0] || document.documentElement ).appendChild( script_jqUi_tooltip);
       
  8612 			( document.getElementsByTagName("head")[0] || document.documentElement ).appendChild( script_jqUi );
       
  8613 			( document.getElementsByTagName("head")[0] || document.documentElement ).appendChild( script_swfObj );
       
  8614 			
       
  8615 
       
  8616 		};
       
  8617 
       
  8618 		/******** Called once all lib are loaded ******/
       
  8619 		var loadLib = 0;
       
  8620 		/* FIXME : ugly */
       
  8621 		function scriptLoadHandler( Mylib ) {
       
  8622 			//alert(Mylib);
       
  8623 			loadLib +=1;
       
  8624 			if( loadLib===3 ) { 
       
  8625 				main(); 			  
       
  8626 			}
       
  8627 		};
       
  8628 
       
  8629 		/******** Our main function ********/
       
  8630 		function main() { 
       
  8631 			
       
  8632 
       
  8633 			//  Make our own IriSP.jQuery and restore window.jQuery if there was one. 
       
  8634 			IriSP.jQuery = window.jQuery.noConflict( true );
       
  8635 			// Call our Jquery
       
  8636 			IriSP.jQuery( document ).ready( function($) { 
       
  8637 				
       
  8638 				/******* Load CSS *******/
       
  8639 				var css_link_jquery = IriSP.jQuery( "<link>", { 
       
  8640 					rel: "stylesheet", 
       
  8641 					type: "text/css", 
       
  8642 					href: libs.cssjQueryUI,
       
  8643 					'class': "dynamic_css"
       
  8644 				} );
       
  8645 				var css_link_custom = IriSP.jQuery( "<link>", { 
       
  8646 					rel: "stylesheet", 
       
  8647 					type: "text/css", 
       
  8648 					href: customCssPath,
       
  8649 					'class': "dynamic_css"
       
  8650 				} );
       
  8651 				
       
  8652 				css_link_jquery.appendTo( 'head' );
       
  8653 				css_link_custom.appendTo( 'head' );   
       
  8654 
       
  8655 				// to see dynamicly loaded css on IE
       
  8656 				if ( $.browser.msie ) {
       
  8657 					$( '.dynamic_css' ).clone().appendTo( 'head' );
       
  8658 				}
       
  8659         
       
  8660         IriSP.setupDataLoader();
       
  8661         IriSP.__dataloader.get(metadata_url, 
       
  8662             function(data) {
       
  8663               /* save the data so that we could re-use it to
       
  8664                  configure the video
       
  8665               */
       
  8666               IriSP.__jsonMetadata = data;
       
  8667               callback.call(window) });
       
  8668       });
       
  8669     }
       
  8670 };
       
  8671 
       
  8672 
       
  8673 IriSP.annotation_template = "{{! template for an annotation displayed in a segmentWidget }}<div title='{{divTitle}}' id='{{id}}'	class='Ldt-iri-chapter' 	style='left: {{startPourcent}}%;          width: {{endPourcent}}%;          background:#{{hexa_color}};' 	></div>";
       
  8674 IriSP.annotationWidget_template = "{{! template for the annotation widget }}<div class='Ldt-AnnotationsWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>      <div class='Ldt-AnnotationContent'>        <div class='Ldt-AnnotationShareIcons'>         <a href=''><img src='{{img_dir}}/facebook.png' alt='share on facebook'></img></a>         <a href=''><img src='{{img_dir}}/twitter.png' alt='share on twitter'></img></a>         <a href=''><img src='{{img_dir}}/google.png' alt='share on google+'></img></a>      </div>		  <div class='Ldt-SaTitle'></div>	  	<div class='Ldt-SaDescription'></div>    </div>  </div></div>";
       
  8675 IriSP.annotation_loading_template = "{{! template shown while the annotation widget is loading }}<div id='Ldt-load-container'><div id='Ldt-loader'>&nbsp;</div> Chargement... </div>";
       
  8676 IriSP.arrowWidget_template = "<div class='Ldt-arrowWidget'></div>";
       
  8677 IriSP.overlay_marker_template = "{{! the template for the small white bars which is z-indexed over our segment widget }}<div class='positionMarker'></div>";
       
  8678 IriSP.player_template = "{{! template for the radio player }}<div class='Ldt-controler demo'>	<div class='Ldt-LeftPlayerControls'>    <div class='Ldt-button Ldt-CtrlPlay'></div>		<div class='Ldt-button Ldt-CtrlAnnotate'></div>    <div class='Ldt-button Ldt-CtrlSearch'></div>	</div>		<div class='Ldt-RightPlayerControls'>    <div class='Ldt-Time'>      <div class='Ldt-ElapsedTime'></div>      <div class='Ldt-TimeSeparator'>/</div>      <div class='Ldt-TotalTime'></div>    </div>		<div class='Ldt-button Ldt-CtrlSound'></div>	</div></div>";
       
  8679 IriSP.search_template = "{{! template for the search container }}<div class='LdtSearchContainer'	style='margin-left: {{margin_left}}; position: absolute; margin-top: -60px;'>	<div class='LdtSearch'		style='display: none; background-color: #EEE; width: 165px; boder: 1px; border-color: #CFCFCF; position: absolute; text-align: center;'>		<input class='LdtSearchInput'			style='margin-top: 2px; margin-bottom: 2px;' />	</div></div><div class='cleaner'></div>";
       
  8680 IriSP.share_template = "{{! social network sharing template }}<a onclick='__IriSP.MyApiPlayer.share(\'delicious\');' title='partager avec delicious'><span class='share shareDelicious'>&nbsp;</span></a>		<a onclick='__IriSP.MyApiPlayer.share(\'facebook\');' title='partager avec facebook'> <span class='share shareFacebook'>&nbsp;</span></a><a onclick='__IriSP.MyApiPlayer.share(\'twitter\');' title='partager avec twitter'>  <span class='share shareTwitter'>&nbsp;</span></a><a onclick='__IriSP.MyApiPlayer.share(\'myspace\');' title='partager avec Myspace'>  <span class='share shareMySpace'>&nbsp;</span></a>";
       
  8681 IriSP.sliderWidget_template = "{{! template for the slider widget - it's composed of two divs we one overlayed on top    of the other }}  <div class='Ldt-sliderBackground'></div><div class='Ldt-sliderForeground'></div><div class='Ldt-sliderPositionMarker'></div>";
       
  8682 IriSP.tooltip_template = "{{! template used by the jquery ui tooltip }}<div class='Ldt-tooltip'>  <div class='title'>{{title}}</div>  <div class='time'>{{begin}} : {{end}} </div>  <div class='description'>{{description}}</div></div>";
       
  8683 IriSP.tooltipWidget_template = "{{! template for the tooltip widget }}<div class='tip'>	<div class='tipcolor' style='height:10px;width:10px'></div>	<div class='tiptext'></div>";
       
  8684 IriSP.tweetWidget_template = "{{! template for the tweet widget }}<div class='Ldt-tweetWidget'>  <div class='Ldt-tweet-DoubleBorder'>      <img src='{{img_dir}}/minimize.png' class='Ldt-tweetWidgetKeepOpen' alt='dont minimize automatically'></img>      <img src='{{img_dir}}/minimize.png' class='Ldt-tweetWidgetMinimize' alt='minimize window'></img>      <div class='Ldt-tweetAvatar'></div>      <img src='{{img_dir}}/profile_arrow.png' class='Ldt-tweetAvatar-profileArrow'></img>      <div class='Ldt-tweetContents'></div>      <a href='' target='_blank' class='Ldt-Retweet'><div class='Ldt-RetweetIcon'></div> - Retweet </a>      <a href='' target='_blank' class='Ldt-TweetReply'><div class='Ldt-TweetReplyIcon'></div> - Reply</a>  </div></div>";/* wrapper that simulates popcorn.js because
       
  8685    popcorn is a bit unstable at the time */
       
  8686 
       
  8687 IriSP.PopcornReplacement = {
       
  8688   msgPump : {} /* used by jquery to receive and send messages */
       
  8689 };
       
  8690 
       
  8691 IriSP.PopcornReplacement.media = { 
       
  8692   "paused": true,
       
  8693   "muted": false
       
  8694 };
       
  8695 
       
  8696 IriSP.PopcornReplacement.listen = function(msg, callback) {
       
  8697 //  IriSP.jQuery(IriSP.PopcornReplacement.msgPump).bind(msg, function(event, rest) { callback(rest); });
       
  8698   if (!IriSP.PopcornReplacement.msgPump.hasOwnProperty(msg))
       
  8699     IriSP.PopcornReplacement.msgPump[msg] = [];
       
  8700 
       
  8701   IriSP.PopcornReplacement.msgPump[msg].push(callback);
       
  8702 };
       
  8703 
       
  8704 IriSP.PopcornReplacement.trigger = function(msg, params) {
       
  8705 //  IriSP.jQuery(IriSP.PopcornReplacement.msgPump).trigger(msg, params);
       
  8706   
       
  8707   if (!IriSP.PopcornReplacement.msgPump.hasOwnProperty(msg))
       
  8708     return;
       
  8709 
       
  8710   var d = IriSP.PopcornReplacement.msgPump[msg];
       
  8711   for(var entry in d) {
       
  8712     d[entry].call(window, params);
       
  8713   }
       
  8714 
       
  8715 };
       
  8716 
       
  8717 IriSP.PopcornReplacement.guid = function(prefix) {
       
  8718   var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
       
  8719       var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
       
  8720       return v.toString(16);
       
  8721    });
       
  8722 
       
  8723   return prefix + str;
       
  8724 };
       
  8725 
       
  8726 IriSP.PopcornReplacement.__initApi = function() {
       
  8727   IriSP.PopcornReplacement.trigger("loadedmetadata"); // we've done more than loading metadata of course,
       
  8728                                                       // but popcorn doesn't need to know more.
       
  8729   IriSP.PopcornReplacement.media.muted = jwplayer(IriSP.PopcornReplacement._container).getMute();
       
  8730 };
       
  8731 
       
  8732 IriSP.PopcornReplacement.jwplayer = function(container, options) {
       
  8733   IriSP.PopcornReplacement._container = container.slice(1); //eschew the '#'
       
  8734   options.events = {
       
  8735       onReady: IriSP.PopcornReplacement.__initApi,
       
  8736       onTime: IriSP.PopcornReplacement.__timeHandler,
       
  8737       onPlay: IriSP.PopcornReplacement.__playHandler,
       
  8738       onPause: IriSP.PopcornReplacement.__pauseHandler,
       
  8739       onSeek: IriSP.PopcornReplacement.__seekHandler 
       
  8740       }
       
  8741     
       
  8742   jwplayer(IriSP.PopcornReplacement._container).setup(options);
       
  8743   IriSP.PopcornReplacement.media.duration = options.duration;
       
  8744   return IriSP.PopcornReplacement;
       
  8745 };
       
  8746 
       
  8747 IriSP.PopcornReplacement.currentTime = function(time) {
       
  8748   if (typeof(time) === "undefined") {
       
  8749       return jwplayer(IriSP.PopcornReplacement._container).getPosition();            
       
  8750   } else {
       
  8751      var currentTime = +time;
       
  8752      jwplayer( IriSP.PopcornReplacement._container ).seek( currentTime );
       
  8753      IriSP.PopcornReplacement.trigger("seeked");
       
  8754      return jwplayer(IriSP.PopcornReplacement._container).getPosition();            
       
  8755   }
       
  8756 };
       
  8757 
       
  8758 IriSP.PopcornReplacement.play = function() {
       
  8759       IriSP.PopcornReplacement.media.paused = false;
       
  8760       IriSP.PopcornReplacement.trigger("play");
       
  8761 //      IriSP.PopcornReplacement.trigger("playing");
       
  8762       jwplayer( IriSP.PopcornReplacement._container ).play();
       
  8763 };
       
  8764     
       
  8765 IriSP.PopcornReplacement.pause = function() {
       
  8766       if ( !IriSP.PopcornReplacement.media.paused ) {
       
  8767         IriSP.PopcornReplacement.media.paused = true;
       
  8768         IriSP.PopcornReplacement.trigger( "pause" );
       
  8769         jwplayer( IriSP.PopcornReplacement._container ).pause();
       
  8770       }
       
  8771 };
       
  8772 
       
  8773 IriSP.PopcornReplacement.muted = function(val) {
       
  8774   if (typeof(val) !== "undefined") {
       
  8775 
       
  8776     if (jwplayer(IriSP.PopcornReplacement._container).getMute() !== val) {
       
  8777       if (val) {
       
  8778         jwplayer(IriSP.PopcornReplacement._container).setMute(true);
       
  8779         IriSP.PopcornReplacement.media.muted = true;
       
  8780       } else {
       
  8781         jwplayer( IriSP.PopcornReplacement._container ).setMute(false);
       
  8782         IriSP.PopcornReplacement.media.muted = false;
       
  8783       }
       
  8784 
       
  8785       IriSP.PopcornReplacement.trigger( "volumechange" );
       
  8786     }
       
  8787     
       
  8788     return jwplayer( IriSP.PopcornReplacement._container ).getMute();
       
  8789   } else {
       
  8790     return jwplayer( IriSP.PopcornReplacement._container ).getMute();
       
  8791   }
       
  8792 };
       
  8793 
       
  8794 IriSP.PopcornReplacement.mute = IriSP.PopcornReplacement.muted;
       
  8795 
       
  8796 IriSP.PopcornReplacement.__codes = [];
       
  8797 IriSP.PopcornReplacement.code = function(options) {
       
  8798   IriSP.PopcornReplacement.__codes.push(options);
       
  8799   return IriSP.PopcornReplacement;
       
  8800 };
       
  8801 
       
  8802 IriSP.PopcornReplacement.__runCode = function() {
       
  8803   var currentTime = jwplayer(IriSP.PopcornReplacement._container).getPosition();
       
  8804   var i = 0;
       
  8805   for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) {
       
  8806     var c = IriSP.PopcornReplacement.__codes[i];
       
  8807     if (currentTime == c.start) {
       
  8808       c.onStart();
       
  8809     }
       
  8810     
       
  8811     if (currentTime == c.end) {
       
  8812       c.onEnd();
       
  8813     }
       
  8814 
       
  8815   }
       
  8816 };
       
  8817 
       
  8818 /* called everytime the player updates itself 
       
  8819    (onTime event)
       
  8820  */
       
  8821 
       
  8822 IriSP.PopcornReplacement.__timeHandler = function(event) {
       
  8823   var pos = event.position;
       
  8824 
       
  8825   var i = 0;
       
  8826   for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) {
       
  8827      var c = IriSP.PopcornReplacement.__codes[i];
       
  8828      
       
  8829      if (pos >= c.start && pos < c.end && 
       
  8830          pos - 0.1 <= c.start) {
       
  8831         c.onStart();
       
  8832      }
       
  8833  
       
  8834      if (pos >= c.start && pos >= c.end && 
       
  8835          pos - 0.1 <= c.end) {
       
  8836         c.onEnd();
       
  8837      }
       
  8838    
       
  8839   }
       
  8840  
       
  8841   IriSP.PopcornReplacement.trigger("timeupdate");
       
  8842 };
       
  8843 
       
  8844 IriSP.PopcornReplacement.__seekHandler = function(event) { 
       
  8845   var i = 0;
       
  8846   for(i = 0; i < IriSP.PopcornReplacement.__codes.length; i++) {
       
  8847      var c = IriSP.PopcornReplacement.__codes[i];
       
  8848     
       
  8849      if (event.position >= c.start && event.position < c.end) {
       
  8850         c.onEnd();
       
  8851      }
       
  8852     
       
  8853      if (typeof(event.offset) === "undefined")
       
  8854        event.offset = 0;
       
  8855      if (event.offset >= c.start && event.offset < c.end) {
       
  8856        c.onStart();
       
  8857      }
       
  8858      
       
  8859    }
       
  8860 
       
  8861   IriSP.PopcornReplacement.trigger("timeupdate");
       
  8862 };
       
  8863 
       
  8864 
       
  8865 IriSP.PopcornReplacement.__playHandler = function(event) {
       
  8866   IriSP.PopcornReplacement.media.paused = false;
       
  8867   IriSP.PopcornReplacement.trigger("play");
       
  8868 };
       
  8869 
       
  8870 IriSP.PopcornReplacement.__pauseHandler = function(event) {
       
  8871   IriSP.PopcornReplacement.media.paused = true;
       
  8872   IriSP.PopcornReplacement.trigger("pause");
       
  8873 };
       
  8874 
       
  8875 IriSP.PopcornReplacement.roundTime = function() {
       
  8876   var currentTime = IriSP.PopcornReplacement.currentTime();
       
  8877   return Math.round(currentTime);
       
  8878 };
       
  8879 /* utils.js - various utils that don't belong anywhere else */
       
  8880 
       
  8881 /* trace function, for debugging */
       
  8882 
       
  8883 IriSP.traceNum = 0;
       
  8884 IriSP.trace = function( msg, value ) {
       
  8885 /*
       
  8886 	if( IriSP.config.gui.debug === true ) {
       
  8887 		IriSP.traceNum += 1;
       
  8888 		IriSP.jQuery( "<div>"+IriSP.traceNum+" - "+msg+" : "+value+"</div>" ).appendTo( "#Ldt-output" );
       
  8889 	}
       
  8890 */
       
  8891 };
       
  8892 
       
  8893 /* used in callbacks - because in callbacks we lose "this",
       
  8894    we need to have a special function which wraps "this" in 
       
  8895    a closure. This way, the 
       
  8896 */   
       
  8897 IriSP.wrap = function (obj, fn) {
       
  8898   return function() {    
       
  8899     var args = Array.prototype.slice.call(arguments, 0);
       
  8900     return fn.apply(obj, args);
       
  8901   }
       
  8902 }
       
  8903 
       
  8904 /* convert a time to a percentage in the media */
       
  8905 IriSP.timeToPourcent = function(time, timetotal){
       
  8906 	var time = Math.abs(time);
       
  8907   var timetotal = Math.abs(timetotal);
       
  8908   
       
  8909 	return Math.floor((time/timetotal) * 100);
       
  8910 };
       
  8911 
       
  8912 IriSP.padWithZeros = function(num) {
       
  8913   if (Math.abs(num) < 10) {
       
  8914     return "0" + num.toString();
       
  8915   } else {
       
  8916     return num.toString();
       
  8917   }
       
  8918 };
       
  8919 /* convert a number of seconds to a tuple of the form 
       
  8920    [hours, minutes, seconds]
       
  8921 */
       
  8922 IriSP.secondsToTime = function(secs) {  
       
  8923   var hours = Math.abs(parseInt( secs / 3600 ) % 24);
       
  8924   var minutes = Math.abs(parseInt( secs / 60 ) % 60);
       
  8925   var seconds = parseFloat(Math.abs(secs % 60).toFixed(0));
       
  8926   
       
  8927   var toString_fn = function() {
       
  8928     var ret = "";
       
  8929     if (hours > 0)
       
  8930        ret = IriSP.padWithZeros(this.hours) + ":";
       
  8931     ret += IriSP.padWithZeros(this.minutes) + ":" + IriSP.padWithZeros(this.seconds);
       
  8932 
       
  8933     return ret;
       
  8934   }
       
  8935   return {"hours" : hours, "minutes" : minutes, "seconds" : seconds, toString: toString_fn};
       
  8936 };
       
  8937 
       
  8938 IriSP.secondsToString
       
  8939 
       
  8940 /* format a tweet - replaces @name by a link to the profile, #hashtag, etc. */
       
  8941 IriSP.formatTweet = function(tweet) {
       
  8942   /*
       
  8943     an array of arrays which hold a regexp and its replacement.
       
  8944   */
       
  8945   var regExps = [
       
  8946     /* copied from http://codegolf.stackexchange.com/questions/464/shortest-url-regex-match-in-javascript/480#480 */
       
  8947     [/((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)/gi, "<a href='$1'>$1</a>"],
       
  8948     [/@(\w+)/gi, "<a href='http://twitter.com/$1'>@$1</a>"], // matches a @handle
       
  8949     [/#(\w+)/gi, "<a href='http://twitter.com/search?q=%23$1'>#$1</a>"],// matches a hashtag
       
  8950     [/(\+\+)/gi, "<span class='Ldt-PolemicPlusPlus'>$1</span>"],
       
  8951     [/(--)/gi, "<span class='Ldt-PolemicMinusMinus'>$1</span>"],
       
  8952     [/(==)/gi, "<span class='Ldt-PolemicEqualEqual'>$1</span>"],
       
  8953     [/(\?\?)/gi, "<span class='Ldt-PolemicQuestion'>$1</span>"]
       
  8954   ]; 
       
  8955 
       
  8956   var i = 0;
       
  8957   for(i = 0; i < regExps.length; i++) {
       
  8958      tweet = tweet.replace(regExps[i][0], regExps[i][1]);
       
  8959   }
       
  8960   
       
  8961   return tweet;
       
  8962 };
       
  8963 
       
  8964 IriSP.countProperties = function(obj) {
       
  8965     var count = 0;
       
  8966 
       
  8967     for(var prop in obj) {
       
  8968         if(obj.hasOwnProperty(prop))
       
  8969                 ++count;
       
  8970     }
       
  8971 
       
  8972     return count;
       
  8973 };
       
  8974 
       
  8975 // conversion de couleur Decimal vers HexaDecimal || 000 si fff
       
  8976 IriSP.DEC_HEXA_COLOR = function (dec) {
       
  8977 	 var hexa='0123456789ABCDEF',hex='';
       
  8978 	 var tmp;
       
  8979 	 while (dec>15){
       
  8980 		  tmp = dec-(Math.floor(dec/16))*16;
       
  8981 		  hex = hexa.charAt(tmp)+hex;
       
  8982 		  dec = Math.floor(dec/16);
       
  8983 	 }
       
  8984 	 hex = hexa.charAt(dec)+hex;
       
  8985 	 if (hex == "FFCC00"){ hex="";/* by default color of Ldt annotation */ }
       
  8986 	 return(hex);
       
  8987 };
       
  8988 
       
  8989 /* shortcut to have global variables in templates */
       
  8990 IriSP.templToHTML = function(template, values) {
       
  8991   var params = IriSP.jQuery.extend(IriSP.default_templates_vars, values);
       
  8992   return Mustache.to_html(template, params);
       
  8993 };
       
  8994 
       
  8995 /* we need to be stricter than encodeURIComponent,
       
  8996    because of twitter
       
  8997 */  
       
  8998 IriSP.encodeURI = function(str) {
       
  8999   return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').  
       
  9000                                  replace(/\)/g, '%29').replace(/\*/g, '%2A');  
       
  9001 }  
       
  9002 
       
  9003 
       
  9004 /* for ie compatibility
       
  9005 if (Object.prototype.__defineGetter__&&!Object.defineProperty) {
       
  9006    Object.defineProperty=function(obj,prop,desc) {
       
  9007       if ("get" in desc) obj.__defineGetter__(prop,desc.get);
       
  9008       if ("set" in desc) obj.__defineSetter__(prop,desc.set);
       
  9009    }
       
  9010 }
       
  9011 */
       
  9012 /* data.js - this file deals with how the players gets and sends data */
       
  9013 
       
  9014 IriSP.DataLoader = function() {
       
  9015   this._cache = {};
       
  9016   
       
  9017   /*
       
  9018     A structure to hold callbacks for specific urls. We need it because
       
  9019     ajax calls are asynchronous, so it means that sometimes we ask
       
  9020     multiple times for a ressource because the first call hasn't been
       
  9021     received yet.
       
  9022   */
       
  9023   this._callbacks = {};
       
  9024 };
       
  9025 
       
  9026 IriSP.DataLoader.prototype.get = function(url, callback) {
       
  9027 
       
  9028   var base_url = url.split("&")[0]
       
  9029   if (this._cache.hasOwnProperty(base_url)) {
       
  9030     callback(this._cache[base_url]);
       
  9031   } else {  
       
  9032     if (!this._callbacks.hasOwnProperty(base_url)) {
       
  9033       this._callbacks[base_url] = [];
       
  9034       this._callbacks[base_url].push(callback);   
       
  9035       /* we need a closure because this gets lost when it's called back */
       
  9036   
       
  9037       // uncomment you don't want to use caching.
       
  9038       // IriSP.jQuery.get(url, callback);
       
  9039       
       
  9040       var func = function(data) {
       
  9041                   this._cache[base_url] = data;                                
       
  9042                   var i = 0;
       
  9043                   
       
  9044                   for (i = 0; i < this._callbacks[base_url].length; i++) {
       
  9045                     this._callbacks[base_url][i](this._cache[base_url]);                                  
       
  9046                   }
       
  9047       };
       
  9048       
       
  9049       /* automagically choose between json and jsonp */
       
  9050       if (url.indexOf(document.location.hostname) === -1 &&
       
  9051           url.indexOf("http://") !== -1 /* not a relative url */ ) {
       
  9052         // we contacting a foreign domain, use JSONP
       
  9053 
       
  9054         IriSP.jQuery.get(url, {}, IriSP.wrap(this, func), "jsonp");
       
  9055       } else {
       
  9056 
       
  9057         // otherwise, hey, whatever rows your boat
       
  9058         IriSP.jQuery.get(url, IriSP.wrap(this, func));
       
  9059       }
       
  9060     
       
  9061     } else {
       
  9062       /* simply push the callback - it'll get called when the ressource
       
  9063          has been received */
       
  9064       
       
  9065       this._callbacks[base_url].push(callback);   
       
  9066    
       
  9067     }
       
  9068   }
       
  9069 }
       
  9070 
       
  9071 /* the base abstract "class" */
       
  9072 IriSP.Serializer = function(DataLoader, url) {
       
  9073   this._DataLoader = DataLoader;
       
  9074   this._url = url;
       
  9075   this._data = [];
       
  9076 };
       
  9077 
       
  9078 IriSP.Serializer.prototype.serialize = function(data) { };
       
  9079 IriSP.Serializer.prototype.deserialize = function(data) {};
       
  9080 
       
  9081 IriSP.Serializer.prototype.currentMedia = function() {  
       
  9082 };
       
  9083 
       
  9084 IriSP.Serializer.prototype.sync = function(callback) {  
       
  9085   callback.call(this, this._data);  
       
  9086 };
       
  9087 
       
  9088 IriSP.SerializerFactory = function(DataLoader) {
       
  9089   this._dataloader = DataLoader;
       
  9090 };
       
  9091 
       
  9092 IriSP.SerializerFactory.prototype.getSerializer = function(metadataOptions) {
       
  9093   /* This function returns serializer set-up with the correct
       
  9094      configuration - takes a metadata struct describing the metadata source
       
  9095   */
       
  9096   
       
  9097   if (metadataOptions === undefined)
       
  9098     /* return an empty serializer */
       
  9099     return IriSP.Serializer("", "");
       
  9100             
       
  9101   switch(metadataOptions.type) {
       
  9102     case "json":
       
  9103       return new IriSP.JSONSerializer(this._dataloader, metadataOptions.src);
       
  9104       break;
       
  9105     
       
  9106     case "dummy": /* only used for unit testing - not defined in production */
       
  9107       return new IriSP.MockSerializer(this._dataloader, metadataOptions.src);
       
  9108       break;
       
  9109     
       
  9110     case "empty":
       
  9111       return new IriSP.Serializer("", "empty");
       
  9112       break;
       
  9113       
       
  9114     default:      
       
  9115       return undefined;
       
  9116   }
       
  9117 };
       
  9118 /* site.js - all our site-dependent config : player chrome, cdn locations, etc...*/
       
  9119 
       
  9120 IriSP.lib = { 
       
  9121 		jQuery : "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js",
       
  9122 		jQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/jquery-ui.js",
       
  9123 		jQueryToolTip : "http://cdn.jquerytools.org/1.2.4/all/jquery.tools.min.js",
       
  9124 		swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js",
       
  9125 		cssjQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/base/jquery-ui.css",
       
  9126     popcorn : "src/js/libs/popcorn.js",
       
  9127     jwplayer : "src/js/libs/jwplayer.js",
       
  9128     "popcorn.mediafragment" : "src/js/libs/popcorn.mediafragment.js",
       
  9129     "popcorn.code" : "src/js/libs/popcorn.code.js",
       
  9130     "popcorn.jwplayer": "src/js/libs/popcorn.jwplayer.js",
       
  9131     "popcorn.youtube": "src/js/libs/popcorn.youtube.js",
       
  9132      raphael: "src/js/libs/raphael.js"
       
  9133 };
       
  9134 
       
  9135 //Player Configuration 
       
  9136 IriSP.config = undefined;
       
  9137 
       
  9138 IriSP.widgetsDefaults = {
       
  9139   "LayoutManager" : {spacer_div_height : "0px" },
       
  9140   "PlayerWidget" : {},
       
  9141   "AnnotationsWidget": {},
       
  9142   "TweetsWidget" : {
       
  9143       default_profile_picture : "https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
       
  9144       tweet_display_period: 10000 // how long do we show a tweet ?
       
  9145   },
       
  9146   "SliderWidget" : {
       
  9147       minimize_period: 850 // how long does the slider stays maximized after the user leaves the zone ?
       
  9148   }
       
  9149 };
       
  9150 
       
  9151 IriSP.paths = {
       
  9152 //  "imgs": "/tweetlive/res/metadataplayer/src/css/imgs"
       
  9153   "imgs": "/mdp/src/css/imgs"
       
  9154 };
       
  9155 IriSP.default_templates_vars = {
       
  9156   "img_dir" : IriSP.paths.imgs 
       
  9157 };
       
  9158 
       
  9159 /* ui.js - ui related functions */
       
  9160 
       
  9161 /* FIXME: use an sharing library */
       
  9162 IriSP.LdtShareTool = IriSP.share_template; /* the contents come from share.html */
       
  9163 
       
  9164 IriSP.createPlayerChrome = function(){
       
  9165 	var width = IriSP.config.gui.width;
       
  9166 	var height = IriSP.config.gui.height;
       
  9167 	var heightS = IriSP.config.gui.height-20;
       
  9168 	
       
  9169 	// AUDIO  */
       
  9170 	// PB dans le html : ; 
       
  9171 	IriSP.trace( "__IriSP.createMyHtml",IriSP.config.gui.container );
       
  9172 
       
  9173 	
       
  9174 	/* FIXME : factor this in another file */
       
  9175 	if( IriSP.config.gui.mode=="radio" ){
       
  9176 
       
  9177 		IriSP.jQuery( "#"+IriSP.config.gui.container ).before(IriSP.search_template);
       
  9178 		var radioPlayer = Mustache.to_html(IriSP.radio_template, {"share_template" : IriSP.share_template});
       
  9179 		IriSP.jQuery(radioPlayer).appendTo("#"+IriSP.config.gui.container);
       
  9180 
       
  9181 		// special tricks for IE 7
       
  9182 		if (IriSP.jQuery.browser.msie==true && IriSP.jQuery.browser.version=="7.0"){
       
  9183 			//LdtSearchContainer
       
  9184 			//__IriSP.jQuery("#LdtPlayer").attr("margin-top","50px");
       
  9185 			IriSP.jQuery("#Ldt-Root").css("padding-top","25px");
       
  9186 			IriSP.trace("__IriSP.createHtml","IE7 SPECIAL ");
       
  9187 		}
       
  9188 	} else if(IriSP.config.gui.mode=="video") {
       
  9189 	
       
  9190 		var videoPlayer = Mustache.to_html(IriSP.video_template, {"share_template" : IriSP.share_template, "heightS" : heightS});
       
  9191 		IriSP.jQuery(videoPlayer).appendTo("#"+IriSP.config.gui.container);
       
  9192 	}
       
  9193 	
       
  9194 	IriSP.jQuery("#Ldt-Annotations").width(width-(75*2));
       
  9195 	IriSP.jQuery("#Ldt-Show-Arrow-container").width(width-(75*2));
       
  9196 	IriSP.jQuery("#Ldt-ShowAnnotation-audio").width(width-10);
       
  9197 	IriSP.jQuery("#Ldt-ShowAnnotation-video").width(width-10);
       
  9198 	IriSP.jQuery("#Ldt-SaKeyword").width(width-10);
       
  9199 	IriSP.jQuery("#Ldt-controler").width(width-10);
       
  9200 	IriSP.jQuery("#Ldt-Control").attr("z-index","100");
       
  9201 	IriSP.jQuery("#Ldt-controler").hide();
       
  9202 	
       
  9203 	IriSP.jQuery(IriSP.annotation_loading_template).appendTo("#Ldt-ShowAnnotation-audio");
       
  9204 
       
  9205 	if(IriSP.config.gui.mode=='radio'){
       
  9206 		IriSP.jQuery("#Ldt-load-container").attr("width",IriSP.config.gui.width);
       
  9207 	}
       
  9208 	// Show or not the output
       
  9209 	if(IriSP.config.gui.debug===true){
       
  9210 		IriSP.jQuery("#Ldt-output").show();
       
  9211 	} else {
       
  9212 		IriSP.jQuery("#Ldt-output").hide();
       
  9213 	}
       
  9214 	
       
  9215 };
       
  9216 
       
  9217 
       
  9218 /* create the buttons and the slider   */
       
  9219 IriSP.createInterface = function( width, height, duration ) {
       
  9220 		
       
  9221 		IriSP.jQuery( "#Ldt-controler" ).show();
       
  9222 		//__IriSP.jQuery("#Ldt-Root").css('display','visible');
       
  9223 		IriSP.trace( "__IriSP.createInterface" , width+","+height+","+duration+"," );
       
  9224 		
       
  9225 		IriSP.jQuery( "#Ldt-ShowAnnotation").click( function () { 
       
  9226 			 //__IriSP.jQuery(this).slideUp(); 
       
  9227 		} );
       
  9228 
       
  9229 		var LdtpPlayerY = IriSP.jQuery("#Ldt-PlaceHolder").attr("top");
       
  9230 		var LdtpPlayerX = IriSP.jQuery("#Ldt-PlaceHolder").attr("left");
       
  9231 		
       
  9232 		IriSP.jQuery( "#slider-range-min" ).slider( { //range: "min",
       
  9233 			value: 0,
       
  9234 			min: 1,
       
  9235 			max: duration/1000,//1:54:52.66 = 3600+3240+
       
  9236 			step: 0.1,
       
  9237 			slide: function(event, ui) {
       
  9238 				
       
  9239 				//__IriSP.jQuery("#amount").val(ui.value+" s");
       
  9240 				//player.sendEvent('SEEK', ui.value)
       
  9241 				IriSP.MyApiPlayer.seek(ui.value);
       
  9242 				//changePageUrlOffset(ui.value);
       
  9243 				//player.sendEvent('PAUSE')
       
  9244 			}
       
  9245 		} );
       
  9246 		
       
  9247 		IriSP.trace("__IriSP.createInterface","ICI");
       
  9248 		IriSP.jQuery("#amount").val(IriSP.jQuery("#slider-range-min").slider("value")+" s");
       
  9249 		IriSP.jQuery(".Ldt-Control1 button:first").button({
       
  9250 			icons: {
       
  9251 				primary: 'ui-icon-play'
       
  9252 			},
       
  9253 			text: false
       
  9254 		}).next().button({
       
  9255 			icons: {
       
  9256 				primary: 'ui-icon-seek-next'
       
  9257 			},
       
  9258 			 text: false
       
  9259 		});
       
  9260 		IriSP.jQuery(".Ldt-Control2 button:first").button({
       
  9261 			icons: {
       
  9262 				primary: 'ui-icon-search'//,
       
  9263 				//secondary: 'ui-icon-volume-off'
       
  9264 			},
       
  9265 			text: false
       
  9266 		}).next().button({
       
  9267 			icons: {
       
  9268 				primary: 'ui-icon-volume-on'
       
  9269 			},
       
  9270 			 text: false
       
  9271 		});
       
  9272 
       
  9273 		// /!\ PB A MODIFIER 
       
  9274 		//__IriSP.MyTags.draw();
       
  9275 		IriSP.trace("__IriSP.createInterface","ICI2");
       
  9276 		IriSP.jQuery( "#ldt-CtrlPlay" ).attr( "style", "background-color:#CD21C24;" );
       
  9277 		
       
  9278 		IriSP.jQuery( "#Ldt-load-container" ).hide();
       
  9279 		
       
  9280 		if( IriSP.config.gui.mode=="radio" & IriSP.jQuery.browser.msie != true ) {
       
  9281 			IriSP.jQuery( "#Ldtplayer1" ).attr( "height", "0" );
       
  9282 		}
       
  9283 		IriSP.trace( "__IriSP.createInterface" , "3" );
       
  9284 
       
  9285 		IriSP.trace( "__IriSP.createInterface", "END" );
       
  9286 		
       
  9287 	};
       
  9288 /* the widget classes and definitions */
       
  9289 
       
  9290 IriSP.Widget = function(Popcorn, config, Serializer) {
       
  9291 
       
  9292   if (config === undefined || config === null) {
       
  9293     config = {}
       
  9294   }
       
  9295   
       
  9296   this._Popcorn = Popcorn;
       
  9297   this._config = config;  
       
  9298   this._serializer = Serializer;
       
  9299   
       
  9300   if (config.hasOwnProperty("container")) {
       
  9301      this._id = config.container;
       
  9302      this.selector = IriSP.jQuery("#" + this._id);
       
  9303   }
       
  9304 
       
  9305   if (config.hasOwnProperty("spacer")) {
       
  9306      this._spacerId = config.spacer;
       
  9307      this.spacer = IriSP.jQuery("#" + this._spacerId);
       
  9308   }
       
  9309 
       
  9310 
       
  9311   if (config.hasOwnProperty("width")) {
       
  9312      // this.width and not this._width because we consider it public.
       
  9313      this.width = config.width;     
       
  9314   }
       
  9315   
       
  9316   if (config.hasOwnProperty("height")) {    
       
  9317      this.height = config.height;     
       
  9318   }
       
  9319   
       
  9320   if (config.hasOwnProperty("heightmax")) {
       
  9321      this.heightmax = config.heightmax;     
       
  9322   }
       
  9323 
       
  9324   if (config.hasOwnProperty("widthmax")) {
       
  9325      this.widthmax = config.widthmax;     
       
  9326   }
       
  9327   
       
  9328 };
       
  9329 
       
  9330 IriSP.Widget.prototype.draw = function() {
       
  9331   /* implemented by "sub-classes" */  
       
  9332 };
       
  9333 
       
  9334 IriSP.Widget.prototype.redraw = function() {
       
  9335   /* implemented by "sub-classes" */  
       
  9336 };
       
  9337 /* modules are non-graphical entities, similar to widgets */
       
  9338 
       
  9339 IriSP.Module = function(Popcorn, config, Serializer) {
       
  9340 
       
  9341   if (config === undefined || config === null) {
       
  9342     config = {}
       
  9343   }
       
  9344   
       
  9345   this._Popcorn = Popcorn;
       
  9346   this._config = config;  
       
  9347   this._serializer = Serializer;
       
  9348 };
       
  9349 /* layout.js - very basic layout management */
       
  9350 
       
  9351 /*
       
  9352   a layout manager manages a div and the layout of objects
       
  9353   inside it.
       
  9354 */
       
  9355 
       
  9356 IriSP.LayoutManager = function(options) {
       
  9357     this._Popcorn = null;
       
  9358     this._widgets = [];
       
  9359     
       
  9360     this._div = "LdtPlayer";
       
  9361     this._width = 640;
       
  9362     
       
  9363     if (options === undefined) {
       
  9364       options = {};
       
  9365     };
       
  9366     
       
  9367     if (options.hasOwnProperty('container')) {
       
  9368       this._div = options.container;
       
  9369     }
       
  9370 
       
  9371     if (options.hasOwnProperty('width')) {
       
  9372       this._width = options.width;
       
  9373     }    
       
  9374     
       
  9375     if (options.hasOwnProperty('height')) {
       
  9376       this._height = options.height;
       
  9377     } 
       
  9378     
       
  9379     /* this is a shortcut */
       
  9380     this.selector = IriSP.jQuery("#" + this._div);
       
  9381     
       
  9382     this.selector.css("width", this._width);
       
  9383     
       
  9384     if (this._height !== undefined)
       
  9385       this.selector.css("height", this._height);
       
  9386 };
       
  9387 
       
  9388 /* we need this special setter because of a chicken and egg problem :
       
  9389    we want the manager to use popcorn but the popcorn div will be managed
       
  9390    by the manager. So we need a way to set the instance the manager uses
       
  9391 */
       
  9392    
       
  9393 IriSP.LayoutManager.prototype.setPopcornInstance = function(popcorn) {
       
  9394     this._Popcorn = popcorn;
       
  9395 }
       
  9396 
       
  9397 /* stem is a string to append to the id of the widget */
       
  9398 IriSP.LayoutManager.prototype.createDiv = function(stem) {
       
  9399     if (typeof(stem) === "undefined")
       
  9400        stem = "";
       
  9401 
       
  9402     var newDiv = Popcorn.guid(this._div + "_widget_" + stem + "_");
       
  9403     var spacerDiv = Popcorn.guid("LdtPlayer_spacer_");
       
  9404     this._widgets.push(newDiv);
       
  9405 
       
  9406     var divTempl = "<div id='{{id}}' style='width: {{width}}px; position: relative;'></div";
       
  9407     var spacerTempl = "<div id='{{spacer_id}}' style='width: {{width}}px; position: relative; height: {{spacer_div_height}};'></div";
       
  9408     
       
  9409     var divCode = Mustache.to_html(divTempl, {id: newDiv, width: this._width});
       
  9410     var spacerCode = Mustache.to_html(spacerTempl, {spacer_id: spacerDiv, width: this._width,
       
  9411                                                     spacer_div_height: IriSP.widgetsDefaults.LayoutManager.spacer_div_height });
       
  9412 
       
  9413     this.selector.append(divCode);
       
  9414     this.selector.append(spacerCode);
       
  9415 
       
  9416     return [newDiv, spacerDiv];
       
  9417 };
       
  9418 /* init.js - initialization and configuration of Popcorn and the widgets
       
  9419 exemple json configuration:
       
  9420  
       
  9421  */
       
  9422 
       
  9423 IriSP.setupDataLoader = function() {
       
  9424   /* we set it up separately because we need to
       
  9425      get data at the very beginning, for instance when
       
  9426      setting up the video */
       
  9427   IriSP.__dataloader = new IriSP.DataLoader();
       
  9428 };
       
  9429 
       
  9430 IriSP.configurePopcorn = function (layoutManager, options) {
       
  9431     var pop;
       
  9432     var ret = layoutManager.createDiv(); 
       
  9433     var containerDiv = ret[0];
       
  9434     
       
  9435     switch(options.type) {
       
  9436       /*
       
  9437         todo : dynamically create the div/video tag which
       
  9438         will contain the video.
       
  9439       */
       
  9440       case "html5":
       
  9441            var tmpId = Popcorn.guid("video"); 
       
  9442            IriSP.jQuery("#" + containerDiv).append("<video src='" + options.file + "' id='" + tmpId + "'></video>");
       
  9443 
       
  9444            if (options.hasOwnProperty("width"))
       
  9445              IriSP.jQuery("#" + containerDiv).css("width", options.width);
       
  9446            
       
  9447            if (options.hasOwnProperty("height"))
       
  9448              IriSP.jQuery("#" + containerDiv).css("height", options.height);
       
  9449 
       
  9450            pop = Popcorn("#" + tmpId).mediafragment({start : 0});
       
  9451         break;
       
  9452         
       
  9453       case "jwplayer":
       
  9454           var opts = IriSP.jQuery.extend({}, options);
       
  9455           delete opts.container;
       
  9456 
       
  9457           if (options.provider === "rtmp") {
       
  9458             /* exit if we can't access the metadata */
       
  9459             if (typeof(IriSP.__jsonMetadata) === "undefined") {
       
  9460                 break;
       
  9461             };
       
  9462 
       
  9463 
       
  9464             // the json format is totally illogical
       
  9465             opts.streamer = IriSP.__jsonMetadata["medias"][0]["meta"]["item"]["value"];
       
  9466             var source = IriSP.__jsonMetadata["medias"][0]["href"];
       
  9467 
       
  9468             // the source if a full url but jwplayer wants an url relative to the
       
  9469             // streamer url, so we've got to remove the common part.
       
  9470             opts.file = source.slice(opts.streamer.length);
       
  9471           } else {
       
  9472             /* other providers type, video for instance -
       
  9473                pass everything as is */
       
  9474           }
       
  9475 
       
  9476           pop = IriSP.PopcornReplacement.jwplayer("#" + containerDiv, opts);
       
  9477         break;
       
  9478       
       
  9479       case "youtube":
       
  9480           var opts = IriSP.jQuery.extend({}, options);
       
  9481           delete opts.container;
       
  9482           opts.controls = 0;
       
  9483           opts.autostart = false;
       
  9484           templ = "width: {{width}}px; height: {{height}}px;";
       
  9485           var str = Mustache.to_html(templ, {width: opts.width, height: opts.height});    
       
  9486           // Popcorn.youtube wants us to specify the size of the player in the style attribute of its container div.
       
  9487           IriSP.jQuery("#" + containerDiv).attr("style", str);
       
  9488           
       
  9489           pop = Popcorn.youtube("#" + containerDiv, opts.video, opts).mediafragment({start : 0});
       
  9490         break;
       
  9491         
       
  9492       default:
       
  9493         pop = undefined;
       
  9494     };
       
  9495     
       
  9496     return pop;
       
  9497 };
       
  9498 
       
  9499 IriSP.configureWidgets = function (popcornInstance, layoutManager, guiOptions) {
       
  9500  
       
  9501   var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader);
       
  9502   var params = {width: guiOptions.width, height: guiOptions.height};
       
  9503 
       
  9504   var ret_widgets = [];
       
  9505   var index;
       
  9506   
       
  9507   for (index = 0; index < guiOptions.widgets.length; index++) {    
       
  9508     var widgetConfig = guiOptions.widgets[index];
       
  9509     var widget = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, widgetConfig);
       
  9510     ret_widgets.push(widget);
       
  9511    
       
  9512   };
       
  9513 
       
  9514   return ret_widgets;
       
  9515 };
       
  9516 
       
  9517 IriSP.configureModules = function (popcornInstance, modulesList) {
       
  9518  
       
  9519   var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader);
       
  9520   var ret_modules = [];
       
  9521   var index;
       
  9522   
       
  9523   for (index = 0; index < modulesList.length; index++) {    
       
  9524     var moduleConfig = modulesList[index];
       
  9525     
       
  9526     var serializer = serialFactory.getSerializer(moduleConfig.metadata);
       
  9527     var module = new IriSP[moduleConfig.type](popcornInstance, moduleConfig, serializer);    
       
  9528     ret_modules.push(module);
       
  9529   };
       
  9530 
       
  9531   return ret_modules;
       
  9532 };
       
  9533 
       
  9534 IriSP.instantiateWidget = function(popcornInstance, serialFactory, layoutManager, widgetConfig) {
       
  9535     /* create div returns us a container for the widget and a spacer */
       
  9536     var ret = layoutManager.createDiv(widgetConfig.type);        
       
  9537     var container = ret[0];
       
  9538     var spacer = ret[1];
       
  9539 
       
  9540     var arr = IriSP.jQuery.extend({}, widgetConfig);
       
  9541     arr.container = container;
       
  9542     arr.spacer = spacer;
       
  9543     
       
  9544     var serializer = serialFactory.getSerializer(widgetConfig.metadata);    
       
  9545     
       
  9546     if (typeof serializer == "undefined")   
       
  9547       debugger;
       
  9548     
       
  9549     // instantiate the object passed as a string
       
  9550     var widget = new IriSP[widgetConfig.type](popcornInstance, arr, serializer);    
       
  9551     
       
  9552     if (widgetConfig.hasOwnProperty("requires")) {
       
  9553       // also create the widgets this one depends on.
       
  9554       // the dependency widget is available in the parent widget context as
       
  9555       // this.WidgetName (for instance, this.TipWidget);
       
  9556       
       
  9557       var i = 0;
       
  9558       for(i = 0; i < widgetConfig.requires.length; i++) {
       
  9559         var widgetName = widgetConfig.requires[i]["type"];
       
  9560         widget[widgetName] = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, widgetConfig.requires[i]);
       
  9561       }
       
  9562     }       
       
  9563      
       
  9564     serializer.sync(IriSP.wrap(widget, function() { this.draw(); }));
       
  9565     return widget;
       
  9566 };
       
  9567 /* mediafragment module */
       
  9568 
       
  9569 IriSP.MediaFragment = function(Popcorn, config, Serializer) {
       
  9570   IriSP.Module.call(this, Popcorn, config, Serializer);
       
  9571 
       
  9572   this.mutex = false; /* a mutex because we access the url from two different functions */
       
  9573 
       
  9574   this._Popcorn.listen( "loadedmetadata", IriSP.wrap(this, IriSP.MediaFragment.advanceTime));
       
  9575   this._Popcorn.listen( "pause", IriSP.wrap(this, IriSP.MediaFragment.updateTime));
       
  9576   this._Popcorn.listen( "seeked", IriSP.wrap(this, IriSP.MediaFragment.updateTime));
       
  9577   this._Popcorn.listen( "IriSP.PolemicTweet.click", IriSP.wrap(this, IriSP.MediaFragment.updateAnnotation));
       
  9578   this._Popcorn.listen( "IriSP.SegmentsWidget.click", IriSP.wrap(this, IriSP.MediaFragment.updateAnnotation));
       
  9579 };
       
  9580 
       
  9581 IriSP.MediaFragment.advanceTime = function() {
       
  9582              var url = window.location.href;
       
  9583 
       
  9584               if ( url.split( "#" )[ 1 ] != null ) {
       
  9585                   pageoffset = url.split( "#" )[1];
       
  9586 
       
  9587                   if ( pageoffset.substring(0, 2) === "t=") {
       
  9588                     // timecode 
       
  9589                     if ( pageoffset.substring( 2 ) != null ) {
       
  9590                     var offsettime = pageoffset.substring( 2 );
       
  9591                     this._Popcorn.currentTime( parseFloat( offsettime ) );
       
  9592                     }
       
  9593                   } else if ( pageoffset.substring(0, 2) === "a=") {
       
  9594                     // annotation
       
  9595                     var annotationId = pageoffset.substring( 2 );
       
  9596 
       
  9597                     // there's no better way than that because
       
  9598                     // of possible race conditions
       
  9599                     this._serializer.sync(IriSP.wrap(this, function() {
       
  9600                           IriSP.MediaFragment.lookupAnnotation.call(this, annotationId); 
       
  9601                           }));
       
  9602                   }
       
  9603               }
       
  9604 };
       
  9605 
       
  9606 IriSP.MediaFragment.updateTime = function() {
       
  9607   if (this.mutex === true) {
       
  9608     return;
       
  9609   }
       
  9610 
       
  9611   var history = window.history;
       
  9612   if ( !history.pushState ) {
       
  9613     return false;
       
  9614   }
       
  9615   
       
  9616   splitArr = window.location.href.split( "#" )
       
  9617   history.replaceState( {}, "", splitArr[0] + "#t=" + this._Popcorn.currentTime().toFixed( 2 ) );
       
  9618 };
       
  9619 
       
  9620 
       
  9621 IriSP.MediaFragment.updateAnnotation = function(annotationId) {
       
  9622   var _this = this;
       
  9623   this.mutex = true;
       
  9624 
       
  9625   var history = window.history;
       
  9626   if ( !history.pushState ) {
       
  9627     return false;
       
  9628   }
       
  9629   
       
  9630   splitArr = window.location.href.split( "#" )
       
  9631   history.replaceState( {}, "", splitArr[0] + "#a=" + annotationId);
       
  9632  
       
  9633   window.setTimeout(function() { _this.mutex = false }, 50);
       
  9634 };
       
  9635 
       
  9636 // lookup and seek to the beginning of an annotation
       
  9637 IriSP.MediaFragment.lookupAnnotation = function(annotationId) {
       
  9638   var annotation = undefined;
       
  9639   var annotations = this._serializer._data.annotations;
       
  9640 
       
  9641   var i;
       
  9642   for (i = 0; i < annotations.length; i++) {
       
  9643       if (annotations[i].id === annotationId) {
       
  9644         annotation = annotations[i];
       
  9645         break;
       
  9646       }
       
  9647   }
       
  9648 
       
  9649   if (typeof(annotation) !== "undefined") {
       
  9650     this._Popcorn.currentTime(annotation.begin / 1000);
       
  9651   }
       
  9652 };
       
  9653 IriSP.AnnotationsWidget = function(Popcorn, config, Serializer) {
       
  9654   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
  9655   
       
  9656 };
       
  9657 
       
  9658 
       
  9659 IriSP.AnnotationsWidget.prototype = new IriSP.Widget();
       
  9660 
       
  9661 IriSP.AnnotationsWidget.prototype.clear = function() {
       
  9662     this.selector.find(".Ldt-SaTitle").text("");
       
  9663     this.selector.find(".Ldt-SaDescription").text("");
       
  9664     this.selector.find(".Ldt-SaKeywordText").text("");
       
  9665 };
       
  9666 
       
  9667 IriSP.AnnotationsWidget.prototype.displayAnnotation = function(annotation) {
       
  9668 
       
  9669     var title = annotation.content.title;
       
  9670     var description = annotation.content.description;
       
  9671     var keywords =  "" // FIXME;
       
  9672     var begin = +annotation.begin / 1000;
       
  9673     var end = +annotation.end / 1000;
       
  9674     var duration = +this._serializer.currentMedia().meta["dc:duration"];
       
  9675 
       
  9676     var title_templ = "{{title}} - ( {{begin}} - {{end}} )";
       
  9677     var endstr = Mustache.to_html(title_templ, {title: title, begin: IriSP.secondsToTime(begin), end: IriSP.secondsToTime(end)});
       
  9678 
       
  9679     this.selector.find(".Ldt-SaTitle").text(endstr);
       
  9680     this.selector.find(".Ldt-SaDescription").text(description);
       
  9681 };
       
  9682 
       
  9683 IriSP.AnnotationsWidget.prototype.clearWidget = function() {
       
  9684 
       
  9685     
       
  9686     /* retract the pane between two annotations */
       
  9687     this.selector.find(".Ldt-SaTitle").text("");
       
  9688     this.selector.find(".Ldt-SaDescription").text("");
       
  9689     this.selector.find(".Ldt-SaKeywordText").html("");
       
  9690     this.selector.find(".Ldt-ShowAnnotation").slideUp();
       
  9691 };
       
  9692 
       
  9693 IriSP.AnnotationsWidget.prototype.draw = function() {
       
  9694   var _this = this;
       
  9695 
       
  9696   var annotationMarkup = IriSP.templToHTML(IriSP.annotationWidget_template);
       
  9697 	this.selector.append(annotationMarkup);
       
  9698   var view;
       
  9699 
       
  9700   if (typeof(this._serializer._data.views) !== "undefined" && this._serializer._data.views !== null)
       
  9701      view = this._serializer._data.views[0];
       
  9702 
       
  9703   var view_type = "";
       
  9704 
       
  9705   if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
       
  9706           view_type = view.annotation_types[0];
       
  9707   }
       
  9708  
       
  9709   var annotations = this._serializer._data.annotations;
       
  9710   var i;
       
  9711   
       
  9712 	for (i in annotations) {    
       
  9713     var annotation = annotations[i];
       
  9714     var begin = Math.round((+ annotation.begin) / 1000);
       
  9715     var end = Math.round((+ annotation.end) / 1000);
       
  9716 
       
  9717     if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
       
  9718           && annotation.meta["id-ref"] != view_type) {
       
  9719         continue;
       
  9720     }
       
  9721 
       
  9722 
       
  9723     var conf = {start: begin, end: end, 
       
  9724                 onStart: 
       
  9725                        function(annotation) { 
       
  9726                         return function() { 
       
  9727                             _this.displayAnnotation(annotation); 
       
  9728                           
       
  9729                         } }(annotation),
       
  9730                 onEnd: 
       
  9731                        function() { _this.clearWidget.call(_this); },
       
  9732                 };
       
  9733     this._Popcorn = this._Popcorn.code(conf);                                             
       
  9734   }
       
  9735 
       
  9736 };
       
  9737 IriSP.ArrowWidget = function(Popcorn, config, Serializer) {
       
  9738   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
  9739 
       
  9740   this._oldAnnotation = null;
       
  9741   
       
  9742 };
       
  9743 
       
  9744 
       
  9745 IriSP.ArrowWidget.prototype = new IriSP.Widget();
       
  9746 
       
  9747 IriSP.ArrowWidget.prototype.clear = function() {
       
  9748 
       
  9749 };
       
  9750 
       
  9751 IriSP.ArrowWidget.prototype.clearWidget = function() {
       
  9752 };
       
  9753 
       
  9754 IriSP.ArrowWidget.prototype.draw = function() {
       
  9755   var templ = Mustache.to_html(IriSP.arrowWidget_template, {});
       
  9756   this.selector.append(templ);
       
  9757   this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
       
  9758 };
       
  9759 
       
  9760 IriSP.ArrowWidget.prototype.timeUpdateHandler = function(percents) {
       
  9761   var currentTime = this._Popcorn.currentTime();
       
  9762   var currentAnnotation = this._serializer.currentAnnotations(currentTime)[0]; // FIXME : use the others ?
       
  9763 
       
  9764   /* move the arrow only if the current annotation changes */
       
  9765   if (currentAnnotation != this._oldAnnotation) {
       
  9766     var begin = (+ currentAnnotation.begin) / 1000;
       
  9767     var end = (+ currentAnnotation.end) / 1000;
       
  9768 
       
  9769     var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
  9770     var middle_time = (begin + end) / 2;
       
  9771     var percents = Math.floor((middle_time / duration) * 100);
       
  9772 
       
  9773     // we need to apply a fix because the arrow has a certain length
       
  9774     // it's half the length of the arrow (27 / 2). We need to convert
       
  9775     // it in percents though.
       
  9776     var totalWidth = this.selector.width();
       
  9777     var correction = ((27 / 2) / totalWidth) * 100;
       
  9778     var corrected_percents = percents - correction;
       
  9779 
       
  9780     /* don't move out of the screen */
       
  9781     if (corrected_percents <= 0)
       
  9782       corrected_percents = 0;
       
  9783 
       
  9784     this.selector.children(".Ldt-arrowWidget").animate({"left" : corrected_percents + "%"});
       
  9785 
       
  9786     this._oldAnnotation = currentAnnotation;
       
  9787   }
       
  9788 }
       
  9789 IriSP.PlayerWidget = function(Popcorn, config, Serializer) {
       
  9790   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
  9791   
       
  9792   this._searchBlockOpen = false;
       
  9793   this._searchLastValue = "";
       
  9794 };
       
  9795 
       
  9796 IriSP.PlayerWidget.prototype = new IriSP.Widget();
       
  9797 
       
  9798 IriSP.PlayerWidget.prototype.draw = function() {
       
  9799   var self = this;
       
  9800   var width = this.width;
       
  9801 	var height = this.height;
       
  9802 	var heightS = this.height-20;
       
  9803 	  
       
  9804 	var Player_templ = Mustache.to_html(IriSP.player_template, {"share_template" : IriSP.share_template});
       
  9805   this.selector.append(Player_templ);		
       
  9806 	
       
  9807   this.selector.children(".Ldt-controler").show();
       
  9808     
       
  9809   // handle clicks by the user on the video.
       
  9810   this._Popcorn.listen("play", IriSP.wrap(this, this.playButtonUpdater));
       
  9811   this._Popcorn.listen("pause", IriSP.wrap(this, this.playButtonUpdater));
       
  9812   
       
  9813   this._Popcorn.listen("volumechange", IriSP.wrap(this, this.muteButtonUpdater));
       
  9814 
       
  9815   this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeDisplayUpdater));
       
  9816   this._Popcorn.listen("IriSP.search.matchFound", IriSP.wrap(this, this.searchMatch));
       
  9817   this._Popcorn.listen("IriSP.search.noMatchFound", IriSP.wrap(this, this.searchNoMatch));
       
  9818   
       
  9819   
       
  9820   this.selector.find(".Ldt-CtrlPlay").click(function() { self.playHandler.call(self); });
       
  9821   this.selector.find(".Ldt-CtrlNext").click(function() { });
       
  9822   this.selector.find(".Ldt-CtrlSearch").click(function() { self.searchButtonHandler.call(self); });
       
  9823   
       
  9824   this.selector.find('.Ldt-CtrlSound').click(function() { self.muteHandler.call(self); } );
       
  9825 
       
  9826   this.selector.find(".Ldt-CtrlPlay").attr( "style", "background-color:#CD21C24;" );
       
  9827   
       
  9828   var searchButtonPos = this.selector.find(".Ldt-CtrlSearch").position();
       
  9829   var searchBox = Mustache.to_html(IriSP.search_template, {margin_left : searchButtonPos.left + "px"});
       
  9830   this.selector.append(searchBox);
       
  9831   
       
  9832   // trigger an IriSP.PlayerWidget.MouseOver to the widgets that are interested (i.e : sliderWidget)
       
  9833   this.selector.hover(function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOver"); }, 
       
  9834                       function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOut"); });
       
  9835  
       
  9836   this.muteButtonUpdater(); /* some player - jwplayer notable - save the state of the mute button between sessions */
       
  9837 };
       
  9838 
       
  9839 /* Update the elasped time div */
       
  9840 IriSP.PlayerWidget.prototype.timeDisplayUpdater = function() {
       
  9841   
       
  9842   if (this._previousSecond === undefined)
       
  9843     this._previousSecond = this._Popcorn.roundTime();
       
  9844   
       
  9845   else {
       
  9846     /* we're still in the same second, so it's not necessary to update time */
       
  9847     if (this._Popcorn.roundTime() == this._previousSecond)
       
  9848       return;
       
  9849       
       
  9850   }
       
  9851   
       
  9852   // we get it at each call because it may change.
       
  9853   var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000; 
       
  9854   var totalTime = IriSP.secondsToTime(duration);
       
  9855   var elapsedTime = IriSP.secondsToTime(this._Popcorn.currentTime());
       
  9856   
       
  9857   this.selector.find(".Ldt-ElapsedTime").html(elapsedTime.toString());
       
  9858   this.selector.find(".Ldt-TotalTime").html(totalTime.toString());
       
  9859   this._previousSecond = this._Popcorn.roundTime();
       
  9860 };
       
  9861 
       
  9862 /* update the icon of the button - separate function from playHandler
       
  9863    because in some cases (for instance, when the user directly clicks on
       
  9864    the jwplayer window) we have to change the icon without playing/pausing
       
  9865 */
       
  9866 IriSP.PlayerWidget.prototype.playButtonUpdater = function() {
       
  9867   var status = this._Popcorn.media.paused;
       
  9868   
       
  9869   if ( status == true ){        
       
  9870     this.selector.find(".Ldt-CtrlPlay").attr("title", "Play");
       
  9871    
       
  9872     // we use templToHTML because it has some predefined
       
  9873     // vars like where to get the images
       
  9874     var templ = IriSP.templToHTML("url({{img_dir}}/play_sprite.png)");
       
  9875     this.selector.find(".Ldt-CtrlPlay").css("background-image", templ);
       
  9876 
       
  9877   } else {
       
  9878     this.selector.find(".Ldt-CtrlPlay").attr("title", "Pause");
       
  9879 
       
  9880     // we use templToHTML because it has some predefined
       
  9881     // vars like where to get the images
       
  9882     var templ = IriSP.templToHTML("url({{img_dir}}/pause_sprite.png)");
       
  9883     this.selector.find(".Ldt-CtrlPlay").css("background-image", templ);
       
  9884   }  
       
  9885 
       
  9886   return;
       
  9887 };
       
  9888 
       
  9889 
       
  9890 IriSP.PlayerWidget.prototype.playHandler = function() {
       
  9891   var status = this._Popcorn.media.paused;
       
  9892   
       
  9893   if ( status == true ){        
       
  9894     this._Popcorn.play();   
       
  9895   } else {
       
  9896     this._Popcorn.pause();
       
  9897   }  
       
  9898 };
       
  9899 
       
  9900 IriSP.PlayerWidget.prototype.muteHandler = function() {
       
  9901   if (!this._Popcorn.muted()) {    
       
  9902       this._Popcorn.mute(true);
       
  9903     } else {
       
  9904       this._Popcorn.mute(false);
       
  9905     }
       
  9906 };
       
  9907 
       
  9908 IriSP.PlayerWidget.prototype.muteButtonUpdater = function() {
       
  9909   var status = this._Popcorn.media.muted;
       
  9910   
       
  9911   if ( status == true ){        
       
  9912     this.selector.find(".Ldt-CtrlSound").attr("title", "Unmute");
       
  9913    
       
  9914     // we use templToHTML because it has some predefined
       
  9915     // vars like where to get the images
       
  9916     var templ = IriSP.templToHTML("url({{img_dir}}/sound_sprite.png)");
       
  9917     this.selector.find(".Ldt-CtrlSound").css("background-image", templ);
       
  9918 
       
  9919   } else {
       
  9920     this.selector.find(".Ldt-CtrlSound").attr("title", "Mute");
       
  9921 
       
  9922     // we use templToHTML because it has some predefined
       
  9923     // vars like where to get the images
       
  9924     var templ = IriSP.templToHTML("url({{img_dir}}/mute_sprite.png)");
       
  9925     this.selector.find(".Ldt-CtrlSound").css("background-image", templ);
       
  9926   }  
       
  9927 
       
  9928   return;
       
  9929 };
       
  9930 
       
  9931 
       
  9932 IriSP.PlayerWidget.prototype.searchButtonHandler = function() {
       
  9933     var self = this;
       
  9934 
       
  9935     /* show the search field if it is not shown */
       
  9936   	if ( this._searchBlockOpen == false ) {      
       
  9937       this.selector.find(".LdtSearch").show(100);
       
  9938       
       
  9939       this.selector.find(".LdtSearchInput").css('background-color','#fff');
       
  9940       this.selector.find(".LdtSearchInput").focus();
       
  9941       this.selector.find(".LdtSearchInput").attr('value', this._searchLastValue);      
       
  9942       this._Popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural.
       
  9943       
       
  9944       this._searchBlockOpen = true;           
       
  9945       this.selector.find(".LdtSearchInput").bind('keyup', null, function() { self.searchHandler.call(self); } );
       
  9946       
       
  9947       // we need this variable because some widget can find a match in
       
  9948       // their data while at the same time other's don't. As we want the
       
  9949       // search field to become green when there's a match, we need a 
       
  9950       // variable to remember that we had one.
       
  9951       this._positiveMatch = false;
       
  9952 
       
  9953       // tell the world the field is open
       
  9954       this._Popcorn.trigger("IriSP.search.open");
       
  9955       
       
  9956 	} else {
       
  9957       this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value');
       
  9958       this.selector.find(".LdtSearchInput").attr('value','');
       
  9959       this.selector.find(".LdtSearch").hide(100);
       
  9960       
       
  9961       // unbind the watcher event.
       
  9962       this.selector.find(".LdtSearchInput").unbind('keypress set');
       
  9963       this._searchBlockOpen = false;
       
  9964 
       
  9965       this._positiveMatch = false;
       
  9966       
       
  9967       this._Popcorn.trigger("IriSP.search.closed");
       
  9968   }
       
  9969 };
       
  9970 
       
  9971 /* this handler is called whenever the content of the search
       
  9972    field changes */
       
  9973 IriSP.PlayerWidget.prototype.searchHandler = function() {
       
  9974   this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value');
       
  9975   this._positiveMatch = false;
       
  9976   
       
  9977   // do nothing if the search field is empty, instead of highlighting everything.
       
  9978   if (this._searchLastValue == "") {
       
  9979     this._Popcorn.trigger("IriSP.search.cleared");
       
  9980     this.selector.find(".LdtSearchInput").css('background-color','');
       
  9981   } else {
       
  9982     this._Popcorn.trigger("IriSP.search", this._searchLastValue);
       
  9983   }
       
  9984 };
       
  9985 
       
  9986 /*
       
  9987   handler for the IriSP.search.found message, which is sent by some views when they
       
  9988   highlight a match.
       
  9989 */
       
  9990 IriSP.PlayerWidget.prototype.searchMatch = function() {
       
  9991   this._positiveMatch = true;
       
  9992   this.selector.find(".LdtSearchInput").css('background-color','#e1ffe1');
       
  9993 }
       
  9994 
       
  9995 /* the same, except that no value could be found */
       
  9996 IriSP.PlayerWidget.prototype.searchNoMatch = function() {
       
  9997   if (this._positiveMatch !== true)
       
  9998     this.selector.find(".LdtSearchInput").css('background-color', "#d62e3a");
       
  9999 }
       
 10000 
       
 10001 /* 
       
 10002  *   
       
 10003  *  Copyright 2010 Institut de recherche et d'innovation 
       
 10004  *  contributor(s) : Samuel Huron 
       
 10005  *   
       
 10006  *  contact@iri.centrepompidou.fr
       
 10007  *  http://www.iri.centrepompidou.fr 
       
 10008  *   
       
 10009  *  This software is a computer program whose purpose is to show and add annotations on a video .
       
 10010  *  This software is governed by the CeCILL-C license under French law and
       
 10011  *  abiding by the rules of distribution of free software. You can  use, 
       
 10012  *  modify and/ or redistribute the software under the terms of the CeCILL-C
       
 10013  *  license as circulated by CEA, CNRS and INRIA at the following URL
       
 10014  *  "http://www.cecill.info". 
       
 10015  *  
       
 10016  *  The fact that you are presently reading this means that you have had
       
 10017  *  knowledge of the CeCILL-C license and that you accept its terms.
       
 10018 */
       
 10019 // CHART TIMELINE / VERSION PROTOTYPE  ::
       
 10020 
       
 10021 IriSP.PolemicWidget = function(Popcorn, config, Serializer) {
       
 10022   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
 10023  
       
 10024   this.userPol    = new Array();
       
 10025   this.userNoPol  = new Array();
       
 10026   this.userst      = new Array();
       
 10027   this.numberOfTweet = 0;
       
 10028   this.Users;
       
 10029   this.TweetPolemic;
       
 10030   this.yMax        = this.height; 
       
 10031   this.PaperSlider;
       
 10032   this.heightOfChart;
       
 10033   this.tweets  = new Array();
       
 10034   this.svgElements = {};
       
 10035   
       
 10036   // Make and define the Raphael area
       
 10037   this.paper = Raphael(document.getElementById(this._id), config.width, config.height);
       
 10038   
       
 10039   this.oldSearchMatches = [];
       
 10040 
       
 10041   // event handlers
       
 10042   this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); }));
       
 10043   this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler));
       
 10044   this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler));
       
 10045 
       
 10046 };
       
 10047 
       
 10048 IriSP.PolemicWidget.prototype = new IriSP.Widget();
       
 10049   
       
 10050 IriSP.PolemicWidget.prototype.draw = function() {
       
 10051   
       
 10052     // variable 
       
 10053     // yMax
       
 10054     
       
 10055     var self = this;
       
 10056     var yCoef        = 2;             // coef for height of 1 tweet 
       
 10057     var frameSize     = 5;             // frame size 
       
 10058     var margin         = 1;            // marge between frame
       
 10059     var lineSize      = this.width;        // timeline pixel width 
       
 10060     var nbrframes     = lineSize/frameSize;     // frame numbers
       
 10061     var numberOfTweet   = 0;            // number of tweet overide later 
       
 10062     var duration      = +this._serializer.currentMedia().meta["dc:duration"];      // timescale width 
       
 10063     var frameLength   = lineSize / frameSize;    // frame timescale  
       
 10064     var timeline;
       
 10065     var colors  = new Array("","#1D973D","#C5A62D","#CE0A15","#036AAE","#585858");
       
 10066     
       
 10067     // array 
       
 10068     //var tweets  = new Array();
       
 10069     var element = new Array();
       
 10070     var cluster = new Array();
       
 10071     var frames  = new Array(frameLength);
       
 10072     var slices  = new Array();
       
 10073     
       
 10074     
       
 10075     // Classes =======================================================================
       
 10076     var Frames = function(){
       
 10077       
       
 10078       var Myclusters;
       
 10079       var x;
       
 10080       var y;
       
 10081       var width;
       
 10082       var height;
       
 10083     };
       
 10084     Frames = function(json){
       
 10085       // make my clusters
       
 10086       // ou Frame vide 
       
 10087     };
       
 10088     Frames.prototype.draw = function(){
       
 10089     };
       
 10090     Frames.prototype.zoom = function(){
       
 10091     };
       
 10092     Frames.prototype.inside = function(){
       
 10093     };
       
 10094     var Clusters = function(){
       
 10095       var Object;
       
 10096       var yDist;
       
 10097       var x;
       
 10098       var y;
       
 10099       var width;
       
 10100       var height;
       
 10101     };
       
 10102     Clusters = function(json){
       
 10103       // make my object
       
 10104     };
       
 10105     var Tweet = function(){
       
 10106     };
       
 10107     // Classes =======================================================================
       
 10108 
       
 10109     // Refactoring (parametere) ************************************************************
       
 10110     // color translastion
       
 10111     var qTweet_0  =0;
       
 10112     var qTweet_Q  =0;
       
 10113     var qTweet_REF=0;
       
 10114     var qTweet_OK =0;
       
 10115     var qTweet_KO =0;
       
 10116     function colorTranslation(value){
       
 10117       if(value == "Q"){
       
 10118         qTweet_Q+=1;
       
 10119         return 2;
       
 10120       }else if(value =="REF"){
       
 10121         qTweet_REF+=1;
       
 10122         return 4;
       
 10123       }else if(value =="OK"){
       
 10124         qTweet_OK+=1;
       
 10125         return 1;
       
 10126       }else if(value =="KO"){
       
 10127         qTweet_KO+=1;
       
 10128         return 3;
       
 10129       }else if(value ==""){
       
 10130         qTweet_0+=1;
       
 10131         return 5;
       
 10132       }
       
 10133     }
       
 10134     
       
 10135 
       
 10136       this._serializer.sync(function(data) { loaded_callback.call(self, data) });
       
 10137       
       
 10138       function loaded_callback (json) {
       
 10139 
       
 10140         // get current view (the first ???)
       
 10141         view = json.views[0];
       
 10142         
       
 10143         // the tweets are by definition of the second annotation type FIXME ?
       
 10144         tweet_annot_type = null;
       
 10145         if(typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
       
 10146           tweet_annot_type = view.annotation_types[1];
       
 10147         }
       
 10148       
       
 10149       for(var i = 0; i < json.annotations.length; i++) {
       
 10150         var item = json.annotations[i];        
       
 10151         var MyTime  = Math.floor(item.begin/duration*lineSize);
       
 10152         var Myframe = Math.floor(MyTime/lineSize*frameLength);
       
 10153 
       
 10154         if (typeof(item.meta) !== "undefined" 
       
 10155           && typeof(item.meta["id-ref"]) !== "undefined"
       
 10156           && item.meta["id-ref"] === tweet_annot_type) {
       
 10157             
       
 10158           var MyTJson = JSON.parse(item.meta['dc:source']['content']);
       
 10159           
       
 10160             if (item.content['polemics'] != undefined 
       
 10161             && item.content['polemics'][0] != null) {
       
 10162             
       
 10163               // a tweet can have many polemics at the same time.
       
 10164               for(var j=0; j<item.content['polemics'].length; j++){
       
 10165                   
       
 10166                   this.tweets[numberOfTweet] = {
       
 10167                         id:i,
       
 10168                         qualification:colorTranslation(item.content['polemics'][j]),
       
 10169                         yIndicator:MyTime,
       
 10170                         yframe:Myframe,
       
 10171                         title:item.content['title'],
       
 10172                         timeframe:item.begin,
       
 10173                         userId: MyTJson.id,
       
 10174                         userScreenName: MyTJson.screen_name,
       
 10175                         tsource:MyTJson,
       
 10176                         cinecast_id: item.id
       
 10177                         };
       
 10178                   numberOfTweet+=1;
       
 10179                   
       
 10180               }
       
 10181           }
       
 10182           else {
       
 10183             this.tweets[numberOfTweet] = {
       
 10184                   id:i,
       
 10185                   qualification:colorTranslation(""),
       
 10186                   yIndicator:MyTime,
       
 10187                   yframe:Myframe,
       
 10188                   title:item.content['title'],
       
 10189                   timeframe:item.begin,
       
 10190                   userId: MyTJson.id,
       
 10191                   userScreenName: MyTJson.screen_name,
       
 10192                   tsource:MyTJson,
       
 10193                   cinecast_id: item.id
       
 10194             };
       
 10195             numberOfTweet+=1;
       
 10196           }
       
 10197           
       
 10198         } 
       
 10199       };  
       
 10200       
       
 10201        DrawTweets.call (this); // FIXME: ugly.
       
 10202        
       
 10203       };      
       
 10204 
       
 10205     // tweet Drawing (in raphael) 
       
 10206     function DrawTweets (){
       
 10207     // GROUPES TWEET ============================================
       
 10208     // Count nbr of cluster and tweet in a frame an save int in "frames"
       
 10209       numberOfTweet = this.tweets.length;
       
 10210       for(var i=0; i<nbrframes; i++) {  
       
 10211         for(var j=0; j<numberOfTweet; j++) {  
       
 10212         
       
 10213           if (i==this.tweets[j].yframe){
       
 10214             
       
 10215             var k = this.tweets[j].qualification;
       
 10216             
       
 10217             // make array for frame cluster
       
 10218             if(frames[i]==undefined){
       
 10219               frames[i] = {id:i,
       
 10220                      qualifVol:new Array(),
       
 10221                      mytweetsID:new Array()
       
 10222                     };
       
 10223             }
       
 10224             // add my tweet to frame
       
 10225             frames[i].mytweetsID.push(this.tweets[j]);
       
 10226             
       
 10227             // count opinion by frame
       
 10228             if( frames[i].qualifVol[k] == undefined){
       
 10229               frames[i].qualifVol[k] = 1;
       
 10230             }else{
       
 10231               frames[i].qualifVol[k] += 1;
       
 10232             }
       
 10233             
       
 10234           }
       
 10235         }
       
 10236       }
       
 10237     
       
 10238     // GROUPES TWEET ============================================    
       
 10239     // max of tweet by Frame 
       
 10240       var max = 0; 
       
 10241       for(var i = 0; i < nbrframes; i++) {
       
 10242         var moy  = 0;
       
 10243         for (var j = 0; j < 6; j++) {    
       
 10244           if (frames[i] != undefined) {
       
 10245             if (frames[i].qualifVol[j] != undefined) {
       
 10246               moy += frames[i].qualifVol[j];
       
 10247             }
       
 10248           }
       
 10249         }
       
 10250         
       
 10251         if (moy > max) {
       
 10252           max = moy;
       
 10253         }
       
 10254       }
       
 10255     
       
 10256       var tweetDrawed = new Array();
       
 10257       var TweetHeight = 5;
       
 10258       
       
 10259       // DRAW  TWEETS ============================================
       
 10260       for(var i = 0; i < nbrframes; i++) {
       
 10261         var addEheight = 5;
       
 10262         if (frames[i] != undefined){                
       
 10263           // by type 
       
 10264           
       
 10265           for (var j = 6; j > -1; j--) {
       
 10266             if (frames[i].qualifVol[j] != undefined) {
       
 10267               // show tweet by type 
       
 10268               for (var k = 0; k < frames[i].mytweetsID.length; k++) {
       
 10269               
       
 10270                 if (frames[i].mytweetsID[k].qualification == j) {                
       
 10271                   var x = i * frameSize;
       
 10272                   var y = this.heightmax - addEheight;
       
 10273                   
       
 10274                   if (this.yMax > y) {
       
 10275                     this.yMax = y;
       
 10276                   }
       
 10277                   
       
 10278                   var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */)
       
 10279                                     .attr({stroke:"#00","stroke-width":0.1,  fill: colors[j]});  
       
 10280                   
       
 10281                   addEheight += TweetHeight;
       
 10282                   
       
 10283                   e.color = colors[j];
       
 10284                   e.time = frames[i].mytweetsID[k].timeframe;
       
 10285                   e.title = frames[i].mytweetsID[k].title;
       
 10286                   e.id = frames[i].mytweetsID[k].cinecast_id;
       
 10287 
       
 10288                   this.svgElements[e.id] = e;
       
 10289 
       
 10290                   e.mouseover(function(element) { return function (event) {
       
 10291                         // event.clientX and event.clientY are to raphael what event.pageX and pageY are to jquery.                        
       
 10292                         self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), event.clientX - 106, event.clientY - 160);
       
 10293                         element.displayed = true;
       
 10294                   }}(e)).mouseout(function(element) { return function () {                          
       
 10295                           self.TooltipWidget.hide.call(self.TooltipWidget);
       
 10296                   }}(e)).mousedown(function () {
       
 10297                     self._Popcorn.currentTime(this.time/1000);
       
 10298                     self._Popcorn.trigger("IriSP.PolemicTweet.click", this.id); 
       
 10299                   });
       
 10300                   
       
 10301                   IriSP.jQuery(e.node).attr('id', 't' + k + '');
       
 10302                   IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title);
       
 10303                   IriSP.jQuery(e.node).attr('begin',  frames[i].mytweetsID[k].timeframe);                  
       
 10304                 }
       
 10305               }
       
 10306             }
       
 10307           }
       
 10308         }
       
 10309 
       
 10310       }    
       
 10311       // DRAW UI :: resize border and bgd      
       
 10312       this.paperBackground = this.paper.rect(0, 0, this.width, this.heightmax).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1});  
       
 10313 
       
 10314       // outer borders
       
 10315       this.outerBorders   = [];
       
 10316       this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
       
 10317       this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
       
 10318 
       
 10319       // inner borders
       
 10320       this.innerBorders   = [];
       
 10321       this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
 10322       this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
 10323       this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8}));  
       
 10324       this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
       
 10325 
       
 10326 
       
 10327 
       
 10328       this.paperSlider   = this.paper.rect(0, 0, 0, this.heightmax).attr({fill:"#D4D5D5", stroke: "none", opacity: 1});
       
 10329       
       
 10330       // the small white line displayed over the slider.
       
 10331       this.sliderTip = this.paper.rect(0, 0, 1, this.heightmax).attr({fill:"#fc00ff", stroke: "none", opacity: 1});
       
 10332       // decalage 
       
 10333       // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1});  
       
 10334       
       
 10335       
       
 10336       this.paperSlider.toBack();
       
 10337       this.paperBackground.toBack();
       
 10338       this.sliderTip.toFront();
       
 10339     }
       
 10340     
       
 10341     this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater));
       
 10342 }
       
 10343 
       
 10344 IriSP.PolemicWidget.prototype.sliderUpdater = function() {
       
 10345 
       
 10346     var time = +this._Popcorn.currentTime();
       
 10347     var duration = +this._serializer.currentMedia().meta["dc:duration"];
       
 10348     
       
 10349     this.paperSlider.attr("width", time * (this.width / (duration / 1000)));
       
 10350         
       
 10351     this.sliderTip.attr("x", time * (this.width / (duration / 1000)));
       
 10352 };
       
 10353     
       
 10354 IriSP.PolemicWidget.prototype.searchHandler = function(searchString) {
       
 10355   if (searchString == "")
       
 10356     return;
       
 10357 
       
 10358   var matches = this._serializer.searchTweetsOccurences(searchString);
       
 10359 
       
 10360   if (IriSP.countProperties(matches) > 0) {
       
 10361     this._Popcorn.trigger("IriSP.search.matchFound");
       
 10362   } else {
       
 10363     this._Popcorn.trigger("IriSP.search.noMatchFound");
       
 10364   }
       
 10365 
       
 10366   for (var id in matches) {
       
 10367     if (this.svgElements.hasOwnProperty(id)) {
       
 10368       var e = this.svgElements[id];
       
 10369       this.svgElements[id].attr({fill: "#fc00ff"});
       
 10370     }
       
 10371   }
       
 10372 
       
 10373   // clean up the blocks that were in the previous search
       
 10374   // but who aren't in the current one.
       
 10375   for (var id in this.oldSearchMatches) {
       
 10376     if (!matches.hasOwnProperty(id)) {
       
 10377       var e = this.svgElements[id];
       
 10378       e.attr({fill: e.color});
       
 10379     }
       
 10380   }
       
 10381   
       
 10382   this.oldSearchMatches = matches;
       
 10383 };
       
 10384 
       
 10385 IriSP.PolemicWidget.prototype.searchFieldClearedHandler = function() {
       
 10386   // clean up the blocks that were in the previous search
       
 10387   // but who aren't in the current one.
       
 10388   for (var id in this.oldSearchMatches) {
       
 10389       var e = this.svgElements[id];
       
 10390       e.attr({fill: e.color});
       
 10391   }
       
 10392  
       
 10393 };
       
 10394 
       
 10395 IriSP.PolemicWidget.prototype.searchFieldClosedHandler = function() {
       
 10396   // clean up the blocks that were in the previous search
       
 10397   // but who aren't in the current one.
       
 10398   for (var id in this.oldSearchMatches) {
       
 10399       var e = this.svgElements[id];
       
 10400       e.attr({fill: e.color});
       
 10401   }
       
 10402  
       
 10403 };
       
 10404    
       
 10405 IriSP.SegmentsWidget = function(Popcorn, config, Serializer) {
       
 10406 
       
 10407   var self = this;
       
 10408   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
 10409   this.oldSearchMatches = [];
       
 10410 
       
 10411   // event handlers
       
 10412   this._Popcorn.listen("IriSP.search", function(searchString) { self.searchHandler.call(self, searchString); });
       
 10413   this._Popcorn.listen("IriSP.search.closed", function() { self.searchFieldClosedHandler.call(self); });
       
 10414   this._Popcorn.listen("IriSP.search.cleared", function() { self.searchFieldClearedHandler.call(self); });
       
 10415 };
       
 10416 
       
 10417 IriSP.SegmentsWidget.prototype = new IriSP.Widget();
       
 10418 
       
 10419 IriSP.SegmentsWidget.prototype.draw = function() {
       
 10420 
       
 10421   var self = this;
       
 10422   var annotations = this._serializer._data.annotations;
       
 10423 
       
 10424   this.selector.addClass("Ldt-SegmentsWidget");
       
 10425 
       
 10426   /* in case we have different types of annotations, we want to display only the first type */
       
 10427   /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either
       
 10428      null or undefined.
       
 10429   */
       
 10430   var view;
       
 10431 
       
 10432   if (typeof(this._serializer._data.views) !== "undefined" && this._serializer._data.views !== null)
       
 10433      view = this._serializer._data.views[0];
       
 10434 
       
 10435   var view_type = "";
       
 10436 
       
 10437   if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
       
 10438           view_type = view.annotation_types[0];
       
 10439   }
       
 10440  
       
 10441   this.selector.append(Mustache.to_html(IriSP.overlay_marker_template));
       
 10442   
       
 10443   this.positionMarker = this.selector.children(":first");
       
 10444   
       
 10445   this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.positionUpdater));
       
 10446   
       
 10447   
       
 10448   var i = 0;
       
 10449   var totalWidth = this.selector.width();
       
 10450   var onePxPercent = 100 / totalWidth; /* the value of a pixel, in percents */
       
 10451  
       
 10452   for (i = 0; i < annotations.length; i++) {
       
 10453     var annotation = annotations[i];
       
 10454 
       
 10455     /* filter the annotations whose type is not the one we want */
       
 10456     if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
       
 10457           && annotation.meta["id-ref"] != view_type) {
       
 10458         continue;
       
 10459     }
       
 10460 
       
 10461     var begin = Math.round((+ annotation.begin) / 1000);
       
 10462     var end = Math.round((+ annotation.end) / 1000);
       
 10463     var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10464     var id = annotation.id;
       
 10465     var startPourcent 	= IriSP.timeToPourcent(begin, duration);
       
 10466     
       
 10467     /* some sort of collapsing occurs, so we only have to substract one pixel to each box instead of
       
 10468        two
       
 10469     */
       
 10470     var endPourcent 	= IriSP.timeToPourcent(end, duration) - startPourcent - onePxPercent * 1;
       
 10471     
       
 10472     /* on the other hand, we have to substract one pixel from the first box because it's the only
       
 10473        one to have to effective 1px margins */
       
 10474     if (i == 0) {
       
 10475 
       
 10476       endPourcent -= onePxPercent;
       
 10477     }
       
 10478     
       
 10479     var divTitle = annotation.content.title.substr(0,55);
       
 10480     var color = annotation.content.color
       
 10481 
       
 10482 
       
 10483     var annotationTemplate = Mustache.to_html(IriSP.annotation_template,
       
 10484         {"divTitle" : divTitle, "id" : id, "startPourcent" : startPourcent,
       
 10485         "endPourcent" : endPourcent, "hexa_color" : IriSP.DEC_HEXA_COLOR(color),
       
 10486         "seekPlace" : Math.round(begin/1000)});
       
 10487 
       
 10488 
       
 10489     this.selector.append(annotationTemplate);
       
 10490 
       
 10491 //    IriSP.jQuery("#" + id).tooltip({ effect: 'slide'});
       
 10492 
       
 10493     IriSP.jQuery("#" + id).fadeTo(0, 0.3);
       
 10494 
       
 10495     IriSP.jQuery("#" + id).mouseover(
       
 10496     /* we wrap the handler in another function because js's scoping
       
 10497        rules are function-based - otherwise, the internal vars like
       
 10498        divTitle are preserved but they are looked-up from the draw
       
 10499        method scope, so after that the loop is run, so they're not
       
 10500        preserved */
       
 10501     (function(divTitle) { 
       
 10502      return function(event) {
       
 10503           IriSP.jQuery(this).animate({opacity: 0.6}, 5);
       
 10504           var offset = IriSP.jQuery(this).offset();
       
 10505           var correction = IriSP.jQuery(this).outerWidth() / 2;
       
 10506 
       
 10507           var offset_x = offset.left + correction - 106;
       
 10508           if (offset_x < 0)
       
 10509             offset_x = 0;
       
 10510 
       
 10511           self.TooltipWidget.show(divTitle, color, offset_x, event.pageY - 160);
       
 10512     } })(divTitle)).mouseout(function(){
       
 10513       IriSP.jQuery(this).animate({opacity: 0.3}, 5);
       
 10514       self.TooltipWidget.hide();
       
 10515     });
       
 10516 
       
 10517     IriSP.jQuery("#" + id).click(function(_this, annotation) {
       
 10518                                     return function() { _this.clickHandler(annotation)};
       
 10519                                  }(this, annotation));
       
 10520   }
       
 10521 };
       
 10522 
       
 10523 /* restores the view after a search */
       
 10524 IriSP.SegmentsWidget.prototype.clear = function() {
       
 10525   this.selector.children(".Ldt-iri-chapter").css('border','none').animate({opacity:0.3}, 100);
       
 10526 };
       
 10527 
       
 10528 IriSP.SegmentsWidget.prototype.clickHandler = function(annotation) {
       
 10529   this._Popcorn.trigger("IriSP.SegmentsWidget.click", annotation.id);
       
 10530   var begin = (+ annotation.begin) / 1000;
       
 10531   this._Popcorn.currentTime(Math.round(begin));
       
 10532 };
       
 10533 
       
 10534 IriSP.SegmentsWidget.prototype.searchHandler = function(searchString) {
       
 10535 
       
 10536   if (searchString == "")
       
 10537     return;
       
 10538 
       
 10539   var matches = this._serializer.searchOccurences(searchString);
       
 10540 
       
 10541   if (IriSP.countProperties(matches) > 0) {
       
 10542     this._Popcorn.trigger("IriSP.search.matchFound");
       
 10543   } else {
       
 10544     this._Popcorn.trigger("IriSP.search.noMatchFound");
       
 10545   }
       
 10546 
       
 10547   // un-highlight all the blocks
       
 10548   this.selector.children(".Ldt-iri-chapter").css("opacity", 0.1);
       
 10549  
       
 10550   // then highlight the ones with matches.
       
 10551   for (var id in matches) {
       
 10552     var factor = 0.5 + matches[id] * 0.2;
       
 10553     this.selector.find("#"+id).dequeue();
       
 10554     this.selector.find("#"+id).css('border','1px red');
       
 10555     this.selector.find("#"+id).animate({opacity:factor}, 200);
       
 10556   }
       
 10557 
       
 10558  
       
 10559   this.oldSearchMatches = matches;
       
 10560 };
       
 10561 
       
 10562 IriSP.SegmentsWidget.prototype.searchFieldClearedHandler = function() {
       
 10563   this.clear();
       
 10564 };
       
 10565 
       
 10566 IriSP.SegmentsWidget.prototype.searchFieldClosedHandler = function() {
       
 10567   this.clear();
       
 10568 };
       
 10569 
       
 10570 IriSP.SegmentsWidget.prototype.positionUpdater = function() {  
       
 10571   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10572   var time = this._Popcorn.currentTime();
       
 10573   var position 	= ((time / duration) * 100).toFixed(2);
       
 10574 
       
 10575   this.positionMarker.css("left", position + "%");  
       
 10576 };
       
 10577 IriSP.SliderWidget = function(Popcorn, config, Serializer) {
       
 10578   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
 10579 };
       
 10580 
       
 10581 IriSP.SliderWidget.prototype = new IriSP.Widget();
       
 10582 
       
 10583 IriSP.SliderWidget.prototype.draw = function() {
       
 10584   var self = this;
       
 10585 
       
 10586   this.selector.append(Mustache.to_html(IriSP.sliderWidget_template, {}));
       
 10587   this.selector.addClass("Ldt-SliderMinimized");
       
 10588 
       
 10589   this.sliderBackground = this.selector.find(".Ldt-sliderBackground");
       
 10590   this.sliderForeground = this.selector.find(".Ldt-sliderForeground");
       
 10591   this.positionMarker = this.selector.find(".Ldt-sliderPositionMarker");
       
 10592 
       
 10593 
       
 10594   // a special variable to stop methods from tinkering
       
 10595   // with the positionMarker when the user is dragging it
       
 10596   this.draggingOngoing = false;
       
 10597 
       
 10598   // another special variable used by the timeout handler to
       
 10599   // open or close the slider.
       
 10600   this.sliderMaximized = false;
       
 10601   this.timeOutId = null;
       
 10602 
       
 10603   this.positionMarker.draggable({axis: "x",
       
 10604   start: IriSP.wrap(this, this.positionMarkerDraggingStartedHandler),
       
 10605   stop: IriSP.wrap(this, this.positionMarkerDraggedHandler),
       
 10606   containment: "parent"
       
 10607   });
       
 10608 
       
 10609   this.sliderBackground.click(function(event) { self.backgroundClickHandler.call(self, event); });
       
 10610   this.sliderForeground.click(function(event) { self.foregroundClickHandler.call(self, event); });
       
 10611 
       
 10612   this.selector.hover(IriSP.wrap(this, this.mouseOverHandler), IriSP.wrap(this, this.mouseOutHandler));
       
 10613 
       
 10614   // update the positions
       
 10615   this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater));
       
 10616 
       
 10617   // special messages :
       
 10618   this._Popcorn.listen("IriSP.PlayerWidget.MouseOver", IriSP.wrap(this, this.mouseOverHandler));
       
 10619   this._Popcorn.listen("IriSP.PlayerWidget.MouseOut", IriSP.wrap(this, this.mouseOutHandler));
       
 10620 };
       
 10621 
       
 10622 /* update the slider and the position marker as time passes */
       
 10623 IriSP.SliderWidget.prototype.sliderUpdater = function() {
       
 10624   if(this.draggingOngoing)
       
 10625     return;
       
 10626 
       
 10627   var time = this._Popcorn.currentTime();
       
 10628 
       
 10629   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10630   var percent = ((time / duration) * 100).toFixed(2);
       
 10631 	this.sliderForeground.css("width", percent + "%");
       
 10632 	this.positionMarker.css("left", percent + "%");
       
 10633 
       
 10634 };
       
 10635 
       
 10636 IriSP.SliderWidget.prototype.backgroundClickHandler = function(event) {
       
 10637   /* this piece of code is a little bit convoluted - here's how it works :
       
 10638      we want to handle clicks on the progress bar and convert those to seeks in the media.
       
 10639      However, jquery only gives us a global position, and we want a number of pixels relative
       
 10640      to our container div, so we get the parent position, and compute an offset to this position,
       
 10641      and finally compute the progress ratio in the media.
       
 10642      Finally we multiply this ratio with the duration to get the correct time
       
 10643   */
       
 10644 
       
 10645   var parentOffset = this.sliderBackground.parent().offset();
       
 10646   var width = this.sliderBackground.width();
       
 10647   var relX = event.pageX - parentOffset.left;
       
 10648 
       
 10649   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10650   var newTime = ((relX / width) * duration).toFixed(2);
       
 10651 
       
 10652   this._Popcorn.currentTime(newTime);
       
 10653 };
       
 10654 
       
 10655 /* same function as the previous one, except that it handles clicks
       
 10656    on the foreground element */
       
 10657 IriSP.SliderWidget.prototype.foregroundClickHandler = function(event) {
       
 10658   var parentOffset = this.sliderForeground.parent().offset();
       
 10659   var width = this.sliderBackground.width();
       
 10660   var relX = event.pageX - parentOffset.left;
       
 10661 
       
 10662   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10663   var newTime = ((relX / width) * duration).toFixed(2);
       
 10664 
       
 10665   this._Popcorn.currentTime(newTime);
       
 10666 };
       
 10667 
       
 10668 /* handles mouse over the slider */
       
 10669 IriSP.SliderWidget.prototype.mouseOverHandler = function(event) {
       
 10670   
       
 10671   if (this.timeOutId !== null) {
       
 10672     window.clearTimeout(this.timeOutId);
       
 10673   }
       
 10674  
       
 10675   this.sliderMaximized = true;
       
 10676 
       
 10677   this.sliderBackground.animate({"height": "9px"}, 100);
       
 10678   this.sliderForeground.animate({"height": "9px"}, 100);
       
 10679   
       
 10680 //  this.selector.removeClass("Ldt-SliderMinimized");
       
 10681 //  this.selector.addClass("Ldt-SliderMaximized");
       
 10682 };
       
 10683 
       
 10684 /* handles when the mouse leaves the slider */
       
 10685 IriSP.SliderWidget.prototype.mouseOutHandler = function(event) {
       
 10686 
       
 10687   this.timeOutId = window.setTimeout(IriSP.wrap(this, this.minimizeOnTimeout),
       
 10688                                      IriSP.widgetsDefaults.SliderWidget.minimize_period);
       
 10689 };
       
 10690 
       
 10691 IriSP.SliderWidget.prototype.minimizeOnTimeout = function(event) {
       
 10692   this.sliderBackground.animate({"height": "5px"}, 100);
       
 10693   this.sliderForeground.animate({"height": "5px"}, 100);
       
 10694   this.sliderMinimized = true;
       
 10695   
       
 10696 //  this.selector.removeClass("Ldt-SliderMaximized");
       
 10697 //  this.selector.addClass("Ldt-SliderMinimized");
       
 10698 
       
 10699 };
       
 10700 
       
 10701 // called when the user starts dragging the position indicator
       
 10702 IriSP.SliderWidget.prototype.positionMarkerDraggingStartedHandler = function(event, ui) {
       
 10703   this.draggingOngoing = true;
       
 10704 };
       
 10705 
       
 10706 IriSP.SliderWidget.prototype.positionMarkerDraggedHandler = function(event, ui) {
       
 10707   var width = this.sliderBackground.width();
       
 10708   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
       
 10709   var newTime = ((ui.offset.left / width) * duration).toFixed(2);
       
 10710 
       
 10711   this._Popcorn.currentTime(newTime);
       
 10712 
       
 10713   this.draggingOngoing = false;
       
 10714 };
       
 10715 
       
 10716 /* this widget displays a small tooltip */
       
 10717 IriSP.TooltipWidget = function(Popcorn, config, Serializer) {
       
 10718   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
 10719 };
       
 10720 
       
 10721 
       
 10722 IriSP.TooltipWidget.prototype = new IriSP.Widget();
       
 10723 
       
 10724 IriSP.TooltipWidget.prototype.draw = function() {
       
 10725   var templ = Mustache.to_html(IriSP.tooltipWidget_template);
       
 10726 
       
 10727   this.selector.append(templ);
       
 10728   this.hide();
       
 10729 
       
 10730 };
       
 10731 
       
 10732 IriSP.TooltipWidget.prototype.clear = function() {
       
 10733 	this.selector.find(".tiptext").text("");
       
 10734 };
       
 10735 
       
 10736 IriSP.TooltipWidget.prototype.show = function(text, color, x, y) {
       
 10737   if (this.selector.find(".tiptext").text() == text)
       
 10738     return;
       
 10739 
       
 10740   this.selector.find(".tipcolor").css("background-color", color);
       
 10741 	this.selector.find(".tiptext").text(text);
       
 10742   this.selector.find(".tip").css("left", x).css("top", y);
       
 10743 };
       
 10744 
       
 10745 IriSP.TooltipWidget.prototype.hide = function() {
       
 10746   this.clear();
       
 10747   this.selector.find(".tip").css("left", -10000).css("top", -100000);
       
 10748 };
       
 10749 /* a widget that displays tweet - used in conjunction with the polemicWidget */
       
 10750 
       
 10751 IriSP.TweetsWidget = function(Popcorn, config, Serializer) {
       
 10752   IriSP.Widget.call(this, Popcorn, config, Serializer);
       
 10753 
       
 10754   this._displayingTweet = false;
       
 10755   this._timeoutId = undefined;  
       
 10756 };
       
 10757 
       
 10758 
       
 10759 IriSP.TweetsWidget.prototype = new IriSP.Widget();
       
 10760 
       
 10761 
       
 10762 IriSP.TweetsWidget.prototype.drawTweet = function(annotation) {
       
 10763     
       
 10764     var title = IriSP.formatTweet(annotation.content.title);
       
 10765     var img = annotation.content.img.src;
       
 10766     if (typeof(img) === "undefined" || img === "" || img === "None") {
       
 10767       img = IriSP.widgetsDefaults.TweetsWidget.default_profile_picture;
       
 10768     }
       
 10769 
       
 10770     var imageMarkup = IriSP.templToHTML("<img src='{{src}}' alt='user image'></img>", 
       
 10771                                        {src : img});
       
 10772     
       
 10773     if (typeof(annotation.meta["dc:source"].content) !== "undefined") {
       
 10774       var tweetContents = JSON.parse(annotation.meta["dc:source"].content);
       
 10775       var creator = tweetContents.user.screen_name;
       
 10776       var real_name = tweetContents.user.name;
       
 10777 
       
 10778       imageMarkup = IriSP.templToHTML("<a href='http://twitter.com/{{creator}}'><img src='{{src}}' alt='user image'></img></a>", 
       
 10779                                        {src : img, creator: creator});
       
 10780             
       
 10781       var formatted_date = new Date(tweetContents.created_at).toLocaleDateString();
       
 10782       title = IriSP.templToHTML("<a class='Ldt-tweet_userHandle' href='http://twitter.com/{{creator}}'>@{{creator}}</a> - " + 
       
 10783                                 "<div class='Ldt-tweet_realName'>{{real_name}}</div>" +
       
 10784                                 "<div class='Ldt-tweet_tweetContents'>{{{ contents }}}</div>" +
       
 10785                                 "<div class='Ldt-tweet_date'>{{ date }}</div>", 
       
 10786                                 {creator: creator, real_name: real_name, contents : title, date : formatted_date});
       
 10787 
       
 10788       this.selector.find(".Ldt-TweetReply").attr("href", "http://twitter.com/home?status=@" + creator + ":%20");
       
 10789 
       
 10790 
       
 10791       var rtText = Mustache.to_html("http://twitter.com/home?status=RT @{{creator}}: {{text}}",
       
 10792                                     {creator: creator, text: IriSP.encodeURI(annotation.content.title)});
       
 10793       this.selector.find(".Ldt-Retweet").attr("href", rtText);
       
 10794     }
       
 10795 
       
 10796     this.selector.find(".Ldt-tweetContents").html(title);
       
 10797     this.selector.find(".Ldt-tweetAvatar").html(imageMarkup);
       
 10798     this.selector.show("blind", 250); 
       
 10799 };
       
 10800 
       
 10801 IriSP.TweetsWidget.prototype.displayTweet = function(annotation) {
       
 10802   if (this._displayingTweet === false) {
       
 10803     this._displayingTweet = true;
       
 10804   } else {
       
 10805     window.clearTimeout(this._timeoutId);
       
 10806   }
       
 10807 
       
 10808   this.drawTweet(annotation);
       
 10809 
       
 10810   var time = this._Popcorn.currentTime();  
       
 10811   this._timeoutId = window.setTimeout(IriSP.wrap(this, this.clearPanel), IriSP.widgetsDefaults.TweetsWidget.tweet_display_period);
       
 10812 };
       
 10813 
       
 10814 
       
 10815 IriSP.TweetsWidget.prototype.clearPanel = function() {  
       
 10816     this._displayingTweet = false;
       
 10817     this._timeoutId = undefined;
       
 10818     this.closePanel();
       
 10819     
       
 10820 };
       
 10821 
       
 10822 IriSP.TweetsWidget.prototype.closePanel = function() {
       
 10823     if (this._timeoutId != undefined) {
       
 10824       /* we're called from the "close window" link */
       
 10825       /* cancel the timeout */
       
 10826       window.clearTimeout(this._timeoutId);
       
 10827       this._timeoutId = null;
       
 10828     }
       
 10829     
       
 10830     this.selector.hide("blind", 400);
       
 10831     
       
 10832 };
       
 10833 
       
 10834 /* cancel the timeout if the user clicks on the keep panel open button */
       
 10835 IriSP.TweetsWidget.prototype.keepPanel = function() {
       
 10836     if (this._timeoutId != undefined) {
       
 10837       /* we're called from the "close window" link */
       
 10838       /* cancel the timeout */
       
 10839       window.clearTimeout(this._timeoutId);
       
 10840       this._timeoutId = null;
       
 10841     }
       
 10842 };
       
 10843 
       
 10844 IriSP.TweetsWidget.prototype.draw = function() {
       
 10845   var _this = this;
       
 10846   
       
 10847   var tweetMarkup = IriSP.templToHTML(IriSP.tweetWidget_template, {"share_template" : IriSP.share_template});
       
 10848   this.selector.append(tweetMarkup);
       
 10849   this.selector.hide();
       
 10850   this.selector.find(".Ldt-tweetWidgetMinimize").click(IriSP.wrap(this, this.closePanel));
       
 10851   this.selector.find(".Ldt-tweetWidgetKeepOpen").click(IriSP.wrap(this, this.keepPanel));
       
 10852   
       
 10853   this._Popcorn.listen("IriSP.PolemicTweet.click", IriSP.wrap(this, this.PolemicTweetClickHandler));
       
 10854 };
       
 10855 
       
 10856 IriSP.TweetsWidget.prototype.PolemicTweetClickHandler = function(tweet_id) {  
       
 10857   var index, annotation;
       
 10858   for (index in this._serializer._data.annotations) {
       
 10859     annotation = this._serializer._data.annotations[index];
       
 10860     
       
 10861     if (annotation.id === tweet_id)
       
 10862       break;
       
 10863   }
       
 10864     
       
 10865   if (annotation.id !== tweet_id)
       
 10866       /* we haven't found it */
       
 10867       return;
       
 10868   
       
 10869   this.displayTweet(annotation);
       
 10870   return;
       
 10871 };
       
 10872 
       
 10873 IriSP.JSONSerializer = function(DataLoader, url) {
       
 10874   IriSP.Serializer.call(this, DataLoader, url);
       
 10875 };
       
 10876 
       
 10877 IriSP.JSONSerializer.prototype = new IriSP.Serializer();
       
 10878 
       
 10879 IriSP.JSONSerializer.prototype.serialize = function(data) {
       
 10880   return JSON.stringify(data);
       
 10881 };
       
 10882 
       
 10883 IriSP.JSONSerializer.prototype.deserialize = function(data) {
       
 10884   return JSON.parse(data);
       
 10885 };
       
 10886 
       
 10887 IriSP.JSONSerializer.prototype.sync = function(callback) {
       
 10888   /* we don't have to do much because jQuery handles json for us */
       
 10889 
       
 10890   var self = this;
       
 10891 
       
 10892   var fn = function(data) {      
       
 10893       self._data = data;      
       
 10894       // sort the data too     
       
 10895       self._data["annotations"].sort(function(a, b) 
       
 10896           { var a_begin = +a.begin;
       
 10897             var b_begin = +b.begin;
       
 10898             return a_begin - b_begin;
       
 10899           });
       
 10900      
       
 10901       callback(data);      
       
 10902   };
       
 10903   
       
 10904   this._DataLoader.get(this._url, fn);
       
 10905 };
       
 10906 
       
 10907 IriSP.JSONSerializer.prototype.currentMedia = function() {  
       
 10908   return this._data.medias[0]; /* FIXME: don't hardcode it */
       
 10909 };
       
 10910 
       
 10911 /* this function searches for an annotation which matches title, description and keyword 
       
 10912    "" matches any field. 
       
 10913    Note: it ignores tweets.
       
 10914 */    
       
 10915 IriSP.JSONSerializer.prototype.searchAnnotations = function(title, description, keyword) {
       
 10916     /* we can have many types of annotations. We want search to only look for regular segments */
       
 10917     /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either
       
 10918        null or undefined.
       
 10919     */
       
 10920     var view;
       
 10921 
       
 10922     if (typeof(this._data.views) !== "undefined" && this._data.views !== null)
       
 10923        view = this._data.views[0];
       
 10924 
       
 10925     var searchViewType = "";
       
 10926 
       
 10927     if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
       
 10928             searchViewType = view.annotation_types[0];
       
 10929     }
       
 10930 
       
 10931     var filterfn = function(annotation) {
       
 10932       if( searchViewType  != "" && 
       
 10933           typeof(annotation.meta) !== "undefined" && 
       
 10934           typeof(annotation.meta["id-ref"]) !== "undefined" &&
       
 10935           annotation.meta["id-ref"] !== searchViewType) {
       
 10936         return true; // don't pass
       
 10937       } else {
       
 10938           return false;
       
 10939       }
       
 10940     };
       
 10941 
       
 10942     return this.searchAnnotationsFilter(title, description, keyword, filterfn);
       
 10943 
       
 10944 };
       
 10945 
       
 10946 /* only look for tweets */
       
 10947 IriSP.JSONSerializer.prototype.searchTweets = function(title, description, keyword) {
       
 10948     /* we can have many types of annotations. We want search to only look for regular segments */
       
 10949     /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either
       
 10950        null or undefined.
       
 10951     */
       
 10952     var view;
       
 10953 
       
 10954     if (typeof(this._data.views) !== "undefined" && this._data.views !== null)
       
 10955        view = this._data.views[0];
       
 10956 
       
 10957     var searchViewType = "";
       
 10958 
       
 10959     if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
       
 10960             searchViewType = view.annotation_types[0];
       
 10961     }
       
 10962 
       
 10963     var filterfn = function(annotation) {
       
 10964       if( searchViewType  != "" && 
       
 10965           typeof(annotation.meta) !== "undefined" && 
       
 10966           typeof(annotation.meta["id-ref"]) !== "undefined" &&
       
 10967           annotation.meta["id-ref"] !== searchViewType) {
       
 10968         return false; // pass
       
 10969       } else {
       
 10970           return true;
       
 10971       }
       
 10972     };
       
 10973 
       
 10974     return this.searchAnnotationsFilter(title, description, keyword, filterfn);
       
 10975 
       
 10976 };
       
 10977 
       
 10978 /*
       
 10979   the previous function call this one, which is more general:
       
 10980  */    
       
 10981 IriSP.JSONSerializer.prototype.searchAnnotationsFilter = function(title, description, keyword, filter) {
       
 10982 
       
 10983     var rTitle;
       
 10984     var rDescription;
       
 10985     var rKeyword;
       
 10986     /* match anything if given the empty string */
       
 10987     if (title == "")
       
 10988       title = ".*";
       
 10989     if (description == "")
       
 10990       description = ".*";
       
 10991     if (keyword == "")
       
 10992       keyword = ".*";
       
 10993     
       
 10994     rTitle = new RegExp(title, "i");  
       
 10995     rDescription = new RegExp(description, "i");  
       
 10996     rKeyword = new RegExp(keyword, "i");  
       
 10997     
       
 10998     var ret_array = [];
       
 10999     
       
 11000     var i;
       
 11001     for (i in this._data.annotations) {
       
 11002       var annotation = this._data.annotations[i];
       
 11003       
       
 11004       /* filter the annotations whose type is not the one we want */
       
 11005       if (filter(annotation)) {
       
 11006           continue;
       
 11007       }
       
 11008       
       
 11009       if (rTitle.test(annotation.content.title) && 
       
 11010           rDescription.test(annotation.content.description)) {
       
 11011           /* FIXME : implement keyword support */
       
 11012           ret_array.push(annotation);
       
 11013       }
       
 11014     }
       
 11015     
       
 11016     return ret_array;
       
 11017 };
       
 11018 
       
 11019 /* breaks a string in words and searches each of these words. Returns an array
       
 11020    of objects with the id of the annotation and its number of occurences.
       
 11021    
       
 11022    FIXME: optimize ? seems to be n^2 in the worst case.
       
 11023 */
       
 11024 IriSP.JSONSerializer.prototype.searchOccurences = function(searchString) {
       
 11025   var ret = { };
       
 11026   var keywords = searchString.split(/\s+/);
       
 11027   
       
 11028   for (var i in keywords) {
       
 11029     var keyword = keywords[i];
       
 11030     
       
 11031     // search this keyword in descriptions and title
       
 11032     var found_annotations = []
       
 11033     found_annotations = found_annotations.concat(this.searchAnnotations(keyword, "", ""));
       
 11034     found_annotations = found_annotations.concat(this.searchAnnotations("", keyword, ""));
       
 11035     
       
 11036     for (var j in found_annotations) {
       
 11037       var current_annotation = found_annotations[j];
       
 11038       
       
 11039       if (!ret.hasOwnProperty(current_annotation.id)) {
       
 11040         ret[current_annotation.id] = 1;
       
 11041       } else {
       
 11042         ret[current_annotation.id] += 1;
       
 11043       }
       
 11044       
       
 11045     }
       
 11046 
       
 11047   };
       
 11048   
       
 11049   return ret;
       
 11050 };
       
 11051 
       
 11052 /* breaks a string in words and searches each of these words. Returns an array
       
 11053    of objects with the id of the annotation and its number of occurences.
       
 11054    
       
 11055    FIXME: optimize ? seems to be n^2 in the worst case.
       
 11056 */
       
 11057 IriSP.JSONSerializer.prototype.searchTweetsOccurences = function(searchString) {
       
 11058   var ret = { };
       
 11059   var keywords = searchString.split(/\s+/);
       
 11060   
       
 11061   for (var i in keywords) {
       
 11062     var keyword = keywords[i];
       
 11063     
       
 11064     // search this keyword in descriptions and title
       
 11065     var found_annotations = []
       
 11066     found_annotations = found_annotations.concat(this.searchTweets(keyword, "", ""));
       
 11067     found_annotations = found_annotations.concat(this.searchTweets("", keyword, ""));
       
 11068     
       
 11069     for (var j in found_annotations) {
       
 11070       var current_annotation = found_annotations[j];
       
 11071       
       
 11072       if (!ret.hasOwnProperty(current_annotation.id)) {
       
 11073         ret[current_annotation.id] = 1;
       
 11074       } else {
       
 11075         ret[current_annotation.id] += 1;
       
 11076       }
       
 11077       
       
 11078     }
       
 11079 
       
 11080   };
       
 11081   
       
 11082   return ret;
       
 11083 };
       
 11084 
       
 11085 /* takes the currentTime and returns all the annotations that are displayable at the moment 
       
 11086    NB: only takes account the first type of annotations - ignores tweets 
       
 11087    currentTime is in seconds.
       
 11088  */
       
 11089 
       
 11090 IriSP.JSONSerializer.prototype.currentAnnotations = function(currentTime) {
       
 11091   var view;
       
 11092   var currentTimeMs = 1000 * currentTime;
       
 11093 
       
 11094   if (typeof(this._data.views) !== "undefined" && this._data.views !== null)
       
 11095      view = this._data.views[0];
       
 11096 
       
 11097   var view_type = "";
       
 11098 
       
 11099   if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length >= 1) {
       
 11100           view_type = view.annotation_types[0];
       
 11101   }
       
 11102 
       
 11103   var ret_array = [];
       
 11104   
       
 11105   var i;
       
 11106  
       
 11107   for (i in this._data.annotations) {
       
 11108     var annotation = this._data.annotations[i];
       
 11109     
       
 11110     if (annotation.meta["id-ref"] === view_type && annotation.begin <= currentTimeMs && annotation.end >= currentTimeMs)
       
 11111       ret_array.push(annotation);
       
 11112   }
       
 11113 
       
 11114   return ret_array;
       
 11115 };