popcorn-js/popcorn.js

1482 строки
41 KiB
JavaScript
Исходник Обычный вид История

(function(global, document) {
2010-12-04 23:54:38 +03:00
// Cache refs to speed up calls to native utils
2011-02-14 21:38:50 +03:00
var
AP = Array.prototype,
OP = Object.prototype,
forEach = AP.forEach,
slice = AP.slice,
hasOwn = OP.hasOwnProperty,
toString = OP.toString,
2010-12-04 23:54:38 +03:00
// ID string matching
2011-02-14 21:38:50 +03:00
rIdExp = /^(#([\w\-\_\.]+))$/,
2011-03-18 18:33:05 +03:00
// Ready fn cache
2011-02-14 21:38:50 +03:00
readyStack = [],
readyBound = false,
readyFired = false,
2011-02-14 21:38:50 +03:00
// Non-public internal data object
internal = {
events: {
hash: {},
apis: {}
}
},
2010-12-04 23:54:38 +03:00
2011-03-18 18:33:05 +03:00
// Declare constructor
2011-02-14 21:38:50 +03:00
// Returns an instance object.
Popcorn = function( entity, options ) {
2010-12-06 01:37:33 +03:00
// Return new Popcorn object
return new Popcorn.p.init( entity, options || null );
2010-12-04 23:54:38 +03:00
};
// Instance caching
2011-02-02 01:52:07 +03:00
Popcorn.instances = [];
Popcorn.instanceIds = {};
Popcorn.removeInstance = function( instance ) {
// If called prior to any instances being created
// Return early to avoid splicing on nothing
if ( !Popcorn.instances.length ) {
return;
}
// Remove instance from Popcorn.instances
Popcorn.instances.splice( Popcorn.instanceIds[ instance.id ], 1 );
// Delete the instance id key
delete Popcorn.instanceIds[ instance.id ];
// Return current modified instances
return Popcorn.instances;
2011-02-02 01:52:07 +03:00
};
// Addes a Popcorn instance to the Popcorn instance array
Popcorn.addInstance = function( instance ) {
var instanceLen = Popcorn.instances.length,
2011-03-24 17:09:20 +03:00
instanceId = instance.media.id && instance.media.id;
2011-03-24 17:09:20 +03:00
// If the media element has its own `id` use it, otherwise provide one
// Ensure that instances have unique ids and unique entries
// Uses `in` operator to avoid false positives on 0
2011-03-18 18:33:05 +03:00
instance.id = !( instanceId in Popcorn.instanceIds ) && instanceId ||
2011-05-17 04:57:04 +04:00
"__popcorn" + instanceLen;
// Create a reference entry for this instance
Popcorn.instanceIds[ instance.id ] = instanceLen;
// Add this instance to the cache
Popcorn.instances.push( instance );
// Return the current modified instances
return Popcorn.instances;
2011-02-02 01:52:07 +03:00
};
2010-12-04 23:54:38 +03:00
// Request Popcorn object instance by id
Popcorn.getInstanceById = function( id ) {
return Popcorn.instances[ Popcorn.instanceIds[ id ] ];
2011-02-02 01:52:07 +03:00
};
// Remove Popcorn object instance by id
Popcorn.removeInstanceById = function( id ) {
return Popcorn.removeInstance( Popcorn.instances[ Popcorn.instanceIds[ id ] ] );
};
2011-02-14 21:38:50 +03:00
// Declare a shortcut (Popcorn.p) to and a definition of
// the new prototype for our Popcorn constructor
2010-12-04 23:54:38 +03:00
Popcorn.p = Popcorn.prototype = {
init: function( entity, options ) {
2010-12-04 23:54:38 +03:00
var matches;
2011-02-14 21:38:50 +03:00
// Supports Popcorn(function () { /../ })
// Originally proposed by Daniel Brooks
2011-02-14 21:38:50 +03:00
2010-12-20 18:01:13 +03:00
if ( typeof entity === "function" ) {
2011-02-14 21:38:50 +03:00
// If document ready has already fired
if ( document.readyState === "interactive" || document.readyState === "complete" ) {
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
entity( document, Popcorn );
2011-02-14 21:38:50 +03:00
return;
}
2011-03-18 18:33:05 +03:00
// Add `entity` fn to ready stack
readyStack.push( entity );
// This process should happen once per page load
if ( !readyBound ) {
// set readyBound flag
readyBound = true;
2011-05-17 04:57:04 +04:00
var DOMContentLoaded = function() {
2011-02-14 21:38:50 +03:00
readyFired = true;
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Remove global DOM ready listener
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// Execute all ready function in the stack
2011-05-17 04:57:04 +04:00
for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) {
2011-05-17 04:57:04 +04:00
readyStack[ i ].call( document, Popcorn );
2010-12-04 23:54:38 +03:00
}
// GC readyStack
2011-02-14 21:38:50 +03:00
readyStack = null;
};
2011-03-18 18:33:05 +03:00
// Register global DOM ready listener
2011-05-17 04:57:04 +04:00
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
}
2011-02-14 21:38:50 +03:00
return;
2010-12-20 18:01:13 +03:00
}
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Check if entity is a valid string id
2010-12-04 23:54:38 +03:00
matches = rIdExp.exec( entity );
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
// Get media element by id or object reference
this.media = matches && matches.length && matches[ 2 ] ?
2011-05-17 04:57:04 +04:00
document.getElementById( matches[ 2 ] ) :
entity;
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
// Create an audio or video element property reference
2011-05-09 04:21:48 +04:00
this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media;
2011-03-24 17:09:20 +03:00
2011-03-18 18:33:05 +03:00
// Register new instance
Popcorn.addInstance( this );
2011-02-14 21:38:50 +03:00
this.options = options || {};
2010-12-04 23:54:38 +03:00
this.data = {
// Allows disabling a plugin per instance
disabled: [],
// Stores DOM event queues by type
2010-12-04 23:54:38 +03:00
events: {},
// Store track event history data
history: [],
// Store track event object references by trackId
trackRefs: {},
// Playback track event queues
trackEvents: {
2011-02-16 00:55:52 +03:00
byStart: [{
start: -1,
end: -1
}],
byEnd: [{
start: -1,
end: -1
}],
2010-12-07 19:15:30 +03:00
startIndex: 0,
2010-12-10 02:18:12 +03:00
endIndex: 0,
2010-12-07 19:15:30 +03:00
previousUpdateTime: 0
}
2010-12-04 23:54:38 +03:00
};
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Wrap true ready check
2010-12-14 00:38:57 +03:00
var isReady = function( that ) {
2010-12-10 19:53:47 +03:00
2011-03-24 17:09:20 +03:00
if ( that.media.readyState >= 2 ) {
2011-03-18 18:33:05 +03:00
// Adding padding to the front and end of the arrays
// this is so we do not fall off either end
2010-12-10 19:53:47 +03:00
var duration = that.media.duration,
// Check for no duration info (NaN)
videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
2011-02-16 20:06:45 +03:00
2010-12-14 00:43:24 +03:00
Popcorn.addTrackEvent( that, {
start: videoDurationPlus,
end: videoDurationPlus
});
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
that.media.addEventListener( "timeupdate", function( event ) {
2010-12-10 02:18:12 +03:00
var currentTime = this.currentTime,
previousTime = that.data.trackEvents.previousUpdateTime,
tracks = that.data.trackEvents,
2010-12-10 02:18:12 +03:00
tracksByEnd = tracks.byEnd,
tracksByStart = tracks.byStart;
2011-03-18 18:33:05 +03:00
// Playbar advancing
2010-12-14 00:43:24 +03:00
if ( previousTime < currentTime ) {
2010-12-10 02:18:12 +03:00
2011-02-16 00:55:52 +03:00
while ( tracksByEnd[ tracks.endIndex ] && tracksByEnd[ tracks.endIndex ].end <= currentTime ) {
2011-03-18 18:33:05 +03:00
// If plugin does not exist on this instance, remove it
2011-02-16 00:55:52 +03:00
if ( !tracksByEnd[ tracks.endIndex ]._natives || !!that[ tracksByEnd[ tracks.endIndex ]._natives.type ] ) {
if ( tracksByEnd[ tracks.endIndex ]._running === true ) {
tracksByEnd[ tracks.endIndex ]._running = false;
tracksByEnd[ tracks.endIndex ]._natives.end.call( that, event, tracksByEnd[ tracks.endIndex ] );
}
tracks.endIndex++;
} else {
// remove track event
Popcorn.removeTrackEvent( that, tracksByEnd[ tracks.endIndex ]._id );
return;
2010-12-10 02:18:12 +03:00
}
}
2011-02-16 00:55:52 +03:00
while ( tracksByStart[ tracks.startIndex ] && tracksByStart[ tracks.startIndex ].start <= currentTime ) {
2011-03-18 18:33:05 +03:00
// If plugin does not exist on this instance, remove it
2011-02-16 00:55:52 +03:00
if ( !tracksByStart[ tracks.startIndex ]._natives || !!that[ tracksByStart[ tracks.startIndex ]._natives.type ] ) {
if ( tracksByStart[ tracks.startIndex ].end > currentTime &&
tracksByStart[ tracks.startIndex ]._running === false &&
that.data.disabled.indexOf( tracksByStart[ tracks.endIndex ]._natives.type ) === -1 ) {
2011-02-16 00:55:52 +03:00
tracksByStart[ tracks.startIndex ]._running = true;
tracksByStart[ tracks.startIndex ]._natives.start.call( that, event, tracksByStart[ tracks.startIndex ] );
}
tracks.startIndex++;
} else {
// remove track event
Popcorn.removeTrackEvent( that, tracksByStart[ tracks.startIndex ]._id );
return;
2010-12-10 02:18:12 +03:00
}
}
// Playbar receding
2010-12-14 00:43:24 +03:00
} else if ( previousTime > currentTime ) {
2010-12-10 02:18:12 +03:00
2011-02-16 00:55:52 +03:00
while ( tracksByStart[ tracks.startIndex ] && tracksByStart[ tracks.startIndex ].start > currentTime ) {
// if plugin does not exist on this instance, remove it
if ( !tracksByStart[ tracks.startIndex ]._natives || !!that[ tracksByStart[ tracks.startIndex ]._natives.type ] ) {
if ( tracksByStart[ tracks.startIndex ]._running === true ) {
tracksByStart[ tracks.startIndex ]._running = false;
tracksByStart[ tracks.startIndex ]._natives.end.call( that, event, tracksByStart[ tracks.startIndex ] );
}
tracks.startIndex--;
} else {
// remove track event
Popcorn.removeTrackEvent( that, tracksByStart[ tracks.startIndex ]._id );
return;
2010-12-10 02:18:12 +03:00
}
}
2011-02-14 21:38:50 +03:00
2011-02-16 00:55:52 +03:00
while ( tracksByEnd[ tracks.endIndex ] && tracksByEnd[ tracks.endIndex ].end > currentTime ) {
// if plugin does not exist on this instance, remove it
if ( !tracksByEnd[ tracks.endIndex ]._natives || !!that[ tracksByEnd[ tracks.endIndex ]._natives.type ] ) {
if ( tracksByEnd[ tracks.endIndex ].start <= currentTime &&
tracksByEnd[ tracks.endIndex ]._running === false &&
that.data.disabled.indexOf( tracksByStart[ tracks.endIndex ]._natives.type ) === -1 ) {
2011-02-16 00:55:52 +03:00
tracksByEnd[ tracks.endIndex ]._running = true;
tracksByEnd[ tracks.endIndex ]._natives.start.call( that, event, tracksByEnd[tracks.endIndex] );
}
tracks.endIndex--;
} else {
// remove track event
Popcorn.removeTrackEvent( that, tracksByEnd[ tracks.endIndex ]._id );
return;
2010-12-10 02:18:12 +03:00
}
}
2011-02-14 21:38:50 +03:00
}
2011-03-18 18:33:05 +03:00
2010-12-10 02:18:12 +03:00
tracks.previousUpdateTime = currentTime;
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
}, false );
2010-12-07 19:15:30 +03:00
} else {
2011-05-17 04:57:04 +04:00
global.setTimeout(function() {
2010-12-14 00:43:24 +03:00
isReady( that );
2011-05-17 04:57:04 +04:00
}, 1 );
2010-12-07 19:15:30 +03:00
}
2010-12-10 02:18:12 +03:00
};
2010-12-10 19:53:47 +03:00
2010-12-14 00:43:24 +03:00
isReady( this );
2010-12-10 02:18:12 +03:00
2010-12-04 23:54:38 +03:00
return this;
}
};
// Extend constructor prototype to instance prototype
2011-03-18 18:33:05 +03:00
// Allows chaining methods to instances
2010-12-04 23:54:38 +03:00
Popcorn.p.init.prototype = Popcorn.p;
Popcorn.forEach = function( obj, fn, context ) {
if ( !obj || !fn ) {
return {};
}
context = context || this;
// Use native whenever possible
if ( forEach && obj.forEach === forEach ) {
2011-05-17 04:57:04 +04:00
return obj.forEach( fn, context );
2011-02-14 21:38:50 +03:00
}
2010-12-04 23:54:38 +03:00
for ( var key in obj ) {
2011-05-17 04:57:04 +04:00
if ( hasOwn.call( obj, key ) ) {
fn.call( context, obj[ key ], key, obj );
2011-02-14 21:38:50 +03:00
}
}
2010-12-04 23:54:38 +03:00
return obj;
2011-02-14 21:38:50 +03:00
};
2010-12-04 23:54:38 +03:00
Popcorn.extend = function( obj ) {
2011-05-17 04:57:04 +04:00
var dest = obj, src = slice.call( arguments, 1 );
2010-12-04 23:54:38 +03:00
Popcorn.forEach( src, function( copy ) {
for ( var prop in copy ) {
2011-05-17 04:57:04 +04:00
dest[ prop ] = copy[ prop ];
2010-12-04 23:54:38 +03:00
}
});
2011-05-17 04:57:04 +04:00
2011-02-14 21:38:50 +03:00
return dest;
2010-12-04 23:54:38 +03:00
};
2010-12-10 19:53:47 +03:00
2010-12-04 23:54:38 +03:00
// A Few reusable utils, memoized onto Popcorn
Popcorn.extend( Popcorn, {
error: function( msg ) {
throw new Error( msg );
},
guid: function( prefix ) {
Popcorn.guid.counter++;
2011-03-18 18:33:05 +03:00
return ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter );
2011-02-14 21:38:50 +03:00
},
2011-05-17 06:56:43 +04:00
sizeOf: function( obj ) {
2010-12-04 23:54:38 +03:00
var size = 0;
2011-05-17 04:57:04 +04:00
for ( var prop in obj ) {
size++;
2010-12-04 23:54:38 +03:00
}
return size;
2011-02-14 21:38:50 +03:00
},
isArray: Array.isArray || function( array ) {
return toString.call( array ) === "[object Array]";
2011-05-17 04:57:04 +04:00
},
2011-04-22 01:37:04 +04:00
2011-05-17 04:57:04 +04:00
nop: function() {},
2011-04-22 01:37:04 +04:00
position: function( elem ) {
var clientRect = elem.getBoundingClientRect(),
2011-05-17 06:56:43 +04:00
bounds = {},
2011-04-22 01:37:04 +04:00
doc = elem.ownerDocument,
docElem = document.documentElement,
body = document.body,
clientTop, clientLeft, scrollTop, scrollLeft, top, left;
// Determine correct clientTop/Left
2011-05-17 06:56:43 +04:00
clientTop = docElem.clientTop || body.clientTop || 0;
2011-04-22 01:37:04 +04:00
clientLeft = docElem.clientLeft || body.clientLeft || 0;
// Determine correct scrollTop/Left
2011-05-17 06:56:43 +04:00
scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
2011-04-22 01:37:04 +04:00
scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
// Temp top/left
2011-05-17 06:56:43 +04:00
top = Math.ceil( clientRect.top + scrollTop - clientTop );
2011-04-22 01:37:04 +04:00
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;
2011-04-22 01:37:04 +04:00
}
2011-02-14 21:38:50 +03:00
});
2011-03-18 18:33:05 +03:00
// Memoized GUID Counter
2011-05-17 04:57:04 +04:00
Popcorn.guid.counter = 1;
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Factory to implement getters, setters and controllers
// as Popcorn instance methods. The IIFE will create and return
// an object with defined methods
2011-05-17 06:56:43 +04:00
Popcorn.extend(Popcorn.p, (function() {
2011-02-14 21:38:50 +03:00
var methods = "load play pause currentTime playbackRate mute volume duration",
2010-12-04 23:54:38 +03:00
ret = {};
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
// Build methods, store in object that is returned and passed to extend
2011-05-17 04:57:04 +04:00
Popcorn.forEach( methods.split( /\s+/g ), function( name ) {
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
ret[ name ] = function( arg ) {
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
if ( typeof this.media[ name ] === "function" ) {
2011-03-24 17:09:20 +03:00
this.media[ name ]();
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
return this;
}
2011-02-14 21:38:50 +03:00
if ( arg !== false && arg !== null && typeof arg !== "undefined" ) {
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
this.media[ name ] = arg;
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
return this;
}
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
return this.media[ name ];
2010-12-04 23:54:38 +03:00
};
});
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
return ret;
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
})()
);
2011-02-14 21:38:50 +03:00
Popcorn.forEach( "enable disable".split(" "), function( method ) {
Popcorn.p[ method ] = function( plugin ) {
return Popcorn[ method ]( this, plugin );
};
});
2010-12-04 23:54:38 +03:00
Popcorn.extend(Popcorn.p, {
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Rounded currentTime
2011-05-17 04:57:04 +04:00
roundTime: function() {
2011-03-24 17:09:20 +03:00
return -~this.media.currentTime;
},
2011-03-18 18:33:05 +03:00
// Attach an event to a single point in time
2011-05-17 04:57:04 +04:00
exec: function( time, fn ) {
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Creating a one second track event with an empty end
Popcorn.addTrackEvent( this, {
start: time,
end: time + 1,
_running: false,
_natives: {
2011-02-23 21:19:23 +03:00
start: fn || Popcorn.nop,
end: Popcorn.nop,
type: "exec"
}
});
2010-12-06 00:07:39 +03:00
return this;
},
position: function() {
2011-04-22 01:37:04 +04:00
return Popcorn.position( this.media );
},
toggle: function( plugin ) {
return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin );
2010-12-04 23:54:38 +03:00
}
});
Popcorn.Events = {
UIEvents: "blur focus focusin focusout load resize scroll unload",
2011-02-14 21:38:50 +03:00
MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick",
Events: "loadstart progress suspend emptied stalled play pause " +
2011-05-17 04:57:04 +04:00
"loadedmetadata loadeddata waiting playing canplay canplaythrough " +
"seeking seeked timeupdate ended ratechange durationchange volumechange"
};
2011-02-14 21:38:50 +03:00
Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " +
2011-05-17 04:57:04 +04:00
Popcorn.Events.MouseEvents + " " +
Popcorn.Events.Events;
2011-02-14 21:38:50 +03:00
internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ];
// Privately compile events table at load time
(function( events, data ) {
2011-02-14 21:38:50 +03:00
var apis = internal.events.apiTypes,
eventsList = events.Natives.split( /\s+/g ),
idx = 0, len = eventsList.length, prop;
2011-02-14 21:38:50 +03:00
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 = {
2011-02-14 21:38:50 +03:00
isNative: function( type ) {
return !!internal.events.hash[ type ];
2011-02-14 21:38:50 +03:00
},
getInterface: function( type ) {
2011-02-14 21:38:50 +03:00
if ( !Popcorn.events.isNative( type ) ) {
return false;
}
2011-02-14 21:38:50 +03:00
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;
2011-02-14 21:38:50 +03:00
},
2011-03-18 18:33:05 +03:00
// Compile all native events to single array
2011-05-17 04:57:04 +04:00
all: Popcorn.Events.Natives.split( /\s+/g ),
2011-03-18 18:33:05 +03:00
// Defines all Event handling static functions
2010-12-04 23:54:38 +03:00
fn: {
2011-05-17 04:57:04 +04:00
trigger: function( type, data ) {
2011-02-14 21:38:50 +03:00
var eventInterface, evt;
2010-12-04 23:54:38 +03:00
// setup checks for custom event system
2011-03-18 18:33:05 +03:00
if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) {
2011-02-14 21:38:50 +03:00
eventInterface = Popcorn.events.getInterface( type );
2011-02-14 21:38:50 +03:00
if ( eventInterface ) {
2011-02-14 21:38:50 +03:00
evt = document.createEvent( eventInterface );
evt.initEvent( type, true, true, global, 1 );
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
this.media.dispatchEvent( evt );
2011-02-14 21:38:50 +03:00
return this;
2011-02-14 21:38:50 +03:00
}
2011-02-14 21:38:50 +03:00
// Custom events
2011-05-17 04:57:04 +04:00
Popcorn.forEach( this.data.events[ type ], function( obj, key ) {
2010-12-04 23:54:38 +03:00
obj.call( this, data );
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
}, this );
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
}
2011-02-14 21:38:50 +03:00
return this;
2011-02-14 21:38:50 +03:00
},
2011-05-17 04:57:04 +04:00
listen: function( type, fn ) {
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
var self = this,
hasEvents = true;
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
if ( !this.data.events[ type ] ) {
this.data.events[ type ] = {};
2010-12-04 23:54:38 +03:00
hasEvents = false;
}
2011-02-14 21:38:50 +03:00
// Register
2011-03-18 18:33:05 +03:00
this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn;
2011-02-14 21:38:50 +03:00
// only attach one event of any type
if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
2010-12-04 23:54:38 +03:00
2011-03-24 17:09:20 +03:00
this.media.addEventListener( type, function( event ) {
2011-02-14 21:38:50 +03:00
2011-05-17 06:56:43 +04:00
Popcorn.forEach( self.data.events[ type ], function( obj, key ) {
2010-12-06 00:07:39 +03:00
if ( typeof obj === "function" ) {
2011-05-17 04:57:04 +04:00
obj.call( self, event );
2010-12-06 00:07:39 +03:00
}
2010-12-04 23:54:38 +03:00
});
2011-02-14 21:38:50 +03:00
2010-12-04 23:54:38 +03:00
//fn.call( self, event );
2011-02-14 21:38:50 +03:00
}, false);
2010-12-04 23:54:38 +03:00
}
return this;
2011-02-14 21:38:50 +03:00
},
2010-12-06 00:07:39 +03:00
unlisten: function( type, fn ) {
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) {
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
delete this.data.events[ type ][ fn ];
2011-02-14 21:38:50 +03:00
2010-12-06 00:07:39 +03:00
return this;
}
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
this.data.events[ type ] = null;
return this;
2010-12-04 23:54:38 +03:00
}
}
};
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
2011-05-17 04:57:04 +04:00
Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) {
2011-03-18 18:33:05 +03:00
Popcorn.p[ key ] = Popcorn.events.fn[ key ];
2011-02-14 21:38:50 +03:00
});
// Protected API methods
Popcorn.protect = {
2011-05-17 04:57:04 +04:00
natives: "load play pause currentTime playbackRate mute volume duration removePlugin roundTime trigger listen unlisten".toLowerCase().split( /\s+/ )
};
2011-02-14 21:38:50 +03:00
// Internal Only - Adds track events to the instance object
2010-12-16 21:44:19 +03:00
Popcorn.addTrackEvent = function( obj, track ) {
2011-02-14 21:38:50 +03:00
if ( track._natives ) {
2011-03-18 18:33:05 +03:00
// Supports user defined track event id
track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id;
2010-12-16 21:44:19 +03:00
// Push track event ids into the history
2011-02-14 21:38:50 +03:00
obj.data.history.push( track._id );
track._natives.start = track._natives.start || Popcorn.nop;
2011-05-17 04:57:04 +04:00
track._natives.end = track._natives.end || Popcorn.nop;
2010-12-16 21:44:19 +03:00
}
2011-02-14 21:38:50 +03:00
track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
2011-05-17 04:57:04 +04:00
track.end = Popcorn.util.toSeconds( track.end, obj.options.framerate );
2011-03-18 18:33:05 +03:00
// Store this definition in an array sorted by times
var byStart = obj.data.trackEvents.byStart,
byEnd = obj.data.trackEvents.byEnd,
idx;
2011-06-14 17:20:23 +04:00
for ( idx = byStart.length - 1; idx >= 0; idx-- ) {
2011-06-14 17:20:23 +04:00
if ( track.start >= byStart[ idx ].start ) {
2011-05-17 04:57:04 +04:00
byStart.splice( idx + 1, 0, track );
2011-05-11 21:41:50 +04:00
break;
}
}
2011-06-14 17:20:23 +04:00
for ( idx = byEnd.length - 1; idx >= 0; idx-- ) {
2011-06-14 17:20:23 +04:00
if ( track.end > byEnd[ idx ].end ) {
2011-05-17 04:57:04 +04:00
byEnd.splice( idx + 1, 0, track );
2011-05-11 21:41:50 +04:00
break;
}
}
2010-12-16 21:44:19 +03:00
// Store references to user added trackevents in ref table
if ( track._id ) {
Popcorn.addTrackEvent.ref( obj, track );
2011-02-16 00:55:52 +03:00
}
};
2011-02-16 00:55:52 +03:00
// 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;
2011-02-16 00:55:52 +03:00
};
2010-12-16 21:44:19 +03:00
Popcorn.removeTrackEvent = function( obj, trackId ) {
2011-02-14 21:38:50 +03:00
var historyLen = obj.data.history.length,
indexWasAt = 0,
byStart = [],
byEnd = [],
history = [];
2011-03-18 18:33:05 +03:00
Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) {
2010-12-16 21:44:19 +03:00
// Preserve the original start/end trackEvents
if ( !o._id ) {
byStart.push( obj.data.trackEvents.byStart[i] );
byEnd.push( obj.data.trackEvents.byEnd[i] );
2011-02-14 21:38:50 +03:00
}
// Filter for user track events (vs system track events)
if ( o._id ) {
2011-02-14 21:38:50 +03:00
// Filter for the trackevent to remove
if ( o._id !== trackId ) {
byStart.push( obj.data.trackEvents.byStart[i] );
byEnd.push( obj.data.trackEvents.byEnd[i] );
2011-02-14 21:38:50 +03:00
}
// Capture the position of the track being removed.
if ( o._id === trackId ) {
indexWasAt = i;
2011-04-16 03:29:34 +04:00
o._natives._teardown && o._natives._teardown.call( obj, o );
2011-02-14 21:38:50 +03:00
}
2010-12-16 21:44:19 +03:00
}
2011-05-17 04:57:04 +04:00
2010-12-16 21:44:19 +03:00
});
2011-02-14 21:38:50 +03:00
// Update
if ( indexWasAt <= obj.data.trackEvents.startIndex ) {
obj.data.trackEvents.startIndex--;
}
if ( indexWasAt <= obj.data.trackEvents.endIndex ) {
obj.data.trackEvents.endIndex--;
}
2011-02-14 21:38:50 +03:00
2010-12-16 21:44:19 +03:00
obj.data.trackEvents.byStart = byStart;
2011-05-17 04:57:04 +04:00
obj.data.trackEvents.byEnd = byEnd;
2010-12-16 21:44:19 +03:00
for ( var i = 0; i < historyLen; i++ ) {
2011-05-17 04:57:04 +04:00
if ( obj.data.history[ i ] !== trackId ) {
history.push( obj.data.history[ i ] );
2010-12-16 21:44:19 +03:00
}
2011-02-14 21:38:50 +03:00
}
// Update ordered history array
2010-12-16 21:44:19 +03:00
obj.data.history = history;
// Update track event references
Popcorn.removeTrackEvent.ref( obj, trackId );
2010-12-16 21:44:19 +03:00
};
2011-02-14 21:38:50 +03:00
// Internal Only - Removes track event references from instance object's trackRefs hash table
Popcorn.removeTrackEvent.ref = function( obj, trackId ) {
delete obj.data.trackRefs[ trackId ];
2011-02-14 21:38:50 +03:00
return obj;
};
2011-02-14 21:38:50 +03:00
// 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 );
2011-02-14 21:38:50 +03:00
}
}
2011-02-14 21:38:50 +03:00
2010-12-16 21:44:19 +03:00
return trackevents;
};
2011-02-14 21:38:50 +03:00
// 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 ) {
2011-06-02 19:30:25 +04:00
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 ];
};
2011-02-14 21:38:50 +03:00
2010-12-16 21:44:19 +03:00
Popcorn.getLastTrackEventId = function( obj ) {
return obj.data.history[ obj.data.history.length - 1 ];
};
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
// Map and Extend TrackEvent functions to all Popcorn instances
2010-12-16 21:44:19 +03:00
Popcorn.extend( Popcorn.p, {
2011-02-14 21:38:50 +03:00
2010-12-16 21:44:19 +03:00
getTrackEvents: function() {
return Popcorn.getTrackEvents.call( null, this );
},
2011-02-14 21:38:50 +03:00
getTrackEvent: function( id ) {
return Popcorn.getTrackEvent.call( null, this, id );
},
2011-02-14 21:38:50 +03:00
2010-12-16 21:44:19 +03:00
getLastTrackEventId: function() {
return Popcorn.getLastTrackEventId.call( null, this );
2011-02-14 21:38:50 +03:00
},
2010-12-16 21:44:19 +03:00
removeTrackEvent: function( id ) {
Popcorn.removeTrackEvent.call( null, this, id );
return this;
},
2011-02-16 00:55:52 +03:00
removePlugin: function( name ) {
Popcorn.removePlugin.call( null, this, name );
return this;
2010-12-16 21:44:19 +03:00
}
});
2011-02-14 21:38:50 +03:00
// Plugin manifests
Popcorn.manifest = {};
2011-02-14 21:38:50 +03:00
// Plugins are registered
2010-12-04 23:54:38 +03:00
Popcorn.registry = [];
Popcorn.registryByName = {};
2011-02-14 21:38:50 +03:00
// An interface for extending Popcorn
2010-12-04 23:54:38 +03:00
// with plugin functionality
2011-02-14 21:38:50 +03:00
Popcorn.plugin = function( name, definition, manifest ) {
2010-12-04 23:54:38 +03:00
if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
Popcorn.error("'" + name + "' is a protected function name");
return;
}
2010-12-04 23:54:38 +03:00
// Provides some sugar, but ultimately extends
2011-02-14 21:38:50 +03:00
// the definition into Popcorn.p
2011-02-23 07:30:20 +03:00
var reserved = [ "start", "end" ],
2011-01-19 22:10:06 +03:00
plugin = {},
2011-02-24 00:19:09 +03:00
setup,
isfn = typeof definition === "function";
2011-02-14 21:38:50 +03:00
2011-02-24 20:46:19 +03:00
// If `manifest` arg is undefined, check for manifest within the `definition` object
// If no `definition.manifest`, an empty object is a sufficient fallback
if ( !manifest ) {
manifest = definition.manifest || {};
}
2011-02-24 20:46:19 +03:00
var pluginFn = function( setup, options ) {
2011-02-14 21:38:50 +03:00
if ( !options ) {
return this;
2011-02-14 21:38:50 +03:00
}
2011-02-23 07:30:20 +03:00
// Storing the plugin natives
options._natives = setup;
options._natives.type = name;
options._running = false;
2011-02-23 07:30:20 +03:00
// 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 = 0;
}
if ( !( "end" in options ) ) {
options.end = this.duration() || Number.MAX_VALUE;
}
2011-02-14 21:38:50 +03:00
// If a _setup was declared, then call it before
// the events commence
if ( "_setup" in setup && typeof setup._setup === "function" ) {
2011-02-14 21:38:50 +03:00
// Resolves 239, 241, 242
2011-02-23 07:16:29 +03:00
if ( !options.target ) {
2011-02-24 20:46:19 +03:00
// Sometimes the manifest may be missing entirely
2011-02-24 20:46:19 +03:00
// or it has an options object that doesn't have a `target` property
var manifestopts = "options" in manifest && manifest.options;
options.target = manifestopts && "target" in manifestopts && manifestopts.target;
2010-12-05 04:01:40 +03:00
}
setup._setup.call( this, options );
}
2010-12-10 19:53:47 +03:00
Popcorn.addTrackEvent( this, options );
2010-12-10 19:53:47 +03:00
2011-02-14 21:38:50 +03:00
// Future support for plugin event definitions
// for all of the native events
2011-05-17 06:56:43 +04:00
Popcorn.forEach( setup, function( callback, type ) {
2011-02-14 21:38:50 +03:00
if ( type !== "type" ) {
2011-02-14 21:38:50 +03:00
2011-03-18 18:33:05 +03:00
if ( reserved.indexOf( type ) === -1 ) {
this.listen( type, callback );
}
}
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
}, this );
2011-02-14 21:38:50 +03:00
return this;
};
2011-02-14 21:38:50 +03:00
// Augment the manifest object
2011-02-23 07:16:29 +03:00
if ( manifest || ( "manifest" in definition ) ) {
2011-02-14 21:38:50 +03:00
Popcorn.manifest[ name ] = manifest || definition.manifest;
}
2011-02-14 21:38:50 +03:00
// Assign new named definition
2011-02-23 07:16:29 +03:00
plugin[ name ] = function( options ) {
return pluginFn.call( this, isfn ? definition.call( this, options ) : definition,
2011-03-18 18:33:05 +03:00
options );
2011-02-23 07:16:29 +03:00
};
2011-02-14 21:38:50 +03:00
// Extend Popcorn.p with new named definition
2010-12-04 23:54:38 +03:00
Popcorn.extend( Popcorn.p, plugin );
2011-02-14 21:38:50 +03:00
// Push into the registry
var entry = {
fn: plugin[ name ],
definition: definition,
base: definition,
parents: [],
name: name
};
2011-04-20 18:55:34 +04:00
Popcorn.registry.push(
Popcorn.extend( plugin, entry, {
type: name
})
);
Popcorn.registryByName[ name ] = entry;
2011-02-14 21:38:50 +03:00
return plugin;
2010-12-04 23:54:38 +03:00
};
2011-02-14 21:38:50 +03:00
// 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;
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,
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.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--;
}
}
}
};
2011-04-22 01:37:04 +04:00
// Popcorn Plugin Inheritance Helper Methods
// Internal use only
Popcorn.plugin.getDefinition = function( name ) {
var registry = Popcorn.registryByName;
2011-05-17 04:57:04 +04:00
if ( registry[ name ] ) {
return registry[ name ];
}
2011-05-17 04:57:04 +04:00
Popcorn.error( "Cannot inherit from " + name + "; Object does not exist" );
2011-04-06 00:29:40 +04:00
};
2011-04-22 01:37:04 +04:00
// Internal use only
2011-04-09 00:52:11 +04:00
Popcorn.plugin.delegate = function( instance, name, plugins ) {
return function() {
2011-04-12 21:58:06 +04:00
var args = arguments;
2011-04-09 00:52:11 +04:00
plugins.forEach( function( plugin ) {
// The new plugin simply calls the delegated methods on
// all of its parents in the order they were specified.
2011-04-12 21:58:06 +04:00
plugin[ name ] && plugin[ name ].apply( instance, args );
});
};
};
2011-04-22 01:37:04 +04:00
// Plugin inheritance
Popcorn.plugin.inherit = function( name, parents, definition, manifest ) {
2011-03-28 06:32:01 +04:00
// Get the names of all of the ancestor classes, in the order that
// we will be calling them. The override is for the class we're
2011-03-28 06:32:01 +04:00
// currently defining, since it's not in the registry yet.
2011-05-17 04:57:04 +04:00
var ancestors = [],
pluginFn, entry;
2011-03-28 06:32:01 +04:00
function getAncestors( name, override ) {
var parents = override || Popcorn.plugin.getDefinition( name ).parents;
2011-03-28 06:32:01 +04:00
for ( var i in parents ) {
2011-03-30 08:05:11 +04:00
if ( hasOwn.call( parents, i ) ) {
2011-03-28 06:32:01 +04:00
var p = parents[ i ];
getAncestors( p );
if ( ancestors.indexOf( p ) === -1 ) {
ancestors.push( p );
2011-03-28 06:32:01 +04:00
}
}
}
}
getAncestors( name, Popcorn.isArray( parents ) ? parents : [ parents ] );
ancestors.push( name );
2011-03-28 06:32:01 +04:00
// Now create the requested plugin under the reqested name.
pluginFn = Popcorn.plugin( name, function( options ) {
2011-05-17 04:57:04 +04:00
var self = this,
2011-04-09 00:52:11 +04:00
plugins;
function instantiate( definition ) {
return definition.call && definition.call( self, options ) || definition;
}
2011-03-28 06:32:01 +04:00
// When the newly-defined plugin is instantiated, it must
// explicitly instantiate all of its ancestors.
2011-04-09 00:52:11 +04:00
plugins = ancestors.map( function( name ) {
return instantiate( Popcorn.plugin.getDefinition( name ).base );
});
return {
2011-04-09 00:52:11 +04:00
_setup: Popcorn.plugin.delegate( self, "_setup", plugins ),
start: Popcorn.plugin.delegate( self, "start", plugins ),
end: Popcorn.plugin.delegate( self, "end", plugins )
};
2011-05-17 04:57:04 +04:00
2011-03-28 06:32:01 +04:00
}, manifest || definition.manifest );
entry = Popcorn.plugin.getDefinition( name );
entry.base = definition;
entry.parents = parents;
return pluginFn;
};
2011-05-17 04:57:04 +04:00
// Augment Popcorn;
Popcorn.inherit = Popcorn.plugin.inherit;
// 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 ) {
2011-05-17 04:57:04 +04:00
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
2011-02-14 21:38:50 +03:00
var natives = Popcorn.events.all,
parseFn,
parser = {};
2011-05-17 06:56:43 +04:00
parseFn = function( filename, callback ) {
if ( !filename ) {
return this;
}
var that = this;
Popcorn.xhr({
url: filename,
dataType: type,
success: function( data ) {
2011-02-14 21:38:50 +03:00
var tracksObject = definition( data ),
tracksData,
tracksDataLen,
tracksDef,
idx = 0;
2011-02-14 21:38:50 +03:00
tracksData = tracksObject.data || [];
tracksDataLen = tracksData.length;
tracksDef = null;
2011-02-14 21:38:50 +03:00
// If no tracks to process, return immediately
if ( !tracksDataLen ) {
return;
}
2011-02-14 21:38:50 +03:00
// Create tracks out of parsed object
for ( ; idx < tracksDataLen; idx++ ) {
2011-02-14 21:38:50 +03:00
tracksDef = tracksData[ idx ];
2011-02-14 21:38:50 +03:00
for ( var key in tracksDef ) {
if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
2011-02-14 21:38:50 +03:00
that[ key ]( tracksDef[ key ] );
}
}
}
if ( callback ) {
callback();
}
}
});
return this;
};
// Assign new named definition
parser[ name ] = parseFn;
2011-02-14 21:38:50 +03:00
// Extend Popcorn.p with new named definition
Popcorn.extend( Popcorn.p, parser );
2011-02-14 21:38:50 +03:00
// keys the function name by filetype extension
//Popcorn.parsers[ name ] = true;
return parser;
};
// Cache references to reused RegExps
var rparams = /\?/,
// XHR Setup object
setup = {
2011-05-17 04:57:04 +04:00
url: "",
data: "",
dataType: "",
2010-12-15 22:32:48 +03:00
success: Popcorn.nop,
2011-05-17 04:57:04 +04:00
type: "GET",
2011-02-14 21:38:50 +03:00
async: true,
2011-05-17 04:57:04 +04:00
xhr: function() {
2010-12-20 18:59:32 +03:00
return new global.XMLHttpRequest();
2010-12-15 22:32:48 +03:00
}
2011-02-14 21:38:50 +03:00
};
2011-05-17 04:57:04 +04:00
Popcorn.xhr = function( options ) {
options.dataType = options.dataType && options.dataType.toLowerCase() || null;
2011-05-17 04:57:04 +04:00
if ( options.dataType &&
( options.dataType === "jsonp" || options.dataType === "script" ) ) {
2011-01-20 20:20:06 +03:00
2011-02-14 21:38:50 +03:00
Popcorn.xhr.getJSONP(
options.url,
options.success,
options.dataType === "script"
);
return;
}
2011-02-14 21:38:50 +03:00
2010-12-15 22:32:48 +03:00
var settings = Popcorn.extend( {}, setup, options );
2011-02-14 21:38:50 +03:00
// Create new XMLHttpRequest object
2010-12-15 22:32:48 +03:00
settings.ajax = settings.xhr();
2011-02-14 21:38:50 +03:00
2010-12-15 22:32:48 +03:00
if ( settings.ajax ) {
2011-02-14 21:38:50 +03:00
if ( settings.type === "GET" && settings.data ) {
2011-02-14 21:38:50 +03:00
// append query string
2011-03-18 18:33:05 +03:00
settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
2011-02-14 21:38:50 +03:00
// Garbage collect and reset settings.data
settings.data = null;
2011-02-14 21:38:50 +03:00
}
2010-12-15 22:32:48 +03:00
2011-02-14 21:38:50 +03:00
settings.ajax.open( settings.type, settings.url, settings.async );
2011-03-01 23:33:12 +03:00
settings.ajax.send( settings.data || null );
2010-12-15 22:32:48 +03:00
return Popcorn.xhr.httpData( settings );
2011-02-14 21:38:50 +03:00
}
2010-12-15 22:32:48 +03:00
};
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
Popcorn.xhr.httpData = function( settings ) {
2011-02-14 21:38:50 +03:00
var data, json = null;
2010-12-20 18:59:32 +03:00
settings.ajax.onreadystatechange = function() {
2010-12-15 22:32:48 +03:00
2011-02-14 21:38:50 +03:00
if ( settings.ajax.readyState === 4 ) {
2010-12-15 22:32:48 +03:00
try {
2011-05-17 04:57:04 +04:00
json = JSON.parse( settings.ajax.responseText );
} catch( e ) {
2010-12-15 22:32:48 +03:00
//suppress
}
2010-12-15 22:32:48 +03:00
data = {
2011-02-14 21:38:50 +03:00
xml: settings.ajax.responseXML,
text: settings.ajax.responseText,
2010-12-15 22:32:48 +03:00
json: json
};
2011-02-14 21:38:50 +03:00
// If a dataType was specified, return that type of data
if ( settings.dataType ) {
data = data[ settings.dataType ];
}
2011-02-14 21:38:50 +03:00
2010-12-15 22:32:48 +03:00
settings.success.call( settings.ajax, data );
2011-02-14 21:38:50 +03:00
}
};
return data;
2010-12-15 22:32:48 +03:00
};
2011-05-17 04:57:04 +04:00
Popcorn.xhr.getJSONP = function( url, success, isScript ) {
2011-02-16 01:54:51 +03:00
// If this is a script request, ensure that we do not call something that has already been loaded
if ( isScript ) {
2011-05-17 04:57:04 +04:00
var scripts = document.querySelectorAll( "script[src=\"" + url + "\"]" );
2011-03-18 18:33:05 +03:00
// If there are scripts with this url loaded, early return
2011-02-16 01:54:51 +03:00
if ( scripts.length ) {
2011-02-16 01:54:51 +03:00
// Execute success callback and pass "exists" flag
success && success( true );
return;
}
}
2011-02-14 21:38:50 +03:00
2011-05-17 04:57:04 +04:00
var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
script = document.createElement( "script" ),
paramStr = url.split( "?" )[ 1 ],
isFired = false,
params = [],
2011-02-17 00:47:47 +03:00
callback, parts, callparam;
2011-02-16 01:54:51 +03:00
if ( paramStr && !isScript ) {
2011-05-17 04:57:04 +04:00
params = paramStr.split( "&" );
}
2011-02-14 21:38:50 +03:00
2011-02-17 00:47:47 +03:00
if ( params.length ) {
2011-05-17 04:57:04 +04:00
parts = params[ params.length - 1 ].split( "=" );
2011-02-17 00:47:47 +03:00
}
2011-05-17 04:57:04 +04:00
callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp";
2011-02-16 01:54:51 +03:00
if ( !paramStr && !isScript ) {
url += "?callback=" + callback;
}
2011-02-14 21:38:50 +03:00
2011-02-16 01:54:51 +03:00
if ( callback && !isScript ) {
2011-03-18 18:33:05 +03:00
// If a callback name already exists
2011-02-17 00:47:47 +03:00
if ( !!window[ callback ] ) {
2011-03-18 18:33:05 +03:00
// Create a new unique callback name
2011-02-17 00:47:47 +03:00
callback = Popcorn.guid( callback );
}
2011-03-18 18:33:05 +03:00
// Define the JSONP success callback globally
2011-05-17 04:57:04 +04:00
window[ callback ] = function( data ) {
2011-02-16 01:54:51 +03:00
success && success( data );
isFired = true;
2011-02-16 01:54:51 +03:00
};
2011-03-18 18:33:05 +03:00
2011-02-17 00:47:47 +03:00
// Replace callback param and callback name
2011-05-17 04:57:04 +04:00
url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
2011-03-18 18:33:05 +03:00
}
2011-03-18 18:33:05 +03:00
script.onload = script.onreadystatechange = function() {
if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
2011-02-16 01:54:51 +03:00
// Handling remote script loading callbacks
if ( isScript ) {
2011-02-16 01:54:51 +03:00
// getScript
success && success();
}
2011-02-16 01:54:51 +03:00
// Executing for JSONP requests
if ( isFired ) {
// Garbage collect the callback
delete window[ callback ];
2011-03-18 18:33:05 +03:00
// Garbage collect the script resource
head.removeChild( script );
}
}
2011-03-18 18:33:05 +03:00
};
script.src = url;
head.insertBefore( script, head.firstChild );
2011-03-18 18:33:05 +03:00
2011-02-16 01:54:51 +03:00
return;
};
2011-03-18 18:33:05 +03:00
2011-02-16 01:54:51 +03:00
Popcorn.getJSONP = Popcorn.xhr.getJSONP;
2011-03-18 18:33:05 +03:00
2011-02-16 01:54:51 +03:00
Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
return Popcorn.xhr.getJSONP( url, success, true );
};
2011-01-20 20:20:06 +03:00
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";
if ( typeof timeStr === "number" ) {
return timeStr;
} else if ( typeof timeStr === "string" ) {
if ( ! validTimeFormat.test( timeStr ) ) {
Popcorn.error( errorMessage );
}
} else {
Popcorn.error( errorMessage );
}
var t = timeStr.split( ":" ),
lastIndex = t.length - 1,
lastElement = t[ lastIndex ];
//Fix last element:
if ( lastElement.indexOf( ";" ) > -1 ) {
var frameInfo = lastElement.split( ";" ),
frameTime = 0;
if ( framerate && ( typeof framerate === "number" ) ) {
frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
}
t[ lastIndex ] =
parseInt( frameInfo[ 0 ], 10 ) + frameTime;
}
if ( t.length === 1 ) {
return parseFloat( t[ 0 ], 10 );
} else if ( t.length === 2 ) {
return ( parseInt( t[ 0 ], 10 ) * 60 ) + parseFloat( t[ 1 ], 10 );
} else if ( t.length === 3 ) {
return ( parseInt( t[ 0 ], 10 ) * 3600 ) +
( parseInt( t[ 1 ], 10 ) * 60 ) +
parseFloat( t[ 2 ], 10 );
}
}
};
2011-02-16 01:54:51 +03:00
2010-12-20 18:59:32 +03:00
// Exposes Popcorn to global context
2010-12-04 23:54:38 +03:00
global.Popcorn = Popcorn;
2011-02-08 23:58:30 +03:00
2011-03-25 00:34:54 +03:00
document.addEventListener( "DOMContentLoaded", function() {
// Supports non-specific elements
2011-03-25 00:34:54 +03:00
var dataAttr = "data-timeline-sources",
medias = document.querySelectorAll( "[" + dataAttr + "]" );
2011-03-24 17:09:20 +03:00
Popcorn.forEach( medias, function( idx, key ) {
2011-01-05 20:22:54 +03:00
2011-03-24 17:09:20 +03:00
var media = medias[ key ],
2011-02-08 23:58:30 +03:00
hasDataSources = false,
2011-03-24 17:09:20 +03:00
dataSources, data, popcornMedia;
2011-02-08 23:58:30 +03:00
// Ensure that the DOM has an id
2011-03-24 17:09:20 +03:00
if ( !media.id ) {
2011-02-08 23:58:30 +03:00
2011-03-24 17:09:20 +03:00
media.id = Popcorn.guid( "__popcorn" );
}
2011-02-14 21:38:50 +03:00
2011-02-08 23:58:30 +03:00
// Ensure we're looking at a dom node
2011-03-24 17:09:20 +03:00
if ( media.nodeType && media.nodeType === 1 ) {
2011-02-14 21:38:50 +03:00
2011-03-24 17:09:20 +03:00
popcornMedia = Popcorn( "#" + media.id );
2011-05-17 04:57:04 +04:00
dataSources = ( media.getAttribute( dataAttr ) || "" ).split( "," );
if ( dataSources[ 0 ] ) {
2011-03-24 17:09:20 +03:00
Popcorn.forEach( dataSources, function( source ) {
// split the parser and data as parser!file
data = source.split( "!" );
// if no parser is defined for the file, assume "parse" + file extension
if ( data.length === 1 ) {
2011-02-08 23:58:30 +03:00
data = source.split( "." );
data[ 0 ] = "parse" + data[ data.length - 1 ].toUpperCase();
data[ 1 ] = source;
2011-01-18 00:07:51 +03:00
}
2011-02-08 23:58:30 +03:00
2011-03-24 17:09:20 +03:00
// If the media has data sources and the correct parser is registered, continue to load
if ( dataSources[ 0 ] && popcornMedia[ data[ 0 ] ] ) {
2011-03-24 17:09:20 +03:00
// Set up the media and load in the datasources
popcornMedia[ data[ 0 ] ]( data[ 1 ] );
}
});
2011-02-14 21:38:50 +03:00
}
2011-03-24 17:09:20 +03:00
// Only play the media if it was specified to do so
if ( !!popcornMedia.autoplay ) {
popcornMedia.play();
2011-02-14 21:38:50 +03:00
}
}
});
}, false );
2011-01-05 20:22:54 +03:00
2011-02-16 00:55:52 +03:00
})(window, window.document);