b2evolution PHP Cross Reference Blogging Systems

Source: /plugins/html5_mediaelementjs_plugin/rsc/mediaelement-and-player.js - 4262 lines - 121328 bytes - Summary - Text - Print

   1  /*!
   2  * MediaElement.js
   3  * HTML5 <video> and <audio> shim and player
   4  * http://mediaelementjs.com/
   5  *
   6  * Creates a JavaScript object that mimics HTML5 MediaElement API
   7  * for browsers that don't understand HTML5 or can't play the provided codec
   8  * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
   9  *
  10  * Copyright 2010-2012, John Dyer (http://j.hn)
  11  * Dual licensed under the MIT or GPL Version 2 licenses.
  12  *
  13  */
  14  // Namespace

  15  var mejs = mejs || {};
  16  
  17  // version number

  18  mejs.version = '2.9.1';
  19  
  20  // player number (for missing, same id attr)

  21  mejs.meIndex = 0;
  22  
  23  // media types accepted by plugins

  24  mejs.plugins = {
  25      silverlight: [
  26          {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
  27      ],
  28      flash: [
  29          {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube']}
  30          //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)

  31      ],
  32      youtube: [
  33          {version: null, types: ['video/youtube', 'video/x-youtube']}
  34      ],
  35      vimeo: [
  36          {version: null, types: ['video/vimeo']}
  37      ]
  38  };
  39  
  40  /*

  41  Utility methods

  42  */
  43  mejs.Utility = {
  44      encodeUrl: function(url) {
  45          return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');

  46      },
  47      escapeHTML: function(s) {
  48          return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
  49      },
  50      absolutizeUrl: function(url) {
  51          var el = document.createElement('div');
  52          el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
  53          return el.firstChild.href;
  54      },
  55      getScriptPath: function(scriptNames) {
  56          var
  57              i = 0,
  58              j,
  59              path = '',
  60              name = '',
  61              script,
  62              scripts = document.getElementsByTagName('script'),
  63              il = scripts.length,
  64              jl = scriptNames.length;
  65  
  66          for (; i < il; i++) {
  67              script = scripts[i].src;
  68              for (j = 0; j < jl; j++) {
  69                  name = scriptNames[j];
  70                  if (script.indexOf(name) > -1) {
  71                      path = script.substring(0, script.indexOf(name));
  72                      break;
  73                  }
  74              }
  75              if (path !== '') {
  76                  break;
  77              }
  78          }
  79          return path;
  80      },
  81      secondsToTimeCode: function(time, forceHours, showFrameCount, fps) {
  82          //add framecount

  83          if (typeof showFrameCount == 'undefined') {
  84              showFrameCount=false;
  85          } else if(typeof fps == 'undefined') {
  86              fps = 25;
  87          }
  88      
  89          var hours = Math.floor(time / 3600) % 24,
  90              minutes = Math.floor(time / 60) % 60,
  91              seconds = Math.floor(time % 60),
  92              frames = Math.floor(((time % 1)*fps).toFixed(3)),
  93              result = 
  94                      ( (forceHours || hours > 0) ? (hours < 10 ? '0' + hours : hours) + ':' : '')
  95                          + (minutes < 10 ? '0' + minutes : minutes) + ':'
  96                          + (seconds < 10 ? '0' + seconds : seconds)
  97                          + ((showFrameCount) ? ':' + (frames < 10 ? '0' + frames : frames) : '');
  98      
  99          return result;
 100      },
 101      
 102      timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
 103          if (typeof showFrameCount == 'undefined') {
 104              showFrameCount=false;
 105          } else if(typeof fps == 'undefined') {
 106              fps = 25;
 107          }
 108      
 109          var tc_array = hh_mm_ss_ff.split(":"),
 110              tc_hh = parseInt(tc_array[0], 10),
 111              tc_mm = parseInt(tc_array[1], 10),
 112              tc_ss = parseInt(tc_array[2], 10),
 113              tc_ff = 0,
 114              tc_in_seconds = 0;
 115          
 116          if (showFrameCount) {
 117              tc_ff = parseInt(tc_array[3])/fps;
 118          }
 119          
 120          tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
 121          
 122          return tc_in_seconds;
 123      },
 124      
 125      /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */

 126      removeSwf: function(id) {
 127          var obj = document.getElementById(id);
 128          if (obj && obj.nodeName == "OBJECT") {
 129              if (mejs.MediaFeatures.isIE) {
 130                  obj.style.display = "none";
 131                  (function(){
 132                      if (obj.readyState == 4) {
 133                          mejs.Utility.removeObjectInIE(id);
 134                      } else {
 135                          setTimeout(arguments.callee, 10);
 136                      }
 137                  })();
 138              } else {
 139                  obj.parentNode.removeChild(obj);
 140              }
 141          }
 142      },
 143      removeObjectInIE: function(id) {
 144          var obj = document.getElementById(id);
 145          if (obj) {
 146              for (var i in obj) {
 147                  if (typeof obj[i] == "function") {
 148                      obj[i] = null;
 149                  }
 150              }
 151              obj.parentNode.removeChild(obj);
 152          }        
 153      }
 154  };
 155  
 156  
 157  // Core detector, plugins are added below
 158  mejs.PluginDetector = {
 159  
 160      // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
 161      hasPluginVersion: function(plugin, v) {
 162          var pv = this.plugins[plugin];
 163          v[1] = v[1] || 0;
 164          v[2] = v[2] || 0;
 165          return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
 166      },
 167  
 168      // cached values
 169      nav: window.navigator,
 170      ua: window.navigator.userAgent.toLowerCase(),
 171  
 172      // stored version numbers
 173      plugins: [],
 174  
 175      // runs detectPlugin() and stores the version number
 176      addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
 177          this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
 178      },
 179  
 180      // get the version number from the mimetype (all but IE) or ActiveX (IE)
 181      detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
 182  
 183          var version = [0,0,0],
 184              description,
 185              i,
 186              ax;
 187  
 188          // Firefox, Webkit, Opera
 189          if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
 190              description = this.nav.plugins[pluginName].description;
 191              if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
 192                  version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
 193                  for (i=0; i<version.length; i++) {
 194                      version[i] = parseInt(version[i].match(/\d+/), 10);
 195                  }
 196              }
 197          // Internet Explorer / ActiveX
 198          } else if (typeof(window.ActiveXObject) != 'undefined') {
 199              try {
 200                  ax = new ActiveXObject(activeX);
 201                  if (ax) {
 202                      version = axDetect(ax);
 203                  }
 204              }
 205              catch (e) { }
 206          }
 207          return version;
 208      }
 209  };
 210  
 211  // Add Flash detection
 212  mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
 213      // adapted from SWFObject
 214      var version = [],
 215          d = ax.GetVariable("$version");
 216      if (d) {
 217          d = d.split(" ")[1].split(",");
 218          version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
 219      }
 220      return version;
 221  });
 222  
 223  // Add Silverlight detection
 224  mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
 225      // Silverlight cannot report its version number to IE
 226      // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
 227      // adapted from http://www.silverlightversion.com/
 228      var v = [0,0,0,0],
 229          loopMatch = function(ax, v, i, n) {
 230              while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
 231                  v[i]+=n;
 232              }
 233              v[i] -= n;
 234          };
 235      loopMatch(ax, v, 0, 1);
 236      loopMatch(ax, v, 1, 1);
 237      loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
 238      loopMatch(ax, v, 2, 1000);
 239      loopMatch(ax, v, 2, 100);
 240      loopMatch(ax, v, 2, 10);
 241      loopMatch(ax, v, 2, 1);
 242      loopMatch(ax, v, 3, 1);
 243  
 244      return v;
 245  });
 246  // add adobe acrobat
 247  /*
 248  PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
 249      var version = [],
 250          d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
 251  
 252      if (d) {
 253          version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
 254      }
 255      return version;
 256  });
 257  */
 258  // necessary detection (fixes for <IE9)
 259  mejs.MediaFeatures = {
 260      init: function() {
 261          var
 262              t = this,
 263              d = document,
 264              nav = mejs.PluginDetector.nav,
 265              ua = mejs.PluginDetector.ua.toLowerCase(),
 266              i,
 267              v,
 268              html5Elements = ['source','track','audio','video'];
 269  
 270          // detect browsers (only the ones that have some kind of quirk we need to work around)
 271          t.isiPad = (ua.match(/ipad/i) !== null);
 272          t.isiPhone = (ua.match(/iphone/i) !== null);
 273          t.isiOS = t.isiPhone || t.isiPad;
 274          t.isAndroid = (ua.match(/android/i) !== null);
 275          t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
 276          t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1);
 277          t.isChrome = (ua.match(/chrome/gi) !== null);
 278          t.isFirefox = (ua.match(/firefox/gi) !== null);
 279          t.isWebkit = (ua.match(/webkit/gi) !== null);
 280          t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit;
 281          t.isOpera = (ua.match(/opera/gi) !== null);
 282          t.hasTouch = ('ontouchstart' in window);
 283  
 284          // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
 285          for (i=0; i<html5Elements.length; i++) {
 286              v = document.createElement(html5Elements[i]);
 287          }
 288          
 289          t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
 290  
 291          // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
 292          
 293          // iOS
 294          t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
 295          
 296          // Webkit/firefox
 297          t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
 298          t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
 299          
 300          t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen);
 301          t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
 302          if (t.hasMozNativeFullScreen) {
 303              t.nativeFullScreenEnabled = v.mozFullScreenEnabled;
 304          }
 305          
 306          
 307          if (this.isChrome) {
 308              t.hasSemiNativeFullScreen = false;
 309          }
 310          
 311          if (t.hasTrueNativeFullScreen) {
 312              t.fullScreenEventName = (t.hasWebkitNativeFullScreen) ? 'webkitfullscreenchange' : 'mozfullscreenchange';
 313              
 314              
 315              t.isFullScreen = function() {
 316                  if (v.mozRequestFullScreen) {
 317                      return d.mozFullScreen;
 318                  } else if (v.webkitRequestFullScreen) {
 319                      return d.webkitIsFullScreen;
 320                  }
 321              }
 322                      
 323              t.requestFullScreen = function(el) {
 324          
 325                  if (t.hasWebkitNativeFullScreen) {
 326                      el.webkitRequestFullScreen();
 327                  } else if (t.hasMozNativeFullScreen) {
 328                      el.mozRequestFullScreen();
 329                  }
 330              }
 331              
 332              t.cancelFullScreen = function() {                
 333                  if (t.hasWebkitNativeFullScreen) {
 334                      document.webkitCancelFullScreen();
 335                  } else if (t.hasMozNativeFullScreen) {
 336                      document.mozCancelFullScreen();
 337                  }
 338              }    
 339              
 340          }
 341          
 342          
 343          // OS X 10.5 can't do this even if it says it can :(
 344          if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
 345              t.hasNativeFullScreen = false;
 346              t.hasSemiNativeFullScreen = false;
 347          }
 348          
 349      }
 350  };
 351  mejs.MediaFeatures.init();
 352  
 353  
 354  /*
 355  extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
 356  */
 357  mejs.HtmlMediaElement = {
 358      pluginType: 'native',
 359      isFullScreen: false,
 360  
 361      setCurrentTime: function (time) {
 362          this.currentTime = time;
 363      },
 364  
 365      setMuted: function (muted) {
 366          this.muted = muted;
 367      },
 368  
 369      setVolume: function (volume) {
 370          this.volume = volume;
 371      },
 372  
 373      // for parity with the plugin versions
 374      stop: function () {
 375          this.pause();
 376      },
 377  
 378      // This can be a url string
 379      // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
 380      setSrc: function (url) {
 381          
 382          // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
 383          var 
 384              existingSources = this.getElementsByTagName('source');
 385          while (existingSources.length > 0){
 386              this.removeChild(existingSources[0]);
 387          }
 388      
 389          if (typeof url == 'string') {
 390              this.src = url;
 391          } else {
 392              var i, media;
 393  
 394              for (i=0; i<url.length; i++) {
 395                  media = url[i];
 396                  if (this.canPlayType(media.type)) {
 397                      this.src = media.src;
 398                  }
 399              }
 400          }
 401      },
 402  
 403      setVideoSize: function (width, height) {
 404          this.width = width;
 405          this.height = height;
 406      }
 407  };
 408  
 409  /*
 410  Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
 411  */
 412  mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
 413      this.id = pluginid;
 414      this.pluginType = pluginType;
 415      this.src = mediaUrl;
 416      this.events = {};
 417  };
 418  
 419  // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
 420  // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
 421  // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
 422  mejs.PluginMediaElement.prototype = {
 423  
 424      // special
 425      pluginElement: null,
 426      pluginType: '',
 427      isFullScreen: false,
 428  
 429      // not implemented :(
 430      playbackRate: -1,
 431      defaultPlaybackRate: -1,
 432      seekable: [],
 433      played: [],
 434  
 435      // HTML5 read-only properties
 436      paused: true,
 437      ended: false,
 438      seeking: false,
 439      duration: 0,
 440      error: null,
 441      tagName: '',
 442  
 443      // HTML5 get/set properties, but only set (updated by event handlers)
 444      muted: false,
 445      volume: 1,
 446      currentTime: 0,
 447  
 448      // HTML5 methods
 449      play: function () {
 450          if (this.pluginApi != null) {
 451              if (this.pluginType == 'youtube') {
 452                  this.pluginApi.playVideo();
 453              } else {
 454                  this.pluginApi.playMedia();
 455              }
 456              this.paused = false;
 457          }
 458      },
 459      load: function () {
 460          if (this.pluginApi != null) {
 461              if (this.pluginType == 'youtube') {
 462              } else {
 463                  this.pluginApi.loadMedia();
 464              }
 465              
 466              this.paused = false;
 467          }
 468      },
 469      pause: function () {
 470          if (this.pluginApi != null) {
 471              if (this.pluginType == 'youtube') {
 472                  this.pluginApi.pauseVideo();
 473              } else {
 474                  this.pluginApi.pauseMedia();
 475              }            
 476              
 477              
 478              this.paused = true;
 479          }
 480      },
 481      stop: function () {
 482          if (this.pluginApi != null) {
 483              if (this.pluginType == 'youtube') {
 484                  this.pluginApi.stopVideo();
 485              } else {
 486                  this.pluginApi.stopMedia();
 487              }    
 488              this.paused = true;
 489          }
 490      },
 491      canPlayType: function(type) {
 492          var i,
 493              j,
 494              pluginInfo,
 495              pluginVersions = mejs.plugins[this.pluginType];
 496  
 497          for (i=0; i<pluginVersions.length; i++) {
 498              pluginInfo = pluginVersions[i];
 499  
 500              // test if user has the correct plugin version
 501              if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
 502  
 503                  // test for plugin playback types
 504                  for (j=0; j<pluginInfo.types.length; j++) {
 505                      // find plugin that can play the type
 506                      if (type == pluginInfo.types[j]) {
 507                          return true;
 508                      }
 509                  }
 510              }
 511          }
 512  
 513          return false;
 514      },
 515      
 516      positionFullscreenButton: function(x,y,visibleAndAbove) {
 517          if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
 518              this.pluginApi.positionFullscreenButton(x,y,visibleAndAbove);
 519          }
 520      },
 521      
 522      hideFullscreenButton: function() {
 523          if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
 524              this.pluginApi.hideFullscreenButton();
 525          }        
 526      },    
 527      
 528  
 529      // custom methods since not all JavaScript implementations support get/set
 530  
 531      // This can be a url string
 532      // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
 533      setSrc: function (url) {
 534          if (typeof url == 'string') {
 535              this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
 536              this.src = mejs.Utility.absolutizeUrl(url);
 537          } else {
 538              var i, media;
 539  
 540              for (i=0; i<url.length; i++) {
 541                  media = url[i];
 542                  if (this.canPlayType(media.type)) {
 543                      this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
 544                      this.src = mejs.Utility.absolutizeUrl(url);
 545                  }
 546              }
 547          }
 548  
 549      },
 550      setCurrentTime: function (time) {
 551          if (this.pluginApi != null) {
 552              if (this.pluginType == 'youtube') {
 553                  this.pluginApi.seekTo(time);
 554              } else {
 555                  this.pluginApi.setCurrentTime(time);
 556              }                
 557              
 558              
 559              
 560              this.currentTime = time;
 561          }
 562      },
 563      setVolume: function (volume) {
 564          if (this.pluginApi != null) {
 565              // same on YouTube and MEjs
 566              if (this.pluginType == 'youtube') {
 567                  this.pluginApi.setVolume(volume * 100);
 568              } else {
 569                  this.pluginApi.setVolume(volume);
 570              }
 571              this.volume = volume;
 572          }
 573      },
 574      setMuted: function (muted) {
 575          if (this.pluginApi != null) {
 576              if (this.pluginType == 'youtube') {
 577                  if (muted) {
 578                      this.pluginApi.mute();
 579                  } else {
 580                      this.pluginApi.unMute();
 581                  }
 582                  this.muted = muted;
 583                  this.dispatchEvent('volumechange');
 584              } else {
 585                  this.pluginApi.setMuted(muted);
 586              }
 587              this.muted = muted;
 588          }
 589      },
 590  
 591      // additional non-HTML5 methods
 592      setVideoSize: function (width, height) {
 593          
 594          //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
 595              if ( this.pluginElement.style) {
 596                  this.pluginElement.style.width = width + 'px';
 597                  this.pluginElement.style.height = height + 'px';
 598              }
 599              if (this.pluginApi != null && this.pluginApi.setVideoSize) {
 600                  this.pluginApi.setVideoSize(width, height);
 601              }
 602          //}
 603      },
 604  
 605      setFullscreen: function (fullscreen) {
 606          if (this.pluginApi != null && this.pluginApi.setFullscreen) {
 607              this.pluginApi.setFullscreen(fullscreen);
 608          }
 609      },
 610      
 611      enterFullScreen: function() {
 612          if (this.pluginApi != null && this.pluginApi.setFullscreen) {
 613              this.setFullscreen(true);
 614          }        
 615          
 616      },
 617      
 618      exitFullScreen: function() {
 619          if (this.pluginApi != null && this.pluginApi.setFullscreen) {
 620              this.setFullscreen(false);
 621          }
 622      },    
 623  
 624      // start: fake events
 625      addEventListener: function (eventName, callback, bubble) {
 626          this.events[eventName] = this.events[eventName] || [];
 627          this.events[eventName].push(callback);
 628      },
 629      removeEventListener: function (eventName, callback) {
 630          if (!eventName) { this.events = {}; return true; }
 631          var callbacks = this.events[eventName];
 632          if (!callbacks) return true;
 633          if (!callback) { this.events[eventName] = []; return true; }
 634          for (i = 0; i < callbacks.length; i++) {
 635              if (callbacks[i] === callback) {
 636                  this.events[eventName].splice(i, 1);
 637                  return true;
 638              }
 639          }
 640          return false;
 641      },    
 642      dispatchEvent: function (eventName) {
 643          var i,
 644              args,
 645              callbacks = this.events[eventName];
 646  
 647          if (callbacks) {
 648              args = Array.prototype.slice.call(arguments, 1);
 649              for (i = 0; i < callbacks.length; i++) {
 650                  callbacks[i].apply(null, args);
 651              }
 652          }
 653      },
 654      // end: fake events
 655      
 656      // fake DOM attribute methods
 657      attributes: {},
 658      hasAttribute: function(name){
 659          return (name in this.attributes);  
 660      },
 661      removeAttribute: function(name){
 662          delete this.attributes[name];
 663      },
 664      getAttribute: function(name){
 665          if (this.hasAttribute(name)) {
 666              return this.attributes[name];
 667          }
 668          return '';
 669      },
 670      setAttribute: function(name, value){
 671          this.attributes[name] = value;
 672      },
 673  
 674      remove: function() {
 675          mejs.Utility.removeSwf(this.pluginElement.id);
 676      }
 677  };
 678  
 679  // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
 680  mejs.MediaPluginBridge = {
 681  
 682      pluginMediaElements:{},
 683      htmlMediaElements:{},
 684  
 685      registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
 686          this.pluginMediaElements[id] = pluginMediaElement;
 687          this.htmlMediaElements[id] = htmlMediaElement;
 688      },
 689  
 690      // when Flash/Silverlight is ready, it calls out to this method
 691      initPlugin: function (id) {
 692  
 693          var pluginMediaElement = this.pluginMediaElements[id],
 694              htmlMediaElement = this.htmlMediaElements[id];
 695  
 696          if (pluginMediaElement) {
 697              // find the javascript bridge
 698              switch (pluginMediaElement.pluginType) {
 699                  case "flash":
 700                      pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
 701                      break;
 702                  case "silverlight":
 703                      pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
 704                      pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
 705                      break;
 706              }
 707      
 708              if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
 709                  pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
 710              }
 711          }
 712      },
 713  
 714      // receives events from Flash/Silverlight and sends them out as HTML5 media events
 715      // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
 716      fireEvent: function (id, eventName, values) {
 717  
 718          var
 719              e,
 720              i,
 721              bufferedTime,
 722              pluginMediaElement = this.pluginMediaElements[id];
 723  
 724          pluginMediaElement.ended = false;
 725          pluginMediaElement.paused = true;
 726  
 727          // fake event object to mimic real HTML media event.
 728          e = {
 729              type: eventName,
 730              target: pluginMediaElement
 731          };
 732  
 733          // attach all values to element and event object
 734          for (i in values) {
 735              pluginMediaElement[i] = values[i];
 736              e[i] = values[i];
 737          }
 738  
 739          // fake the newer W3C buffered TimeRange (loaded and total have been removed)
 740          bufferedTime = values.bufferedTime || 0;
 741  
 742          e.target.buffered = e.buffered = {
 743              start: function(index) {
 744                  return 0;
 745              },
 746              end: function (index) {
 747                  return bufferedTime;
 748              },
 749              length: 1
 750          };
 751  
 752          pluginMediaElement.dispatchEvent(e.type, e);
 753      }
 754  };
 755  
 756  /*
 757  Default options
 758  */
 759  mejs.MediaElementDefaults = {
 760      // allows testing on HTML5, flash, silverlight
 761      // auto: attempts to detect what the browser can do
 762      // native: forces HTML5 playback
 763      // shim: disallows HTML5, will attempt either Flash or Silverlight
 764      // none: forces fallback view
 765      mode: 'auto',
 766      // remove or reorder to change plugin priority and availability
 767      plugins: ['flash','silverlight','youtube','vimeo'],
 768      // shows debug errors on screen
 769      enablePluginDebug: false,
 770      // overrides the type specified, useful for dynamic instantiation
 771      type: '',
 772      // path to Flash and Silverlight plugins
 773      pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
 774      // name of flash file
 775      flashName: 'flashmediaelement.swf',
 776      // turns on the smoothing filter in Flash
 777      enablePluginSmoothing: false,
 778      // name of silverlight file
 779      silverlightName: 'silverlightmediaelement.xap',
 780      // default if the <video width> is not specified
 781      defaultVideoWidth: 480,
 782      // default if the <video height> is not specified
 783      defaultVideoHeight: 270,
 784      // overrides <video width>
 785      pluginWidth: -1,
 786      // overrides <video height>
 787      pluginHeight: -1,
 788      // additional plugin variables in 'key=value' form
 789      pluginVars: [],    
 790      // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
 791      // larger number is less accurate, but less strain on plugin->JavaScript bridge
 792      timerRate: 250,
 793      // initial volume for player
 794      startVolume: 0.8,
 795      success: function () { },
 796      error: function () { }
 797  };
 798  
 799  /*
 800  Determines if a browser supports the <video> or <audio> element
 801  and returns either the native element or a Flash/Silverlight version that
 802  mimics HTML5 MediaElement
 803  */
 804  mejs.MediaElement = function (el, o) {
 805      return mejs.HtmlMediaElementShim.create(el,o);
 806  };
 807  
 808  mejs.HtmlMediaElementShim = {
 809  
 810      create: function(el, o) {
 811          var
 812              options = mejs.MediaElementDefaults,
 813              htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
 814              tagName = htmlMediaElement.tagName.toLowerCase(),
 815              isMediaTag = (tagName === 'audio' || tagName === 'video'),
 816              src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
 817              poster = htmlMediaElement.getAttribute('poster'),
 818              autoplay =  htmlMediaElement.getAttribute('autoplay'),
 819              preload =  htmlMediaElement.getAttribute('preload'),
 820              controls =  htmlMediaElement.getAttribute('controls'),
 821              playback,
 822              prop;
 823  
 824          // extend options
 825          for (prop in o) {
 826              options[prop] = o[prop];
 827          }
 828  
 829          // clean up attributes
 830          src =         (typeof src == 'undefined'     || src === null || src == '') ? null : src;        
 831          poster =    (typeof poster == 'undefined'     || poster === null) ? '' : poster;
 832          preload =     (typeof preload == 'undefined'     || preload === null || preload === 'false') ? 'none' : preload;
 833          autoplay =     !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
 834          controls =     !(typeof controls == 'undefined' || controls === null || controls === 'false');
 835  
 836          // test for HTML5 and plugin capabilities
 837          playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
 838          playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
 839  
 840          if (playback.method == 'native') {
 841              // second fix for android
 842              if (mejs.MediaFeatures.isBustedAndroid) {
 843                  htmlMediaElement.src = playback.url;
 844                  htmlMediaElement.addEventListener('click', function() {
 845                      htmlMediaElement.play();
 846                  }, false);
 847              }
 848          
 849              // add methods to native HTMLMediaElement
 850              return this.updateNative(playback, options, autoplay, preload);
 851          } else if (playback.method !== '') {
 852              // create plugin to mimic HTMLMediaElement
 853              
 854              return this.createPlugin( playback,  options, poster, autoplay, preload, controls);
 855          } else {
 856              // boo, no HTML5, no Flash, no Silverlight.
 857              this.createErrorMessage( playback, options, poster );
 858              
 859              return this;
 860          }
 861      },
 862      
 863      determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
 864          var
 865              mediaFiles = [],
 866              i,
 867              j,
 868              k,
 869              l,
 870              n,
 871              type,
 872              result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
 873              pluginName,
 874              pluginVersions,
 875              pluginInfo,
 876              dummy;
 877              
 878          // STEP 1: Get URL and type from <video src> or <source src>
 879  
 880          // supplied type overrides <video type> and <source type>
 881          if (typeof options.type != 'undefined' && options.type !== '') {
 882              
 883              // accept either string or array of types
 884              if (typeof options.type == 'string') {
 885                  mediaFiles.push({type:options.type, url:src});
 886              } else {
 887                  
 888                  for (i=0; i<options.type.length; i++) {
 889                      mediaFiles.push({type:options.type[i], url:src});
 890                  }
 891              }
 892  
 893          // test for src attribute first
 894          } else if (src !== null) {
 895              type = this.formatType(src, htmlMediaElement.getAttribute('type'));
 896              mediaFiles.push({type:type, url:src});
 897  
 898          // then test for <source> elements
 899          } else {
 900              // test <source> types to see if they are usable
 901              for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
 902                  n = htmlMediaElement.childNodes[i];
 903                  if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
 904                      src = n.getAttribute('src');
 905                      type = this.formatType(src, n.getAttribute('type'));
 906                      mediaFiles.push({type:type, url:src});
 907                  }
 908              }
 909          }
 910          
 911          // in the case of dynamicly created players
 912          // check for audio types
 913          if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
 914              result.isVideo = false;
 915          }
 916          
 917  
 918          // STEP 2: Test for playback method
 919          
 920          // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
 921          if (mejs.MediaFeatures.isBustedAndroid) {
 922              htmlMediaElement.canPlayType = function(type) {
 923                  return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
 924              };
 925          }        
 926          
 927  
 928          // test for native playback first
 929          if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'native')) {
 930                          
 931              if (!isMediaTag) {
 932  
 933                  // create a real HTML5 Media Element 
 934                  dummy = document.createElement( result.isVideo ? 'video' : 'audio');            
 935                  htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
 936                  htmlMediaElement.style.display = 'none';
 937                  
 938                  // use this one from now on
 939                  result.htmlMediaElement = htmlMediaElement = dummy;
 940              }
 941                  
 942              for (i=0; i<mediaFiles.length; i++) {
 943                  // normal check
 944                  if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== '' 
 945                      // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
 946                      || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '') {
 947                      result.method = 'native';
 948                      result.url = mediaFiles[i].url;
 949                      break;
 950                  }
 951              }            
 952              
 953              if (result.method === 'native') {
 954                  if (result.url !== null) {
 955                      htmlMediaElement.src = result.url;
 956                  }
 957              
 958                  return result;
 959              }
 960          }
 961  
 962          // if native playback didn't work, then test plugins
 963          if (options.mode === 'auto' || options.mode === 'shim') {
 964              for (i=0; i<mediaFiles.length; i++) {
 965                  type = mediaFiles[i].type;
 966  
 967                  // test all plugins in order of preference [silverlight, flash]
 968                  for (j=0; j<options.plugins.length; j++) {
 969  
 970                      pluginName = options.plugins[j];
 971              
 972                      // test version of plugin (for future features)
 973                      pluginVersions = mejs.plugins[pluginName];                
 974                      
 975                      for (k=0; k<pluginVersions.length; k++) {
 976                          pluginInfo = pluginVersions[k];
 977                      
 978                          // test if user has the correct plugin version
 979                          
 980                          // for youtube/vimeo
 981                          if (pluginInfo.version == null || 
 982                              
 983                              mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
 984  
 985                              // test for plugin playback types
 986                              for (l=0; l<pluginInfo.types.length; l++) {
 987                                  // find plugin that can play the type
 988                                  if (type == pluginInfo.types[l]) {
 989                                      result.method = pluginName;
 990                                      result.url = mediaFiles[i].url;
 991                                      return result;
 992                                  }
 993                              }
 994                          }
 995                      }
 996                  }
 997              }
 998          }
 999          
