text: /**
   * 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 <stevenlevithan.com>
     * 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
    MediaError = 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(){}
    MediaElementProto.prototype = {
  
      _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.
      addEventListener: function( type, listener, useCapture ) {
        document.addEventListener( this._eventNamespace + type, listener, useCapture );
      },
  
      removeEventListener: function( type, listener, useCapture ) {
        document.removeEventListener( this._eventNamespace + type, listener, useCapture );
      },
  
      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 );
      },
  
      load: Popcorn.nop,
  
      canPlayType: function( url ) {
        return "";
      },
  
      // Popcorn expects getBoundingClientRect to exist, forward to parent node.
      getBoundingClientRect: function() {
        return this.parentNode.getBoundingClientRect();
      },
  
      NETWORK_EMPTY: 0,
      NETWORK_IDLE: 1,
      NETWORK_LOADING: 2,
      NETWORK_NO_SOURCE: 3,
  
      HAVE_NOTHING: 0,
      HAVE_METADATA: 1,
      HAVE_CURRENT_DATA: 2,
      HAVE_FUTURE_DATA: 3,
      HAVE_ENOUGH_DATA: 4
  
    };
  
    MediaElementProto.prototype.constructor = MediaElementProto;
  
    Object.defineProperties( MediaElementProto.prototype, {
  
      currentSrc: {
        get: function() {
          return this.src !== undefined ? this.src : "";
        }
      },
  
      // We really can't do much more than "auto" with most of these.
      preload: {
        get: function() {
          return "auto";
        },
        set: Popcorn.nop
      },
  
      controls: {
        get: function() {
          return true;
        },
        set: Popcorn.nop
      },
  
      // TODO: it would be good to overlay an <img> using this URL
      poster: {
        get: function() {
          return "";
        },
        set: Popcorn.nop
      },
  
      crossorigin: {
        get: function() {
          return "";
        }
      },
  
      played: {
        get: function() {
          return _fakeTimeRanges;
        }
      },
  
      seekable: {
        get: function() {
          return _fakeTimeRanges;
        }
      },
  
      buffered: {
        get: function() {
          return _fakeTimeRanges;
        }
      },
  
      defaultMuted: {
        get: function() {
          return false;
        }
      },
  
      defaultPlaybackRate: {
        get: function() {
          return 1.0;
        }
      }
  
      // TODO:
      //   initialTime
      //   playbackRate
      //   startOffsetTime
  
    });
  
    Popcorn._MediaElementProto = MediaElementProto;
  
  }( Popcorn, window.document ));
  