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
    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 = {},
          qualities = [],
          currentQuality  = -1,
          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, data) {
        var customEvent = document.createEvent("CustomEvent"),
          detail = {
            type: name,
            target: this.parentNode,
            data: data
          };
  
        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 <img> 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
        },
        qualities: {
          get: function() {
            return qualities;
          },
          set: function(val) {
            if (val) {
              qualities = val;
            }
          },
          configurable: true
        },
        currentQuality : {
          get: function() {
            return currentQuality ;
          },
          set: function(val) {
            currentQuality  = val;
          },
          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));
  