1000          // what if there's nothing to play? just grab the first available
1001          if (result.method === '' && mediaFiles.length > 0) {
1002              result.url = mediaFiles[0].url;
1003          }
1004  
1005          return result;
1006      },
1007  
1008      formatType: function(url, type) {
1009          var ext;
1010  
1011          // if no type is supplied, fake it with the extension
1012          if (url && !type) {        
1013              return this.getTypeFromFile(url);
1014          } else {
1015              // only return the mime part of the type in case the attribute contains the codec
1016              // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
1017              // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
1018              
1019              if (type && ~type.indexOf(';')) {
1020                  return type.substr(0, type.indexOf(';')); 
1021              } else {
1022                  return type;
1023              }
1024          }
1025      },
1026      
1027      getTypeFromFile: function(url) {
1028          var ext = url.substring(url.lastIndexOf('.') + 1);
1029          return (/(mp4|m4v|ogg|ogv|webm|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + ext;
1030      },
1031  
1032      createErrorMessage: function(playback, options, poster) {
1033          var 
1034              htmlMediaElement = playback.htmlMediaElement,
1035              errorContainer = document.createElement('div');
1036              
1037          errorContainer.className = 'me-cannotplay';
1038  
1039          try {
1040              errorContainer.style.width = htmlMediaElement.width + 'px';
1041              errorContainer.style.height = htmlMediaElement.height + 'px';
1042          } catch (e) {}
1043  
1044          errorContainer.innerHTML = (poster !== '') ?
1045              '<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' :
1046              '<a href="' + playback.url + '"><span>Download File</span></a>';
1047  
1048          htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
1049          htmlMediaElement.style.display = 'none';
1050  
1051          options.error(htmlMediaElement);
1052      },
1053  
1054      createPlugin:function(playback, options, poster, autoplay, preload, controls) {
1055          var 
1056              htmlMediaElement = playback.htmlMediaElement,
1057              width = 1,
1058              height = 1,
1059              pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
1060              pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
1061              container = document.createElement('div'),
1062              specialIEContainer,
1063              node,
1064              initVars;
1065  
1066          // copy tagName from html media element
1067          pluginMediaElement.tagName = htmlMediaElement.tagName
1068  
1069          // copy attributes from html media element to plugin media element
1070          for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
1071              var attribute = htmlMediaElement.attributes[i];
1072              if (attribute.specified == true) {
1073                  pluginMediaElement.setAttribute(attribute.name, attribute.value);
1074              }
1075          }
1076  
1077          // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
1078          node = htmlMediaElement.parentNode;
1079          while (node !== null && node.tagName.toLowerCase() != 'body') {
1080              if (node.parentNode.tagName.toLowerCase() == 'p') {
1081                  node.parentNode.parentNode.insertBefore(node, node.parentNode);
1082                  break;
1083              }
1084              node = node.parentNode;
1085          }
1086  
1087          if (playback.isVideo) {
1088              width = (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
1089              height = (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
1090          
1091              // in case of '%' make sure it's encoded
1092              width = mejs.Utility.encodeUrl(width);
1093              height = mejs.Utility.encodeUrl(height);
1094          
1095          } else {
1096              if (options.enablePluginDebug) {
1097                  width = 320;
1098                  height = 240;
1099              }
1100          }
1101  
1102          // register plugin
1103          pluginMediaElement.success = options.success;
1104          mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
1105  
1106          // add container (must be added to DOM before inserting HTML for IE)
1107          container.className = 'me-plugin';
1108          container.id = pluginid + '_container';
1109          
1110          if (playback.isVideo) {
1111                  htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
1112          } else {
1113                  document.body.insertBefore(container, document.body.childNodes[0]);
1114          }
1115  
1116          // flash/silverlight vars
1117          initVars = [
1118              'id=' + pluginid,
1119              'isvideo=' + ((playback.isVideo) ? "true" : "false"),
1120              'autoplay=' + ((autoplay) ? "true" : "false"),
1121              'preload=' + preload,
1122              'width=' + width,
1123              'startvolume=' + options.startVolume,
1124              'timerrate=' + options.timerRate,
1125              'height=' + height];
1126  
1127          if (playback.url !== null) {
1128              if (playback.method == 'flash') {
1129                  initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
1130              } else {
1131                  initVars.push('file=' + playback.url);
1132              }
1133          }
1134          if (options.enablePluginDebug) {
1135              initVars.push('debug=true');
1136          }
1137          if (options.enablePluginSmoothing) {
1138              initVars.push('smoothing=true');
1139          }
1140          if (controls) {
1141              initVars.push('controls=true'); // shows controls in the plugin if desired
1142          }
1143          if (options.pluginVars) {
1144              initVars = initVars.concat(options.pluginVars);
1145          }        
1146  
1147          switch (playback.method) {
1148              case 'silverlight':
1149                  container.innerHTML =
1150  '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '">' +
1151  '<param name="initParams" value="' + initVars.join(',') + '" />' +
1152  '<param name="windowless" value="true" />' +
1153  '<param name="background" value="black" />' +
1154  '<param name="minRuntimeVersion" value="3.0.0.0" />' +
1155  '<param name="autoUpgrade" value="true" />' +
1156  '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
1157  '</object>';
1158                      break;
1159  
1160              case 'flash':
1161  
1162                  if (mejs.MediaFeatures.isIE) {
1163                      specialIEContainer = document.createElement('div');
1164                      container.appendChild(specialIEContainer);
1165                      specialIEContainer.outerHTML =
1166  '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
1167  'id="' + pluginid + '" width="' + width + '" height="' + height + '">' +
1168  '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
1169  '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
1170  '<param name="quality" value="high" />' +
1171  '<param name="bgcolor" value="#000000" />' +
1172  '<param name="wmode" value="transparent" />' +
1173  '<param name="allowScriptAccess" value="always" />' +
1174  '<param name="allowFullScreen" value="true" />' +
1175  '</object>';
1176  
1177                  } else {
1178  
1179                      container.innerHTML =
1180  '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
1181  'play="true" ' +
1182  'loop="false" ' +
1183  'quality="high" ' +
1184  'bgcolor="#000000" ' +
1185  'wmode="transparent" ' +
1186  'allowScriptAccess="always" ' +
1187  'allowFullScreen="true" ' +
1188  'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
1189  'src="' + options.pluginPath + options.flashName + '" ' +
1190  'flashvars="' + initVars.join('&') + '" ' +
1191  'width="' + width + '" ' +
1192  'height="' + height + '"></embed>';
1193                  }
1194                  break;
1195              
1196              case 'youtube':
1197              
1198                  
1199                  var
1200                      videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
1201                      youtubeSettings = {
1202                          container: container,
1203                          containerId: container.id,
1204                          pluginMediaElement: pluginMediaElement,
1205                          pluginId: pluginid,
1206                          videoId: videoId,
1207                          height: height,
1208                          width: width    
1209                      };                
1210                  
1211                  if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
1212                      mejs.YouTubeApi.createFlash(youtubeSettings);
1213                  } else {
1214                      mejs.YouTubeApi.enqueueIframe(youtubeSettings);        
1215                  }
1216                  
1217                  break;
1218              
1219              // DEMO Code. Does NOT work.
1220              case 'vimeo':
1221                  console.log('vimeoid');
1222                  
1223                  pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
1224                  
1225                  container.innerHTML =
1226                      '<object width="' + width + '" height="' + height + '">' +
1227                          '<param name="allowfullscreen" value="true" />' +
1228                          '<param name="allowscriptaccess" value="always" />' +
1229                          '<param name="flashvars" value="api=1" />' + 
1230                          '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=' + pluginMediaElement.vimeoid  + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" />' +
1231                          '<embed src="//vimeo.com/moogaloop.swf?api=1&amp;clip_id=' + pluginMediaElement.vimeoid + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="' + width + '" height="' + height + '"></embed>' +
1232                      '</object>';
1233                      
1234                  break;            
1235          }
1236          // hide original element
1237          htmlMediaElement.style.display = 'none';
1238  
1239          // FYI: options.success will be fired by the MediaPluginBridge
1240          
1241          return pluginMediaElement;
1242      },
1243  
1244      updateNative: function(playback, options, autoplay, preload) {
1245          
1246          var htmlMediaElement = playback.htmlMediaElement,
1247              m;
1248          
1249          
1250          // add methods to video object to bring it into parity with Flash Object
1251          for (m in mejs.HtmlMediaElement) {
1252              htmlMediaElement[m] = mejs.HtmlMediaElement[m];
1253          }
1254  
1255          /*
1256          Chrome now supports preload="none"
1257          if (mejs.MediaFeatures.isChrome) {
1258          
1259              // special case to enforce preload attribute (Chrome doesn't respect this)
1260              if (preload === 'none' && !autoplay) {
1261              
1262                  // forces the browser to stop loading (note: fails in IE9)
1263                  htmlMediaElement.src = '';
1264                  htmlMediaElement.load();
1265                  htmlMediaElement.canceledPreload = true;
1266  
1267                  htmlMediaElement.addEventListener('play',function() {
1268                      if (htmlMediaElement.canceledPreload) {
1269                          htmlMediaElement.src = playback.url;
1270                          htmlMediaElement.load();
1271                          htmlMediaElement.play();
1272                          htmlMediaElement.canceledPreload = false;
1273                      }
1274                  }, false);
1275              // for some reason Chrome forgets how to autoplay sometimes.
1276              } else if (autoplay) {
1277                  htmlMediaElement.load();
1278                  htmlMediaElement.play();
1279              }
1280          }
1281          */
1282  
1283          // fire success code
1284          options.success(htmlMediaElement, htmlMediaElement);
1285          
1286          return htmlMediaElement;
1287      }
1288  };
1289  
1290  /*
1291   - test on IE (object vs. embed)
1292   - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
1293   - fullscreen?
1294  */
1295  
1296  // YouTube Flash and Iframe API
1297  mejs.YouTubeApi = {
1298      isIframeStarted: false,
1299      isIframeLoaded: false,
1300      loadIframeApi: function() {
1301          if (!this.isIframeStarted) {
1302              var tag = document.createElement('script');
1303              tag.src = "http://www.youtube.com/player_api";
1304              var firstScriptTag = document.getElementsByTagName('script')[0];
1305              firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
1306              this.isIframeStarted = true;
1307          }
1308      },
1309      iframeQueue: [],
1310      enqueueIframe: function(yt) {
1311          
1312          if (this.isLoaded) {
1313              this.createIframe(yt);
1314          } else {
1315              this.loadIframeApi();
1316              this.iframeQueue.push(yt);
1317          }
1318      },
1319      createIframe: function(settings) {
1320          
1321          var
1322          pluginMediaElement = settings.pluginMediaElement,    
1323          player = new YT.Player(settings.containerId, {
1324              height: settings.height,
1325              width: settings.width,
1326              videoId: settings.videoId,
1327              playerVars: {controls:0},
1328              events: {
1329                  'onReady': function() {
1330                      
1331                      // hook up iframe object to MEjs
1332                      settings.pluginMediaElement.pluginApi = player;
1333                      
1334                      // init mejs
1335                      mejs.MediaPluginBridge.initPlugin(settings.pluginId);
1336                      
1337                      // create timer
1338                      setInterval(function() {
1339                          mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1340                      }, 250);                    
1341                  },
1342                  'onStateChange': function(e) {
1343                      
1344                      mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
1345                      
1346                  }
1347              }
1348          });
1349      },
1350      
1351      createEvent: function (player, pluginMediaElement, eventName) {
1352          var obj = {
1353              type: eventName,
1354              target: pluginMediaElement
1355          };
1356  
1357          if (player && player.getDuration) {
1358              
1359              // time 
1360              pluginMediaElement.currentTime = obj.currentTime = player.getCurrentTime();
1361              pluginMediaElement.duration = obj.duration = player.getDuration();
1362              
1363              // state
1364              obj.paused = pluginMediaElement.paused;
1365              obj.ended = pluginMediaElement.ended;            
1366              
1367              // sound
1368              obj.muted = player.isMuted();
1369              obj.volume = player.getVolume() / 100;
1370              
1371              // progress
1372              obj.bytesTotal = player.getVideoBytesTotal();
1373              obj.bufferedBytes = player.getVideoBytesLoaded();
1374              
1375              // fake the W3C buffered TimeRange
1376              var bufferedTime = obj.bufferedBytes / obj.bytesTotal * obj.duration;
1377              
1378              obj.target.buffered = obj.buffered = {
1379                  start: function(index) {
1380                      return 0;
1381                  },
1382                  end: function (index) {
1383                      return bufferedTime;
1384                  },
1385                  length: 1
1386              };
1387              
1388          }
1389          
1390          // send event up the chain
1391          pluginMediaElement.dispatchEvent(obj.type, obj);
1392      },    
1393      
1394      iFrameReady: function() {
1395          
1396          this.isLoaded = true;
1397          this.isIframeLoaded = true;
1398          
1399          while (this.iframeQueue.length > 0) {
1400              var settings = this.iframeQueue.pop();
1401              this.createIframe(settings);
1402          }    
1403      },
1404      
1405      // FLASH!
1406      flashPlayers: {},
1407      createFlash: function(settings) {
1408          
1409          this.flashPlayers[settings.pluginId] = settings;
1410          
1411          /*
1412          settings.container.innerHTML =
1413              '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
1414                  'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; ">' +
1415                  '<param name="allowScriptAccess" value="always">' +
1416                  '<param name="wmode" value="transparent">' +
1417              '</object>';
1418          */
1419  
1420          var specialIEContainer,
1421              youtubeUrl = 'http://www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId  + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
1422              
1423          if (mejs.MediaFeatures.isIE) {
1424              
1425              specialIEContainer = document.createElement('div');
1426              settings.container.appendChild(specialIEContainer);
1427              specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
1428  'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '">' +
1429      '<param name="movie" value="' + youtubeUrl + '" />' +
1430      '<param name="wmode" value="transparent" />' +
1431      '<param name="allowScriptAccess" value="always" />' +
1432      '<param name="allowFullScreen" value="true" />' +
1433  '</object>';
1434          } else {
1435          settings.container.innerHTML =
1436              '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
1437                  'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; ">' +
1438                  '<param name="allowScriptAccess" value="always">' +
1439                  '<param name="wmode" value="transparent">' +
1440              '</object>';
1441          }        
1442          
1443      },
1444      
1445      flashReady: function(id) {
1446          var
1447              settings = this.flashPlayers[id],
1448              player = document.getElementById(id),
1449              pluginMediaElement = settings.pluginMediaElement;
1450          
1451          // hook up and return to MediaELementPlayer.success    
1452          pluginMediaElement.pluginApi = 
1453          pluginMediaElement.pluginElement = player;
1454          mejs.MediaPluginBridge.initPlugin(id);
1455          
1456          // load the youtube video
1457          player.cueVideoById(settings.videoId);
1458          
1459          var callbackName = settings.containerId + '_callback'
1460          
1461          window[callbackName] = function(e) {
1462              mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
1463          }
1464          
1465          player.addEventListener('onStateChange', callbackName);
1466          
1467          setInterval(function() {
1468              mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1469          }, 250);
1470      },
1471      
1472      handleStateChange: function(youTubeState, player, pluginMediaElement) {
1473          switch (youTubeState) {
1474              case -1: // not started
1475                  pluginMediaElement.paused = true;
1476                  pluginMediaElement.ended = true;
1477                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
1478                  //createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
1479                  break;
1480              case 0:
1481                  pluginMediaElement.paused = false;
1482                  pluginMediaElement.ended = true;
1483                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
1484                  break;
1485              case 1:
1486                  pluginMediaElement.paused = false;
1487                  pluginMediaElement.ended = false;                
1488                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
1489                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
1490                  break;
1491              case 2:
1492                  pluginMediaElement.paused = true;
1493                  pluginMediaElement.ended = false;                
1494                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
1495                  break;
1496              case 3: // buffering
1497                  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
1498                  break;
1499              case 5:
1500                  // cued?
1501                  break;                        
1502              
1503          }            
1504          
1505      }
1506  }
1507  // IFRAME
1508  function onYouTubePlayerAPIReady() {
1509      mejs.YouTubeApi.iFrameReady();
1510  }
1511  // FLASH
1512  function onYouTubePlayerReady(id) {
1513      mejs.YouTubeApi.flashReady(id);
1514  }
1515  
1516  window.mejs = mejs;
1517  window.MediaElement = mejs.MediaElement;
1518  
1519  /*!
1520   * MediaElementPlayer
1521   * http://mediaelementjs.com/
1522   *
1523   * Creates a controller bar for HTML5 <video> add <audio> tags
1524   * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
1525   *
1526   * Copyright 2010-2012, John Dyer (http://j.hn/)
1527   * Dual licensed under the MIT or GPL Version 2 licenses.
1528   *
1529   */
1530  if (typeof jQuery != 'undefined') {
1531      mejs.$ = jQuery;
1532  } else if (typeof ender != 'undefined') {
1533      mejs.$ = ender;
1534  }
1535  (function ($) {
1536  
1537      // default player values

1538      mejs.MepDefaults = {
1539          // url to poster (to fix iOS 3.x)

1540          poster: '',
1541          // default if the <video width> is not specified

1542          defaultVideoWidth: 480,
1543          // default if the <video height> is not specified

1544          defaultVideoHeight: 270,
1545          // if set, overrides <video width>

1546          videoWidth: -1,
1547          // if set, overrides <video height>

1548          videoHeight: -1,
1549          // default if the user doesn't specify

1550          defaultAudioWidth: 400,
1551          // default if the user doesn't specify

1552          defaultAudioHeight: 30,
1553          // width of audio player

1554          audioWidth: -1,
1555          // height of audio player

1556          audioHeight: -1,        
1557          // initial volume when the player starts (overrided by user cookie)

1558          startVolume: 0.8,
1559          // useful for <audio> player loops

1560          loop: false,
1561          // resize to media dimensions

1562          enableAutosize: true,
1563          // forces the hour marker (##:00:00)

1564          alwaysShowHours: false,
1565  
1566          // show framecount in timecode (##:00:00:00)

1567          showTimecodeFrameCount: false,
1568          // used when showTimecodeFrameCount is set to true

1569          framesPerSecond: 25,
1570          
1571          // automatically calculate the width of the progress bar based on the sizes of other elements

1572          autosizeProgress : true,
1573          // Hide controls when playing and mouse is not over the video

1574          alwaysShowControls: false,
1575          // force iPad's native controls

1576          iPadUseNativeControls: false,
1577          // force iPhone's native controls

1578          iPhoneUseNativeControls: false,    
1579          // force Android's native controls

1580          AndroidUseNativeControls: false,            
1581          // features to show

1582          features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
1583          // only for dynamic

1584          isVideo: true,
1585          
1586          // turns keyboard support on and off for this instance

1587          enableKeyboard: true,
1588          
1589          // whenthis player starts, it will pause other players

1590          pauseOtherPlayers: true,
1591          
1592          // array of keyboard actions such as play pause

1593          keyActions: [
1594                  {
1595                          keys: [
1596                                  32, // SPACE
1597                                  179 // GOOGLE play/pause button
1598                                ],
1599                          action: function(player, media) {
1600                                  if (media.paused || media.ended) {
1601                                          media.play();    
1602                                  } else {
1603                                          media.pause();
1604                                  }                                        
1605                          }
1606                  },
1607                  {
1608                          keys: [38], // UP
1609                          action: function(player, media) {
1610                                  var newVolume = Math.min(media.volume + 0.1, 1);
1611                                  media.setVolume(newVolume);
1612                          }
1613                  },
1614                  {
1615                          keys: [40], // DOWN
1616                          action: function(player, media) {
1617                                  var newVolume = Math.max(media.volume - 0.1, 0);
1618                                  media.setVolume(newVolume);
1619                          }
1620                  },
1621                  {
1622                          keys: [
1623                                  37, // LEFT
1624                                  227 // Google TV rewind
1625                          ],
1626                          action: function(player, media) {
1627                                  if (!isNaN(media.duration) && media.duration > 0) {
1628                                          if (player.isVideo) {
1629                                                  player.showControls();
1630                                                  player.startControlsTimer();
1631                                          }
1632                                          
1633                                          // 5%

1634                                          var newTime = Math.max(media.currentTime - (media.duration * 0.05), 0);
1635                                          media.setCurrentTime(newTime);
1636                                  }
1637                          }
1638                  },
1639                  {
1640                          keys: [
1641                                  39, // RIGHT
1642                                  228 // Google TV forward
1643                          ], 
1644                          action: function(player, media) {
1645                                  if (!isNaN(media.duration) && media.duration > 0) {
1646                                          if (player.isVideo) {
1647                                                  player.showControls();
1648                                                  player.startControlsTimer();
1649                                          }
1650                                          
1651                                          // 5%

1652                                          var newTime = Math.min(media.currentTime + (media.duration * 0.05), media.duration);
1653                                          media.setCurrentTime(newTime);
1654                                  }
1655                          }
1656                  },
1657                  {
1658                          keys: [70], // f
1659                          action: function(player, media) {
1660                                  if (typeof player.enterFullScreen != 'undefined') {
1661                                          if (player.isFullScreen) {
1662                                                  player.exitFullScreen();
1663                                          } else {
1664                                                  player.enterFullScreen();
1665                                          }
1666                                  }
1667                          }
1668                  }                    
1669          ]        
1670      };
1671  
1672      mejs.mepIndex = 0;
1673      
1674      mejs.players = [];
1675  
1676      // wraps a MediaElement object in player controls

1677      mejs.MediaElementPlayer = function(node, o) {
1678          // enforce object, even without "new" (via John Resig)

1679          if ( !(this instanceof mejs.MediaElementPlayer) ) {
1680              return new mejs.MediaElementPlayer(node, o);
1681          } 
1682  
1683          var t = this;
1684          
1685          // these will be reset after the MediaElement.success fires

1686          t.$media = t.$node = $(node);
1687          t.node = t.media = t.$media[0];        
1688          
1689          // check for existing player

1690          if (typeof t.node.player != 'undefined') {
1691              return t.node.player;
1692          } else {
1693              // attach player to DOM node for reference

1694              t.node.player = t;
1695          }
1696                  
1697                  
1698          // try to get options from data-mejsoptions

1699          if (typeof o == 'undefined') {
1700              o = t.$node.data('mejsoptions');    
1701          }
1702              
1703          // extend default options

1704          t.options = $.extend({},mejs.MepDefaults,o);
1705          
1706          // add to player array (for focus events)

1707          mejs.players.push(t);
1708          
1709          // start up

1710          t.init();
1711  
1712          return t;
1713      };
1714  
1715      // actual player

1716      mejs.MediaElementPlayer.prototype = {
1717          
1718          hasFocus: false,
1719          
1720          controlsAreVisible: true,
1721          
1722          init: function() {
1723  
1724              var
1725                  t = this,
1726                  mf = mejs.MediaFeatures,
1727                  // options for MediaElement (shim)

1728                  meOptions = $.extend(true, {}, t.options, {
1729                      success: function(media, domNode) { t.meReady(media, domNode); },
1730                      error: function(e) { t.handleError(e);}
1731                  }),
1732                  tagName = t.media.tagName.toLowerCase();
1733          
1734              t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
1735              
1736              if (t.isDynamic) {    
1737                  // get video from src or href?                

1738                  t.isVideo = t.options.isVideo;                        
1739              } else {
1740                  t.isVideo = (tagName !== 'audio' && t.options.isVideo);
1741              }
1742          
1743              // use native controls in iPad, iPhone, and Android    

1744              if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
1745                  
1746                  // add controls and stop

1747                  t.$media.attr('controls', 'controls');
1748  
1749                  // attempt to fix iOS 3 bug

1750                  //t.$media.removeAttr('poster');

1751                                  // no Issue found on iOS3 -ttroxell

1752  
1753                  // override Apple's autoplay override for iPads

1754                  if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
1755                      t.media.load();
1756                      t.media.play();
1757                  }
1758                      
1759              } else if (mf.isAndroid && t.AndroidUseNativeControls) {
1760                  
1761                  // leave default player

1762  
1763              } else {
1764  
1765                  // DESKTOP: use MediaElementPlayer controls

1766                  
1767                  // remove native controls             

1768                  t.$media.removeAttr('controls');                    
1769                  
1770                  // unique ID

1771                  t.id = 'mep_' + mejs.mepIndex++;
1772  
1773                  // build container

1774                  t.container =
1775                      $('<div id="' + t.id + '" class="mejs-container">'+
1776                          '<div class="mejs-inner">'+
1777                              '<div class="mejs-mediaelement"></div>'+
1778                              '<div class="mejs-layers"></div>'+
1779                              '<div class="mejs-controls"></div>'+
1780                              '<div class="mejs-clear"></div>'+
1781                          '</div>' +
1782                      '</div>')
1783                      .addClass(t.$media[0].className)
1784                      .insertBefore(t.$media);    
1785                      
1786                  // add classes for user and content

1787                  t.container.addClass(
1788                      (mf.isAndroid ? 'mejs-android ' : '') +
1789                      (mf.isiOS ? 'mejs-ios ' : '') +
1790                      (mf.isiPad ? 'mejs-ipad ' : '') +
1791                      (mf.isiPhone ? 'mejs-iphone ' : '') +
1792                      (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
1793                  );    
1794                      
1795  
1796                  // move the <video/video> tag into the right spot

1797                  if (mf.isiOS) {
1798                  
1799                      // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!

1800                      var $newMedia = t.$media.clone();
1801                      
1802                      t.container.find('.mejs-mediaelement').append($newMedia);
1803                      
1804                      t.$media.remove();
1805                      t.$node = t.$media = $newMedia;
1806                      t.node = t.media = $newMedia[0]
1807                      
1808                  } else {
1809                      
1810                      // normal way of moving it into place (doesn't work on iOS)

1811                      t.container.find('.mejs-mediaelement').append(t.$media);
1812                  }
1813                  
1814                  // find parts

1815                  t.controls = t.container.find('.mejs-controls');
1816                  t.layers = t.container.find('.mejs-layers');
1817  
1818                  // determine the size

1819                  
1820                  /* size priority:

1821                      (1) videoWidth (forced), 

1822                      (2) style="width;height;"

1823                      (3) width attribute,

1824                      (4) defaultVideoWidth (for unspecified cases)

1825                  */
1826                  
1827                  var capsTagName = tagName.substring(0,1).toUpperCase() + tagName.substring(1);
1828                  
1829                  if (t.options[tagName + 'Width'] > 0 || t.options[tagName + 'Width'].toString().indexOf('%') > -1) {
1830                      t.width = t.options[tagName + 'Width'];
1831                  } else if (t.media.style.width !== '' && t.media.style.width !== null) {
1832                      t.width = t.media.style.width;                        
1833                  } else if (t.media.getAttribute('width') !== null) {
1834                      t.width = t.$media.attr('width');
1835                  } else {
1836                      t.width = t.options['default' + capsTagName + 'Width'];
1837                  }
1838                  
1839                  if (t.options[tagName + 'Height'] > 0 || t.options[tagName + 'Height'].toString().indexOf('%') > -1) {
1840                      t.height = t.options[tagName + 'Height'];
1841                  } else if (t.media.style.height !== '' && t.media.style.height !== null) {
1842                      t.height = t.media.style.height;
1843                  } else if (t.$media[0].getAttribute('height') !== null) {
1844                      t.height = t.$media.attr('height');    
1845                  } else {
1846                      t.height = t.options['default' + capsTagName + 'Height'];
1847                  }
1848  
1849                  // set the size, while we wait for the plugins to load below

1850                  t.setPlayerSize(t.width, t.height);
1851                  
1852                  // create MediaElementShim

1853                  meOptions.pluginWidth = t.height;
1854                  meOptions.pluginHeight = t.width;                
1855              }
1856              
1857              
1858  
1859              // create MediaElement shim

1860              mejs.MediaElement(t.$media[0], meOptions);
1861          },
1862          
1863          showControls: function(doAnimation) {
1864              var t = this;
1865              
1866              doAnimation = typeof doAnimation == 'undefined' || doAnimation;
1867              
1868              if (t.controlsAreVisible)
1869                  return;
1870              
1871              if (doAnimation) {
1872                  t.controls
1873                      .css('visibility','visible')
1874                      .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});    
1875      
1876                  // any additional controls people might add and want to hide

1877                  t.container.find('.mejs-control')
1878                      .css('visibility','visible')
1879                      .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});    
1880                      
1881              } else {
1882                  t.controls
1883                      .css('visibility','visible')
1884                      .css('display','block');
1885      
1886                  // any additional controls people might add and want to hide

1887                  t.container.find('.mejs-control')
1888                      .css('visibility','visible')
1889                      .css('display','block');
1890                      
1891                  t.controlsAreVisible = true;
1892              }
1893              
1894              t.setControlsSize();
1895              
1896          },
1897  
1898          hideControls: function(doAnimation) {
1899              var t = this;
1900              
1901              doAnimation = typeof doAnimation == 'undefined' || doAnimation;
1902              
1903              if (!t.controlsAreVisible)
1904                  return;
1905              
1906              if (doAnimation) {
1907                  // fade out main controls

1908                  t.controls.stop(true, true).fadeOut(200, function() {
1909                      $(this)
1910                          .css('visibility','hidden')
1911                          .css('display','block');
1912                          
1913                      t.controlsAreVisible = false;
1914                  });    
1915      
1916                  // any additional controls people might add and want to hide

1917                  t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
1918                      $(this)
1919                          .css('visibility','hidden')
1920                          .css('display','block');
1921                  });    
1922              } else {
1923                  
1924                  // hide main controls

1925                  t.controls
1926                      .css('visibility','hidden')
1927                      .css('display','block');        
1928                  
1929                  // hide others

1930                  t.container.find('.mejs-control')
1931                      .css('visibility','hidden')
1932                      .css('display','block');
1933                      
1934                  t.controlsAreVisible = false;
1935              }
1936          },        
1937  
1938          controlsTimer: null,
1939  
1940          startControlsTimer: function(timeout) {
1941  
1942              var t = this;
1943              
1944              timeout = typeof timeout != 'undefined' ? timeout : 1500;
1945  
1946              t.killControlsTimer('start');
1947  
1948              t.controlsTimer = setTimeout(function() {
1949                  //console.log('timer fired');

1950                  t.hideControls();
1951                  t.killControlsTimer('hide');
1952              }, timeout);
1953          },
1954  
1955          killControlsTimer: function(src) {
1956  
1957              var t = this;
1958  
1959              if (t.controlsTimer !== null) {
1960                  clearTimeout(t.controlsTimer);
1961                  delete t.controlsTimer;
1962                  t.controlsTimer = null;
1963              }
1964          },        
1965          
1966          controlsEnabled: true,
1967          
1968          disableControls: function() {
1969              var t= this;
1970              
1971              t.killControlsTimer();
1972              t.hideControls(false);
1973              this.controlsEnabled = false;
1974          },
1975          
1976          enableControls: function() {
1977              var t= this;
1978              
1979              t.showControls(false);
1980              
1981              t.controlsEnabled = true;
1982          },        
1983          
1984  
1985          // Sets up all controls and events

