src/js/libs/popcorn.js
branchpopcorn-port
changeset 434 0907b73a1f00
parent 429 e84a7100cc3f
equal deleted inserted replaced
424:dcf7121b1202 434:0907b73a1f00
     8 
     8 
     9     var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " +
     9     var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " +
    10           "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
    10           "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
    11           "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/);
    11           "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/);
    12 
    12 
    13     while( methods.length ) {
    13     while ( methods.length ) {
    14       global.Popcorn[ methods.shift() ] = function() {};
    14       global.Popcorn[ methods.shift() ] = function() {};
    15     }
    15     }
    16     return;
    16     return;
    17   }
    17   }
    18 
    18 
    56       function( callback, element ) {
    56       function( callback, element ) {
    57         global.setTimeout( callback, 16 );
    57         global.setTimeout( callback, 16 );
    58       };
    58       };
    59   }()),
    59   }()),
    60 
    60 
       
    61   refresh = function( obj ) {
       
    62     var currentTime = obj.media.currentTime,
       
    63       animation = obj.options.frameAnimation,
       
    64       disabled = obj.data.disabled,
       
    65       tracks = obj.data.trackEvents,
       
    66       animating = tracks.animating,
       
    67       start = tracks.startIndex,
       
    68       registryByName = Popcorn.registryByName,
       
    69       animIndex = 0,
       
    70       byStart, natives, type;
       
    71 
       
    72     start = Math.min( start + 1, tracks.byStart.length - 2 );
       
    73 
       
    74     while ( start > 0 && tracks.byStart[ start ] ) {
       
    75 
       
    76       byStart = tracks.byStart[ start ];
       
    77       natives = byStart._natives;
       
    78       type = natives && natives.type;
       
    79 
       
    80       if ( !natives ||
       
    81           ( !!registryByName[ type ] || !!obj[ type ] ) ) {
       
    82 
       
    83         if ( ( byStart.start <= currentTime && byStart.end > currentTime ) &&
       
    84                 disabled.indexOf( type ) === -1 ) {
       
    85 
       
    86           if ( !byStart._running ) {
       
    87             byStart._running = true;
       
    88             natives.start.call( obj, null, byStart );
       
    89 
       
    90             // if the 'frameAnimation' option is used,
       
    91             // push the current byStart object into the `animating` cue
       
    92             if ( animation &&
       
    93                 ( byStart && byStart._running && byStart.natives.frame ) ) {
       
    94 
       
    95               natives.frame.call( obj, null, byStart, currentTime );
       
    96             }
       
    97           }
       
    98         } else if ( byStart._running === true ) {
       
    99 
       
   100           byStart._running = false;
       
   101           natives.end.call( obj, null, byStart );
       
   102 
       
   103           if ( animation && byStart._natives.frame ) {
       
   104             animIndex = animating.indexOf( byStart );
       
   105             if ( animIndex >= 0 ) {
       
   106               animating.splice( animIndex, 1 );
       
   107             }
       
   108           }
       
   109         }
       
   110       }
       
   111 
       
   112       start--;
       
   113     }
       
   114   },
       
   115 
    61   //  Declare constructor
   116   //  Declare constructor
    62   //  Returns an instance object.
   117   //  Returns an instance object.
    63   Popcorn = function( entity, options ) {
   118   Popcorn = function( entity, options ) {
    64     //  Return new Popcorn object
   119     //  Return new Popcorn object
    65     return new Popcorn.p.init( entity, options || null );
   120     return new Popcorn.p.init( entity, options || null );
   144       this.options = options || {};
   199       this.options = options || {};
   145 
   200 
   146       this.isDestroyed = false;
   201       this.isDestroyed = false;
   147 
   202 
   148       this.data = {
   203       this.data = {
       
   204 
       
   205         // Executed by either timeupdate event or in rAF loop
       
   206         timeUpdate: Popcorn.nop,
   149 
   207 
   150         // Allows disabling a plugin per instance
   208         // Allows disabling a plugin per instance
   151         disabled: [],
   209         disabled: [],
   152 
   210 
   153         // Stores DOM event queues by type
   211         // Stores DOM event queues by type
   186       };
   244       };
   187 
   245 
   188       //  Wrap true ready check
   246       //  Wrap true ready check
   189       var isReady = function( that ) {
   247       var isReady = function( that ) {
   190 
   248 
   191         var duration, videoDurationPlus, animate;
   249         var duration, videoDurationPlus;
   192 
   250 
   193         if ( that.media.readyState >= 2 ) {
   251         if ( that.media.readyState >= 2 ) {
   194           //  Adding padding to the front and end of the arrays
   252           //  Adding padding to the front and end of the arrays
   195           //  this is so we do not fall off either end
   253           //  this is so we do not fall off either end
   196 
   254 
   206           if ( that.options.frameAnimation ) {
   264           if ( that.options.frameAnimation ) {
   207             //  if Popcorn is created with frameAnimation option set to true,
   265             //  if Popcorn is created with frameAnimation option set to true,
   208             //  requestAnimFrame is used instead of "timeupdate" media event.
   266             //  requestAnimFrame is used instead of "timeupdate" media event.
   209             //  This is for greater frame time accuracy, theoretically up to
   267             //  This is for greater frame time accuracy, theoretically up to
   210             //  60 frames per second as opposed to ~4 ( ~every 15-250ms)
   268             //  60 frames per second as opposed to ~4 ( ~every 15-250ms)
   211             animate = function () {
   269             that.data.timeUpdate = function () {
   212 
   270 
   213               Popcorn.timeUpdate( that, {} );
   271               Popcorn.timeUpdate( that, {} );
   214 
   272 
   215               that.trigger( "timeupdate" );
   273               that.trigger( "timeupdate" );
   216 
   274 
   217               requestAnimFrame( animate );
   275               !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
   218             };
   276             };
   219 
   277 
   220             requestAnimFrame( animate );
   278             !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
   221 
   279 
   222           } else {
   280           } else {
   223 
   281 
   224             that.data.timeUpdateFunction = function( event ) {
   282             that.data.timeUpdate = function( event ) {
   225               Popcorn.timeUpdate( that, event );
   283               Popcorn.timeUpdate( that, event );
   226             };
   284             };
   227 
   285 
   228             if ( !that.isDestroyed ) {
   286             if ( !that.isDestroyed ) {
   229               that.media.addEventListener( "timeupdate", that.data.timeUpdateFunction, false );
   287               that.media.addEventListener( "timeupdate", that.data.timeUpdate, false );
   230             }
   288             }
   231           }
   289           }
   232         } else {
   290         } else {
   233           global.setTimeout(function() {
   291           global.setTimeout(function() {
   234             isReady( that );
   292             isReady( that );
   355 
   413 
   356       if ( disabled.indexOf( plugin ) === -1 ) {
   414       if ( disabled.indexOf( plugin ) === -1 ) {
   357         disabled.push( plugin );
   415         disabled.push( plugin );
   358       }
   416       }
   359 
   417 
       
   418       refresh( instance );
       
   419 
   360       return instance;
   420       return instance;
   361     },
   421     },
   362     enable: function( instance, plugin ) {
   422     enable: function( instance, plugin ) {
   363 
   423 
   364       var disabled = instance.data.disabled,
   424       var disabled = instance.data.disabled,
   365           index = disabled.indexOf( plugin );
   425           index = disabled.indexOf( plugin );
   366 
   426 
   367       if ( index > -1 ) {
   427       if ( index > -1 ) {
   368         disabled.splice( index, 1 );
   428         disabled.splice( index, 1 );
   369       }
   429       }
       
   430 
       
   431       refresh( instance );
   370 
   432 
   371       return instance;
   433       return instance;
   372     },
   434     },
   373     destroy: function( instance ) {
   435     destroy: function( instance ) {
   374       var events = instance.data.events,
   436       var events = instance.data.events,
   382         }
   444         }
   383         events[ item ] = null;
   445         events[ item ] = null;
   384       }
   446       }
   385 
   447 
   386       if ( !instance.isDestroyed ) {
   448       if ( !instance.isDestroyed ) {
   387         instance.media.removeEventListener( "timeupdate", instance.data.timeUpdateFunction, false );
   449         instance.data.timeUpdate && instance.media.removeEventListener( "timeupdate", instance.data.timeUpdate, false );
   388         instance.isDestroyed = true;
   450         instance.isDestroyed = true;
   389       }
   451       }
   390     }
   452     }
   391   });
   453   });
   392 
   454 
   778     track.end   = Popcorn.util.toSeconds( track.end, obj.options.framerate );
   840     track.end   = Popcorn.util.toSeconds( track.end, obj.options.framerate );
   779 
   841 
   780     //  Store this definition in an array sorted by times
   842     //  Store this definition in an array sorted by times
   781     var byStart = obj.data.trackEvents.byStart,
   843     var byStart = obj.data.trackEvents.byStart,
   782         byEnd = obj.data.trackEvents.byEnd,
   844         byEnd = obj.data.trackEvents.byEnd,
   783         idx;
   845         startIndex, endIndex,
   784 
   846         currentTime;
   785     for ( idx = byStart.length - 1; idx >= 0; idx-- ) {
   847 
   786 
   848     for ( startIndex = byStart.length - 1; startIndex >= 0; startIndex-- ) {
   787       if ( track.start >= byStart[ idx ].start ) {
   849 
   788         byStart.splice( idx + 1, 0, track );
   850       if ( track.start >= byStart[ startIndex ].start ) {
       
   851         byStart.splice( startIndex + 1, 0, track );
   789         break;
   852         break;
   790       }
   853       }
   791     }
   854     }
   792 
   855 
   793     for ( idx = byEnd.length - 1; idx >= 0; idx-- ) {
   856     for ( endIndex = byEnd.length - 1; endIndex >= 0; endIndex-- ) {
   794 
   857 
   795       if ( track.end > byEnd[ idx ].end ) {
   858       if ( track.end > byEnd[ endIndex ].end ) {
   796         byEnd.splice( idx + 1, 0, track );
   859         byEnd.splice( endIndex + 1, 0, track );
   797         break;
   860         break;
   798       }
   861       }
   799     }
   862     }
   800 
   863 
   801     this.timeUpdate( obj, null );
   864     // Display track event immediately if it's enabled and current
       
   865     if ( track._natives &&
       
   866         ( !!Popcorn.registryByName[ track._natives.type ] || !!obj[ track._natives.type ] ) ) {
       
   867 
       
   868       currentTime = obj.media.currentTime;
       
   869       if ( track.end > currentTime &&
       
   870         track.start <= currentTime &&
       
   871         obj.data.disabled.indexOf( track._natives.type ) === -1 ) {
       
   872 
       
   873         track._running = true;
       
   874         track._natives.start.call( obj, null, track );
       
   875 
       
   876         if ( obj.options.frameAnimation &&
       
   877           track._natives.frame ) {
       
   878 
       
   879           obj.data.trackEvents.animating.push( track );
       
   880           track._natives.frame.call( obj, null, track, currentTime );
       
   881         }
       
   882       }
       
   883     }
       
   884 
       
   885     // update startIndex and endIndex
       
   886     if ( startIndex <= obj.data.trackEvents.startIndex &&
       
   887       track.start <= obj.data.trackEvents.previousUpdateTime ) {
       
   888 
       
   889       obj.data.trackEvents.startIndex++;
       
   890     }
       
   891 
       
   892     if ( endIndex <= obj.data.trackEvents.endIndex &&
       
   893       track.end < obj.data.trackEvents.previousUpdateTime ) {
       
   894 
       
   895       obj.data.trackEvents.endIndex++;
       
   896     }
       
   897 
       
   898     this.timeUpdate( obj, null, true );
   802 
   899 
   803     // Store references to user added trackevents in ref table
   900     // Store references to user added trackevents in ref table
   804     if ( track._id ) {
   901     if ( track._id ) {
   805       Popcorn.addTrackEvent.ref( obj, track );
   902       Popcorn.addTrackEvent.ref( obj, track );
   806     }
   903     }
  1206       options._natives.type = name;
  1303       options._natives.type = name;
  1207       options._running = false;
  1304       options._running = false;
  1208 
  1305 
  1209       natives.start = natives.start || natives[ "in" ];
  1306       natives.start = natives.start || natives[ "in" ];
  1210       natives.end = natives.end || natives[ "out" ];
  1307       natives.end = natives.end || natives[ "out" ];
       
  1308 
       
  1309       // extend teardown to always call end if running
       
  1310       natives._teardown = combineFn(function() {
       
  1311 
       
  1312         var args = slice.call( arguments );
       
  1313 
       
  1314         // end function signature is not the same as teardown,
       
  1315         // put null on the front of arguments for the event parameter
       
  1316         args.unshift( null );
       
  1317 
       
  1318         // only call end if event is running
       
  1319         args[ 1 ]._running && natives.end.apply( this, args );
       
  1320       }, natives._teardown );
  1211 
  1321 
  1212       // Check for previously set default options
  1322       // Check for previously set default options
  1213       defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
  1323       defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
  1214 
  1324 
  1215       // default to an empty string if no effect exists
  1325       // default to an empty string if no effect exists
  1362       // remove plugin reference from registry
  1472       // remove plugin reference from registry
  1363       for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
  1473       for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
  1364         if ( Popcorn.registry[ registryIdx ].name === name ) {
  1474         if ( Popcorn.registry[ registryIdx ].name === name ) {
  1365           Popcorn.registry.splice( registryIdx, 1 );
  1475           Popcorn.registry.splice( registryIdx, 1 );
  1366           delete Popcorn.registryByName[ name ];
  1476           delete Popcorn.registryByName[ name ];
       
  1477           delete Popcorn.manifest[ name ];
  1367 
  1478 
  1368           // delete the plugin
  1479           // delete the plugin
  1369           delete obj[ name ];
  1480           delete obj[ name ];
  1370 
  1481 
  1371           // plugin found and removed, stop checking, we are done
  1482           // plugin found and removed, stop checking, we are done
  1552           basePlayer[ val ] = container[ val ];
  1663           basePlayer[ val ] = container[ val ];
  1553         } else if ( typeof container[ val ] === "function" ) {
  1664         } else if ( typeof container[ val ] === "function" ) {
  1554 
  1665 
  1555           basePlayer[ val ] = (function( value ) {
  1666           basePlayer[ val ] = (function( value ) {
  1556 
  1667 
  1557             return function() {
  1668             // this is a stupid ugly kludgy hack in honour of Safari
  1558 
  1669             // in Safari a NodeList is a function, not an object
  1559               return container[ value ].apply( container, arguments );
  1670             if ( "length" in container[ value ] && !container[ value ].call ) {
  1560             };
  1671 
       
  1672               return container[ value ];
       
  1673             } else {
       
  1674 
       
  1675               return function() {
       
  1676 
       
  1677                 return container[ value ].apply( container, arguments );
       
  1678               };
       
  1679             }
  1561           }( val ));
  1680           }( val ));
  1562         } else {
  1681         } else {
  1563 
  1682 
  1564           Popcorn.player.defineProperty( basePlayer, val, {
  1683           Popcorn.player.defineProperty( basePlayer, val, {
  1565             get: (function( value ) {
  1684             get: (function( value ) {
  1795   };
  1914   };
  1796 
  1915 
  1797 
  1916 
  1798   Popcorn.xhr.httpData = function( settings ) {
  1917   Popcorn.xhr.httpData = function( settings ) {
  1799 
  1918 
  1800     var data, json = null;
  1919     var data, json = null,
       
  1920         parser, xml = null;
  1801 
  1921 
  1802     settings.ajax.onreadystatechange = function() {
  1922     settings.ajax.onreadystatechange = function() {
  1803 
  1923 
  1804       if ( settings.ajax.readyState === 4 ) {
  1924       if ( settings.ajax.readyState === 4 ) {
  1805 
  1925 
  1812         data = {
  1932         data = {
  1813           xml: settings.ajax.responseXML,
  1933           xml: settings.ajax.responseXML,
  1814           text: settings.ajax.responseText,
  1934           text: settings.ajax.responseText,
  1815           json: json
  1935           json: json
  1816         };
  1936         };
       
  1937 
       
  1938         // Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
       
  1939         if ( !data.xml || !data.xml.documentElement ) {
       
  1940           data.xml = null;
       
  1941 
       
  1942           try {
       
  1943             parser = new DOMParser();
       
  1944             xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
       
  1945 
       
  1946             if ( !xml.getElementsByTagName( "parsererror" ).length ) {
       
  1947               data.xml = xml;
       
  1948             }
       
  1949           } catch ( e ) {
       
  1950             // data.xml remains null
       
  1951           }
       
  1952         }
  1817 
  1953 
  1818         //  If a dataType was specified, return that type of data
  1954         //  If a dataType was specified, return that type of data
  1819         if ( settings.dataType ) {
  1955         if ( settings.dataType ) {
  1820           data = data[ settings.dataType ];
  1956           data = data[ settings.dataType ];
  1821         }
  1957         }
  2044     return list.join( "," ).toLowerCase().split( ",");
  2180     return list.join( "," ).toLowerCase().split( ",");
  2045   }
  2181   }
  2046 
  2182 
  2047   //  Protected API methods
  2183   //  Protected API methods
  2048   Popcorn.protect = {
  2184   Popcorn.protect = {
  2049     natives: getItems() 
  2185     natives: getItems()
  2050   };
  2186   };
  2051 
  2187 
  2052   //  Exposes Popcorn to global context
  2188   //  Exposes Popcorn to global context
  2053   global.Popcorn = Popcorn;
  2189   global.Popcorn = Popcorn;
  2054 
  2190