/** * The Popcorn._MediaElementProto object is meant to be used as a base * prototype for HTML*VideoElement and HTML*AudioElement wrappers. * MediaElementProto requires that users provide: * - parentNode: the element owning the media div/iframe * - _eventNamespace: the unique namespace for all events */ (function( Popcorn, document ) { /********************************************************************************* * parseUri 1.2.2 * http://blog.stevenlevithan.com/archives/parseuri * (c) Steven Levithan * MIT License */ function parseUri (str) { var o = parseUri.options, m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), uri = {}, i = 14; while (i--) { uri[o.key[i]] = m[i] || ""; } uri[o.q.name] = {}; uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { if ($1) { uri[o.q.name][$1] = $2; } }); return uri; } parseUri.options = { strictMode: false, key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], q: { name: "queryKey", parser: /(?:^|&)([^&=]*)=?([^&]*)/g }, parser: { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ } }; /*********************************************************************************/ // Fake a TimeRanges object var _fakeTimeRanges = { length: 0, start: Popcorn.nop, end: Popcorn.nop }; // Make sure the browser has MediaError window.MediaError = window.MediaError || (function() { function MediaError(code, msg) { this.code = code || null; this.message = msg || ""; } MediaError.MEDIA_ERR_NONE_ACTIVE = 0; MediaError.MEDIA_ERR_ABORTED = 1; MediaError.MEDIA_ERR_NETWORK = 2; MediaError.MEDIA_ERR_DECODE = 3; MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; return MediaError; }()); function MediaElementProto() { var protoElement = {}, events = {}, parentNode; if ( !Object.prototype.__defineGetter__ ) { protoElement = document.createElement( "div" ); } protoElement._util = { // Each wrapper stamps a type. type: "HTML5", // How often to trigger timeupdate events TIMEUPDATE_MS: 250, // Standard width and height MIN_WIDTH: 300, MIN_HEIGHT: 150, // Check for attribute being set or value being set in JS. The following are true: // autoplay // autoplay="true" // v.autoplay=true; isAttributeSet: function( value ) { return ( typeof value === "string" || value === true ); }, parseUri: parseUri }; // Mimic DOM events with custom, namespaced events on the document. // Each media element using this prototype needs to provide a unique // namespace for all its events via _eventNamespace. protoElement.addEventListener = function( type, listener, useCapture ) { document.addEventListener( this._eventNamespace + type, listener, useCapture ); }; protoElement.removeEventListener = function( type, listener, useCapture ) { document.removeEventListener( this._eventNamespace + type, listener, useCapture ); }; protoElement.dispatchEvent = function( name ) { var customEvent = document.createEvent( "CustomEvent" ), detail = { type: name, target: this.parentNode, data: null }; customEvent.initCustomEvent( this._eventNamespace + name, false, false, detail ); document.dispatchEvent( customEvent ); }; protoElement.load = Popcorn.nop; protoElement.canPlayType = function( url ) { return ""; }; // Popcorn expects getBoundingClientRect to exist, forward to parent node. protoElement.getBoundingClientRect = function() { return parentNode.getBoundingClientRect(); }; protoElement.NETWORK_EMPTY = 0; protoElement.NETWORK_IDLE = 1; protoElement.NETWORK_LOADING = 2; protoElement.NETWORK_NO_SOURCE = 3; protoElement.HAVE_NOTHING = 0; protoElement.HAVE_METADATA = 1; protoElement.HAVE_CURRENT_DATA = 2; protoElement.HAVE_FUTURE_DATA = 3; protoElement.HAVE_ENOUGH_DATA = 4; Object.defineProperties( protoElement, { currentSrc: { get: function() { return this.src !== undefined ? this.src : ""; }, configurable: true }, parentNode: { get: function() { return parentNode; }, set: function( val ) { parentNode = val; }, configurable: true }, // We really can't do much more than "auto" with most of these. preload: { get: function() { return "auto"; }, set: Popcorn.nop, configurable: true }, controls: { get: function() { return true; }, set: Popcorn.nop, configurable: true }, // TODO: it would be good to overlay an using this URL poster: { get: function() { return ""; }, set: Popcorn.nop, configurable: true }, crossorigin: { get: function() { return ""; }, configurable: true }, played: { get: function() { return _fakeTimeRanges; }, configurable: true }, seekable: { get: function() { return _fakeTimeRanges; }, configurable: true }, buffered: { get: function() { return _fakeTimeRanges; }, configurable: true }, defaultMuted: { get: function() { return false; }, configurable: true }, defaultPlaybackRate: { get: function() { return 1.0; }, configurable: true }, style: { get: function() { return this.parentNode.style; }, configurable: true }, id: { get: function() { return this.parentNode.id; }, configurable: true } // TODO: // initialTime // playbackRate // startOffsetTime }); return protoElement; } Popcorn._MediaElementProto = MediaElementProto; }( Popcorn, window.document ));