1986          meReady: function(media, domNode) {            
1987          
1988          
1989              var t = this,
1990                  mf = mejs.MediaFeatures,
1991                  autoplayAttr = domNode.getAttribute('autoplay'),
1992                  autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
1993                  featureIndex,
1994                  feature;
1995  
1996              // make sure it can't create itself again if a plugin reloads

1997              if (t.created)
1998                  return;
1999              else
2000                  t.created = true;            
2001  
2002              t.media = media;
2003              t.domNode = domNode;
2004              
2005              if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {                
2006                  
2007                  // two built in features

2008                  t.buildposter(t, t.controls, t.layers, t.media);
2009                  t.buildkeyboard(t, t.controls, t.layers, t.media);
2010                  t.buildoverlays(t, t.controls, t.layers, t.media);
2011  
2012                  // grab for use by features

2013                  t.findTracks();
2014  
2015                  // add user-defined features/controls

2016                  for (featureIndex in t.options.features) {
2017                      feature = t.options.features[featureIndex];
2018                      if (t['build' + feature]) {
2019                          try {
2020                              t['build' + feature](t, t.controls, t.layers, t.media);
2021                          } catch (e) {
2022                              // TODO: report control error

2023                              //throw e;

2024                              //console.log('error building ' + feature);

2025                              //console.log(e);

2026                          }
2027                      }
2028                  }
2029  
2030                  t.container.trigger('controlsready');
2031                  
2032                  // reset all layers and controls

2033                  t.setPlayerSize(t.width, t.height);
2034                  t.setControlsSize();
2035                  
2036  
2037                  // controls fade

2038                  if (t.isVideo) {
2039                  
2040                      if (mejs.MediaFeatures.hasTouch) {
2041                          
2042                          // for touch devices (iOS, Android)

2043                          // show/hide without animation on touch

2044                          
2045                          t.$media.bind('touchstart', function() {
2046                              
2047                              
2048                              // toggle controls

2049                              if (t.controlsAreVisible) {
2050                                  t.hideControls(false);
2051                              } else {
2052                                  if (t.controlsEnabled) {
2053                                      t.showControls(false);
2054                                  }
2055                              }
2056                          });                    
2057                      
2058                      } else {
2059                          // click controls

2060                          var clickElement = (t.media.pluginType == 'native') ? t.$media : $(t.media.pluginElement);
2061                          
2062                          // click to play/pause

2063                          clickElement.click(function() {
2064                              if (media.paused) {
2065                                  media.play();
2066                              } else {
2067                                  media.pause();
2068                              }
2069                          });
2070                          
2071                      
2072                          // show/hide controls

2073                          t.container
2074                              .bind('mouseenter mouseover', function () {
2075                                  if (t.controlsEnabled) {
2076                                      if (!t.options.alwaysShowControls) {                                
2077                                          t.killControlsTimer('enter');
2078                                          t.showControls();
2079                                          t.startControlsTimer(2500);        
2080                                      }
2081                                  }
2082                              })
2083                              .bind('mousemove', function() {
2084                                  if (t.controlsEnabled) {
2085                                      if (!t.controlsAreVisible) {
2086                                          t.showControls();
2087                                      }
2088                                      //t.killControlsTimer('move');

2089                                      if (!t.options.alwaysShowControls) {
2090                                          t.startControlsTimer(2500);
2091                                      }
2092                                  }
2093                              })
2094                              .bind('mouseleave', function () {
2095                                  if (t.controlsEnabled) {
2096                                      if (!t.media.paused && !t.options.alwaysShowControls) {
2097                                          t.startControlsTimer(1000);                                
2098                                      }
2099                                  }
2100                              });
2101                      }
2102                      
2103                      // check for autoplay

2104                      if (autoplay && !t.options.alwaysShowControls) {
2105                          t.hideControls();
2106                      }
2107  
2108                      // resizer

2109                      if (t.options.enableAutosize) {
2110                          t.media.addEventListener('loadedmetadata', function(e) {
2111                              // if the <video height> was not set and the options.videoHeight was not set

2112                              // then resize to the real dimensions

2113                              if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
2114                                  t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
2115                                  t.setControlsSize();
2116                                  t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
2117                              }
2118                          }, false);
2119                      }
2120                  }
2121                  
2122                  // EVENTS

2123  
2124                  // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)

2125                  media.addEventListener('play', function() {
2126                          
2127                          // go through all other players

2128                          for (var i=0, il=mejs.players.length; i<il; i++) {
2129                              var p = mejs.players[i];
2130                              if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
2131                                  p.pause();
2132                              }
2133                              p.hasFocus = false;
2134                          }
2135                          
2136                          t.hasFocus = true;
2137                  },false);
2138                                  
2139  
2140                  // ended for all

