# HG changeset patch # User hamidouk # Date 1322821914 -3600 # Node ID 30277c1e3d46669c0721fb335c0d9e002eda74b6 # Parent d1fe53ad8d723f39aa64c94cd00459a5dca90b43 replaced most of popcorn's functions. diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/libs/popcorn.code.js --- a/src/js/libs/popcorn.code.js Fri Dec 02 11:06:30 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -// PLUGIN: Code - -(function ( Popcorn ) { - - /** - * Code Popcorn Plug-in - * - * Adds the ability to run arbitrary code (JavaScript functions) according to video timing. - * - * @param {Object} options - * - * Required parameters: start, end, template, data, and target. - * Optional parameter: static. - * - * start: the time in seconds when the mustache template should be rendered - * in the target div. - * - * end: the time in seconds when the rendered mustache template should be - * removed from the target div. - * - * onStart: the function to be run when the start time is reached. - * - * onFrame: [optional] a function to be run on each paint call - * (e.g., called ~60 times per second) between the start and end times. - * - * onEnd: [optional] a function to be run when the end time is reached. - * - * Example: - var p = Popcorn('#video') - - // onStart function only - .code({ - start: 1, - end: 4, - onStart: function( options ) { - // called on start - } - }) - - // onStart + onEnd only - .code({ - start: 6, - end: 8, - onStart: function( options ) { - // called on start - }, - onEnd: function ( options ) { - // called on end - } - }) - - // onStart, onEnd, onFrame - .code({ - start: 10, - end: 14, - onStart: function( options ) { - // called on start - }, - onFrame: function ( options ) { - // called on every paint frame between start and end. - // uses mozRequestAnimationFrame, webkitRequestAnimationFrame, - // or setTimeout with 16ms window. - }, - onEnd: function ( options ) { - // called on end - } - }); - * - */ - - Popcorn.plugin( "code" , function( options ) { - var running = false; - - // Setup a proper frame interval function (60fps), favouring paint events. - var step = (function() { - - var buildFrameRunner = function( runner ) { - return function( f, options ) { - - var _f = function() { - running && f(); - running && runner( _f ); - }; - - _f(); - }; - }; - - // Figure out which level of browser support we have for this - if ( window.webkitRequestAnimationFrame ) { - return buildFrameRunner( window.webkitRequestAnimationFrame ); - } else if ( window.mozRequestAnimationFrame ) { - return buildFrameRunner( window.mozRequestAnimationFrame ); - } else { - return buildFrameRunner( function( f ) { - window.setTimeout( f, 16 ); - }); - } - - })(); - - if ( !options.onStart || typeof options.onStart !== "function" ) { - - if ( Popcorn.plugin.debug ) { - throw new Error( "Popcorn Code Plugin Error: onStart must be a function." ); - } - options.onStart = Popcorn.nop; - } - - if ( options.onEnd && typeof options.onEnd !== "function" ) { - - if ( Popcorn.plugin.debug ) { - throw new Error( "Popcorn Code Plugin Error: onEnd must be a function." ); - } - options.onEnd = undefined; - } - - if ( options.onFrame && typeof options.onFrame !== "function" ) { - - if ( Popcorn.plugin.debug ) { - throw new Error( "Popcorn Code Plugin Error: onFrame must be a function." ); - } - options.onFrame = undefined; - } - - return { - start: function( event, options ) { - options.onStart( options ); - - if ( options.onFrame ) { - running = true; - step( options.onFrame, options ); - } - }, - - end: function( event, options ) { - if ( options.onFrame ) { - running = false; - } - - if ( options.onEnd ) { - options.onEnd( options ); - } - } - }; - }, - { - about: { - name: "Popcorn Code Plugin", - version: "0.1", - author: "David Humphrey (@humphd)", - website: "http://vocamus.net/dave" - }, - options: { - start: { - elem: "input", - type: "text", - label: "In" - }, - end: { - elem: "input", - type: "text", - label: "Out" - }, - onStart: { - elem: "input", - type: "function", - label: "onStart" - }, - onFrame: { - elem: "input", - type: "function", - label: "onFrame" - }, - onEnd: { - elem: "input", - type: "function", - label: "onEnd" - } - } - }); -})( Popcorn ); diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/libs/popcorn.js --- a/src/js/libs/popcorn.js Fri Dec 02 11:06:30 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2055 +0,0 @@ -(function(global, document) { - - // Popcorn.js does not support archaic browsers - if ( !document.addEventListener ) { - global.Popcorn = { - isSupported: false - }; - - var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " + - "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " + - "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/); - - while( methods.length ) { - global.Popcorn[ methods.shift() ] = function() {}; - } - return; - } - - var - - AP = Array.prototype, - OP = Object.prototype, - - forEach = AP.forEach, - slice = AP.slice, - hasOwn = OP.hasOwnProperty, - toString = OP.toString, - - // Copy global Popcorn (may not exist) - _Popcorn = global.Popcorn, - - // ID string matching - rIdExp = /^(#([\w\-\_\.]+))$/, - - // Ready fn cache - readyStack = [], - readyBound = false, - readyFired = false, - - // Non-public internal data object - internal = { - events: { - hash: {}, - apis: {} - } - }, - - // Non-public `requestAnimFrame` - // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - requestAnimFrame = (function(){ - return global.requestAnimationFrame || - global.webkitRequestAnimationFrame || - global.mozRequestAnimationFrame || - global.oRequestAnimationFrame || - global.msRequestAnimationFrame || - function( callback, element ) { - global.setTimeout( callback, 16 ); - }; - }()), - - // Declare constructor - // Returns an instance object. - Popcorn = function( entity, options ) { - // Return new Popcorn object - return new Popcorn.p.init( entity, options || null ); - }; - - // Popcorn API version, automatically inserted via build system. - Popcorn.version = "@VERSION"; - - // Boolean flag allowing a client to determine if Popcorn can be supported - Popcorn.isSupported = true; - - // Instance caching - Popcorn.instances = []; - - // Declare a shortcut (Popcorn.p) to and a definition of - // the new prototype for our Popcorn constructor - Popcorn.p = Popcorn.prototype = { - - init: function( entity, options ) { - - var matches; - - // Supports Popcorn(function () { /../ }) - // Originally proposed by Daniel Brooks - - if ( typeof entity === "function" ) { - - // If document ready has already fired - if ( document.readyState === "interactive" || document.readyState === "complete" ) { - - entity( document, Popcorn ); - - return; - } - // Add `entity` fn to ready stack - readyStack.push( entity ); - - // This process should happen once per page load - if ( !readyBound ) { - - // set readyBound flag - readyBound = true; - - var DOMContentLoaded = function() { - - readyFired = true; - - // Remove global DOM ready listener - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // Execute all ready function in the stack - for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) { - - readyStack[ i ].call( document, Popcorn ); - - } - // GC readyStack - readyStack = null; - }; - - // Register global DOM ready listener - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - } - - return; - } - - // Check if entity is a valid string id - matches = rIdExp.exec( entity ); - - // Get media element by id or object reference - this.media = matches && matches.length && matches[ 2 ] ? - document.getElementById( matches[ 2 ] ) : - entity; - - // Create an audio or video element property reference - this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media; - - // Register new instance - Popcorn.instances.push( this ); - - this.options = options || {}; - - this.isDestroyed = false; - - this.data = { - - // Allows disabling a plugin per instance - disabled: [], - - // Stores DOM event queues by type - events: {}, - - // Stores Special event hooks data - hooks: {}, - - // Store track event history data - history: [], - - // Stores ad-hoc state related data] - state: { - volume: this.media.volume - }, - - // Store track event object references by trackId - trackRefs: {}, - - // Playback track event queues - trackEvents: { - byStart: [{ - - start: -1, - end: -1 - }], - byEnd: [{ - start: -1, - end: -1 - }], - animating: [], - startIndex: 0, - endIndex: 0, - previousUpdateTime: -1 - } - }; - - // Wrap true ready check - var isReady = function( that ) { - - var duration, videoDurationPlus, animate; - - if ( that.media.readyState >= 2 ) { - // Adding padding to the front and end of the arrays - // this is so we do not fall off either end - - duration = that.media.duration; - // Check for no duration info (NaN) - videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1; - - Popcorn.addTrackEvent( that, { - start: videoDurationPlus, - end: videoDurationPlus - }); - - if ( that.options.frameAnimation ) { - // if Popcorn is created with frameAnimation option set to true, - // requestAnimFrame is used instead of "timeupdate" media event. - // This is for greater frame time accuracy, theoretically up to - // 60 frames per second as opposed to ~4 ( ~every 15-250ms) - animate = function () { - - Popcorn.timeUpdate( that, {} ); - - that.trigger( "timeupdate" ); - - requestAnimFrame( animate ); - }; - - requestAnimFrame( animate ); - - } else { - - that.data.timeUpdateFunction = function( event ) { - Popcorn.timeUpdate( that, event ); - }; - - if ( !that.isDestroyed ) { - that.media.addEventListener( "timeupdate", that.data.timeUpdateFunction, false ); - } - } - } else { - global.setTimeout(function() { - isReady( that ); - }, 1 ); - } - }; - - isReady( this ); - - return this; - } - }; - - // Extend constructor prototype to instance prototype - // Allows chaining methods to instances - Popcorn.p.init.prototype = Popcorn.p; - - Popcorn.forEach = function( obj, fn, context ) { - - if ( !obj || !fn ) { - return {}; - } - - context = context || this; - - var key, len; - - // Use native whenever possible - if ( forEach && obj.forEach === forEach ) { - return obj.forEach( fn, context ); - } - - if ( toString.call( obj ) === "[object NodeList]" ) { - for ( key = 0, len = obj.length; key < len; key++ ) { - fn.call( context, obj[ key ], key, obj ); - } - return obj; - } - - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - fn.call( context, obj[ key ], key, obj ); - } - } - return obj; - }; - - Popcorn.extend = function( obj ) { - var dest = obj, src = slice.call( arguments, 1 ); - - Popcorn.forEach( src, function( copy ) { - for ( var prop in copy ) { - dest[ prop ] = copy[ prop ]; - } - }); - - return dest; - }; - - - // A Few reusable utils, memoized onto Popcorn - Popcorn.extend( Popcorn, { - noConflict: function( deep ) { - - if ( deep ) { - global.Popcorn = _Popcorn; - } - - return Popcorn; - }, - error: function( msg ) { - throw new Error( msg ); - }, - guid: function( prefix ) { - Popcorn.guid.counter++; - return ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter ); - }, - sizeOf: function( obj ) { - var size = 0; - - for ( var prop in obj ) { - size++; - } - - return size; - }, - isArray: Array.isArray || function( array ) { - return toString.call( array ) === "[object Array]"; - }, - - nop: function() {}, - - position: function( elem ) { - - var clientRect = elem.getBoundingClientRect(), - bounds = {}, - doc = elem.ownerDocument, - docElem = document.documentElement, - body = document.body, - clientTop, clientLeft, scrollTop, scrollLeft, top, left; - - // Determine correct clientTop/Left - clientTop = docElem.clientTop || body.clientTop || 0; - clientLeft = docElem.clientLeft || body.clientLeft || 0; - - // Determine correct scrollTop/Left - scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop ); - scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft ); - - // Temp top/left - top = Math.ceil( clientRect.top + scrollTop - clientTop ); - left = Math.ceil( clientRect.left + scrollLeft - clientLeft ); - - for ( var p in clientRect ) { - bounds[ p ] = Math.round( clientRect[ p ] ); - } - - return Popcorn.extend({}, bounds, { top: top, left: left }); - }, - - disable: function( instance, plugin ) { - - var disabled = instance.data.disabled; - - if ( disabled.indexOf( plugin ) === -1 ) { - disabled.push( plugin ); - } - - return instance; - }, - enable: function( instance, plugin ) { - - var disabled = instance.data.disabled, - index = disabled.indexOf( plugin ); - - if ( index > -1 ) { - disabled.splice( index, 1 ); - } - - return instance; - }, - destroy: function( instance ) { - var events = instance.data.events, - singleEvent, item, fn; - - // Iterate through all events and remove them - for ( item in events ) { - singleEvent = events[ item ]; - for ( fn in singleEvent ) { - delete singleEvent[ fn ]; - } - events[ item ] = null; - } - - if ( !instance.isDestroyed ) { - instance.media.removeEventListener( "timeupdate", instance.data.timeUpdateFunction, false ); - instance.isDestroyed = true; - } - } - }); - - // Memoized GUID Counter - Popcorn.guid.counter = 1; - - // Factory to implement getters, setters and controllers - // as Popcorn instance methods. The IIFE will create and return - // an object with defined methods - Popcorn.extend(Popcorn.p, (function() { - - var methods = "load play pause currentTime playbackRate volume duration preload playbackRate " + - "autoplay loop controls muted buffered readyState seeking paused played seekable ended", - ret = {}; - - - // Build methods, store in object that is returned and passed to extend - Popcorn.forEach( methods.split( /\s+/g ), function( name ) { - - ret[ name ] = function( arg ) { - - if ( typeof this.media[ name ] === "function" ) { - - // Support for shorthanded play(n)/pause(n) jump to currentTime - // If arg is not null or undefined and called by one of the - // allowed shorthandable methods, then set the currentTime - // Supports time as seconds or SMPTE - if ( arg != null && /play|pause/.test( name ) ) { - this.media.currentTime = Popcorn.util.toSeconds( arg ); - } - - this.media[ name ](); - - return this; - } - - - if ( arg != null ) { - - this.media[ name ] = arg; - - return this; - } - - return this.media[ name ]; - }; - }); - - return ret; - - })() - ); - - Popcorn.forEach( "enable disable".split(" "), function( method ) { - Popcorn.p[ method ] = function( plugin ) { - return Popcorn[ method ]( this, plugin ); - }; - }); - - Popcorn.extend(Popcorn.p, { - - // Rounded currentTime - roundTime: function() { - return -~this.media.currentTime; - }, - - // Attach an event to a single point in time - exec: function( time, fn ) { - - // Creating a one second track event with an empty end - Popcorn.addTrackEvent( this, { - start: time, - end: time + 1, - _running: false, - _natives: { - start: fn || Popcorn.nop, - end: Popcorn.nop, - type: "exec" - } - }); - - return this; - }, - - // Mute the calling media, optionally toggle - mute: function( toggle ) { - - var event = toggle == null || toggle === true ? "muted" : "unmuted"; - - // If `toggle` is explicitly `false`, - // unmute the media and restore the volume level - if ( event === "unmuted" ) { - this.media.muted = false; - this.media.volume = this.data.state.volume; - } - - // If `toggle` is either null or undefined, - // save the current volume and mute the media element - if ( event === "muted" ) { - this.data.state.volume = this.media.volume; - this.media.muted = true; - } - - // Trigger either muted|unmuted event - this.trigger( event ); - - return this; - }, - - // Convenience method, unmute the calling media - unmute: function( toggle ) { - - return this.mute( toggle == null ? false : !toggle ); - }, - - // Get the client bounding box of an instance element - position: function() { - return Popcorn.position( this.media ); - }, - - // Toggle a plugin's playback behaviour (on or off) per instance - toggle: function( plugin ) { - return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin ); - }, - - // Set default values for plugin options objects per instance - defaults: function( plugin, defaults ) { - - // If an array of default configurations is provided, - // iterate and apply each to this instance - if ( Popcorn.isArray( plugin ) ) { - - Popcorn.forEach( plugin, function( obj ) { - for ( var name in obj ) { - this.defaults( name, obj[ name ] ); - } - }, this ); - - return this; - } - - if ( !this.options.defaults ) { - this.options.defaults = {}; - } - - if ( !this.options.defaults[ plugin ] ) { - this.options.defaults[ plugin ] = {}; - } - - Popcorn.extend( this.options.defaults[ plugin ], defaults ); - - return this; - } - }); - - Popcorn.Events = { - UIEvents: "blur focus focusin focusout load resize scroll unload", - MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick", - Events: "loadstart progress suspend emptied stalled play pause " + - "loadedmetadata loadeddata waiting playing canplay canplaythrough " + - "seeking seeked timeupdate ended ratechange durationchange volumechange" - }; - - Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " + - Popcorn.Events.MouseEvents + " " + - Popcorn.Events.Events; - - internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ]; - - // Privately compile events table at load time - (function( events, data ) { - - var apis = internal.events.apiTypes, - eventsList = events.Natives.split( /\s+/g ), - idx = 0, len = eventsList.length, prop; - - for( ; idx < len; idx++ ) { - data.hash[ eventsList[idx] ] = true; - } - - apis.forEach(function( val, idx ) { - - data.apis[ val ] = {}; - - var apiEvents = events[ val ].split( /\s+/g ), - len = apiEvents.length, - k = 0; - - for ( ; k < len; k++ ) { - data.apis[ val ][ apiEvents[ k ] ] = true; - } - }); - })( Popcorn.Events, internal.events ); - - Popcorn.events = { - - isNative: function( type ) { - return !!internal.events.hash[ type ]; - }, - getInterface: function( type ) { - - if ( !Popcorn.events.isNative( type ) ) { - return false; - } - - var eventApi = internal.events, - apis = eventApi.apiTypes, - apihash = eventApi.apis, - idx = 0, len = apis.length, api, tmp; - - for ( ; idx < len; idx++ ) { - tmp = apis[ idx ]; - - if ( apihash[ tmp ][ type ] ) { - api = tmp; - break; - } - } - return api; - }, - // Compile all native events to single array - all: Popcorn.Events.Natives.split( /\s+/g ), - // Defines all Event handling static functions - fn: { - trigger: function( type, data ) { - - var eventInterface, evt; - // setup checks for custom event system - if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) { - - eventInterface = Popcorn.events.getInterface( type ); - - if ( eventInterface ) { - - evt = document.createEvent( eventInterface ); - evt.initEvent( type, true, true, global, 1 ); - - this.media.dispatchEvent( evt ); - - return this; - } - - // Custom events - Popcorn.forEach( this.data.events[ type ], function( obj, key ) { - - obj.call( this, data ); - - }, this ); - - } - - return this; - }, - listen: function( type, fn ) { - - var self = this, - hasEvents = true, - eventHook = Popcorn.events.hooks[ type ], - origType = type, - tmp; - - if ( !this.data.events[ type ] ) { - this.data.events[ type ] = {}; - hasEvents = false; - } - - // Check and setup event hooks - if ( eventHook ) { - - // Execute hook add method if defined - if ( eventHook.add ) { - eventHook.add.call( this, {}, fn ); - } - - // Reassign event type to our piggyback event type if defined - if ( eventHook.bind ) { - type = eventHook.bind; - } - - // Reassign handler if defined - if ( eventHook.handler ) { - tmp = fn; - - fn = function wrapper( event ) { - eventHook.handler.call( self, event, tmp ); - }; - } - - // assume the piggy back event is registered - hasEvents = true; - - // Setup event registry entry - if ( !this.data.events[ type ] ) { - this.data.events[ type ] = {}; - // Toggle if the previous assumption was untrue - hasEvents = false; - } - } - - // Register event and handler - this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn; - - // only attach one event of any type - if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) { - - this.media.addEventListener( type, function( event ) { - - Popcorn.forEach( self.data.events[ type ], function( obj, key ) { - if ( typeof obj === "function" ) { - obj.call( self, event ); - } - }); - - }, false); - } - return this; - }, - unlisten: function( type, fn ) { - - if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) { - - delete this.data.events[ type ][ fn ]; - - return this; - } - - this.data.events[ type ] = null; - - return this; - } - }, - hooks: { - canplayall: { - bind: "canplaythrough", - add: function( event, callback ) { - - var state = false; - - if ( this.media.readyState ) { - - callback.call( this, event ); - - state = true; - } - - this.data.hooks.canplayall = { - fired: state - }; - }, - // declare special handling instructions - handler: function canplayall( event, callback ) { - - if ( !this.data.hooks.canplayall.fired ) { - // trigger original user callback once - callback.call( this, event ); - - this.data.hooks.canplayall.fired = true; - } - } - } - } - }; - - // Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances - Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) { - Popcorn.p[ key ] = Popcorn.events.fn[ key ]; - }); - - // Internal Only - Adds track events to the instance object - Popcorn.addTrackEvent = function( obj, track ) { - - // Determine if this track has default options set for it - // If so, apply them to the track object - if ( track && track._natives && track._natives.type && - ( obj.options.defaults && obj.options.defaults[ track._natives.type ] ) ) { - - track = Popcorn.extend( {}, obj.options.defaults[ track._natives.type ], track ); - } - - if ( track._natives ) { - // Supports user defined track event id - track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id; - - // Push track event ids into the history - obj.data.history.push( track._id ); - } - - track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate ); - track.end = Popcorn.util.toSeconds( track.end, obj.options.framerate ); - - // Store this definition in an array sorted by times - var byStart = obj.data.trackEvents.byStart, - byEnd = obj.data.trackEvents.byEnd, - idx; - - for ( idx = byStart.length - 1; idx >= 0; idx-- ) { - - if ( track.start >= byStart[ idx ].start ) { - byStart.splice( idx + 1, 0, track ); - break; - } - } - - for ( idx = byEnd.length - 1; idx >= 0; idx-- ) { - - if ( track.end > byEnd[ idx ].end ) { - byEnd.splice( idx + 1, 0, track ); - break; - } - } - - this.timeUpdate( obj, null ); - - // Store references to user added trackevents in ref table - if ( track._id ) { - Popcorn.addTrackEvent.ref( obj, track ); - } - }; - - // Internal Only - Adds track event references to the instance object's trackRefs hash table - Popcorn.addTrackEvent.ref = function( obj, track ) { - obj.data.trackRefs[ track._id ] = track; - - return obj; - }; - - Popcorn.removeTrackEvent = function( obj, trackId ) { - - var historyLen = obj.data.history.length, - indexWasAt = 0, - byStart = [], - byEnd = [], - animating = [], - history = []; - - Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) { - // Preserve the original start/end trackEvents - if ( !o._id ) { - byStart.push( obj.data.trackEvents.byStart[i] ); - byEnd.push( obj.data.trackEvents.byEnd[i] ); - } - - // Filter for user track events (vs system track events) - if ( o._id ) { - - // Filter for the trackevent to remove - if ( o._id !== trackId ) { - byStart.push( obj.data.trackEvents.byStart[i] ); - byEnd.push( obj.data.trackEvents.byEnd[i] ); - } - - // Capture the position of the track being removed. - if ( o._id === trackId ) { - indexWasAt = i; - o._natives._teardown && o._natives._teardown.call( obj, o ); - } - } - - }); - - if ( obj.data.trackEvents.animating.length ) { - Popcorn.forEach( obj.data.trackEvents.animating, function( o, i, context ) { - // Preserve the original start/end trackEvents - if ( !o._id ) { - animating.push( obj.data.trackEvents.animating[i] ); - } - - // Filter for user track events (vs system track events) - if ( o._id ) { - // Filter for the trackevent to remove - if ( o._id !== trackId ) { - animating.push( obj.data.trackEvents.animating[i] ); - } - } - }); - } - - // Update - if ( indexWasAt <= obj.data.trackEvents.startIndex ) { - obj.data.trackEvents.startIndex--; - } - - if ( indexWasAt <= obj.data.trackEvents.endIndex ) { - obj.data.trackEvents.endIndex--; - } - - obj.data.trackEvents.byStart = byStart; - obj.data.trackEvents.byEnd = byEnd; - obj.data.trackEvents.animating = animating; - - for ( var i = 0; i < historyLen; i++ ) { - if ( obj.data.history[ i ] !== trackId ) { - history.push( obj.data.history[ i ] ); - } - } - - // Update ordered history array - obj.data.history = history; - - // Update track event references - Popcorn.removeTrackEvent.ref( obj, trackId ); - }; - - // Internal Only - Removes track event references from instance object's trackRefs hash table - Popcorn.removeTrackEvent.ref = function( obj, trackId ) { - delete obj.data.trackRefs[ trackId ]; - - return obj; - }; - - // Return an array of track events bound to this instance object - Popcorn.getTrackEvents = function( obj ) { - - var trackevents = [], - refs = obj.data.trackEvents.byStart, - length = refs.length, - idx = 0, - ref; - - for ( ; idx < length; idx++ ) { - ref = refs[ idx ]; - // Return only user attributed track event references - if ( ref._id ) { - trackevents.push( ref ); - } - } - - return trackevents; - }; - - // Internal Only - Returns an instance object's trackRefs hash table - Popcorn.getTrackEvents.ref = function( obj ) { - return obj.data.trackRefs; - }; - - // Return a single track event bound to this instance object - Popcorn.getTrackEvent = function( obj, trackId ) { - return obj.data.trackRefs[ trackId ]; - }; - - // Internal Only - Returns an instance object's track reference by track id - Popcorn.getTrackEvent.ref = function( obj, trackId ) { - return obj.data.trackRefs[ trackId ]; - }; - - Popcorn.getLastTrackEventId = function( obj ) { - return obj.data.history[ obj.data.history.length - 1 ]; - }; - - Popcorn.timeUpdate = function( obj, event ) { - - var currentTime = obj.media.currentTime, - previousTime = obj.data.trackEvents.previousUpdateTime, - tracks = obj.data.trackEvents, - animating = tracks.animating, - end = tracks.endIndex, - start = tracks.startIndex, - animIndex = 0, - - registryByName = Popcorn.registryByName, - - byEnd, byStart, byAnimate, natives, type; - - // Playbar advancing - if ( previousTime <= currentTime ) { - - while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end <= currentTime ) { - - byEnd = tracks.byEnd[ end ]; - natives = byEnd._natives; - type = natives && natives.type; - - // If plugin does not exist on this instance, remove it - if ( !natives || - ( !!registryByName[ type ] || - !!obj[ type ] ) ) { - - if ( byEnd._running === true ) { - byEnd._running = false; - natives.end.call( obj, event, byEnd ); - } - - end++; - } else { - // remove track event - Popcorn.removeTrackEvent( obj, byEnd._id ); - return; - } - } - - while ( tracks.byStart[ start ] && tracks.byStart[ start ].start <= currentTime ) { - - byStart = tracks.byStart[ start ]; - natives = byStart._natives; - type = natives && natives.type; - - // If plugin does not exist on this instance, remove it - if ( !natives || - ( !!registryByName[ type ] || - !!obj[ type ] ) ) { - - if ( byStart.end > currentTime && - byStart._running === false && - obj.data.disabled.indexOf( type ) === -1 ) { - - byStart._running = true; - natives.start.call( obj, event, byStart ); - - // If the `frameAnimation` option is used, - // push the current byStart object into the `animating` cue - if ( obj.options.frameAnimation && - ( byStart && byStart._running && byStart._natives.frame ) ) { - - animating.push( byStart ); - } - } - start++; - } else { - // remove track event - Popcorn.removeTrackEvent( obj, byStart._id ); - return; - } - } - - // If the `frameAnimation` option is used, iterate the animating track - // and execute the `frame` callback - if ( obj.options.frameAnimation ) { - while ( animIndex < animating.length ) { - - byAnimate = animating[ animIndex ]; - - if ( !byAnimate._running ) { - animating.splice( animIndex, 1 ); - } else { - byAnimate._natives.frame.call( obj, event, byAnimate, currentTime ); - animIndex++; - } - } - } - - // Playbar receding - } else if ( previousTime > currentTime ) { - - while ( tracks.byStart[ start ] && tracks.byStart[ start ].start > currentTime ) { - - byStart = tracks.byStart[ start ]; - natives = byStart._natives; - type = natives && natives.type; - - // if plugin does not exist on this instance, remove it - if ( !natives || - ( !!registryByName[ type ] || - !!obj[ type ] ) ) { - - if ( byStart._running === true ) { - byStart._running = false; - natives.end.call( obj, event, byStart ); - } - start--; - } else { - // remove track event - Popcorn.removeTrackEvent( obj, byStart._id ); - return; - } - } - - while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end > currentTime ) { - - byEnd = tracks.byEnd[ end ]; - natives = byEnd._natives; - type = natives && natives.type; - - // if plugin does not exist on this instance, remove it - if ( !natives || - ( !!registryByName[ type ] || - !!obj[ type ] ) ) { - - if ( byEnd.start <= currentTime && - byEnd._running === false && - obj.data.disabled.indexOf( type ) === -1 ) { - - byEnd._running = true; - natives.start.call( obj, event, byEnd ); - - // If the `frameAnimation` option is used, - // push the current byEnd object into the `animating` cue - if ( obj.options.frameAnimation && - ( byEnd && byEnd._running && byEnd._natives.frame ) ) { - - animating.push( byEnd ); - } - } - end--; - } else { - // remove track event - Popcorn.removeTrackEvent( obj, byEnd._id ); - return; - } - } - - // If the `frameAnimation` option is used, iterate the animating track - // and execute the `frame` callback - if ( obj.options.frameAnimation ) { - while ( animIndex < animating.length ) { - - byAnimate = animating[ animIndex ]; - - if ( !byAnimate._running ) { - animating.splice( animIndex, 1 ); - } else { - byAnimate._natives.frame.call( obj, event, byAnimate, currentTime ); - animIndex++; - } - } - } - // time bar is not moving ( video is paused ) - } - - tracks.endIndex = end; - tracks.startIndex = start; - tracks.previousUpdateTime = currentTime; - }; - - // Map and Extend TrackEvent functions to all Popcorn instances - Popcorn.extend( Popcorn.p, { - - getTrackEvents: function() { - return Popcorn.getTrackEvents.call( null, this ); - }, - - getTrackEvent: function( id ) { - return Popcorn.getTrackEvent.call( null, this, id ); - }, - - getLastTrackEventId: function() { - return Popcorn.getLastTrackEventId.call( null, this ); - }, - - removeTrackEvent: function( id ) { - - Popcorn.removeTrackEvent.call( null, this, id ); - return this; - }, - - removePlugin: function( name ) { - Popcorn.removePlugin.call( null, this, name ); - return this; - }, - - timeUpdate: function( event ) { - Popcorn.timeUpdate.call( null, this, event ); - return this; - }, - - destroy: function() { - Popcorn.destroy.call( null, this ); - return this; - } - }); - - // Plugin manifests - Popcorn.manifest = {}; - // Plugins are registered - Popcorn.registry = []; - Popcorn.registryByName = {}; - // An interface for extending Popcorn - // with plugin functionality - Popcorn.plugin = function( name, definition, manifest ) { - - if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) { - Popcorn.error( "'" + name + "' is a protected function name" ); - return; - } - - // Provides some sugar, but ultimately extends - // the definition into Popcorn.p - var reserved = [ "start", "end" ], - plugin = {}, - setup, - isfn = typeof definition === "function", - methods = [ "_setup", "_teardown", "start", "end", "frame" ]; - - // combines calls of two function calls into one - var combineFn = function( first, second ) { - - first = first || Popcorn.nop; - second = second || Popcorn.nop; - - return function() { - first.apply( this, arguments ); - second.apply( this, arguments ); - }; - }; - - // If `manifest` arg is undefined, check for manifest within the `definition` object - // If no `definition.manifest`, an empty object is a sufficient fallback - Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {}; - - // apply safe, and empty default functions - methods.forEach(function( method ) { - definition[ method ] = safeTry( definition[ method ] || Popcorn.nop, name ); - }); - - var pluginFn = function( setup, options ) { - - if ( !options ) { - return this; - } - - // Storing the plugin natives - var natives = options._natives = {}, - compose = "", - defaults, originalOpts, manifestOpts, mergedSetupOpts; - - Popcorn.extend( natives, setup ); - - options._natives.type = name; - options._running = false; - - natives.start = natives.start || natives[ "in" ]; - natives.end = natives.end || natives[ "out" ]; - - // Check for previously set default options - defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ]; - - // default to an empty string if no effect exists - // split string into an array of effects - options.compose = options.compose && options.compose.split( " " ) || []; - options.effect = options.effect && options.effect.split( " " ) || []; - - // join the two arrays together - options.compose = options.compose.concat( options.effect ); - - options.compose.forEach(function( composeOption ) { - - // if the requested compose is garbage, throw it away - compose = Popcorn.compositions[ composeOption ] || {}; - - // extends previous functions with compose function - methods.forEach(function( method ) { - natives[ method ] = combineFn( natives[ method ], compose[ method ] ); - }); - }); - - // Ensure a manifest object, an empty object is a sufficient fallback - options._natives.manifest = manifest; - - // Checks for expected properties - if ( !( "start" in options ) ) { - options.start = options[ "in" ] || 0; - } - - if ( !( "end" in options ) ) { - options.end = options[ "out" ] || this.duration() || Number.MAX_VALUE; - } - - // Merge with defaults if they exist, make sure per call is prioritized - mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) : - options; - - // Resolves 239, 241, 242 - if ( !mergedSetupOpts.target ) { - - // Sometimes the manifest may be missing entirely - // or it has an options object that doesn't have a `target` property - manifestOpts = "options" in manifest && manifest.options; - - mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target; - } - - // Trigger _setup method if exists - options._natives._setup && options._natives._setup.call( this, mergedSetupOpts ); - - // Create new track event for this instance - Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) ); - - // Future support for plugin event definitions - // for all of the native events - Popcorn.forEach( setup, function( callback, type ) { - - if ( type !== "type" ) { - - if ( reserved.indexOf( type ) === -1 ) { - - this.listen( type, callback ); - } - } - - }, this ); - - return this; - }; - - // Assign new named definition - plugin[ name ] = function( options ) { - return pluginFn.call( this, isfn ? definition.call( this, options ) : definition, - options ); - }; - - // Extend Popcorn.p with new named definition - Popcorn.extend( Popcorn.p, plugin ); - - // Push into the registry - var entry = { - fn: plugin[ name ], - definition: definition, - base: definition, - parents: [], - name: name - }; - Popcorn.registry.push( - Popcorn.extend( plugin, entry, { - type: name - }) - ); - Popcorn.registryByName[ name ] = entry; - - return plugin; - }; - - // Storage for plugin function errors - Popcorn.plugin.errors = []; - - // Returns wrapped plugin function - function safeTry( fn, pluginName ) { - return function() { - - // When Popcorn.plugin.debug is true, do not suppress errors - if ( Popcorn.plugin.debug ) { - return fn.apply( this, arguments ); - } - - try { - return fn.apply( this, arguments ); - } catch ( ex ) { - - // Push plugin function errors into logging queue - Popcorn.plugin.errors.push({ - plugin: pluginName, - thrown: ex, - source: fn.toString() - }); - - // Trigger an error that the instance can listen for - // and react to - this.trigger( "error", Popcorn.plugin.errors ); - } - }; - } - - // Debug-mode flag for plugin development - Popcorn.plugin.debug = false; - - // removePlugin( type ) removes all tracks of that from all instances of popcorn - // removePlugin( obj, type ) removes all tracks of type from obj, where obj is a single instance of popcorn - Popcorn.removePlugin = function( obj, name ) { - - // Check if we are removing plugin from an instance or from all of Popcorn - if ( !name ) { - - // Fix the order - name = obj; - obj = Popcorn.p; - - if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) { - Popcorn.error( "'" + name + "' is a protected function name" ); - return; - } - - var registryLen = Popcorn.registry.length, - registryIdx; - - // remove plugin reference from registry - for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) { - if ( Popcorn.registry[ registryIdx ].name === name ) { - Popcorn.registry.splice( registryIdx, 1 ); - delete Popcorn.registryByName[ name ]; - - // delete the plugin - delete obj[ name ]; - - // plugin found and removed, stop checking, we are done - return; - } - } - - } - - var byStart = obj.data.trackEvents.byStart, - byEnd = obj.data.trackEvents.byEnd, - animating = obj.data.trackEvents.animating, - idx, sl; - - // remove all trackEvents - for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) { - - if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) && - ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) { - - byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] ); - - byStart.splice( idx, 1 ); - byEnd.splice( idx, 1 ); - - // update for loop if something removed, but keep checking - idx--; sl--; - if ( obj.data.trackEvents.startIndex <= idx ) { - obj.data.trackEvents.startIndex--; - obj.data.trackEvents.endIndex--; - } - } - } - - //remove all animating events - for ( idx = 0, sl = animating.length; idx < sl; idx++ ) { - - if ( animating[ idx ] && animating[ idx ]._natives && animating[ idx ]._natives.type === name ) { - - animating.splice( idx, 1 ); - - // update for loop if something removed, but keep checking - idx--; sl--; - } - } - - }; - - Popcorn.compositions = {}; - - // Plugin inheritance - Popcorn.compose = function( name, definition, manifest ) { - - // If `manifest` arg is undefined, check for manifest within the `definition` object - // If no `definition.manifest`, an empty object is a sufficient fallback - Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {}; - - // register the effect by name - Popcorn.compositions[ name ] = definition; - }; - - Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose; - - // stores parsers keyed on filetype - Popcorn.parsers = {}; - - // An interface for extending Popcorn - // with parser functionality - Popcorn.parser = function( name, type, definition ) { - - if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) { - Popcorn.error( "'" + name + "' is a protected function name" ); - return; - } - - // fixes parameters for overloaded function call - if ( typeof type === "function" && !definition ) { - definition = type; - type = ""; - } - - if ( typeof definition !== "function" || typeof type !== "string" ) { - return; - } - - // Provides some sugar, but ultimately extends - // the definition into Popcorn.p - - var natives = Popcorn.events.all, - parseFn, - parser = {}; - - parseFn = function( filename, callback ) { - - if ( !filename ) { - return this; - } - - var that = this; - - Popcorn.xhr({ - url: filename, - dataType: type, - success: function( data ) { - - var tracksObject = definition( data ), - tracksData, - tracksDataLen, - tracksDef, - idx = 0; - - tracksData = tracksObject.data || []; - tracksDataLen = tracksData.length; - tracksDef = null; - - // If no tracks to process, return immediately - if ( !tracksDataLen ) { - return; - } - - // Create tracks out of parsed object - for ( ; idx < tracksDataLen; idx++ ) { - - tracksDef = tracksData[ idx ]; - - for ( var key in tracksDef ) { - - if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) { - - that[ key ]( tracksDef[ key ] ); - } - } - } - if ( callback ) { - callback(); - } - } - }); - - return this; - }; - - // Assign new named definition - parser[ name ] = parseFn; - - // Extend Popcorn.p with new named definition - Popcorn.extend( Popcorn.p, parser ); - - // keys the function name by filetype extension - //Popcorn.parsers[ name ] = true; - - return parser; - }; - - Popcorn.player = function( name, player ) { - - player = player || {}; - - var playerFn = function( target, src, options ) { - - options = options || {}; - - // List of events - var date = new Date() / 1000, - baselineTime = date, - currentTime = 0, - volume = 1, - muted = false, - events = {}, - - // The container div of the resource - container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) || - document.getElementById( target ) || - target, - basePlayer = {}, - timeout, - popcorn; - - // copies a div into the media object - for( var val in container ) { - - if ( typeof container[ val ] === "object" ) { - - basePlayer[ val ] = container[ val ]; - } else if ( typeof container[ val ] === "function" ) { - - basePlayer[ val ] = (function( value ) { - - return function() { - - return container[ value ].apply( container, arguments ); - }; - }( val )); - } else { - - Popcorn.player.defineProperty( basePlayer, val, { - get: (function( value ) { - - return function() { - - return container[ value ]; - }; - }( val )), - set: Popcorn.nop, - configurable: true - }); - } - } - - var timeupdate = function() { - - date = new Date() / 1000; - - if ( !basePlayer.paused ) { - - basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime ); - basePlayer.dispatchEvent( "timeupdate" ); - timeout = setTimeout( timeupdate, 10 ); - } - - baselineTime = date; - }; - - basePlayer.play = function() { - - this.paused = false; - - if ( basePlayer.readyState >= 4 ) { - - baselineTime = new Date() / 1000; - basePlayer.dispatchEvent( "play" ); - timeupdate(); - } - }; - - basePlayer.pause = function() { - - this.paused = true; - basePlayer.dispatchEvent( "pause" ); - }; - - Popcorn.player.defineProperty( basePlayer, "currentTime", { - get: function() { - - return currentTime; - }, - set: function( val ) { - - // make sure val is a number - currentTime = +val; - basePlayer.dispatchEvent( "timeupdate" ); - return currentTime; - }, - configurable: true - }); - - Popcorn.player.defineProperty( basePlayer, "volume", { - get: function() { - - return volume; - }, - set: function( val ) { - - // make sure val is a number - volume = +val; - basePlayer.dispatchEvent( "volumechange" ); - return volume; - }, - configurable: true - }); - - Popcorn.player.defineProperty( basePlayer, "muted", { - get: function() { - - return muted; - }, - set: function( val ) { - - // make sure val is a number - muted = +val; - basePlayer.dispatchEvent( "volumechange" ); - return muted; - }, - configurable: true - }); - - // Adds an event listener to the object - basePlayer.addEventListener = function( evtName, fn ) { - - if ( !events[ evtName ] ) { - - events[ evtName ] = []; - } - - events[ evtName ].push( fn ); - return fn; - }; - - // Can take event object or simple string - basePlayer.dispatchEvent = function( oEvent ) { - - var evt, - self = this, - eventInterface, - eventName = oEvent.type; - - // A string was passed, create event object - if ( !eventName ) { - - eventName = oEvent; - eventInterface = Popcorn.events.getInterface( eventName ); - - if ( eventInterface ) { - - evt = document.createEvent( eventInterface ); - evt.initEvent( eventName, true, true, window, 1 ); - } - } - - Popcorn.forEach( events[ eventName ], function( val ) { - - val.call( self, evt, self ); - }); - }; - - // Attempt to get src from playerFn parameter - basePlayer.src = src || ""; - basePlayer.readyState = 0; - basePlayer.duration = 0; - basePlayer.paused = true; - basePlayer.ended = 0; - - if ( player._setup ) { - - player._setup.call( basePlayer, options ); - } else { - - // there is no setup, which means there is nothing to load - basePlayer.readyState = 4; - basePlayer.dispatchEvent( "load" ); - basePlayer.dispatchEvent( "loadeddata" ); - } - - // when a custom player is loaded, load basePlayer state into custom player - basePlayer.addEventListener( "load", function() { - - // if a player is not ready before currentTime is called, this will set it after it is ready - basePlayer.currentTime = currentTime; - - // same as above with volume and muted - basePlayer.volume = volume; - basePlayer.muted = muted; - }); - - basePlayer.addEventListener( "loadeddata", function() { - - // if play was called before player ready, start playing video - !basePlayer.paused && basePlayer.play(); - }); - - popcorn = new Popcorn.p.init( basePlayer, options ); - - return popcorn; - }; - - Popcorn[ name ] = Popcorn[ name ] || playerFn; - }; - - Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) { - - object.__defineGetter__( description, options.get || Popcorn.nop ); - object.__defineSetter__( description, options.set || Popcorn.nop ); - }; - - // Cache references to reused RegExps - var rparams = /\?/, - // XHR Setup object - setup = { - url: "", - data: "", - dataType: "", - success: Popcorn.nop, - type: "GET", - async: true, - xhr: function() { - return new global.XMLHttpRequest(); - } - }; - - Popcorn.xhr = function( options ) { - - options.dataType = options.dataType && options.dataType.toLowerCase() || null; - - if ( options.dataType && - ( options.dataType === "jsonp" || options.dataType === "script" ) ) { - - Popcorn.xhr.getJSONP( - options.url, - options.success, - options.dataType === "script" - ); - return; - } - - var settings = Popcorn.extend( {}, setup, options ); - - // Create new XMLHttpRequest object - settings.ajax = settings.xhr(); - - if ( settings.ajax ) { - - if ( settings.type === "GET" && settings.data ) { - - // append query string - settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data; - - // Garbage collect and reset settings.data - settings.data = null; - } - - - settings.ajax.open( settings.type, settings.url, settings.async ); - settings.ajax.send( settings.data || null ); - - return Popcorn.xhr.httpData( settings ); - } - }; - - - Popcorn.xhr.httpData = function( settings ) { - - var data, json = null; - - settings.ajax.onreadystatechange = function() { - - if ( settings.ajax.readyState === 4 ) { - - try { - json = JSON.parse( settings.ajax.responseText ); - } catch( e ) { - //suppress - } - - data = { - xml: settings.ajax.responseXML, - text: settings.ajax.responseText, - json: json - }; - - // If a dataType was specified, return that type of data - if ( settings.dataType ) { - data = data[ settings.dataType ]; - } - - - settings.success.call( settings.ajax, data ); - - } - }; - return data; - }; - - Popcorn.xhr.getJSONP = function( url, success, isScript ) { - - var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement, - script = document.createElement( "script" ), - paramStr = url.split( "?" )[ 1 ], - isFired = false, - params = [], - callback, parts, callparam; - - if ( paramStr && !isScript ) { - params = paramStr.split( "&" ); - } - - if ( params.length ) { - parts = params[ params.length - 1 ].split( "=" ); - } - - callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp"; - - if ( !paramStr && !isScript ) { - url += "?callback=" + callback; - } - - if ( callback && !isScript ) { - - // If a callback name already exists - if ( !!window[ callback ] ) { - // Create a new unique callback name - callback = Popcorn.guid( callback ); - } - - // Define the JSONP success callback globally - window[ callback ] = function( data ) { - // Fire success callbacks - success && success( data ); - isFired = true; - }; - - // Replace callback param and callback name - url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback ); - } - - script.onload = function() { - - // Handling remote script loading callbacks - if ( isScript ) { - // getScript - success && success(); - } - - // Executing for JSONP requests - if ( isFired ) { - // Garbage collect the callback - delete window[ callback ]; - } - // Garbage collect the script resource - head.removeChild( script ); - }; - - script.src = url; - - head.insertBefore( script, head.firstChild ); - - return; - }; - - Popcorn.getJSONP = Popcorn.xhr.getJSONP; - - Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) { - - return Popcorn.xhr.getJSONP( url, success, true ); - }; - - Popcorn.util = { - // Simple function to parse a timestamp into seconds - // Acceptable formats are: - // HH:MM:SS.MMM - // HH:MM:SS;FF - // Hours and minutes are optional. They default to 0 - toSeconds: function( timeStr, framerate ) { - // Hours and minutes are optional - // Seconds must be specified - // Seconds can be followed by milliseconds OR by the frame information - var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/, - errorMessage = "Invalid time format", - digitPairs, lastIndex, lastPair, firstPair, - frameInfo, frameTime; - - if ( typeof timeStr === "number" ) { - return timeStr; - } - - if ( typeof timeStr === "string" && - !validTimeFormat.test( timeStr ) ) { - Popcorn.error( errorMessage ); - } - - digitPairs = timeStr.split( ":" ); - lastIndex = digitPairs.length - 1; - lastPair = digitPairs[ lastIndex ]; - - // Fix last element: - if ( lastPair.indexOf( ";" ) > -1 ) { - - frameInfo = lastPair.split( ";" ); - frameTime = 0; - - if ( framerate && ( typeof framerate === "number" ) ) { - frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate; - } - - digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime; - } - - firstPair = digitPairs[ 0 ]; - - return { - - 1: parseFloat( firstPair, 10 ), - - 2: ( parseInt( firstPair, 10 ) * 60 ) + - parseFloat( digitPairs[ 1 ], 10 ), - - 3: ( parseInt( firstPair, 10 ) * 3600 ) + - ( parseInt( digitPairs[ 1 ], 10 ) * 60 ) + - parseFloat( digitPairs[ 2 ], 10 ) - - }[ digitPairs.length || 1 ]; - } - }; - - - // Initialize locale data - // Based on http://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes - function initLocale( arg ) { - - var locale = typeof arg === "string" ? arg : [ arg.language, arg.region ].join( "-" ), - parts = locale.split( "-" ); - - // Setup locale data table - return { - iso6391: locale, - language: parts[ 0 ] || "", - region: parts[ 1 ] || "" - }; - } - - // Declare locale data table - var localeData = initLocale( global.navigator.userLanguage || global.navigator.language ); - - Popcorn.locale = { - - // Popcorn.locale.get() - // returns reference to privately - // defined localeData - get: function() { - return localeData; - }, - - // Popcorn.locale.set( string|object ); - set: function( arg ) { - - localeData = initLocale( arg ); - - Popcorn.locale.broadcast(); - - return localeData; - }, - - // Popcorn.locale.broadcast( type ) - // Sends events to all popcorn media instances that are - // listening for locale events - broadcast: function( type ) { - - var instances = Popcorn.instances, - length = instances.length, - idx = 0, - instance; - - type = type || "locale:changed"; - - // Iterate all current instances - for ( ; idx < length; idx++ ) { - instance = instances[ idx ]; - - // For those instances with locale event listeners, - // trigger a locale change event - if ( type in instance.data.events ) { - instance.trigger( type ); - } - } - } - }; - - // alias for exec function - Popcorn.p.cue = Popcorn.p.exec; - - function getItems() { - - var item, - list = []; - - if ( Object.keys ) { - list = Object.keys( Popcorn.p ); - } else { - - for ( item in Popcorn.p ) { - if ( hasOwn.call( Popcorn.p, item ) ) { - list.push( item ); - } - } - } - - return list.join( "," ).toLowerCase().split( ","); - } - - // Protected API methods - Popcorn.protect = { - natives: getItems() - }; - - // Exposes Popcorn to global context - global.Popcorn = Popcorn; - -})(window, window.document); diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/libs/popcorn.jwplayer.js --- a/src/js/libs/popcorn.jwplayer.js Fri Dec 02 11:06:30 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -var jwplayerObjects = {}; - -Popcorn.player( "jwplayer", { - _setup: function( options ) { - - var media = this, - player = {}, - container = document.createElement( "div" ), - currentTime = 0, - seekTime = 0, - seeking = false, - dataLoaded = false; - container.id = media.id + Popcorn.guid(); - - media.appendChild( container ); - - var initApi = function () { - jwplayer( container.id ).onTime(function() { - currentTime = jwplayer(container.id).getPosition(); - media.dispatchEvent( "timeupdate" ); - // timeout = setTimeout( timeupdate, 10 ); - }); - - media.play = function() { - media.paused = false; - media.dispatchEvent( "play" ); - - media.dispatchEvent( "playing" ); - jwplayer( container.id ).play(); - }; - - media.pause = function() { - - if ( !media.paused ) { - media.paused = true; - media.dispatchEvent( "pause" ); - jwplayer( container.id ).pause(); - } - }; - - Popcorn.player.defineProperty( media, "currentTime", { - set: function( val ) { - // make sure val is a number - currentTime = seekTime = +val; - seeking = true; - media.dispatchEvent( "seeked" ); - media.dispatchEvent( "timeupdate" ); - jwplayer( container.id ).seek( currentTime ); - return currentTime; - }, - get: function() { - return jwplayer( container.id ).getPosition(); - } - }); - - Popcorn.player.defineProperty( media, "muted", { - set: function( val ) { - if ( jwplayer( container.id ).getMute() !== val ) { - if ( val ) { - jwplayer( container.id ).setMute(true); - } else { - jwplayer( container.id ).setMute(false); - } - - media.dispatchEvent( "volumechange" ); - } - - return jwplayer( container.id ).getMute(); - }, - get: function() { - return jwplayer( container.id ).getMute(); - } - }); - - Popcorn.player.defineProperty( media, "volume", { - - set: function( val ) { - - if ( jwplayer( container.id ).getVolume() !== val *100 ) { - jwplayer( container.id ).setVolume( val * 100); - media.dispatchEvent( "volumechange" ); - } - - return (jwplayer( container.id ).getVolume()) / 100; - }, - - get: function() { - return jwplayer( container.id ).getVolume() / 100; - } - }); - - media.readyState = 4; - media.dispatchEvent( 'load' ); - dataLoaded = true; - - media.duration = options.duration; - media.dispatchEvent( 'durationchange' ); - - media.paused && media.dispatchEvent( 'loadeddata' ); - - }; - - options.events = { - onReady: initApi - }; - - jwplayer( container.id ).setup(options); - - } -}); - diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/libs/popcorn.mediafragment.js --- a/src/js/libs/popcorn.mediafragment.js Fri Dec 02 11:06:30 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -// PLUGIN: Mediafragment - -(function ( Popcorn ) { - - /** - * Mediafragment popcorn plug-in - * Adds (limited) support for mediafragment requests - * to a popcorn video. - * @param {Object} options - * - **/ - Popcorn.plugin( "mediafragment" , { - - manifest: { - about: { - name: "Popcorn mediafragment plugin", - version: "0.1", - author: "Karim Hamidou", - website: "http://neyret.fr/~karim" - }, - options: { - } - }, - - _setup: function( options ) { - var advanceTime = function() { - var url = window.location.href; - - if ( url.split( "#" )[ 1 ] != null ) { - pageoffset = url.split( "#" )[1]; - - if ( pageoffset.substring( 2 ) != null ) { - var offsettime = pageoffset.substring( 2 ); - this.currentTime( parseFloat( offsettime ) ); - } - } - } - - var updateTime = function() { - var history = window.history; - if ( !history.pushState ) { - return false; - } - - splitArr = window.location.href.split( "#" ) - history.replaceState( {}, "", splitArr[0] + "#t=" + this.currentTime().toFixed( 2 ) ); - }; - - this.listen( "loadedmetadata", advanceTime ); - this.listen( "pause", updateTime ); - this.listen( "seeked", updateTime ); - }, - - _teardown: function( options ) { - // FIXME: anything to implement here ? - } - }); -})( Popcorn ); diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/libs/popcorn.youtube.js --- a/src/js/libs/popcorn.youtube.js Fri Dec 02 11:06:30 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,220 +0,0 @@ -// A global callback for youtube... that makes me angry -var onYouTubePlayerReady = function( containerId ) { - - onYouTubePlayerReady[ containerId ] && onYouTubePlayerReady[ containerId ](); -}; -onYouTubePlayerReady.stateChangeEventHandler = {}; - -Popcorn.player( "youtube", { - _setup: function( options ) { - - var media = this, - youtubeObject, - container = document.createElement( "div" ), - currentTime = 0, - seekTime = 0, - seeking = false, - - // state code for volume changed polling - volumeChanged = false, - lastMuted = false, - lastVolume = 0; - - container.id = media.id + Popcorn.guid(); - - media.appendChild( container ); - - var youtubeInit = function() { - - var flashvars, - params, - attributes, - src; - - // expose a callback to this scope, that is called from the global callback youtube calls - onYouTubePlayerReady[ container.id ] = function() { - - youtubeObject = document.getElementById( container.id ); - - // more youtube callback nonsense - onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) { - - // playing is state 1 - // paused is state 2 - if ( state === 1 ) { - - media.paused && media.play(); - // youtube fires paused events while seeking - // this is the only way to get seeking events - } else if ( state === 2 ) { - - // silly logic forced on me by the youtube API - // calling youtube.seekTo triggers multiple events - // with the second events getCurrentTime being the old time - if ( seeking && seekTime === currentTime && seekTime !== youtubeObject.getCurrentTime() ) { - - seeking = false; - youtubeObject.seekTo( currentTime ); - return; - } - - currentTime = youtubeObject.getCurrentTime(); - media.dispatchEvent( "timeupdate" ); - !media.paused && media.pause(); - } - }; - - // youtube requires callbacks to be a string to a function path from the global scope - youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id ); - - var timeupdate = function() { - - if ( !media.paused ) { - - currentTime = youtubeObject.getCurrentTime(); - media.dispatchEvent( "timeupdate" ); - setTimeout( timeupdate, 10 ); - } - }; - - var volumeupdate = function() { - - if ( lastMuted !== youtubeObject.isMuted() ) { - - lastMuted = youtubeObject.isMuted(); - media.dispatchEvent( "volumechange" ); - } - - if ( lastVolume !== youtubeObject.getVolume() ) { - - lastVolume = youtubeObject.getVolume(); - media.dispatchEvent( "volumechange" ); - } - - setTimeout( volumeupdate, 250 ); - }; - - media.play = function() { - - media.paused = false; - media.dispatchEvent( "play" ); - - media.dispatchEvent( "playing" ); - timeupdate(); - youtubeObject.playVideo(); - }; - - media.pause = function() { - - if ( !media.paused ) { - - media.paused = true; - media.dispatchEvent( "pause" ); - youtubeObject.pauseVideo(); - } - }; - - Popcorn.player.defineProperty( media, "currentTime", { - set: function( val ) { - - // make sure val is a number - currentTime = seekTime = +val; - seeking = true; - media.dispatchEvent( "seeked" ); - media.dispatchEvent( "timeupdate" ); - youtubeObject.seekTo( currentTime ); - return currentTime; - }, - get: function() { - - return currentTime; - } - }); - - Popcorn.player.defineProperty( media, "muted", { - set: function( val ) { - - if ( youtubeObject.isMuted() !== val ) { - - if ( val ) { - - youtubeObject.mute(); - } else { - - youtubeObject.unMute(); - } - - lastMuted = youtubeObject.isMuted(); - media.dispatchEvent( "volumechange" ); - } - - return youtubeObject.isMuted(); - }, - get: function() { - - return youtubeObject.isMuted(); - } - }); - - Popcorn.player.defineProperty( media, "volume", { - set: function( val ) { - - if ( youtubeObject.getVolume() !== val ) { - - youtubeObject.setVolume( val ); - lastVolume = youtubeObject.getVolume(); - media.dispatchEvent( "volumechange" ); - } - - return youtubeObject.getVolume(); - }, - get: function() { - - return youtubeObject.getVolume(); - } - }); - - media.readyState = 4; - media.dispatchEvent( "load" ); - media.duration = youtubeObject.getDuration(); - media.dispatchEvent( "durationchange" ); - volumeupdate(); - - media.dispatchEvent( "loadeddata" ); - }; - - options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1; - options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1; - - flashvars = { - playerapiid: container.id, - controls: options.controls, - iv_load_policy: options.annotations - }; - - params = { - wmode: "transparent", - allowScriptAccess: "always" - }; - - attributes = { - id: container.id - }; - - src = /^.*[\/=](.{11})/.exec( media.src )[ 1 ]; - - swfobject.embedSWF( "http://www.youtube.com/e/" + src + "?enablejsapi=1&playerapiid=" + container.id + "&version=3", - container.id, media.offsetWidth, media.offsetHeight, "8", null, - flashvars, params, attributes ); - }; - - if ( !window.swfobject ) { - - Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", youtubeInit ); - } else { - - youtubeInit(); - } - } -}); - diff -r d1fe53ad8d72 -r 30277c1e3d46 src/js/pop.js --- a/src/js/pop.js Fri Dec 02 11:06:30 2011 +0100 +++ b/src/js/pop.js Fri Dec 02 11:31:54 2011 +0100 @@ -1,2 +1,81 @@ /* wrapper that simulates popcorn.js because popcorn is a bit unstable at the time */ + +Popcorn = {}; +Popcorn.listen = function(msg, callback) { + IriSP.jQuery(Popcorn).bind(msg, callback); +}; + +Popcorn.trigger = function(msg, params) { + IriSP.jQuery(msg, params); +}; + +Popcorn.guid = function(prefix) { + var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); + + return prefix + str; +}; + +Popcorn.__initApi = function() { + Popcorn.trigger("timeupdate"); +}; + +Popcorn.jwplayer = function(container, options) { + Popcorn._container = container; + options.events = { + onReady: Popcorn.__initApi + }; + + jwplayer(Popcorn._container).setup(options); + +}; + +Popcorn.currentTime = function(time) { + if (typeof(time) === "undefined") { + return jwplayer(Popcorn._container).getPosition(); + } else { + var currentTime = +time; + media.dispatchEvent( "seeked" ); + media.dispatchEvent( "timeupdate" ); + jwplayer( Popcorn._container ).seek( currentTime ); + return jwplayer(Popcorn._container).getPosition(); + } +}; + +Popcorn.play = function() { + Popcorn.paused = false; + Popcorn.trigger("play"); + Popcorn.trigger("playing"); + jwplayer( Popcorn._container ).play(); +}; + +Popcorn.pause = function() { + if ( !media.paused ) { + media.paused = true; + Popcorn.trigger( "pause" ); + jwplayer( Popcorn._container ).pause(); + } +}; + +Popcorn.muted = function(val) { + if (typeof(val) !== "undefined") { + if ( jwplayer( Popcorn._container ).getMute() !== val ) { + if ( val ) { + jwplayer( Popcorn._container ).setMute(true); + } else { + jwplayer( Popcorn._container ).setMute(false); + } + + media.dispatchEvent( "volumechange" ); + } + + return jwplayer( Popcorn._container ).getMute(); + } else { + return jwplayer( Popcorn._container ).getMute(); + } +}); + +