1/*! 2 * 3 * MediaElement.js 4 * HTML5 <video> and <audio> shim and player 5 * http://mediaelementjs.com/ 6 * 7 * Creates a JavaScript object that mimics HTML5 MediaElement API 8 * for browsers that don't understand HTML5 or can't play the provided codec 9 * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3 10 * 11 * Copyright 2010-2014, John Dyer (http://j.hn) 12 * License: MIT 13 * 14 */ 15// Namespace 16var mejs = mejs || {}; 17 18// version number 19mejs.version = '2.18.2'; 20 21 22// player number (for missing, same id attr) 23mejs.meIndex = 0; 24 25// media types accepted by plugins 26mejs.plugins = { 27 silverlight: [ 28 {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']} 29 ], 30 flash: [ 31 {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube', 'video/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL']} 32 //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!) 33 ], 34 youtube: [ 35 {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']} 36 ], 37 vimeo: [ 38 {version: null, types: ['video/vimeo', 'video/x-vimeo']} 39 ] 40}; 41 42/* 43Utility methods 44*/ 45mejs.Utility = { 46 encodeUrl: function(url) { 47 return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26'); 48 }, 49 escapeHTML: function(s) { 50 return s.toString().split('&').join('&').split('<').join('<').split('"').join('"'); 51 }, 52 absolutizeUrl: function(url) { 53 var el = document.createElement('div'); 54 el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>'; 55 return el.firstChild.href; 56 }, 57 getScriptPath: function(scriptNames) { 58 var 59 i = 0, 60 j, 61 codePath = '', 62 testname = '', 63 slashPos, 64 filenamePos, 65 scriptUrl, 66 scriptPath, 67 scriptFilename, 68 scripts = document.getElementsByTagName('script'), 69 il = scripts.length, 70 jl = scriptNames.length; 71 72 // go through all <script> tags 73 for (; i < il; i++) { 74 scriptUrl = scripts[i].src; 75 slashPos = scriptUrl.lastIndexOf('/'); 76 if (slashPos > -1) { 77 scriptFilename = scriptUrl.substring(slashPos + 1); 78 scriptPath = scriptUrl.substring(0, slashPos + 1); 79 } else { 80 scriptFilename = scriptUrl; 81 scriptPath = ''; 82 } 83 84 // see if any <script> tags have a file name that matches the 85 for (j = 0; j < jl; j++) { 86 testname = scriptNames[j]; 87 filenamePos = scriptFilename.indexOf(testname); 88 if (filenamePos > -1) { 89 codePath = scriptPath; 90 break; 91 } 92 } 93 94 // if we found a path, then break and return it 95 if (codePath !== '') { 96 break; 97 } 98 } 99 100 // send the best path back 101 return codePath; 102 }, 103 /* 104 * Calculate the time format to use. We have a default format set in the 105 * options but it can be imcomplete. We ajust it according to the media 106 * duration. 107 * 108 * We support format like 'hh:mm:ss:ff'. 109 */ 110 calculateTimeFormat: function(time, options, fps) { 111 if (time < 0) { 112 time = 0; 113 } 114 115 if(typeof fps == 'undefined') { 116 fps = 25; 117 } 118 119 var format = options.timeFormat, 120 firstChar = format[0], 121 firstTwoPlaces = (format[1] == format[0]), 122 separatorIndex = firstTwoPlaces? 2: 1, 123 separator = ':', 124 hours = Math.floor(time / 3600) % 24, 125 minutes = Math.floor(time / 60) % 60, 126 seconds = Math.floor(time % 60), 127 frames = Math.floor(((time % 1)*fps).toFixed(3)), 128 lis = [ 129 [frames, 'f'], 130 [seconds, 's'], 131 [minutes, 'm'], 132 [hours, 'h'] 133 ]; 134 135 // Try to get the separator from the format 136 if (format.length < separatorIndex) { 137 separator = format[separatorIndex]; 138 } 139 140 var required = false; 141 142 for (var i=0, len=lis.length; i < len; i++) { 143 if (format.indexOf(lis[i][1]) !== -1) { 144 required=true; 145 } 146 else if (required) { 147 var hasNextValue = false; 148 for (var j=i; j < len; j++) { 149 if (lis[j][0] > 0) { 150 hasNextValue = true; 151 break; 152 } 153 } 154 155 if (! hasNextValue) { 156 break; 157 } 158 159 if (!firstTwoPlaces) { 160 format = firstChar + format; 161 } 162 format = lis[i][1] + separator + format; 163 if (firstTwoPlaces) { 164 format = lis[i][1] + format; 165 } 166 firstChar = lis[i][1]; 167 } 168 } 169 options.currentTimeFormat = format; 170 }, 171 /* 172 * Prefix the given number by zero if it is lower than 10. 173 */ 174 twoDigitsString: function(n) { 175 if (n < 10) { 176 return '0' + n; 177 } 178 return String(n); 179 }, 180 secondsToTimeCode: function(time, options) { 181 if (time < 0) { 182 time = 0; 183 } 184 185 // Maintain backward compatibility with method signature before v2.18. 186 if (typeof options !== 'object') { 187 var format = 'm:ss'; 188 format = arguments[1] ? 'hh:mm:ss' : format; // forceHours 189 format = arguments[2] ? format + ':ff' : format; // showFrameCount 190 191 options = { 192 currentTimeFormat: format, 193 framesPerSecond: arguments[3] || 25 194 }; 195 } 196 197 var fps = options.framesPerSecond; 198 if(typeof fps === 'undefined') { 199 fps = 25; 200 } 201 202 var format = options.currentTimeFormat, 203 hours = Math.floor(time / 3600) % 24, 204 minutes = Math.floor(time / 60) % 60, 205 seconds = Math.floor(time % 60), 206 frames = Math.floor(((time % 1)*fps).toFixed(3)); 207 lis = [ 208 [frames, 'f'], 209 [seconds, 's'], 210 [minutes, 'm'], 211 [hours, 'h'] 212 ]; 213 214 var res = format; 215 for (i=0,len=lis.length; i < len; i++) { 216 res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0])); 217 res = res.replace(lis[i][1], lis[i][0]); 218 } 219 return res; 220 }, 221 222 timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){ 223 if (typeof showFrameCount == 'undefined') { 224 showFrameCount=false; 225 } else if(typeof fps == 'undefined') { 226 fps = 25; 227 } 228 229 var tc_array = hh_mm_ss_ff.split(":"), 230 tc_hh = parseInt(tc_array[0], 10), 231 tc_mm = parseInt(tc_array[1], 10), 232 tc_ss = parseInt(tc_array[2], 10), 233 tc_ff = 0, 234 tc_in_seconds = 0; 235 236 if (showFrameCount) { 237 tc_ff = parseInt(tc_array[3])/fps; 238 } 239 240 tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff; 241 242 return tc_in_seconds; 243 }, 244 245 246 convertSMPTEtoSeconds: function (SMPTE) { 247 if (typeof SMPTE != 'string') 248 return false;
249 250 SMPTE = SMPTE.replace(',', '.'); 251 252 var secs = 0, 253 decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0, 254 multiplier = 1; 255 256 SMPTE = SMPTE.split(':').reverse(); 257 258 for (var i = 0; i < SMPTE.length; i++) { 259 multiplier = 1; 260 if (i > 0) { 261 multiplier = Math.pow(60, i); 262 } 263 secs += Number(SMPTE[i]) * multiplier; 264 } 265 return Number(secs.toFixed(decimalLen)); 266 }, 267 268 /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */ 269 removeSwf: function(id) { 270 var obj = document.getElementById(id); 271 if (obj && /object|embed/i.test(obj.nodeName)) { 272 if (mejs.MediaFeatures.isIE) { 273 obj.style.display = "none"; 274 (function(){ 275 if (obj.readyState == 4) { 276 mejs.Utility.removeObjectInIE(id); 277 } else { 278 setTimeout(arguments.callee, 10); 279 } 280 })(); 281 } else { 282 obj.parentNode.removeChild(obj); 283 } 284 } 285 }, 286 removeObjectInIE: function(id) { 287 var obj = document.getElementById(id); 288 if (obj) { 289 for (var i in obj) { 290 if (typeof obj[i] == "function") { 291 obj[i] = null; 292 } 293 } 294 obj.parentNode.removeChild(obj); 295 } 296 } 297}; 298 299 300// Core detector, plugins are added below 301mejs.PluginDetector = { 302 303 // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]); 304 hasPluginVersion: function(plugin, v) { 305 var pv = this.plugins[plugin]; 306 v[1] = v[1] || 0; 307 v[2] = v[2] || 0; 308 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; 309 }, 310 311 // cached values 312 nav: window.navigator, 313 ua: window.navigator.userAgent.toLowerCase(), 314 315 // stored version numbers 316 plugins: [], 317 318 // runs detectPlugin() and stores the version number 319 addPlugin: function(p, pluginName, mimeType, activeX, axDetect) { 320 this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect); 321 }, 322 323 // get the version number from the mimetype (all but IE) or ActiveX (IE) 324 detectPlugin: function(pluginName, mimeType, activeX, axDetect) { 325 326 var version = [0,0,0], 327 description, 328 i, 329 ax; 330 331 // Firefox, Webkit, Opera 332 if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') { 333 description = this.nav.plugins[pluginName].description; 334 if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) { 335 version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.'); 336 for (i=0; i<version.length; i++) { 337 version[i] = parseInt(version[i].match(/\d+/), 10); 338 } 339 } 340 // Internet Explorer / ActiveX 341 } else if (typeof(window.ActiveXObject) != 'undefined') { 342 try { 343 ax = new ActiveXObject(activeX); 344 if (ax) { 345 version = axDetect(ax); 346 } 347 } 348 catch (e) { } 349 } 350 return version; 351 } 352}; 353 354// Add Flash detection 355mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) { 356 // adapted from SWFObject 357 var version = [], 358 d = ax.GetVariable("$version"); 359 if (d) { 360 d = d.split(" ")[1].split(","); 361 version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 362 } 363 return version; 364}); 365 366// Add Silverlight detection 367mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) { 368 // Silverlight cannot report its version number to IE 369 // but it does have a isVersionSupported function, so we have to loop through it to get a version number. 370 // adapted from http://www.silverlightversion.com/ 371 var v = [0,0,0,0], 372 loopMatch = function(ax, v, i, n) { 373 while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){ 374 v[i]+=n; 375 } 376 v[i] -= n; 377 }; 378 loopMatch(ax, v, 0, 1); 379 loopMatch(ax, v, 1, 1); 380 loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx) 381 loopMatch(ax, v, 2, 1000); 382 loopMatch(ax, v, 2, 100); 383 loopMatch(ax, v, 2, 10); 384 loopMatch(ax, v, 2, 1); 385 loopMatch(ax, v, 3, 1); 386 387 return v; 388}); 389// add adobe acrobat 390/* 391PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) { 392 var version = [], 393 d = ax.GetVersions().split(',')[0].split('=')[1].split('.'); 394 395 if (d) { 396 version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 397 } 398 return version; 399});
400*/ 401// necessary detection (fixes for <IE9) 402mejs.MediaFeatures = { 403 init: function() { 404 var 405 t = this, 406 d = document, 407 nav = mejs.PluginDetector.nav, 408 ua = mejs.PluginDetector.ua.toLowerCase(), 409 i, 410 v, 411 html5Elements = ['source','track','audio','video']; 412 413 // detect browsers (only the ones that have some kind of quirk we need to work around) 414 t.isiPad = (ua.match(/ipad/i) !== null); 415 t.isiPhone = (ua.match(/iphone/i) !== null); 416 t.isiOS = t.isiPhone || t.isiPad; 417 t.isAndroid = (ua.match(/android/i) !== null); 418 t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null); 419 t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null)); 420 t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null); 421 t.isChrome = (ua.match(/chrome/gi) !== null); 422 t.isChromium = (ua.match(/chromium/gi) !== null); 423 t.isFirefox = (ua.match(/firefox/gi) !== null); 424 t.isWebkit = (ua.match(/webkit/gi) !== null); 425 t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE; 426 t.isOpera = (ua.match(/opera/gi) !== null); 427 t.hasTouch = ('ontouchstart' in window); // && window.ontouchstart != null); // this breaks iOS 7 428 429 // borrowed from Modernizr 430 t.svg = !! document.createElementNS && 431 !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect; 432 433 // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection 434 for (i=0; i<html5Elements.length; i++) { 435 v = document.createElement(html5Elements[i]); 436 } 437 438 t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid); 439 440 // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer) 441 try{ 442 v.canPlayType("video/mp4"); 443 }catch(e){ 444 t.supportsMediaTag = false; 445 } 446 447 // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails) 448 449 // iOS 450 t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined'); 451 452 // W3C 453 t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined'); 454 455 // webkit/firefox/IE11+ 456 t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined'); 457 t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined'); 458 t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined'); 459 460 t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen); 461 t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen; 462 463 // Enabled? 464 if (t.hasMozNativeFullScreen) { 465 t.nativeFullScreenEnabled = document.mozFullScreenEnabled; 466 } else if (t.hasMsNativeFullScreen) { 467 t.nativeFullScreenEnabled = document.msFullscreenEnabled; 468 } 469 470 if (t.isChrome) { 471 t.hasSemiNativeFullScreen = false; 472 } 473 474 if (t.hasTrueNativeFullScreen) { 475 476 t.fullScreenEventName = ''; 477 if (t.hasWebkitNativeFullScreen) { 478 t.fullScreenEventName = 'webkitfullscreenchange'; 479 480 } else if (t.hasMozNativeFullScreen) { 481 t.fullScreenEventName = 'mozfullscreenchange'; 482 483 } else if (t.hasMsNativeFullScreen) { 484 t.fullScreenEventName = 'MSFullscreenChange'; 485 } 486 487 t.isFullScreen = function() { 488 if (t.hasMozNativeFullScreen) { 489 return d.mozFullScreen; 490 491 } else if (t.hasWebkitNativeFullScreen) { 492 return d.webkitIsFullScreen; 493 494 } else if (t.hasMsNativeFullScreen) { 495 return d.msFullscreenElement !== null; 496 } 497 } 498 499 t.requestFullScreen = function(el) { 500 501 if (t.hasWebkitNativeFullScreen) { 502 el.webkitRequestFullScreen(); 503 504 } else if (t.hasMozNativeFullScreen) { 505 el.mozRequestFullScreen(); 506 507 } else if (t.hasMsNativeFullScreen) { 508 el.msRequestFullscreen(); 509 510 } 511 } 512 513 t.cancelFullScreen = function() { 514 if (t.hasWebkitNativeFullScreen) { 515 document.webkitCancelFullScreen(); 516 517 } else if (t.hasMozNativeFullScreen) { 518 document.mozCancelFullScreen(); 519 520 } else if (t.hasMsNativeFullScreen) { 521 document.msExitFullscreen(); 522 523 } 524 } 525 526 } 527 528 529 // OS X 10.5 can't do this even if it says it can :( 530 if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) { 531 t.hasNativeFullScreen = false;
532 t.hasSemiNativeFullScreen = false; 533 } 534 535 } 536}; 537mejs.MediaFeatures.init(); 538 539/* 540extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below) 541*/ 542mejs.HtmlMediaElement = { 543 pluginType: 'native', 544 isFullScreen: false, 545 546 setCurrentTime: function (time) { 547 this.currentTime = time; 548 }, 549 550 setMuted: function (muted) { 551 this.muted = muted; 552 }, 553 554 setVolume: function (volume) { 555 this.volume = volume; 556 }, 557 558 // for parity with the plugin versions 559 stop: function () { 560 this.pause(); 561 }, 562 563 // This can be a url string 564 // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}] 565 setSrc: function (url) { 566 567 // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right? 568 var 569 existingSources = this.getElementsByTagName('source'); 570 while (existingSources.length > 0){ 571 this.removeChild(existingSources[0]); 572 } 573 574 if (typeof url == 'string') { 575 this.src = url; 576 } else { 577 var i, media; 578 579 for (i=0; i<url.length; i++) { 580 media = url[i]; 581 if (this.canPlayType(media.type)) { 582 this.src = media.src; 583 break; 584 } 585 } 586 } 587 }, 588 589 setVideoSize: function (width, height) { 590 this.width = width; 591 this.height = height; 592 } 593}; 594 595/* 596Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember] 597*/ 598mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) { 599 this.id = pluginid; 600 this.pluginType = pluginType; 601 this.src = mediaUrl; 602 this.events = {}; 603 this.attributes = {}; 604}; 605 606// JavaScript values and ExternalInterface methods that match HTML5 video properties methods 607// http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html 608// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html 609mejs.PluginMediaElement.prototype = { 610 611 // special 612 pluginElement: null, 613 pluginType: '', 614 isFullScreen: false, 615 616 // not implemented :( 617 playbackRate: -1, 618 defaultPlaybackRate: -1, 619 seekable: [], 620 played: [], 621 622 // HTML5 read-only properties 623 paused: true, 624 ended: false, 625 seeking: false, 626 duration: 0, 627 error: null, 628 tagName: '', 629 630 // HTML5 get/set properties, but only set (updated by event handlers) 631 muted: false, 632 volume: 1, 633 currentTime: 0, 634 635 // HTML5 methods 636 play: function () { 637 if (this.pluginApi != null) { 638 if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') { 639 this.pluginApi.playVideo(); 640 } else { 641 this.pluginApi.playMedia(); 642 } 643 this.paused = false; 644 } 645 }, 646 load: function () { 647 if (this.pluginApi != null) { 648 if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') { 649 } else { 650 this.pluginApi.loadMedia(); 651 } 652 653 this.paused = false; 654 } 655 }, 656 pause: function () { 657 if (this.pluginApi != null) { 658 if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') { 659 this.pluginApi.pauseVideo(); 660 } else { 661 this.pluginApi.pauseMedia(); 662 } 663 664 665 this.paused = true; 666 } 667 }, 668 stop: function () { 669 if (this.pluginApi != null) { 670 if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') { 671 this.pluginApi.stopVideo(); 672 } else { 673 this.pluginApi.stopMedia(); 674 } 675 this.paused = true; 676 } 677 }, 678 canPlayType: function(type) { 679 var i, 680 j, 681 pluginInfo, 682 pluginVersions = mejs.plugins[this.pluginType]; 683 684 for (i=0; i<pluginVersions.length; i++) { 685 pluginInfo = pluginVersions[i]; 686 687 // test if user has the correct plugin version 688 if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) { 689 690 // test for plugin playback types 691 for (j=0; j<pluginInfo.types.length; j++) { 692 // find plugin that can play the type 693 if (type == pluginInfo.types[j]) { 694 return 'probably'; 695 } 696 } 697 } 698 } 699 700 return ''; 701 }, 702 703 positionFullscreenButton: function(x,y,visibleAndAbove) { 704 if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) { 705 this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove); 706 } 707 }, 708 709 hideFullscreenButton: function() { 710 if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) { 711 this.pluginApi.hideFullscreenButton(); 712 } 713 }, 714 715 716 // custom methods since not all JavaScript implementations supp
716ort get/set 717 718 // This can be a url string 719 // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}] 720 setSrc: function (url) { 721 if (typeof url == 'string') { 722 this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url)); 723 this.src = mejs.Utility.absolutizeUrl(url); 724 } else { 725 var i, media; 726 727 for (i=0; i<url.length; i++) { 728 media = url[i]; 729 if (this.canPlayType(media.type)) { 730 this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src)); 731 this.src = mejs.Utility.absolutizeUrl(media.src); 732 break; 733 } 734 } 735 } 736 737 }, 738 setCurrentTime: function (time) { 739 if (this.pluginApi != null) { 740 if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') { 741 this.pluginApi.seekTo(time); 742 } else { 743 this.pluginApi.setCurrentTime(time); 744 } 745 746 747 748 this.currentTime = time; 749 } 750 }, 751 setVolume: function (volume) { 752 if (this.pluginApi != null) { 753 // same on YouTube and MEjs 754 if (this.pluginType == 'youtube') { 755 this.pluginApi.setVolume(volume * 100); 756 } else { 757 this.pluginApi.setVolume(volume); 758 } 759 this.volume = volume; 760 } 761 }, 762 setMuted: function (muted) { 763 if (this.pluginApi != null) { 764 if (this.pluginType == 'youtube') { 765 if (muted) { 766 this.pluginApi.mute(); 767 } else { 768 this.pluginApi.unMute(); 769 } 770 this.muted = muted; 771 this.dispatchEvent({type:'volumechange'}); 772 } else { 773 this.pluginApi.setMuted(muted); 774 } 775 this.muted = muted; 776 } 777 }, 778 779 // additional non-HTML5 methods 780 setVideoSize: function (width, height) { 781 782 //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') { 783 if (this.pluginElement && this.pluginElement.style) { 784 this.pluginElement.style.width = width + 'px'; 785 this.pluginElement.style.height = height + 'px'; 786 } 787 if (this.pluginApi != null && this.pluginApi.setVideoSize) { 788 this.pluginApi.setVideoSize(width, height); 789 } 790 //} 791 }, 792 793 setFullscreen: function (fullscreen) { 794 if (this.pluginApi != null && this.pluginApi.setFullscreen) { 795 this.pluginApi.setFullscreen(fullscreen); 796 } 797 }, 798 799 enterFullScreen: function() { 800 if (this.pluginApi != null && this.pluginApi.setFullscreen) { 801 this.setFullscreen(true); 802 } 803 804 }, 805 806 exitFullScreen: function() { 807 if (this.pluginApi != null && this.pluginApi.setFullscreen) { 808 this.setFullscreen(false); 809 } 810 }, 811 812 // start: fake events 813 addEventListener: function (eventName, callback, bubble) { 814 this.events[eventName] = this.events[eventName] || []; 815 this.events[eventName].push(callback); 816 }, 817 removeEventListener: function (eventName, callback) { 818 if (!eventName) { this.events = {}; return true; } 819 var callbacks = this.events[eventName]; 820 if (!callbacks) return true; 821 if (!callback) { this.events[eventName] = []; return true; } 822 for (var i = 0; i < callbacks.length; i++) { 823 if (callbacks[i] === callback) { 824 this.events[eventName].splice(i, 1); 825 return true; 826 } 827 } 828 return false; 829 }, 830 dispatchEvent: function (event) { 831 var i, 832 args, 833 callbacks = this.events[event.type]; 834 835 if (callbacks) { 836 for (i = 0; i < callbacks.length; i++) { 837 callbacks[i].apply(this, [event]); 838 } 839 } 840 }, 841 // end: fake events 842 843 // fake DOM attribute methods 844 hasAttribute: function(name){ 845 return (name in this.attributes); 846 }, 847 removeAttribute: function(name){ 848 delete this.attributes[name]; 849 }, 850 getAttribute: function(name){ 851 if (this.hasAttribute(name)) { 852 return this.attributes[name]; 853 } 854 return ''; 855 }, 856 setAttribute: function(name, value){ 857 this.attributes[name] = value; 858 }, 859 860 remove: function() { 861 mejs.Utility.removeSwf(this.pluginElement.id); 862 mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id); 863 } 864}; 865 866// Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties 867mejs.MediaPluginBridge = { 868 869 pluginMediaElements:{}, 870 htmlMediaElements:{}, 871 872 registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) { 873 this.pluginMediaElements[id] = pluginMediaElement; 874 this.htmlMediaElements[id] = htmlMediaElement; 875 }, 876 877 unregisterPluginElement: function (id) { 878 delete this.pluginMediaElements[id]; 879 delete this.htmlMediaElements[id]; 880 }, 881 882 // when Flash/Silverlight is ready, it calls out to this method 883 initPlugin: function (id) { 884 885 var pluginMediaElement = this.pluginMediaElements[id], 886 htmlMediaElement = this.htmlMediaElements[id]; 887 888 if (pluginMediaElement) { 889 // find the javascript bridge 890 switch (pluginMediaElement.pluginType) { 891 case "flash": 892 pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id); 893 break; 894 case "silverlight": 895 pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id); 896 pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS; 897 break; 898 } 899 900 if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) { 901 pluginMediaElement.success(pluginMediaElement, htmlMediaElement); 902 } 903 } 904 }, 905 906 // receives events from Flash/Silverlight and sends them out as HTML5 media events 907 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html 908 fireEvent: function (id, eventName, values) { 909 910 var 911 e, 912 i, 913 bufferedTime, 914 pluginMediaElement = this.pluginMediaElements[id]; 915 916 if(!pluginMediaElement){ 917 return; 918 } 919 920 // fake event object to mimic real HTML media event. 921 e = { 922 type: eventName, 923 target: pluginMediaElement 924 }; 925 926 // attach all values to element and event object 927 for (i in values) { 928 pluginMediaElement[i] = values[i]; 929 e[i] = values[i]; 930 } 931 932 // fake the newer W3C buffered TimeRange (loaded and total have been removed) 933 bufferedTime = values.bufferedTime || 0; 934 935 e.target.buffered = e.buffered = { 936 start: function(index) { 937 return 0; 938 }, 939 end: function (index) { 940 return bufferedTime; 941 }, 942 length: 1 943 }; 944 945 pluginMediaElement.dispatchEvent(e); 946 } 947}; 948 949/* 950Default options 951*/ 952mejs.MediaElementDefaults = { 953 // allows testing on HTML5, flash, silverlight 954 // auto: attempts to detect what the browser can do 955 // auto_plugin: prefer plugins and then attempt native HTML5 956 // native: forces HTML5 playback 957 // shim: disallows HTML5, will attempt either Flash or Silverlight 958 // none: forces fallback view 959 mode: 'auto', 960 // remove or reorder to change plugin priority and availability 961 plugins: ['flash','silverlight','youtube','vimeo'], 962 // shows debug errors on screen 963 enablePluginDebug: false, 964 // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites 965 httpsBasicAuthSite: false, 966 // overrides the type specified, useful for dynamic instantiation 967 type: '', 968 // path to Flash and Silverlight plugins 969 pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']), 970 // name of flash file 971 flashName: 'flashmediaelement.swf', 972 // streamer for RTMP streaming 973 flashStreamer: '', 974 // set to 'always' for CDN version 975 flashScriptAccess: 'sameDomain', 976 // turns on the smoothing filter in Flash 977 enablePluginSmoothing: false, 978 // enabled pseudo-streaming (seek) on .mp4 files 979 enablePseudoStreaming: false, 980 // start query parameter sent to server for pseudo-streaming 981 pseudoStreamingStartQueryParam: 'start', 982 // name of silverlight file 983 silverlightName: 'silverlightmediaelement.xap', 984 // default if the <video width> is not specified 985 defaultVideoWidth: 480, 986 // default if the <video height> is not specified 987 defaultVideoHeight: 270, 988 // overrides <video width> 989 pluginWidth: -1, 990 // overrides <video height> 991 pluginHeight: -1, 992 // additional plugin variables in 'key=value' form 993 pluginVars: [], 994 // rate in milliseconds for Flash and Silverlight to fire the timeupdate event 995 // larger number is less accurate, but less strain on plugin->JavaScript bridge 996 timerRate: 250, 997 // initial volume for player 998 startVolume: 0.8, 999 success: function () { }, 1000 error: function () { } 1001}; 1002 1003/* 1004Determines if a browser supports the <video> or <audio> element
1005and returns either the native element or a Flash/Silverlight version that 1006mimics HTML5 MediaElement 1007*/ 1008mejs.MediaElement = function (el, o) { 1009 return mejs.HtmlMediaElementShim.create(el,o); 1010}; 1011 1012mejs.HtmlMediaElementShim = { 1013 1014 create: function(el, o) { 1015 var 1016 options = {}, 1017 htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el, 1018 tagName = htmlMediaElement.tagName.toLowerCase(), 1019 isMediaTag = (tagName === 'audio' || tagName === 'video'), 1020 src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'), 1021 poster = htmlMediaElement.getAttribute('poster'), 1022 autoplay = htmlMediaElement.getAttribute('autoplay'), 1023 preload = htmlMediaElement.getAttribute('preload'), 1024 controls = htmlMediaElement.getAttribute('controls'), 1025 playback, 1026 prop; 1027 1028 // extend options 1029 for (prop in mejs.MediaElementDefaults) { 1030 options[prop] = mejs.MediaElementDefaults[prop]; 1031 } 1032 for (prop in o) { 1033 options[prop] = o[prop]; 1034 } 1035 1036 1037 // clean up attributes 1038 src = (typeof src == 'undefined' || src === null || src == '') ? null : src; 1039 poster = (typeof poster == 'undefined' || poster === null) ? '' : poster; 1040 preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload; 1041 autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false'); 1042 controls = !(typeof controls == 'undefined' || controls === null || controls === 'false'); 1043 1044 // test for HTML5 and plugin capabilities 1045 playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src); 1046 playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : ''; 1047 1048 if (playback.method == 'native') { 1049 // second fix for android 1050 if (mejs.MediaFeatures.isBustedAndroid) { 1051 htmlMediaElement.src = playback.url; 1052 htmlMediaElement.addEventListener('click', function() { 1053 htmlMediaElement.play(); 1054 }, false); 1055 } 1056 1057 // add methods to native HTMLMediaElement 1058 return this.updateNative(playback, options, autoplay, preload); 1059 } else if (playback.method !== '') { 1060 // create plugin to mimic HTMLMediaElement 1061 1062 return this.createPlugin( playback, options, poster, autoplay, preload, controls); 1063 } else { 1064 // boo, no HTML5, no Flash, no Silverlight. 1065 this.createErrorMessage( playback, options, poster ); 1066 1067 return this; 1068 } 1069 }, 1070 1071 determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) { 1072 var 1073 mediaFiles = [], 1074 i, 1075 j, 1076 k, 1077 l, 1078 n, 1079 type, 1080 result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')}, 1081 pluginName, 1082 pluginVersions, 1083 pluginInfo, 1084 dummy, 1085 media; 1086 1087 // STEP 1: Get URL and type from <video src> or <source src> 1088 1089 // supplied type overrides <video type> and <source type> 1090 if (typeof options.type != 'undefined' && options.type !== '') { 1091 1092 // accept either string or array of types 1093 if (typeof options.type == 'string') { 1094 mediaFiles.push({type:options.type, url:src}); 1095 } else { 1096 1097 for (i=0; i<options.type.length; i++) { 1098 mediaFiles.push({type:options.type[i], url:src}); 1099 } 1100 } 1101 1102 // test for src attribute first 1103 } else if (src !== null) { 1104 type = this.formatType(src, htmlMediaElement.getAttribute('type')); 1105 mediaFiles.push({type:type, url:src}); 1106 1107 // then test for <source> elements 1108 } else { 1109 // test <source> types to see if they are usable 1110 for (i = 0; i < htmlMediaElement.childNodes.length; i++) { 1111 n = htmlMediaElement.childNodes[i]; 1112 if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') { 1113 src = n.getAttribute('src'); 1114 type = this.formatType(src, n.getAttribute('type')); 1115 media = n.getAttribute('media'); 1116 1117 if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) { 1118 mediaFiles.push({type:type, url:src}); 1119 } 1120 } 1121 } 1122 } 1123 1124 // in the case of dynamicly created players 1125 // check for audio types 1126 if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) { 1127 result.isVideo = false;
1128 } 1129 1130 1131 // STEP 2: Test for playback method 1132 1133 // special case for Android which sadly doesn't implement the canPlayType function (always returns '') 1134 if (mejs.MediaFeatures.isBustedAndroid) { 1135 htmlMediaElement.canPlayType = function(type) { 1136 return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : ''; 1137 }; 1138 } 1139 1140 // special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora) 1141 if (mejs.MediaFeatures.isChromium) { 1142 htmlMediaElement.canPlayType = function(type) { 1143 return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : ''; 1144 }; 1145 } 1146 1147 // test for native playback first 1148 if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) { 1149 1150 if (!isMediaTag) { 1151 1152 // create a real HTML5 Media Element 1153 dummy = document.createElement( result.isVideo ? 'video' : 'audio'); 1154 htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement); 1155 htmlMediaElement.style.display = 'none'; 1156 1157 // use this one from now on 1158 result.htmlMediaElement = htmlMediaElement = dummy; 1159 } 1160 1161 for (i=0; i<mediaFiles.length; i++) { 1162 // normal check 1163 if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== '' 1164 // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg') 1165 || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '' 1166 // special case for m4a supported by detecting mp4 support 1167 || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).replace(/no/, '') !== '') { 1168 result.method = 'native'; 1169 result.url = mediaFiles[i].url; 1170 break; 1171 } 1172 } 1173 1174 if (result.method === 'native') { 1175 if (result.url !== null) { 1176 htmlMediaElement.src = result.url; 1177 } 1178 1179 // if `auto_plugin` mode, then cache the native result but try plugins. 1180 if (options.mode !== 'auto_plugin') { 1181 return result; 1182 } 1183 } 1184 } 1185 1186 // if native playback didn't work, then test plugins 1187 if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') { 1188 for (i=0; i<mediaFiles.length; i++) { 1189 type = mediaFiles[i].type; 1190 1191 // test all plugins in order of preference [silverlight, flash] 1192 for (j=0; j<options.plugins.length; j++) { 1193 1194 pluginName = options.plugins[j]; 1195 1196 // test version of plugin (for future features) 1197 pluginVersions = mejs.plugins[pluginName]; 1198 1199 for (k=0; k<pluginVersions.length; k++) { 1200 pluginInfo = pluginVersions[k]; 1201 1202 // test if user has the correct plugin version 1203 1204 // for youtube/vimeo 1205 if (pluginInfo.version == null || 1206 1207 mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) { 1208 1209 // test for plugin playback types 1210 for (l=0; l<pluginInfo.types.length; l++) { 1211 // find plugin that can play the type 1212 if (type.toLowerCase() == pluginInfo.types[l].toLowerCase()) { 1213 result.method = pluginName; 1214 result.url = mediaFiles[i].url; 1215 return result; 1216 } 1217 } 1218 } 1219 } 1220 } 1221 } 1222 } 1223 1224 // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed. 1225 // if we have native support then return that. 1226 if (options.mode === 'auto_plugin' && result.method === 'native') { 1227 return result; 1228 } 1229 1230 // what if there's nothing to play? just grab the first available 1231 if (result.method === '' && mediaFiles.length > 0) { 1232 result.url = mediaFiles[0].url; 1233 } 1234 1235 return result; 1236 }, 1237 1238 formatType: function(url, type) { 1239 // if no type is supplied, fake it with the extension 1240 if (url && !type) { 1241 return this.getTypeFromFile(url); 1242 } else { 1243 // only return the mime part of the type in case the attribute contains the codec 1244 // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element 1245 // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4` 1246 1247 if (type && ~type.indexOf(';')) { 1248 return type.substr(0, type.indexOf(';')); 1249 } else { 1250 return type; 1251 } 1252 } 1253 }, 1254 1255 getTypeFromFile: function(url) { 1256 url = url.split('?')[0]; 1257 var 1258 ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(), 1259 av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/'; 1260 return this.getTypeFromExtension(ext, av); 1261 }, 1262 1263 getTypeFromExtension: function(ext, av) { 1264 av = av || ''; 1265 1266 switch (ext) { 1267 case 'mp4': 1268 case 'm4v': 1269 case 'm4a': 1270 case 'f4v': 1271 case 'f4a': 1272 return av + 'mp4'; 1273 case 'flv': 1274 return av + 'x-flv'; 1275 case 'webm': 1276 case 'webma': 1277 case 'webmv': 1278 return av + 'webm'; 1279 case 'ogg': 1280 case 'oga': 1281 case 'ogv': 1282 return av + 'ogg'; 1283 case 'm3u8': 1284 return 'application/x-mpegurl'; 1285 case 'ts': 1286 return av + 'mp2t'; 1287 default: 1288 return av + ext; 1289 } 1290 }, 1291 1292 createErrorMessage: function(playback, options, poster) { 1293 var 1294 htmlMediaElement = playback.htmlMediaElement, 1295 errorContainer = document.createElement('div'), 1296 errorContent = options.customError; 1297 1298 errorContainer.className = 'me-cannotplay'; 1299 1300 try { 1301 errorContainer.style.width = htmlMediaElement.width + 'px'; 1302 errorContainer.style.height = htmlMediaElement.height + 'px'; 1303 } catch (e) {} 1304 1305 if (!errorContent) { 1306 errorContent = '<a href="' + playback.url + '">'; 1307 1308 if (poster !== '') { 1309 errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />'; 1310 } 1311 1312 errorContent += '<span>' + mejs.i18n.t('Download File') + '</span></a>'; 1313 } 1314 1315 errorContainer.innerHTML = errorContent; 1316 1317 htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement); 1318 htmlMediaElement.style.display = 'none'; 1319 1320 options.error(htmlMediaElement); 1321 }, 1322 1323 createPlugin:function(playback, options, poster, autoplay, preload, controls) { 1324 var 1325 htmlMediaElement = playback.htmlMediaElement, 1326 width = 1, 1327 height = 1, 1328 pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++), 1329 pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url), 1330 container = document.createElement('div'), 1331 specialIEContainer, 1332 node, 1333 initVars; 1334 1335 // copy tagName from html media element 1336 pluginMediaElement.tagName = htmlMediaElement.tagName 1337 1338 // copy attributes from html media element to plugin media element 1339 for (var i = 0; i < htmlMediaElement.attributes.length; i++) { 1340 var attribute = htmlMediaElement.attributes[i]; 1341 if (attribute.specified) { 1342 pluginMediaElement.setAttribute(attribute.name, attribute.value); 1343 } 1344 } 1345 1346 // check for placement inside a <p> tag (sometimes WYSIWYG editors do this) 1347 node = htmlMediaElement.parentNode; 1348 1349 while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' && 1350 node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") { 1351 if (node.parentNode.tagName.toLowerCase() === 'p') { 1352 node.parentNode.parentNode.insertBefore(node, node.parentNode); 1353 break; 1354 } 1355 node = node.parentNode; 1356 } 1357 1358 if (playback.isVideo) { 1359 width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth; 1360 height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight; 1361 1362 // in case of '%' make sure it's encoded 1363 width = mejs.Utility.encodeUrl(width); 1364 height = mejs.Utility.encodeUrl(height); 1365 1366 } else { 1367 if (options.enablePluginDebug) { 1368 width = 320; 1369 height = 240; 1370 } 1371 } 1372 1373 // register plugin 1374 pluginMediaElement.success = options.success; 1375 mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement); 1376 1377 // add container (must be added to DOM before inserting HTML for IE) 1378 container.className = 'me-plugin'; 1379 container.id = pluginid + '_container'; 1380 1381 if (playback.isVideo) { 1382 htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement); 1383 } else { 1384 document.body.insertBefore(container, document.body.childNodes[0]); 1385 } 1386 1387 // flash/silverlight vars 1388 initVars = [
1389 'id=' + pluginid, 1390 'jsinitfunction=' + "mejs.MediaPluginBridge.initPlugin", 1391 'jscallbackfunction=' + "mejs.MediaPluginBridge.fireEvent", 1392 'isvideo=' + ((playback.isVideo) ? "true" : "false"), 1393 'autoplay=' + ((autoplay) ? "true" : "false"), 1394 'preload=' + preload, 1395 'width=' + width, 1396 'startvolume=' + options.startVolume, 1397 'timerrate=' + options.timerRate, 1398 'flashstreamer=' + options.flashStreamer, 1399 'height=' + height, 1400 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam]; 1401 1402 if (playback.url !== null) { 1403 if (playback.method == 'flash') { 1404 initVars.push('file=' + mejs.Utility.encodeUrl(playback.url)); 1405 } else { 1406 initVars.push('file=' + playback.url); 1407 } 1408 } 1409 if (options.enablePluginDebug) { 1410 initVars.push('debug=true'); 1411 } 1412 if (options.enablePluginSmoothing) { 1413 initVars.push('smoothing=true'); 1414 } 1415 if (options.enablePseudoStreaming) { 1416 initVars.push('pseudostreaming=true'); 1417 } 1418 if (controls) { 1419 initVars.push('controls=true'); // shows controls in the plugin if desired 1420 } 1421 if (options.pluginVars) { 1422 initVars = initVars.concat(options.pluginVars); 1423 } 1424 1425 switch (playback.method) { 1426 case 'silverlight': 1427 container.innerHTML = 1428'<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' + 1429'<param name="initParams" value="' + initVars.join(',') + '" />' + 1430'<param name="windowless" value="true" />' + 1431'<param name="background" value="black" />' + 1432'<param name="minRuntimeVersion" value="3.0.0.0" />' + 1433'<param name="autoUpgrade" value="true" />' + 1434'<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' + 1435'</object>'; 1436 break; 1437 1438 case 'flash': 1439 1440 if (mejs.MediaFeatures.isIE) { 1441 specialIEContainer = document.createElement('div'); 1442 container.appendChild(specialIEContainer); 1443 specialIEContainer.outerHTML = 1444'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' + 1445'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' + 1446'<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' + 1447'<param name="flashvars" value="' + initVars.join('&') + '" />' + 1448'<param name="quality" value="high" />' + 1449'<param name="bgcolor" value="#000000" />' + 1450'<param name="wmode" value="transparent" />' + 1451'<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' + 1452'<param name="allowFullScreen" value="true" />' + 1453'<param name="scale" value="default" />' + 1454'</object>'; 1455 1456 } else { 1457 1458 container.innerHTML = 1459'<embed id="' + pluginid + '" name="' + pluginid + '" ' + 1460'play="true" ' + 1461'loop="false" ' + 1462'quality="high" ' + 1463'bgcolor="#000000" ' + 1464'wmode="transparent" ' + 1465'allowScriptAccess="' + options.flashScriptAccess + '" ' + 1466'allowFullScreen="true" ' + 1467'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' + 1468'src="' + options.pluginPath + options.flashName + '" ' + 1469'flashvars="' + initVars.join('&') + '" ' + 1470'width="' + width + '" ' + 1471'height="' + height + '" ' + 1472'scale="default"' + 1473'class="mejs-shim"></embed>'; 1474 } 1475 break; 1476 1477 case 'youtube': 1478 1479 1480 var videoId; 1481 // youtu.be url from share button 1482 if (playback.url.lastIndexOf("youtu.be") != -1) { 1483 videoId = playback.url.substr(playback.url.lastIndexOf('/')+1); 1484 if (videoId.indexOf('?') != -1) { 1485 videoId = videoId.substr(0, videoId.indexOf('?')); 1486 } 1487 } 1488 else { 1489 videoId = playback.url.substr(playback.url.lastIndexOf('=')+1); 1490 } 1491 youtubeSettings = { 1492 container: container, 1493 containerId: container.id, 1494 pluginMediaElement: pluginMediaElement, 1495 pluginId: pluginid, 1496 videoId: videoId, 1497 height: height, 1498 width: width 1499 }; 1500 1501 if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) { 1502 mejs.YouTubeApi.createFlash(youtubeSettings, options); 1503 } else { 1504 mejs.YouTubeApi.enqueueIframe(youtubeSettings); 1505 } 1506 1507 break; 1508 1509 // DEMO Code. Does NOT work. 1510 case 'vimeo': 1511 var player_id = pluginid + "_player"; 1512 pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1); 1513 1514 container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'; 1515 if (typeof($f) == 'function') { // froogaloop available 1516 var player = $f(container.childNodes[0]); 1517 1518 player.addEvent('ready', function() { 1519 1520 player.playVideo = function() { 1521 player.api( 'play' ); 1522 } 1523 player.stopVideo = function() { 1524 player.api( 'unload' ); 1525 } 1526 player.pauseVideo = function() { 1527 player.api( 'pause' ); 1528 } 1529 player.seekTo = function( seconds ) { 1530 player.api( 'seekTo', seconds ); 1531 } 1532 player.setVolume = function( volume ) { 1533 player.api( 'setVolume', volume ); 1534 } 1535 player.setMuted = function( muted ) { 1536 if( muted ) { 1537 player.lastVolume = player.api( 'getVolume' );
1538 player.api( 'setVolume', 0 ); 1539 } else { 1540 player.api( 'setVolume', player.lastVolume ); 1541 delete player.lastVolume; 1542 } 1543 } 1544 1545 function createEvent(player, pluginMediaElement, eventName, e) { 1546 var event = { 1547 type: eventName, 1548 target: pluginMediaElement 1549 }; 1550 if (eventName == 'timeupdate') { 1551 pluginMediaElement.currentTime = event.currentTime = e.seconds; 1552 pluginMediaElement.duration = event.duration = e.duration; 1553 } 1554 pluginMediaElement.dispatchEvent(event); 1555 } 1556 1557 player.addEvent('play', function() { 1558 createEvent(player, pluginMediaElement, 'play'); 1559 createEvent(player, pluginMediaElement, 'playing'); 1560 }); 1561 1562 player.addEvent('pause', function() { 1563 createEvent(player, pluginMediaElement, 'pause'); 1564 }); 1565 1566 player.addEvent('finish', function() { 1567 createEvent(player, pluginMediaElement, 'ended'); 1568 }); 1569 1570 player.addEvent('playProgress', function(e) { 1571 createEvent(player, pluginMediaElement, 'timeupdate', e); 1572 }); 1573 1574 pluginMediaElement.pluginElement = container; 1575 pluginMediaElement.pluginApi = player; 1576 1577 // init mejs 1578 mejs.MediaPluginBridge.initPlugin(pluginid); 1579 }); 1580 } 1581 else { 1582 console.warn("You need to include froogaloop for vimeo to work"); 1583 } 1584 break; 1585 } 1586 // hide original element 1587 htmlMediaElement.style.display = 'none'; 1588 // prevent browser from autoplaying when using a plugin 1589 htmlMediaElement.removeAttribute('autoplay'); 1590 1591 // FYI: options.success will be fired by the MediaPluginBridge 1592 1593 return pluginMediaElement; 1594 }, 1595 1596 updateNative: function(playback, options, autoplay, preload) { 1597 1598 var htmlMediaElement = playback.htmlMediaElement, 1599 m; 1600 1601 1602 // add methods to video object to bring it into parity with Flash Object 1603 for (m in mejs.HtmlMediaElement) { 1604 htmlMediaElement[m] = mejs.HtmlMediaElement[m]; 1605 } 1606 1607 /* 1608 Chrome now supports preload="none" 1609 if (mejs.MediaFeatures.isChrome) { 1610 1611 // special case to enforce preload attribute (Chrome doesn't respect this) 1612 if (preload === 'none' && !autoplay) { 1613 1614 // forces the browser to stop loading (note: fails in IE9) 1615 htmlMediaElement.src = ''; 1616 htmlMediaElement.load(); 1617 htmlMediaElement.canceledPreload = true; 1618 1619 htmlMediaElement.addEventListener('play',function() { 1620 if (htmlMediaElement.canceledPreload) { 1621 htmlMediaElement.src = playback.url; 1622 htmlMediaElement.load(); 1623 htmlMediaElement.play(); 1624 htmlMediaElement.canceledPreload = false; 1625 } 1626 }, false); 1627 // for some reason Chrome forgets how to autoplay sometimes. 1628 } else if (autoplay) { 1629 htmlMediaElement.load(); 1630 htmlMediaElement.play(); 1631 } 1632 } 1633 */ 1634 1635 // fire success code 1636 options.success(htmlMediaElement, htmlMediaElement); 1637 1638 return htmlMediaElement; 1639 } 1640}; 1641 1642/* 1643 - test on IE (object vs. embed) 1644 - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE) 1645 - fullscreen? 1646*/ 1647 1648// YouTube Flash and Iframe API 1649mejs.YouTubeApi = { 1650 isIframeStarted: false, 1651 isIframeLoaded: false, 1652 loadIframeApi: function() { 1653 if (!this.isIframeStarted) { 1654 var tag = document.createElement('script'); 1655 tag.src = "//www.youtube.com/player_api"; 1656 var firstScriptTag = document.getElementsByTagName('script')[0]; 1657 firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 1658 this.isIframeStarted = true; 1659 } 1660 }, 1661 iframeQueue: [], 1662 enqueueIframe: function(yt) { 1663 1664 if (this.isLoaded) { 1665 this.createIframe(yt); 1666 } else { 1667 this.loadIframeApi(); 1668 this.iframeQueue.push(yt); 1669 } 1670 }, 1671 createIframe: function(settings) { 1672 1673 var 1674 pluginMediaElement = settings.pluginMediaElement, 1675 player = new YT.Player(settings.containerId, { 1676 height: settings.height, 1677 width: settings.width, 1678 videoId: settings.videoId, 1679 playerVars: {controls:0}, 1680 events: { 1681 'onReady': function() { 1682 1683 // hook up iframe object to MEjs 1684 settings.pluginMediaElement.pluginApi = player; 1685 1686 // init mejs 1687 mejs.MediaPluginBridge.initPlugin(settings.pluginId); 1688 1689 // create timer 1690 setInterval(function() { 1691 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate'); 1692 }, 250); 1693 }, 1694 'onStateChange': function(e) { 1695 1696 mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement); 1697 1698 } 1699 } 1700 }); 1701 }, 1702 1703 createEvent: function (player, pluginMediaElement, eventName) { 1704 var event = { 1705 type: eventName, 1706 target: pluginMediaElement 1707 }; 1708 1709 if (player && player.getDuration) { 1710 1711 // time 1712 pluginMediaElement.currentTime = event.currentTime = player.getCurrentTime(); 1713 pluginMediaElement.duration = event.duration = player.getDuration(); 1714 1715 // state 1716 event.paused = pluginMediaElement.paused; 1717 event.ended = pluginMediaElement.ended; 1718 1719 // sound 1720 event.muted = player.isMuted(); 1721 event.volume = player.getVolume() / 100; 1722 1723 // progress 1724 event.bytesTotal = player.getVideoBytesTotal(); 1725 event.bufferedBytes = player.getVideoBytesLoaded(); 1726 1727 // fake the W3C buffered TimeRange 1728 var bufferedTime = event.bufferedBytes / event.bytesTotal * event.duration; 1729 1730 event.target.buffered = event.buffered = { 1731 start: function(index) { 1732 return 0; 1733 }, 1734 end: function (index) { 1735 return bufferedTime; 1736 }, 1737 length: 1 1738 }; 1739 1740 } 1741 1742 // send event up the chain 1743 pluginMediaElement.dispatchEvent(event); 1744 }, 1745 1746 iFrameReady: function() { 1747 1748 this.isLoaded = true; 1749 this.isIframeLoaded = true; 1750 1751 while (this.iframeQueue.length > 0) { 1752 var settings = this.iframeQueue.pop(); 1753 this.createIframe(settings); 1754 } 1755 }, 1756 1757 // FLASH! 1758 flashPlayers: {}, 1759 createFlash: function(settings) { 1760 1761 this.flashPlayers[settings.pluginId] = settings; 1762 1763 /* 1764 settings.container.innerHTML = 1765 '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings.pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0" ' + 1766 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' + 1767 '<param name="allowScriptAccess" value="sameDomain">' + 1768 '<param name="wmode" value="transparent">' + 1769 '</object>'; 1770 */ 1771 1772 var specialIEContainer, 1773 youtubeUrl = '//www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings.pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0'; 1774 1775 if (mejs.MediaFeatures.isIE) { 1776 1777 specialIEContainer = document.createElement('div'); 1778 settings.container.appendChild(specialIEContainer); 1779 specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/s
1779hockwave/cabs/flash/swflash.cab" ' + 1780'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' + 1781 '<param name="movie" value="' + youtubeUrl + '" />' + 1782 '<param name="wmode" value="transparent" />' + 1783 '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' + 1784 '<param name="allowFullScreen" value="true" />' + 1785'</object>'; 1786 } else { 1787 settings.container.innerHTML = 1788 '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' + 1789 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' + 1790 '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '">' + 1791 '<param name="wmode" value="transparent">' + 1792 '</object>'; 1793 } 1794 1795 }, 1796 1797 flashReady: function(id) { 1798 var 1799 settings = this.flashPlayers[id], 1800 player = document.getElementById(id), 1801 pluginMediaElement = settings.pluginMediaElement; 1802 1803 // hook up and return to MediaELementPlayer.success 1804 pluginMediaElement.pluginApi = 1805 pluginMediaElement.pluginElement = player; 1806 mejs.MediaPluginBridge.initPlugin(id); 1807 1808 // load the youtube video 1809 player.cueVideoById(settings.videoId); 1810 1811 var callbackName = settings.containerId + '_callback'; 1812 1813 window[callbackName] = function(e) { 1814 mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement); 1815 } 1816 1817 player.addEventListener('onStateChange', callbackName); 1818 1819 setInterval(function() { 1820 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate'); 1821 }, 250); 1822 1823 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay'); 1824 }, 1825 1826 handleStateChange: function(youTubeState, player, pluginMediaElement) { 1827 switch (youTubeState) { 1828 case -1: // not started 1829 pluginMediaElement.paused = true; 1830 pluginMediaElement.ended = true; 1831 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata'); 1832 //createYouTubeEvent(player, pluginMediaElement, 'loadeddata'); 1833 break; 1834 case 0: 1835 pluginMediaElement.paused = false; 1836 pluginMediaElement.ended = true; 1837 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended'); 1838 break; 1839 case 1: 1840 pluginMediaElement.paused = false; 1841 pluginMediaElement.ended = false; 1842 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play'); 1843 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing'); 1844 break; 1845 case 2: 1846 pluginMediaElement.paused = true; 1847 pluginMediaElement.ended = false; 1848 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause'); 1849 break; 1850 case 3: // buffering 1851 mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress'); 1852 break; 1853 case 5: 1854 // cued? 1855 break; 1856 1857 } 1858 1859 } 1860} 1861// IFRAME 1862window.onYouTubePlayerAPIReady = function() { 1863 mejs.YouTubeApi.iFrameReady(); 1864}; 1865// FLASH 1866window.onYouTubePlayerReady = function(id) { 1867 mejs.YouTubeApi.flashReady(id); 1868}; 1869 1870window.mejs = mejs; 1871window.MediaElement = mejs.MediaElement; 1872 1873/* 1874 * Adds Internationalization and localization to mediaelement. 1875 * 1876 * This file does not contain translations, you have to add them manually. 1877 * The schema is always the same: me-i18n-locale-[IETF-language-tag].js 1878 * 1879 * Examples are provided both for german and chinese translation. 1880 * 1881 * 1882 * What is the concept beyond i18n? 1883 * http://en.wikipedia.org/wiki/Internationalization_and_localization 1884 * 1885 * What langcode should i use? 1886 * http://en.wikipedia.org/wiki/IETF_language_tag 1887 * https://tools.ietf.org/html/rfc5646 1888 * 1889 * 1890 * License? 1891 * 1892 * The i18n file uses methods from the Drupal project (drupal.js): 1893 * - i18n.methods.t() (modified) 1894 * - i18n.methods.checkPlain() (full copy) 1895 * 1896 * The Drupal project is (like mediaelementjs) licensed under GPLv2. 1897 * - http://drupal.org/licensing/faq/#q1 1898 * - https://github.com/johndyer/mediaelement 1899 * - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 1900 * 1901 * 1902 * @author 1903 * Tim Latz ([email protected]) 1904 * 1905 * 1906 * @params 1907 * - context - document, iframe .. 1908 * - exports - CommonJS, window .. 1909 * 1910 */ 1911;(function(context, exports, undefined) { 1912 "use strict"; 1913 1914 var i18n = { 1915 "locale": { 1916 // Ensure previous values aren't overwritten. 1917 "language" : (exports.i18n && exports.i18n.locale.language) || '', 1918 "strings" : (exports.i18n && exports.i18n.locale.strings) || {} 1919 }, 1920 "ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/, 1921 "methods" : {} 1922 }; 1923// start i18n 1924 1925 1926 /** 1927 * Get language, fallback to browser's language if empty 1928 * 1929 * IETF: RFC 5646, https://tools.ietf.org/html/rfc5646 1930 * Examples: en, zh-CN, cmn-Hans-CN, sr-Latn-RS, es-419, x-private 1931 */ 1932 i18n.getLanguage = function () { 1933 var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language; 1934 return i18n.ietf_lang_regex.exec(language) ? language : null; 1935 1936 //(WAS: convert to iso 639-1 (2-letters, lower case)) 1937 //return language.substr(0, 2).toLowerCase(); 1938 }; 1939 1940 // i18n fixes for compatibility with WordPress 1941 if ( typeof mejsL10n != 'undefined' ) { 1942 i18n.locale.language = mejsL10n.language; 1943 } 1944 1945 1946 1947 /** 1948 * Encode special characters in a plain-text string for display as HTML. 1949 */ 1950 i18n.methods.checkPlain = function (str) { 1951 var character, regex, 1952 replace = { 1953 '&': '&', 1954 '"': '"', 1955 '<': '<', 1956 '>': '>' 1957 }; 1958 str = String(str); 1959 for (character in replace) { 1960 if (replace.hasOwnProperty(character)) { 1961 regex = new RegExp(character, 'g'); 1962 str = str.replace(regex, replace[character]); 1963 } 1964 } 1965 return str; 1966 }; 1967 1968 /** 1969 * Translate strings to the page language or a given language. 1970 * 1971 * 1972 * @param str 1973 * A string containing the English string to translate. 1974 * 1975 * @param options 1976 * - 'context' (defaults to the default context): The context the source string 1977 * belongs to. 1978 * 1979 * @return 1980 * The translated string, escaped via i18n.methods.checkPlain() 1981 */ 1982 i18n.methods.t = function (str, options) { 1983 1984 // Fetch the localized version of the string. 1985 if (i18n.locale.strings && i18n.locale.strings[options.context] && i18n.locale.strings[options.context][str]) { 1986 str = i18n.locale.strings[options.context][str]; 1987 } 1988 1989 return i18n.methods.checkPlain(str); 1990 }; 1991 1992 1993 /** 1994 * Wrapper for i18n.methods.t() 1995 * 1996 * @see i18n.methods.t() 1997 * @throws InvalidArgumentException 1998 */ 1999 i18n.t = function(str, options) { 2000 2001 if (typeof str === 'string' && str.length > 0) { 2002 2003 // check every time due language can change for 2004 // different reasons (translation, lang switcher ..) 2005 var language = i18n.getLanguage(); 2006 2007 options = options || { 2008 "context" : language 2009 }; 2010 2011 return i18n.methods.t(str, options); 2012 } 2013 else { 2014 throw { 2015 "name" : 'InvalidArgumentException', 2016 "message" : 'First argument is either not a string or empty.' 2017 }; 2018 } 2019 }; 2020 2021// end i18n 2022 exports.i18n = i18n; 2023}(document, mejs)); 2024 2025// i18n fixes for compatibility with WordPress 2026;(function(exports, undefined) { 2027 2028 "use strict"; 2029 2030 if ( typeof mejsL10n != 'undefined' ) { 2031 exports[mejsL10n.language] = mejsL10n.strings; 2032 } 2033 2034}(mejs.i18n.locale.strings)); 2035 2036/*! 2037 * 2038 * MediaElementPlayer 2039 * http://mediaelementjs.com/ 2040 * 2041 * Creates a controller bar for HTML5 <video> add <audio> tags 2042 * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper) 2043 * 2044 * Copyright 2010-2013, John Dyer (http://j.hn/) 2045 * License: MIT 2046 * 2047 */ 2048if (typeof jQuery != 'undefined') { 2049 mejs.$ = jQuery; 2050} else if (typeof Zepto != 'undefined') { 2051 mejs.$ = Zepto; 2052 2053 // define `outerWidth` method which has not been realized in Zepto 2054 Zepto.fn.outerWidth = function(includeMargin) { 2055 var width = $(this).width(); 2056 if (includeMargin) { 2057 width += parseInt($(this).css('margin-right'), 10); 2058 width += parseInt($(this).css('margin-left'), 10); 2059 } 2060 return width 2061 } 2062 2063} else if (typeof ender != 'undefined') { 2064 mejs.$ = ender; 2065} 2066(function ($) { 2067 2068 // default player values 2069 mejs.MepDefaults = { 2070 // url to poster (to fix iOS 3.x) 2071 poster: '', 2072 // When the video is ended, we can show the poster. 2073 showPosterWhenEnded: false, 2074 // default if the <video width> is not specified 2075 defaultVideoWidth: 480, 2076 // default if the <video height> is not specified 2077 defaultVideoHeight: 270, 2078 // if set, overrides <video width> 2079 videoWidth: -1, 2080 // if set, overrides <video height> 2081 videoHeight: -1, 2082 // default if the user doesn't specify 2083 defaultAudioWidth: 400, 2084 // default if the user doesn't specify 2085 defaultAudioHeight: 30, 2086 2087 // default amount to move back when back key is pressed 2088 defaultSeekBackwardInterval: function(media) { 2089 return (media.duration * 0.05); 2090 }, 2091 // default amount to move forward when forward key is pressed 2092 defaultSeekForwardInterval: function(media) { 2093 return (media.duration * 0.05); 2094 }, 2095 2096 // set dimensions via JS instead of CSS 2097 setDimensions: true, 2098 2099 // width of audio player 2100 audioWidth: -1, 2101 // height of audio player 2102 audioHeight: -1, 2103 // initial volume when the player starts (overrided by user cookie) 2104 startVolume: 0.8, 2105 // useful for <audio> player loops 2106 loop: false, 2107 // rewind to beginning when media ends 2108 autoRewind: true, 2109 // resize to media dimensions 2110 enableAutosize: true, 2111 2112 /* 2113 * Time format to use. Default: 'mm:ss' 2114 * Supported units: 2115 * h: hour 2116 * m: minute 2117 * s: second 2118 * f: frame count 2119 * When using 'hh', 'mm', 'ss' or 'ff' we always display 2 digits. 2120 * If you use 'h', 'm', 's' or 'f' we display 1 digit if possible. 2121 * 2122 * Example to display 75 seconds: 2123 * Format 'mm:ss': 01:15 2124 * Format 'm:ss': 1:15 2125 * Format 'm:s': 1:15 2126 */ 2127 timeFormat: '', 2128 // forces the hour marker (##:00:00) 2129 alwaysShowHours: false, 2130 // show framecount in timecode (##:00:00:00)
2131 showTimecodeFrameCount: false, 2132 // used when showTimecodeFrameCount is set to true 2133 framesPerSecond: 25, 2134 2135 // automatically calculate the width of the progress bar based on the sizes of other elements 2136 autosizeProgress : true, 2137 // Hide controls when playing and mouse is not over the video 2138 alwaysShowControls: false, 2139 // Display the video control 2140 hideVideoControlsOnLoad: false, 2141 // Enable click video element to toggle play/pause 2142 clickToPlayPause: true, 2143 // force iPad's native controls 2144 iPadUseNativeControls: false, 2145 // force iPhone's native controls 2146 iPhoneUseNativeControls: false, 2147 // force Android's native controls 2148 AndroidUseNativeControls: false, 2149 // features to show 2150 features: ['playpause','current','progress','duration','tracks','volume','fullscreen'], 2151 // only for dynamic 2152 isVideo: true, 2153 2154 // turns keyboard support on and off for this instance 2155 enableKeyboard: true, 2156 2157 // whenthis player starts, it will pause other players 2158 pauseOtherPlayers: true, 2159 2160 // array of keyboard actions such as play pause 2161 keyActions: [ 2162 { 2163 keys: [ 2164 32, // SPACE 2165 179 // GOOGLE play/pause button 2166 ], 2167 action: function(player, media) { 2168 if (media.paused || media.ended) { 2169 media.play(); 2170 } else { 2171 media.pause(); 2172 } 2173 } 2174 }, 2175 { 2176 keys: [38], // UP 2177 action: function(player, media) { 2178 player.container.find('.mejs-volume-slider').css('display','block'); 2179 if (player.isVideo) { 2180 player.showControls(); 2181 player.startControlsTimer(); 2182 } 2183 2184 var newVolume = Math.min(media.volume + 0.1, 1); 2185 media.setVolume(newVolume); 2186 } 2187 }, 2188 { 2189 keys: [40], // DOWN 2190 action: function(player, media) { 2191 player.container.find('.mejs-volume-slider').css('display','block'); 2192 if (player.isVideo) { 2193 player.showControls(); 2194 player.startControlsTimer(); 2195 } 2196 2197 var newVolume = Math.max(media.volume - 0.1, 0); 2198 media.setVolume(newVolume); 2199 } 2200 }, 2201 { 2202 keys: [ 2203 37, // LEFT 2204 227 // Google TV rewind 2205 ], 2206 action: function(player, media) { 2207 if (!isNaN(media.duration) && media.duration > 0) { 2208 if (player.isVideo) { 2209 player.showControls(); 2210 player.startControlsTimer(); 2211 } 2212 2213 // 5% 2214 var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0); 2215 media.setCurrentTime(newTime); 2216 } 2217 } 2218 }, 2219 { 2220 keys: [ 2221 39, // RIGHT 2222 228 // Google TV forward 2223 ], 2224 action: function(player, media) { 2225 if (!isNaN(media.duration) && media.duration > 0) { 2226 if (player.isVideo) { 2227 player.showControls(); 2228 player.startControlsTimer(); 2229 } 2230 2231 // 5% 2232 var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration); 2233 media.setCurrentTime(newTime); 2234 } 2235 } 2236 }, 2237 { 2238 keys: [70], // F 2239 action: function(player, media) { 2240 if (typeof player.enterFullScreen != 'undefined') { 2241 if (player.isFullScreen) { 2242 player.exitFullScreen(); 2243 } else { 2244 player.enterFullScreen(); 2245 } 2246 } 2247 } 2248 }, 2249 { 2250 keys: [77], // M 2251 action: function(player, media) { 2252 player.container.find('.mejs-volume-slider').css('display','block'); 2253 if (player.isVideo) { 2254 player.showControls(); 2255 player.startControlsTimer(); 2256 } 2257 if (player.media.muted) { 2258 player.setMuted(false); 2259 } else { 2260 player.setMuted(true); 2261 } 2262 } 2263 } 2264 ] 2265 }; 2266 2267 mejs.mepIndex = 0; 2268 2269 mejs.players = {}; 2270 2271 // wraps a MediaElement object in player controls 2272 mejs.MediaElementPlayer = function(node, o) { 2273 // enforce object, even without "new" (via John Resig) 2274 if ( !(this instanceof mejs.MediaElementPlayer) ) { 2275 return new mejs.MediaElementPlayer(node, o); 2276 } 2277 2278 var t = this; 2279 2280 // these will be reset after the MediaElement.success fires 2281 t.$media = t.$node = $(node); 2282 t.node = t.media = t.$media[0]; 2283 2284 if(!t.node) { 2285 return 2286 } 2287 2288 // check for existing player 2289 if (typeof t.node.player != 'undefined') { 2290 return t.node.player; 2291 } 2292 2293 2294 // try to get options from data-mejsoptions 2295 if (typeof o == 'undefined') { 2296 o = t.$node.data('mejsoptions'); 2297 } 2298 2299 // extend default options 2300 t.options = $.extend({},mejs.MepDefaults,o); 2301 2302 if (!t.options.timeFormat) { 2303 // Generate the time format according to options 2304 t.options.timeFormat = 'mm:ss'; 2305 if (t.options.alwaysShowHours) { 2306 t.options.timeFormat = 'hh:mm:ss'; 2307 }
2308 if (t.options.showTimecodeFrameCount) { 2309 t.options.timeFormat += ':ff'; 2310 } 2311 } 2312 2313 mejs.Utility.calculateTimeFormat(0, t.options, t.options.framesPerSecond || 25); 2314 2315 // unique ID 2316 t.id = 'mep_' + mejs.mepIndex++; 2317 2318 // add to player array (for focus events) 2319 mejs.players[t.id] = t; 2320 2321 // start up 2322 t.init(); 2323 2324 return t; 2325 }; 2326 2327 // actual player 2328 mejs.MediaElementPlayer.prototype = { 2329 2330 hasFocus: false, 2331 2332 controlsAreVisible: true, 2333 2334 init: function() { 2335 2336 var 2337 t = this, 2338 mf = mejs.MediaFeatures, 2339 // options for MediaElement (shim) 2340 meOptions = $.extend(true, {}, t.options, { 2341 success: function(media, domNode) { t.meReady(media, domNode); }, 2342 error: function(e) { t.handleError(e);} 2343 }), 2344 tagName = t.media.tagName.toLowerCase(); 2345 2346 t.isDynamic = (tagName !== 'audio' && tagName !== 'video'); 2347 2348 if (t.isDynamic) { 2349 // get video from src or href? 2350 t.isVideo = t.options.isVideo; 2351 } else { 2352 t.isVideo = (tagName !== 'audio' && t.options.isVideo); 2353 } 2354 2355 // use native controls in iPad, iPhone, and Android 2356 if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) { 2357 2358 // add controls and stop 2359 t.$media.attr('controls', 'controls'); 2360 2361 // attempt to fix iOS 3 bug 2362 //t.$media.removeAttr('poster'); 2363 // no Issue found on iOS3 -ttroxell 2364 2365 // override Apple's autoplay override for iPads 2366 if (mf.isiPad && t.media.getAttribute('autoplay') !== null) { 2367 t.play(); 2368 } 2369 2370 } else if (mf.isAndroid && t.options.AndroidUseNativeControls) { 2371 2372 // leave default player 2373 2374 } else { 2375 2376 // DESKTOP: use MediaElementPlayer controls 2377 2378 // remove native controls 2379 t.$media.removeAttr('controls'); 2380 var videoPlayerTitle = t.isVideo ? 2381 mejs.i18n.t('Video Player') : mejs.i18n.t('Audio Player'); 2382 // insert description for screen readers 2383 $('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media); 2384 // build container 2385 t.container = 2386 $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') + 2387 '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+ 2388 '<div class="mejs-inner">'+ 2389 '<div class="mejs-mediaelement"></div>'+ 2390 '<div class="mejs-layers"></div>'+ 2391 '<div class="mejs-controls"></div>'+ 2392 '<div class="mejs-clear"></div>'+ 2393 '</div>' + 2394 '</div>') 2395 .addClass(t.$media[0].className) 2396 .insertBefore(t.$media) 2397 .focus(function ( e ) { 2398 if( !t.controlsAreVisible ) { 2399 t.showControls(true); 2400 var playButton = t.container.find('.mejs-playpause-button > button'); 2401 playButton.focus(); 2402 } 2403 }); 2404 2405 // add classes for user and content 2406 t.container.addClass( 2407 (mf.isAndroid ? 'mejs-android ' : '') + 2408 (mf.isiOS ? 'mejs-ios ' : '') + 2409 (mf.isiPad ? 'mejs-ipad ' : '') + 2410 (mf.isiPhone ? 'mejs-iphone ' : '') + 2411 (t.isVideo ? 'mejs-video ' : 'mejs-audio ') 2412 ); 2413 2414 2415 // move the <video/video> tag into the right spot 2416 if (mf.isiOS) { 2417 2418 // sadly, you can't move nodes in iOS, so we have to destroy and recreate it! 2419 var $newMedia = t.$media.clone(); 2420 2421 t.container.find('.mejs-mediaelement').append($newMedia); 2422 2423 t.$media.remove(); 2424 t.$node = t.$media = $newMedia; 2425 t.node = t.media = $newMedia[0]; 2426 2427 } else { 2428 2429 // normal way of moving it into place (doesn't work on iOS) 2430 t.container.find('.mejs-mediaelement').append(t.$media); 2431 } 2432 2433 // needs to be assigned here, after iOS remap 2434 t.node.player = t; 2435 2436 // find parts 2437 t.controls = t.container.find('.mejs-controls'); 2438 t.layers = t.container.find('.mejs-layers'); 2439 2440 // determine the size 2441 2442 /* size priority: 2443 (1) videoWidth (forced), 2444 (2) style="width;height;" 2445 (3) width attribute, 2446 (4) defaultVideoWidth (for unspecified cases) 2447 */ 2448 2449 var tagType = (t.isVideo ? 'video' : 'audio'), 2450 capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1); 2451 2452 2453 2454 if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) { 2455 t.width = t.options[tagType + 'Width']; 2456 } else if (t.media.style.width !== '' && t.media.style.width !== null) { 2457 t.width = t.media.style.width; 2458 } else if (t.media.getAttribute('width') !== null) { 2459 t.width = t.$media.attr('width'); 2460 } else { 2461 t.width = t.options['default' + capsTagName + 'Width']; 2462 } 2463 2464 if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) { 2465 t.height = t.options[tagType + 'Height']; 2466 } else if (t.media.style.height !== '' && t.media.style.height !== null) { 2467 t.height = t.media.style.height; 2468 } else if (t.$media[0].getAttribute('height') !== null) { 2469 t.height = t.$media.attr('height'); 2470 } else { 2471 t.height = t.options['default' + capsTagName + 'Height']; 2472 } 2473 2474 // set the size, while we wait for the plugins to load below 2475 t.setPlayerSize(t.width, t.height); 2476 2477 // create MediaElementShim 2478 meOptions.pluginWidth = t.width; 2479 meOptions.pluginHeight = t.height; 2480 } 2481 2482 // create MediaElement shim 2483 mejs.MediaElement(t.$media[0], meOptions); 2484 2485 if (typeof(t.container) != 'undefined' && t.controlsAreVisible){ 2486 // controls are shown when loaded 2487 t.container.trigger('controlsshown'); 2488 } 2489 }, 2490 2491 showControls: function(doAnimation) { 2492 var t = this; 2493 2494 doAnimation = typeof doAnimation == 'undefined' || doAnimation; 2495 2496 if (t.controlsAreVisible) 2497 return; 2498 2499 if (doAnimation) { 2500 t.controls 2501 .css('visibility','visible') 2502 .stop(true, true).fadeIn(200, function() { 2503 t.controlsAreVisible = true; 2504 t.container.trigger('controlsshown'); 2505 }); 2506
2507 // any additional controls people might add and want to hide 2508 t.container.find('.mejs-control') 2509 .css('visibility','visible') 2510 .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;}); 2511 2512 } else { 2513 t.controls 2514 .css('visibility','visible') 2515 .css('display','block'); 2516 2517 // any additional controls people might add and want to hide 2518 t.container.find('.mejs-control') 2519 .css('visibility','visible') 2520 .css('display','block'); 2521 2522 t.controlsAreVisible = true; 2523 t.container.trigger('controlsshown'); 2524 } 2525 2526 t.setControlsSize(); 2527 2528 }, 2529 2530 hideControls: function(doAnimation) { 2531 var t = this; 2532 2533 doAnimation = typeof doAnimation == 'undefined' || doAnimation; 2534 2535 if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction) 2536 return; 2537 2538 if (doAnimation) { 2539 // fade out main controls 2540 t.controls.stop(true, true).fadeOut(200, function() { 2541 $(this) 2542 .css('visibility','hidden') 2543 .css('display','block'); 2544 2545 t.controlsAreVisible = false; 2546 t.container.trigger('controlshidden'); 2547 }); 2548 2549 // any additional controls people might add and want to hide 2550 t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() { 2551 $(this) 2552 .css('visibility','hidden') 2553 .css('display','block'); 2554 }); 2555 } else { 2556 2557 // hide main controls 2558 t.controls 2559 .css('visibility','hidden') 2560 .css('display','block'); 2561 2562 // hide others 2563 t.container.find('.mejs-control') 2564 .css('visibility','hidden') 2565 .css('display','block'); 2566 2567 t.controlsAreVisible = false; 2568 t.container.trigger('controlshidden'); 2569 } 2570 }, 2571 2572 controlsTimer: null, 2573 2574 startControlsTimer: function(timeout) { 2575 2576 var t = this; 2577 2578 timeout = typeof timeout != 'undefined' ? timeout : 1500; 2579 2580 t.killControlsTimer('start'); 2581 2582 t.controlsTimer = setTimeout(function() { 2583 // 2584 t.hideControls(); 2585 t.killControlsTimer('hide'); 2586 }, timeout); 2587 }, 2588 2589 killControlsTimer: function(src) { 2590 2591 var t = this; 2592 2593 if (t.controlsTimer !== null) { 2594 clearTimeout(t.controlsTimer); 2595 delete t.controlsTimer; 2596 t.controlsTimer = null; 2597 } 2598 }, 2599 2600 controlsEnabled: true, 2601 2602 disableControls: function() { 2603 var t= this; 2604 2605 t.killControlsTimer(); 2606 t.hideControls(false); 2607 this.controlsEnabled = false; 2608 }, 2609 2610 enableControls: function() { 2611 var t= this; 2612 2613 t.showControls(false); 2614 2615 t.controlsEnabled = true; 2616 }, 2617 2618 2619 // Sets up all controls and events 2620 meReady: function(media, domNode) { 2621 2622 2623 var t = this, 2624 mf = mejs.MediaFeatures, 2625 autoplayAttr = domNode.getAttribute('autoplay'), 2626 autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'), 2627 featureIndex, 2628 feature; 2629 2630 // make sure it can't create itself again if a plugin reloads 2631 if (t.created) { 2632 return; 2633 } else { 2634 t.created = true; 2635 } 2636 2637 t.media = media; 2638 t.domNode = domNode; 2639 2640 if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) { 2641 2642 // two built in features 2643 t.buildposter(t, t.controls, t.layers, t.media); 2644 t.buildkeyboard(t, t.controls, t.layers, t.media); 2645 t.buildoverlays(t, t.controls, t.layers, t.media); 2646 2647 // grab for use by features 2648 t.findTracks(); 2649 2650 // add user-defined features/controls 2651 for (featureIndex in t.options.features) { 2652 feature = t.options.features[featureIndex]; 2653 if (t['build' + feature]) { 2654 try { 2655 t['build' + feature](t, t.controls, t.layers, t.media); 2656 } catch (e) { 2657 // TODO: report control error 2658 //throw e; 2659 2660 2661 } 2662 } 2663 } 2664 2665 t.container.trigger('controlsready'); 2666 2667 // reset all layers and controls 2668 t.setPlayerSize(t.width, t.height); 2669 t.setControlsSize(); 2670 2671 2672 // controls fade 2673 if (t.isVideo) { 2674 2675 if (mejs.MediaFeatures.hasTouch) { 2676 2677 // for touch devices (iOS, Android) 2678 // show/hide without animation on touch 2679 2680 t.$media.bind('touchstart', function() { 2681 2682 2683 // toggle controls 2684 if (t.controlsAreVisible) { 2685 t.hideControls(false); 2686 } else { 2687 if (t.controlsEnabled) { 2688 t.showControls(false); 2689 } 2690 } 2691 }); 2692 2693 } else { 2694 2695 // create callback here since it needs access to current 2696 // MediaElement object 2697 t.clickToPlayPauseCallback = function() { 2698 // 2699 2700 if (t.options.clickToPlayPause) { 2701 if (t.media.paused) { 2702 t.play(); 2703 } else { 2704 t.pause(); 2705 } 2706 } 2707 }; 2708 2709 // click to play/pause 2710 t.media.addEventListener('click', t.clickToPlayPauseCallback, false); 2711 2712 // show/hide controls 2713 t.container 2714 .bind('mouseenter', function () { 2715 if (t.controlsEnabled) { 2716 if (!t.options.alwaysShowControls ) { 2717 t.killControlsTimer('enter'); 2718 t.showControls(); 2719 t.startControlsTimer(2500); 2720 } 2721 } 2722 }) 2723 .bind('mousemove', function() { 2724 if (t.controlsEnabled) { 2725 if (!t.controlsAreVisible) { 2726 t.showControls(); 2727 } 2728 if (!t.options.alwaysShowControls) { 2729 t.startControlsTimer(2500); 2730 } 2731 } 2732 }) 2733 .bind('mouseleave', function () { 2734 if (t.controlsEnabled) { 2735 if (!t.media.paused && !t.options.alwaysShowControls) { 2736 t.startControlsTimer(1000); 2737 } 2738 } 2739 }); 2740 } 2741 2742 if(t.options.hideVideoControlsOnLoad) { 2743 t.hideControls(false); 2744 } 2745 2746 // check for autoplay 2747 if (autoplay && !t.options.alwaysShowControls) { 2748 t.hideControls(); 2749 } 2750 2751 // resizer 2752 if (t.options.enableAutosize) { 2753 t.media.addEventListener('loadedmetadata', function(e) { 2754 // if the <video height> was not set and the options.videoHeight was not set 2755 // then resize to the real dimensions 2756 if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) { 2757 t.setPlayerSize(e.target.videoWidth, e.target.videoHeight); 2758 t.setControlsSize(); 2759 t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight); 2760 } 2761 }, false); 2762 } 2763 } 2764 2765 // EVENTS 2766 2767 // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them) 2768 media.addEventListener('play', function() { 2769 var playerIndex; 2770 2771 // go through all other players 2772 for (playerIndex in mejs.players) { 2773 var p = mejs.players[playerIndex]; 2774 if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) { 2775 p.pause(); 2776 } 2777 p.hasFocus = false;
2778 } 2779 2780 t.hasFocus = true; 2781 },false); 2782 2783 2784 // ended for all 2785 t.media.addEventListener('ended', function (e) { 2786 if(t.options.autoRewind) { 2787 try{ 2788 t.media.setCurrentTime(0); 2789 // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning 2790 window.setTimeout(function(){ 2791 $(t.container).find('.mejs-overlay-loading').parent().hide(); 2792 }, 20); 2793 } catch (exp) { 2794 2795 } 2796 } 2797 t.media.pause(); 2798 2799 if (t.setProgressRail) { 2800 t.setProgressRail(); 2801 } 2802 if (t.setCurrentRail) { 2803 t.setCurrentRail(); 2804 } 2805 2806 if (t.options.loop) { 2807 t.play(); 2808 } else if (!t.options.alwaysShowControls && t.controlsEnabled) { 2809 t.showControls(); 2810 } 2811 }, false); 2812 2813 // resize on the first play 2814 t.media.addEventListener('loadedmetadata', function(e) { 2815 if (t.updateDuration) { 2816 t.updateDuration(); 2817 } 2818 if (t.updateCurrent) { 2819 t.updateCurrent(); 2820 } 2821 2822 if (!t.isFullScreen) { 2823 t.setPlayerSize(t.width, t.height); 2824 t.setControlsSize(); 2825 } 2826 }, false); 2827 2828 // Only change the time format when necessary 2829 var duration = null; 2830 t.media.addEventListener('timeupdate',function() { 2831 if (duration !== this.duration) { 2832 duration = this.duration; 2833 mejs.Utility.calculateTimeFormat(duration, t.options, t.options.framesPerSecond || 25); 2834 } 2835 }, false); 2836 2837 t.container.focusout(function (e) { 2838 if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787 2839 var $target = $(e.relatedTarget); 2840 if (t.keyboardAction && $target.parents('.mejs-container').length === 0) { 2841 t.keyboardAction = false; 2842 t.hideControls(true); 2843 } 2844 } 2845 }); 2846 2847 // webkit has trouble doing this without a delay 2848 setTimeout(function () { 2849 t.setPlayerSize(t.width, t.height); 2850 t.setControlsSize(); 2851 }, 50); 2852 2853 // adjust controls whenever window sizes (used to be in fullscreen only) 2854 t.globalBind('resize', function() { 2855 2856 // don't resize for fullscreen mode 2857 if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) { 2858 t.setPlayerSize(t.width, t.height); 2859 } 2860 2861 // always adjust controls 2862 t.setControlsSize(); 2863 }); 2864 2865 // This is a work-around for a bug in the YouTube iFrame player, which means 2866 // we can't use the play() API for the initial playback on iOS or Android; 2867 // user has to start playback directly by tapping on the iFrame. 2868 if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) { 2869 t.container.find('.mejs-overlay-play').hide(); 2870 } 2871 } 2872 2873 // force autoplay for HTML5 2874 if (autoplay && media.pluginType == 'native') { 2875 t.play(); 2876 } 2877 2878 2879 if (t.options.success) { 2880 2881 if (typeof t.options.success == 'string') { 2882 window[t.options.success](t.media, t.domNode, t); 2883 } else { 2884 t.options.success(t.media, t.domNode, t); 2885 } 2886 } 2887 }, 2888 2889 handleError: function(e) { 2890 var t = this; 2891 2892 t.controls.hide(); 2893 2894 // Tell user that the file cannot be played 2895 if (t.options.error) { 2896 t.options.error(e); 2897 } 2898 }, 2899 2900 setPlayerSize: function(width,height) { 2901 var t = this; 2902 2903 if( !t.options.setDimensions ) { 2904 return false; 2905 } 2906 2907 if (typeof width != 'undefined') { 2908 t.width = width; 2909 } 2910 2911 if (typeof height != 'undefined') { 2912 t.height = height; 2913 } 2914 2915 // detect 100% mode - use currentStyle for IE since css() doesn't return percentages 2916 if (t.height.toString().indexOf('%') > 0 || (t.$node.css('max-width') !== 'none' && t.$node.css('max-width') !== 't.width') || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) { 2917 2918 // do we have the native dimensions yet? 2919 var nativeWidth = (function() { 2920 if (t.isVideo) { 2921 if (t.media.videoWidth && t.media.videoWidth > 0) { 2922 return t.media.videoWidth; 2923 } else if (t.media.getAttribute('width') !== null) { 2924 return t.media.getAttribute('width'); 2925 } else { 2926 return t.options.defaultVideoWidth; 2927 } 2928 } else { 2929 return t.options.defaultAudioWidth; 2930 } 2931 })(); 2932 2933 var nativeHeight = (function() { 2934 if (t.isVideo) { 2935 if (t.media.videoHeight && t.media.videoHeight > 0) { 2936 return t.media.videoHeight; 2937 } else if (t.media.getAttribute('height') !== null) { 2938 return t.media.getAttribute('height'); 2939 } else { 2940 return t.options.defaultVideoHeight; 2941 } 2942 } else { 2943 return t.options.defaultAudioHeight; 2944 } 2945 })(); 2946 2947 var 2948 parentWidth = t.container.parent().closest(':visible').width(), 2949 parentHeight = t.container.parent().closest(':visible').height(), 2950 newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight; 2951 2952 // When we use percent, the newHeight can't be calculated so we get the container height 2953 if (isNaN(newHeight)) { 2954 newHeight = parentHeight; 2955 } 2956 2957 if (t.container.parent().length > 0 && t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) { 2958 parentWidth = $(window).width(); 2959 newHeight = $(window).height(); 2960 } 2961 2962 if ( newHeight && parentWidth ) { 2963 2964 // set outer container size 2965 t.container 2966 .width(parentWidth) 2967 .height(newHeight); 2968 2969 // set native <video> or <audio> and shims 2970 t.$media.add(t.container.find('.mejs-shim')) 2971 .width('100%') 2972 .height('100%'); 2973 2974 // if shim is ready, send the size to the embeded plugin 2975 if (t.isVideo) { 2976 if (t.media.setVideoSize) { 2977 t.media.setVideoSize(parentWidth, newHeight); 2978 } 2979 } 2980 2981 // set the layers 2982 t.layers.children('.mejs-layer') 2983 .width('100%') 2984 .height('100%'); 2985 } 2986 2987 2988 } else { 2989 2990 t.container 2991 .width(t.width) 2992 .height(t.height); 2993 2994 t.layers.children('.mejs-layer') 2995 .width(t.width) 2996 .height(t.height); 2997 2998 } 2999 3000 }, 3001 3002 setControlsSize: function() { 3003 var t = this, 3004 usedWidth = 0, 3005 railWidth = 0, 3006 rail = t.controls.find('.mejs-time-rail'), 3007 total = t.controls.find('.mejs-time-total'), 3008 others = rail.siblings(), 3009 lastControl = others.last(), 3010 lastControlPosition = null; 3011 3012 // skip calculation if hidden 3013 if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) { 3014 return; 3015 } 3016 3017 3018 // allow the size to come from custom CSS 3019 if (t.options && !t.options.autosizeProgress) { 3020 // Also, frontends devs can be more flexible 3021 // due the opportunity of absolute positioning. 3022 railWidth = parseInt(rail.css('width'), 10); 3023 } 3024 3025 // attempt to autosize 3026 if (railWidth === 0 || !railWidth) { 3027 3028 // find the size of all the other controls besides the rail 3029 others.each(function() { 3030 var $this = $(this); 3031 if ($this.css('position') != 'absolute' && $this.is(':visible')) { 3032 usedWidth += $(this).outerWidth(true); 3033 } 3034 }); 3035 3036 // fit the rail into the remaining space 3037 railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width()); 3038 } 3039 3040 // resize the rail, 3041 // but then check if the last control (say, the fullscreen button) got pushed down 3042 // this often happens when zoomed 3043 do { 3044 // outer area 3045 rail.width(railWidth); 3046 // dark space 3047 total.width(railWidth - (total.outerWidth(true) - total.width())); 3048 3049 if (lastControl.css('position') != 'absolute') { 3050 lastControlPosition = lastControl.length ? lastControl.position() : null; 3051 railWidth--; 3052 } 3053 } while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0); 3054 3055 t.container.trigger('controlsresize'); 3056 }, 3057 3058 3059 buildposter: function(player, controls, layers, media) { 3060 var t = this, 3061 poster = 3062 $('<div class="mejs-poster mejs-layer">' + 3063 '</div>') 3064 .appendTo(layers), 3065 posterUrl = player.$media.attr('poster'); 3066 3067 // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster) 3068 if (player.options.poster !== '') { 3069 posterUrl = player.options.poster; 3070 } 3071
3072 // second, try the real poster 3073 if ( posterUrl ) { 3074 t.setPoster(posterUrl); 3075 } else { 3076 poster.hide(); 3077 } 3078 3079 media.addEventListener('play',function() { 3080 poster.hide(); 3081 }, false); 3082 3083 if(player.options.showPosterWhenEnded && player.options.autoRewind){ 3084 media.addEventListener('ended',function() { 3085 poster.show(); 3086 }, false); 3087 } 3088 }, 3089 3090 setPoster: function(url) { 3091 var t = this, 3092 posterDiv = t.container.find('.mejs-poster'), 3093 posterImg = posterDiv.find('img'); 3094 3095 if (posterImg.length === 0) { 3096 posterImg = $('<img width="100%" height="100%" alt="" />').appendTo(posterDiv); 3097 } 3098 3099 posterImg.attr('src', url); 3100 posterDiv.css({'background-image' : 'url(' + url + ')'}); 3101 }, 3102 3103 buildoverlays: function(player, controls, layers, media) { 3104 var t = this; 3105 if (!player.isVideo) 3106 return; 3107 3108 var 3109 loading = 3110 $('<div class="mejs-overlay mejs-layer">'+ 3111 '<div class="mejs-overlay-loading"><span></span></div>'+ 3112 '</div>') 3113 .hide() // start out hidden 3114 .appendTo(layers), 3115 error = 3116 $('<div class="mejs-overlay mejs-layer">'+ 3117 '<div class="mejs-overlay-error"></div>'+ 3118 '</div>') 3119 .hide() // start out hidden 3120 .appendTo(layers), 3121 // this needs to come last so it's on top 3122 bigPlay = 3123 $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+ 3124 '<div class="mejs-overlay-button"></div>'+ 3125 '</div>') 3126 .appendTo(layers) 3127 .bind('click', function() { // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video 3128 if (t.options.clickToPlayPause) { 3129 if (media.paused) { 3130 media.play(); 3131 } 3132 } 3133 }); 3134 3135 /* 3136 if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) { 3137 bigPlay.remove(); 3138 loading.remove(); 3139 } 3140 */ 3141 3142 3143 // show/hide big play button 3144 media.addEventListener('play',function() { 3145 bigPlay.hide(); 3146 loading.hide(); 3147 controls.find('.mejs-time-buffering').hide(); 3148 error.hide(); 3149 }, false); 3150 3151 media.addEventListener('playing', function() { 3152 bigPlay.hide(); 3153 loading.hide(); 3154 controls.find('.mejs-time-buffering').hide(); 3155 error.hide(); 3156 }, false); 3157 3158 media.addEventListener('seeking', function() { 3159 loading.show(); 3160 controls.find('.mejs-time-buffering').show(); 3161 }, false); 3162 3163 media.addEventListener('seeked', function() { 3164 loading.hide(); 3165 controls.find('.mejs-time-buffering').hide(); 3166 }, false); 3167 3168 media.addEventListener('pause',function() { 3169 if (!mejs.MediaFeatures.isiPhone) { 3170 bigPlay.show(); 3171 } 3172 }, false); 3173 3174 media.addEventListener('waiting', function() { 3175 loading.show(); 3176 controls.find('.mejs-time-buffering').show(); 3177 }, false); 3178 3179 3180 // show/hide loading 3181 media.addEventListener('loadeddata',function() { 3182 // for some reason Chrome is firing this event 3183 //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none') 3184 // return; 3185 3186 loading.show(); 3187 controls.find('.mejs-time-buffering').show(); 3188 // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305) 3189 if (mejs.MediaFeatures.isAndroid) { 3190 media.canplayTimeout = window.setTimeout( 3191 function() { 3192 if (document.createEvent) { 3193 var evt = document.createEvent('HTMLEvents'); 3194 evt.initEvent('canplay', true, true); 3195 return media.dispatchEvent(evt); 3196 } 3197 }, 300 3198 ); 3199 } 3200 }, false); 3201 media.addEventListener('canplay',function() { 3202 loading.hide(); 3203 controls.find('.mejs-time-buffering').hide(); 3204 clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice 3205 }, false); 3206 3207 // error handling 3208 media.addEventListener('error',function(e) { 3209 t.handleError(e); 3210 loading.hide(); 3211 bigPlay.hide(); 3212 error.show(); 3213 error.find('.mejs-overlay-error').html("Error loading this resource"); 3214 }, false); 3215 3216 media.addEventListener('keydown', function(e) { 3217 t.onkeydown(player, media, e); 3218 }, false); 3219 }, 3220 3221 buildkeyboard: function(player, controls, layers, media) { 3222 3223 var t = this; 3224 3225 t.container.keydown(function () { 3226 t.keyboardAction = true; 3227 }); 3228 3229 // listen for key presses 3230 t.globalBind('keydown', function(event) { 3231 player.hasFocus = $(event.target).closest('.mejs-container').length !== 0; 3232 return t.onkeydown(player, media, event); 3233 }); 3234 3235 3236 // check if someone clicked outside a player region, then kill its focus 3237 t.globalBind('click', function(event) { 3238 player.hasFocus = $(event.target).closest('.mejs-container').length !== 0; 3239 }); 3240 3241 }, 3242 onkeydown: function(player, media, e) { 3243 if (player.hasFocus && player.options.enableKeyboard) { 3244 // find a matching key 3245 for (var i = 0, il = player.options.keyActions.length; i < il; i++) { 3246 var keyAction = player.options.keyActions[i]; 3247 3248 for (var j = 0, jl = keyAction.keys.length; j < jl; j++) { 3249 if (e.keyCode == keyAction.keys[j]) { 3250 if (typeof(e.preventDefault) == "function") e.preventDefault(); 3251 keyAction.action(player, media, e.keyCode); 3252 return false;
3253 } 3254 } 3255 } 3256 } 3257 3258 return true; 3259 }, 3260 3261 findTracks: function() { 3262 var t = this, 3263 tracktags = t.$media.find('track'); 3264 3265 // store for use by plugins 3266 t.tracks = []; 3267 tracktags.each(function(index, track) { 3268 3269 track = $(track); 3270 3271 t.tracks.push({ 3272 srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '', 3273 src: track.attr('src'), 3274 kind: track.attr('kind'), 3275 label: track.attr('label') || '', 3276 entries: [], 3277 isLoaded: false 3278 }); 3279 }); 3280 }, 3281 changeSkin: function(className) { 3282 this.container[0].className = 'mejs-container ' + className; 3283 this.setPlayerSize(this.width, this.height); 3284 this.setControlsSize(); 3285 }, 3286 play: function() { 3287 this.load(); 3288 this.media.play(); 3289 }, 3290 pause: function() { 3291 try { 3292 this.media.pause(); 3293 } catch (e) {} 3294 }, 3295 load: function() { 3296 if (!this.isLoaded) { 3297 this.media.load(); 3298 } 3299 3300 this.isLoaded = true; 3301 }, 3302 setMuted: function(muted) { 3303 this.media.setMuted(muted); 3304 }, 3305 setCurrentTime: function(time) { 3306 this.media.setCurrentTime(time); 3307 }, 3308 getCurrentTime: function() { 3309 return this.media.currentTime; 3310 }, 3311 setVolume: function(volume) { 3312 this.media.setVolume(volume); 3313 }, 3314 getVolume: function() { 3315 return this.media.volume; 3316 }, 3317 setSrc: function(src) { 3318 this.media.setSrc(src); 3319 }, 3320 remove: function() { 3321 var t = this, featureIndex, feature; 3322 3323 t.container.prev('.mejs-offscreen').remove(); 3324 3325 // invoke features cleanup 3326 for (featureIndex in t.options.features) { 3327 feature = t.options.features[featureIndex]; 3328 if (t['clean' + feature]) { 3329 try { 3330 t['clean' + feature](t); 3331 } catch (e) { 3332 // TODO: report control error 3333 //throw e; 3334 // 3335 // 3336 } 3337 } 3338 } 3339 3340 // grab video and put it back in place 3341 if (!t.isDynamic) { 3342 t.$media.prop('controls', true); 3343 // detach events from the video 3344 // TODO: detach event listeners better than this; 3345 // also detach ONLY the events attached by this plugin! 3346 t.$node.clone().insertBefore(t.container).show(); 3347 t.$node.remove(); 3348 } else { 3349 t.$node.insertBefore(t.container); 3350 } 3351 3352 if (t.media.pluginType !== 'native') { 3353 t.media.remove(); 3354 } 3355 3356 // Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api. 3357 delete mejs.players[t.id]; 3358 3359 if (typeof t.container == 'object') { 3360 t.container.remove(); 3361 } 3362 t.globalUnbind(); 3363 delete t.node.player; 3364 }, 3365 rebuildtracks: function(){ 3366 var t = this; 3367 t.findTracks(); 3368 t.buildtracks(t, t.controls, t.layers, t.media); 3369 }, 3370 resetSize: function(){ 3371 var t = this; 3372 // webkit has trouble doing this without a delay 3373 setTimeout(function () { 3374 // 3375 t.setPlayerSize(t.width, t.height); 3376 t.setControlsSize(); 3377 }, 50); 3378 } 3379 }; 3380 3381 (function(){ 3382 var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/; 3383 3384 function splitEvents(events, id) { 3385 // add player ID as an event namespace so it's easier to unbind them all later 3386 var ret = {d: [], w: []}; 3387 $.each((events || '').split(' '), function(k, v){ 3388 var eventname = v + '.' + id; 3389 if (eventname.indexOf('.') === 0) { 3390 ret.d.push(eventname); 3391 ret.w.push(eventname); 3392 } 3393 else { 3394 ret[rwindow.test(v) ? 'w' : 'd'].push(eventname); 3395 } 3396 }); 3397 ret.d = ret.d.join(' '); 3398 ret.w = ret.w.join(' '); 3399 return ret; 3400 } 3401 3402 mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) { 3403 var t = this; 3404 var doc = t.node ? t.node.ownerDocument : document; 3405 3406 events = splitEvents(events, t.id); 3407 if (events.d) $(doc).bind(events.d, data, callback); 3408 if (events.w) $(window).bind(events.w, data, callback); 3409 }; 3410 3411 mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) { 3412 var t = this; 3413 var doc = t.node ? t.node.ownerDocument : document; 3414 3415 events = splitEvents(events, t.id); 3416 if (events.d) $(doc).unbind(events.d, callback); 3417 if (events.w) $(window).unbind(events.w, callback); 3418 }; 3419 })(); 3420 3421 // turn into jQuery plugin 3422 if (typeof $ != 'undefined') { 3423 $.fn.mediaelementplayer = function (options) { 3424 if (options === false) { 3425 this.each(function () { 3426 var player = $(this).data('mediaelementplayer'); 3427 if (player) { 3428 player.remove(); 3429 } 3430 $(this).removeData('mediaelementplayer'); 3431 }); 3432 } 3433 else { 3434 this.each(function () { 3435 $(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options)); 3436 }); 3437 } 3438 return this; 3439 }; 3440 3441 3442 $(document).ready(function() { 3443 // auto enable using JSON attribute 3444 $('.mejs-player').mediaelementplayer(); 3445 }); 3446 } 3447 3448 // push out to window 3449 window.MediaElementPlayer = mejs.MediaElementPlayer; 3450 3451})(mejs.$); 3452 3453(function($) { 3454 3455 $.extend(mejs.MepDefaults, { 3456 playText: mejs.i18n.t('Play'), 3457 pauseText: mejs.i18n.t('Pause') 3458 }); 3459 3460 // PLAY/pause BUTTON 3461 $.extend(MediaElementPlayer.prototype, { 3462 buildplaypause: function(player, controls, layers, media) { 3463 var 3464 t = this, 3465 op = t.options, 3466 play = 3467 $('<div class="mejs-button mejs-playpause-button mejs-play" >' + 3468 '<button type="button" aria-controls="' + t.id + '" title="' + op.playText + '" aria-label="' + op.playText + '"></button>' + 3469 '</div>') 3470 .appendTo(controls) 3471 .click(function(e) { 3472 e.preventDefault(); 3473 3474 if (media.paused) { 3475 media.play(); 3476 } else { 3477 media.pause(); 3478 } 3479 3480 return false;
3481 }), 3482 play_btn = play.find('button'); 3483 3484 3485 function togglePlayPause(which) { 3486 if ('play' === which) { 3487 play.removeClass('mejs-play').addClass('mejs-pause'); 3488 play_btn.attr({ 3489 'title': op.pauseText, 3490 'aria-label': op.pauseText 3491 }); 3492 } else { 3493 play.removeClass('mejs-pause').addClass('mejs-play'); 3494 play_btn.attr({ 3495 'title': op.playText, 3496 'aria-label': op.playText 3497 }); 3498 } 3499 }; 3500 togglePlayPause('pse'); 3501 3502 3503 media.addEventListener('play',function() { 3504 togglePlayPause('play'); 3505 }, false); 3506 media.addEventListener('playing',function() { 3507 togglePlayPause('play'); 3508 }, false); 3509 3510 3511 media.addEventListener('pause',function() { 3512 togglePlayPause('pse'); 3513 }, false); 3514 media.addEventListener('paused',function() { 3515 togglePlayPause('pse'); 3516 }, false); 3517 } 3518 }); 3519 3520})(mejs.$); 3521 3522(function($) { 3523 3524 $.extend(mejs.MepDefaults, { 3525 stopText: 'Stop' 3526 }); 3527 3528 // STOP BUTTON 3529 $.extend(MediaElementPlayer.prototype, { 3530 buildstop: function(player, controls, layers, media) { 3531 var t = this; 3532 3533 $('<div class="mejs-button mejs-stop-button mejs-stop">' + 3534 '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' + 3535 '</div>') 3536 .appendTo(controls) 3537 .click(function() { 3538 if (!media.paused) { 3539 media.pause(); 3540 } 3541 if (media.currentTime > 0) { 3542 media.setCurrentTime(0); 3543 media.pause(); 3544 controls.find('.mejs-time-current').width('0px'); 3545 controls.find('.mejs-time-handle').css('left', '0px'); 3546 controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0, player.options)); 3547 controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0, player.options)); 3548 layers.find('.mejs-poster').show(); 3549 } 3550 }); 3551 } 3552 }); 3553 3554})(mejs.$); 3555 3556(function($) { 3557 3558 $.extend(mejs.MepDefaults, { 3559 progessHelpText: mejs.i18n.t( 3560 'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.') 3561 }); 3562 3563 // progress/loaded bar 3564 $.extend(MediaElementPlayer.prototype, { 3565 buildprogress: function(player, controls, layers, media) { 3566 3567 $('<div class="mejs-time-rail">' + 3568 '<span class="mejs-time-total mejs-time-slider">' + 3569 //'<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' + 3570 '<span class="mejs-time-buffering"></span>' + 3571 '<span class="mejs-time-loaded"></span>' + 3572 '<span class="mejs-time-current"></span>' + 3573 '<span class="mejs-time-handle"></span>' + 3574 '<span class="mejs-time-float">' + 3575 '<span class="mejs-time-float-current">00:00</span>' + 3576 '<span class="mejs-time-float-corner"></span>' + 3577 '</span>' + 3578 '</span>' + 3579 '</div>') 3580 .appendTo(controls); 3581 controls.find('.mejs-time-buffering').hide(); 3582 3583 var 3584 t = this, 3585 total = controls.find('.mejs-time-total'), 3586 loaded = controls.find('.mejs-time-loaded'), 3587 current = controls.find('.mejs-time-current'), 3588 handle = controls.find('.mejs-time-handle'), 3589 timefloat = controls.find('.mejs-time-float'), 3590 timefloatcurrent = controls.find('.mejs-time-float-current'), 3591 slider = controls.find('.mejs-time-slider'), 3592 handleMouseMove = function (e) { 3593 3594 var offset = total.offset(), 3595 width = total.width(), 3596 percentage = 0, 3597 newTime = 0, 3598 pos = 0, 3599 x; 3600 3601 // mouse or touch position relative to the object 3602 if (e.originalEvent && e.originalEvent.changedTouches) { 3603 x = e.originalEvent.changedTouches[0].pageX; 3604 } else if (e.changedTouches) { // for Zepto 3605 x = e.changedTouches[0].pageX; 3606 } else { 3607 x = e.pageX; 3608 } 3609 3610 if (media.duration) { 3611 if (x < offset.left) { 3612 x = offset.left; 3613 } else if (x > width + offset.left) { 3614 x = width + offset.left; 3615 } 3616 3617 pos = x - offset.left; 3618 percentage = (pos / width); 3619 newTime = (percentage <= 0.02) ? 0 : percentage * media.duration; 3620 3621 // seek to where the mouse is 3622 if (mouseIsDown && newTime !== media.currentTime) { 3623 media.setCurrentTime(newTime); 3624 } 3625 3626 // position floating time box 3627 if (!mejs.MediaFeatures.hasTouch) { 3628 timefloat.css('left', pos); 3629 timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime, player.options) ); 3630 timefloat.show(); 3631 } 3632 } 3633 }, 3634 mouseIsDown = false, 3635 mouseIsOver = false, 3636 lastKeyPressTime = 0, 3637 startedPaused = false, 3638 autoRewindInitial = player.options.autoRewind; 3639 // Accessibility for slider 3640 var updateSlider = function (e) { 3641 3642 var seconds = media.currentTime, 3643 timeSliderText = mejs.i18n.t('Time Slider'), 3644 time = mejs.Utility.secondsToTimeCode(seconds, player.options), 3645 duration = media.duration; 3646 3647 slider.attr({ 3648 'aria-label': timeSliderText, 3649 'aria-valuemin': 0, 3650 'aria-valuemax': duration, 3651 'aria-valuenow': seconds, 3652 'aria-valuetext': time, 3653 'role': 'slider', 3654 'tabindex': 0 3655 }); 3656 3657 }; 3658 3659 var restartPlayer = function () { 3660 var now = new Date(); 3661 if (now - lastKeyPressTime >= 1000) { 3662 media.play(); 3663 } 3664 }; 3665 3666 slider.bind('focus', function (e) { 3667 player.options.autoRewind = false;
3668 }); 3669 3670 slider.bind('blur', function (e) { 3671 player.options.autoRewind = autoRewindInitial; 3672 }); 3673 3674 slider.bind('keydown', function (e) { 3675 3676 if ((new Date() - lastKeyPressTime) >= 1000) { 3677 startedPaused = media.paused; 3678 } 3679 3680 var keyCode = e.keyCode, 3681 duration = media.duration, 3682 seekTime = media.currentTime; 3683 3684 switch (keyCode) { 3685 case 37: // left 3686 seekTime -= 1; 3687 break; 3688 case 39: // Right 3689 seekTime += 1; 3690 break; 3691 case 38: // Up 3692 seekTime += Math.floor(duration * 0.1); 3693 break; 3694 case 40: // Down 3695 seekTime -= Math.floor(duration * 0.1); 3696 break; 3697 case 36: // Home 3698 seekTime = 0; 3699 break; 3700 case 35: // end 3701 seekTime = duration; 3702 break; 3703 case 10: // enter 3704 media.paused ? media.play() : media.pause(); 3705 return; 3706 case 13: // space 3707 media.paused ? media.play() : media.pause(); 3708 return; 3709 default: 3710 return; 3711 } 3712 3713 seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime)); 3714 lastKeyPressTime = new Date(); 3715 if (!startedPaused) { 3716 media.pause(); 3717 } 3718 3719 if (seekTime < media.duration && !startedPaused) { 3720 setTimeout(restartPlayer, 1100); 3721 } 3722 3723 media.setCurrentTime(seekTime); 3724 3725 e.preventDefault(); 3726 e.stopPropagation(); 3727 return false; 3728 }); 3729 3730 3731 // handle clicks 3732 //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove); 3733 total 3734 .bind('mousedown touchstart', function (e) { 3735 // only handle left clicks or touch 3736 if (e.which === 1 || e.which === 0) { 3737 mouseIsDown = true; 3738 handleMouseMove(e); 3739 t.globalBind('mousemove.dur touchmove.dur', function(e) { 3740 handleMouseMove(e); 3741 }); 3742 t.globalBind('mouseup.dur touchend.dur', function (e) { 3743 mouseIsDown = false; 3744 timefloat.hide(); 3745 t.globalUnbind('.dur'); 3746 }); 3747 } 3748 }) 3749 .bind('mouseenter', function(e) { 3750 mouseIsOver = true; 3751 t.globalBind('mousemove.dur', function(e) { 3752 handleMouseMove(e); 3753 }); 3754 if (!mejs.MediaFeatures.hasTouch) { 3755 timefloat.show(); 3756 } 3757 }) 3758 .bind('mouseleave',function(e) { 3759 mouseIsOver = false; 3760 if (!mouseIsDown) { 3761 t.globalUnbind('.dur'); 3762 timefloat.hide(); 3763 } 3764 }); 3765 3766 // loading 3767 media.addEventListener('progress', function (e) { 3768 player.setProgressRail(e); 3769 player.setCurrentRail(e); 3770 }, false); 3771 3772 // current time 3773 media.addEventListener('timeupdate', function(e) { 3774 player.setProgressRail(e); 3775 player.setCurrentRail(e); 3776 updateSlider(e); 3777 }, false); 3778 3779 t.container.on('controlsresize', function() { 3780 player.setProgressRail(); 3781 player.setCurrentRail(); 3782 }); 3783 3784 // store for later use 3785 t.loaded = loaded; 3786 t.total = total; 3787 t.current = current; 3788 t.handle = handle; 3789 }, 3790 setProgressRail: function(e) { 3791 3792 var 3793 t = this, 3794 target = (e !== undefined) ? e.target : t.media, 3795 percent = null; 3796 3797 // newest HTML5 spec has buffered array (FF4, Webkit) 3798 if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) { 3799 // account for a real array with multiple values - always read the end of the last buffer 3800 percent = target.buffered.end(target.buffered.length - 1) / target.duration; 3801 } 3802 // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end() 3803 // to be anything other than 0. If the byte count is available we use this instead. 3804 // Browsers that support the else if do not seem to have the bufferedBytes value and 3805 // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8. 3806 else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) { 3807 percent = target.bufferedBytes / target.bytesTotal; 3808 } 3809 // Firefox 3 with an Ogg file seems to go this way 3810 else if (e && e.lengthComputable && e.total !== 0) { 3811 percent = e.loaded / e.total; 3812 } 3813 3814 // finally update the progress bar 3815 if (percent !== null) { 3816 percent = Math.min(1, Math.max(0, percent)); 3817 // update loaded bar 3818 if (t.loaded && t.total) { 3819 t.loaded.width(t.total.width() * percent); 3820 } 3821 } 3822 }, 3823 setCurrentRail: function() { 3824 3825 var t = this; 3826
3827 if (t.media.currentTime !== undefined && t.media.duration) { 3828 3829 // update bar and handle 3830 if (t.total && t.handle) { 3831 var 3832 newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration), 3833 handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2); 3834 3835 t.current.width(newWidth); 3836 t.handle.css('left', handlePos); 3837 } 3838 } 3839 3840 } 3841 }); 3842})(mejs.$); 3843 3844(function($) { 3845 3846 // options 3847 $.extend(mejs.MepDefaults, { 3848 duration: -1, 3849 timeAndDurationSeparator: '<span> | </span>' 3850 }); 3851 3852 3853 // current and duration 00:00 / 00:00 3854 $.extend(MediaElementPlayer.prototype, { 3855 buildcurrent: function(player, controls, layers, media) { 3856 var t = this; 3857 3858 $('<div class="mejs-time" role="timer" aria-live="off">' + 3859 '<span class="mejs-currenttime">' + 3860 mejs.Utility.secondsToTimeCode(0, player.options) + 3861 '</span>'+ 3862 '</div>') 3863 .appendTo(controls); 3864 3865 t.currenttime = t.controls.find('.mejs-currenttime'); 3866 3867 media.addEventListener('timeupdate',function() { 3868 player.updateCurrent(); 3869 }, false); 3870 }, 3871 3872 3873 buildduration: function(player, controls, layers, media) { 3874 var t = this; 3875 3876 if (controls.children().last().find('.mejs-currenttime').length > 0) { 3877 $(t.options.timeAndDurationSeparator + 3878 '<span class="mejs-duration">' + 3879 mejs.Utility.secondsToTimeCode(t.options.duration, t.options) + 3880 '</span>') 3881 .appendTo(controls.find('.mejs-time')); 3882 } else { 3883 3884 // add class to current time 3885 controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container'); 3886 3887 $('<div class="mejs-time mejs-duration-container">'+ 3888 '<span class="mejs-duration">' + 3889 mejs.Utility.secondsToTimeCode(t.options.duration, t.options) + 3890 '</span>' + 3891 '</div>') 3892 .appendTo(controls); 3893 } 3894 3895 t.durationD = t.controls.find('.mejs-duration'); 3896 3897 media.addEventListener('timeupdate',function() { 3898 player.updateDuration(); 3899 }, false); 3900 }, 3901 3902 updateCurrent: function() { 3903 var t = this; 3904 3905 if (t.currenttime) { 3906 t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options)); 3907 } 3908 }, 3909 3910 updateDuration: function() { 3911 var t = this; 3912 3913 //Toggle the long video class if the video is longer than an hour. 3914 t.container.toggleClass("mejs-long-video", t.media.duration > 3600); 3915 3916 if (t.durationD && (t.options.duration > 0 || t.media.duration)) { 3917 t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options)); 3918 } 3919 } 3920 }); 3921 3922})(mejs.$); 3923 3924(function($) { 3925 3926 $.extend(mejs.MepDefaults, { 3927 muteText: mejs.i18n.t('Mute Toggle'), 3928 allyVolumeControlText: mejs.i18n.t('Use Up/Down Arrow keys to increase or decrease volume.'), 3929 hideVolumeOnTouchDevices: true, 3930 3931 audioVolume: 'horizontal', 3932 videoVolume: 'vertical' 3933 }); 3934 3935 $.extend(MediaElementPlayer.prototype, { 3936 buildvolume: function(player, controls, layers, media) { 3937 3938 // Android and iOS don't support volume controls 3939 if ((mejs.MediaFeatures.isAndroid || mejs.MediaFeatures.isiOS) && this.options.hideVolumeOnTouchDevices) 3940 return; 3941 3942 var t = this, 3943 mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume, 3944 mute = (mode == 'horizontal') ? 3945 3946 // horizontal version 3947 $('<div class="mejs-button mejs-volume-button mejs-mute">' + 3948 '<button type="button" aria-controls="' + t.id + 3949 '" title="' + t.options.muteText + 3950 '" aria-label="' + t.options.muteText + 3951 '"></button>'+ 3952 '</div>' + 3953 '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background 3954 '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' + 3955 '<div class="mejs-horizontal-volume-total"></div>'+ // line background 3956 '<div class="mejs-horizontal-volume-current"></div>'+ // current volume 3957 '<div class="mejs-horizontal-volume-handle"></div>'+ // handle 3958 '</a>' 3959 ) 3960 .appendTo(controls) : 3961 3962 // vertical version 3963 $('<div class="mejs-button mejs-volume-button mejs-mute">'+ 3964 '<button type="button" aria-controls="' + t.id + 3965 '" title="' + t.options.muteText + 3966 '" aria-label="' + t.options.muteText + 3967 '"></button>'+ 3968 '<a href="javascript:void(0);" class="mejs-volume-slider">'+ // outer background 3969 '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' + 3970 '<div class="mejs-volume-total"></div>'+ // line background 3971 '<div class="mejs-volume-current"></div>'+ // current volume 3972 '<div class="mejs-volume-handle"></div>'+ // handle 3973 '</a>'+ 3974 '</div>') 3975 .appendTo(controls), 3976 volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'), 3977 volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'), 3978 volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'), 3979 volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'), 3980 3981 positionVolumeHandle = function(volume, secondTry) { 3982 3983 if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') { 3984 volumeSlider.show(); 3985 positionVolumeHandle(volume, true); 3986 volumeSlider.hide(); 3987 return; 3988 } 3989 3990 // correct to 0-1 3991 volume = Math.max(0,volume); 3992 volume = Math.min(volume,1); 3993 3994 // ajust mute button style 3995 if (volume === 0) { 3996 mute.removeClass('mejs-mute').addClass('mejs-unmute'); 3997 mute.children('button').attr('title', mejs.i18n.t('Unmute')).attr('aria-label', mejs.i18n.t('Unmute')); 3998 } else { 3999 mute.removeClass('mejs-unmute').addClass('mejs-mute'); 4000 mute.children('button').attr('title', mejs.i18n.t('Mute')).attr('aria-label', mejs.i18n.t('Mute')); 4001 } 4002 4003 // top/left of full size volume slider background 4004 var totalPosition = volumeTotal.position(); 4005 // position slider 4006 if (mode == 'vertical') { 4007 var 4008 // height of the full size volume slider background 4009 totalHeight = volumeTotal.height(), 4010 4011 // the new top position based on the current volume 4012 // 70% volume on 100px height == top:30px 4013 newTop = totalHeight - (totalHeight * volume); 4014 4015 // handle 4016 volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2))); 4017 4018 // show the current visibility 4019 volumeCurrent.height(totalHeight - newTop ); 4020 volumeCurrent.css('top', totalPosition.top + newTop); 4021 } else { 4022 var 4023 // height of the full size volume slider background 4024 totalWidth = volumeTotal.width(), 4025 4026 // the new left position based on the current volume 4027 newLeft = totalWidth * volume; 4028 4029 // handle 4030 volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2))); 4031 4032 // rezize the current part of the volume bar 4033 volumeCurrent.width( Math.round(newLeft) ); 4034 } 4035 }, 4036 handleVolumeMove = function(e) { 4037 4038 var volume = null, 4039 totalOffset = volumeTotal.offset(); 4040 4041 // calculate the new volume based on the moust position 4042 if (mode === 'vertical') { 4043 4044 var 4045 railHeight = volumeTotal.height(), 4046 newY = e.pageY - totalOffset.top; 4047 4048 volume = (railHeight - newY) / railHeight; 4049 4050 // the controls just hide themselves (usually when mouse moves too far up) 4051 if (totalOffset.top === 0 || totalOffset.left === 0) { 4052 return; 4053 } 4054 4055 } else { 4056 var 4057 railWidth = volumeTotal.width(), 4058 newX = e.pageX - totalOffset.left; 4059 4060 volume = newX / railWidth; 4061 } 4062 4063 // ensure the volume isn't outside 0-1 4064 volume = Math.max(0,volume); 4065 volume = Math.min(volume,1); 4066 4067 // position the slider and handle 4068 positionVolumeHandle(volume); 4069 4070 // set the media object (this will trigger the volumechanged event) 4071 if (volume === 0) { 4072 media.setMuted(true); 4073 } else { 4074 media.setMuted(false); 4075 } 4076 media.setVolume(volume); 4077 }, 4078 mouseIsDown = false, 4079 mouseIsOver = false;
4080 4081 // SLIDER 4082 4083 mute 4084 .hover(function() { 4085 volumeSlider.show(); 4086 mouseIsOver = true; 4087 }, function() { 4088 mouseIsOver = false; 4089 4090 if (!mouseIsDown && mode == 'vertical') { 4091 volumeSlider.hide(); 4092 } 4093 }); 4094 4095 var updateVolumeSlider = function (e) { 4096 4097 var volume = Math.floor(media.volume*100); 4098 4099 volumeSlider.attr({ 4100 'aria-label': mejs.i18n.t('volumeSlider'), 4101 'aria-valuemin': 0, 4102 'aria-valuemax': 100, 4103 'aria-valuenow': volume, 4104 'aria-valuetext': volume+'%', 4105 'role': 'slider', 4106 'tabindex': 0 4107 }); 4108 4109 }; 4110 4111 volumeSlider 4112 .bind('mouseover', function() { 4113 mouseIsOver = true; 4114 }) 4115 .bind('mousedown', function (e) { 4116 handleVolumeMove(e); 4117 t.globalBind('mousemove.vol', function(e) { 4118 handleVolumeMove(e); 4119 }); 4120 t.globalBind('mouseup.vol', function () { 4121 mouseIsDown = false; 4122 t.globalUnbind('.vol'); 4123 4124 if (!mouseIsOver && mode == 'vertical') { 4125 volumeSlider.hide(); 4126 } 4127 }); 4128 mouseIsDown = true; 4129 4130 return false; 4131 }) 4132 .bind('keydown', function (e) { 4133 var keyCode = e.keyCode; 4134 var volume = media.volume; 4135 switch (keyCode) { 4136 case 38: // Up 4137 volume += 0.1; 4138 break; 4139 case 40: // Down 4140 volume = volume - 0.1; 4141 break; 4142 default: 4143 return true; 4144 } 4145 4146 mouseIsDown = false; 4147 positionVolumeHandle(volume); 4148 media.setVolume(volume); 4149 return false; 4150 }); 4151 4152 // MUTE button 4153 mute.find('button').click(function() { 4154 media.setMuted( !media.muted ); 4155 }); 4156 4157 //Keyboard input 4158 mute.find('button').bind('focus', function () { 4159 volumeSlider.show(); 4160 }); 4161 4162 // listen for volume change events from other sources 4163 media.addEventListener('volumechange', function(e) { 4164 if (!mouseIsDown) { 4165 if (media.muted) { 4166 positionVolumeHandle(0); 4167 mute.removeClass('mejs-mute').addClass('mejs-unmute'); 4168 } else { 4169 positionVolumeHandle(media.volume); 4170 mute.removeClass('mejs-unmute').addClass('mejs-mute'); 4171 } 4172 } 4173 updateVolumeSlider(e); 4174 }, false); 4175 4176 // mutes the media and sets the volume icon muted if the initial volume is set to 0 4177 if (player.options.startVolume === 0) { 4178 media.setMuted(true); 4179 } 4180 4181 // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements 4182 if (media.pluginType === 'native') { 4183 media.setVolume(player.options.startVolume); 4184 } 4185 4186 t.container.on('controlsresize', function() { 4187 positionVolumeHandle(media.volume); 4188 }); 4189 } 4190 }); 4191 4192})(mejs.$); 4193 4194(function($) { 4195 4196 $.extend(mejs.MepDefaults, { 4197 usePluginFullScreen: true, 4198 newWindowCallback: function() { return '';}, 4199 fullscreenText: mejs.i18n.t('Fullscreen') 4200 }); 4201 4202 $.extend(MediaElementPlayer.prototype, { 4203 4204 isFullScreen: false, 4205 4206 isNativeFullScreen: false, 4207 4208 isInIframe: false, 4209 4210 buildfullscreen: function(player, controls, layers, media) { 4211 4212 if (!player.isVideo) 4213 return; 4214 4215 player.isInIframe = (window.location != window.parent.location); 4216 4217 // native events 4218 if (mejs.MediaFeatures.hasTrueNativeFullScreen) { 4219 4220 // chrome doesn't alays fire this in an iframe 4221 var func = function(e) { 4222 if (player.isFullScreen) { 4223 if (mejs.MediaFeatures.isFullScreen()) { 4224 player.isNativeFullScreen = true; 4225 // reset the controls once we are fully in full screen 4226 player.setControlsSize(); 4227 } else { 4228 player.isNativeFullScreen = false; 4229 // when a user presses ESC 4230 // make sure to put the player back into place 4231 player.exitFullScreen(); 4232 } 4233 } 4234 }; 4235 4236 player.globalBind(mejs.MediaFeatures.fullScreenEventName, func); 4237 } 4238 4239 var t = this, 4240 fullscreenBtn = 4241 $('<div class="mejs-button mejs-fullscreen-button">' + 4242 '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' + 4243 '</div>') 4244 .appendTo(controls); 4245 4246 if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) { 4247 4248 fullscreenBtn.click(function() { 4249 var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen; 4250 4251 if (isFullScreen) { 4252 player.exitFullScreen(); 4253 } else { 4254 player.enterFullScreen(); 4255 } 4256 }); 4257 4258 } else { 4259 4260 var hideTimeout = null, 4261 supportsPointerEvents = (function() { 4262 // TAKEN FROM MODERNIZR 4263 var element = document.createElement('x'), 4264 documentElement = document.documentElement, 4265 getComputedStyle = window.getComputedStyle, 4266 supports; 4267 if(!('pointerEvents' in element.style)){ 4268 return false;
4269 } 4270 element.style.pointerEvents = 'auto'; 4271 element.style.pointerEvents = 'x'; 4272 documentElement.appendChild(element); 4273 supports = getComputedStyle && 4274 getComputedStyle(element, '').pointerEvents === 'auto'; 4275 documentElement.removeChild(element); 4276 return !!supports; 4277 })(); 4278 4279 // 4280 4281 if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :( 4282 4283 // allows clicking through the fullscreen button and controls down directly to Flash 4284 4285 /* 4286 When a user puts his mouse over the fullscreen button, the controls are disabled 4287 So we put a div over the video and another one on iether side of the fullscreen button 4288 that caputre mouse movement 4289 and restore the controls once the mouse moves outside of the fullscreen button 4290 */ 4291 4292 var fullscreenIsDisabled = false, 4293 restoreControls = function() { 4294 if (fullscreenIsDisabled) { 4295 // hide the hovers 4296 for (var i in hoverDivs) { 4297 hoverDivs[i].hide(); 4298 } 4299 4300 // restore the control bar 4301 fullscreenBtn.css('pointer-events', ''); 4302 t.controls.css('pointer-events', ''); 4303 4304 // prevent clicks from pausing video 4305 t.media.removeEventListener('click', t.clickToPlayPauseCallback); 4306 4307 // store for later 4308 fullscreenIsDisabled = false; 4309 } 4310 }, 4311 hoverDivs = {}, 4312 hoverDivNames = ['top', 'left', 'right', 'bottom'], 4313 i, len, 4314 positionHoverDivs = function() { 4315 var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left, 4316 fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top, 4317 fullScreenBtnWidth = fullscreenBtn.outerWidth(true), 4318 fullScreenBtnHeight = fullscreenBtn.outerHeight(true), 4319 containerWidth = t.container.width(), 4320 containerHeight = t.container.height(); 4321 4322 for (i in hoverDivs) { 4323 hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'}); 4324 } 4325 4326 // over video, but not controls 4327 hoverDivs['top'] 4328 .width( containerWidth ) 4329 .height( fullScreenBtnOffsetTop ); 4330 4331 // over controls, but not the fullscreen button 4332 hoverDivs['left'] 4333 .width( fullScreenBtnOffsetLeft ) 4334 .height( fullScreenBtnHeight ) 4335 .css({top: fullScreenBtnOffsetTop}); 4336 4337 // after the fullscreen button 4338 hoverDivs['right'] 4339 .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth ) 4340 .height( fullScreenBtnHeight ) 4341 .css({top: fullScreenBtnOffsetTop, 4342 left: fullScreenBtnOffsetLeft + fullScreenBtnWidth}); 4343 4344 // under the fullscreen button 4345 hoverDivs['bottom'] 4346 .width( containerWidth ) 4347 .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop ) 4348 .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight}); 4349 }; 4350 4351 t.globalBind('resize', function() { 4352 positionHoverDivs(); 4353 }); 4354 4355 for (i = 0, len = hoverDivNames.length; i < len; i++) { 4356 hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide(); 4357 } 4358 4359 // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash 4360 fullscreenBtn.on('mouseover',function() { 4361 4362 if (!t.isFullScreen) { 4363 4364 var buttonPos = fullscreenBtn.offset(), 4365 containerPos = player.container.offset(); 4366 4367 // move the button in Flash into place 4368 media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false); 4369 4370 // allows click through 4371 fullscreenBtn.css('pointer-events', 'none'); 4372 t.controls.css('pointer-events', 'none'); 4373 4374 // restore click-to-play 4375 t.media.addEventListener('click', t.clickToPlayPauseCallback); 4376 4377 // show the divs that will restore things 4378 for (i in hoverDivs) { 4379 hoverDivs[i].show(); 4380 } 4381 4382 positionHoverDivs(); 4383 4384 fullscreenIsDisabled = true; 4385 } 4386 4387 }); 4388 4389 // restore controls anytime the user enters or leaves fullscreen 4390 media.addEventListener('fullscreenchange', function(e) { 4391 t.isFullScreen = !t.isFullScreen; 4392 // don't allow plugin click to pause video - messes with 4393 // plugin's controls 4394 if (t.isFullScreen) { 4395 t.media.removeEventListener('click', t.clickToPlayPauseCallback); 4396 } else { 4397 t.media.addEventListener('click', t.clickToPlayPauseCallback); 4398 } 4399 restoreControls(); 4400 }); 4401 4402 4403 // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events 4404 // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button 4405 4406 t.globalBind('mousemove', function(e) { 4407 4408 // if the mouse is anywhere but the fullsceen button, then restore it all 4409 if (fullscreenIsDisabled) { 4410 4411 var fullscreenBtnPos = fullscreenBtn.offset(); 4412 4413 4414 if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) || 4415 e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true) 4416 ) { 4417 4418 fullscreenBtn.css('pointer-events', ''); 4419 t.controls.css('pointer-events', ''); 4420 4421 fullscreenIsDisabled = false;
4422 } 4423 } 4424 }); 4425 4426 4427 4428 } else { 4429 4430 // the hover state will show the fullscreen button in Flash to hover up and click 4431 4432 fullscreenBtn 4433 .on('mouseover', function() { 4434 4435 if (hideTimeout !== null) { 4436 clearTimeout(hideTimeout); 4437 delete hideTimeout; 4438 } 4439 4440 var buttonPos = fullscreenBtn.offset(), 4441 containerPos = player.container.offset(); 4442 4443 media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true); 4444 4445 }) 4446 .on('mouseout', function() { 4447 4448 if (hideTimeout !== null) { 4449 clearTimeout(hideTimeout); 4450 delete hideTimeout; 4451 } 4452 4453 hideTimeout = setTimeout(function() { 4454 media.hideFullscreenButton(); 4455 }, 1500); 4456 4457 4458 }); 4459 } 4460 } 4461 4462 player.fullscreenBtn = fullscreenBtn; 4463 4464 t.globalBind('keydown',function (e) { 4465 if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) { 4466 player.exitFullScreen(); 4467 } 4468 }); 4469 4470 t.normalHeight = 0; 4471 t.normalWidth = 0; 4472 4473 }, 4474 4475 cleanfullscreen: function(player) { 4476 player.exitFullScreen(); 4477 }, 4478 4479 containerSizeTimeout: null, 4480 4481 enterFullScreen: function() { 4482 4483 var t = this; 4484 4485 // firefox+flash can't adjust plugin sizes without resetting :( 4486 if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) { 4487 //t.media.setFullscreen(true); 4488 //player.isFullScreen = true; 4489 return; 4490 } 4491 4492 // set it to not show scroll bars so 100% will work 4493 $(document.documentElement).addClass('mejs-fullscreen'); 4494 4495 // store sizing 4496 t.normalHeight = t.container.height(); 4497 t.normalWidth = t.container.width(); 4498 4499 // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now) 4500 if (t.media.pluginType === 'native') { 4501 if (mejs.MediaFeatures.hasTrueNativeFullScreen) { 4502 4503 mejs.MediaFeatures.requestFullScreen(t.container[0]); 4504 //return; 4505 4506 if (t.isInIframe) { 4507 // sometimes exiting from fullscreen doesn't work 4508 // notably in Chrome <iframe>. Fixed in version 17 4509 setTimeout(function checkFullscreen() { 4510 4511 if (t.isNativeFullScreen) { 4512 var zoomMultiplier = window["devicePixelRatio"] || 1, 4513 // Use a percent error margin since devicePixelRatio is a float and not exact. 4514 percentErrorMargin = 0.002, // 0.2% 4515 windowWidth = zoomMultiplier * $(window).width(), 4516 screenWidth = screen.width, 4517 // ** 13twelve 4518 // Screen width is sort of useless: http://www.quirksmode.org/blog/archives/2013/11/screenwidth_is.html 4519 // My rMBP ignores devicePixelRatio when returning the values, so fullscreen would always fail the "suddenly not fullscreen" test 4520 // Theory: the gap between reported values should give us an indication of browser behavior with screen.width and devicePixelRatio 4521 zoomedWindowWidth = zoomMultiplier * windowWidth; 4522 4523 if (Math.abs(screenWidth-windowWidth) > Math.abs(screenWidth-zoomedWindowWidth)) { 4524 // screen.width is likely true pixels, not CSS pixels, so we need to use the zoomed window width for comparison 4525 windowWidth = zoomedWindowWidth; 4526 } 4527 // ** / 13twelve 4528 4529 var absDiff = Math.abs(screenWidth - windowWidth), 4530 marginError = screenWidth * percentErrorMargin; 4531 4532 // check if the video is suddenly not really fullscreen 4533 if (absDiff > marginError) { 4534 // manually exit 4535 t.exitFullScreen(); 4536 } else { 4537 // test again 4538 setTimeout(checkFullscreen, 500); 4539 } 4540 } 4541 4542 }, 1000); 4543 } 4544 4545 } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) { 4546 t.media.webkitEnterFullscreen(); 4547 return; 4548 } 4549 } 4550 4551 // check for iframe launch 4552 if (t.isInIframe) { 4553 var url = t.options.newWindowCallback(this); 4554 4555 4556 if (url !== '') { 4557 4558 // launch immediately 4559 if (!mejs.MediaFeatures.hasTrueNativeFullScreen) { 4560 t.pause(); 4561 window.open(url, t.id, 'top=0,left=0,width=' + screen.availWi
4561dth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no'); 4562 return; 4563 } else { 4564 setTimeout(function() { 4565 if (!t.isNativeFullScreen) { 4566 t.pause(); 4567 window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no'); 4568 } 4569 }, 250); 4570 } 4571 } 4572 4573 } 4574 4575 // full window code 4576 4577 4578 4579 // make full size 4580 t.container 4581 .addClass('mejs-container-fullscreen') 4582 .width('100%') 4583 .height('100%'); 4584 //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000}); 4585 4586 // Only needed for safari 5.1 native full screen, can cause display issues elsewhere 4587 // Actually, it seems to be needed for IE8, too 4588 //if (mejs.MediaFeatures.hasTrueNativeFullScreen) { 4589 t.containerSizeTimeout = setTimeout(function() { 4590 t.container.css({width: '100%', height: '100%'}); 4591 t.setControlsSize(); 4592 }, 500); 4593 //} 4594 4595 if (t.media.pluginType === 'native') { 4596 t.$media 4597 .width('100%') 4598 .height('100%'); 4599 } else { 4600 t.container.find('.mejs-shim') 4601 .width('100%') 4602 .height('100%'); 4603 4604 //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) { 4605 t.media.setVideoSize($(window).width(),$(window).height()); 4606 //} 4607 } 4608 4609 t.layers.children('div') 4610 .width('100%') 4611 .height('100%'); 4612 4613 if (t.fullscreenBtn) { 4614 t.fullscreenBtn 4615 .removeClass('mejs-fullscreen') 4616 .addClass('mejs-unfullscreen'); 4617 } 4618 4619 t.setControlsSize(); 4620 t.isFullScreen = true; 4621 4622 t.container.find('.mejs-captions-text').css('font-size', screen.width / t.width * 1.00 * 100 + '%'); 4623 t.container.find('.mejs-captions-position').css('bottom', '45px'); 4624 4625 t.container.trigger('enteredfullscreen'); 4626 }, 4627 4628 exitFullScreen: function() { 4629 4630 var t = this; 4631 4632 // Prevent container from attempting to stretch a second time 4633 clearTimeout(t.containerSizeTimeout); 4634 4635 // firefox can't adjust plugins 4636 if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) { 4637 t.media.setFullscreen(false); 4638 //player.isFullScreen = false; 4639 return; 4640 } 4641 4642 // come outo of native fullscreen 4643 if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) { 4644 mejs.MediaFeatures.cancelFullScreen(); 4645 } 4646 4647 // restore scroll bars to document 4648 $(document.documentElement).removeClass('mejs-fullscreen'); 4649 4650 t.container 4651 .removeClass('mejs-container-fullscreen') 4652 .width(t.normalWidth) 4653 .height(t.normalHeight); 4654 4655 if (t.media.pluginType === 'native') { 4656 t.$media 4657 .width(t.normalWidth) 4658 .height(t.normalHeight); 4659 } else { 4660 t.container.find('.mejs-shim') 4661 .width(t.normalWidth) 4662 .height(t.normalHeight); 4663 4664 t.media.setVideoSize(t.normalWidth, t.normalHeight); 4665 } 4666 4667 t.layers.children('div') 4668 .width(t.normalWidth) 4669 .height(t.normalHeight); 4670 4671 t.fullscreenBtn 4672 .removeClass('mejs-unfullscreen') 4673 .addClass('mejs-fullscreen'); 4674 4675 t.setControlsSize(); 4676 t.isFullScreen = false; 4677 4678 t.container.find('.mejs-captions-text').css('font-size',''); 4679 t.container.find('.mejs-captions-position').css('bottom', ''); 4680 4681 t.container.trigger('exitedfullscreen'); 4682 } 4683 }); 4684 4685})(mejs.$); 4686 4687(function($) { 4688 4689 // Speed 4690 $.extend(mejs.MepDefaults, { 4691 4692 // We also support to pass object like this: 4693 // [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...] 4694 speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'], 4695 4696 defaultSpeed: '1.00', 4697 4698 speedChar: 'x' 4699 4700 }); 4701 4702 $.extend(MediaElementPlayer.prototype, { 4703 4704 buildspeed: function(player, controls, layers, media) { 4705 var t = this; 4706 4707 if (t.media.pluginType == 'native') { 4708 var 4709 speedButton = null, 4710 speedSelector = null, 4711 playbackSpeed = null, 4712 inputId = null; 4713 4714 var speeds = []; 4715 var defaultInArray = false; 4716 for (var i=0, len=t.options.speeds.length; i < len; i++) { 4717 var s = t.options.speeds[i]; 4718 if (typeof(s) === 'string'){ 4719 speeds.push({ 4720 name: s + t.options.speedChar, 4721 value: s 4722 }); 4723 if(s === t.options.defaultSpeed) { 4724 defaultInArray = true; 4725 } 4726 } 4727 else { 4728 speeds.push(s); 4729 if(s.value === t.options.defaultSpeed) { 4730 defaultInArray = true; 4731 } 4732 } 4733 } 4734 4735 if (!defaultInArray) { 4736 speeds.push({ 4737 name: t.options.defaultSpeed + t.options.speedChar, 4738 value: t.options.defaultSpeed 4739 }); 4740 } 4741 4742 speeds.sort(function(a, b) { 4743 return parseFloat(b.value) - parseFloat(a.value); 4744 }); 4745 4746 var getSpeedNameFromValue = function(value) { 4747 for(i=0,len=speeds.length; i <len; i++) { 4748 if (speeds[i].value === value) { 4749 return speeds[i].name; 4750 } 4751 } 4752 }; 4753 4754 var html = '<div class="mejs-button mejs-speed-button">' + 4755 '<button type="button">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>' + 4756 '<div class="mejs-speed-selector">' + 4757 '<ul>'; 4758 4759 for (i = 0, il = speeds.length; i<il; i++) { 4760 inputId = t.id + '-speed-' + speeds[i].value; 4761 html += '<li>' + 4762 '<input type="radio" name="speed" ' + 4763 'value="' + speeds[i].value + '" ' + 4764 'id="' + inputId + '" ' + 4765 (speeds[i].value === t.options.defaultSpeed ? ' checked' : '') + 4766 ' />' + 4767 '<label for="' + inputId + '" ' + 4768 (speeds[i].value === t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') + 4769 '>' + speeds[i].name + '</label>' + 4770 '</li>'; 4771 } 4772 html += '</ul></div></div>'; 4773 4774 speedButton = $(html).appendTo(controls); 4775 speedSelector = speedButton.find('.mejs-speed-selector'); 4776 4777 playbackSpeed = t.options.defaultSpeed; 4778 4779 media.addEventListener('loadedmetadata', function(e) { 4780 if (playbackSpeed) { 4781 media.playbackRate = parseFloat(playbackSpeed); 4782 } 4783 }, true); 4784 4785 speedSelector 4786 .on('click', 'input[type="radio"]', function() { 4787 var newSpeed = $(this).attr('value'); 4788 playbackSpeed = newSpeed; 4789 media.playbackRate = parseFloat(newSpeed); 4790 speedButton.find('button').html(getSpeedNameFromValue(newSpeed)); 4791 speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected'); 4792 speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected'); 4793 }); 4794 speedButton 4795 .one( 'mouseenter focusin', function() { 4796 speedSelector 4797 .height( 4798 speedButton.find('.mejs-speed-selector ul').outerHeight(true) + 4799 speedButton.find('.mejs-speed-translations').outerHeight(true)) 4800 .css('top', (-1 * speedSelector.height()) + 'px'); 4801 }); 4802 } 4803 } 4804 }); 4805 4806})(mejs.$); 4807 4808(function($) { 4809 4810 // add extra default options 4811 $.extend(mejs.MepDefaults, { 4812 // this will automatically turn on a <track> 4813 startLanguage: '', 4814 4815 tracksText: mejs.i18n.t('Captions/Subtitles'), 4816 4817 // By default, no WAI-ARIA live region - don't make a 4818 // screen reader speak captions over an audio track. 4819 tracksAriaLive: false, 4820 4821 // option to remove the [cc] button when no <track kind="subtitles"> are present 4822 hideCaptionsButtonWhenEmpty: true, 4823 4824 // If true and we only have one track, change captions to popup 4825 toggleCaptionsButtonWhenOnlyOne: false, 4826 4827 // #id or .class 4828 slidesSelector: '' 4829 }); 4830 4831 $.extend(MediaElementPlayer.prototype, { 4832 4833 hasChapters: false, 4834 4835 cleartracks: function(player, controls, layers, media){ 4836 if(player) { 4837 if(player.captions) player.captions.remove(); 4838 if(player.chapters) player.chapters.remove(); 4839 if(player.captionsText) player.captionsText.remove(); 4840 if(player.captionsButton) player.captionsButton.remove(); 4841 } 4842 }, 4843 buildtracks: function(player, controls, layers, media) { 4844 if (player.tracks.length === 0) 4845 return; 4846 4847 var t = this, 4848 attr = t.options.tracksAriaLive ? 4849 'role="log" aria-live="assertive" aria-atomic="false"' : '', 4850 i; 4851 4852 if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide 4853 for (i = t.domNode.textTracks.length - 1; i >= 0; i--) { 4854 t.domNode.textTracks[i].mode = "hidden"; 4855 } 4856 } 4857 t.cleartracks(player, controls, layers, media); 4858 player.chapters = 4859 $('<div class="mejs-chapters mejs-layer"></div>') 4860 .prependTo(layers).hide(); 4861 player.captions = 4862 $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' + 4863 attr + '><span class="mejs-captions-text"></span></div></div>') 4864 .prependTo(layers).hide(); 4865 player.captionsText = player.captions.find('.mejs-captions-text'); 4866 player.captionsButton = 4867 $('<div class="mejs-button mejs-captions-button">'+ 4868 '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+ 4869 '<div class="mejs-captions-selector">'+ 4870 '<ul>'+ 4871 '<li>'+ 4872 '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' + 4873 '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') +'</label>'+ 4874 '</li>' + 4875 '</ul>'+ 4876 '</div>'+ 4877 '</div>') 4878 .appendTo(controls); 4879 4880 4881 var subtitleCount = 0; 4882 for (i=0; i<player.tracks.length; i++) { 4883 if (player.tracks[i].kind == 'subtitles') { 4884 subtitleCount++; 4885 } 4886 } 4887 4888 // if only one language then just make the button a toggle 4889 if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){ 4890 // click 4891 player.captionsButton.on('click',function() { 4892 if (player.selectedTrack === null) { 4893 lang = player.tracks[0].srclang; 4894 } else { 4895 lang = 'none'; 4896 } 4897 player.setTrack(lang); 4898 }); 4899 } else { 4900 // hover or keyboard focus 4901 player.captionsButton.on( 'mouseenter focusin', function() { 4902 $(this).find('.mejs-captions-selector').css('visibility','visible'); 4903 }) 4904 4905 // handle clicks to the language radio buttons 4906 .on('click','input[type=radio]',function() { 4907 lang = this.value; 4908 player.setTrack(lang); 4909 }); 4910 4911 player.captionsButton.on( 'mouseleave focusout', function() { 4912 $(this).find(".mejs-captions-selector").css("visibility","hidden"); 4913 }); 4914 4915 } 4916 4917 if (!player.options.alwaysShowControls) { 4918 // move with controls 4919 player.container 4920 .bind('controlsshown', function () { 4921 // push captions above controls 4922 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover'); 4923 4924 }) 4925 .bind('controlshidden', function () { 4926 if (!media.paused) { 4927 // move back to normal place 4928 player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover'); 4929 } 4930 }); 4931 } else { 4932 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover'); 4933 } 4934 4935 player.trackToLoad = -1; 4936 player.selectedTrack = null; 4937 player.isLoadingTrack = false;
4938 4939 // add to list 4940 for (i=0; i<player.tracks.length; i++) { 4941 if (player.tracks[i].kind == 'subtitles') { 4942 player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label); 4943 } 4944 } 4945 4946 // start loading tracks 4947 player.loadNextTrack(); 4948 4949 media.addEventListener('timeupdate',function(e) { 4950 player.displayCaptions(); 4951 }, false); 4952 4953 if (player.options.slidesSelector !== '') { 4954 player.slidesContainer = $(player.options.slidesSelector); 4955 4956 media.addEventListener('timeupdate',function(e) { 4957 player.displaySlides(); 4958 }, false); 4959 4960 } 4961 4962 media.addEventListener('loadedmetadata', function(e) { 4963 player.displayChapters(); 4964 }, false); 4965 4966 player.container.hover( 4967 function () { 4968 // chapters 4969 if (player.hasChapters) { 4970 player.chapters.css('visibility','visible'); 4971 player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight()); 4972 } 4973 }, 4974 function () { 4975 if (player.hasChapters && !media.paused) { 4976 player.chapters.fadeOut(200, function() { 4977 $(this).css('visibility','hidden'); 4978 $(this).css('display','block'); 4979 }); 4980 } 4981 }); 4982 4983 t.container.on('controlsresize', function() { 4984 t.adjustLanguageBox(); 4985 }); 4986 4987 // check for autoplay 4988 if (player.node.getAttribute('autoplay') !== null) { 4989 player.chapters.css('visibility','hidden'); 4990 } 4991 }, 4992 4993 setTrack: function(lang){ 4994 4995 var t = this, 4996 i; 4997 4998 if (lang == 'none') { 4999 t.selectedTrack = null; 5000 t.captionsButton.removeClass('mejs-captions-enabled'); 5001 } else { 5002 for (i=0; i<t.tracks.length; i++) { 5003 if (t.tracks[i].srclang == lang) { 5004 if (t.selectedTrack === null) 5005 t.captionsButton.addClass('mejs-captions-enabled'); 5006 t.selectedTrack = t.tracks[i]; 5007 t.captions.attr('lang', t.selectedTrack.srclang); 5008 t.displayCaptions(); 5009 break; 5010 } 5011 } 5012 } 5013 }, 5014 5015 loadNextTrack: function() { 5016 var t = this; 5017 5018 t.trackToLoad++; 5019 if (t.trackToLoad < t.tracks.length) { 5020 t.isLoadingTrack = true; 5021 t.loadTrack(t.trackToLoad); 5022 } else { 5023 // add done? 5024 t.isLoadingTrack = false; 5025 5026 t.checkForTracks(); 5027 } 5028 }, 5029 5030 loadTrack: function(index){ 5031 var 5032 t = this, 5033 track = t.tracks[index], 5034 after = function() { 5035 5036 track.isLoaded = true; 5037 5038 t.enableTrackButton(track.srclang, track.label); 5039 5040 t.loadNextTrack(); 5041 5042 }; 5043 5044 5045 $.ajax({ 5046 url: track.src, 5047 dataType: "text", 5048 success: function(d) { 5049 5050 // parse the loaded file 5051 if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) { 5052 track.entries = mejs.TrackFormatParser.dfxp.parse(d); 5053 } else { 5054 track.entries = mejs.TrackFormatParser.webvtt.parse(d); 5055 } 5056 5057 after(); 5058 5059 if (track.kind == 'chapters') { 5060 t.media.addEventListener('play', function(e) { 5061 if (t.media.duration > 0) { 5062 t.displayChapters(track); 5063 } 5064 }, false); 5065 } 5066 5067 if (track.kind == 'slides') { 5068 t.setupSlides(track); 5069 } 5070 }, 5071 error: function() { 5072 t.removeTrackButton(track.srclang); 5073 t.loadNextTrack(); 5074 } 5075 }); 5076 }, 5077 5078 enableTrackButton: function(lang, label) { 5079 var t = this; 5080 5081 if (label === '') { 5082 label = mejs.language.codes[lang] || lang; 5083 } 5084 5085 t.captionsButton 5086 .find('input[value=' + lang + ']') 5087 .prop('disabled',false) 5088 .siblings('label') 5089 .html( label ); 5090 5091 // auto select 5092 if (t.options.startLanguage == lang) { 5093 $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click'); 5094 } 5095 5096 t.adjustLanguageBox(); 5097 }, 5098 5099 removeTrackButton: function(lang) { 5100 var t = this; 5101 5102 t.captionsButton.find('input[value=' + lang + ']').closest('li').remove(); 5103 5104 t.adjustLanguageBox(); 5105 }, 5106 5107 addTrackButton: function(lang, label) { 5108 var t = this; 5109 if (label === '') { 5110 label = mejs.language.codes[lang] || lang; 5111 } 5112 5113 t.captionsButton.find('ul').append( 5114 $('<li>'+ 5115 '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' + 5116 '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+ 5117 '</li>') 5118 ); 5119 5120 t.adjustLanguageBox(); 5121 5122 // remove this from the dropdownlist (if it exists) 5123 t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove(); 5124 }, 5125 5126 adjustLanguageBox:function() { 5127 var t = this; 5128 // adjust the size of the outer box 5129 t.captionsButton.find('.mejs-captions-selector').height( 5130 t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) + 5131 t.captionsButton.find('.mejs-captions-translations').outerHeight(true) 5132 ); 5133 }, 5134 5135 checkForTracks: function() { 5136 var 5137 t = this, 5138 hasSubtitles = false;
5139 5140 // check if any subtitles 5141 if (t.options.hideCaptionsButtonWhenEmpty) { 5142 for (i=0; i<t.tracks.length; i++) { 5143 if (t.tracks[i].kind == 'subtitles' && t.tracks[i].isLoaded) { 5144 hasSubtitles = true; 5145 break; 5146 } 5147 } 5148 5149 if (!hasSubtitles) { 5150 t.captionsButton.hide(); 5151 t.setControlsSize(); 5152 } 5153 } 5154 }, 5155 5156 displayCaptions: function() { 5157 5158 if (typeof this.tracks == 'undefined') 5159 return; 5160 5161 var 5162 t = this, 5163 i, 5164 track = t.selectedTrack; 5165 5166 if (track !== null && track.isLoaded) { 5167 for (i=0; i<track.entries.times.length; i++) { 5168 if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) { 5169 // Set the line before the timecode as a class so the cue can be targeted if needed 5170 t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || '')); 5171 t.captions.show().height(0); 5172 return; // exit out if one is visible; 5173 } 5174 } 5175 t.captions.hide(); 5176 } else { 5177 t.captions.hide(); 5178 } 5179 }, 5180 5181 setupSlides: function(track) { 5182 var t = this; 5183 5184 t.slides = track; 5185 t.slides.entries.imgs = [t.slides.entries.text.length]; 5186 t.showSlide(0); 5187 5188 }, 5189 5190 showSlide: function(index) { 5191 if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') { 5192 return; 5193 } 5194 5195 var t = this, 5196 url = t.slides.entries.text[index], 5197 img = t.slides.entries.imgs[index]; 5198 5199 if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') { 5200 5201 t.slides.entries.imgs[index] = img = $('<img src="' + url + '">') 5202 .on('load', function() { 5203 img.appendTo(t.slidesContainer) 5204 .hide() 5205 .fadeIn() 5206 .siblings(':visible') 5207 .fadeOut(); 5208 5209 }); 5210 5211 } else { 5212 5213 if (!img.is(':visible') && !img.is(':animated')) { 5214 5215 // 5216 5217 img.fadeIn() 5218 .siblings(':visible') 5219 .fadeOut(); 5220 } 5221 } 5222 5223 }, 5224 5225 displaySlides: function() { 5226 5227 if (typeof this.slides == 'undefined') 5228 return; 5229 5230 var 5231 t = this, 5232 slides = t.slides, 5233 i; 5234 5235 for (i=0; i<slides.entries.times.length; i++) { 5236 if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){ 5237 5238 t.showSlide(i); 5239 5240 return; // exit out if one is visible; 5241 } 5242 } 5243 }, 5244 5245 displayChapters: function() { 5246 var 5247 t = this, 5248 i; 5249 5250 for (i=0; i<t.tracks.length; i++) { 5251 if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) { 5252 t.drawChapters(t.tracks[i]); 5253 t.hasChapters = true; 5254 break; 5255 } 5256 } 5257 }, 5258 5259 drawChapters: function(chapters) { 5260 var 5261 t = this, 5262 i, 5263 dur, 5264 //width, 5265 //left, 5266 percent = 0, 5267 usedPercent = 0; 5268 5269 t.chapters.empty(); 5270 5271 for (i=0; i<chapters.entries.times.length; i++) { 5272 dur = chapters.entries.times[i].stop - chapters.entries.times[i].start; 5273 percent = Math.floor(dur / t.media.duration * 100); 5274 if (percent + usedPercent > 100 || // too large 5275 i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in 5276 { 5277 percent = 100 - usedPercent; 5278 } 5279 //width = Math.floor(t.width * dur / t.media.duration); 5280 //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration); 5281 //if (left + width > t.width) { 5282 // width = t.width - left; 5283 //} 5284 5285 t.chapters.append( $( 5286 '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' + 5287 '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' + 5288 '<span class="ch-title">' + chapters.entries.text[i] + '</span>' + 5289 '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '–' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' + 5290 '</div>' + 5291 '</div>')); 5292 usedPercent += percent; 5293 } 5294 5295 t.chapters.find('div.mejs-chapter').click(function() { 5296 t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) ); 5297 if (t.media.paused) { 5298 t.media.play(); 5299 } 5300 }); 5301 5302 t.chapters.show(); 5303 } 5304 }); 5305 5306 5307 5308 mejs.language = { 5309 codes: { 5310 af:'Afrikaans', 5311 sq:'Albanian', 5312 ar:'Arabic', 5313 be:'Belarusian', 5314 bg:'Bulgarian', 5315 ca:'Catalan', 5316 zh:'Chinese', 5317 'zh-cn':'Chinese Simplified', 5318 'zh-tw':'Chinese Traditional', 5319 hr:'Croatian', 5320 cs:'Czech', 5321 da:'Danish', 5322 nl:'Dutch', 5323 en:'English', 5324 et:'Estonian', 5325 fl:'Filipino', 5326 fi:'Finnish', 5327 fr:'French', 5328 gl:'Galician', 5329 de:'German', 5330 el:'Greek', 5331 ht:'Haitian Creole', 5332 iw:'Hebrew', 5333 hi:'Hindi', 5334 hu:'Hungarian', 5335 is:'Icelandic', 5336 id:'Indonesian', 5337 ga:'Irish', 5338 it:'Italian', 5339 ja:'Japanese', 5340 ko:'Korean', 5341 lv:'Latvian', 5342 lt:'Lithuanian', 5343 mk:'Macedonian', 5344 ms:'Malay', 5345 mt:'Maltese', 5346 no:'Norwegian', 5347 fa:'Persian', 5348 pl:'Polish', 5349 pt:'Portuguese', 5350 // 'pt-pt':'Portuguese (Portugal)', 5351 ro:'Romanian', 5352 ru:'Russian', 5353 sr:'Serbian', 5354 sk:'Slovak', 5355 sl:'Slovenian', 5356 es:'Spanish', 5357 sw:'Swahili', 5358 sv:'Swedish', 5359 tl:'Tagalog', 5360 th:'Thai', 5361 tr:'Turkish', 5362 uk:'Ukrainian', 5363 vi:'Vietnamese', 5364 cy:'Welsh', 5365 yi:'Yiddish' 5366 } 5367 }; 5368 5369 /* 5370 Parses WebVTT format which should be formatted as 5371 ================================ 5372 WEBVTT 5373 5374 1 5375 00:00:01,1 --> 00:00:05,000 5376 A line of text 5377 5378 2 5379 00:01:15,1 --> 00:02:05,000 5380 A second line of text 5381 5382 =============================== 5383 5384 Adapted from: http://www.delphiki.com/html5/playr 5385 */ 5386 mejs.TrackFormatParser = { 5387 webvtt: {
5388 pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/, 5389 5390 parse: function(trackText) { 5391 var 5392 i = 0, 5393 lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/), 5394 entries = {text:[], times:[]}, 5395 timecode, 5396 text, 5397 identifier; 5398 for(; i<lines.length; i++) { 5399 timecode = this.pattern_timecode.exec(lines[i]); 5400 5401 if (timecode && i<lines.length) { 5402 if ((i - 1) >= 0 && lines[i - 1] !== '') { 5403 identifier = lines[i - 1]; 5404 } 5405 i++; 5406 // grab all the (possibly multi-line) text that follows 5407 text = lines[i]; 5408 i++; 5409 while(lines[i] !== '' && i<lines.length){ 5410 text = text + '\n' + lines[i]; 5411 i++; 5412 } 5413 text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>"); 5414 // Text is in a different array so I can use .join 5415 entries.text.push(text); 5416 entries.times.push( 5417 { 5418 identifier: identifier, 5419 start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]), 5420 stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]), 5421 settings: timecode[5] 5422 }); 5423 } 5424 identifier = ''; 5425 } 5426 return entries; 5427 } 5428 }, 5429 // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420 5430 dfxp: { 5431 parse: function(trackText) { 5432 trackText = $(trackText).filter("tt"); 5433 var 5434 i = 0, 5435 container = trackText.children("div").eq(0), 5436 lines = container.find("p"), 5437 styleNode = trackText.find("#" + container.attr("style")), 5438 styles, 5439 text, 5440 entries = {text:[], times:[]}; 5441 5442 5443 if (styleNode.length) { 5444 var attributes = styleNode.removeAttr("id").get(0).attributes; 5445 if (attributes.length) { 5446 styles = {}; 5447 for (i = 0; i < attributes.length; i++) { 5448 styles[attributes[i].name.split(":")[1]] = attributes[i].value; 5449 } 5450 } 5451 } 5452 5453 for(i = 0; i<lines.length; i++) { 5454 var style; 5455 var _temp_times = { 5456 start: null, 5457 stop: null, 5458 style: null 5459 }; 5460 if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin")); 5461 if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end")); 5462 if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end")); 5463 if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin")); 5464 if (styles) { 5465 style = ""; 5466 for (var _style in styles) { 5467 style += _style + ":" + styles[_style] + ";"; 5468 } 5469 } 5470 if (style) _temp_times.style = style; 5471 if (_temp_times.start === 0) _temp_times.start = 0.200; 5472 entries.times.push(_temp_times); 5473 text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>"); 5474 entries.text.push(text); 5475 if (entries.times.start === 0) entries.times.start = 2; 5476 } 5477 return entries; 5478 } 5479 }, 5480 split2: function (text, regex) { 5481 // normal version for compliant browsers 5482 // see below for IE fix 5483 return text.split(regex); 5484 } 5485 }; 5486 5487 // test for browsers with bad String.split method. 5488 if ('x\n\ny'.split(/\n/gi).length != 3) { 5489 // add super slow IE8 and below version 5490 mejs.TrackFormatParser.split2 = function(text, regex) { 5491 var 5492 parts = [], 5493 chunk = '', 5494 i; 5495 5496 for (i=0; i<text.length; i++) { 5497 chunk += text.substring(i,i+1); 5498 if (regex.test(chunk)) { 5499 parts.push(chunk.replace(regex, '')); 5500 chunk = ''; 5501 } 5502 } 5503 parts.push(chunk); 5504 return parts; 5505 }; 5506 } 5507 5508})(mejs.$); 5509 5510/* 5511* ContextMenu Plugin 5512* 5513* 5514*/ 5515 5516(function($) { 5517 5518$.extend(mejs.MepDefaults, 5519 { 'contextMenuItems': [ 5520 // demo of a fullscreen option 5521 { 5522 render: function(player) { 5523 5524 // check for fullscreen plugin 5525 if (typeof player.enterFullScreen == 'undefined') 5526 return null; 5527 5528 if (player.isFullScreen) { 5529 return mejs.i18n.t('Turn off Fullscreen'); 5530 } else { 5531 return mejs.i18n.t('Go Fullscreen'); 5532 } 5533 }, 5534 click: function(player) { 5535 if (player.isFullScreen) { 5536 player.exitFullScreen(); 5537 } else { 5538 player.enterFullScreen(); 5539 } 5540 } 5541 } 5542 , 5543 // demo of a mute/unmute button 5544 { 5545 render: function(player) { 5546 if (player.media.muted) { 5547 return mejs.i18n.t('Unmute'); 5548 } else { 5549 return mejs.i18n.t('Mute'); 5550 } 5551 }, 5552 click: function(player) { 5553 if (player.media.muted) { 5554 player.setMuted(false); 5555 } else { 5556 player.setMuted(true); 5557 } 5558 } 5559 }, 5560 // separator 5561 { 5562 isSeparator: true 5563 } 5564 , 5565 // demo of simple download video 5566 { 5567 render: function(player) { 5568 return mejs.i18n.t('Download Video'); 5569 }, 5570 click: function(player) { 5571 window.location.href = player.media.currentSrc; 5572 } 5573 } 5574 ]} 5575); 5576 5577 5578 $.extend(MediaElementPlayer.prototype, { 5579 buildcontextmenu: function(player, controls, layers, media) { 5580 5581 // create context menu
5582 player.contextMenu = $('<div class="mejs-contextmenu"></div>') 5583 .appendTo($('body')) 5584 .hide(); 5585 5586 // create events for showing context menu 5587 player.container.bind('contextmenu', function(e) { 5588 if (player.isContextMenuEnabled) { 5589 e.preventDefault(); 5590 player.renderContextMenu(e.clientX-1, e.clientY-1); 5591 return false; 5592 } 5593 }); 5594 player.container.bind('click', function() { 5595 player.contextMenu.hide(); 5596 }); 5597 player.contextMenu.bind('mouseleave', function() { 5598 5599 // 5600 player.startContextMenuTimer(); 5601 5602 }); 5603 }, 5604 5605 cleancontextmenu: function(player) { 5606 player.contextMenu.remove(); 5607 }, 5608 5609 isContextMenuEnabled: true, 5610 enableContextMenu: function() { 5611 this.isContextMenuEnabled = true; 5612 }, 5613 disableContextMenu: function() { 5614 this.isContextMenuEnabled = false; 5615 }, 5616 5617 contextMenuTimeout: null, 5618 startContextMenuTimer: function() { 5619 // 5620 5621 var t = this; 5622 5623 t.killContextMenuTimer(); 5624 5625 t.contextMenuTimer = setTimeout(function() { 5626 t.hideContextMenu(); 5627 t.killContextMenuTimer(); 5628 }, 750); 5629 }, 5630 killContextMenuTimer: function() { 5631 var timer = this.contextMenuTimer; 5632 5633 // 5634 5635 if (timer != null) { 5636 clearTimeout(timer); 5637 delete timer; 5638 timer = null; 5639 } 5640 }, 5641 5642 hideContextMenu: function() { 5643 this.contextMenu.hide(); 5644 }, 5645 5646 renderContextMenu: function(x,y) { 5647 5648 // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly 5649 var t = this, 5650 html = '', 5651 items = t.options.contextMenuItems; 5652 5653 for (var i=0, il=items.length; i<il; i++) { 5654 5655 if (items[i].isSeparator) { 5656 html += '<div class="mejs-contextmenu-separator"></div>'; 5657 } else { 5658 5659 var rendered = items[i].render(t); 5660 5661 // render can return null if the item doesn't need to be used at the moment 5662 if (rendered != null) { 5663 html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>'; 5664 } 5665 } 5666 } 5667 5668 // position and show the context menu 5669 t.contextMenu 5670 .empty() 5671 .append($(html)) 5672 .css({top:y, left:x}) 5673 .show(); 5674 5675 // bind events 5676 t.contextMenu.find('.mejs-contextmenu-item').each(function() { 5677 5678 // which one is this? 5679 var $dom = $(this), 5680 itemIndex = parseInt( $dom.data('itemindex'), 10 ), 5681 item = t.options.contextMenuItems[itemIndex]; 5682 5683 // bind extra functionality? 5684 if (typeof item.show != 'undefined') 5685 item.show( $dom , t); 5686 5687 // bind click action 5688 $dom.click(function() { 5689 // perform click action 5690 if (typeof item.click != 'undefined') 5691 item.click(t); 5692 5693 // close 5694 t.contextMenu.hide(); 5695 }); 5696 }); 5697 5698 // stop the controls from hiding 5699 setTimeout(function() { 5700 t.killControlsTimer('rev3'); 5701 }, 100); 5702 5703 } 5704 }); 5705 5706})(mejs.$); 5707(function($) { 5708 // skip back button 5709 5710 $.extend(mejs.MepDefaults, { 5711 skipBackInterval: 30, 5712 // %1 will be replaced with skipBackInterval in this string 5713 skipBackText: mejs.i18n.t('Skip back %1 seconds') 5714 }); 5715 5716 $.extend(MediaElementPlayer.prototype, { 5717 buildskipback: function(player, controls, layers, media) { 5718 var 5719 t = this, 5720 // Replace %1 with skip back interval 5721 backText = t.options.skipBackText.replace('%1', t.options.skipBackInterval), 5722 // create the loop button 5723 loop = 5724 $('<div class="mejs-button mejs-skip-back-button">' + 5725 '<button type="button" aria-controls="' + t.id + '" title="' + backText + '" aria-label="' + backText + '">' + t.options.skipBackInterval + '</button>' + 5726 '</div>') 5727 // append it to the toolbar 5728 .appendTo(controls) 5729 // add a click toggle event 5730 .click(function() { 5731 media.setCurrentTime(Math.max(media.currentTime - t.options.skipBackInterval, 0)); 5732 $(this).find('button').blur(); 5733 }); 5734 } 5735 }); 5736 5737})(mejs.$); 5738 5739/** 5740 * Postroll plugin 5741 */ 5742(function($) { 5743 5744 $.extend(mejs.MepDefaults, { 5745 postrollCloseText: mejs.i18n.t('Close') 5746 }); 5747 5748 // Postroll 5749 $.extend(MediaElementPlayer.prototype, { 5750 buildpostroll: function(player, controls, layers, media) { 5751 var 5752 t = this, 5753 postrollLink = t.container.find('link[rel="postroll"]').attr('href'); 5754 5755 if (typeof postrollLink !== 'undefined') { 5756 player.postroll = 5757 $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a>
5757<div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide(); 5758 5759 t.media.addEventListener('ended', function (e) { 5760 $.ajax({ 5761 dataType: 'html', 5762 url: postrollLink, 5763 success: function (data, textStatus) { 5764 layers.find('.mejs-postroll-layer-content').html(data); 5765 } 5766 }); 5767 player.postroll.show(); 5768 }, false); 5769 } 5770 } 5771 }); 5772 5773})(mejs.$); 5774define("mediaelement", function(){}); 5775 5776/* 5777* Google Analytics Plugin 5778* Requires 5779* 5780*/ 5781 5782(function($) { 5783 5784$.extend(mejs.MepDefaults, { 5785 googleAnalyticsTitle: '', 5786 googleAnalyticsCategory: 'Videos', 5787 googleAnalyticsEventPlay: 'Play', 5788 googleAnalyticsEventPause: 'Pause', 5789 googleAnalyticsEventEnded: 'Ended', 5790 googleAnalyticsEventTime: 'Time' 5791}); 5792 5793 5794$.extend(MediaElementPlayer.prototype, { 5795 buildgoogleanalytics: function(player, controls, layers, media) { 5796 5797 media.addEventListener('play', function() { 5798 if (typeof _gaq != 'undefined') { 5799 _gaq.push(['_trackEvent', 5800 player.options.googleAnalyticsCategory, 5801 player.options.googleAnalyticsEventPlay, 5802 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5803 ]); 5804 } 5805 }, false); 5806 5807 media.addEventListener('pause', function() { 5808 if (typeof _gaq != 'undefined') { 5809 _gaq.push(['_trackEvent', 5810 player.options.googleAnalyticsCategory, 5811 player.options.googleAnalyticsEventPause, 5812 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5813 ]); 5814 } 5815 }, false); 5816 5817 media.addEventListener('ended', function() { 5818 if (typeof _gaq != 'undefined') { 5819 _gaq.push(['_trackEvent', 5820 player.options.googleAnalyticsCategory, 5821 player.options.googleAnalyticsEventEnded, 5822 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5823 ]); 5824 } 5825 }, false); 5826 5827 /* 5828 media.addEventListener('timeupdate', function() { 5829 if (typeof _gaq != 'undefined') { 5830 _gaq.push(['_trackEvent', 5831 player.options.googleAnalyticsCategory, 5832 player.options.googleAnalyticsEventEnded, 5833 player.options.googleAnalyticsTime, 5834 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle, 5835 player.currentTime 5836 ]); 5837 } 5838 }, true); 5839 */ 5840 } 5841}); 5842 5843})(mejs.$); 5844 5845define("mep-feature-googleanalytics", function(){}); 5846 5847/* 5848* analytics.js Google Analytics Plugin 5849* Requires JQuery 5850*/ 5851 5852(function($) { 5853 5854$.extend(mejs.MepDefaults, { 5855 googleAnalyticsTitle: '', 5856 googleAnalyticsCategory: 'Videos', 5857 googleAnalyticsEventPlay: 'Play', 5858 googleAnalyticsEventPause: 'Pause', 5859 googleAnalyticsEventEnded: 'Ended', 5860 googleAnalyticsEventTime: 'Time' 5861}); 5862 5863 5864$.extend(MediaElementPlayer.prototype, { 5865 builduniversalgoogleanalytics: function(player, controls, layers, media) { 5866 5867 media.addEventListener('play', function() { 5868 if (typeof ga != 'undefined') { 5869 ga('send', 'event', 5870 player.options.googleAnalyticsCategory, 5871 player.options.googleAnalyticsEventPlay, 5872 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5873 ); 5874 } 5875 }, false); 5876 5877 media.addEventListener('pause', function() { 5878 if (typeof ga != 'undefined') { 5879 ga('send', 'event', 5880 player.options.googleAnalyticsCategory, 5881 player.options.googleAnalyticsEventPause, 5882 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5883 ); 5884 } 5885 }, false); 5886 5887 media.addEventListener('ended', function() { 5888 if (typeof ga != 'undefined') { 5889 ga('send', 'event', 5890 player.options.googleAnalyticsCategory, 5891 player.options.googleAnalyticsEventEnded, 5892 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle 5893 ); 5894 } 5895 }, false); 5896 5897 /* 5898 media.addEventListener('timeupdate', function() { 5899 if (typeof ga != 'undefined') { 5900 ga('send', 'event', 5901 player.options.googleAnalyticsCategory, 5902 player.options.googleAnalyticsEventEnded, 5903 player.options.googleAnalyticsTime, 5904 (player.options.googleAnalyticsTitle === '') ? player.media.currentSrc : player.options.googleAnalyticsTitle, 5905 player.currentTime 5906 ); 5907 } 5908 }, true); 5909 */ 5910 } 5911}); 5912 5913})(mejs.$); 5914 5915define("mep-feature-universalgoogleanalytics", function(){}); 5916 5917/* global define */ 5918define('wildcard-patterns-video',[ 5919 'jquery', 5920 'mockup-patterns-base', 5921 'mediaelement', 5922 'mep-feature-googleanalytics', 5923 'mep-feature-universalgoogleanalytics' 5924], function($, Base) { 5925 'use strict'; 5926 5927 var Media = Base.extend({ 5928 name: 'media', 5929 trigger: '.pat-media', 5930 defaults: { 5931 }, 5932 init: function() { 5933 var self = this; 5934 self.$el.mediaelementplayer({ 5935 pluginPath: '++resource++wildcard-media/components/mediaelement/build/', 5936 features: ['playpause','current','progress','duration','tracks','volume','fullscreen', 5937 'googleanalytics', 'universalgoogleanalytics'] 5938 }); 5939 } 5940 }); 5941 5942 return Media; 5943 5944}); 5945 5946/* global require */ 5947 5948if(window.jQuery){ 5949 define('jquery', [],function(){ 5950 return window.jQuery; 5951 }); 5952} 5953 5954 5955require([
5956 'jquery', 5957 'wildcard-patterns-video' 5958 ], function($){ 5959 'use strict'; 5960 5961 $(document).ready(function(){ 5962 5963 $('span.wcvideo a').each(function(){ 5964 var $a = $(this); 5965 var $span = $a.parents('span.wcvideo'); 5966 var width, height; 5967 if($span.hasClass('video-large')){ 5968 width = 720; 5969 height = 480; 5970 }else if($span.hasClass('video-small')){ 5971 width = 320; 5972 height = 240; 5973 } 5974 $.ajax({ 5975 url: $a.attr('href') + '/@@wildcard_video_macro', 5976 success: function(data){ 5977 var $video = $(data); 5978 if(width && height){ 5979 $video.find('[width]').attr('width', width); 5980 $video.find('[height]').attr('height', height); 5981 } 5982 $span.replaceWith($video); 5983 $video.find('video').mediaelementplayer({ 5984 pluginPath: '++resource++wildcard-media/components/mediaelement/build/', 5985 features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen', 5986 'googleanalytics', 'universalgoogleanalytics'] 5987 }); 5988 } 5989 }); 5990 }); 5991 $('span.wcaudio a').each(function(){ 5992 var $a = $(this); 5993 var $span = $a.parents('span.wcaudio'); 5994 var $audio = $('<audio controls="controls" preload="none"' + 5995 'src="' + $a.attr('href') + '/@@view/++widget++form.widgets.IAudio.audio_file/@@download/file.mp3' + '"></audio>'); 5996 $span.replaceWith($audio); 5997 $audio.mediaelementplayer({ 5998 features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen', 5999 'googleanalytics', 'universalgoogleanalytics'] 6000 }); 6001 }); 6002 6003 var selector = '.template-wildcardvideo #form-widgets-IVideo-upload_video_to_youtube-0'; 6004 var checkFields = function(){ 6005 if($(selector)[0].checked){ 6006 $('#formfield-form-widgets-IVideo-youtube_url').hide(); 6007 }else{ 6008 $('#formfield-form-widgets-IVideo-youtube_url').show(); 6009 } 6010 }; 6011 if($(selector).change(checkFields).size() > 0){ 6012 checkFields(); 6013 } 6014 6015 }); 6016 6017}); 6018 6019define("js/bundle.js", function(){}); 6020
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.