2141                  t.media.addEventListener('ended', function (e) {
2142                      try{
2143                          t.media.setCurrentTime(0);
2144                      } catch (exp) {
2145                          
2146                      }
2147                      t.media.pause();
2148                      
2149                      if (t.setProgressRail)
2150                          t.setProgressRail();
2151                      if (t.setCurrentRail)
2152                          t.setCurrentRail();                        
2153  
2154                      if (t.options.loop) {
2155                          t.media.play();
2156                      } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
2157                          t.showControls();
2158                      }
2159                  }, false);
2160                  
2161                  // resize on the first play

2162                  t.media.addEventListener('loadedmetadata', function(e) {
2163                      if (t.updateDuration) {
2164                          t.updateDuration();
2165                      }
2166                      if (t.updateCurrent) {
2167                          t.updateCurrent();
2168                      }
2169                      
2170                      if (!t.isFullScreen) {
2171                          t.setPlayerSize(t.width, t.height);
2172                          t.setControlsSize();
2173                      }
2174                  }, false);
2175  
2176  
2177                  // webkit has trouble doing this without a delay

2178                  setTimeout(function () {
2179                      t.setPlayerSize(t.width, t.height);
2180                      t.setControlsSize();
2181                  }, 50);
2182                  
2183                  // adjust controls whenever window sizes (used to be in fullscreen only)

