annotate build/mediaelement-and-player.js @ 25:4a4bd554b4c1 tip

Closing this sub branch.
author Daniele Barchiesi <daniele.barchiesi@eecs.qmul.ac.uk>
date Mon, 25 Mar 2013 14:02:54 +0000
parents 032bc65ebafc
children
rev   line source
gyorgy@0 1 /*!
gyorgy@0 2 * MediaElement.js
gyorgy@0 3 * HTML5 <video> and <audio> shim and player
gyorgy@0 4 * http://mediaelementjs.com/
gyorgy@0 5 *
gyorgy@0 6 * Creates a JavaScript object that mimics HTML5 MediaElement API
gyorgy@0 7 * for browsers that don't understand HTML5 or can't play the provided codec
gyorgy@0 8 * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
gyorgy@0 9 *
gyorgy@0 10 * Copyright 2010-2011, John Dyer (http://j.hn)
gyorgy@0 11 * Dual licensed under the MIT or GPL Version 2 licenses.
gyorgy@0 12 *
gyorgy@0 13 */
gyorgy@0 14 // Namespace
gyorgy@0 15 var mejs = mejs || {};
gyorgy@0 16
gyorgy@0 17 // version number
gyorgy@0 18 mejs.version = '2.1.9';
gyorgy@0 19
gyorgy@0 20 // player number (for missing, same id attr)
gyorgy@0 21 mejs.meIndex = 0;
gyorgy@0 22
gyorgy@0 23 // media types accepted by plugins
gyorgy@0 24 mejs.plugins = {
gyorgy@0 25 silverlight: [
gyorgy@0 26 {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
gyorgy@0 27 ],
gyorgy@0 28 flash: [
gyorgy@0 29 {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg']}
gyorgy@0 30 //,{version: [11,0], types: ['video/webm']} // for future reference
gyorgy@0 31 ]
gyorgy@0 32 };
gyorgy@0 33
gyorgy@0 34 /*
gyorgy@0 35 Utility methods
gyorgy@0 36 */
gyorgy@0 37 mejs.Utility = {
gyorgy@0 38 encodeUrl: function(url) {
gyorgy@0 39 return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
gyorgy@0 40 },
gyorgy@0 41 escapeHTML: function(s) {
gyorgy@0 42 return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
gyorgy@0 43 },
gyorgy@0 44 absolutizeUrl: function(url) {
gyorgy@0 45 var el = document.createElement('div');
gyorgy@0 46 el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
gyorgy@0 47 return el.firstChild.href;
gyorgy@0 48 },
gyorgy@0 49 getScriptPath: function(scriptNames) {
gyorgy@0 50 var
gyorgy@0 51 i = 0,
gyorgy@0 52 j,
gyorgy@0 53 path = '',
gyorgy@0 54 name = '',
gyorgy@0 55 script,
gyorgy@0 56 scripts = document.getElementsByTagName('script');
gyorgy@0 57
gyorgy@0 58 for (; i < scripts.length; i++) {
gyorgy@0 59 script = scripts[i].src;
gyorgy@0 60 for (j = 0; j < scriptNames.length; j++) {
gyorgy@0 61 name = scriptNames[j];
gyorgy@0 62 if (script.indexOf(name) > -1) {
gyorgy@0 63 path = script.substring(0, script.indexOf(name));
gyorgy@0 64 break;
gyorgy@0 65 }
gyorgy@0 66 }
gyorgy@0 67 if (path !== '') {
gyorgy@0 68 break;
gyorgy@0 69 }
gyorgy@0 70 }
gyorgy@0 71 return path;
gyorgy@0 72 },
gyorgy@0 73 secondsToTimeCode: function(seconds,forceHours) {
gyorgy@0 74 seconds = Math.round(seconds);
gyorgy@0 75 var hours,
gyorgy@0 76 minutes = Math.floor(seconds / 60);
gyorgy@0 77 if (minutes >= 60) {
gyorgy@0 78 hours = Math.floor(minutes / 60);
gyorgy@0 79 minutes = minutes % 60;
gyorgy@0 80 }
gyorgy@0 81 hours = hours === undefined ? "00" : (hours >= 10) ? hours : "0" + hours;
gyorgy@0 82 minutes = (minutes >= 10) ? minutes : "0" + minutes;
gyorgy@0 83 seconds = Math.floor(seconds % 60);
gyorgy@0 84 seconds = (seconds >= 10) ? seconds : "0" + seconds;
gyorgy@0 85 return ((hours > 0 || forceHours === true) ? hours + ":" :'') + minutes + ":" + seconds;
gyorgy@0 86 },
gyorgy@0 87 timeCodeToSeconds: function(timecode){
gyorgy@0 88 var tab = timecode.split(':');
gyorgy@0 89 return tab[0]*60*60 + tab[1]*60 + parseFloat(tab[2].replace(',','.'));
gyorgy@0 90 }
gyorgy@0 91 };
gyorgy@0 92
gyorgy@0 93
gyorgy@0 94 // Core detector, plugins are added below
gyorgy@0 95 mejs.PluginDetector = {
gyorgy@0 96
gyorgy@0 97 // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
gyorgy@0 98 hasPluginVersion: function(plugin, v) {
gyorgy@0 99 var pv = this.plugins[plugin];
gyorgy@0 100 v[1] = v[1] || 0;
gyorgy@0 101 v[2] = v[2] || 0;
gyorgy@0 102 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;
gyorgy@0 103 },
gyorgy@0 104
gyorgy@0 105 // cached values
gyorgy@0 106 nav: window.navigator,
gyorgy@0 107 ua: window.navigator.userAgent.toLowerCase(),
gyorgy@0 108
gyorgy@0 109 // stored version numbers
gyorgy@0 110 plugins: [],
gyorgy@0 111
gyorgy@0 112 // runs detectPlugin() and stores the version number
gyorgy@0 113 addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
gyorgy@0 114 this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
gyorgy@0 115 },
gyorgy@0 116
gyorgy@0 117 // get the version number from the mimetype (all but IE) or ActiveX (IE)
gyorgy@0 118 detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
gyorgy@0 119
gyorgy@0 120 var version = [0,0,0],
gyorgy@0 121 description,
gyorgy@0 122 i,
gyorgy@0 123 ax;
gyorgy@0 124
gyorgy@0 125 // Firefox, Webkit, Opera
gyorgy@0 126 if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
gyorgy@0 127 description = this.nav.plugins[pluginName].description;
gyorgy@0 128 if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
gyorgy@0 129 version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
gyorgy@0 130 for (i=0; i<version.length; i++) {
gyorgy@0 131 version[i] = parseInt(version[i].match(/\d+/), 10);
gyorgy@0 132 }
gyorgy@0 133 }
gyorgy@0 134 // Internet Explorer / ActiveX
gyorgy@0 135 } else if (typeof(window.ActiveXObject) != 'undefined') {
gyorgy@0 136 try {
gyorgy@0 137 ax = new ActiveXObject(activeX);
gyorgy@0 138 if (ax) {
gyorgy@0 139 version = axDetect(ax);
gyorgy@0 140 }
gyorgy@0 141 }
gyorgy@0 142 catch (e) { }
gyorgy@0 143 }
gyorgy@0 144 return version;
gyorgy@0 145 }
gyorgy@0 146 };
gyorgy@0 147
gyorgy@0 148 // Add Flash detection
gyorgy@0 149 mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
gyorgy@0 150 // adapted from SWFObject
gyorgy@0 151 var version = [],
gyorgy@0 152 d = ax.GetVariable("$version");
gyorgy@0 153 if (d) {
gyorgy@0 154 d = d.split(" ")[1].split(",");
gyorgy@0 155 version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
gyorgy@0 156 }
gyorgy@0 157 return version;
gyorgy@0 158 });
gyorgy@0 159
gyorgy@0 160 // Add Silverlight detection
gyorgy@0 161 mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
gyorgy@0 162 // Silverlight cannot report its version number to IE
gyorgy@0 163 // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
gyorgy@0 164 // adapted from http://www.silverlightversion.com/
gyorgy@0 165 var v = [0,0,0,0],
gyorgy@0 166 loopMatch = function(ax, v, i, n) {
gyorgy@0 167 while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
gyorgy@0 168 v[i]+=n;
gyorgy@0 169 }
gyorgy@0 170 v[i] -= n;
gyorgy@0 171 };
gyorgy@0 172 loopMatch(ax, v, 0, 1);
gyorgy@0 173 loopMatch(ax, v, 1, 1);
gyorgy@0 174 loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
gyorgy@0 175 loopMatch(ax, v, 2, 1000);
gyorgy@0 176 loopMatch(ax, v, 2, 100);
gyorgy@0 177 loopMatch(ax, v, 2, 10);
gyorgy@0 178 loopMatch(ax, v, 2, 1);
gyorgy@0 179 loopMatch(ax, v, 3, 1);
gyorgy@0 180
gyorgy@0 181 return v;
gyorgy@0 182 });
gyorgy@0 183 // add adobe acrobat
gyorgy@0 184 /*
gyorgy@0 185 PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
gyorgy@0 186 var version = [],
gyorgy@0 187 d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
gyorgy@0 188
gyorgy@0 189 if (d) {
gyorgy@0 190 version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
gyorgy@0 191 }
gyorgy@0 192 return version;
gyorgy@0 193 });
gyorgy@0 194 */
gyorgy@0 195 // necessary detection (fixes for <IE9)
gyorgy@0 196 mejs.MediaFeatures = {
gyorgy@0 197 init: function() {
gyorgy@0 198 var
gyorgy@0 199 nav = mejs.PluginDetector.nav,
gyorgy@0 200 ua = mejs.PluginDetector.ua.toLowerCase(),
gyorgy@0 201 i,
gyorgy@0 202 v,
gyorgy@0 203 html5Elements = ['source','track','audio','video'];
gyorgy@0 204
gyorgy@0 205 // detect browsers (only the ones that have some kind of quirk we need to work around)
gyorgy@0 206 this.isiPad = (ua.match(/ipad/i) !== null);
gyorgy@0 207 this.isiPhone = (ua.match(/iphone/i) !== null);
gyorgy@0 208 this.isAndroid = (ua.match(/android/i) !== null);
gyorgy@0 209 this.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
gyorgy@0 210 this.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1);
gyorgy@0 211 this.isChrome = (ua.match(/chrome/gi) !== null);
gyorgy@0 212 this.isFirefox = (ua.match(/firefox/gi) !== null);
gyorgy@0 213
gyorgy@0 214 // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
gyorgy@0 215 for (i=0; i<html5Elements.length; i++) {
gyorgy@0 216 v = document.createElement(html5Elements[i]);
gyorgy@0 217 }
gyorgy@0 218
gyorgy@0 219 // detect native JavaScript fullscreen (Safari only, Chrome fails)
gyorgy@0 220 this.hasNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
gyorgy@0 221 if (this.isChrome) {
gyorgy@0 222 this.hasNativeFullScreen = false;
gyorgy@0 223 }
gyorgy@0 224 // OS X 10.5 can't do this even if it says it can :(
gyorgy@0 225 if (this.hasNativeFullScreen && ua.match(/mac os x 10_5/i)) {
gyorgy@0 226 this.hasNativeFullScreen = false;
gyorgy@0 227 }
gyorgy@0 228 }
gyorgy@0 229 };
gyorgy@0 230 mejs.MediaFeatures.init();
gyorgy@0 231
gyorgy@0 232
gyorgy@0 233 /*
gyorgy@0 234 extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
gyorgy@0 235 */
gyorgy@0 236 mejs.HtmlMediaElement = {
gyorgy@0 237 pluginType: 'native',
gyorgy@0 238 isFullScreen: false,
gyorgy@0 239
gyorgy@0 240 setCurrentTime: function (time) {
gyorgy@0 241 this.currentTime = time;
gyorgy@0 242 },
gyorgy@0 243
gyorgy@0 244 setMuted: function (muted) {
gyorgy@0 245 this.muted = muted;
gyorgy@0 246 },
gyorgy@0 247
gyorgy@0 248 setVolume: function (volume) {
gyorgy@0 249 this.volume = volume;
gyorgy@0 250 },
gyorgy@0 251
gyorgy@0 252 // for parity with the plugin versions
gyorgy@0 253 stop: function () {
gyorgy@0 254 this.pause();
gyorgy@0 255 },
gyorgy@0 256
gyorgy@0 257 // This can be a url string
gyorgy@0 258 // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
gyorgy@0 259 setSrc: function (url) {
gyorgy@0 260 if (typeof url == 'string') {
gyorgy@0 261 this.src = url;
gyorgy@0 262 } else {
gyorgy@0 263 var i, media;
gyorgy@0 264
gyorgy@0 265 for (i=0; i<url.length; i++) {
gyorgy@0 266 media = url[i];
gyorgy@0 267 if (this.canPlayType(media.type)) {
gyorgy@0 268 this.src = media.src;
gyorgy@0 269 }
gyorgy@0 270 }
gyorgy@0 271 }
gyorgy@0 272 },
gyorgy@0 273
gyorgy@0 274 setVideoSize: function (width, height) {
gyorgy@0 275 this.width = width;
gyorgy@0 276 this.height = height;
gyorgy@0 277 }
gyorgy@0 278 };
gyorgy@0 279
gyorgy@0 280 /*
gyorgy@0 281 Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
gyorgy@0 282 */
gyorgy@0 283 mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
gyorgy@0 284 this.id = pluginid;
gyorgy@0 285 this.pluginType = pluginType;
gyorgy@0 286 this.src = mediaUrl;
gyorgy@0 287 this.events = {};
gyorgy@0 288 };
gyorgy@0 289
gyorgy@0 290 // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
gyorgy@0 291 // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
gyorgy@0 292 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
gyorgy@0 293 mejs.PluginMediaElement.prototype = {
gyorgy@0 294
gyorgy@0 295 // special
gyorgy@0 296 pluginElement: null,
gyorgy@0 297 pluginType: '',
gyorgy@0 298 isFullScreen: false,
gyorgy@0 299
gyorgy@0 300 // not implemented :(
gyorgy@0 301 playbackRate: -1,
gyorgy@0 302 defaultPlaybackRate: -1,
gyorgy@0 303 seekable: [],
gyorgy@0 304 played: [],
gyorgy@0 305
gyorgy@0 306 // HTML5 read-only properties
gyorgy@0 307 paused: true,
gyorgy@0 308 ended: false,
gyorgy@0 309 seeking: false,
gyorgy@0 310 duration: 0,
gyorgy@0 311 error: null,
gyorgy@0 312
gyorgy@0 313 // HTML5 get/set properties, but only set (updated by event handlers)
gyorgy@0 314 muted: false,
gyorgy@0 315 volume: 1,
gyorgy@0 316 currentTime: 0,
gyorgy@0 317
gyorgy@0 318 // HTML5 methods
gyorgy@0 319 play: function () {
gyorgy@0 320 if (this.pluginApi != null) {
gyorgy@0 321 this.pluginApi.playMedia();
gyorgy@0 322 this.paused = false;
gyorgy@0 323 }
gyorgy@0 324 },
gyorgy@0 325 load: function () {
gyorgy@0 326 if (this.pluginApi != null) {
gyorgy@0 327 this.pluginApi.loadMedia();
gyorgy@0 328 this.paused = false;
gyorgy@0 329 }
gyorgy@0 330 },
gyorgy@0 331 pause: function () {
gyorgy@0 332 if (this.pluginApi != null) {
gyorgy@0 333 this.pluginApi.pauseMedia();
gyorgy@0 334 this.paused = true;
gyorgy@0 335 }
gyorgy@0 336 },
gyorgy@0 337 stop: function () {
gyorgy@0 338 if (this.pluginApi != null) {
gyorgy@0 339 this.pluginApi.stopMedia();
gyorgy@0 340 this.paused = true;
gyorgy@0 341 }
gyorgy@0 342 },
gyorgy@0 343 canPlayType: function(type) {
gyorgy@0 344 var i,
gyorgy@0 345 j,
gyorgy@0 346 pluginInfo,
gyorgy@0 347 pluginVersions = mejs.plugins[this.pluginType];
gyorgy@0 348
gyorgy@0 349 for (i=0; i<pluginVersions.length; i++) {
gyorgy@0 350 pluginInfo = pluginVersions[i];
gyorgy@0 351
gyorgy@0 352 // test if user has the correct plugin version
gyorgy@0 353 if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
gyorgy@0 354
gyorgy@0 355 // test for plugin playback types
gyorgy@0 356 for (j=0; j<pluginInfo.types.length; j++) {
gyorgy@0 357 // find plugin that can play the type
gyorgy@0 358 if (type == pluginInfo.types[j]) {
gyorgy@0 359 return true;
gyorgy@0 360 }
gyorgy@0 361 }
gyorgy@0 362 }
gyorgy@0 363 }
gyorgy@0 364
gyorgy@0 365 return false;
gyorgy@0 366 },
gyorgy@0 367
gyorgy@0 368 // custom methods since not all JavaScript implementations support get/set
gyorgy@0 369
gyorgy@0 370 // This can be a url string
gyorgy@0 371 // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
gyorgy@0 372 setSrc: function (url) {
gyorgy@0 373 if (typeof url == 'string') {
gyorgy@0 374 this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
gyorgy@0 375 this.src = mejs.Utility.absolutizeUrl(url);
gyorgy@0 376 } else {
gyorgy@0 377 var i, media;
gyorgy@0 378
gyorgy@0 379 for (i=0; i<url.length; i++) {
gyorgy@0 380 media = url[i];
gyorgy@0 381 if (this.canPlayType(media.type)) {
gyorgy@0 382 this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
gyorgy@0 383 this.src = mejs.Utility.absolutizeUrl(url);
gyorgy@0 384 }
gyorgy@0 385 }
gyorgy@0 386 }
gyorgy@0 387
gyorgy@0 388 },
gyorgy@0 389 setCurrentTime: function (time) {
gyorgy@0 390 if (this.pluginApi != null) {
gyorgy@0 391 this.pluginApi.setCurrentTime(time);
gyorgy@0 392 this.currentTime = time;
gyorgy@0 393 }
gyorgy@0 394 },
gyorgy@0 395 setVolume: function (volume) {
gyorgy@0 396 if (this.pluginApi != null) {
gyorgy@0 397 this.pluginApi.setVolume(volume);
gyorgy@0 398 this.volume = volume;
gyorgy@0 399 }
gyorgy@0 400 },
gyorgy@0 401 setMuted: function (muted) {
gyorgy@0 402 if (this.pluginApi != null) {
gyorgy@0 403 this.pluginApi.setMuted(muted);
gyorgy@0 404 this.muted = muted;
gyorgy@0 405 }
gyorgy@0 406 },
gyorgy@0 407
gyorgy@0 408 // additional non-HTML5 methods
gyorgy@0 409 setVideoSize: function (width, height) {
gyorgy@0 410 if ( this.pluginElement.style) {
gyorgy@0 411 this.pluginElement.style.width = width + 'px';
gyorgy@0 412 this.pluginElement.style.height = height + 'px';
gyorgy@0 413 }
gyorgy@0 414 if (this.pluginApi != null) {
gyorgy@0 415 this.pluginApi.setVideoSize(width, height);
gyorgy@0 416 }
gyorgy@0 417 },
gyorgy@0 418
gyorgy@0 419 setFullscreen: function (fullscreen) {
gyorgy@0 420 if (this.pluginApi != null) {
gyorgy@0 421 this.pluginApi.setFullscreen(fullscreen);
gyorgy@0 422 }
gyorgy@0 423 },
gyorgy@0 424
gyorgy@0 425 // start: fake events
gyorgy@0 426 addEventListener: function (eventName, callback, bubble) {
gyorgy@0 427 this.events[eventName] = this.events[eventName] || [];
gyorgy@0 428 this.events[eventName].push(callback);
gyorgy@0 429 },
gyorgy@0 430 removeEventListener: function (eventName, callback) {
gyorgy@0 431 if (!eventName) { this.events = {}; return true; }
gyorgy@0 432 var callbacks = this.events[eventName];
gyorgy@0 433 if (!callbacks) return true;
gyorgy@0 434 if (!callback) { this.events[eventName] = []; return true; }
gyorgy@0 435 for (i = 0; i < callbacks.length; i++) {
gyorgy@0 436 if (callbacks[i] === callback) {
gyorgy@0 437 this.events[eventName].splice(i, 1);
gyorgy@0 438 return true;
gyorgy@0 439 }
gyorgy@0 440 }
gyorgy@0 441 return false;
gyorgy@0 442 },
gyorgy@0 443 dispatchEvent: function (eventName) {
gyorgy@0 444 var i,
gyorgy@0 445 args,
gyorgy@0 446 callbacks = this.events[eventName];
gyorgy@0 447
gyorgy@0 448 if (callbacks) {
gyorgy@0 449 args = Array.prototype.slice.call(arguments, 1);
gyorgy@0 450 for (i = 0; i < callbacks.length; i++) {
gyorgy@0 451 callbacks[i].apply(null, args);
gyorgy@0 452 }
gyorgy@0 453 }
gyorgy@0 454 }
gyorgy@0 455 // end: fake events
gyorgy@0 456 };
gyorgy@0 457
gyorgy@0 458
gyorgy@0 459 // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
gyorgy@0 460 mejs.MediaPluginBridge = {
gyorgy@0 461
gyorgy@0 462 pluginMediaElements:{},
gyorgy@0 463 htmlMediaElements:{},
gyorgy@0 464
gyorgy@0 465 registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
gyorgy@0 466 this.pluginMediaElements[id] = pluginMediaElement;
gyorgy@0 467 this.htmlMediaElements[id] = htmlMediaElement;
gyorgy@0 468 },
gyorgy@0 469
gyorgy@0 470 // when Flash/Silverlight is ready, it calls out to this method
gyorgy@0 471 initPlugin: function (id) {
gyorgy@0 472
gyorgy@0 473 var pluginMediaElement = this.pluginMediaElements[id],
gyorgy@0 474 htmlMediaElement = this.htmlMediaElements[id];
gyorgy@0 475
gyorgy@0 476 // find the javascript bridge
gyorgy@0 477 switch (pluginMediaElement.pluginType) {
gyorgy@0 478 case "flash":
gyorgy@0 479 pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
gyorgy@0 480 break;
gyorgy@0 481 case "silverlight":
gyorgy@0 482 pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
gyorgy@0 483 pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
gyorgy@0 484 break;
gyorgy@0 485 }
gyorgy@0 486
gyorgy@0 487 if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
gyorgy@0 488 pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
gyorgy@0 489 }
gyorgy@0 490 },
gyorgy@0 491
gyorgy@0 492 // receives events from Flash/Silverlight and sends them out as HTML5 media events
gyorgy@0 493 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
gyorgy@0 494 fireEvent: function (id, eventName, values) {
gyorgy@0 495
gyorgy@0 496 var
gyorgy@0 497 e,
gyorgy@0 498 i,
gyorgy@0 499 bufferedTime,
gyorgy@0 500 pluginMediaElement = this.pluginMediaElements[id];
gyorgy@0 501
gyorgy@0 502 pluginMediaElement.ended = false;
gyorgy@0 503 pluginMediaElement.paused = true;
gyorgy@0 504
gyorgy@0 505 // fake event object to mimic real HTML media event.
gyorgy@0 506 e = {
gyorgy@0 507 type: eventName,
gyorgy@0 508 target: pluginMediaElement
gyorgy@0 509 };
gyorgy@0 510
gyorgy@0 511 // attach all values to element and event object
gyorgy@0 512 for (i in values) {
gyorgy@0 513 pluginMediaElement[i] = values[i];
gyorgy@0 514 e[i] = values[i];
gyorgy@0 515 }
gyorgy@0 516
gyorgy@0 517 // fake the newer W3C buffered TimeRange (loaded and total have been removed)
gyorgy@0 518 bufferedTime = values.bufferedTime || 0;
gyorgy@0 519
gyorgy@0 520 e.target.buffered = e.buffered = {
gyorgy@0 521 start: function(index) {
gyorgy@0 522 return 0;
gyorgy@0 523 },
gyorgy@0 524 end: function (index) {
gyorgy@0 525 return bufferedTime;
gyorgy@0 526 },
gyorgy@0 527 length: 1
gyorgy@0 528 };
gyorgy@0 529
gyorgy@0 530 pluginMediaElement.dispatchEvent(e.type, e);
gyorgy@0 531 }
gyorgy@0 532 };
gyorgy@0 533
gyorgy@0 534 /*
gyorgy@0 535 Default options
gyorgy@0 536 */
gyorgy@0 537 mejs.MediaElementDefaults = {
gyorgy@0 538 // allows testing on HTML5, flash, silverlight
gyorgy@0 539 // auto: attempts to detect what the browser can do
gyorgy@0 540 // native: forces HTML5 playback
gyorgy@0 541 // shim: disallows HTML5, will attempt either Flash or Silverlight
gyorgy@0 542 // none: forces fallback view
gyorgy@0 543 mode: 'auto',
gyorgy@0 544 // remove or reorder to change plugin priority and availability
gyorgy@0 545 plugins: ['flash','silverlight'],
gyorgy@0 546 // shows debug errors on screen
gyorgy@0 547 enablePluginDebug: false,
gyorgy@0 548 // overrides the type specified, useful for dynamic instantiation
gyorgy@0 549 type: '',
gyorgy@0 550 // path to Flash and Silverlight plugins
gyorgy@0 551 pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
gyorgy@0 552 // name of flash file
gyorgy@0 553 flashName: 'flashmediaelement.swf',
gyorgy@0 554 // turns on the smoothing filter in Flash
gyorgy@0 555 enablePluginSmoothing: false,
gyorgy@0 556 // name of silverlight file
gyorgy@0 557 silverlightName: 'silverlightmediaelement.xap',
gyorgy@0 558 // default if the <video width> is not specified
gyorgy@0 559 defaultVideoWidth: 480,
gyorgy@0 560 // default if the <video height> is not specified
gyorgy@0 561 defaultVideoHeight: 270,
gyorgy@0 562 // overrides <video width>
gyorgy@0 563 pluginWidth: -1,
gyorgy@0 564 // overrides <video height>
gyorgy@0 565 pluginHeight: -1,
gyorgy@0 566 // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
gyorgy@0 567 // larger number is less accurate, but less strain on plugin->JavaScript bridge
gyorgy@0 568 timerRate: 250,
gyorgy@0 569 success: function () { },
gyorgy@0 570 error: function () { }
gyorgy@0 571 };
gyorgy@0 572
gyorgy@0 573 /*
gyorgy@0 574 Determines if a browser supports the <video> or <audio> element
gyorgy@0 575 and returns either the native element or a Flash/Silverlight version that
gyorgy@0 576 mimics HTML5 MediaElement
gyorgy@0 577 */
gyorgy@0 578 mejs.MediaElement = function (el, o) {
gyorgy@0 579 return mejs.HtmlMediaElementShim.create(el,o);
gyorgy@0 580 };
gyorgy@0 581
gyorgy@0 582 mejs.HtmlMediaElementShim = {
gyorgy@0 583
gyorgy@0 584 create: function(el, o) {
gyorgy@0 585 var
gyorgy@0 586 options = mejs.MediaElementDefaults,
gyorgy@0 587 htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
gyorgy@0 588 isVideo = (htmlMediaElement.tagName.toLowerCase() == 'video'),
gyorgy@0 589 supportsMediaTag = (typeof(htmlMediaElement.canPlayType) != 'undefined'),
gyorgy@0 590 playback = {method:'', url:''},
gyorgy@0 591 poster = htmlMediaElement.getAttribute('poster'),
gyorgy@0 592 autoplay = htmlMediaElement.getAttribute('autoplay'),
gyorgy@0 593 preload = htmlMediaElement.getAttribute('preload'),
gyorgy@0 594 controls = htmlMediaElement.getAttribute('controls'),
gyorgy@0 595 prop;
gyorgy@0 596
gyorgy@0 597 // extend options
gyorgy@0 598 for (prop in o) {
gyorgy@0 599 options[prop] = o[prop];
gyorgy@0 600 }
gyorgy@0 601
gyorgy@0 602 // check for real poster
gyorgy@0 603 poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
gyorgy@0 604 preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
gyorgy@0 605 autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
gyorgy@0 606 controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
gyorgy@0 607
gyorgy@0 608 // test for HTML5 and plugin capabilities
gyorgy@0 609 playback = this.determinePlayback(htmlMediaElement, options, isVideo, supportsMediaTag);
gyorgy@0 610
gyorgy@0 611 if (playback.method == 'native') {
gyorgy@0 612 // second fix for android
gyorgy@0 613 if (mejs.MediaFeatures.isBustedAndroid) {
gyorgy@0 614 htmlMediaElement.src = playback.url;
gyorgy@0 615 htmlMediaElement.addEventListener('click', function() {
gyorgy@0 616 htmlMediaElement.play();
gyorgy@0 617 }, true);
gyorgy@0 618 }
gyorgy@0 619
gyorgy@0 620 // add methods to native HTMLMediaElement
gyorgy@0 621 return this.updateNative( htmlMediaElement, options, autoplay, preload, playback);
gyorgy@0 622 } else if (playback.method !== '') {
gyorgy@0 623 // create plugin to mimic HTMLMediaElement
gyorgy@0 624 return this.createPlugin( htmlMediaElement, options, isVideo, playback.method, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster, autoplay, preload, controls);
gyorgy@0 625 } else {
gyorgy@0 626 // boo, no HTML5, no Flash, no Silverlight.
gyorgy@0 627 this.createErrorMessage( htmlMediaElement, options, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster );
gyorgy@0 628 }
gyorgy@0 629 },
gyorgy@0 630
gyorgy@0 631 determinePlayback: function(htmlMediaElement, options, isVideo, supportsMediaTag) {
gyorgy@0 632 var
gyorgy@0 633 mediaFiles = [],
gyorgy@0 634 i,
gyorgy@0 635 j,
gyorgy@0 636 k,
gyorgy@0 637 l,
gyorgy@0 638 n,
gyorgy@0 639 type,
gyorgy@0 640 result = { method: '', url: ''},
gyorgy@0 641 src = htmlMediaElement.getAttribute('src'),
gyorgy@0 642 pluginName,
gyorgy@0 643 pluginVersions,
gyorgy@0 644 pluginInfo;
gyorgy@0 645
gyorgy@0 646 // clean up src attr
gyorgy@0 647 if (src == 'undefined' || src == '' || src === null)
gyorgy@0 648 src = null;
gyorgy@0 649
gyorgy@0 650 // STEP 1: Get URL and type from <video src> or <source src>
gyorgy@0 651
gyorgy@0 652 // supplied type overrides all HTML
gyorgy@0 653 if (typeof (options.type) != 'undefined' && options.type !== '') {
gyorgy@0 654 mediaFiles.push({type:options.type, url:src});
gyorgy@0 655
gyorgy@0 656 // test for src attribute first
gyorgy@0 657 } else if (src !== null) {
gyorgy@0 658 type = this.checkType(src, htmlMediaElement.getAttribute('type'), isVideo);
gyorgy@0 659 mediaFiles.push({type:type, url:src});
gyorgy@0 660
gyorgy@0 661 // then test for <source> elements
gyorgy@0 662 } else {
gyorgy@0 663 // test <source> types to see if they are usable
gyorgy@0 664 for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
gyorgy@0 665 n = htmlMediaElement.childNodes[i];
gyorgy@0 666 if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
gyorgy@0 667 src = n.getAttribute('src');
gyorgy@0 668 type = this.checkType(src, n.getAttribute('type'), isVideo);
gyorgy@0 669 mediaFiles.push({type:type, url:src});
gyorgy@0 670 }
gyorgy@0 671 }
gyorgy@0 672 }
gyorgy@0 673
gyorgy@0 674 // STEP 2: Test for playback method
gyorgy@0 675
gyorgy@0 676 // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
gyorgy@0 677 if (mejs.MediaFeatures.isBustedAndroid) {
gyorgy@0 678 htmlMediaElement.canPlayType = function(type) {
gyorgy@0 679 return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
gyorgy@0 680 };
gyorgy@0 681 }
gyorgy@0 682
gyorgy@0 683
gyorgy@0 684 // test for native playback first
gyorgy@0 685 if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'native')) {
gyorgy@0 686 for (i=0; i<mediaFiles.length; i++) {
gyorgy@0 687 // normal check
gyorgy@0 688 if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
gyorgy@0 689 // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
gyorgy@0 690 || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '') {
gyorgy@0 691 result.method = 'native';
gyorgy@0 692 result.url = mediaFiles[i].url;
gyorgy@0 693 return result;
gyorgy@0 694 }
gyorgy@0 695 }
gyorgy@0 696 }
gyorgy@0 697
gyorgy@0 698 // if native playback didn't work, then test plugins
gyorgy@0 699 if (options.mode === 'auto' || options.mode === 'shim') {
gyorgy@0 700 for (i=0; i<mediaFiles.length; i++) {
gyorgy@0 701 type = mediaFiles[i].type;
gyorgy@0 702
gyorgy@0 703 // test all plugins in order of preference [silverlight, flash]
gyorgy@0 704 for (j=0; j<options.plugins.length; j++) {
gyorgy@0 705
gyorgy@0 706 pluginName = options.plugins[j];
gyorgy@0 707
gyorgy@0 708 // test version of plugin (for future features)
gyorgy@0 709 pluginVersions = mejs.plugins[pluginName];
gyorgy@0 710 for (k=0; k<pluginVersions.length; k++) {
gyorgy@0 711 pluginInfo = pluginVersions[k];
gyorgy@0 712
gyorgy@0 713 // test if user has the correct plugin version
gyorgy@0 714 if (mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
gyorgy@0 715
gyorgy@0 716 // test for plugin playback types
gyorgy@0 717 for (l=0; l<pluginInfo.types.length; l++) {
gyorgy@0 718 // find plugin that can play the type
gyorgy@0 719 if (type == pluginInfo.types[l]) {
gyorgy@0 720 result.method = pluginName;
gyorgy@0 721 result.url = mediaFiles[i].url;
gyorgy@0 722 return result;
gyorgy@0 723 }
gyorgy@0 724 }
gyorgy@0 725 }
gyorgy@0 726 }
gyorgy@0 727 }
gyorgy@0 728 }
gyorgy@0 729 }
gyorgy@0 730
gyorgy@0 731 // what if there's nothing to play? just grab the first available
gyorgy@0 732 if (result.method === '') {
gyorgy@0 733 result.url = mediaFiles[0].url;
gyorgy@0 734 }
gyorgy@0 735
gyorgy@0 736 return result;
gyorgy@0 737 },
gyorgy@0 738
gyorgy@0 739 checkType: function(url, type, isVideo) {
gyorgy@0 740 var ext;
gyorgy@0 741
gyorgy@0 742 // if no type is supplied, fake it with the extension
gyorgy@0 743 if (url && !type) {
gyorgy@0 744 ext = url.substring(url.lastIndexOf('.') + 1);
gyorgy@0 745 return ((isVideo) ? 'video' : 'audio') + '/' + ext;
gyorgy@0 746 } else {
gyorgy@0 747 // only return the mime part of the type in case the attribute contains the codec
gyorgy@0 748 // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
gyorgy@0 749 // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
gyorgy@0 750
gyorgy@0 751 if (type && ~type.indexOf(';')) {
gyorgy@0 752 return type.substr(0, type.indexOf(';'));
gyorgy@0 753 } else {
gyorgy@0 754 return type;
gyorgy@0 755 }
gyorgy@0 756 }
gyorgy@0 757 },
gyorgy@0 758
gyorgy@0 759 createErrorMessage: function(htmlMediaElement, options, downloadUrl, poster) {
gyorgy@0 760 var errorContainer = document.createElement('div');
gyorgy@0 761 errorContainer.className = 'me-cannotplay';
gyorgy@0 762
gyorgy@0 763 try {
gyorgy@0 764 errorContainer.style.width = htmlMediaElement.width + 'px';
gyorgy@0 765 errorContainer.style.height = htmlMediaElement.height + 'px';
gyorgy@0 766 } catch (e) {}
gyorgy@0 767
gyorgy@0 768 errorContainer.innerHTML = (poster !== '') ?
gyorgy@0 769 '<a href="' + downloadUrl + '"><img src="' + poster + '" /></a>' :
gyorgy@0 770 '<a href="' + downloadUrl + '"><span>Download File</span></a>';
gyorgy@0 771
gyorgy@0 772 htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
gyorgy@0 773 htmlMediaElement.style.display = 'none';
gyorgy@0 774
gyorgy@0 775 options.error(htmlMediaElement);
gyorgy@0 776 },
gyorgy@0 777
gyorgy@0 778 createPlugin:function(htmlMediaElement, options, isVideo, pluginType, mediaUrl, poster, autoplay, preload, controls) {
gyorgy@0 779 var width = 1,
gyorgy@0 780 height = 1,
gyorgy@0 781 pluginid = 'me_' + pluginType + '_' + (mejs.meIndex++),
gyorgy@0 782 pluginMediaElement = new mejs.PluginMediaElement(pluginid, pluginType, mediaUrl),
gyorgy@0 783 container = document.createElement('div'),
gyorgy@0 784 specialIEContainer,
gyorgy@0 785 node,
gyorgy@0 786 initVars;
gyorgy@0 787
gyorgy@0 788 // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
gyorgy@0 789 node = htmlMediaElement.parentNode;
gyorgy@0 790 while (node !== null && node.tagName.toLowerCase() != 'body') {
gyorgy@0 791 if (node.parentNode.tagName.toLowerCase() == 'p') {
gyorgy@0 792 node.parentNode.parentNode.insertBefore(node, node.parentNode);
gyorgy@0 793 break;
gyorgy@0 794 }
gyorgy@0 795 node = node.parentNode;
gyorgy@0 796 }
gyorgy@0 797
gyorgy@0 798 if (isVideo) {
gyorgy@0 799 width = (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
gyorgy@0 800 height = (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
gyorgy@0 801 } else {
gyorgy@0 802 if (options.enablePluginDebug) {
gyorgy@0 803 width = 320;
gyorgy@0 804 height = 240;
gyorgy@0 805 }
gyorgy@0 806 }
gyorgy@0 807
gyorgy@0 808 // register plugin
gyorgy@0 809 pluginMediaElement.success = options.success;
gyorgy@0 810 mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
gyorgy@0 811
gyorgy@0 812 // add container (must be added to DOM before inserting HTML for IE)
gyorgy@0 813 container.className = 'me-plugin';
gyorgy@0 814 htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
gyorgy@0 815
gyorgy@0 816 // flash/silverlight vars
gyorgy@0 817 initVars = [
gyorgy@0 818 'id=' + pluginid,
gyorgy@0 819 'isvideo=' + ((isVideo) ? "true" : "false"),
gyorgy@0 820 'autoplay=' + ((autoplay) ? "true" : "false"),
gyorgy@0 821 'preload=' + preload,
gyorgy@0 822 'width=' + width,
gyorgy@0 823 'startvolume=' + options.startVolume,
gyorgy@0 824 'timerrate=' + options.timerRate,
gyorgy@0 825 'height=' + height];
gyorgy@0 826
gyorgy@0 827 if (mediaUrl !== null) {
gyorgy@0 828 if (pluginType == 'flash') {
gyorgy@0 829 initVars.push('file=' + mejs.Utility.encodeUrl(mediaUrl));
gyorgy@0 830 } else {
gyorgy@0 831 initVars.push('file=' + mediaUrl);
gyorgy@0 832 }
gyorgy@0 833 }
gyorgy@0 834 if (options.enablePluginDebug) {
gyorgy@0 835 initVars.push('debug=true');
gyorgy@0 836 }
gyorgy@0 837 if (options.enablePluginSmoothing) {
gyorgy@0 838 initVars.push('smoothing=true');
gyorgy@0 839 }
gyorgy@0 840 if (controls) {
gyorgy@0 841 initVars.push('controls=true'); // shows controls in the plugin if desired
gyorgy@0 842 }
gyorgy@0 843
gyorgy@0 844 switch (pluginType) {
gyorgy@0 845 case 'silverlight':
gyorgy@0 846 container.innerHTML =
gyorgy@0 847 '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '">' +
gyorgy@0 848 '<param name="initParams" value="' + initVars.join(',') + '" />' +
gyorgy@0 849 '<param name="windowless" value="true" />' +
gyorgy@0 850 '<param name="background" value="black" />' +
gyorgy@0 851 '<param name="minRuntimeVersion" value="3.0.0.0" />' +
gyorgy@0 852 '<param name="autoUpgrade" value="true" />' +
gyorgy@0 853 '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
gyorgy@0 854 '</object>';
gyorgy@0 855 break;
gyorgy@0 856
gyorgy@0 857 case 'flash':
gyorgy@0 858
gyorgy@0 859 if (mejs.MediaFeatures.isIE) {
gyorgy@0 860 specialIEContainer = document.createElement('div');
gyorgy@0 861 container.appendChild(specialIEContainer);
gyorgy@0 862 specialIEContainer.outerHTML =
gyorgy@0 863 '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
gyorgy@0 864 'id="' + pluginid + '" width="' + width + '" height="' + height + '">' +
gyorgy@0 865 '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
gyorgy@0 866 '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
gyorgy@0 867 '<param name="quality" value="high" />' +
gyorgy@0 868 '<param name="bgcolor" value="#000000" />' +
gyorgy@0 869 '<param name="wmode" value="transparent" />' +
gyorgy@0 870 '<param name="allowScriptAccess" value="always" />' +
gyorgy@0 871 '<param name="allowFullScreen" value="true" />' +
gyorgy@0 872 '</object>';
gyorgy@0 873
gyorgy@0 874 } else {
gyorgy@0 875
gyorgy@0 876 container.innerHTML =
gyorgy@0 877 '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
gyorgy@0 878 'play="true" ' +
gyorgy@0 879 'loop="false" ' +
gyorgy@0 880 'quality="high" ' +
gyorgy@0 881 'bgcolor="#000000" ' +
gyorgy@0 882 'wmode="transparent" ' +
gyorgy@0 883 'allowScriptAccess="always" ' +
gyorgy@0 884 'allowFullScreen="true" ' +
gyorgy@0 885 'type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" ' +
gyorgy@0 886 'src="' + options.pluginPath + options.flashName + '" ' +
gyorgy@0 887 'flashvars="' + initVars.join('&') + '" ' +
gyorgy@0 888 'width="' + width + '" ' +
gyorgy@0 889 'height="' + height + '"></embed>';
gyorgy@0 890 }
gyorgy@0 891 break;
gyorgy@0 892 }
gyorgy@0 893 // hide original element
gyorgy@0 894 htmlMediaElement.style.display = 'none';
gyorgy@0 895
gyorgy@0 896 // FYI: options.success will be fired by the MediaPluginBridge
gyorgy@0 897
gyorgy@0 898 return pluginMediaElement;
gyorgy@0 899 },
gyorgy@0 900
gyorgy@0 901 updateNative: function(htmlMediaElement, options, autoplay, preload, playback) {
gyorgy@0 902 // add methods to video object to bring it into parity with Flash Object
gyorgy@0 903 for (var m in mejs.HtmlMediaElement) {
gyorgy@0 904 htmlMediaElement[m] = mejs.HtmlMediaElement[m];
gyorgy@0 905 }
gyorgy@0 906
gyorgy@0 907 /*
gyorgy@0 908 Chrome now supports preload="none"
gyorgy@0 909 if (mejs.MediaFeatures.isChrome) {
gyorgy@0 910
gyorgy@0 911 // special case to enforce preload attribute (Chrome doesn't respect this)
gyorgy@0 912 if (preload === 'none' && !autoplay) {
gyorgy@0 913
gyorgy@0 914 // forces the browser to stop loading (note: fails in IE9)
gyorgy@0 915 htmlMediaElement.src = '';
gyorgy@0 916 htmlMediaElement.load();
gyorgy@0 917 htmlMediaElement.canceledPreload = true;
gyorgy@0 918
gyorgy@0 919 htmlMediaElement.addEventListener('play',function() {
gyorgy@0 920 if (htmlMediaElement.canceledPreload) {
gyorgy@0 921 htmlMediaElement.src = playback.url;
gyorgy@0 922 htmlMediaElement.load();
gyorgy@0 923 htmlMediaElement.play();
gyorgy@0 924 htmlMediaElement.canceledPreload = false;
gyorgy@0 925 }
gyorgy@0 926 }, false);
gyorgy@0 927 // for some reason Chrome forgets how to autoplay sometimes.
gyorgy@0 928 } else if (autoplay) {
gyorgy@0 929 htmlMediaElement.load();
gyorgy@0 930 htmlMediaElement.play();
gyorgy@0 931 }
gyorgy@0 932 }
gyorgy@0 933 */
gyorgy@0 934
gyorgy@0 935 // fire success code
gyorgy@0 936 options.success(htmlMediaElement, htmlMediaElement);
gyorgy@0 937
gyorgy@0 938 return htmlMediaElement;
gyorgy@0 939 }
gyorgy@0 940 };
gyorgy@0 941
gyorgy@0 942 window.mejs = mejs;
gyorgy@0 943 window.MediaElement = mejs.MediaElement;
gyorgy@0 944
gyorgy@0 945 /*!
gyorgy@0 946 * MediaElementPlayer
gyorgy@0 947 * http://mediaelementjs.com/
gyorgy@0 948 *
gyorgy@0 949 * Creates a controller bar for HTML5 <video> add <audio> tags
gyorgy@0 950 * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
gyorgy@0 951 *
gyorgy@0 952 * Copyright 2010-2011, John Dyer (http://j.hn/)
gyorgy@0 953 * Dual licensed under the MIT or GPL Version 2 licenses.
gyorgy@0 954 *
gyorgy@0 955 */
gyorgy@0 956 if (typeof jQuery != 'undefined') {
gyorgy@0 957 mejs.$ = jQuery;
gyorgy@0 958 } else if (typeof ender != 'undefined') {
gyorgy@0 959 mejs.$ = ender;
gyorgy@0 960 }
gyorgy@0 961 (function ($) {
gyorgy@0 962
gyorgy@0 963 // default player values
gyorgy@0 964 mejs.MepDefaults = {
gyorgy@0 965 // url to poster (to fix iOS 3.x)
gyorgy@0 966 poster: '',
gyorgy@0 967 // default if the <video width> is not specified
gyorgy@0 968 defaultVideoWidth: 480,
gyorgy@0 969 // default if the <video height> is not specified
gyorgy@0 970 defaultVideoHeight: 270,
gyorgy@0 971 // if set, overrides <video width>
gyorgy@0 972 videoWidth: -1,
gyorgy@0 973 // if set, overrides <video height>
gyorgy@0 974 videoHeight: -1,
gyorgy@0 975 // width of audio player
gyorgy@0 976 audioWidth: 400,
gyorgy@0 977 // height of audio player
gyorgy@0 978 audioHeight: 30,
gyorgy@0 979 // initial volume when the player starts (overrided by user cookie)
gyorgy@0 980 startVolume: 0.8,
gyorgy@0 981 // useful for <audio> player loops
gyorgy@0 982 loop: false,
gyorgy@0 983 // resize to media dimensions
gyorgy@0 984 enableAutosize: true,
gyorgy@0 985 // forces the hour marker (##:00:00)
gyorgy@0 986 alwaysShowHours: false,
gyorgy@0 987 // Hide controls when playing and mouse is not over the video
gyorgy@0 988 alwaysShowControls: false,
gyorgy@0 989 // force iPad's native controls
gyorgy@0 990 iPadUseNativeControls: true,
gyorgy@0 991 // features to show
gyorgy@0 992 features: ['playpause','current','progress','duration','tracks','volume','fullscreen']
gyorgy@0 993 };
gyorgy@0 994
gyorgy@0 995 mejs.mepIndex = 0;
gyorgy@0 996
gyorgy@0 997 // wraps a MediaElement object in player controls
gyorgy@0 998 mejs.MediaElementPlayer = function(node, o) {
gyorgy@0 999 // enforce object, even without "new" (via John Resig)
gyorgy@0 1000 if ( !(this instanceof mejs.MediaElementPlayer) ) {
gyorgy@0 1001 return new mejs.MediaElementPlayer(node, o);
gyorgy@0 1002 }
gyorgy@0 1003
gyorgy@0 1004 var
gyorgy@0 1005 t = this,
gyorgy@0 1006 mf = mejs.MediaFeatures;
gyorgy@0 1007
gyorgy@0 1008 // create options
gyorgy@0 1009 t.options = $.extend({},mejs.MepDefaults,o);
gyorgy@0 1010
gyorgy@0 1011 // these will be reset after the MediaElement.success fires
gyorgy@0 1012 t.$media = t.$node = $(node);
gyorgy@0 1013 t.node = t.media = t.$media[0];
gyorgy@0 1014
gyorgy@0 1015 // check for existing player
gyorgy@0 1016 if (typeof t.node.player != 'undefined') {
gyorgy@0 1017 return t.node.player;
gyorgy@0 1018 } else {
gyorgy@0 1019 // attach player to DOM node for reference
gyorgy@0 1020 t.node.player = t;
gyorgy@0 1021 }
gyorgy@0 1022
gyorgy@0 1023 t.isVideo = (t.media.tagName.toLowerCase() === 'video');
gyorgy@0 1024
gyorgy@0 1025 /* FUTURE WORK = create player without existing <video> or <audio> node
gyorgy@0 1026
gyorgy@0 1027 // if not a video or audio tag, then we'll dynamically create it
gyorgy@0 1028 if (tagName == 'video' || tagName == 'audio') {
gyorgy@0 1029 t.$media = $($node);
gyorgy@0 1030 } else if (o.tagName !== '' && o.src !== '') {
gyorgy@0 1031 // create a new node
gyorgy@0 1032 if (o.mode == 'auto' || o.mode == 'native') {
gyorgy@0 1033
gyorgy@0 1034 $media = $(o.tagName);
gyorgy@0 1035 if (typeof o.src == 'string') {
gyorgy@0 1036 $media.attr('src',o.src);
gyorgy@0 1037 } else if (typeof o.src == 'object') {
gyorgy@0 1038 // create source nodes
gyorgy@0 1039 for (var x in o.src) {
gyorgy@0 1040 $media.append($('<source src="' + o.src[x].src + '" type="' + o.src[x].type + '" />'));
gyorgy@0 1041 }
gyorgy@0 1042 }
gyorgy@0 1043 if (o.type != '') {
gyorgy@0 1044 $media.attr('type',o.type);
gyorgy@0 1045 }
gyorgy@0 1046 if (o.poster != '') {
gyorgy@0 1047 $media.attr('poster',o.poster);
gyorgy@0 1048 }
gyorgy@0 1049 if (o.videoWidth > 0) {
gyorgy@0 1050 $media.attr('width',o.videoWidth);
gyorgy@0 1051 }
gyorgy@0 1052 if (o.videoHeight > 0) {
gyorgy@0 1053 $media.attr('height',o.videoHeight);
gyorgy@0 1054 }
gyorgy@0 1055
gyorgy@0 1056 $node.clear();
gyorgy@0 1057 $node.append($media);
gyorgy@0 1058 t.$media = $media;
gyorgy@0 1059 } else if (o.mode == 'shim') {
gyorgy@0 1060 $media = $();
gyorgy@0 1061 // doesn't want a media node
gyorgy@0 1062 // let MediaElement object handle this
gyorgy@0 1063 }
gyorgy@0 1064 } else {
gyorgy@0 1065 // fail?
gyorgy@0 1066 return;
gyorgy@0 1067 }
gyorgy@0 1068 */
gyorgy@0 1069
gyorgy@0 1070 t.init();
gyorgy@0 1071
gyorgy@0 1072 return t;
gyorgy@0 1073 };
gyorgy@0 1074
gyorgy@0 1075 // actual player
gyorgy@0 1076 mejs.MediaElementPlayer.prototype = {
gyorgy@0 1077 init: function() {
gyorgy@0 1078
gyorgy@0 1079 var
gyorgy@0 1080 t = this,
gyorgy@0 1081 mf = mejs.MediaFeatures,
gyorgy@0 1082 // options for MediaElement (shim)
gyorgy@0 1083 meOptions = $.extend(true, {}, t.options, {
gyorgy@0 1084 success: function(media, domNode) { t.meReady(media, domNode); },
gyorgy@0 1085 error: function(e) { t.handleError(e);}
gyorgy@0 1086 });
gyorgy@0 1087
gyorgy@0 1088
gyorgy@0 1089 // use native controls in iPad, iPhone, and Android
gyorgy@0 1090 if ((mf.isiPad && t.options.iPadUseNativeControls) || mf.isiPhone) {
gyorgy@0 1091 // add controls and stop
gyorgy@0 1092 t.$media.attr('controls', 'controls');
gyorgy@0 1093
gyorgy@0 1094 // attempt to fix iOS 3 bug
gyorgy@0 1095 t.$media.removeAttr('poster');
gyorgy@0 1096
gyorgy@0 1097 // override Apple's autoplay override for iPads
gyorgy@0 1098 if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
gyorgy@0 1099 t.media.load();
gyorgy@0 1100 t.media.play();
gyorgy@0 1101 }
gyorgy@0 1102
gyorgy@0 1103 } else if (mf.isAndroid && t.isVideo) {
gyorgy@0 1104
gyorgy@0 1105 // leave default player
gyorgy@0 1106
gyorgy@0 1107 } else {
gyorgy@0 1108
gyorgy@0 1109 // DESKTOP: use MediaElementPlayer controls
gyorgy@0 1110
gyorgy@0 1111 // remove native controls
gyorgy@0 1112 t.$media.removeAttr('controls');
gyorgy@0 1113
gyorgy@0 1114 // unique ID
gyorgy@0 1115 t.id = 'mep_' + mejs.mepIndex++;
gyorgy@0 1116
gyorgy@0 1117 // build container
gyorgy@0 1118 t.container =
gyorgy@0 1119 $('<div id="' + t.id + '" class="mejs-container">'+
gyorgy@0 1120 '<div class="mejs-inner">'+
gyorgy@0 1121 '<div class="mejs-mediaelement"></div>'+
gyorgy@0 1122 '<div class="mejs-layers"></div>'+
gyorgy@0 1123 '<div class="mejs-controls"></div>'+
gyorgy@0 1124 '<div class="mejs-clear"></div>'+
gyorgy@0 1125 '</div>' +
gyorgy@0 1126 '</div>')
gyorgy@0 1127 .addClass(t.$media[0].className)
gyorgy@0 1128 .insertBefore(t.$media);
gyorgy@0 1129
gyorgy@0 1130 // move the <video/video> tag into the right spot
gyorgy@0 1131 t.container.find('.mejs-mediaelement').append(t.$media);
gyorgy@0 1132
gyorgy@0 1133 // find parts
gyorgy@0 1134 t.controls = t.container.find('.mejs-controls');
gyorgy@0 1135 t.layers = t.container.find('.mejs-layers');
gyorgy@0 1136
gyorgy@0 1137 // determine the size
gyorgy@0 1138 if (t.isVideo) {
gyorgy@0 1139 // priority = videoWidth (forced), width attribute, defaultVideoWidth
gyorgy@0 1140 t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth;
gyorgy@0 1141 t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight;
gyorgy@0 1142 } else {
gyorgy@0 1143 t.width = t.options.audioWidth;
gyorgy@0 1144 t.height = t.options.audioHeight;
gyorgy@0 1145 }
gyorgy@0 1146
gyorgy@0 1147 // set the size, while we wait for the plugins to load below
gyorgy@0 1148 t.setPlayerSize(t.width, t.height);
gyorgy@0 1149
gyorgy@0 1150 // create MediaElementShim
gyorgy@0 1151 meOptions.pluginWidth = t.height;
gyorgy@0 1152 meOptions.pluginHeight = t.width;
gyorgy@0 1153 }
gyorgy@0 1154
gyorgy@0 1155 // create MediaElement shim
gyorgy@0 1156 mejs.MediaElement(t.$media[0], meOptions);
gyorgy@0 1157 },
gyorgy@0 1158
gyorgy@0 1159 // Sets up all controls and events
gyorgy@0 1160 meReady: function(media, domNode) {
gyorgy@0 1161
gyorgy@0 1162
gyorgy@0 1163 var t = this,
gyorgy@0 1164 mf = mejs.MediaFeatures,
gyorgy@0 1165 f,
gyorgy@0 1166 feature;
gyorgy@0 1167
gyorgy@0 1168 // make sure it can't create itself again if a plugin reloads
gyorgy@0 1169 if (t.created)
gyorgy@0 1170 return;
gyorgy@0 1171 else
gyorgy@0 1172 t.created = true;
gyorgy@0 1173
gyorgy@0 1174 t.media = media;
gyorgy@0 1175 t.domNode = domNode;
gyorgy@0 1176
gyorgy@0 1177 if (!mf.isiPhone && !mf.isAndroid && !(mf.isiPad && t.options.iPadUseNativeControls)) {
gyorgy@0 1178
gyorgy@0 1179 // two built in features
gyorgy@0 1180 t.buildposter(t, t.controls, t.layers, t.media);
gyorgy@0 1181 t.buildoverlays(t, t.controls, t.layers, t.media);
gyorgy@0 1182
gyorgy@0 1183 // grab for use by features
gyorgy@0 1184 t.findTracks();
gyorgy@0 1185
gyorgy@0 1186 // add user-defined features/controls
gyorgy@0 1187 for (f in t.options.features) {
gyorgy@0 1188 feature = t.options.features[f];
gyorgy@0 1189 if (t['build' + feature]) {
gyorgy@0 1190 try {
gyorgy@0 1191 t['build' + feature](t, t.controls, t.layers, t.media);
gyorgy@0 1192 } catch (e) {
gyorgy@0 1193 // TODO: report control error
gyorgy@0 1194 //throw e;
gyorgy@0 1195 }
gyorgy@0 1196 }
gyorgy@0 1197 }
gyorgy@0 1198
gyorgy@0 1199 t.container.trigger('controlsready');
gyorgy@0 1200
gyorgy@0 1201 // reset all layers and controls
gyorgy@0 1202 t.setPlayerSize(t.width, t.height);
gyorgy@0 1203 t.setControlsSize();
gyorgy@0 1204
gyorgy@0 1205
gyorgy@0 1206 // controls fade
gyorgy@0 1207 if (t.isVideo) {
gyorgy@0 1208 // show/hide controls
gyorgy@0 1209 t.container
gyorgy@0 1210 .bind('mouseenter', function () {
gyorgy@0 1211 if (!t.options.alwaysShowControls) {
gyorgy@0 1212 t.controls.css('visibility','visible');
gyorgy@0 1213 t.controls.stop(true, true).fadeIn(200);
gyorgy@0 1214 }
gyorgy@0 1215 })
gyorgy@0 1216 .bind('mouseleave', function () {
gyorgy@0 1217 if (!t.media.paused && !t.options.alwaysShowControls) {
gyorgy@0 1218 t.controls.stop(true, true).fadeOut(200, function() {
gyorgy@0 1219 $(this).css('visibility','hidden');
gyorgy@0 1220 $(this).css('display','block');
gyorgy@0 1221 });
gyorgy@0 1222 }
gyorgy@0 1223 });
gyorgy@0 1224
gyorgy@0 1225 // check for autoplay
gyorgy@0 1226 if (t.domNode.getAttribute('autoplay') !== null && !t.options.alwaysShowControls) {
gyorgy@0 1227 t.controls.css('visibility','hidden');
gyorgy@0 1228 }
gyorgy@0 1229
gyorgy@0 1230 // resizer
gyorgy@0 1231 if (t.options.enableAutosize) {
gyorgy@0 1232 t.media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 1233 // if the <video height> was not set and the options.videoHeight was not set
gyorgy@0 1234 // then resize to the real dimensions
gyorgy@0 1235 if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
gyorgy@0 1236 t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
gyorgy@0 1237 t.setControlsSize();
gyorgy@0 1238 t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
gyorgy@0 1239 }
gyorgy@0 1240 }, false);
gyorgy@0 1241 }
gyorgy@0 1242 }
gyorgy@0 1243
gyorgy@0 1244 // ended for all
gyorgy@0 1245 t.media.addEventListener('ended', function (e) {
gyorgy@0 1246 t.media.setCurrentTime(0);
gyorgy@0 1247 t.media.pause();
gyorgy@0 1248
gyorgy@0 1249 if (t.setProgressRail)
gyorgy@0 1250 t.setProgressRail();
gyorgy@0 1251 if (t.setCurrentRail)
gyorgy@0 1252 t.setCurrentRail();
gyorgy@0 1253
gyorgy@0 1254 if (t.options.loop) {
gyorgy@0 1255 t.media.play();
gyorgy@0 1256 } else if (!t.options.alwaysShowControls) {
gyorgy@0 1257 t.controls.css('visibility','visible');
gyorgy@0 1258 }
gyorgy@0 1259 }, true);
gyorgy@0 1260
gyorgy@0 1261 // resize on the first play
gyorgy@0 1262 t.media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 1263 if (t.updateDuration) {
gyorgy@0 1264 t.updateDuration();
gyorgy@0 1265 }
gyorgy@0 1266 if (t.updateCurrent) {
gyorgy@0 1267 t.updateCurrent();
gyorgy@0 1268 }
gyorgy@0 1269
gyorgy@0 1270 t.setControlsSize();
gyorgy@0 1271 }, true);
gyorgy@0 1272
gyorgy@0 1273
gyorgy@0 1274 // webkit has trouble doing this without a delay
gyorgy@0 1275 setTimeout(function () {
gyorgy@0 1276 t.setControlsSize();
gyorgy@0 1277 t.setPlayerSize(t.width, t.height);
gyorgy@0 1278 }, 50);
gyorgy@0 1279
gyorgy@0 1280 }
gyorgy@0 1281
gyorgy@0 1282
gyorgy@0 1283 if (t.options.success) {
gyorgy@0 1284 t.options.success(t.media, t.domNode);
gyorgy@0 1285 }
gyorgy@0 1286 },
gyorgy@0 1287
gyorgy@0 1288 handleError: function(e) {
gyorgy@0 1289 // Tell user that the file cannot be played
gyorgy@0 1290 if (this.options.error) {
gyorgy@0 1291 this.options.error(e);
gyorgy@0 1292 }
gyorgy@0 1293 },
gyorgy@0 1294
gyorgy@0 1295 setPlayerSize: function(width,height) {
gyorgy@0 1296 var t = this;
gyorgy@0 1297
gyorgy@0 1298 // ie9 appears to need this (jQuery bug?)
gyorgy@0 1299 t.width = parseInt(width, 10);
gyorgy@0 1300 t.height = parseInt(height, 10);
gyorgy@0 1301
gyorgy@0 1302 t.container
gyorgy@0 1303 .width(t.width)
gyorgy@0 1304 .height(t.height);
gyorgy@0 1305
gyorgy@0 1306 t.layers.children('.mejs-layer')
gyorgy@0 1307 .width(t.width)
gyorgy@0 1308 .height(t.height);
gyorgy@0 1309 },
gyorgy@0 1310
gyorgy@0 1311 setControlsSize: function() {
gyorgy@0 1312 var t = this,
gyorgy@0 1313 usedWidth = 0,
gyorgy@0 1314 railWidth = 0,
gyorgy@0 1315 rail = t.controls.find('.mejs-time-rail'),
gyorgy@0 1316 total = t.controls.find('.mejs-time-total'),
gyorgy@0 1317 current = t.controls.find('.mejs-time-current'),
gyorgy@0 1318 loaded = t.controls.find('.mejs-time-loaded');
gyorgy@0 1319 others = rail.siblings();
gyorgy@0 1320
gyorgy@0 1321 // find the size of all the other controls besides the rail
gyorgy@0 1322 others.each(function() {
gyorgy@0 1323 if ($(this).css('position') != 'absolute') {
gyorgy@0 1324 usedWidth += $(this).outerWidth(true);
gyorgy@0 1325 }
gyorgy@0 1326 });
gyorgy@0 1327 // fit the rail into the remaining space
gyorgy@0 1328 railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false));
gyorgy@0 1329
gyorgy@0 1330 // outer area
gyorgy@0 1331 rail.width(railWidth);
gyorgy@0 1332 // dark space
gyorgy@0 1333 total.width(railWidth - (total.outerWidth(true) - total.width()));
gyorgy@0 1334
gyorgy@0 1335 if (t.setProgressRail)
gyorgy@0 1336 t.setProgressRail();
gyorgy@0 1337 if (t.setCurrentRail)
gyorgy@0 1338 t.setCurrentRail();
gyorgy@0 1339 },
gyorgy@0 1340
gyorgy@0 1341
gyorgy@0 1342 buildposter: function(player, controls, layers, media) {
gyorgy@0 1343 var poster =
gyorgy@0 1344 $('<div class="mejs-poster mejs-layer">'+
gyorgy@0 1345 '<img />'+
gyorgy@0 1346 '</div>')
gyorgy@0 1347 .appendTo(layers),
gyorgy@0 1348 posterUrl = player.$media.attr('poster'),
gyorgy@0 1349 posterImg = poster.find('img').width(player.width).height(player.height);
gyorgy@0 1350
gyorgy@0 1351 // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
gyorgy@0 1352 if (player.options.poster != '') {
gyorgy@0 1353 posterImg.attr('src',player.options.poster);
gyorgy@0 1354 // second, try the real poster
gyorgy@0 1355 } else if (posterUrl !== '' && posterUrl != null) {
gyorgy@0 1356 posterImg.attr('src',posterUrl);
gyorgy@0 1357 } else {
gyorgy@0 1358 poster.remove();
gyorgy@0 1359 }
gyorgy@0 1360
gyorgy@0 1361 media.addEventListener('play',function() {
gyorgy@0 1362 poster.hide();
gyorgy@0 1363 }, false);
gyorgy@0 1364 },
gyorgy@0 1365
gyorgy@0 1366 buildoverlays: function(player, controls, layers, media) {
gyorgy@0 1367 if (!player.isVideo)
gyorgy@0 1368 return;
gyorgy@0 1369
gyorgy@0 1370 var
gyorgy@0 1371 loading =
gyorgy@0 1372 $('<div class="mejs-overlay mejs-layer">'+
gyorgy@0 1373 '<div class="mejs-overlay-loading"><span></span></div>'+
gyorgy@0 1374 '</div>')
gyorgy@0 1375 .hide() // start out hidden
gyorgy@0 1376 .appendTo(layers),
gyorgy@0 1377 error =
gyorgy@0 1378 $('<div class="mejs-overlay mejs-layer">'+
gyorgy@0 1379 '<div class="mejs-overlay-error"></div>'+
gyorgy@0 1380 '</div>')
gyorgy@0 1381 .hide() // start out hidden
gyorgy@0 1382 .appendTo(layers),
gyorgy@0 1383
gyorgy@0 1384 // this needs to come last so it's on top
gyorgy@0 1385 bigPlay =
gyorgy@0 1386 $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
gyorgy@0 1387 '<div class="mejs-overlay-button"></div>'+
gyorgy@0 1388 '</div>')
gyorgy@0 1389 .appendTo(layers)
gyorgy@0 1390 .click(function() {
gyorgy@0 1391 if (media.paused) {
gyorgy@0 1392 media.play();
gyorgy@0 1393 } else {
gyorgy@0 1394 media.pause();
gyorgy@0 1395 }
gyorgy@0 1396 });
gyorgy@0 1397
gyorgy@0 1398
gyorgy@0 1399 // show/hide big play button
gyorgy@0 1400 media.addEventListener('play',function() {
gyorgy@0 1401 bigPlay.hide();
gyorgy@0 1402 error.hide();
gyorgy@0 1403 }, false);
gyorgy@0 1404 media.addEventListener('pause',function() {
gyorgy@0 1405 bigPlay.show();
gyorgy@0 1406 }, false);
gyorgy@0 1407
gyorgy@0 1408 // show/hide loading
gyorgy@0 1409 media.addEventListener('loadstart',function() {
gyorgy@0 1410 // for some reason Chrome is firing this event
gyorgy@0 1411 if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
gyorgy@0 1412 return;
gyorgy@0 1413
gyorgy@0 1414 loading.show();
gyorgy@0 1415 }, false);
gyorgy@0 1416 media.addEventListener('canplay',function() {
gyorgy@0 1417 loading.hide();
gyorgy@0 1418 }, false);
gyorgy@0 1419
gyorgy@0 1420 // error handling
gyorgy@0 1421 media.addEventListener('error',function() {
gyorgy@0 1422 loading.hide();
gyorgy@0 1423 error.show();
gyorgy@0 1424 error.find('mejs-overlay-error').html("Error loading this resource");
gyorgy@0 1425 }, false);
gyorgy@0 1426 },
gyorgy@0 1427
gyorgy@0 1428 findTracks: function() {
gyorgy@0 1429 var t = this,
gyorgy@0 1430 tracktags = t.$media.find('track');
gyorgy@0 1431
gyorgy@0 1432 // store for use by plugins
gyorgy@0 1433 t.tracks = [];
gyorgy@0 1434 tracktags.each(function() {
gyorgy@0 1435 t.tracks.push({
gyorgy@0 1436 srclang: $(this).attr('srclang').toLowerCase(),
gyorgy@0 1437 src: $(this).attr('src'),
gyorgy@0 1438 kind: $(this).attr('kind'),
gyorgy@0 1439 entries: [],
gyorgy@0 1440 isLoaded: false
gyorgy@0 1441 });
gyorgy@0 1442 });
gyorgy@0 1443 },
gyorgy@0 1444 changeSkin: function(className) {
gyorgy@0 1445 this.container[0].className = 'mejs-container ' + className;
gyorgy@0 1446 this.setPlayerSize();
gyorgy@0 1447 this.setControlsSize();
gyorgy@0 1448 },
gyorgy@0 1449 play: function() {
gyorgy@0 1450 this.media.play();
gyorgy@0 1451 },
gyorgy@0 1452 pause: function() {
gyorgy@0 1453 this.media.pause();
gyorgy@0 1454 },
gyorgy@0 1455 load: function() {
gyorgy@0 1456 this.media.load();
gyorgy@0 1457 },
gyorgy@0 1458 setMuted: function(muted) {
gyorgy@0 1459 this.media.setMuted(muted);
gyorgy@0 1460 },
gyorgy@0 1461 setCurrentTime: function(time) {
gyorgy@0 1462 this.media.setCurrentTime(time);
gyorgy@0 1463 },
gyorgy@0 1464 getCurrentTime: function() {
gyorgy@0 1465 return this.media.currentTime;
gyorgy@0 1466 },
gyorgy@0 1467 setVolume: function(volume) {
gyorgy@0 1468 this.media.setVolume(volume);
gyorgy@0 1469 },
gyorgy@0 1470 getVolume: function() {
gyorgy@0 1471 return this.media.volume;
gyorgy@0 1472 },
gyorgy@0 1473 setSrc: function(src) {
gyorgy@0 1474 this.media.setSrc(src);
gyorgy@0 1475 }
gyorgy@0 1476 };
gyorgy@0 1477
gyorgy@0 1478 // turn into jQuery plugin
gyorgy@0 1479 if (typeof jQuery != 'undefined') {
gyorgy@0 1480 jQuery.fn.mediaelementplayer = function (options) {
gyorgy@0 1481 return this.each(function () {
gyorgy@0 1482 new mejs.MediaElementPlayer(this, options);
gyorgy@0 1483 });
gyorgy@0 1484 };
gyorgy@0 1485 }
gyorgy@0 1486
gyorgy@0 1487 // push out to window
gyorgy@0 1488 window.MediaElementPlayer = mejs.MediaElementPlayer;
gyorgy@0 1489
gyorgy@0 1490 })(mejs.$);
gyorgy@0 1491 (function($) {
gyorgy@0 1492 // PLAY/pause BUTTON
gyorgy@0 1493 MediaElementPlayer.prototype.buildplaypause = function(player, controls, layers, media) {
gyorgy@0 1494 var play =
gyorgy@0 1495 $('<div class="mejs-button mejs-playpause-button mejs-play" type="button">' +
gyorgy@0 1496 '<button type="button"></button>' +
gyorgy@0 1497 '</div>')
gyorgy@0 1498 .appendTo(controls)
gyorgy@0 1499 .click(function(e) {
gyorgy@0 1500 e.preventDefault();
gyorgy@0 1501
gyorgy@0 1502 if (media.paused) {
gyorgy@0 1503 media.play();
gyorgy@0 1504 } else {
gyorgy@0 1505 media.pause();
gyorgy@0 1506 }
gyorgy@0 1507
gyorgy@0 1508 return false;
gyorgy@0 1509 });
gyorgy@0 1510
gyorgy@0 1511 media.addEventListener('play',function() {
gyorgy@0 1512 play.removeClass('mejs-play').addClass('mejs-pause');
gyorgy@0 1513 }, false);
gyorgy@0 1514 media.addEventListener('playing',function() {
gyorgy@0 1515 play.removeClass('mejs-play').addClass('mejs-pause');
gyorgy@0 1516 }, false);
gyorgy@0 1517
gyorgy@0 1518
gyorgy@0 1519 media.addEventListener('pause',function() {
gyorgy@0 1520 play.removeClass('mejs-pause').addClass('mejs-play');
gyorgy@0 1521 }, false);
gyorgy@0 1522 media.addEventListener('paused',function() {
gyorgy@0 1523 play.removeClass('mejs-pause').addClass('mejs-play');
gyorgy@0 1524 }, false);
gyorgy@0 1525 }
gyorgy@0 1526
gyorgy@0 1527 })(mejs.$);
gyorgy@0 1528 (function($) {
gyorgy@0 1529 // STOP BUTTON
gyorgy@0 1530 MediaElementPlayer.prototype.buildstop = function(player, controls, layers, media) {
gyorgy@0 1531 var stop =
gyorgy@0 1532 $('<div class="mejs-button mejs-stop-button mejs-stop">' +
gyorgy@0 1533 '<button type="button"></button>' +
gyorgy@0 1534 '</div>')
gyorgy@0 1535 .appendTo(controls)
gyorgy@0 1536 .click(function() {
gyorgy@0 1537 if (!media.paused) {
gyorgy@0 1538 media.pause();
gyorgy@0 1539 }
gyorgy@0 1540 if (media.currentTime > 0) {
gyorgy@0 1541 media.setCurrentTime(0);
gyorgy@0 1542 controls.find('.mejs-time-current').width('0px');
gyorgy@0 1543 controls.find('.mejs-time-handle').css('left', '0px');
gyorgy@0 1544 controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
gyorgy@0 1545 controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );
gyorgy@0 1546 layers.find('.mejs-poster').show();
gyorgy@0 1547 }
gyorgy@0 1548 });
gyorgy@0 1549 }
gyorgy@0 1550
gyorgy@0 1551 })(mejs.$);
gyorgy@0 1552 (function($) {
gyorgy@0 1553 // progress/loaded bar
gyorgy@0 1554 MediaElementPlayer.prototype.buildprogress = function(player, controls, layers, media) {
gyorgy@0 1555
gyorgy@0 1556 $('<div class="mejs-time-rail">'+
gyorgy@0 1557 '<span class="mejs-time-total">'+
gyorgy@0 1558 '<span class="mejs-time-loaded"></span>'+
gyorgy@0 1559 '<span class="mejs-time-current"></span>'+
gyorgy@0 1560 '<span class="mejs-time-handle"></span>'+
gyorgy@0 1561 '<span class="mejs-time-float">' +
gyorgy@0 1562 '<span class="mejs-time-float-current">00:00</span>' +
gyorgy@0 1563 '<span class="mejs-time-float-corner"></span>' +
gyorgy@0 1564 '</span>'+
gyorgy@0 1565 '</span>'+
gyorgy@0 1566 '</div>')
gyorgy@0 1567 .appendTo(controls);
gyorgy@0 1568
gyorgy@0 1569 var
gyorgy@0 1570 t = this,
gyorgy@0 1571 total = controls.find('.mejs-time-total'),
gyorgy@0 1572 loaded = controls.find('.mejs-time-loaded'),
gyorgy@0 1573 current = controls.find('.mejs-time-current'),
gyorgy@0 1574 handle = controls.find('.mejs-time-handle'),
gyorgy@0 1575 timefloat = controls.find('.mejs-time-float'),
gyorgy@0 1576 timefloatcurrent = controls.find('.mejs-time-float-current'),
gyorgy@0 1577 handleMouseMove = function (e) {
gyorgy@0 1578 // mouse position relative to the object
gyorgy@0 1579 var x = e.pageX,
gyorgy@0 1580 offset = total.offset(),
gyorgy@0 1581 width = total.outerWidth(),
gyorgy@0 1582 percentage = 0,
gyorgy@0 1583 newTime = 0;
gyorgy@0 1584
gyorgy@0 1585
gyorgy@0 1586 if (x > offset.left && x <= width + offset.left && media.duration) {
gyorgy@0 1587 percentage = ((x - offset.left) / width);
gyorgy@0 1588 newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
gyorgy@0 1589
gyorgy@0 1590 // seek to where the mouse is
gyorgy@0 1591 if (mouseIsDown) {
gyorgy@0 1592 media.setCurrentTime(newTime);
gyorgy@0 1593 }
gyorgy@0 1594
gyorgy@0 1595 // position floating time box
gyorgy@0 1596 var pos = x - offset.left;
gyorgy@0 1597 timefloat.css('left', pos);
gyorgy@0 1598 timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
gyorgy@0 1599 }
gyorgy@0 1600 },
gyorgy@0 1601 mouseIsDown = false,
gyorgy@0 1602 mouseIsOver = false;
gyorgy@0 1603
gyorgy@0 1604 // handle clicks
gyorgy@0 1605 //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
gyorgy@0 1606 total
gyorgy@0 1607 .bind('mousedown', function (e) {
gyorgy@0 1608 mouseIsDown = true;
gyorgy@0 1609 handleMouseMove(e);
gyorgy@0 1610 return false;
gyorgy@0 1611 });
gyorgy@0 1612
gyorgy@0 1613 controls.find('.mejs-time-rail')
gyorgy@0 1614 .bind('mouseenter', function(e) {
gyorgy@0 1615 mouseIsOver = true;
gyorgy@0 1616 })
gyorgy@0 1617 .bind('mouseleave',function(e) {
gyorgy@0 1618 mouseIsOver = false;
gyorgy@0 1619 });
gyorgy@0 1620
gyorgy@0 1621 $(document)
gyorgy@0 1622 .bind('mouseup', function (e) {
gyorgy@0 1623 mouseIsDown = false;
gyorgy@0 1624 //handleMouseMove(e);
gyorgy@0 1625 })
gyorgy@0 1626 .bind('mousemove', function (e) {
gyorgy@0 1627 if (mouseIsDown || mouseIsOver) {
gyorgy@0 1628 handleMouseMove(e);
gyorgy@0 1629 }
gyorgy@0 1630 });
gyorgy@0 1631
gyorgy@0 1632 // loading
gyorgy@0 1633 media.addEventListener('progress', function (e) {
gyorgy@0 1634 player.setProgressRail(e);
gyorgy@0 1635 player.setCurrentRail(e);
gyorgy@0 1636 }, false);
gyorgy@0 1637
gyorgy@0 1638 // current time
gyorgy@0 1639 media.addEventListener('timeupdate', function(e) {
gyorgy@0 1640 player.setProgressRail(e);
gyorgy@0 1641 player.setCurrentRail(e);
gyorgy@0 1642 }, false);
gyorgy@0 1643
gyorgy@0 1644
gyorgy@0 1645 // store for later use
gyorgy@0 1646 t.loaded = loaded;
gyorgy@0 1647 t.total = total;
gyorgy@0 1648 t.current = current;
gyorgy@0 1649 t.handle = handle;
gyorgy@0 1650 }
gyorgy@0 1651 MediaElementPlayer.prototype.setProgressRail = function(e) {
gyorgy@0 1652
gyorgy@0 1653 var
gyorgy@0 1654 t = this,
gyorgy@0 1655 target = (e != undefined) ? e.target : t.media,
gyorgy@0 1656 percent = null;
gyorgy@0 1657
gyorgy@0 1658 // newest HTML5 spec has buffered array (FF4, Webkit)
gyorgy@0 1659 if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
gyorgy@0 1660 // TODO: account for a real array with multiple values (only Firefox 4 has this so far)
gyorgy@0 1661 percent = target.buffered.end(0) / target.duration;
gyorgy@0 1662 }
gyorgy@0 1663 // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
gyorgy@0 1664 // to be anything other than 0. If the byte count is available we use this instead.
gyorgy@0 1665 // Browsers that support the else if do not seem to have the bufferedBytes value and
gyorgy@0 1666 // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
gyorgy@0 1667 else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
gyorgy@0 1668 percent = target.bufferedBytes / target.bytesTotal;
gyorgy@0 1669 }
gyorgy@0 1670 // Firefox 3 with an Ogg file seems to go this way
gyorgy@0 1671 else if (e && e.lengthComputable && e.total != 0) {
gyorgy@0 1672 percent = e.loaded/e.total;
gyorgy@0 1673 }
gyorgy@0 1674
gyorgy@0 1675 // finally update the progress bar
gyorgy@0 1676 if (percent !== null) {
gyorgy@0 1677 percent = Math.min(1, Math.max(0, percent));
gyorgy@0 1678 // update loaded bar
gyorgy@0 1679 if (t.loaded && t.total) {
gyorgy@0 1680 t.loaded.width(t.total.width() * percent);
gyorgy@0 1681 }
gyorgy@0 1682 }
gyorgy@0 1683 }
gyorgy@0 1684 MediaElementPlayer.prototype.setCurrentRail = function() {
gyorgy@0 1685
gyorgy@0 1686 var t = this;
gyorgy@0 1687
gyorgy@0 1688 if (t.media.currentTime != undefined && t.media.duration) {
gyorgy@0 1689
gyorgy@0 1690 // update bar and handle
gyorgy@0 1691 if (t.total && t.handle) {
gyorgy@0 1692 var
gyorgy@0 1693 newWidth = t.total.width() * t.media.currentTime / t.media.duration,
gyorgy@0 1694 handlePos = newWidth - (t.handle.outerWidth(true) / 2);
gyorgy@0 1695
gyorgy@0 1696 t.current.width(newWidth);
gyorgy@0 1697 t.handle.css('left', handlePos);
gyorgy@0 1698 }
gyorgy@0 1699 }
gyorgy@0 1700
gyorgy@0 1701 }
gyorgy@0 1702
gyorgy@0 1703 })(mejs.$);
gyorgy@0 1704 (function($) {
gyorgy@0 1705 // current and duration 00:00 / 00:00
gyorgy@0 1706 MediaElementPlayer.prototype.buildcurrent = function(player, controls, layers, media) {
gyorgy@0 1707 var t = this;
gyorgy@0 1708
gyorgy@0 1709 $('<div class="mejs-time">'+
gyorgy@0 1710 '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>'+
gyorgy@0 1711 '</div>')
gyorgy@0 1712 .appendTo(controls);
gyorgy@0 1713
gyorgy@0 1714 t.currenttime = t.controls.find('.mejs-currenttime');
gyorgy@0 1715
gyorgy@0 1716 media.addEventListener('timeupdate',function() {
gyorgy@0 1717 player.updateCurrent();
gyorgy@0 1718 }, false);
gyorgy@0 1719 };
gyorgy@0 1720
gyorgy@0 1721 MediaElementPlayer.prototype.buildduration = function(player, controls, layers, media) {
gyorgy@0 1722 var t = this;
gyorgy@0 1723
gyorgy@0 1724 if (controls.children().last().find('.mejs-currenttime').length > 0) {
gyorgy@0 1725 $(' <span> | </span> '+
gyorgy@0 1726 '<span class="mejs-duration">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>')
gyorgy@0 1727 .appendTo(controls.find('.mejs-time'));
gyorgy@0 1728 } else {
gyorgy@0 1729
gyorgy@0 1730 // add class to current time
gyorgy@0 1731 controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
gyorgy@0 1732
gyorgy@0 1733 $('<div class="mejs-time mejs-duration-container">'+
gyorgy@0 1734 '<span class="mejs-duration">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>'+
gyorgy@0 1735 '</div>')
gyorgy@0 1736 .appendTo(controls);
gyorgy@0 1737 }
gyorgy@0 1738
gyorgy@0 1739 t.durationD = t.controls.find('.mejs-duration');
gyorgy@0 1740
gyorgy@0 1741 media.addEventListener('timeupdate',function() {
gyorgy@0 1742 player.updateDuration();
gyorgy@0 1743 }, false);
gyorgy@0 1744 };
gyorgy@0 1745
gyorgy@0 1746 MediaElementPlayer.prototype.updateCurrent = function() {
gyorgy@0 1747 var t = this;
gyorgy@0 1748
gyorgy@0 1749 if (t.currenttime) {
gyorgy@0 1750 t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime | 0, t.options.alwaysShowHours || t.media.duration > 3600 ));
gyorgy@0 1751 }
gyorgy@0 1752 }
gyorgy@0 1753 MediaElementPlayer.prototype.updateDuration = function() {
gyorgy@0 1754 var t = this;
gyorgy@0 1755
gyorgy@0 1756 if (t.media.duration && t.durationD) {
gyorgy@0 1757 t.durationD.html(mejs.Utility.secondsToTimeCode(t.media.duration, t.options.alwaysShowHours));
gyorgy@0 1758 }
gyorgy@0 1759 };
gyorgy@0 1760
gyorgy@0 1761 })(mejs.$);
gyorgy@0 1762 (function($) {
gyorgy@0 1763 MediaElementPlayer.prototype.buildvolume = function(player, controls, layers, media) {
gyorgy@0 1764 var mute =
gyorgy@0 1765 $('<div class="mejs-button mejs-volume-button mejs-mute">'+
gyorgy@0 1766 '<button type="button"></button>'+
gyorgy@0 1767 '<div class="mejs-volume-slider">'+ // outer background
gyorgy@0 1768 '<div class="mejs-volume-total"></div>'+ // line background
gyorgy@0 1769 '<div class="mejs-volume-current"></div>'+ // current volume
gyorgy@0 1770 '<div class="mejs-volume-handle"></div>'+ // handle
gyorgy@0 1771 '</div>'+
gyorgy@0 1772 '</div>')
gyorgy@0 1773 .appendTo(controls),
gyorgy@0 1774 volumeSlider = mute.find('.mejs-volume-slider'),
gyorgy@0 1775 volumeTotal = mute.find('.mejs-volume-total'),
gyorgy@0 1776 volumeCurrent = mute.find('.mejs-volume-current'),
gyorgy@0 1777 volumeHandle = mute.find('.mejs-volume-handle'),
gyorgy@0 1778
gyorgy@0 1779 positionVolumeHandle = function(volume) {
gyorgy@0 1780
gyorgy@0 1781 var
gyorgy@0 1782 top = volumeTotal.height() - (volumeTotal.height() * volume);
gyorgy@0 1783
gyorgy@0 1784 // handle
gyorgy@0 1785 volumeHandle.css('top', top - (volumeHandle.height() / 2));
gyorgy@0 1786
gyorgy@0 1787 // show the current visibility
gyorgy@0 1788 volumeCurrent.height(volumeTotal.height() - top + parseInt(volumeTotal.css('top').replace(/px/,''),10));
gyorgy@0 1789 volumeCurrent.css('top', top);
gyorgy@0 1790 },
gyorgy@0 1791 handleVolumeMove = function(e) {
gyorgy@0 1792 var
gyorgy@0 1793 railHeight = volumeTotal.height(),
gyorgy@0 1794 totalOffset = volumeTotal.offset(),
gyorgy@0 1795 totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
gyorgy@0 1796 newY = e.pageY - totalOffset.top,
gyorgy@0 1797 volume = (railHeight - newY) / railHeight
gyorgy@0 1798
gyorgy@0 1799 // TODO: handle vertical and horizontal CSS
gyorgy@0 1800 // only allow it to move within the rail
gyorgy@0 1801 if (newY < 0)
gyorgy@0 1802 newY = 0;
gyorgy@0 1803 else if (newY > railHeight)
gyorgy@0 1804 newY = railHeight;
gyorgy@0 1805
gyorgy@0 1806 // move the handle to match the mouse
gyorgy@0 1807 volumeHandle.css('top', newY - (volumeHandle.height() / 2) + totalTop );
gyorgy@0 1808
gyorgy@0 1809 // show the current visibility
gyorgy@0 1810 volumeCurrent.height(railHeight-newY);
gyorgy@0 1811 volumeCurrent.css('top',newY+totalTop);
gyorgy@0 1812
gyorgy@0 1813 // set mute status
gyorgy@0 1814 if (volume == 0) {
gyorgy@0 1815 media.setMuted(true);
gyorgy@0 1816 mute.removeClass('mejs-mute').addClass('mejs-unmute');
gyorgy@0 1817 } else {
gyorgy@0 1818 media.setMuted(false);
gyorgy@0 1819 mute.removeClass('mejs-unmute').addClass('mejs-mute');
gyorgy@0 1820 }
gyorgy@0 1821
gyorgy@0 1822 volume = Math.max(0,volume);
gyorgy@0 1823 volume = Math.min(volume,1);
gyorgy@0 1824
gyorgy@0 1825 // set the volume
gyorgy@0 1826 media.setVolume(volume);
gyorgy@0 1827 },
gyorgy@0 1828 mouseIsDown = false;
gyorgy@0 1829
gyorgy@0 1830 // SLIDER
gyorgy@0 1831 volumeSlider
gyorgy@0 1832 .bind('mousedown', function (e) {
gyorgy@0 1833 handleVolumeMove(e);
gyorgy@0 1834 mouseIsDown = true;
gyorgy@0 1835 return false;
gyorgy@0 1836 });
gyorgy@0 1837 $(document)
gyorgy@0 1838 .bind('mouseup', function (e) {
gyorgy@0 1839 mouseIsDown = false;
gyorgy@0 1840 })
gyorgy@0 1841 .bind('mousemove', function (e) {
gyorgy@0 1842 if (mouseIsDown) {
gyorgy@0 1843 handleVolumeMove(e);
gyorgy@0 1844 }
gyorgy@0 1845 });
gyorgy@0 1846
gyorgy@0 1847
gyorgy@0 1848 // MUTE button
gyorgy@0 1849 mute.find('button').click(function() {
gyorgy@0 1850 if (media.muted) {
gyorgy@0 1851 media.setMuted(false);
gyorgy@0 1852 mute.removeClass('mejs-unmute').addClass('mejs-mute');
gyorgy@0 1853 positionVolumeHandle(1);
gyorgy@0 1854 } else {
gyorgy@0 1855 media.setMuted(true);
gyorgy@0 1856 mute.removeClass('mejs-mute').addClass('mejs-unmute');
gyorgy@0 1857 positionVolumeHandle(0);
gyorgy@0 1858 }
gyorgy@0 1859 });
gyorgy@0 1860
gyorgy@0 1861 // listen for volume change events from other sources
gyorgy@0 1862 media.addEventListener('volumechange', function(e) {
gyorgy@0 1863 if (!mouseIsDown) {
gyorgy@0 1864 positionVolumeHandle(e.target.volume);
gyorgy@0 1865 }
gyorgy@0 1866 }, true);
gyorgy@0 1867
gyorgy@0 1868 // set initial volume
gyorgy@0 1869 positionVolumeHandle(player.options.startVolume);
gyorgy@0 1870
gyorgy@0 1871 // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
gyorgy@0 1872 if (media.pluginType === 'native') {
gyorgy@0 1873 media.setVolume(player.options.startVolume);
gyorgy@0 1874 }
gyorgy@0 1875 }
gyorgy@0 1876
gyorgy@0 1877 })(mejs.$);
gyorgy@0 1878
gyorgy@0 1879 (function($) {
gyorgy@0 1880 mejs.MediaElementDefaults.forcePluginFullScreen = false;
gyorgy@0 1881
gyorgy@0 1882 MediaElementPlayer.prototype.isFullScreen = false;
gyorgy@0 1883 MediaElementPlayer.prototype.buildfullscreen = function(player, controls, layers, media) {
gyorgy@0 1884
gyorgy@0 1885 if (!player.isVideo)
gyorgy@0 1886 return;
gyorgy@0 1887
gyorgy@0 1888 // native events
gyorgy@0 1889 if (mejs.MediaFeatures.hasNativeFullScreen) {
gyorgy@0 1890 player.container.bind('webkitfullscreenchange', function(e) {
gyorgy@0 1891
gyorgy@0 1892 if (document.webkitIsFullScreen) {
gyorgy@0 1893 // reset the controls once we are fully in full screen
gyorgy@0 1894 player.setControlsSize();
gyorgy@0 1895 } else {
gyorgy@0 1896 // when a user presses ESC
gyorgy@0 1897 // make sure to put the player back into place
gyorgy@0 1898 player.exitFullScreen();
gyorgy@0 1899 }
gyorgy@0 1900 });
gyorgy@0 1901 }
gyorgy@0 1902
gyorgy@0 1903 var
gyorgy@0 1904 normalHeight = 0,
gyorgy@0 1905 normalWidth = 0,
gyorgy@0 1906 container = player.container,
gyorgy@0 1907 docElement = document.documentElement,
gyorgy@0 1908 docStyleOverflow,
gyorgy@0 1909 parentWindow = window.parent,
gyorgy@0 1910 parentiframes,
gyorgy@0 1911 iframe,
gyorgy@0 1912 fullscreenBtn =
gyorgy@0 1913 $('<div class="mejs-button mejs-fullscreen-button"><button type="button"></button></div>')
gyorgy@0 1914 .appendTo(controls)
gyorgy@0 1915 .click(function() {
gyorgy@0 1916 var isFullScreen = (mejs.MediaFeatures.hasNativeFullScreen) ?
gyorgy@0 1917 document.webkitIsFullScreen :
gyorgy@0 1918 player.isFullScreen;
gyorgy@0 1919
gyorgy@0 1920 if (isFullScreen) {
gyorgy@0 1921 player.exitFullScreen();
gyorgy@0 1922 } else {
gyorgy@0 1923 player.enterFullScreen();
gyorgy@0 1924 }
gyorgy@0 1925 });
gyorgy@0 1926
gyorgy@0 1927 player.enterFullScreen = function() {
gyorgy@0 1928
gyorgy@0 1929 // firefox can't adjust plugin sizes without resetting :(
gyorgy@0 1930 if (player.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || player.options.forcePluginFullScreen)) {
gyorgy@0 1931 media.setFullscreen(true);
gyorgy@0 1932 //player.isFullScreen = true;
gyorgy@0 1933 return;
gyorgy@0 1934 }
gyorgy@0 1935
gyorgy@0 1936 // attempt to set fullscreen
gyorgy@0 1937 if (mejs.MediaFeatures.hasNativeFullScreen) {
gyorgy@0 1938 player.container[0].webkitRequestFullScreen();
gyorgy@0 1939 }
gyorgy@0 1940
gyorgy@0 1941 // store overflow
gyorgy@0 1942 docStyleOverflow = docElement.style.overflow;
gyorgy@0 1943 // set it to not show scroll bars so 100% will work
gyorgy@0 1944 docElement.style.overflow = 'hidden';
gyorgy@0 1945
gyorgy@0 1946 // store
gyorgy@0 1947 normalHeight = player.container.height();
gyorgy@0 1948 normalWidth = player.container.width();
gyorgy@0 1949
gyorgy@0 1950 // make full size
gyorgy@0 1951 container
gyorgy@0 1952 .addClass('mejs-container-fullscreen')
gyorgy@0 1953 .width('100%')
gyorgy@0 1954 .height('100%')
gyorgy@0 1955 .css('z-index', 1000);
gyorgy@0 1956 //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
gyorgy@0 1957
gyorgy@0 1958 if (player.pluginType === 'native') {
gyorgy@0 1959 player.$media
gyorgy@0 1960 .width('100%')
gyorgy@0 1961 .height('100%');
gyorgy@0 1962 } else {
gyorgy@0 1963 container.find('object embed')
gyorgy@0 1964 .width('100%')
gyorgy@0 1965 .height('100%');
gyorgy@0 1966 player.media.setVideoSize($(window).width(),$(window).height());
gyorgy@0 1967 }
gyorgy@0 1968
gyorgy@0 1969 layers.children('div')
gyorgy@0 1970 .width('100%')
gyorgy@0 1971 .height('100%');
gyorgy@0 1972
gyorgy@0 1973 fullscreenBtn
gyorgy@0 1974 .removeClass('mejs-fullscreen')
gyorgy@0 1975 .addClass('mejs-unfullscreen');
gyorgy@0 1976
gyorgy@0 1977 player.setControlsSize();
gyorgy@0 1978 player.isFullScreen = true;
gyorgy@0 1979 };
gyorgy@0 1980 player.exitFullScreen = function() {
gyorgy@0 1981
gyorgy@0 1982 // firefox can't adjust plugins
gyorgy@0 1983 if (player.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
gyorgy@0 1984 media.setFullscreen(false);
gyorgy@0 1985 //player.isFullScreen = false;
gyorgy@0 1986 return;
gyorgy@0 1987 }
gyorgy@0 1988
gyorgy@0 1989 // come outo of native fullscreen
gyorgy@0 1990 if (mejs.MediaFeatures.hasNativeFullScreen && document.webkitIsFullScreen) {
gyorgy@0 1991 document.webkitCancelFullScreen();
gyorgy@0 1992 }
gyorgy@0 1993
gyorgy@0 1994 // restore scroll bars to document
gyorgy@0 1995 docElement.style.overflow = docStyleOverflow;
gyorgy@0 1996
gyorgy@0 1997 container
gyorgy@0 1998 .removeClass('mejs-container-fullscreen')
gyorgy@0 1999 .width(normalWidth)
gyorgy@0 2000 .height(normalHeight)
gyorgy@0 2001 .css('z-index', 1);
gyorgy@0 2002 //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
gyorgy@0 2003
gyorgy@0 2004 if (player.pluginType === 'native') {
gyorgy@0 2005 player.$media
gyorgy@0 2006 .width(normalWidth)
gyorgy@0 2007 .height(normalHeight);
gyorgy@0 2008 } else {
gyorgy@0 2009 container.find('object embed')
gyorgy@0 2010 .width(normalWidth)
gyorgy@0 2011 .height(normalHeight);
gyorgy@0 2012
gyorgy@0 2013 player.media.setVideoSize(normalWidth, normalHeight);
gyorgy@0 2014 }
gyorgy@0 2015
gyorgy@0 2016 layers.children('div')
gyorgy@0 2017 .width(normalWidth)
gyorgy@0 2018 .height(normalHeight);
gyorgy@0 2019
gyorgy@0 2020 fullscreenBtn
gyorgy@0 2021 .removeClass('mejs-unfullscreen')
gyorgy@0 2022 .addClass('mejs-fullscreen');
gyorgy@0 2023
gyorgy@0 2024 player.setControlsSize();
gyorgy@0 2025 player.isFullScreen = false;
gyorgy@0 2026 };
gyorgy@0 2027
gyorgy@0 2028 $(window).bind('resize',function (e) {
gyorgy@0 2029 player.setControlsSize();
gyorgy@0 2030 });
gyorgy@0 2031
gyorgy@0 2032 $(document).bind('keydown',function (e) {
gyorgy@0 2033 if (player.isFullScreen && e.keyCode == 27) {
gyorgy@0 2034 player.exitFullScreen();
gyorgy@0 2035 }
gyorgy@0 2036 });
gyorgy@0 2037
gyorgy@0 2038 }
gyorgy@0 2039
gyorgy@0 2040 })(mejs.$);
gyorgy@0 2041 (function($) {
gyorgy@0 2042
gyorgy@0 2043 // add extra default options
gyorgy@0 2044 $.extend(mejs.MepDefaults, {
gyorgy@0 2045 // this will automatically turn on a <track>
gyorgy@0 2046 startLanguage: '',
gyorgy@0 2047 // a list of languages to auto-translate via Google
gyorgy@0 2048 translations: [],
gyorgy@0 2049 // a dropdownlist of automatic translations
gyorgy@0 2050 translationSelector: false,
gyorgy@0 2051 // key for tranlsations
gyorgy@0 2052 googleApiKey: ''
gyorgy@0 2053 });
gyorgy@0 2054
gyorgy@0 2055 $.extend(MediaElementPlayer.prototype, {
gyorgy@0 2056
gyorgy@0 2057 buildtracks: function(player, controls, layers, media) {
gyorgy@0 2058 if (!player.isVideo)
gyorgy@0 2059 return;
gyorgy@0 2060
gyorgy@0 2061 if (player.tracks.length == 0)
gyorgy@0 2062 return;
gyorgy@0 2063
gyorgy@0 2064 var i, options = '';
gyorgy@0 2065
gyorgy@0 2066 player.chapters =
gyorgy@0 2067 $('<div class="mejs-chapters mejs-layer"></div>')
gyorgy@0 2068 .prependTo(layers).hide();
gyorgy@0 2069 player.captions =
gyorgy@0 2070 $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position"><span class="mejs-captions-text"></span></div></div>')
gyorgy@0 2071 .prependTo(layers).hide();
gyorgy@0 2072 player.captionsText = player.captions.find('.mejs-captions-text');
gyorgy@0 2073 player.captionsButton =
gyorgy@0 2074 $('<div class="mejs-button mejs-captions-button">'+
gyorgy@0 2075 '<button type="button" ></button>'+
gyorgy@0 2076 '<div class="mejs-captions-selector">'+
gyorgy@0 2077 '<ul>'+
gyorgy@0 2078 '<li>'+
gyorgy@0 2079 '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
gyorgy@0 2080 '<label for="' + player.id + '_captions_none">None</label>'+
gyorgy@0 2081 '</li>' +
gyorgy@0 2082 '</ul>'+
gyorgy@0 2083 '</div>'+
gyorgy@0 2084 '</button>')
gyorgy@0 2085 .appendTo(controls)
gyorgy@0 2086 // handle clicks to the language radio buttons
gyorgy@0 2087 .delegate('input[type=radio]','click',function() {
gyorgy@0 2088 lang = this.value;
gyorgy@0 2089
gyorgy@0 2090 if (lang == 'none') {
gyorgy@0 2091 player.selectedTrack = null;
gyorgy@0 2092 } else {
gyorgy@0 2093 for (i=0; i<player.tracks.length; i++) {
gyorgy@0 2094 if (player.tracks[i].srclang == lang) {
gyorgy@0 2095 player.selectedTrack = player.tracks[i];
gyorgy@0 2096 player.captions.attr('lang', player.selectedTrack.srclang);
gyorgy@0 2097 player.displayCaptions();
gyorgy@0 2098 break;
gyorgy@0 2099 }
gyorgy@0 2100 }
gyorgy@0 2101 }
gyorgy@0 2102 });
gyorgy@0 2103 //.bind('mouseenter', function() {
gyorgy@0 2104 // player.captionsButton.find('.mejs-captions-selector').css('visibility','visible')
gyorgy@0 2105 //});
gyorgy@0 2106
gyorgy@0 2107 if (!player.options.alwaysShowControls) {
gyorgy@0 2108 // move with controls
gyorgy@0 2109 player.container
gyorgy@0 2110 .bind('mouseenter', function () {
gyorgy@0 2111 // push captions above controls
gyorgy@0 2112 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
gyorgy@0 2113
gyorgy@0 2114 })
gyorgy@0 2115 .bind('mouseleave', function () {
gyorgy@0 2116 if (!media.paused) {
gyorgy@0 2117 // move back to normal place
gyorgy@0 2118 player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
gyorgy@0 2119 }
gyorgy@0 2120 });
gyorgy@0 2121 } else {
gyorgy@0 2122 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
gyorgy@0 2123 }
gyorgy@0 2124
gyorgy@0 2125 player.trackToLoad = -1;
gyorgy@0 2126 player.selectedTrack = null;
gyorgy@0 2127 player.isLoadingTrack = false;
gyorgy@0 2128
gyorgy@0 2129 // add user-defined translations
gyorgy@0 2130 if (player.tracks.length > 0 && player.options.translations.length > 0) {
gyorgy@0 2131 for (i=0; i<player.options.translations.length; i++) {
gyorgy@0 2132 player.tracks.push({
gyorgy@0 2133 srclang: player.options.translations[i].toLowerCase(),
gyorgy@0 2134 src: null,
gyorgy@0 2135 kind: 'subtitles',
gyorgy@0 2136 entries: [],
gyorgy@0 2137 isLoaded: false,
gyorgy@0 2138 isTranslation: true
gyorgy@0 2139 });
gyorgy@0 2140 }
gyorgy@0 2141 }
gyorgy@0 2142
gyorgy@0 2143 // add to list
gyorgy@0 2144 for (i=0; i<player.tracks.length; i++) {
gyorgy@0 2145 if (player.tracks[i].kind == 'subtitles') {
gyorgy@0 2146 player.addTrackButton(player.tracks[i].srclang, player.tracks[i].isTranslation);
gyorgy@0 2147 }
gyorgy@0 2148 }
gyorgy@0 2149
gyorgy@0 2150 player.loadNextTrack();
gyorgy@0 2151
gyorgy@0 2152
gyorgy@0 2153 media.addEventListener('timeupdate',function(e) {
gyorgy@0 2154 player.displayCaptions();
gyorgy@0 2155 }, false);
gyorgy@0 2156
gyorgy@0 2157 media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 2158 player.displayChapters();
gyorgy@0 2159 }, false);
gyorgy@0 2160
gyorgy@0 2161 player.container.hover(
gyorgy@0 2162 function () {
gyorgy@0 2163 // chapters
gyorgy@0 2164 player.chapters.css('visibility','visible');
gyorgy@0 2165 player.chapters.fadeIn(200);
gyorgy@0 2166 },
gyorgy@0 2167 function () {
gyorgy@0 2168 if (!media.paused) {
gyorgy@0 2169 player.chapters.fadeOut(200, function() {
gyorgy@0 2170 $(this).css('visibility','hidden');
gyorgy@0 2171 $(this).css('display','block');
gyorgy@0 2172 });
gyorgy@0 2173 }
gyorgy@0 2174 });
gyorgy@0 2175
gyorgy@0 2176 // check for autoplay
gyorgy@0 2177 if (player.node.getAttribute('autoplay') !== null) {
gyorgy@0 2178 player.chapters.css('visibility','hidden');
gyorgy@0 2179 }
gyorgy@0 2180
gyorgy@0 2181 // auto selector
gyorgy@0 2182 if (player.options.translationSelector) {
gyorgy@0 2183 for (i in mejs.language.codes) {
gyorgy@0 2184 options += '<option value="' + i + '">' + mejs.language.codes[i] + '</option>';
gyorgy@0 2185 }
gyorgy@0 2186 player.container.find('.mejs-captions-selector ul').before($(
gyorgy@0 2187 '<select class="mejs-captions-translations">' +
gyorgy@0 2188 '<option value="">--Add Translation--</option>' +
gyorgy@0 2189 options +
gyorgy@0 2190 '</select>'
gyorgy@0 2191 ));
gyorgy@0 2192 // add clicks
gyorgy@0 2193 player.container.find('.mejs-captions-translations').change(function() {
gyorgy@0 2194 var
gyorgy@0 2195 option = $(this);
gyorgy@0 2196 lang = option.val();
gyorgy@0 2197 // add this language to the tracks list
gyorgy@0 2198 if (lang != '') {
gyorgy@0 2199 player.tracks.push({
gyorgy@0 2200 srclang: lang,
gyorgy@0 2201 src: null,
gyorgy@0 2202 entries: [],
gyorgy@0 2203 isLoaded: false,
gyorgy@0 2204 isTranslation: true
gyorgy@0 2205 });
gyorgy@0 2206
gyorgy@0 2207 if (!player.isLoadingTrack) {
gyorgy@0 2208 player.trackToLoad--;
gyorgy@0 2209 player.addTrackButton(lang,true);
gyorgy@0 2210 player.options.startLanguage = lang;
gyorgy@0 2211 player.loadNextTrack();
gyorgy@0 2212 }
gyorgy@0 2213 }
gyorgy@0 2214 });
gyorgy@0 2215 }
gyorgy@0 2216
gyorgy@0 2217 },
gyorgy@0 2218
gyorgy@0 2219 loadNextTrack: function() {
gyorgy@0 2220 var t = this;
gyorgy@0 2221
gyorgy@0 2222 t.trackToLoad++;
gyorgy@0 2223 if (t.trackToLoad < t.tracks.length) {
gyorgy@0 2224 t.isLoadingTrack = true;
gyorgy@0 2225 t.loadTrack(t.trackToLoad);
gyorgy@0 2226 } else {
gyorgy@0 2227 // add done?
gyorgy@0 2228 t.isLoadingTrack = false;
gyorgy@0 2229 }
gyorgy@0 2230 },
gyorgy@0 2231
gyorgy@0 2232 loadTrack: function(index){
gyorgy@0 2233 var
gyorgy@0 2234 t = this,
gyorgy@0 2235 track = t.tracks[index],
gyorgy@0 2236 after = function() {
gyorgy@0 2237
gyorgy@0 2238 track.isLoaded = true;
gyorgy@0 2239
gyorgy@0 2240 // create button
gyorgy@0 2241 //t.addTrackButton(track.srclang);
gyorgy@0 2242 t.enableTrackButton(track.srclang);
gyorgy@0 2243
gyorgy@0 2244 t.loadNextTrack();
gyorgy@0 2245
gyorgy@0 2246 };
gyorgy@0 2247
gyorgy@0 2248 if (track.isTranslation) {
gyorgy@0 2249
gyorgy@0 2250 // translate the first track
gyorgy@0 2251 mejs.TrackFormatParser.translateTrackText(t.tracks[0].entries, t.tracks[0].srclang, track.srclang, t.options.googleApiKey, function(newOne) {
gyorgy@0 2252
gyorgy@0 2253 // store the new translation
gyorgy@0 2254 track.entries = newOne;
gyorgy@0 2255
gyorgy@0 2256 after();
gyorgy@0 2257 });
gyorgy@0 2258
gyorgy@0 2259 } else {
gyorgy@0 2260 $.ajax({
gyorgy@0 2261 url: track.src,
gyorgy@0 2262 success: function(d) {
gyorgy@0 2263
gyorgy@0 2264 // parse the loaded file
gyorgy@0 2265 track.entries = mejs.TrackFormatParser.parse(d);
gyorgy@0 2266 after();
gyorgy@0 2267
gyorgy@0 2268 if (track.kind == 'chapters' && t.media.duration > 0) {
gyorgy@0 2269 t.drawChapters(track);
gyorgy@0 2270 }
gyorgy@0 2271 },
gyorgy@0 2272 error: function() {
gyorgy@0 2273 t.loadNextTrack();
gyorgy@0 2274 }
gyorgy@0 2275 });
gyorgy@0 2276 }
gyorgy@0 2277 },
gyorgy@0 2278
gyorgy@0 2279 enableTrackButton: function(lang) {
gyorgy@0 2280 var t = this;
gyorgy@0 2281
gyorgy@0 2282 t.captionsButton
gyorgy@0 2283 .find('input[value=' + lang + ']')
gyorgy@0 2284 .prop('disabled',false)
gyorgy@0 2285 .siblings('label')
gyorgy@0 2286 .html( mejs.language.codes[lang] || lang );
gyorgy@0 2287
gyorgy@0 2288 // auto select
gyorgy@0 2289 if (t.options.startLanguage == lang) {
gyorgy@0 2290 $('#' + t.id + '_captions_' + lang).click();
gyorgy@0 2291 }
gyorgy@0 2292
gyorgy@0 2293 t.adjustLanguageBox();
gyorgy@0 2294 },
gyorgy@0 2295
gyorgy@0 2296 addTrackButton: function(lang, isTranslation) {
gyorgy@0 2297 var t = this,
gyorgy@0 2298 l = mejs.language.codes[lang] || lang;
gyorgy@0 2299
gyorgy@0 2300 t.captionsButton.find('ul').append(
gyorgy@0 2301 $('<li>'+
gyorgy@0 2302 '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
gyorgy@0 2303 '<label for="' + t.id + '_captions_' + lang + '">' + l + ((isTranslation) ? ' (translating)' : ' (loading)') + '</label>'+
gyorgy@0 2304 '</li>')
gyorgy@0 2305 );
gyorgy@0 2306
gyorgy@0 2307 t.adjustLanguageBox();
gyorgy@0 2308
gyorgy@0 2309 // remove this from the dropdownlist (if it exists)
gyorgy@0 2310 t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
gyorgy@0 2311 },
gyorgy@0 2312
gyorgy@0 2313 adjustLanguageBox:function() {
gyorgy@0 2314 var t = this;
gyorgy@0 2315 // adjust the size of the outer box
gyorgy@0 2316 t.captionsButton.find('.mejs-captions-selector').height(
gyorgy@0 2317 t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
gyorgy@0 2318 t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
gyorgy@0 2319 );
gyorgy@0 2320 },
gyorgy@0 2321
gyorgy@0 2322 displayCaptions: function() {
gyorgy@0 2323
gyorgy@0 2324 if (typeof this.tracks == 'undefined')
gyorgy@0 2325 return;
gyorgy@0 2326
gyorgy@0 2327 var
gyorgy@0 2328 t = this,
gyorgy@0 2329 i,
gyorgy@0 2330 track = t.selectedTrack;
gyorgy@0 2331
gyorgy@0 2332 if (track != null && track.isLoaded) {
gyorgy@0 2333 for (i=0; i<track.entries.times.length; i++) {
gyorgy@0 2334 if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
gyorgy@0 2335 t.captionsText.html(track.entries.text[i]);
gyorgy@0 2336 t.captions.show();
gyorgy@0 2337 return; // exit out if one is visible;
gyorgy@0 2338 }
gyorgy@0 2339 }
gyorgy@0 2340 t.captions.hide();
gyorgy@0 2341 } else {
gyorgy@0 2342 t.captions.hide();
gyorgy@0 2343 }
gyorgy@0 2344 },
gyorgy@0 2345
gyorgy@0 2346 displayChapters: function() {
gyorgy@0 2347 var
gyorgy@0 2348 t = this,
gyorgy@0 2349 i;
gyorgy@0 2350
gyorgy@0 2351 for (i=0; i<t.tracks.length; i++) {
gyorgy@0 2352 if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
gyorgy@0 2353 t.drawChapters(t.tracks[i]);
gyorgy@0 2354 break;
gyorgy@0 2355 }
gyorgy@0 2356 }
gyorgy@0 2357 },
gyorgy@0 2358
gyorgy@0 2359 drawChapters: function(chapters) {
gyorgy@0 2360 var
gyorgy@0 2361 t = this,
gyorgy@0 2362 i,
gyorgy@0 2363 dur,
gyorgy@0 2364 //width,
gyorgy@0 2365 //left,
gyorgy@0 2366 percent = 0,
gyorgy@0 2367 usedPercent = 0;
gyorgy@0 2368
gyorgy@0 2369 t.chapters.empty();
gyorgy@0 2370
gyorgy@0 2371 for (i=0; i<chapters.entries.times.length; i++) {
gyorgy@0 2372 dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
gyorgy@0 2373 percent = Math.floor(dur / t.media.duration * 100);
gyorgy@0 2374 if (percent + usedPercent > 100 || // too large
gyorgy@0 2375 i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
gyorgy@0 2376 {
gyorgy@0 2377 percent = 100 - usedPercent;
gyorgy@0 2378 }
gyorgy@0 2379 //width = Math.floor(t.width * dur / t.media.duration);
gyorgy@0 2380 //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
gyorgy@0 2381 //if (left + width > t.width) {
gyorgy@0 2382 // width = t.width - left;
gyorgy@0 2383 //}
gyorgy@0 2384
gyorgy@0 2385 t.chapters.append( $(
gyorgy@0 2386 '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
gyorgy@0 2387 '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
gyorgy@0 2388 '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
gyorgy@0 2389 '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
gyorgy@0 2390 '</div>' +
gyorgy@0 2391 '</div>'));
gyorgy@0 2392 usedPercent += percent;
gyorgy@0 2393 }
gyorgy@0 2394
gyorgy@0 2395 t.chapters.find('div.mejs-chapter').click(function() {
gyorgy@0 2396 t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
gyorgy@0 2397 if (t.media.paused) {
gyorgy@0 2398 t.media.play();
gyorgy@0 2399 }
gyorgy@0 2400 });
gyorgy@0 2401
gyorgy@0 2402 t.chapters.show();
gyorgy@0 2403 }
gyorgy@0 2404 });
gyorgy@0 2405
gyorgy@0 2406
gyorgy@0 2407
gyorgy@0 2408 mejs.language = {
gyorgy@0 2409 codes: {
gyorgy@0 2410 af:'Afrikaans',
gyorgy@0 2411 sq:'Albanian',
gyorgy@0 2412 ar:'Arabic',
gyorgy@0 2413 be:'Belarusian',
gyorgy@0 2414 bg:'Bulgarian',
gyorgy@0 2415 ca:'Catalan',
gyorgy@0 2416 zh:'Chinese',
gyorgy@0 2417 'zh-cn':'Chinese Simplified',
gyorgy@0 2418 'zh-tw':'Chinese Traditional',
gyorgy@0 2419 hr:'Croatian',
gyorgy@0 2420 cs:'Czech',
gyorgy@0 2421 da:'Danish',
gyorgy@0 2422 nl:'Dutch',
gyorgy@0 2423 en:'English',
gyorgy@0 2424 et:'Estonian',
gyorgy@0 2425 tl:'Filipino',
gyorgy@0 2426 fi:'Finnish',
gyorgy@0 2427 fr:'French',
gyorgy@0 2428 gl:'Galician',
gyorgy@0 2429 de:'German',
gyorgy@0 2430 el:'Greek',
gyorgy@0 2431 ht:'Haitian Creole',
gyorgy@0 2432 iw:'Hebrew',
gyorgy@0 2433 hi:'Hindi',
gyorgy@0 2434 hu:'Hungarian',
gyorgy@0 2435 is:'Icelandic',
gyorgy@0 2436 id:'Indonesian',
gyorgy@0 2437 ga:'Irish',
gyorgy@0 2438 it:'Italian',
gyorgy@0 2439 ja:'Japanese',
gyorgy@0 2440 ko:'Korean',
gyorgy@0 2441 lv:'Latvian',
gyorgy@0 2442 lt:'Lithuanian',
gyorgy@0 2443 mk:'Macedonian',
gyorgy@0 2444 ms:'Malay',
gyorgy@0 2445 mt:'Maltese',
gyorgy@0 2446 no:'Norwegian',
gyorgy@0 2447 fa:'Persian',
gyorgy@0 2448 pl:'Polish',
gyorgy@0 2449 pt:'Portuguese',
gyorgy@0 2450 //'pt-pt':'Portuguese (Portugal)',
gyorgy@0 2451 ro:'Romanian',
gyorgy@0 2452 ru:'Russian',
gyorgy@0 2453 sr:'Serbian',
gyorgy@0 2454 sk:'Slovak',
gyorgy@0 2455 sl:'Slovenian',
gyorgy@0 2456 es:'Spanish',
gyorgy@0 2457 sw:'Swahili',
gyorgy@0 2458 sv:'Swedish',
gyorgy@0 2459 tl:'Tagalog',
gyorgy@0 2460 th:'Thai',
gyorgy@0 2461 tr:'Turkish',
gyorgy@0 2462 uk:'Ukrainian',
gyorgy@0 2463 vi:'Vietnamese',
gyorgy@0 2464 cy:'Welsh',
gyorgy@0 2465 yi:'Yiddish'
gyorgy@0 2466 }
gyorgy@0 2467 };
gyorgy@0 2468
gyorgy@0 2469 /*
gyorgy@0 2470 Parses WebVVT format which should be formatted as
gyorgy@0 2471 ================================
gyorgy@0 2472 WEBVTT
gyorgy@0 2473
gyorgy@0 2474 1
gyorgy@0 2475 00:00:01,1 --> 00:00:05,000
gyorgy@0 2476 A line of text
gyorgy@0 2477
gyorgy@0 2478 2
gyorgy@0 2479 00:01:15,1 --> 00:02:05,000
gyorgy@0 2480 A second line of text
gyorgy@0 2481
gyorgy@0 2482 ===============================
gyorgy@0 2483
gyorgy@0 2484 Adapted from: http://www.delphiki.com/html5/playr
gyorgy@0 2485 */
gyorgy@0 2486 mejs.TrackFormatParser = {
gyorgy@0 2487 pattern_identifier: /^[0-9]+$/,
gyorgy@0 2488 pattern_timecode: /^([0-9]{2}:[0-9]{2}:[0-9]{2}(,[0-9]{1,3})?) --\> ([0-9]{2}:[0-9]{2}:[0-9]{2}(,[0-9]{3})?)(.*)$/,
gyorgy@0 2489
gyorgy@0 2490 split2: function (text, regex) {
gyorgy@0 2491 // normal version for compliant browsers
gyorgy@0 2492 // see below for IE fix
gyorgy@0 2493 return text.split(regex);
gyorgy@0 2494 },
gyorgy@0 2495 parse: function(trackText) {
gyorgy@0 2496 var
gyorgy@0 2497 i = 0,
gyorgy@0 2498 lines = this.split2(trackText, /\r?\n/),
gyorgy@0 2499 entries = {text:[], times:[]},
gyorgy@0 2500 timecode,
gyorgy@0 2501 text;
gyorgy@0 2502
gyorgy@0 2503 for(; i<lines.length; i++) {
gyorgy@0 2504 // check for the line number
gyorgy@0 2505 if (this.pattern_identifier.exec(lines[i])){
gyorgy@0 2506 // skip to the next line where the start --> end time code should be
gyorgy@0 2507 i++;
gyorgy@0 2508 timecode = this.pattern_timecode.exec(lines[i]);
gyorgy@0 2509 if (timecode && i<lines.length){
gyorgy@0 2510 i++;
gyorgy@0 2511 // grab all the (possibly multi-line) text that follows
gyorgy@0 2512 text = lines[i];
gyorgy@0 2513 i++;
gyorgy@0 2514 while(lines[i] !== '' && i<lines.length){
gyorgy@0 2515 text = text + '\n' + lines[i];
gyorgy@0 2516 i++;
gyorgy@0 2517 }
gyorgy@0 2518
gyorgy@0 2519 // Text is in a different array so I can use .join
gyorgy@0 2520 entries.text.push(text);
gyorgy@0 2521 entries.times.push(
gyorgy@0 2522 {
gyorgy@0 2523 start: mejs.Utility.timeCodeToSeconds(timecode[1]),
gyorgy@0 2524 stop: mejs.Utility.timeCodeToSeconds(timecode[3]),
gyorgy@0 2525 settings: timecode[5]
gyorgy@0 2526 });
gyorgy@0 2527 }
gyorgy@0 2528 }
gyorgy@0 2529 }
gyorgy@0 2530
gyorgy@0 2531 return entries;
gyorgy@0 2532 },
gyorgy@0 2533
gyorgy@0 2534 translateTrackText: function(trackData, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 2535
gyorgy@0 2536 var
gyorgy@0 2537 entries = {text:[], times:[]},
gyorgy@0 2538 lines,
gyorgy@0 2539 i
gyorgy@0 2540
gyorgy@0 2541 this.translateText( trackData.text.join(' <a></a>'), fromLang, toLang, googleApiKey, function(result) {
gyorgy@0 2542 // split on separators
gyorgy@0 2543 lines = result.split('<a></a>');
gyorgy@0 2544
gyorgy@0 2545 // create new entries
gyorgy@0 2546 for (i=0;i<trackData.text.length; i++) {
gyorgy@0 2547 // add translated line
gyorgy@0 2548 entries.text[i] = lines[i];
gyorgy@0 2549 // copy existing times
gyorgy@0 2550 entries.times[i] = {
gyorgy@0 2551 start: trackData.times[i].start,
gyorgy@0 2552 stop: trackData.times[i].stop,
gyorgy@0 2553 settings: trackData.times[i].settings
gyorgy@0 2554 };
gyorgy@0 2555 }
gyorgy@0 2556
gyorgy@0 2557 callback(entries);
gyorgy@0 2558 });
gyorgy@0 2559 },
gyorgy@0 2560
gyorgy@0 2561 translateText: function(text, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 2562
gyorgy@0 2563 var
gyorgy@0 2564 separatorIndex,
gyorgy@0 2565 chunks = [],
gyorgy@0 2566 chunk,
gyorgy@0 2567 maxlength = 1000,
gyorgy@0 2568 result = '',
gyorgy@0 2569 nextChunk= function() {
gyorgy@0 2570 if (chunks.length > 0) {
gyorgy@0 2571 chunk = chunks.shift();
gyorgy@0 2572 mejs.TrackFormatParser.translateChunk(chunk, fromLang, toLang, googleApiKey, function(r) {
gyorgy@0 2573 if (r != 'undefined') {
gyorgy@0 2574 result += r;
gyorgy@0 2575 }
gyorgy@0 2576 nextChunk();
gyorgy@0 2577 });
gyorgy@0 2578 } else {
gyorgy@0 2579 callback(result);
gyorgy@0 2580 }
gyorgy@0 2581 };
gyorgy@0 2582
gyorgy@0 2583 // split into chunks
gyorgy@0 2584 while (text.length > 0) {
gyorgy@0 2585 if (text.length > maxlength) {
gyorgy@0 2586 separatorIndex = text.lastIndexOf('.', maxlength);
gyorgy@0 2587 chunks.push(text.substring(0, separatorIndex));
gyorgy@0 2588 text = text.substring(separatorIndex+1);
gyorgy@0 2589 } else {
gyorgy@0 2590 chunks.push(text);
gyorgy@0 2591 text = '';
gyorgy@0 2592 }
gyorgy@0 2593 }
gyorgy@0 2594
gyorgy@0 2595 // start handling the chunks
gyorgy@0 2596 nextChunk();
gyorgy@0 2597 },
gyorgy@0 2598 translateChunk: function(text, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 2599
gyorgy@0 2600 var data = {
gyorgy@0 2601 q: text,
gyorgy@0 2602 langpair: fromLang + '|' + toLang,
gyorgy@0 2603 v: '1.0'
gyorgy@0 2604 };
gyorgy@0 2605 if (googleApiKey !== '' && googleApiKey !== null) {
gyorgy@0 2606 data.key = googleApiKey;
gyorgy@0 2607 }
gyorgy@0 2608
gyorgy@0 2609 $.ajax({
gyorgy@0 2610 url: 'https://ajax.googleapis.com/ajax/services/language/translate', // 'https://www.google.com/uds/Gtranslate', //'https://ajax.googleapis.com/ajax/services/language/translate', //
gyorgy@0 2611 data: data,
gyorgy@0 2612 type: 'GET',
gyorgy@0 2613 dataType: 'jsonp',
gyorgy@0 2614 success: function(d) {
gyorgy@0 2615 callback(d.responseData.translatedText);
gyorgy@0 2616 },
gyorgy@0 2617 error: function(e) {
gyorgy@0 2618 callback(null);
gyorgy@0 2619 }
gyorgy@0 2620 });
gyorgy@0 2621 }
gyorgy@0 2622 };
gyorgy@0 2623 // test for browsers with bad String.split method.
gyorgy@0 2624 if ('x\n\ny'.split(/\n/gi).length != 3) {
gyorgy@0 2625 // add super slow IE8 and below version
gyorgy@0 2626 mejs.TrackFormatParser.split2 = function(text, regex) {
gyorgy@0 2627 var
gyorgy@0 2628 parts = [],
gyorgy@0 2629 chunk = '',
gyorgy@0 2630 i;
gyorgy@0 2631
gyorgy@0 2632 for (i=0; i<text.length; i++) {
gyorgy@0 2633 chunk += text.substring(i,i+1);
gyorgy@0 2634 if (regex.test(chunk)) {
gyorgy@0 2635 parts.push(chunk.replace(regex, ''));
gyorgy@0 2636 chunk = '';
gyorgy@0 2637 }
gyorgy@0 2638 }
gyorgy@0 2639 parts.push(chunk);
gyorgy@0 2640 return parts;
gyorgy@0 2641 }
gyorgy@0 2642 }
gyorgy@0 2643
gyorgy@0 2644 })(mejs.$);
gyorgy@0 2645
gyorgy@0 2646