2184                  $(window).resize(function() {
2185                      
2186                      // don't resize for fullscreen mode                

2187                      if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
2188                          t.setPlayerSize(t.width, t.height);
2189                      }
2190                      
2191                      // always adjust controls

2192                      t.setControlsSize();
2193                  });                
2194  
2195                  // TEMP: needs to be moved somewhere else

2196                  if (t.media.pluginType == 'youtube') {
2197                      t.container.find('.mejs-overlay-play').hide();    
2198                  }
2199              }
2200              
2201              // force autoplay for HTML5

2202              if (autoplay && media.pluginType == 'native') {
2203                  media.load();
2204                  media.play();
2205              }
2206  
2207  
2208              if (t.options.success) {
2209                  
2210                  if (typeof t.options.success == 'string') {
2211                          window[t.options.success](t.media, t.domNode, t);
2212                  } else {
2213                          t.options.success(t.media, t.domNode, t);
2214                  }
2215              }
2216          },
2217  
2218          handleError: function(e) {
2219              var t = this;
2220              
2221              t.controls.hide();
2222          
2223              // Tell user that the file cannot be played

2224              if (t.options.error) {
2225                  t.options.error(e);
2226              }
2227          },
2228  
2229          setPlayerSize: function(width,height) {
2230              var t = this;
2231  
2232              if (typeof width != 'undefined')
2233                  t.width = width;
2234                  
2235              if (typeof height != 'undefined')
2236                  t.height = height;
2237  
2238              // detect 100% mode

2239              if (t.height.toString().indexOf('%') > 0) {
2240              
2241                  // do we have the native dimensions yet?

2242                  var 
2243                      nativeWidth = (t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth,
2244                      nativeHeight = (t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight,
2245                      parentWidth = t.container.parent().width(),
2246                      newHeight = parseInt(parentWidth * nativeHeight/nativeWidth, 10);
2247                      
2248                  if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
2249                      parentWidth = $(window).width();
2250                      newHeight = $(window).height();
2251                  }
2252                      
2253                  
2254                  // set outer container size

2255                  t.container
2256                      .width(parentWidth)
2257                      .height(newHeight);
2258                      
2259                  // set native <video>

2260                  t.$media
2261                      .width('100%')
2262                      .height('100%');
2263                      
2264                  // set shims

2265                  t.container.find('object, embed, iframe')
2266                      .width('100%')
2267                      .height('100%');
2268                      
2269                  // if shim is ready, send the size to the embeded plugin    

2270                  if (t.media.setVideoSize)
2271                      t.media.setVideoSize(parentWidth, newHeight);
2272                      
2273                  // set the layers

2274                  t.layers.children('.mejs-layer')
2275                      .width('100%')
2276                      .height('100%');                    
2277              
2278              
2279              } else {
2280  
2281                  t.container
2282                      .width(t.width)
2283                      .height(t.height);
2284      
2285                  t.layers.children('.mejs-layer')
2286                      .width(t.width)
2287                      .height(t.height);
2288                      
2289              }
2290          },
2291  
2292          setControlsSize: function() {
2293              var t = this,
2294                  usedWidth = 0,
2295                  railWidth = 0,
2296                  rail = t.controls.find('.mejs-time-rail'),
2297                  total = t.controls.find('.mejs-time-total'),
2298                  current = t.controls.find('.mejs-time-current'),
2299                  loaded = t.controls.find('.mejs-time-loaded'),
2300                  others = rail.siblings();
2301              
2302  
2303              // allow the size to come from custom CSS

2304              if (t.options && !t.options.autosizeProgress) {
2305                  // Also, frontends devs can be more flexible 

2306                  // due the opportunity of absolute positioning.

2307                  railWidth = parseInt(rail.css('width'));
2308              }
2309              
2310              // attempt to autosize

2311              if (railWidth === 0 || !railWidth) {
2312                  
2313                  // find the size of all the other controls besides the rail

2314                  others.each(function() {
2315                      if ($(this).css('position') != 'absolute') {
2316                          usedWidth += $(this).outerWidth(true);
2317                      }
2318                  });
2319                  
2320                  // fit the rail into the remaining space

2321                  railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
2322              }
2323  
2324              // outer area

2325              rail.width(railWidth);
2326              // dark space

2327              total.width(railWidth - (total.outerWidth(true) - total.width()));
2328              
2329              if (t.setProgressRail)
2330                  t.setProgressRail();
2331              if (t.setCurrentRail)
2332                  t.setCurrentRail();                
2333          },
2334  
2335  
2336          buildposter: function(player, controls, layers, media) {
2337              var t = this,
2338                  poster = 
2339                  $('<div class="mejs-poster mejs-layer">' +
2340                  '</div>')
2341                      .appendTo(layers),
2342                  posterUrl = player.$media.attr('poster');
2343  
2344              // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)

2345              if (player.options.poster !== '') {
2346                  posterUrl = player.options.poster;
2347              }    
2348                  
2349              // second, try the real poster

2350              if (posterUrl !== '' && posterUrl != null) {
2351                  t.setPoster(posterUrl);
2352              } else {
2353                  poster.hide();
2354              }
2355  
2356              media.addEventListener('play',function() {
2357                  poster.hide();
2358              }, false);
2359          },
2360          
2361          setPoster: function(url) {
2362              var t = this,
2363                  posterDiv = t.container.find('.mejs-poster'),
2364                  posterImg = posterDiv.find('img');
2365                  
2366              if (posterImg.length == 0) {
2367                  posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
2368              }    
2369              
2370              posterImg.attr('src', url);
2371          },
2372  
2373          buildoverlays: function(player, controls, layers, media) {
2374              if (!player.isVideo)
2375                  return;
2376  
2377              var 
2378              loading = 
2379                  $('<div class="mejs-overlay mejs-layer">'+
2380                      '<div class="mejs-overlay-loading"><span></span></div>'+
2381                  '</div>')
2382                  .hide() // start out hidden
2383                  .appendTo(layers),
2384              error = 
2385                  $('<div class="mejs-overlay mejs-layer">'+
2386                      '<div class="mejs-overlay-error"></div>'+
2387                  '</div>')
2388                  .hide() // start out hidden
2389                  .appendTo(layers),
2390              // this needs to come last so it's on top

2391              bigPlay = 
2392                  $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
2393                      '<div class="mejs-overlay-button"></div>'+
2394                  '</div>')
2395                  .appendTo(layers)
2396                  .click(function() {
2397                      if (media.paused) {
2398                          media.play();
2399                      } else {
2400                          media.pause();
2401                      }
2402                  });
2403              
2404              /*

2405              if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {

2406                  bigPlay.remove();

2407                  loading.remove();

2408              }

2409              */
2410      
2411  
2412              // show/hide big play button

2413              media.addEventListener('play',function() {
2414                  bigPlay.hide();
2415                  loading.hide();
2416                  controls.find('.mejs-time-buffering').hide();
2417                  error.hide();
2418              }, false);    
2419              
2420              media.addEventListener('playing', function() {
2421                  bigPlay.hide();
2422                  loading.hide();
2423                  controls.find('.mejs-time-buffering').hide();
2424                  error.hide();            
2425              }, false);
2426  
2427              media.addEventListener('seeking', function() {
2428                  loading.show();
2429                  controls.find('.mejs-time-buffering').show();
2430              }, false);
2431  
2432              media.addEventListener('seeked', function() {
2433                  loading.hide();
2434                  controls.find('.mejs-time-buffering').hide();
2435              }, false);
2436      
2437              media.addEventListener('pause',function() {
2438                  if (!mejs.MediaFeatures.isiPhone) {
2439                      bigPlay.show();
2440                  }
2441              }, false);
2442              
2443              media.addEventListener('waiting', function() {
2444                  loading.show();    
2445                  controls.find('.mejs-time-buffering').show();
2446              }, false);            
2447              
2448              
2449              // show/hide loading            

2450              media.addEventListener('loadeddata',function() {
2451                  // for some reason Chrome is firing this event

2452                  //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')

2453                  //    return;

2454                      
2455                  loading.show();
2456                  controls.find('.mejs-time-buffering').show();
2457              }, false);    
2458              media.addEventListener('canplay',function() {
2459                  loading.hide();
2460                  controls.find('.mejs-time-buffering').hide();
2461              }, false);    
2462  
2463              // error handling

2464              media.addEventListener('error',function() {
2465                  loading.hide();
2466                  controls.find('.mejs-time-buffering').hide();
2467                  error.show();
2468                  error.find('mejs-overlay-error').html("Error loading this resource");
2469              }, false);                
2470          },
2471          
2472          buildkeyboard: function(player, controls, layers, media) {
2473  
2474                  var t = this;
2475                  
2476                  // listen for key presses

2477                  $(document).keydown(function(e) {
2478                          
2479                          if (player.hasFocus && player.options.enableKeyboard) {
2480                                          
2481                                  // find a matching key

2482                                  for (var i=0, il=player.options.keyActions.length; i<il; i++) {
2483                                          var keyAction = player.options.keyActions[i];
2484                                          
2485                                          for (var j=0, jl=keyAction.keys.length; j<jl; j++) {
2486                                                  if (e.keyCode == keyAction.keys[j]) {
2487                                                          e.preventDefault();
2488                                                          keyAction.action(player, media);
2489                                                          return false;
2490                                                  }                                                
2491                                          }
2492                                  }
2493                          }
2494                          
2495                          return true;
2496                  });
2497                  
2498                  // check if someone clicked outside a player region, then kill its focus

2499                  $(document).click(function(event) {
2500                          if ($(event.target).closest('.mejs-container').length == 0) {
2501                                  player.hasFocus = false;
2502                          }
2503                  });
2504              
2505          },
2506  
2507          findTracks: function() {
2508              var t = this,
2509                  tracktags = t.$media.find('track');
2510  
2511              // store for use by plugins

2512              t.tracks = [];
2513              tracktags.each(function(index, track) {
2514                  
2515                  track = $(track);
2516                  
2517                  t.tracks.push({
2518                      srclang: track.attr('srclang').toLowerCase(),
2519                      src: track.attr('src'),
2520                      kind: track.attr('kind'),
2521                      label: track.attr('label') || '',
2522                      entries: [],
2523                      isLoaded: false
2524                  });
2525              });
2526          },
2527          changeSkin: function(className) {
2528              this.container[0].className = 'mejs-container ' + className;
2529              this.setPlayerSize(this.width, this.height);
2530              this.setControlsSize();
2531          },
2532          play: function() {
2533              this.media.play();
2534          },
2535          pause: function() {
2536              this.media.pause();
2537          },
2538          load: function() {
2539              this.media.load();
2540          },
2541          setMuted: function(muted) {
2542              this.media.setMuted(muted);
2543          },
2544          setCurrentTime: function(time) {
2545              this.media.setCurrentTime(time);
2546          },
2547          getCurrentTime: function() {
2548              return this.media.currentTime;
2549          },
2550          setVolume: function(volume) {
2551              this.media.setVolume(volume);
2552          },
2553          getVolume: function() {
2554              return this.media.volume;
2555          },
2556          setSrc: function(src) {
2557              this.media.setSrc(src);
2558          },
2559          remove: function() {
2560              var t = this;
2561              
2562              if (t.media.pluginType == 'flash') {
2563                  t.media.remove();
2564              } else if (t.media.pluginType == 'native') {
2565                  t.media.prop('controls', true);
2566              }
2567              
2568              // grab video and put it back in place

2569              if (!t.isDynamic) {
2570                  t.$node.insertBefore(t.container)
2571              }
2572              
2573              t.container.remove();
2574          }
2575      };
2576  
2577      // turn into jQuery plugin

2578      if (typeof jQuery != 'undefined') {
2579          jQuery.fn.mediaelementplayer = function (options) {
2580              return this.each(function () {
2581                  new mejs.MediaElementPlayer(this, options);
2582              });
2583          };
2584      }
2585      
2586      $(document).ready(function() {
2587          // auto enable using JSON attribute

2588          $('.mejs-player').mediaelementplayer();
2589      });
2590      
2591      // push out to window

2592      window.MediaElementPlayer = mejs.MediaElementPlayer;
2593  
2594  })(mejs.$);
2595  
2596  (function($) {
2597  
2598      $.extend(mejs.MepDefaults, {
2599          playpauseText: 'Play/Pause'
2600      });
2601  
2602      // PLAY/pause BUTTON
2603      $.extend(MediaElementPlayer.prototype, {
2604          buildplaypause: function(player, controls, layers, media) {
2605              var 
2606                  t = this,
2607                  play = 
2608                  $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
2609                      '<button type="button" aria-controls="' + t.id + '" title="' + t.options.playpauseText + '"></button>' +
2610                  '</div>')
2611                  .appendTo(controls)
2612                  .click(function(e) {
2613                      e.preventDefault();
2614                  
2615                      if (media.paused) {
2616                          media.play();
2617                      } else {
2618                          media.pause();
2619                      }
2620                      
2621                      return false;
2622                  });
2623  
2624              media.addEventListener('play',function() {
2625                  play.removeClass('mejs-play').addClass('mejs-pause');
2626              }, false);
2627              media.addEventListener('playing',function() {
2628                  play.removeClass('mejs-play').addClass('mejs-pause');
2629              }, false);
2630  
2631  
2632              media.addEventListener('pause',function() {
2633                  play.removeClass('mejs-pause').addClass('mejs-play');
2634              }, false);
2635              media.addEventListener('paused',function() {
2636                  play.removeClass('mejs-pause').addClass('mejs-play');
2637              }, false);
2638          }
2639      });
2640      
2641  })(mejs.$);
2642  (function($) {
2643  
2644      $.extend(mejs.MepDefaults, {
2645          stopText: 'Stop'
2646      });
2647  
2648      // STOP BUTTON
2649      $.extend(MediaElementPlayer.prototype, {
2650          buildstop: function(player, controls, layers, media) {
2651              var t = this,
2652                  stop = 
2653                  $('<div class="mejs-button mejs-stop-button mejs-stop">' +
2654                      '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '"></button>' +
2655                  '</div>')
2656                  .appendTo(controls)
2657                  .click(function() {
2658                      if (!media.paused) {
2659                          media.pause();
2660                      }
2661                      if (media.currentTime > 0) {
2662                          media.setCurrentTime(0);    
2663                          controls.find('.mejs-time-current').width('0px');
2664                          controls.find('.mejs-time-handle').css('left', '0px');
2665                          controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
2666                          controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );                    
2667                          layers.find('.mejs-poster').show();
2668                      }
2669                  });
2670          }
2671      });
2672      
2673  })(mejs.$);
2674  (function($) {
2675      // progress/loaded bar
2676      $.extend(MediaElementPlayer.prototype, {
2677          buildprogress: function(player, controls, layers, media) {
2678  
2679              $('<div class="mejs-time-rail">'+
2680                  '<span class="mejs-time-total">'+
2681                      '<span class="mejs-time-buffering"></span>'+
2682                      '<span class="mejs-time-loaded"></span>'+
2683                      '<span class="mejs-time-current"></span>'+
2684                      '<span class="mejs-time-handle"></span>'+
2685                      '<span class="mejs-time-float">' + 
2686                          '<span class="mejs-time-float-current">00:00</span>' + 
2687                          '<span class="mejs-time-float-corner"></span>' + 
2688                      '</span>'+
2689                  '</span>'+
2690              '</div>')
2691                  .appendTo(controls);
2692                  controls.find('.mejs-time-buffering').hide();
2693  
2694              var 
2695                  t = this,
2696                  total = controls.find('.mejs-time-total'),
2697                  loaded  = controls.find('.mejs-time-loaded'),
2698                  current  = controls.find('.mejs-time-current'),
2699                  handle  = controls.find('.mejs-time-handle'),
2700                  timefloat  = controls.find('.mejs-time-float'),
2701                  timefloatcurrent  = controls.find('.mejs-time-float-current'),
2702                  handleMouseMove = function (e) {
2703                      // mouse position relative to the object
2704                      var x = e.pageX,
2705                          offset = total.offset(),
2706                          width = total.outerWidth(),
2707                          percentage = 0,
2708                          newTime = 0,
2709                          pos = x - offset.left;
2710  
2711  
2712                      if (x > offset.left && x <= width + offset.left && media.duration) {
2713                          percentage = ((x - offset.left) / width);
2714                          newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
2715  
2716                          // seek to where the mouse is
2717                          if (mouseIsDown) {
2718                              media.setCurrentTime(newTime);
2719                          }
2720  
2721                          // position floating time box
2722                          if (!mejs.MediaFeatures.hasTouch) {
2723                                  timefloat.css('left', pos);
2724                                  timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
2725                                  timefloat.show();
2726                          }
2727                      }
2728                  },
2729                  mouseIsDown = false,
2730                  mouseIsOver = false;
2731  
2732              // handle clicks
2733              //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
2734              total
2735                  .bind('mousedown', function (e) {
2736                      // only handle left clicks
2737                      if (e.which === 1) {
2738                          mouseIsDown = true;
2739                          handleMouseMove(e);
2740                          $(document)
2741                              .bind('mousemove.dur', function(e) {
2742                                  handleMouseMove(e);
2743                              })
2744                              .bind('mouseup.dur', function (e) {
2745                                  mouseIsDown = false;
2746                                  timefloat.hide();
2747                                  $(document).unbind('.dur');
2748                              });
2749                          return false;
2750                      }
2751                  })
2752                  .bind('mouseenter', function(e) {
2753                      mouseIsOver = true;
2754                      $(document).bind('mousemove.dur', function(e) {
2755                          handleMouseMove(e);
2756                      });
2757                      if (!mejs.MediaFeatures.hasTouch) {
2758                          timefloat.show();
2759                      }
2760                  })
2761                  .bind('mouseleave',function(e) {
2762                      mouseIsOver = false;
2763                      if (!mouseIsDown) {
2764                          $(document).unbind('.dur');
2765                          timefloat.hide();
2766                      }
2767                  });
2768  
2769              // loading
2770              media.addEventListener('progress', function (e) {
2771                  player.setProgressRail(e);
2772                  player.setCurrentRail(e);
2773              }, false);
2774  
2775              // current time
2776              media.addEventListener('timeupdate', function(e) {
2777                  player.setProgressRail(e);
2778                  player.setCurrentRail(e);
2779              }, false);
2780              
2781              
2782              // store for later use
2783              t.loaded = loaded;
2784              t.total = total;
2785              t.current = current;
2786              t.handle = handle;
2787          },
2788          setProgressRail: function(e) {
2789  
2790              var
2791                  t = this,
2792                  target = (e != undefined) ? e.target : t.media,
2793                  percent = null;            
2794  
2795              // newest HTML5 spec has buffered array (FF4, Webkit)
2796              if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
2797                  // TODO: account for a real array with multiple values (only Firefox 4 has this so far) 
2798                  percent = target.buffered.end(0) / target.duration;
2799              } 
2800              // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
2801              // to be anything other than 0. If the byte count is available we use this instead.
2802              // Browsers that support the else if do not seem to have the bufferedBytes value and
2803              // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
2804              else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
2805                  percent = target.bufferedBytes / target.bytesTotal;
2806              }
2807              // Firefox 3 with an Ogg file seems to go this way
2808              else if (e && e.lengthComputable && e.total != 0) {
2809                  percent = e.loaded/e.total;
2810              }
2811  
2812              // finally update the progress bar
2813              if (percent !== null) {
2814                  percent = Math.min(1, Math.max(0, percent));
2815                  // update loaded bar
2816                  if (t.loaded && t.total) {
2817                      t.loaded.width(t.total.width() * percent);
2818                  }
2819              }
2820          },
2821          setCurrentRail: function() {
2822  
2823              var t = this;
2824          
2825              if (t.media.currentTime != undefined && t.media.duration) {
2826  
2827                  // update bar and handle
2828                  if (t.total && t.handle) {
2829                      var 
2830                          newWidth = t.total.width() * t.media.currentTime / t.media.duration,
2831                          handlePos = newWidth - (t.handle.outerWidth(true) / 2);
2832  
2833                      t.current.width(newWidth);
2834                      t.handle.css('left', handlePos);
2835                  }
2836              }
2837  
2838          }    
2839      });
2840  })(mejs.$);
2841  (function($) {
2842      
2843      // options

2844      $.extend(mejs.MepDefaults, {
2845          duration: -1,
2846          timeAndDurationSeparator: ' <span> | </span> '
2847      });
2848  
2849  
2850      // current and duration 00:00 / 00:00

2851      $.extend(MediaElementPlayer.prototype, {
2852          buildcurrent: function(player, controls, layers, media) {
2853              var t = this;
2854              
2855              $('<div class="mejs-time">'+
2856                      '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '')
2857                      + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ '</span>'+
2858                      '</div>')
2859                      .appendTo(controls);
2860              
2861              t.currenttime = t.controls.find('.mejs-currenttime');
2862  
2863              media.addEventListener('timeupdate',function() {
2864                  player.updateCurrent();
2865              }, false);
2866          },
2867  
2868  
2869          buildduration: function(player, controls, layers, media) {
2870              var t = this;
2871              
2872              if (controls.children().last().find('.mejs-currenttime').length > 0) {
2873                  $(t.options.timeAndDurationSeparator +
2874                      '<span class="mejs-duration">' + 
2875                          (t.options.duration > 0 ? 
2876                              mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount,  t.options.framesPerSecond || 25) :
2877                                 ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')) 
2878                             ) + 
2879                      '</span>')
2880                      .appendTo(controls.find('.mejs-time'));
2881              } else {
2882  
2883                  // add class to current time

2884                  controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
2885                  
2886                  $('<div class="mejs-time mejs-duration-container">'+
2887                      '<span class="mejs-duration">' + 
2888                          (t.options.duration > 0 ? 
2889                              mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount,  t.options.framesPerSecond || 25) :
2890                                 ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')) 
2891                             ) + 
2892                      '</span>' +
2893                  '</div>')
2894                  .appendTo(controls);
2895              }
2896              
2897              t.durationD = t.controls.find('.mejs-duration');
2898  
2899              media.addEventListener('timeupdate',function() {
2900                  player.updateDuration();
2901              }, false);
2902          },
2903          
2904          updateCurrent:  function() {
2905              var t = this;
2906  
2907              if (t.currenttime) {
2908                  t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount,  t.options.framesPerSecond || 25));
2909              }
2910          },
2911          
2912          updateDuration: function() {    
2913              var t = this;
2914              
2915              if (t.media.duration && t.durationD) {
2916                  t.durationD.html(mejs.Utility.secondsToTimeCode(t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
2917              }        
2918          }
2919      });
2920  
2921  })(mejs.$);
2922  (function($) {
2923  
2924      $.extend(mejs.MepDefaults, {
2925          muteText: 'Mute Toggle',
2926          hideVolumeOnTouchDevices: true,
2927          
2928          audioVolume: 'horizontal',
2929          videoVolume: 'vertical'
2930      });
2931  
2932      $.extend(MediaElementPlayer.prototype, {
2933          buildvolume: function(player, controls, layers, media) {
2934                  
2935              // Android and iOS don't support volume controls
2936              if (mejs.MediaFeatures.hasTouch && this.options.hideVolumeOnTouchDevices)
2937                  return;
2938              
2939              var t = this,
2940                  mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
2941                  mute = (mode == 'horizontal') ?
2942                  
2943                  // horizontal version
2944                  $('<div class="mejs-button mejs-volume-button mejs-mute">'+
2945                      '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '"></button>'+
2946                  '</div>' +
2947                  '<div class="mejs-horizontal-volume-slider">'+ // outer background
2948                      '<div class="mejs-horizontal-volume-total"></div>'+ // line background
2949                      '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
2950                      '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
2951                  '</div>'
2952                  )
2953                      .appendTo(controls) :
2954                  
2955                  // vertical version
2956                  $('<div class="mejs-button mejs-volume-button mejs-mute">'+
2957                      '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '"></button>'+
2958                      '<div class="mejs-volume-slider">'+ // outer background
2959                          '<div class="mejs-volume-total"></div>'+ // line background
2960                          '<div class="mejs-volume-current"></div>'+ // current volume
2961                          '<div class="mejs-volume-handle"></div>'+ // handle
2962                      '</div>'+
2963                  '</div>')
2964                      .appendTo(controls),
2965              volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
2966              volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
2967              volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
2968              volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
2969  
2970              positionVolumeHandle = function(volume, secondTry) {
2971  
2972                  if (!volumeSlider.is(':visible') && typeof secondTry != 'undefined') {
2973                      volumeSlider.show();
2974                      positionVolumeHandle(volume, true);
2975                      volumeSlider.hide()
2976                      return;
2977                  }
2978              
2979                  // correct to 0-1
2980                  volume = Math.max(0,volume);
2981                  volume = Math.min(volume,1);                    
2982                  
2983                  // ajust mute button style
2984                  if (volume == 0) {
2985                      mute.removeClass('mejs-mute').addClass('mejs-unmute');
2986                  } else {
2987                      mute.removeClass('mejs-unmute').addClass('mejs-mute');
2988                  }                
2989  
2990                  // position slider 
2991                  if (mode == 'vertical') {
2992                      var 
2993                      
2994                          // height of the full size volume slider background
2995                          totalHeight = volumeTotal.height(),
2996                          
2997                          // top/left of full size volume slider background
2998                          totalPosition = volumeTotal.position(),
2999                          
3000                          // the new top position based on the current volume
3001                          // 70% volume on 100px height == top:30px
3002                          newTop = totalHeight - (totalHeight * volume);
3003      
3004                      // handle
3005                      volumeHandle.css('top', totalPosition.top + newTop - (volumeHandle.height() / 2));
3006      
3007                      // show the current visibility
3008                      volumeCurrent.height(totalHeight - newTop );
3009                      volumeCurrent.css('top', totalPosition.top + newTop);
3010                  } else {
3011                      var 
3012                      
3013                          // height of the full size volume slider background
3014                          totalWidth = volumeTotal.width(),
3015                          
3016                          // top/left of full size volume slider background
3017                          totalPosition = volumeTotal.position(),
3018                          
3019                          // the new left position based on the current volume
3020                          newLeft = totalWidth * volume;
3021      
3022                      // handle
3023                      volumeHandle.css('left', totalPosition.left + newLeft - (volumeHandle.width() / 2));
3024      
3025                      // rezize the current part of the volume bar
3026                      volumeCurrent.width( newLeft );
3027                  }
3028              },
3029              handleVolumeMove = function(e) {
3030                  
3031                  var volume = null,
3032                      totalOffset = volumeTotal.offset();
3033                  
3034                  // calculate the new volume based on the moust position
3035                  if (mode == 'vertical') {
3036                  
3037                      var
3038                          railHeight = volumeTotal.height(),
3039                          totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
3040                          newY = e.pageY - totalOffset.top;
3041                          
3042                      volume = (railHeight - newY) / railHeight;
3043                          
3044                      // the controls just hide themselves (usually when mouse moves too far up)
3045                      if (totalOffset.top == 0 || totalOffset.left == 0)
3046                          return;
3047                      
3048                  } else {
3049                      var
3050                          railWidth = volumeTotal.width(),
3051                          newX = e.pageX - totalOffset.left;
3052                          
3053                      volume = newX / railWidth;
3054                  }
3055                  
3056                  // ensure the volume isn't outside 0-1
3057                  volume = Math.max(0,volume);
3058                  volume = Math.min(volume,1);
3059                  
3060                  // position the slider and handle            
3061                  positionVolumeHandle(volume);
3062                  
3063                  // set the media object (this will trigger the volumechanged event)
3064                  if (volume == 0) {
3065                      media.setMuted(true);
3066                  } else {
3067                      media.setMuted(false);
3068                  }
3069                  media.setVolume(volume);            
3070              },
3071              mouseIsDown = false,
3072              mouseIsOver = false;
3073  
3074              // SLIDER
3075              
3076              mute
3077                  .hover(function() {
3078                      volumeSlider.show();
3079                      mouseIsOver = true;
3080                  }, function() {
3081                      mouseIsOver = false;    
3082                          
3083                      if (!mouseIsDown && mode == 'vertical')    {
3084                          volumeSlider.hide();
3085                      }
3086                  });
3087              
3088              volumeSlider
3089                  .bind('mouseover', function() {
3090                      mouseIsOver = true;    
3091                  })
3092                  .bind('mousedown', function (e) {
3093                      handleVolumeMove(e);
3094                      $(document)
3095                          .bind('mousemove.vol', function(e) {
3096                              handleVolumeMove(e);
3097                          })
3098                          .bind('mouseup.vol', function () {
3099                              mouseIsDown = false;
3100                              $(document).unbind('.vol');
3101  
3102                              if (!mouseIsOver && mode == 'vertical') {
3103                                  volumeSlider.hide();
3104                              }
3105                          });
3106                      mouseIsDown = true;
3107                          
3108                      return false;
3109                  });
3110  
3111  
3112              // MUTE button
3113              mute.find('button').click(function() {
3114                  media.setMuted( !media.muted );
3115              });
3116  
3117              // listen for volume change events from other sources
3118              media.addEventListener('volumechange', function(e) {
3119                  if (!mouseIsDown) {
3120                      if (media.muted) {
3121                          positionVolumeHandle(0);
3122                          mute.removeClass('mejs-mute').addClass('mejs-unmute');
3123                      } else {
3124                          positionVolumeHandle(media.volume);
3125                          mute.removeClass('mejs-unmute').addClass('mejs-mute');
3126                      }
3127                  }
3128              }, false);
3129  
3130              if (t.container.is(':visible')) {
3131                  // set initial volume
3132                  positionVolumeHandle(player.options.startVolume);
3133                  
3134                  // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
3135                  if (media.pluginType === 'native') {
3136                      media.setVolume(player.options.startVolume);
3137                  }
3138              }
3139          }
3140      });
3141      
3142  })(mejs.$);
3143  
3144  (function($) {
3145      
3146      $.extend(mejs.MepDefaults, {
3147          usePluginFullScreen: true,
3148          newWindowCallback: function() { return '';},
3149          fullscreenText: 'Fullscreen'
3150      });
3151      
3152      $.extend(MediaElementPlayer.prototype, {
3153          
3154          isFullScreen: false,
3155          
3156          isNativeFullScreen: false,
3157          
3158          docStyleOverflow: null,
3159          
3160          isInIframe: false,
3161          
3162          buildfullscreen: function(player, controls, layers, media) {
3163  
3164              if (!player.isVideo)
3165                  return;
3166                  
3167              player.isInIframe = (window.location != window.parent.location);
3168                  
3169              // native events

3170              if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
3171                  
3172                  // chrome doesn't alays fire this in an iframe

3173                  var target = null;
3174                  
3175                  if (mejs.MediaFeatures.hasMozNativeFullScreen) {
3176                      target = $(document);
3177                  } else {
3178                      target = player.container;
3179                  }
3180                  
3181                  target.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
3182                  //player.container.bind('webkitfullscreenchange', function(e) {

3183                  
3184                      
3185                      if (mejs.MediaFeatures.isFullScreen()) {
3186                          player.isNativeFullScreen = true;
3187                          // reset the controls once we are fully in full screen

3188                          player.setControlsSize();
3189                      } else {
3190                          player.isNativeFullScreen = false;
3191                          // when a user presses ESC

3192                          // make sure to put the player back into place                                

3193                          player.exitFullScreen();                
3194                      }
3195                  });
3196              }
3197  
3198              var t = this,        
3199                  normalHeight = 0,
3200                  normalWidth = 0,
3201                  container = player.container,                        
3202                  fullscreenBtn = 
3203                      $('<div class="mejs-button mejs-fullscreen-button">' + 
3204                          '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '"></button>' + 
3205                      '</div>')
3206                      .appendTo(controls);
3207                  
3208                  if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
3209                      
3210                      fullscreenBtn.click(function() {
3211                          var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;                                                    
3212                          
3213                          if (isFullScreen) {
3214                              player.exitFullScreen();
3215                          } else {                        
3216                              player.enterFullScreen();
3217                          }
3218                      });
3219                      
3220                  } else {
3221  
3222                      var hideTimeout = null,
3223                          supportsPointerEvents = (function() {
3224                              // TAKEN FROM MODERNIZR

3225                              var element = document.createElement('x'),
3226                                  documentElement = document.documentElement,
3227                                  getComputedStyle = window.getComputedStyle,
3228                                  supports;
3229                              if(!('pointerEvents' in element.style)){
3230                                  return false;
3231                              }
3232                              element.style.pointerEvents = 'auto';
3233                              element.style.pointerEvents = 'x';
3234                              documentElement.appendChild(element);
3235                              supports = getComputedStyle && 
3236                                  getComputedStyle(element, '').pointerEvents === 'auto';
3237                              documentElement.removeChild(element);
3238                              return !!supports;                            
3239                          })();
3240                          
3241                      console.log('supportsPointerEvents', supportsPointerEvents);
3242                          
3243                      if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
3244                          
3245                          // allows clicking through the fullscreen button and controls down directly to Flash

3246                          
3247                          /*

3248                           When a user puts his mouse over the fullscreen button, the controls are disabled

3249                           So we put a div over the video and another one on iether side of the fullscreen button

3250                           that caputre mouse movement

3251                           and restore the controls once the mouse moves outside of the fullscreen button

3252                          */
3253                          
3254                          var fullscreenIsDisabled = false,
3255                              restoreControls = function() {
3256                                  if (fullscreenIsDisabled) {
3257                                      // hide the hovers

3258                                      videoHoverDiv.hide();
3259                                      controlsLeftHoverDiv.hide();
3260                                      controlsRightHoverDiv.hide();
3261                                      
3262                                      // restore the control bar

3263                                      fullscreenBtn.css('pointer-events', '');
3264                                      t.controls.css('pointer-events', '');
3265                                      
3266                                      // store for later

3267                                      fullscreenIsDisabled = false;
3268                                  }
3269                              },
3270                              videoHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
3271                              controlsLeftHoverDiv = $('<div class="mejs-fullscreen-hover"  />').appendTo(t.container).mouseover(restoreControls),
3272                              controlsRightHoverDiv = $('<div class="mejs-fullscreen-hover"  />').appendTo(t.container).mouseover(restoreControls),
3273                              positionHoverDivs = function() {
3274                                  var style = {position: 'absolute', top: 0, left: 0}; //, backgroundColor: '#f00'};

3275                                  videoHoverDiv.css(style);
3276                                  controlsLeftHoverDiv.css(style);
3277                                  controlsRightHoverDiv.css(style);
3278                                  
3279                                  // over video, but not controls

3280                                  videoHoverDiv
3281                                      .width( t.container.width() )
3282                                      .height( t.container.height() - t.controls.height() );
3283                                  
3284                                  // over controls, but not the fullscreen button

3285                                  var fullScreenBtnOffset = fullscreenBtn.offset().left - t.container.offset().left;
3286                                      fullScreenBtnWidth = fullscreenBtn.outerWidth(true);
3287                                      
3288                                  controlsLeftHoverDiv
3289                                      .width( fullScreenBtnOffset )
3290                                      .height( t.controls.height() )
3291                                      .css({top: t.container.height() - t.controls.height()});
3292                                      
3293                                  // after the fullscreen button

3294                                  controlsRightHoverDiv
3295                                      .width( t.container.width() - fullScreenBtnOffset - fullScreenBtnWidth )
3296                                      .height( t.controls.height() )
3297                                      .css({top: t.container.height() - t.controls.height(),
3298                                           left: fullScreenBtnOffset + fullScreenBtnWidth});                                
3299                              };
3300                          
3301                          $(document).resize(function() {
3302                              positionHoverDivs();
3303                          });
3304                                                  
3305                          // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash

3306                          fullscreenBtn
3307                              .mouseover(function() {
3308                                  
3309                                  if (!t.isFullScreen) {
3310                                      
3311                                      var buttonPos = fullscreenBtn.offset(),
3312                                          containerPos = player.container.offset();
3313                                      
3314                                      // move the button in Flash into place

3315                                      media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);                                    
3316                                      
3317                                      // allows click through

3318                                      fullscreenBtn.css('pointer-events', 'none');
3319                                      t.controls.css('pointer-events', 'none');
3320                                      
3321                                      // show the divs that will restore things

3322                                      videoHoverDiv.show();
3323                                      controlsRightHoverDiv.show();
3324                                      controlsLeftHoverDiv.show();
3325                                      positionHoverDivs();
3326                                      
3327                                      fullscreenIsDisabled = true;
3328                                  }
3329                              
3330                              });
3331                          
3332                          // restore controls anytime the user enters or leaves fullscreen    

3333                          media.addEventListener('fullscreenchange', function(e) {
3334                              restoreControls();
3335                          });
3336                          
3337                          
3338                          // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events

3339                          // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button 

3340                          /*

3341                          $(document).mousemove(function(e) {

3342                              

3343                              // if the mouse is anywhere but the fullsceen button, then restore it all

3344                              if (fullscreenIsDisabled) {

3345                                  

3346                                  var fullscreenBtnPos = fullscreenBtn.offset();

3347                                  

3348  

3349                                  if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||

3350                                      e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)

3351                                      ) {

3352                                  

3353                                      fullscreenBtn.css('pointer-events', '');

3354                                      t.controls.css('pointer-events', '');

3355                                      

3356                                      fullscreenIsDisabled = false;

3357                                  }

3358                              }

3359                          });

3360                          */
3361                          
3362                          
3363                      } else {
3364                          
3365                          // the hover state will show the fullscreen button in Flash to hover up and click

3366                          
3367                          fullscreenBtn
3368                              .mouseover(function() {
3369                                  
3370                                  if (hideTimeout !== null) {
3371                                      clearTimeout(hideTimeout);
3372                                      delete hideTimeout;
3373                                  }
3374                                  
3375                                  var buttonPos = fullscreenBtn.offset(),
3376                                      containerPos = player.container.offset();
3377                                      
3378                                  media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
3379                              
3380                              })
3381                              .mouseout(function() {
3382                              
3383                                  if (hideTimeout !== null) {
3384                                      clearTimeout(hideTimeout);
3385                                      delete hideTimeout;
3386                                  }
3387                                  
3388                                  hideTimeout = setTimeout(function() {    
3389                                      media.hideFullscreenButton();
3390                                  }, 1500);
3391                                  
3392                                  
3393                              });                        
3394                      }
3395                  }
3396              
3397              player.fullscreenBtn = fullscreenBtn;    
3398  
3399              $(document).bind('keydown',function (e) {
3400                  if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
3401                      player.exitFullScreen();
3402                  }
3403              });
3404                  
3405          },
3406          enterFullScreen: function() {
3407              
3408              var t = this;
3409              
3410              // firefox+flash can't adjust plugin sizes without resetting :(

3411              if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
3412                  //t.media.setFullscreen(true);

3413                  //player.isFullScreen = true;

3414                  return;
3415              }            
3416                          
3417              // store overflow 

3418              docStyleOverflow = document.documentElement.style.overflow;
3419              // set it to not show scroll bars so 100% will work

3420              document.documentElement.style.overflow = 'hidden';            
3421          
3422              // store sizing

3423              normalHeight = t.container.height();
3424              normalWidth = t.container.width();
3425              
3426              // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)

3427              if (t.media.pluginType === 'native') {
3428                  if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
3429                              
3430                      mejs.MediaFeatures.requestFullScreen(t.container[0]);
3431                      //return;

3432                      
3433                      if (t.isInIframe) {
3434                          // sometimes exiting from fullscreen doesn't work

3435                          // notably in Chrome <iframe>. Fixed in version 17

3436                          setTimeout(function checkFullscreen() {
3437                                  
3438                              if (t.isNativeFullScreen) {
3439                                  
3440                                  // check if the video is suddenly not really fullscreen

3441                                  if ($(window).width() !== screen.width) {
3442                                      // manually exit

3443                                      t.exitFullScreen();
3444                                  } else {
3445                                      // test again

3446                                      setTimeout(checkFullscreen, 500);                                                        
3447                                  }
3448                              }
3449                              
3450                              
3451                          }, 500);
3452                      }
3453                      
3454                  } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
3455                      t.media.webkitEnterFullscreen();
3456                      return;
3457                  }
3458              }
3459              
3460              // check for iframe launch

3461              if (t.isInIframe) {
3462                  var url = t.options.newWindowCallback(this);
3463                  
3464                  
3465                  if (url !== '') {
3466                      
3467                      // launch immediately

3468                      if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
3469                          t.pause();
3470                          window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
3471                          return;
3472                      } else {
3473                          setTimeout(function() {
3474                              if (!t.isNativeFullScreen) {
3475                                  t.pause();
3476                                  window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');                                
3477                              }
3478                          }, 250);
3479                      }
3480                  }    
3481                  
3482              }
3483              
3484              // full window code

3485  
3486              
3487  
3488              // make full size

3489              t.container
3490                  .addClass('mejs-container-fullscreen')
3491                  .width('100%')
3492                  .height('100%');
3493                  //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});                

3494  
3495              // Only needed for safari 5.1 native full screen, can cause display issues elsewhere

3496              // Actually, it seems to be needed for IE8, too

3497              //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

3498                  setTimeout(function() {
3499                      t.container.css({width: '100%', height: '100%'});
3500                      t.setControlsSize();
3501                  }, 500);
3502              //}

3503                  
3504              if (t.pluginType === 'native') {
3505                  t.$media
3506                      .width('100%')
3507                      .height('100%');
3508              } else {
3509                  t.container.find('object, embed, iframe')
3510                      .width('100%')
3511                      .height('100%');
3512                      
3513                  //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {

3514                      t.media.setVideoSize($(window).width(),$(window).height());
3515                  //}

3516              }
3517              
3518              t.layers.children('div')
3519                  .width('100%')
3520                  .height('100%');
3521  
3522              if (t.fullscreenBtn) {
3523                  t.fullscreenBtn
3524                      .removeClass('mejs-fullscreen')
3525                      .addClass('mejs-unfullscreen');
3526              }
3527  
3528              t.setControlsSize();
3529              t.isFullScreen = true;
3530          },
3531          
3532          exitFullScreen: function() {
3533              
3534              var t = this;        
3535          
3536              // firefox can't adjust plugins

3537              if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {                
3538                  t.media.setFullscreen(false);
3539                  //player.isFullScreen = false;

3540                  return;
3541              }        
3542          
3543              // come outo of native fullscreen

3544              if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
3545                  mejs.MediaFeatures.cancelFullScreen();
3546              }    
3547  
3548              // restore scroll bars to document

3549              document.documentElement.style.overflow = docStyleOverflow;                    
3550                  
3551              t.container
3552                  .removeClass('mejs-container-fullscreen')
3553                  .width(normalWidth)
3554                  .height(normalHeight);
3555                  //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

3556              
3557              if (t.pluginType === 'native') {
3558                  t.$media
3559                      .width(normalWidth)
3560                      .height(normalHeight);
3561              } else {
3562                  t.container.find('object embed')
3563                      .width(normalWidth)
3564                      .height(normalHeight);
3565                      
3566                  t.media.setVideoSize(normalWidth, normalHeight);
3567              }                
3568  
3569              t.layers.children('div')
3570                  .width(normalWidth)
3571                  .height(normalHeight);
3572  
3573              t.fullscreenBtn
3574                  .removeClass('mejs-unfullscreen')
3575                  .addClass('mejs-fullscreen');
3576  
3577              t.setControlsSize();
3578              t.isFullScreen = false;
3579          }    
3580      });
3581  
3582  })(mejs.$);
3583  
3584  (function($) {
3585  
3586      // add extra default options 
3587      $.extend(mejs.MepDefaults, {
3588          // this will automatically turn on a <track>
3589          startLanguage: '',
3590          
3591          tracksText: 'Captions/Subtitles'
3592      });
3593  
3594      $.extend(MediaElementPlayer.prototype, {
3595      
3596          hasChapters: false,
3597  
3598          buildtracks: function(player, controls, layers, media) {
3599              if (!player.isVideo)
3600                  return;
3601  
3602              if (player.tracks.length == 0)
3603                  return;
3604  
3605              var t= this, i, options = '';
3606  
3607              player.chapters = 
3608                      $('<div class="mejs-chapters mejs-layer"></div>')
3609                          .prependTo(layers).hide();
3610              player.captions = 
3611                      $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position"><span class="mejs-captions-text"></span></div></div>')
3612                          .prependTo(layers).hide();
3613              player.captionsText = player.captions.find('.mejs-captions-text');
3614              player.captionsButton = 
3615                      $('<div class="mejs-button mejs-captions-button">'+
3616                          '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '"></button>'+
3617                          '<div class="mejs-captions-selector">'+
3618                              '<ul>'+
3619                                  '<li>'+
3620                                      '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
3621                                      '<label for="' + player.id + '_captions_none">None</label>'+
3622                                  '</li>'    +
3623                              '</ul>'+
3624                          '</div>'+
3625                      '</div>')
3626                          .appendTo(controls)
3627                          
3628                          // hover
3629                          .hover(function() {
3630                              $(this).find('.mejs-captions-selector').css('visibility','visible');
3631                          }, function() {
3632                              $(this).find('.mejs-captions-selector').css('visibility','hidden');
3633                          })                    
3634                          
3635                          // handle clicks to the language radio buttons
3636                          .delegate('input[type=radio]','click',function() {
3637                              lang = this.value;
3638  
3639                              if (lang == 'none') {
3640                                  player.selectedTrack = null;
3641                              } else {
3642                                  for (i=0; i<player.tracks.length; i++) {
3643                                      if (player.tracks[i].srclang == lang) {
3644                                          player.selectedTrack = player.tracks[i];
3645                                          player.captions.attr('lang', player.selectedTrack.srclang);
3646                                          player.displayCaptions();
3647                                          break;
3648                                      }
3649                                  }
3650                              }
3651                          });
3652                          //.bind('mouseenter', function() {
3653                          //    player.captionsButton.find('.mejs-captions-selector').css('visibility','visible')
3654                          //});
3655  
3656              if (!player.options.alwaysShowControls) {
3657                  // move with controls
3658                  player.container
3659                      .bind('mouseenter', function () {
3660                          // push captions above controls
3661                          player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
3662  
3663                      })
3664                      .bind('mouseleave', function () {
3665                          if (!media.paused) {
3666                              // move back to normal place
3667                              player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
3668                          }
3669                      });
3670              } else {
3671                  player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
3672              }
3673  
3674              player.trackToLoad = -1;
3675              player.selectedTrack = null;
3676              player.isLoadingTrack = false;
3677  
3678              
3679  
3680              // add to list
3681              for (i=0; i<player.tracks.length; i++) {
3682                  if (player.tracks[i].kind == 'subtitles') {
3683                      player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
3684                  }
3685              }
3686  
3687              player.loadNextTrack();
3688  
3689  
3690              media.addEventListener('timeupdate',function(e) {
3691                  player.displayCaptions();
3692              }, false);
3693  
3694              media.addEventListener('loadedmetadata', function(e) {
3695                  player.displayChapters();
3696              }, false);
3697  
3698              player.container.hover(
3699                  function () {
3700                      // chapters
3701                      if (player.hasChapters) {
3702                          player.chapters.css('visibility','visible');
3703                          player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
3704                      }
3705                  },
3706                  function () {
3707                      if (player.hasChapters && !media.paused) {
3708                          player.chapters.fadeOut(200, function() {
3709                              $(this).css('visibility','hidden');
3710                              $(this).css('display','block');
3711                          });
3712                      }
3713                  });
3714                  
3715              // check for autoplay
3716              if (player.node.getAttribute('autoplay') !== null) {
3717                  player.chapters.css('visibility','hidden');
3718              }
3719          },
3720  
3721          loadNextTrack: function() {
3722              var t = this;
3723  
3724              t.trackToLoad++;
3725              if (t.trackToLoad < t.tracks.length) {
3726                  t.isLoadingTrack = true;
3727                  t.loadTrack(t.trackToLoad);
3728              } else {
3729                  // add done?
3730                  t.isLoadingTrack = false;
3731              }
3732          },
3733  
3734          loadTrack: function(index){
3735              var
3736                  t = this,
3737                  track = t.tracks[index],
3738                  after = function() {
3739  
3740                      track.isLoaded = true;
3741  
3742                      // create button
3743                      //t.addTrackButton(track.srclang);
3744                      t.enableTrackButton(track.srclang, track.label);
3745  
3746                      t.loadNextTrack();
3747  
3748                  };
3749  
3750              if (track.isTranslation) {
3751  
3752                  // translate the first track
3753                  mejs.TrackFormatParser.translateTrackText(t.tracks[0].entries, t.tracks[0].srclang, track.srclang, t.options.googleApiKey, function(newOne) {
3754  
3755                      // store the new translation
3756                      track.entries = newOne;
3757  
3758                      after();
3759                  });
3760  
3761              } else {
3762                  $.ajax({
3763                      url: track.src,
3764                      success: function(d) {
3765  
3766                          // parse the loaded file
3767                          track.entries = mejs.TrackFormatParser.parse(d);
3768                          after();
3769  
3770                          if (track.kind == 'chapters' && t.media.duration > 0) {
3771                              t.drawChapters(track);
3772                          }
3773                      },
3774                      error: function() {
3775                          t.loadNextTrack();
3776                      }
3777                  });
3778              }
3779          },
3780  
3781          enableTrackButton: function(lang, label) {
3782              var t = this;
3783              
3784              if (label === '') {
3785                  label = mejs.language.codes[lang] || lang;
3786              }            
3787  
3788              t.captionsButton
3789                  .find('input[value=' + lang + ']')
3790                      .prop('disabled',false)
3791                  .siblings('label')
3792                      .html( label );
3793  
3794              // auto select
3795              if (t.options.startLanguage == lang) {
3796                  $('#' + t.id + '_captions_' + lang).click();
3797              }
3798  
3799              t.adjustLanguageBox();
3800          },
3801  
3802          addTrackButton: function(lang, label) {
3803              var t = this;
3804              if (label === '') {
3805                  label = mejs.language.codes[lang] || lang;
3806              }
3807  
3808              t.captionsButton.find('ul').append(
3809                  $('<li>'+
3810                      '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
3811                      '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
3812                  '</li>')
3813              );
3814  
3815              t.adjustLanguageBox();
3816  
3817              // remove this from the dropdownlist (if it exists)
3818              t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
3819          },
3820  
3821          adjustLanguageBox:function() {
3822              var t = this;
3823              // adjust the size of the outer box
3824              t.captionsButton.find('.mejs-captions-selector').height(
3825                  t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
3826                  t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
3827              );
3828          },
3829  
3830          displayCaptions: function() {
3831  
3832              if (typeof this.tracks == 'undefined')
3833                  return;
3834  
3835              var
3836                  t = this,
3837                  i,
3838                  track = t.selectedTrack;
3839  
3840              if (track != null && track.isLoaded) {
3841                  for (i=0; i<track.entries.times.length; i++) {
3842                      if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
3843                          t.captionsText.html(track.entries.text[i]);
3844                          t.captions.show().height(0);
3845                          return; // exit out if one is visible;
3846                      }
3847                  }
3848                  t.captions.hide();
3849              } else {
3850                  t.captions.hide();
3851              }
3852          },
3853  
3854          displayChapters: function() {
3855              var 
3856                  t = this,
3857                  i;
3858  
3859              for (i=0; i<t.tracks.length; i++) {
3860                  if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
3861                      t.drawChapters(t.tracks[i]);
3862                      t.hasChapters = true;
3863                      break;
3864                  }
3865              }
3866          },
3867  
3868          drawChapters: function(chapters) {
3869              var 
3870                  t = this,
3871                  i,
3872                  dur,
3873                  //width,
3874                  //left,
3875                  percent = 0,
3876                  usedPercent = 0;
3877  
3878              t.chapters.empty();
3879  
3880              for (i=0; i<chapters.entries.times.length; i++) {
3881                  dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
3882                  percent = Math.floor(dur / t.media.duration * 100);
3883                  if (percent + usedPercent > 100 || // too large
3884                      i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
3885                      {
3886                      percent = 100 - usedPercent;
3887                  }
3888                  //width = Math.floor(t.width * dur / t.media.duration);
3889                  //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
3890                  //if (left + width > t.width) {
3891                  //    width = t.width - left;
3892                  //}
3893  
3894                  t.chapters.append( $(
3895                      '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' + 
3896                          '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' + 
3897                              '<span class="ch-title">' + chapters.entries.text[i] + '</span>' + 
3898                              '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' + 
3899                          '</div>' +
3900                      '</div>'));
3901                  usedPercent += percent;
3902              }
3903  
3904              t.chapters.find('div.mejs-chapter').click(function() {
3905                  t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
3906                  if (t.media.paused) {
3907                      t.media.play(); 
3908                  }
3909              });
3910  
3911              t.chapters.show();
3912          }
3913      });
3914  
3915  
3916  
3917      mejs.language = {
3918          codes:  {
3919              af:'Afrikaans',
3920              sq:'Albanian',
3921              ar:'Arabic',
3922              be:'Belarusian',
3923              bg:'Bulgarian',
3924              ca:'Catalan',
3925              zh:'Chinese',
3926              'zh-cn':'Chinese Simplified',
3927              'zh-tw':'Chinese Traditional',
3928              hr:'Croatian',
3929              cs:'Czech',
3930              da:'Danish',
3931              nl:'Dutch',
3932              en:'English',
3933              et:'Estonian',
3934              tl:'Filipino',
3935              fi:'Finnish',
3936              fr:'French',
3937              gl:'Galician',
3938              de:'German',
3939              el:'Greek',
3940              ht:'Haitian Creole',
3941              iw:'Hebrew',
3942              hi:'Hindi',
3943              hu:'Hungarian',
3944              is:'Icelandic',
3945              id:'Indonesian',
3946              ga:'Irish',
3947              it:'Italian',
3948              ja:'Japanese',
3949              ko:'Korean',
3950              lv:'Latvian',
3951              lt:'Lithuanian',
3952              mk:'Macedonian',
3953              ms:'Malay',
3954              mt:'Maltese',
3955              no:'Norwegian',
3956              fa:'Persian',
3957              pl:'Polish',
3958              pt:'Portuguese',
3959              //'pt-pt':'Portuguese (Portugal)',
3960              ro:'Romanian',
3961              ru:'Russian',
3962              sr:'Serbian',
3963              sk:'Slovak',
3964              sl:'Slovenian',
3965              es:'Spanish',
3966              sw:'Swahili',
3967              sv:'Swedish',
3968              tl:'Tagalog',
3969              th:'Thai',
3970              tr:'Turkish',
3971              uk:'Ukrainian',
3972              vi:'Vietnamese',
3973              cy:'Welsh',
3974              yi:'Yiddish'
3975          }
3976      };
3977  
3978      /*
3979      Parses WebVVT format which should be formatted as
3980      ================================
3981      WEBVTT
3982      
3983      1
3984      00:00:01,1 --> 00:00:05,000
3985      A line of text
3986  
3987      2
3988      00:01:15,1 --> 00:02:05,000
3989      A second line of text
3990      
3991      ===============================
3992  
3993      Adapted from: http://www.delphiki.com/html5/playr
3994      */
3995      mejs.TrackFormatParser = {
3996          // match start "chapter-" (or anythingelse)
3997          pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/,
3998          pattern_timecode: /^([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
3999  
4000          split2: function (text, regex) {
4001              // normal version for compliant browsers
4002              // see below for IE fix
4003              return text.split(regex);
4004          },
4005          parse: function(trackText) {
4006              var 
4007                  i = 0,
4008                  lines = this.split2(trackText, /\r?\n/),
4009                  entries = {text:[], times:[]},
4010                  timecode,
4011                  text;
4012  
4013              for(; i<lines.length; i++) {
4014                  // check for the line number
4015                  if (this.pattern_identifier.exec(lines[i])){
4016                      // skip to the next line where the start --> end time code should be
4017                      i++;
4018                      timecode = this.pattern_timecode.exec(lines[i]);                
4019                      
4020                      if (timecode && i<lines.length){
4021                          i++;
4022                          // grab all the (possibly multi-line) text that follows
4023                          text = lines[i];
4024                          i++;
4025                          while(lines[i] !== '' && i<lines.length){
4026                              text = text + '\n' + lines[i];
4027                              i++;
4028                          }
4029  
4030                          // Text is in a different array so I can use .join
4031                          entries.text.push(text);
4032                          entries.times.push(
4033                          {
4034                              start: mejs.Utility.timeCodeToSeconds(timecode[1]),
4035                              stop: mejs.Utility.timeCodeToSeconds(timecode[3]),
4036                              settings: timecode[5]
4037                          });
4038                      }
4039                  }
4040              }
4041  
4042              return entries;
4043          }
4044      };
4045      
4046      // test for browsers with bad String.split method.
4047      if ('x\n\ny'.split(/\n/gi).length != 3) {
4048          // add super slow IE8 and below version
4049          mejs.TrackFormatParser.split2 = function(text, regex) {
4050              var 
4051                  parts = [], 
4052                  chunk = '',
4053                  i;
4054  
4055              for (i=0; i<text.length; i++) {
4056                  chunk += text.substring(i,i+1);
4057                  if (regex.test(chunk)) {
4058                      parts.push(chunk.replace(regex, ''));
4059                      chunk = '';
4060                  }
4061              }
4062              parts.push(chunk);
4063              return parts;
4064          }
4065      }    
4066  
4067  })(mejs.$);
4068  
4069  /*

4070  * ContextMenu Plugin

4071  * 

4072  *

4073  */
4074  
4075  (function($) {
4076  
4077  $.extend(mejs.MepDefaults,
4078      { 'contextMenuItems': [
4079          // demo of a fullscreen option

4080          { 
4081              render: function(player) {
4082                  
4083                  // check for fullscreen plugin

4084                  if (typeof player.enterFullScreen == 'undefined')
4085                      return null;
4086              
4087                  if (player.isFullScreen) {
4088                      return "Turn off Fullscreen";
4089                  } else {
4090                      return "Go Fullscreen";
4091                  }
4092              },
4093              click: function(player) {
4094                  if (player.isFullScreen) {
4095                      player.exitFullScreen();
4096                  } else {
4097                      player.enterFullScreen();
4098                  }
4099              }
4100          }
4101          ,
4102          // demo of a mute/unmute button

4103          { 
4104              render: function(player) {
4105                  if (player.media.muted) {
4106                      return "Unmute";
4107                  } else {
4108                      return "Mute";
4109                  }
4110              },
4111              click: function(player) {
4112                  if (player.media.muted) {
4113                      player.setMuted(false);
4114                  } else {
4115                      player.setMuted(true);
4116                  }
4117              }
4118          },
4119          // separator

4120          {
4121              isSeparator: true
4122          }
4123          ,
4124          // demo of simple download video

4125          { 
4126              render: function(player) {
4127                  return "Download Video";
4128              },
4129              click: function(player) {
4130                  window.location.href = player.media.currentSrc;
4131              }
4132          }    
4133      ]}
4134  );
4135  
4136  
4137      $.extend(MediaElementPlayer.prototype, {
4138          buildcontextmenu: function(player, controls, layers, media) {
4139              
4140              // create context menu

4141              player.contextMenu = $('<div class="mejs-contextmenu"></div>')
4142                                  .appendTo($('body'))
4143                                  .hide();
4144              
4145              // create events for showing context menu

4146              player.container.bind('contextmenu', function(e) {
4147                  if (player.isContextMenuEnabled) {
4148                      e.preventDefault();
4149                      player.renderContextMenu(e.clientX-1, e.clientY-1);
4150                      return false;
4151                  }
4152              });
4153              player.container.bind('click', function() {
4154                  player.contextMenu.hide();
4155              });    
4156              player.contextMenu.bind('mouseleave', function() {
4157  
4158                  //console.log('context hover out');

4159                  player.startContextMenuTimer();
4160                  
4161              });        
4162          },
4163          
4164          isContextMenuEnabled: true,
4165          enableContextMenu: function() {
4166              this.isContextMenuEnabled = true;
4167          },
4168          disableContextMenu: function() {
4169              this.isContextMenuEnabled = false;
4170          },
4171          
4172          contextMenuTimeout: null,
4173          startContextMenuTimer: function() {
4174              //console.log('startContextMenuTimer');

4175              
4176              var t = this;
4177              
4178              t.killContextMenuTimer();
4179              
4180              t.contextMenuTimer = setTimeout(function() {
4181                  t.hideContextMenu();
4182                  t.killContextMenuTimer();
4183              }, 750);
4184          },
4185          killContextMenuTimer: function() {
4186              var timer = this.contextMenuTimer;
4187              
4188              //console.log('killContextMenuTimer', timer);

4189              
4190              if (timer != null) {                
4191                  clearTimeout(timer);
4192                  delete timer;
4193                  timer = null;
4194              }
4195          },        
4196          
4197          hideContextMenu: function() {
4198              this.contextMenu.hide();
4199          },
4200          
4201          renderContextMenu: function(x,y) {
4202              
4203              // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly

4204              var t = this,
4205                  html = '',
4206                  items = t.options.contextMenuItems;
4207              
4208              for (var i=0, il=items.length; i<il; i++) {
4209                  
4210                  if (items[i].isSeparator) {
4211                      html += '<div class="mejs-contextmenu-separator"></div>';
4212                  } else {
4213                  
4214                      var rendered = items[i].render(t);
4215                  
4216                      // render can return null if the item doesn't need to be used at the moment

4217                      if (rendered != null) {
4218                          html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
4219                      }
4220                  }
4221              }
4222              
4223              // position and show the context menu

4224              t.contextMenu
4225                  .empty()
4226                  .append($(html))
4227                  .css({top:y, left:x})
4228                  .show();
4229                  
4230              // bind events

4231              t.contextMenu.find('.mejs-contextmenu-item').each(function() {
4232                              
4233                  // which one is this?

4234                  var $dom = $(this),
4235                      itemIndex = parseInt( $dom.data('itemindex'), 10 ),
4236                      item = t.options.contextMenuItems[itemIndex];
4237                  
4238                  // bind extra functionality?

4239                  if (typeof item.show != 'undefined')
4240                      item.show( $dom , t);
4241                  
4242                  // bind click action

4243                  $dom.click(function() {            
4244                      // perform click action

4245                      if (typeof item.click != 'undefined')
4246                          item.click(t);
4247                      
4248                      // close

4249                      t.contextMenu.hide();                
4250                  });                
4251              });    
4252              
4253              // stop the controls from hiding

4254              setTimeout(function() {
4255                  t.killControlsTimer('rev3');    
4256              }, 100);
4257                          
4258          }
4259      });
4260      
4261  })(mejs.$);
4262  

title

Description

title

Description

title

Description

title

title

Body