annotate johndyer-mediaelement-13fa20a/build/mediaelementplayer.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 * MediaElementPlayer
gyorgy@0 3 * http://mediaelementjs.com/
gyorgy@0 4 *
gyorgy@0 5 * Creates a controller bar for HTML5 <video> add <audio> tags
gyorgy@0 6 * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
gyorgy@0 7 *
gyorgy@0 8 * Copyright 2010-2011, John Dyer (http://j.hn/)
gyorgy@0 9 * Dual licensed under the MIT or GPL Version 2 licenses.
gyorgy@0 10 *
gyorgy@0 11 */
gyorgy@0 12 if (typeof jQuery != 'undefined') {
gyorgy@0 13 mejs.$ = jQuery;
gyorgy@0 14 } else if (typeof ender != 'undefined') {
gyorgy@0 15 mejs.$ = ender;
gyorgy@0 16 }
gyorgy@0 17 (function ($) {
gyorgy@0 18
gyorgy@0 19 // default player values
gyorgy@0 20 mejs.MepDefaults = {
gyorgy@0 21 // url to poster (to fix iOS 3.x)
gyorgy@0 22 poster: '',
gyorgy@0 23 // default if the <video width> is not specified
gyorgy@0 24 defaultVideoWidth: 480,
gyorgy@0 25 // default if the <video height> is not specified
gyorgy@0 26 defaultVideoHeight: 270,
gyorgy@0 27 // if set, overrides <video width>
gyorgy@0 28 videoWidth: -1,
gyorgy@0 29 // if set, overrides <video height>
gyorgy@0 30 videoHeight: -1,
gyorgy@0 31 // width of audio player
gyorgy@0 32 audioWidth: 400,
gyorgy@0 33 // height of audio player
gyorgy@0 34 audioHeight: 30,
gyorgy@0 35 // initial volume when the player starts (overrided by user cookie)
gyorgy@0 36 startVolume: 0.8,
gyorgy@0 37 // useful for <audio> player loops
gyorgy@0 38 loop: false,
gyorgy@0 39 // resize to media dimensions
gyorgy@0 40 enableAutosize: true,
gyorgy@0 41 // forces the hour marker (##:00:00)
gyorgy@0 42 alwaysShowHours: false,
gyorgy@0 43 // Hide controls when playing and mouse is not over the video
gyorgy@0 44 alwaysShowControls: false,
gyorgy@0 45 // force iPad's native controls
gyorgy@0 46 iPadUseNativeControls: true,
gyorgy@0 47 // features to show
gyorgy@0 48 features: ['playpause','current','progress','duration','tracks','volume','fullscreen']
gyorgy@0 49 };
gyorgy@0 50
gyorgy@0 51 mejs.mepIndex = 0;
gyorgy@0 52
gyorgy@0 53 // wraps a MediaElement object in player controls
gyorgy@0 54 mejs.MediaElementPlayer = function(node, o) {
gyorgy@0 55 // enforce object, even without "new" (via John Resig)
gyorgy@0 56 if ( !(this instanceof mejs.MediaElementPlayer) ) {
gyorgy@0 57 return new mejs.MediaElementPlayer(node, o);
gyorgy@0 58 }
gyorgy@0 59
gyorgy@0 60 var
gyorgy@0 61 t = this,
gyorgy@0 62 mf = mejs.MediaFeatures;
gyorgy@0 63
gyorgy@0 64 // create options
gyorgy@0 65 t.options = $.extend({},mejs.MepDefaults,o);
gyorgy@0 66
gyorgy@0 67 // these will be reset after the MediaElement.success fires
gyorgy@0 68 t.$media = t.$node = $(node);
gyorgy@0 69 t.node = t.media = t.$media[0];
gyorgy@0 70
gyorgy@0 71 // check for existing player
gyorgy@0 72 if (typeof t.node.player != 'undefined') {
gyorgy@0 73 return t.node.player;
gyorgy@0 74 } else {
gyorgy@0 75 // attach player to DOM node for reference
gyorgy@0 76 t.node.player = t;
gyorgy@0 77 }
gyorgy@0 78
gyorgy@0 79 t.isVideo = (t.media.tagName.toLowerCase() === 'video');
gyorgy@0 80
gyorgy@0 81 /* FUTURE WORK = create player without existing <video> or <audio> node
gyorgy@0 82
gyorgy@0 83 // if not a video or audio tag, then we'll dynamically create it
gyorgy@0 84 if (tagName == 'video' || tagName == 'audio') {
gyorgy@0 85 t.$media = $($node);
gyorgy@0 86 } else if (o.tagName !== '' && o.src !== '') {
gyorgy@0 87 // create a new node
gyorgy@0 88 if (o.mode == 'auto' || o.mode == 'native') {
gyorgy@0 89
gyorgy@0 90 $media = $(o.tagName);
gyorgy@0 91 if (typeof o.src == 'string') {
gyorgy@0 92 $media.attr('src',o.src);
gyorgy@0 93 } else if (typeof o.src == 'object') {
gyorgy@0 94 // create source nodes
gyorgy@0 95 for (var x in o.src) {
gyorgy@0 96 $media.append($('<source src="' + o.src[x].src + '" type="' + o.src[x].type + '" />'));
gyorgy@0 97 }
gyorgy@0 98 }
gyorgy@0 99 if (o.type != '') {
gyorgy@0 100 $media.attr('type',o.type);
gyorgy@0 101 }
gyorgy@0 102 if (o.poster != '') {
gyorgy@0 103 $media.attr('poster',o.poster);
gyorgy@0 104 }
gyorgy@0 105 if (o.videoWidth > 0) {
gyorgy@0 106 $media.attr('width',o.videoWidth);
gyorgy@0 107 }
gyorgy@0 108 if (o.videoHeight > 0) {
gyorgy@0 109 $media.attr('height',o.videoHeight);
gyorgy@0 110 }
gyorgy@0 111
gyorgy@0 112 $node.clear();
gyorgy@0 113 $node.append($media);
gyorgy@0 114 t.$media = $media;
gyorgy@0 115 } else if (o.mode == 'shim') {
gyorgy@0 116 $media = $();
gyorgy@0 117 // doesn't want a media node
gyorgy@0 118 // let MediaElement object handle this
gyorgy@0 119 }
gyorgy@0 120 } else {
gyorgy@0 121 // fail?
gyorgy@0 122 return;
gyorgy@0 123 }
gyorgy@0 124 */
gyorgy@0 125
gyorgy@0 126 t.init();
gyorgy@0 127
gyorgy@0 128 return t;
gyorgy@0 129 };
gyorgy@0 130
gyorgy@0 131 // actual player
gyorgy@0 132 mejs.MediaElementPlayer.prototype = {
gyorgy@0 133 init: function() {
gyorgy@0 134
gyorgy@0 135 var
gyorgy@0 136 t = this,
gyorgy@0 137 mf = mejs.MediaFeatures,
gyorgy@0 138 // options for MediaElement (shim)
gyorgy@0 139 meOptions = $.extend(true, {}, t.options, {
gyorgy@0 140 success: function(media, domNode) { t.meReady(media, domNode); },
gyorgy@0 141 error: function(e) { t.handleError(e);}
gyorgy@0 142 });
gyorgy@0 143
gyorgy@0 144
gyorgy@0 145 // use native controls in iPad, iPhone, and Android
gyorgy@0 146 if ((mf.isiPad && t.options.iPadUseNativeControls) || mf.isiPhone) {
gyorgy@0 147 // add controls and stop
gyorgy@0 148 t.$media.attr('controls', 'controls');
gyorgy@0 149
gyorgy@0 150 // attempt to fix iOS 3 bug
gyorgy@0 151 t.$media.removeAttr('poster');
gyorgy@0 152
gyorgy@0 153 // override Apple's autoplay override for iPads
gyorgy@0 154 if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
gyorgy@0 155 t.media.load();
gyorgy@0 156 t.media.play();
gyorgy@0 157 }
gyorgy@0 158
gyorgy@0 159 } else if (mf.isAndroid && t.isVideo) {
gyorgy@0 160
gyorgy@0 161 // leave default player
gyorgy@0 162
gyorgy@0 163 } else {
gyorgy@0 164
gyorgy@0 165 // DESKTOP: use MediaElementPlayer controls
gyorgy@0 166
gyorgy@0 167 // remove native controls
gyorgy@0 168 t.$media.removeAttr('controls');
gyorgy@0 169
gyorgy@0 170 // unique ID
gyorgy@0 171 t.id = 'mep_' + mejs.mepIndex++;
gyorgy@0 172
gyorgy@0 173 // build container
gyorgy@0 174 t.container =
gyorgy@0 175 $('<div id="' + t.id + '" class="mejs-container">'+
gyorgy@0 176 '<div class="mejs-inner">'+
gyorgy@0 177 '<div class="mejs-mediaelement"></div>'+
gyorgy@0 178 '<div class="mejs-layers"></div>'+
gyorgy@0 179 '<div class="mejs-controls"></div>'+
gyorgy@0 180 '<div class="mejs-clear"></div>'+
gyorgy@0 181 '</div>' +
gyorgy@0 182 '</div>')
gyorgy@0 183 .addClass(t.$media[0].className)
gyorgy@0 184 .insertBefore(t.$media);
gyorgy@0 185
gyorgy@0 186 // move the <video/video> tag into the right spot
gyorgy@0 187 t.container.find('.mejs-mediaelement').append(t.$media);
gyorgy@0 188
gyorgy@0 189 // find parts
gyorgy@0 190 t.controls = t.container.find('.mejs-controls');
gyorgy@0 191 t.layers = t.container.find('.mejs-layers');
gyorgy@0 192
gyorgy@0 193 // determine the size
gyorgy@0 194 if (t.isVideo) {
gyorgy@0 195 // priority = videoWidth (forced), width attribute, defaultVideoWidth
gyorgy@0 196 t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth;
gyorgy@0 197 t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight;
gyorgy@0 198 } else {
gyorgy@0 199 t.width = t.options.audioWidth;
gyorgy@0 200 t.height = t.options.audioHeight;
gyorgy@0 201 }
gyorgy@0 202
gyorgy@0 203 // set the size, while we wait for the plugins to load below
gyorgy@0 204 t.setPlayerSize(t.width, t.height);
gyorgy@0 205
gyorgy@0 206 // create MediaElementShim
gyorgy@0 207 meOptions.pluginWidth = t.height;
gyorgy@0 208 meOptions.pluginHeight = t.width;
gyorgy@0 209 }
gyorgy@0 210
gyorgy@0 211 // create MediaElement shim
gyorgy@0 212 mejs.MediaElement(t.$media[0], meOptions);
gyorgy@0 213 },
gyorgy@0 214
gyorgy@0 215 // Sets up all controls and events
gyorgy@0 216 meReady: function(media, domNode) {
gyorgy@0 217
gyorgy@0 218
gyorgy@0 219 var t = this,
gyorgy@0 220 mf = mejs.MediaFeatures,
gyorgy@0 221 f,
gyorgy@0 222 feature;
gyorgy@0 223
gyorgy@0 224 // make sure it can't create itself again if a plugin reloads
gyorgy@0 225 if (t.created)
gyorgy@0 226 return;
gyorgy@0 227 else
gyorgy@0 228 t.created = true;
gyorgy@0 229
gyorgy@0 230 t.media = media;
gyorgy@0 231 t.domNode = domNode;
gyorgy@0 232
gyorgy@0 233 if (!mf.isiPhone && !mf.isAndroid && !(mf.isiPad && t.options.iPadUseNativeControls)) {
gyorgy@0 234
gyorgy@0 235 // two built in features
gyorgy@0 236 t.buildposter(t, t.controls, t.layers, t.media);
gyorgy@0 237 t.buildoverlays(t, t.controls, t.layers, t.media);
gyorgy@0 238
gyorgy@0 239 // grab for use by features
gyorgy@0 240 t.findTracks();
gyorgy@0 241
gyorgy@0 242 // add user-defined features/controls
gyorgy@0 243 for (f in t.options.features) {
gyorgy@0 244 feature = t.options.features[f];
gyorgy@0 245 if (t['build' + feature]) {
gyorgy@0 246 try {
gyorgy@0 247 t['build' + feature](t, t.controls, t.layers, t.media);
gyorgy@0 248 } catch (e) {
gyorgy@0 249 // TODO: report control error
gyorgy@0 250 //throw e;
gyorgy@0 251 }
gyorgy@0 252 }
gyorgy@0 253 }
gyorgy@0 254
gyorgy@0 255 t.container.trigger('controlsready');
gyorgy@0 256
gyorgy@0 257 // reset all layers and controls
gyorgy@0 258 t.setPlayerSize(t.width, t.height);
gyorgy@0 259 t.setControlsSize();
gyorgy@0 260
gyorgy@0 261
gyorgy@0 262 // controls fade
gyorgy@0 263 if (t.isVideo) {
gyorgy@0 264 // show/hide controls
gyorgy@0 265 t.container
gyorgy@0 266 .bind('mouseenter', function () {
gyorgy@0 267 if (!t.options.alwaysShowControls) {
gyorgy@0 268 t.controls.css('visibility','visible');
gyorgy@0 269 t.controls.stop(true, true).fadeIn(200);
gyorgy@0 270 }
gyorgy@0 271 })
gyorgy@0 272 .bind('mouseleave', function () {
gyorgy@0 273 if (!t.media.paused && !t.options.alwaysShowControls) {
gyorgy@0 274 t.controls.stop(true, true).fadeOut(200, function() {
gyorgy@0 275 $(this).css('visibility','hidden');
gyorgy@0 276 $(this).css('display','block');
gyorgy@0 277 });
gyorgy@0 278 }
gyorgy@0 279 });
gyorgy@0 280
gyorgy@0 281 // check for autoplay
gyorgy@0 282 if (t.domNode.getAttribute('autoplay') !== null && !t.options.alwaysShowControls) {
gyorgy@0 283 t.controls.css('visibility','hidden');
gyorgy@0 284 }
gyorgy@0 285
gyorgy@0 286 // resizer
gyorgy@0 287 if (t.options.enableAutosize) {
gyorgy@0 288 t.media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 289 // if the <video height> was not set and the options.videoHeight was not set
gyorgy@0 290 // then resize to the real dimensions
gyorgy@0 291 if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
gyorgy@0 292 t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
gyorgy@0 293 t.setControlsSize();
gyorgy@0 294 t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
gyorgy@0 295 }
gyorgy@0 296 }, false);
gyorgy@0 297 }
gyorgy@0 298 }
gyorgy@0 299
gyorgy@0 300 // ended for all
gyorgy@0 301 t.media.addEventListener('ended', function (e) {
gyorgy@0 302 t.media.setCurrentTime(0);
gyorgy@0 303 t.media.pause();
gyorgy@0 304
gyorgy@0 305 if (t.setProgressRail)
gyorgy@0 306 t.setProgressRail();
gyorgy@0 307 if (t.setCurrentRail)
gyorgy@0 308 t.setCurrentRail();
gyorgy@0 309
gyorgy@0 310 if (t.options.loop) {
gyorgy@0 311 t.media.play();
gyorgy@0 312 } else if (!t.options.alwaysShowControls) {
gyorgy@0 313 t.controls.css('visibility','visible');
gyorgy@0 314 }
gyorgy@0 315 }, true);
gyorgy@0 316
gyorgy@0 317 // resize on the first play
gyorgy@0 318 t.media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 319 if (t.updateDuration) {
gyorgy@0 320 t.updateDuration();
gyorgy@0 321 }
gyorgy@0 322 if (t.updateCurrent) {
gyorgy@0 323 t.updateCurrent();
gyorgy@0 324 }
gyorgy@0 325
gyorgy@0 326 t.setControlsSize();
gyorgy@0 327 }, true);
gyorgy@0 328
gyorgy@0 329
gyorgy@0 330 // webkit has trouble doing this without a delay
gyorgy@0 331 setTimeout(function () {
gyorgy@0 332 t.setControlsSize();
gyorgy@0 333 t.setPlayerSize(t.width, t.height);
gyorgy@0 334 }, 50);
gyorgy@0 335
gyorgy@0 336 }
gyorgy@0 337
gyorgy@0 338
gyorgy@0 339 if (t.options.success) {
gyorgy@0 340 t.options.success(t.media, t.domNode);
gyorgy@0 341 }
gyorgy@0 342 },
gyorgy@0 343
gyorgy@0 344 handleError: function(e) {
gyorgy@0 345 // Tell user that the file cannot be played
gyorgy@0 346 if (this.options.error) {
gyorgy@0 347 this.options.error(e);
gyorgy@0 348 }
gyorgy@0 349 },
gyorgy@0 350
gyorgy@0 351 setPlayerSize: function(width,height) {
gyorgy@0 352 var t = this;
gyorgy@0 353
gyorgy@0 354 // ie9 appears to need this (jQuery bug?)
gyorgy@0 355 t.width = parseInt(width, 10);
gyorgy@0 356 t.height = parseInt(height, 10);
gyorgy@0 357
gyorgy@0 358 t.container
gyorgy@0 359 .width(t.width)
gyorgy@0 360 .height(t.height);
gyorgy@0 361
gyorgy@0 362 t.layers.children('.mejs-layer')
gyorgy@0 363 .width(t.width)
gyorgy@0 364 .height(t.height);
gyorgy@0 365 },
gyorgy@0 366
gyorgy@0 367 setControlsSize: function() {
gyorgy@0 368 var t = this,
gyorgy@0 369 usedWidth = 0,
gyorgy@0 370 railWidth = 0,
gyorgy@0 371 rail = t.controls.find('.mejs-time-rail'),
gyorgy@0 372 total = t.controls.find('.mejs-time-total'),
gyorgy@0 373 current = t.controls.find('.mejs-time-current'),
gyorgy@0 374 loaded = t.controls.find('.mejs-time-loaded');
gyorgy@0 375 others = rail.siblings();
gyorgy@0 376
gyorgy@0 377 // find the size of all the other controls besides the rail
gyorgy@0 378 others.each(function() {
gyorgy@0 379 if ($(this).css('position') != 'absolute') {
gyorgy@0 380 usedWidth += $(this).outerWidth(true);
gyorgy@0 381 }
gyorgy@0 382 });
gyorgy@0 383 // fit the rail into the remaining space
gyorgy@0 384 railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false));
gyorgy@0 385
gyorgy@0 386 // outer area
gyorgy@0 387 rail.width(railWidth);
gyorgy@0 388 // dark space
gyorgy@0 389 total.width(railWidth - (total.outerWidth(true) - total.width()));
gyorgy@0 390
gyorgy@0 391 if (t.setProgressRail)
gyorgy@0 392 t.setProgressRail();
gyorgy@0 393 if (t.setCurrentRail)
gyorgy@0 394 t.setCurrentRail();
gyorgy@0 395 },
gyorgy@0 396
gyorgy@0 397
gyorgy@0 398 buildposter: function(player, controls, layers, media) {
gyorgy@0 399 var poster =
gyorgy@0 400 $('<div class="mejs-poster mejs-layer">'+
gyorgy@0 401 '<img />'+
gyorgy@0 402 '</div>')
gyorgy@0 403 .appendTo(layers),
gyorgy@0 404 posterUrl = player.$media.attr('poster'),
gyorgy@0 405 posterImg = poster.find('img').width(player.width).height(player.height);
gyorgy@0 406
gyorgy@0 407 // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
gyorgy@0 408 if (player.options.poster != '') {
gyorgy@0 409 posterImg.attr('src',player.options.poster);
gyorgy@0 410 // second, try the real poster
gyorgy@0 411 } else if (posterUrl !== '' && posterUrl != null) {
gyorgy@0 412 posterImg.attr('src',posterUrl);
gyorgy@0 413 } else {
gyorgy@0 414 poster.remove();
gyorgy@0 415 }
gyorgy@0 416
gyorgy@0 417 media.addEventListener('play',function() {
gyorgy@0 418 poster.hide();
gyorgy@0 419 }, false);
gyorgy@0 420 },
gyorgy@0 421
gyorgy@0 422 buildoverlays: function(player, controls, layers, media) {
gyorgy@0 423 if (!player.isVideo)
gyorgy@0 424 return;
gyorgy@0 425
gyorgy@0 426 var
gyorgy@0 427 loading =
gyorgy@0 428 $('<div class="mejs-overlay mejs-layer">'+
gyorgy@0 429 '<div class="mejs-overlay-loading"><span></span></div>'+
gyorgy@0 430 '</div>')
gyorgy@0 431 .hide() // start out hidden
gyorgy@0 432 .appendTo(layers),
gyorgy@0 433 error =
gyorgy@0 434 $('<div class="mejs-overlay mejs-layer">'+
gyorgy@0 435 '<div class="mejs-overlay-error"></div>'+
gyorgy@0 436 '</div>')
gyorgy@0 437 .hide() // start out hidden
gyorgy@0 438 .appendTo(layers),
gyorgy@0 439
gyorgy@0 440 // this needs to come last so it's on top
gyorgy@0 441 bigPlay =
gyorgy@0 442 $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
gyorgy@0 443 '<div class="mejs-overlay-button"></div>'+
gyorgy@0 444 '</div>')
gyorgy@0 445 .appendTo(layers)
gyorgy@0 446 .click(function() {
gyorgy@0 447 if (media.paused) {
gyorgy@0 448 media.play();
gyorgy@0 449 } else {
gyorgy@0 450 media.pause();
gyorgy@0 451 }
gyorgy@0 452 });
gyorgy@0 453
gyorgy@0 454
gyorgy@0 455 // show/hide big play button
gyorgy@0 456 media.addEventListener('play',function() {
gyorgy@0 457 bigPlay.hide();
gyorgy@0 458 error.hide();
gyorgy@0 459 }, false);
gyorgy@0 460 media.addEventListener('pause',function() {
gyorgy@0 461 bigPlay.show();
gyorgy@0 462 }, false);
gyorgy@0 463
gyorgy@0 464 // show/hide loading
gyorgy@0 465 media.addEventListener('loadstart',function() {
gyorgy@0 466 // for some reason Chrome is firing this event
gyorgy@0 467 if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
gyorgy@0 468 return;
gyorgy@0 469
gyorgy@0 470 loading.show();
gyorgy@0 471 }, false);
gyorgy@0 472 media.addEventListener('canplay',function() {
gyorgy@0 473 loading.hide();
gyorgy@0 474 }, false);
gyorgy@0 475
gyorgy@0 476 // error handling
gyorgy@0 477 media.addEventListener('error',function() {
gyorgy@0 478 loading.hide();
gyorgy@0 479 error.show();
gyorgy@0 480 error.find('mejs-overlay-error').html("Error loading this resource");
gyorgy@0 481 }, false);
gyorgy@0 482 },
gyorgy@0 483
gyorgy@0 484 findTracks: function() {
gyorgy@0 485 var t = this,
gyorgy@0 486 tracktags = t.$media.find('track');
gyorgy@0 487
gyorgy@0 488 // store for use by plugins
gyorgy@0 489 t.tracks = [];
gyorgy@0 490 tracktags.each(function() {
gyorgy@0 491 t.tracks.push({
gyorgy@0 492 srclang: $(this).attr('srclang').toLowerCase(),
gyorgy@0 493 src: $(this).attr('src'),
gyorgy@0 494 kind: $(this).attr('kind'),
gyorgy@0 495 entries: [],
gyorgy@0 496 isLoaded: false
gyorgy@0 497 });
gyorgy@0 498 });
gyorgy@0 499 },
gyorgy@0 500 changeSkin: function(className) {
gyorgy@0 501 this.container[0].className = 'mejs-container ' + className;
gyorgy@0 502 this.setPlayerSize();
gyorgy@0 503 this.setControlsSize();
gyorgy@0 504 },
gyorgy@0 505 play: function() {
gyorgy@0 506 this.media.play();
gyorgy@0 507 },
gyorgy@0 508 pause: function() {
gyorgy@0 509 this.media.pause();
gyorgy@0 510 },
gyorgy@0 511 load: function() {
gyorgy@0 512 this.media.load();
gyorgy@0 513 },
gyorgy@0 514 setMuted: function(muted) {
gyorgy@0 515 this.media.setMuted(muted);
gyorgy@0 516 },
gyorgy@0 517 setCurrentTime: function(time) {
gyorgy@0 518 this.media.setCurrentTime(time);
gyorgy@0 519 },
gyorgy@0 520 getCurrentTime: function() {
gyorgy@0 521 return this.media.currentTime;
gyorgy@0 522 },
gyorgy@0 523 setVolume: function(volume) {
gyorgy@0 524 this.media.setVolume(volume);
gyorgy@0 525 },
gyorgy@0 526 getVolume: function() {
gyorgy@0 527 return this.media.volume;
gyorgy@0 528 },
gyorgy@0 529 setSrc: function(src) {
gyorgy@0 530 this.media.setSrc(src);
gyorgy@0 531 }
gyorgy@0 532 };
gyorgy@0 533
gyorgy@0 534 // turn into jQuery plugin
gyorgy@0 535 if (typeof jQuery != 'undefined') {
gyorgy@0 536 jQuery.fn.mediaelementplayer = function (options) {
gyorgy@0 537 return this.each(function () {
gyorgy@0 538 new mejs.MediaElementPlayer(this, options);
gyorgy@0 539 });
gyorgy@0 540 };
gyorgy@0 541 }
gyorgy@0 542
gyorgy@0 543 // push out to window
gyorgy@0 544 window.MediaElementPlayer = mejs.MediaElementPlayer;
gyorgy@0 545
gyorgy@0 546 })(mejs.$);
gyorgy@0 547 (function($) {
gyorgy@0 548 // PLAY/pause BUTTON
gyorgy@0 549 MediaElementPlayer.prototype.buildplaypause = function(player, controls, layers, media) {
gyorgy@0 550 var play =
gyorgy@0 551 $('<div class="mejs-button mejs-playpause-button mejs-play" type="button">' +
gyorgy@0 552 '<button type="button"></button>' +
gyorgy@0 553 '</div>')
gyorgy@0 554 .appendTo(controls)
gyorgy@0 555 .click(function(e) {
gyorgy@0 556 e.preventDefault();
gyorgy@0 557
gyorgy@0 558 if (media.paused) {
gyorgy@0 559 media.play();
gyorgy@0 560 } else {
gyorgy@0 561 media.pause();
gyorgy@0 562 }
gyorgy@0 563
gyorgy@0 564 return false;
gyorgy@0 565 });
gyorgy@0 566
gyorgy@0 567 media.addEventListener('play',function() {
gyorgy@0 568 play.removeClass('mejs-play').addClass('mejs-pause');
gyorgy@0 569 }, false);
gyorgy@0 570 media.addEventListener('playing',function() {
gyorgy@0 571 play.removeClass('mejs-play').addClass('mejs-pause');
gyorgy@0 572 }, false);
gyorgy@0 573
gyorgy@0 574
gyorgy@0 575 media.addEventListener('pause',function() {
gyorgy@0 576 play.removeClass('mejs-pause').addClass('mejs-play');
gyorgy@0 577 }, false);
gyorgy@0 578 media.addEventListener('paused',function() {
gyorgy@0 579 play.removeClass('mejs-pause').addClass('mejs-play');
gyorgy@0 580 }, false);
gyorgy@0 581 }
gyorgy@0 582
gyorgy@0 583 })(mejs.$);
gyorgy@0 584 (function($) {
gyorgy@0 585 // STOP BUTTON
gyorgy@0 586 MediaElementPlayer.prototype.buildstop = function(player, controls, layers, media) {
gyorgy@0 587 var stop =
gyorgy@0 588 $('<div class="mejs-button mejs-stop-button mejs-stop">' +
gyorgy@0 589 '<button type="button"></button>' +
gyorgy@0 590 '</div>')
gyorgy@0 591 .appendTo(controls)
gyorgy@0 592 .click(function() {
gyorgy@0 593 if (!media.paused) {
gyorgy@0 594 media.pause();
gyorgy@0 595 }
gyorgy@0 596 if (media.currentTime > 0) {
gyorgy@0 597 media.setCurrentTime(0);
gyorgy@0 598 controls.find('.mejs-time-current').width('0px');
gyorgy@0 599 controls.find('.mejs-time-handle').css('left', '0px');
gyorgy@0 600 controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
gyorgy@0 601 controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );
gyorgy@0 602 layers.find('.mejs-poster').show();
gyorgy@0 603 }
gyorgy@0 604 });
gyorgy@0 605 }
gyorgy@0 606
gyorgy@0 607 })(mejs.$);
gyorgy@0 608 (function($) {
gyorgy@0 609 // progress/loaded bar
gyorgy@0 610 MediaElementPlayer.prototype.buildprogress = function(player, controls, layers, media) {
gyorgy@0 611
gyorgy@0 612 $('<div class="mejs-time-rail">'+
gyorgy@0 613 '<span class="mejs-time-total">'+
gyorgy@0 614 '<span class="mejs-time-loaded"></span>'+
gyorgy@0 615 '<span class="mejs-time-current"></span>'+
gyorgy@0 616 '<span class="mejs-time-handle"></span>'+
gyorgy@0 617 '<span class="mejs-time-float">' +
gyorgy@0 618 '<span class="mejs-time-float-current">00:00</span>' +
gyorgy@0 619 '<span class="mejs-time-float-corner"></span>' +
gyorgy@0 620 '</span>'+
gyorgy@0 621 '</span>'+
gyorgy@0 622 '</div>')
gyorgy@0 623 .appendTo(controls);
gyorgy@0 624
gyorgy@0 625 var
gyorgy@0 626 t = this,
gyorgy@0 627 total = controls.find('.mejs-time-total'),
gyorgy@0 628 loaded = controls.find('.mejs-time-loaded'),
gyorgy@0 629 current = controls.find('.mejs-time-current'),
gyorgy@0 630 handle = controls.find('.mejs-time-handle'),
gyorgy@0 631 timefloat = controls.find('.mejs-time-float'),
gyorgy@0 632 timefloatcurrent = controls.find('.mejs-time-float-current'),
gyorgy@0 633 handleMouseMove = function (e) {
gyorgy@0 634 // mouse position relative to the object
gyorgy@0 635 var x = e.pageX,
gyorgy@0 636 offset = total.offset(),
gyorgy@0 637 width = total.outerWidth(),
gyorgy@0 638 percentage = 0,
gyorgy@0 639 newTime = 0;
gyorgy@0 640
gyorgy@0 641
gyorgy@0 642 if (x > offset.left && x <= width + offset.left && media.duration) {
gyorgy@0 643 percentage = ((x - offset.left) / width);
gyorgy@0 644 newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
gyorgy@0 645
gyorgy@0 646 // seek to where the mouse is
gyorgy@0 647 if (mouseIsDown) {
gyorgy@0 648 media.setCurrentTime(newTime);
gyorgy@0 649 }
gyorgy@0 650
gyorgy@0 651 // position floating time box
gyorgy@0 652 var pos = x - offset.left;
gyorgy@0 653 timefloat.css('left', pos);
gyorgy@0 654 timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
gyorgy@0 655 }
gyorgy@0 656 },
gyorgy@0 657 mouseIsDown = false,
gyorgy@0 658 mouseIsOver = false;
gyorgy@0 659
gyorgy@0 660 // handle clicks
gyorgy@0 661 //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
gyorgy@0 662 total
gyorgy@0 663 .bind('mousedown', function (e) {
gyorgy@0 664 mouseIsDown = true;
gyorgy@0 665 handleMouseMove(e);
gyorgy@0 666 return false;
gyorgy@0 667 });
gyorgy@0 668
gyorgy@0 669 controls.find('.mejs-time-rail')
gyorgy@0 670 .bind('mouseenter', function(e) {
gyorgy@0 671 mouseIsOver = true;
gyorgy@0 672 })
gyorgy@0 673 .bind('mouseleave',function(e) {
gyorgy@0 674 mouseIsOver = false;
gyorgy@0 675 });
gyorgy@0 676
gyorgy@0 677 $(document)
gyorgy@0 678 .bind('mouseup', function (e) {
gyorgy@0 679 mouseIsDown = false;
gyorgy@0 680 //handleMouseMove(e);
gyorgy@0 681 })
gyorgy@0 682 .bind('mousemove', function (e) {
gyorgy@0 683 if (mouseIsDown || mouseIsOver) {
gyorgy@0 684 handleMouseMove(e);
gyorgy@0 685 }
gyorgy@0 686 });
gyorgy@0 687
gyorgy@0 688 // loading
gyorgy@0 689 media.addEventListener('progress', function (e) {
gyorgy@0 690 player.setProgressRail(e);
gyorgy@0 691 player.setCurrentRail(e);
gyorgy@0 692 }, false);
gyorgy@0 693
gyorgy@0 694 // current time
gyorgy@0 695 media.addEventListener('timeupdate', function(e) {
gyorgy@0 696 player.setProgressRail(e);
gyorgy@0 697 player.setCurrentRail(e);
gyorgy@0 698 }, false);
gyorgy@0 699
gyorgy@0 700
gyorgy@0 701 // store for later use
gyorgy@0 702 t.loaded = loaded;
gyorgy@0 703 t.total = total;
gyorgy@0 704 t.current = current;
gyorgy@0 705 t.handle = handle;
gyorgy@0 706 }
gyorgy@0 707 MediaElementPlayer.prototype.setProgressRail = function(e) {
gyorgy@0 708
gyorgy@0 709 var
gyorgy@0 710 t = this,
gyorgy@0 711 target = (e != undefined) ? e.target : t.media,
gyorgy@0 712 percent = null;
gyorgy@0 713
gyorgy@0 714 // newest HTML5 spec has buffered array (FF4, Webkit)
gyorgy@0 715 if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
gyorgy@0 716 // TODO: account for a real array with multiple values (only Firefox 4 has this so far)
gyorgy@0 717 percent = target.buffered.end(0) / target.duration;
gyorgy@0 718 }
gyorgy@0 719 // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
gyorgy@0 720 // to be anything other than 0. If the byte count is available we use this instead.
gyorgy@0 721 // Browsers that support the else if do not seem to have the bufferedBytes value and
gyorgy@0 722 // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
gyorgy@0 723 else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
gyorgy@0 724 percent = target.bufferedBytes / target.bytesTotal;
gyorgy@0 725 }
gyorgy@0 726 // Firefox 3 with an Ogg file seems to go this way
gyorgy@0 727 else if (e && e.lengthComputable && e.total != 0) {
gyorgy@0 728 percent = e.loaded/e.total;
gyorgy@0 729 }
gyorgy@0 730
gyorgy@0 731 // finally update the progress bar
gyorgy@0 732 if (percent !== null) {
gyorgy@0 733 percent = Math.min(1, Math.max(0, percent));
gyorgy@0 734 // update loaded bar
gyorgy@0 735 if (t.loaded && t.total) {
gyorgy@0 736 t.loaded.width(t.total.width() * percent);
gyorgy@0 737 }
gyorgy@0 738 }
gyorgy@0 739 }
gyorgy@0 740 MediaElementPlayer.prototype.setCurrentRail = function() {
gyorgy@0 741
gyorgy@0 742 var t = this;
gyorgy@0 743
gyorgy@0 744 if (t.media.currentTime != undefined && t.media.duration) {
gyorgy@0 745
gyorgy@0 746 // update bar and handle
gyorgy@0 747 if (t.total && t.handle) {
gyorgy@0 748 var
gyorgy@0 749 newWidth = t.total.width() * t.media.currentTime / t.media.duration,
gyorgy@0 750 handlePos = newWidth - (t.handle.outerWidth(true) / 2);
gyorgy@0 751
gyorgy@0 752 t.current.width(newWidth);
gyorgy@0 753 t.handle.css('left', handlePos);
gyorgy@0 754 }
gyorgy@0 755 }
gyorgy@0 756
gyorgy@0 757 }
gyorgy@0 758
gyorgy@0 759 })(mejs.$);
gyorgy@0 760 (function($) {
gyorgy@0 761 // current and duration 00:00 / 00:00
gyorgy@0 762 MediaElementPlayer.prototype.buildcurrent = function(player, controls, layers, media) {
gyorgy@0 763 var t = this;
gyorgy@0 764
gyorgy@0 765 $('<div class="mejs-time">'+
gyorgy@0 766 '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>'+
gyorgy@0 767 '</div>')
gyorgy@0 768 .appendTo(controls);
gyorgy@0 769
gyorgy@0 770 t.currenttime = t.controls.find('.mejs-currenttime');
gyorgy@0 771
gyorgy@0 772 media.addEventListener('timeupdate',function() {
gyorgy@0 773 player.updateCurrent();
gyorgy@0 774 }, false);
gyorgy@0 775 };
gyorgy@0 776
gyorgy@0 777 MediaElementPlayer.prototype.buildduration = function(player, controls, layers, media) {
gyorgy@0 778 var t = this;
gyorgy@0 779
gyorgy@0 780 if (controls.children().last().find('.mejs-currenttime').length > 0) {
gyorgy@0 781 $(' <span> | </span> '+
gyorgy@0 782 '<span class="mejs-duration">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>')
gyorgy@0 783 .appendTo(controls.find('.mejs-time'));
gyorgy@0 784 } else {
gyorgy@0 785
gyorgy@0 786 // add class to current time
gyorgy@0 787 controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
gyorgy@0 788
gyorgy@0 789 $('<div class="mejs-time mejs-duration-container">'+
gyorgy@0 790 '<span class="mejs-duration">' + (player.options.alwaysShowHours ? '00:' : '') + '00:00</span>'+
gyorgy@0 791 '</div>')
gyorgy@0 792 .appendTo(controls);
gyorgy@0 793 }
gyorgy@0 794
gyorgy@0 795 t.durationD = t.controls.find('.mejs-duration');
gyorgy@0 796
gyorgy@0 797 media.addEventListener('timeupdate',function() {
gyorgy@0 798 player.updateDuration();
gyorgy@0 799 }, false);
gyorgy@0 800 };
gyorgy@0 801
gyorgy@0 802 MediaElementPlayer.prototype.updateCurrent = function() {
gyorgy@0 803 var t = this;
gyorgy@0 804
gyorgy@0 805 if (t.currenttime) {
gyorgy@0 806 t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime | 0, t.options.alwaysShowHours || t.media.duration > 3600 ));
gyorgy@0 807 }
gyorgy@0 808 }
gyorgy@0 809 MediaElementPlayer.prototype.updateDuration = function() {
gyorgy@0 810 var t = this;
gyorgy@0 811
gyorgy@0 812 if (t.media.duration && t.durationD) {
gyorgy@0 813 t.durationD.html(mejs.Utility.secondsToTimeCode(t.media.duration, t.options.alwaysShowHours));
gyorgy@0 814 }
gyorgy@0 815 };
gyorgy@0 816
gyorgy@0 817 })(mejs.$);
gyorgy@0 818 (function($) {
gyorgy@0 819 MediaElementPlayer.prototype.buildvolume = function(player, controls, layers, media) {
gyorgy@0 820 var mute =
gyorgy@0 821 $('<div class="mejs-button mejs-volume-button mejs-mute">'+
gyorgy@0 822 '<button type="button"></button>'+
gyorgy@0 823 '<div class="mejs-volume-slider">'+ // outer background
gyorgy@0 824 '<div class="mejs-volume-total"></div>'+ // line background
gyorgy@0 825 '<div class="mejs-volume-current"></div>'+ // current volume
gyorgy@0 826 '<div class="mejs-volume-handle"></div>'+ // handle
gyorgy@0 827 '</div>'+
gyorgy@0 828 '</div>')
gyorgy@0 829 .appendTo(controls),
gyorgy@0 830 volumeSlider = mute.find('.mejs-volume-slider'),
gyorgy@0 831 volumeTotal = mute.find('.mejs-volume-total'),
gyorgy@0 832 volumeCurrent = mute.find('.mejs-volume-current'),
gyorgy@0 833 volumeHandle = mute.find('.mejs-volume-handle'),
gyorgy@0 834
gyorgy@0 835 positionVolumeHandle = function(volume) {
gyorgy@0 836
gyorgy@0 837 var
gyorgy@0 838 top = volumeTotal.height() - (volumeTotal.height() * volume);
gyorgy@0 839
gyorgy@0 840 // handle
gyorgy@0 841 volumeHandle.css('top', top - (volumeHandle.height() / 2));
gyorgy@0 842
gyorgy@0 843 // show the current visibility
gyorgy@0 844 volumeCurrent.height(volumeTotal.height() - top + parseInt(volumeTotal.css('top').replace(/px/,''),10));
gyorgy@0 845 volumeCurrent.css('top', top);
gyorgy@0 846 },
gyorgy@0 847 handleVolumeMove = function(e) {
gyorgy@0 848 var
gyorgy@0 849 railHeight = volumeTotal.height(),
gyorgy@0 850 totalOffset = volumeTotal.offset(),
gyorgy@0 851 totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
gyorgy@0 852 newY = e.pageY - totalOffset.top,
gyorgy@0 853 volume = (railHeight - newY) / railHeight
gyorgy@0 854
gyorgy@0 855 // TODO: handle vertical and horizontal CSS
gyorgy@0 856 // only allow it to move within the rail
gyorgy@0 857 if (newY < 0)
gyorgy@0 858 newY = 0;
gyorgy@0 859 else if (newY > railHeight)
gyorgy@0 860 newY = railHeight;
gyorgy@0 861
gyorgy@0 862 // move the handle to match the mouse
gyorgy@0 863 volumeHandle.css('top', newY - (volumeHandle.height() / 2) + totalTop );
gyorgy@0 864
gyorgy@0 865 // show the current visibility
gyorgy@0 866 volumeCurrent.height(railHeight-newY);
gyorgy@0 867 volumeCurrent.css('top',newY+totalTop);
gyorgy@0 868
gyorgy@0 869 // set mute status
gyorgy@0 870 if (volume == 0) {
gyorgy@0 871 media.setMuted(true);
gyorgy@0 872 mute.removeClass('mejs-mute').addClass('mejs-unmute');
gyorgy@0 873 } else {
gyorgy@0 874 media.setMuted(false);
gyorgy@0 875 mute.removeClass('mejs-unmute').addClass('mejs-mute');
gyorgy@0 876 }
gyorgy@0 877
gyorgy@0 878 volume = Math.max(0,volume);
gyorgy@0 879 volume = Math.min(volume,1);
gyorgy@0 880
gyorgy@0 881 // set the volume
gyorgy@0 882 media.setVolume(volume);
gyorgy@0 883 },
gyorgy@0 884 mouseIsDown = false;
gyorgy@0 885
gyorgy@0 886 // SLIDER
gyorgy@0 887 volumeSlider
gyorgy@0 888 .bind('mousedown', function (e) {
gyorgy@0 889 handleVolumeMove(e);
gyorgy@0 890 mouseIsDown = true;
gyorgy@0 891 return false;
gyorgy@0 892 });
gyorgy@0 893 $(document)
gyorgy@0 894 .bind('mouseup', function (e) {
gyorgy@0 895 mouseIsDown = false;
gyorgy@0 896 })
gyorgy@0 897 .bind('mousemove', function (e) {
gyorgy@0 898 if (mouseIsDown) {
gyorgy@0 899 handleVolumeMove(e);
gyorgy@0 900 }
gyorgy@0 901 });
gyorgy@0 902
gyorgy@0 903
gyorgy@0 904 // MUTE button
gyorgy@0 905 mute.find('button').click(function() {
gyorgy@0 906 if (media.muted) {
gyorgy@0 907 media.setMuted(false);
gyorgy@0 908 mute.removeClass('mejs-unmute').addClass('mejs-mute');
gyorgy@0 909 positionVolumeHandle(1);
gyorgy@0 910 } else {
gyorgy@0 911 media.setMuted(true);
gyorgy@0 912 mute.removeClass('mejs-mute').addClass('mejs-unmute');
gyorgy@0 913 positionVolumeHandle(0);
gyorgy@0 914 }
gyorgy@0 915 });
gyorgy@0 916
gyorgy@0 917 // listen for volume change events from other sources
gyorgy@0 918 media.addEventListener('volumechange', function(e) {
gyorgy@0 919 if (!mouseIsDown) {
gyorgy@0 920 positionVolumeHandle(e.target.volume);
gyorgy@0 921 }
gyorgy@0 922 }, true);
gyorgy@0 923
gyorgy@0 924 // set initial volume
gyorgy@0 925 positionVolumeHandle(player.options.startVolume);
gyorgy@0 926
gyorgy@0 927 // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
gyorgy@0 928 if (media.pluginType === 'native') {
gyorgy@0 929 media.setVolume(player.options.startVolume);
gyorgy@0 930 }
gyorgy@0 931 }
gyorgy@0 932
gyorgy@0 933 })(mejs.$);
gyorgy@0 934
gyorgy@0 935 (function($) {
gyorgy@0 936 mejs.MediaElementDefaults.forcePluginFullScreen = false;
gyorgy@0 937
gyorgy@0 938 MediaElementPlayer.prototype.isFullScreen = false;
gyorgy@0 939 MediaElementPlayer.prototype.buildfullscreen = function(player, controls, layers, media) {
gyorgy@0 940
gyorgy@0 941 if (!player.isVideo)
gyorgy@0 942 return;
gyorgy@0 943
gyorgy@0 944 // native events
gyorgy@0 945 if (mejs.MediaFeatures.hasNativeFullScreen) {
gyorgy@0 946 player.container.bind('webkitfullscreenchange', function(e) {
gyorgy@0 947
gyorgy@0 948 if (document.webkitIsFullScreen) {
gyorgy@0 949 // reset the controls once we are fully in full screen
gyorgy@0 950 player.setControlsSize();
gyorgy@0 951 } else {
gyorgy@0 952 // when a user presses ESC
gyorgy@0 953 // make sure to put the player back into place
gyorgy@0 954 player.exitFullScreen();
gyorgy@0 955 }
gyorgy@0 956 });
gyorgy@0 957 }
gyorgy@0 958
gyorgy@0 959 var
gyorgy@0 960 normalHeight = 0,
gyorgy@0 961 normalWidth = 0,
gyorgy@0 962 container = player.container,
gyorgy@0 963 docElement = document.documentElement,
gyorgy@0 964 docStyleOverflow,
gyorgy@0 965 parentWindow = window.parent,
gyorgy@0 966 parentiframes,
gyorgy@0 967 iframe,
gyorgy@0 968 fullscreenBtn =
gyorgy@0 969 $('<div class="mejs-button mejs-fullscreen-button"><button type="button"></button></div>')
gyorgy@0 970 .appendTo(controls)
gyorgy@0 971 .click(function() {
gyorgy@0 972 var isFullScreen = (mejs.MediaFeatures.hasNativeFullScreen) ?
gyorgy@0 973 document.webkitIsFullScreen :
gyorgy@0 974 player.isFullScreen;
gyorgy@0 975
gyorgy@0 976 if (isFullScreen) {
gyorgy@0 977 player.exitFullScreen();
gyorgy@0 978 } else {
gyorgy@0 979 player.enterFullScreen();
gyorgy@0 980 }
gyorgy@0 981 });
gyorgy@0 982
gyorgy@0 983 player.enterFullScreen = function() {
gyorgy@0 984
gyorgy@0 985 // firefox can't adjust plugin sizes without resetting :(
gyorgy@0 986 if (player.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || player.options.forcePluginFullScreen)) {
gyorgy@0 987 media.setFullscreen(true);
gyorgy@0 988 //player.isFullScreen = true;
gyorgy@0 989 return;
gyorgy@0 990 }
gyorgy@0 991
gyorgy@0 992 // attempt to set fullscreen
gyorgy@0 993 if (mejs.MediaFeatures.hasNativeFullScreen) {
gyorgy@0 994 player.container[0].webkitRequestFullScreen();
gyorgy@0 995 }
gyorgy@0 996
gyorgy@0 997 // store overflow
gyorgy@0 998 docStyleOverflow = docElement.style.overflow;
gyorgy@0 999 // set it to not show scroll bars so 100% will work
gyorgy@0 1000 docElement.style.overflow = 'hidden';
gyorgy@0 1001
gyorgy@0 1002 // store
gyorgy@0 1003 normalHeight = player.container.height();
gyorgy@0 1004 normalWidth = player.container.width();
gyorgy@0 1005
gyorgy@0 1006 // make full size
gyorgy@0 1007 container
gyorgy@0 1008 .addClass('mejs-container-fullscreen')
gyorgy@0 1009 .width('100%')
gyorgy@0 1010 .height('100%')
gyorgy@0 1011 .css('z-index', 1000);
gyorgy@0 1012 //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
gyorgy@0 1013
gyorgy@0 1014 if (player.pluginType === 'native') {
gyorgy@0 1015 player.$media
gyorgy@0 1016 .width('100%')
gyorgy@0 1017 .height('100%');
gyorgy@0 1018 } else {
gyorgy@0 1019 container.find('object embed')
gyorgy@0 1020 .width('100%')
gyorgy@0 1021 .height('100%');
gyorgy@0 1022 player.media.setVideoSize($(window).width(),$(window).height());
gyorgy@0 1023 }
gyorgy@0 1024
gyorgy@0 1025 layers.children('div')
gyorgy@0 1026 .width('100%')
gyorgy@0 1027 .height('100%');
gyorgy@0 1028
gyorgy@0 1029 fullscreenBtn
gyorgy@0 1030 .removeClass('mejs-fullscreen')
gyorgy@0 1031 .addClass('mejs-unfullscreen');
gyorgy@0 1032
gyorgy@0 1033 player.setControlsSize();
gyorgy@0 1034 player.isFullScreen = true;
gyorgy@0 1035 };
gyorgy@0 1036 player.exitFullScreen = function() {
gyorgy@0 1037
gyorgy@0 1038 // firefox can't adjust plugins
gyorgy@0 1039 if (player.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
gyorgy@0 1040 media.setFullscreen(false);
gyorgy@0 1041 //player.isFullScreen = false;
gyorgy@0 1042 return;
gyorgy@0 1043 }
gyorgy@0 1044
gyorgy@0 1045 // come outo of native fullscreen
gyorgy@0 1046 if (mejs.MediaFeatures.hasNativeFullScreen && document.webkitIsFullScreen) {
gyorgy@0 1047 document.webkitCancelFullScreen();
gyorgy@0 1048 }
gyorgy@0 1049
gyorgy@0 1050 // restore scroll bars to document
gyorgy@0 1051 docElement.style.overflow = docStyleOverflow;
gyorgy@0 1052
gyorgy@0 1053 container
gyorgy@0 1054 .removeClass('mejs-container-fullscreen')
gyorgy@0 1055 .width(normalWidth)
gyorgy@0 1056 .height(normalHeight)
gyorgy@0 1057 .css('z-index', 1);
gyorgy@0 1058 //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
gyorgy@0 1059
gyorgy@0 1060 if (player.pluginType === 'native') {
gyorgy@0 1061 player.$media
gyorgy@0 1062 .width(normalWidth)
gyorgy@0 1063 .height(normalHeight);
gyorgy@0 1064 } else {
gyorgy@0 1065 container.find('object embed')
gyorgy@0 1066 .width(normalWidth)
gyorgy@0 1067 .height(normalHeight);
gyorgy@0 1068
gyorgy@0 1069 player.media.setVideoSize(normalWidth, normalHeight);
gyorgy@0 1070 }
gyorgy@0 1071
gyorgy@0 1072 layers.children('div')
gyorgy@0 1073 .width(normalWidth)
gyorgy@0 1074 .height(normalHeight);
gyorgy@0 1075
gyorgy@0 1076 fullscreenBtn
gyorgy@0 1077 .removeClass('mejs-unfullscreen')
gyorgy@0 1078 .addClass('mejs-fullscreen');
gyorgy@0 1079
gyorgy@0 1080 player.setControlsSize();
gyorgy@0 1081 player.isFullScreen = false;
gyorgy@0 1082 };
gyorgy@0 1083
gyorgy@0 1084 $(window).bind('resize',function (e) {
gyorgy@0 1085 player.setControlsSize();
gyorgy@0 1086 });
gyorgy@0 1087
gyorgy@0 1088 $(document).bind('keydown',function (e) {
gyorgy@0 1089 if (player.isFullScreen && e.keyCode == 27) {
gyorgy@0 1090 player.exitFullScreen();
gyorgy@0 1091 }
gyorgy@0 1092 });
gyorgy@0 1093
gyorgy@0 1094 }
gyorgy@0 1095
gyorgy@0 1096 })(mejs.$);
gyorgy@0 1097 (function($) {
gyorgy@0 1098
gyorgy@0 1099 // add extra default options
gyorgy@0 1100 $.extend(mejs.MepDefaults, {
gyorgy@0 1101 // this will automatically turn on a <track>
gyorgy@0 1102 startLanguage: '',
gyorgy@0 1103 // a list of languages to auto-translate via Google
gyorgy@0 1104 translations: [],
gyorgy@0 1105 // a dropdownlist of automatic translations
gyorgy@0 1106 translationSelector: false,
gyorgy@0 1107 // key for tranlsations
gyorgy@0 1108 googleApiKey: ''
gyorgy@0 1109 });
gyorgy@0 1110
gyorgy@0 1111 $.extend(MediaElementPlayer.prototype, {
gyorgy@0 1112
gyorgy@0 1113 buildtracks: function(player, controls, layers, media) {
gyorgy@0 1114 if (!player.isVideo)
gyorgy@0 1115 return;
gyorgy@0 1116
gyorgy@0 1117 if (player.tracks.length == 0)
gyorgy@0 1118 return;
gyorgy@0 1119
gyorgy@0 1120 var i, options = '';
gyorgy@0 1121
gyorgy@0 1122 player.chapters =
gyorgy@0 1123 $('<div class="mejs-chapters mejs-layer"></div>')
gyorgy@0 1124 .prependTo(layers).hide();
gyorgy@0 1125 player.captions =
gyorgy@0 1126 $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position"><span class="mejs-captions-text"></span></div></div>')
gyorgy@0 1127 .prependTo(layers).hide();
gyorgy@0 1128 player.captionsText = player.captions.find('.mejs-captions-text');
gyorgy@0 1129 player.captionsButton =
gyorgy@0 1130 $('<div class="mejs-button mejs-captions-button">'+
gyorgy@0 1131 '<button type="button" ></button>'+
gyorgy@0 1132 '<div class="mejs-captions-selector">'+
gyorgy@0 1133 '<ul>'+
gyorgy@0 1134 '<li>'+
gyorgy@0 1135 '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
gyorgy@0 1136 '<label for="' + player.id + '_captions_none">None</label>'+
gyorgy@0 1137 '</li>' +
gyorgy@0 1138 '</ul>'+
gyorgy@0 1139 '</div>'+
gyorgy@0 1140 '</button>')
gyorgy@0 1141 .appendTo(controls)
gyorgy@0 1142 // handle clicks to the language radio buttons
gyorgy@0 1143 .delegate('input[type=radio]','click',function() {
gyorgy@0 1144 lang = this.value;
gyorgy@0 1145
gyorgy@0 1146 if (lang == 'none') {
gyorgy@0 1147 player.selectedTrack = null;
gyorgy@0 1148 } else {
gyorgy@0 1149 for (i=0; i<player.tracks.length; i++) {
gyorgy@0 1150 if (player.tracks[i].srclang == lang) {
gyorgy@0 1151 player.selectedTrack = player.tracks[i];
gyorgy@0 1152 player.captions.attr('lang', player.selectedTrack.srclang);
gyorgy@0 1153 player.displayCaptions();
gyorgy@0 1154 break;
gyorgy@0 1155 }
gyorgy@0 1156 }
gyorgy@0 1157 }
gyorgy@0 1158 });
gyorgy@0 1159 //.bind('mouseenter', function() {
gyorgy@0 1160 // player.captionsButton.find('.mejs-captions-selector').css('visibility','visible')
gyorgy@0 1161 //});
gyorgy@0 1162
gyorgy@0 1163 if (!player.options.alwaysShowControls) {
gyorgy@0 1164 // move with controls
gyorgy@0 1165 player.container
gyorgy@0 1166 .bind('mouseenter', function () {
gyorgy@0 1167 // push captions above controls
gyorgy@0 1168 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
gyorgy@0 1169
gyorgy@0 1170 })
gyorgy@0 1171 .bind('mouseleave', function () {
gyorgy@0 1172 if (!media.paused) {
gyorgy@0 1173 // move back to normal place
gyorgy@0 1174 player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
gyorgy@0 1175 }
gyorgy@0 1176 });
gyorgy@0 1177 } else {
gyorgy@0 1178 player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
gyorgy@0 1179 }
gyorgy@0 1180
gyorgy@0 1181 player.trackToLoad = -1;
gyorgy@0 1182 player.selectedTrack = null;
gyorgy@0 1183 player.isLoadingTrack = false;
gyorgy@0 1184
gyorgy@0 1185 // add user-defined translations
gyorgy@0 1186 if (player.tracks.length > 0 && player.options.translations.length > 0) {
gyorgy@0 1187 for (i=0; i<player.options.translations.length; i++) {
gyorgy@0 1188 player.tracks.push({
gyorgy@0 1189 srclang: player.options.translations[i].toLowerCase(),
gyorgy@0 1190 src: null,
gyorgy@0 1191 kind: 'subtitles',
gyorgy@0 1192 entries: [],
gyorgy@0 1193 isLoaded: false,
gyorgy@0 1194 isTranslation: true
gyorgy@0 1195 });
gyorgy@0 1196 }
gyorgy@0 1197 }
gyorgy@0 1198
gyorgy@0 1199 // add to list
gyorgy@0 1200 for (i=0; i<player.tracks.length; i++) {
gyorgy@0 1201 if (player.tracks[i].kind == 'subtitles') {
gyorgy@0 1202 player.addTrackButton(player.tracks[i].srclang, player.tracks[i].isTranslation);
gyorgy@0 1203 }
gyorgy@0 1204 }
gyorgy@0 1205
gyorgy@0 1206 player.loadNextTrack();
gyorgy@0 1207
gyorgy@0 1208
gyorgy@0 1209 media.addEventListener('timeupdate',function(e) {
gyorgy@0 1210 player.displayCaptions();
gyorgy@0 1211 }, false);
gyorgy@0 1212
gyorgy@0 1213 media.addEventListener('loadedmetadata', function(e) {
gyorgy@0 1214 player.displayChapters();
gyorgy@0 1215 }, false);
gyorgy@0 1216
gyorgy@0 1217 player.container.hover(
gyorgy@0 1218 function () {
gyorgy@0 1219 // chapters
gyorgy@0 1220 player.chapters.css('visibility','visible');
gyorgy@0 1221 player.chapters.fadeIn(200);
gyorgy@0 1222 },
gyorgy@0 1223 function () {
gyorgy@0 1224 if (!media.paused) {
gyorgy@0 1225 player.chapters.fadeOut(200, function() {
gyorgy@0 1226 $(this).css('visibility','hidden');
gyorgy@0 1227 $(this).css('display','block');
gyorgy@0 1228 });
gyorgy@0 1229 }
gyorgy@0 1230 });
gyorgy@0 1231
gyorgy@0 1232 // check for autoplay
gyorgy@0 1233 if (player.node.getAttribute('autoplay') !== null) {
gyorgy@0 1234 player.chapters.css('visibility','hidden');
gyorgy@0 1235 }
gyorgy@0 1236
gyorgy@0 1237 // auto selector
gyorgy@0 1238 if (player.options.translationSelector) {
gyorgy@0 1239 for (i in mejs.language.codes) {
gyorgy@0 1240 options += '<option value="' + i + '">' + mejs.language.codes[i] + '</option>';
gyorgy@0 1241 }
gyorgy@0 1242 player.container.find('.mejs-captions-selector ul').before($(
gyorgy@0 1243 '<select class="mejs-captions-translations">' +
gyorgy@0 1244 '<option value="">--Add Translation--</option>' +
gyorgy@0 1245 options +
gyorgy@0 1246 '</select>'
gyorgy@0 1247 ));
gyorgy@0 1248 // add clicks
gyorgy@0 1249 player.container.find('.mejs-captions-translations').change(function() {
gyorgy@0 1250 var
gyorgy@0 1251 option = $(this);
gyorgy@0 1252 lang = option.val();
gyorgy@0 1253 // add this language to the tracks list
gyorgy@0 1254 if (lang != '') {
gyorgy@0 1255 player.tracks.push({
gyorgy@0 1256 srclang: lang,
gyorgy@0 1257 src: null,
gyorgy@0 1258 entries: [],
gyorgy@0 1259 isLoaded: false,
gyorgy@0 1260 isTranslation: true
gyorgy@0 1261 });
gyorgy@0 1262
gyorgy@0 1263 if (!player.isLoadingTrack) {
gyorgy@0 1264 player.trackToLoad--;
gyorgy@0 1265 player.addTrackButton(lang,true);
gyorgy@0 1266 player.options.startLanguage = lang;
gyorgy@0 1267 player.loadNextTrack();
gyorgy@0 1268 }
gyorgy@0 1269 }
gyorgy@0 1270 });
gyorgy@0 1271 }
gyorgy@0 1272
gyorgy@0 1273 },
gyorgy@0 1274
gyorgy@0 1275 loadNextTrack: function() {
gyorgy@0 1276 var t = this;
gyorgy@0 1277
gyorgy@0 1278 t.trackToLoad++;
gyorgy@0 1279 if (t.trackToLoad < t.tracks.length) {
gyorgy@0 1280 t.isLoadingTrack = true;
gyorgy@0 1281 t.loadTrack(t.trackToLoad);
gyorgy@0 1282 } else {
gyorgy@0 1283 // add done?
gyorgy@0 1284 t.isLoadingTrack = false;
gyorgy@0 1285 }
gyorgy@0 1286 },
gyorgy@0 1287
gyorgy@0 1288 loadTrack: function(index){
gyorgy@0 1289 var
gyorgy@0 1290 t = this,
gyorgy@0 1291 track = t.tracks[index],
gyorgy@0 1292 after = function() {
gyorgy@0 1293
gyorgy@0 1294 track.isLoaded = true;
gyorgy@0 1295
gyorgy@0 1296 // create button
gyorgy@0 1297 //t.addTrackButton(track.srclang);
gyorgy@0 1298 t.enableTrackButton(track.srclang);
gyorgy@0 1299
gyorgy@0 1300 t.loadNextTrack();
gyorgy@0 1301
gyorgy@0 1302 };
gyorgy@0 1303
gyorgy@0 1304 if (track.isTranslation) {
gyorgy@0 1305
gyorgy@0 1306 // translate the first track
gyorgy@0 1307 mejs.TrackFormatParser.translateTrackText(t.tracks[0].entries, t.tracks[0].srclang, track.srclang, t.options.googleApiKey, function(newOne) {
gyorgy@0 1308
gyorgy@0 1309 // store the new translation
gyorgy@0 1310 track.entries = newOne;
gyorgy@0 1311
gyorgy@0 1312 after();
gyorgy@0 1313 });
gyorgy@0 1314
gyorgy@0 1315 } else {
gyorgy@0 1316 $.ajax({
gyorgy@0 1317 url: track.src,
gyorgy@0 1318 success: function(d) {
gyorgy@0 1319
gyorgy@0 1320 // parse the loaded file
gyorgy@0 1321 track.entries = mejs.TrackFormatParser.parse(d);
gyorgy@0 1322 after();
gyorgy@0 1323
gyorgy@0 1324 if (track.kind == 'chapters' && t.media.duration > 0) {
gyorgy@0 1325 t.drawChapters(track);
gyorgy@0 1326 }
gyorgy@0 1327 },
gyorgy@0 1328 error: function() {
gyorgy@0 1329 t.loadNextTrack();
gyorgy@0 1330 }
gyorgy@0 1331 });
gyorgy@0 1332 }
gyorgy@0 1333 },
gyorgy@0 1334
gyorgy@0 1335 enableTrackButton: function(lang) {
gyorgy@0 1336 var t = this;
gyorgy@0 1337
gyorgy@0 1338 t.captionsButton
gyorgy@0 1339 .find('input[value=' + lang + ']')
gyorgy@0 1340 .prop('disabled',false)
gyorgy@0 1341 .siblings('label')
gyorgy@0 1342 .html( mejs.language.codes[lang] || lang );
gyorgy@0 1343
gyorgy@0 1344 // auto select
gyorgy@0 1345 if (t.options.startLanguage == lang) {
gyorgy@0 1346 $('#' + t.id + '_captions_' + lang).click();
gyorgy@0 1347 }
gyorgy@0 1348
gyorgy@0 1349 t.adjustLanguageBox();
gyorgy@0 1350 },
gyorgy@0 1351
gyorgy@0 1352 addTrackButton: function(lang, isTranslation) {
gyorgy@0 1353 var t = this,
gyorgy@0 1354 l = mejs.language.codes[lang] || lang;
gyorgy@0 1355
gyorgy@0 1356 t.captionsButton.find('ul').append(
gyorgy@0 1357 $('<li>'+
gyorgy@0 1358 '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
gyorgy@0 1359 '<label for="' + t.id + '_captions_' + lang + '">' + l + ((isTranslation) ? ' (translating)' : ' (loading)') + '</label>'+
gyorgy@0 1360 '</li>')
gyorgy@0 1361 );
gyorgy@0 1362
gyorgy@0 1363 t.adjustLanguageBox();
gyorgy@0 1364
gyorgy@0 1365 // remove this from the dropdownlist (if it exists)
gyorgy@0 1366 t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
gyorgy@0 1367 },
gyorgy@0 1368
gyorgy@0 1369 adjustLanguageBox:function() {
gyorgy@0 1370 var t = this;
gyorgy@0 1371 // adjust the size of the outer box
gyorgy@0 1372 t.captionsButton.find('.mejs-captions-selector').height(
gyorgy@0 1373 t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
gyorgy@0 1374 t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
gyorgy@0 1375 );
gyorgy@0 1376 },
gyorgy@0 1377
gyorgy@0 1378 displayCaptions: function() {
gyorgy@0 1379
gyorgy@0 1380 if (typeof this.tracks == 'undefined')
gyorgy@0 1381 return;
gyorgy@0 1382
gyorgy@0 1383 var
gyorgy@0 1384 t = this,
gyorgy@0 1385 i,
gyorgy@0 1386 track = t.selectedTrack;
gyorgy@0 1387
gyorgy@0 1388 if (track != null && track.isLoaded) {
gyorgy@0 1389 for (i=0; i<track.entries.times.length; i++) {
gyorgy@0 1390 if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
gyorgy@0 1391 t.captionsText.html(track.entries.text[i]);
gyorgy@0 1392 t.captions.show();
gyorgy@0 1393 return; // exit out if one is visible;
gyorgy@0 1394 }
gyorgy@0 1395 }
gyorgy@0 1396 t.captions.hide();
gyorgy@0 1397 } else {
gyorgy@0 1398 t.captions.hide();
gyorgy@0 1399 }
gyorgy@0 1400 },
gyorgy@0 1401
gyorgy@0 1402 displayChapters: function() {
gyorgy@0 1403 var
gyorgy@0 1404 t = this,
gyorgy@0 1405 i;
gyorgy@0 1406
gyorgy@0 1407 for (i=0; i<t.tracks.length; i++) {
gyorgy@0 1408 if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
gyorgy@0 1409 t.drawChapters(t.tracks[i]);
gyorgy@0 1410 break;
gyorgy@0 1411 }
gyorgy@0 1412 }
gyorgy@0 1413 },
gyorgy@0 1414
gyorgy@0 1415 drawChapters: function(chapters) {
gyorgy@0 1416 var
gyorgy@0 1417 t = this,
gyorgy@0 1418 i,
gyorgy@0 1419 dur,
gyorgy@0 1420 //width,
gyorgy@0 1421 //left,
gyorgy@0 1422 percent = 0,
gyorgy@0 1423 usedPercent = 0;
gyorgy@0 1424
gyorgy@0 1425 t.chapters.empty();
gyorgy@0 1426
gyorgy@0 1427 for (i=0; i<chapters.entries.times.length; i++) {
gyorgy@0 1428 dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
gyorgy@0 1429 percent = Math.floor(dur / t.media.duration * 100);
gyorgy@0 1430 if (percent + usedPercent > 100 || // too large
gyorgy@0 1431 i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
gyorgy@0 1432 {
gyorgy@0 1433 percent = 100 - usedPercent;
gyorgy@0 1434 }
gyorgy@0 1435 //width = Math.floor(t.width * dur / t.media.duration);
gyorgy@0 1436 //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
gyorgy@0 1437 //if (left + width > t.width) {
gyorgy@0 1438 // width = t.width - left;
gyorgy@0 1439 //}
gyorgy@0 1440
gyorgy@0 1441 t.chapters.append( $(
gyorgy@0 1442 '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
gyorgy@0 1443 '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
gyorgy@0 1444 '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
gyorgy@0 1445 '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
gyorgy@0 1446 '</div>' +
gyorgy@0 1447 '</div>'));
gyorgy@0 1448 usedPercent += percent;
gyorgy@0 1449 }
gyorgy@0 1450
gyorgy@0 1451 t.chapters.find('div.mejs-chapter').click(function() {
gyorgy@0 1452 t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
gyorgy@0 1453 if (t.media.paused) {
gyorgy@0 1454 t.media.play();
gyorgy@0 1455 }
gyorgy@0 1456 });
gyorgy@0 1457
gyorgy@0 1458 t.chapters.show();
gyorgy@0 1459 }
gyorgy@0 1460 });
gyorgy@0 1461
gyorgy@0 1462
gyorgy@0 1463
gyorgy@0 1464 mejs.language = {
gyorgy@0 1465 codes: {
gyorgy@0 1466 af:'Afrikaans',
gyorgy@0 1467 sq:'Albanian',
gyorgy@0 1468 ar:'Arabic',
gyorgy@0 1469 be:'Belarusian',
gyorgy@0 1470 bg:'Bulgarian',
gyorgy@0 1471 ca:'Catalan',
gyorgy@0 1472 zh:'Chinese',
gyorgy@0 1473 'zh-cn':'Chinese Simplified',
gyorgy@0 1474 'zh-tw':'Chinese Traditional',
gyorgy@0 1475 hr:'Croatian',
gyorgy@0 1476 cs:'Czech',
gyorgy@0 1477 da:'Danish',
gyorgy@0 1478 nl:'Dutch',
gyorgy@0 1479 en:'English',
gyorgy@0 1480 et:'Estonian',
gyorgy@0 1481 tl:'Filipino',
gyorgy@0 1482 fi:'Finnish',
gyorgy@0 1483 fr:'French',
gyorgy@0 1484 gl:'Galician',
gyorgy@0 1485 de:'German',
gyorgy@0 1486 el:'Greek',
gyorgy@0 1487 ht:'Haitian Creole',
gyorgy@0 1488 iw:'Hebrew',
gyorgy@0 1489 hi:'Hindi',
gyorgy@0 1490 hu:'Hungarian',
gyorgy@0 1491 is:'Icelandic',
gyorgy@0 1492 id:'Indonesian',
gyorgy@0 1493 ga:'Irish',
gyorgy@0 1494 it:'Italian',
gyorgy@0 1495 ja:'Japanese',
gyorgy@0 1496 ko:'Korean',
gyorgy@0 1497 lv:'Latvian',
gyorgy@0 1498 lt:'Lithuanian',
gyorgy@0 1499 mk:'Macedonian',
gyorgy@0 1500 ms:'Malay',
gyorgy@0 1501 mt:'Maltese',
gyorgy@0 1502 no:'Norwegian',
gyorgy@0 1503 fa:'Persian',
gyorgy@0 1504 pl:'Polish',
gyorgy@0 1505 pt:'Portuguese',
gyorgy@0 1506 //'pt-pt':'Portuguese (Portugal)',
gyorgy@0 1507 ro:'Romanian',
gyorgy@0 1508 ru:'Russian',
gyorgy@0 1509 sr:'Serbian',
gyorgy@0 1510 sk:'Slovak',
gyorgy@0 1511 sl:'Slovenian',
gyorgy@0 1512 es:'Spanish',
gyorgy@0 1513 sw:'Swahili',
gyorgy@0 1514 sv:'Swedish',
gyorgy@0 1515 tl:'Tagalog',
gyorgy@0 1516 th:'Thai',
gyorgy@0 1517 tr:'Turkish',
gyorgy@0 1518 uk:'Ukrainian',
gyorgy@0 1519 vi:'Vietnamese',
gyorgy@0 1520 cy:'Welsh',
gyorgy@0 1521 yi:'Yiddish'
gyorgy@0 1522 }
gyorgy@0 1523 };
gyorgy@0 1524
gyorgy@0 1525 /*
gyorgy@0 1526 Parses WebVVT format which should be formatted as
gyorgy@0 1527 ================================
gyorgy@0 1528 WEBVTT
gyorgy@0 1529
gyorgy@0 1530 1
gyorgy@0 1531 00:00:01,1 --> 00:00:05,000
gyorgy@0 1532 A line of text
gyorgy@0 1533
gyorgy@0 1534 2
gyorgy@0 1535 00:01:15,1 --> 00:02:05,000
gyorgy@0 1536 A second line of text
gyorgy@0 1537
gyorgy@0 1538 ===============================
gyorgy@0 1539
gyorgy@0 1540 Adapted from: http://www.delphiki.com/html5/playr
gyorgy@0 1541 */
gyorgy@0 1542 mejs.TrackFormatParser = {
gyorgy@0 1543 pattern_identifier: /^[0-9]+$/,
gyorgy@0 1544 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 1545
gyorgy@0 1546 split2: function (text, regex) {
gyorgy@0 1547 // normal version for compliant browsers
gyorgy@0 1548 // see below for IE fix
gyorgy@0 1549 return text.split(regex);
gyorgy@0 1550 },
gyorgy@0 1551 parse: function(trackText) {
gyorgy@0 1552 var
gyorgy@0 1553 i = 0,
gyorgy@0 1554 lines = this.split2(trackText, /\r?\n/),
gyorgy@0 1555 entries = {text:[], times:[]},
gyorgy@0 1556 timecode,
gyorgy@0 1557 text;
gyorgy@0 1558
gyorgy@0 1559 for(; i<lines.length; i++) {
gyorgy@0 1560 // check for the line number
gyorgy@0 1561 if (this.pattern_identifier.exec(lines[i])){
gyorgy@0 1562 // skip to the next line where the start --> end time code should be
gyorgy@0 1563 i++;
gyorgy@0 1564 timecode = this.pattern_timecode.exec(lines[i]);
gyorgy@0 1565 if (timecode && i<lines.length){
gyorgy@0 1566 i++;
gyorgy@0 1567 // grab all the (possibly multi-line) text that follows
gyorgy@0 1568 text = lines[i];
gyorgy@0 1569 i++;
gyorgy@0 1570 while(lines[i] !== '' && i<lines.length){
gyorgy@0 1571 text = text + '\n' + lines[i];
gyorgy@0 1572 i++;
gyorgy@0 1573 }
gyorgy@0 1574
gyorgy@0 1575 // Text is in a different array so I can use .join
gyorgy@0 1576 entries.text.push(text);
gyorgy@0 1577 entries.times.push(
gyorgy@0 1578 {
gyorgy@0 1579 start: mejs.Utility.timeCodeToSeconds(timecode[1]),
gyorgy@0 1580 stop: mejs.Utility.timeCodeToSeconds(timecode[3]),
gyorgy@0 1581 settings: timecode[5]
gyorgy@0 1582 });
gyorgy@0 1583 }
gyorgy@0 1584 }
gyorgy@0 1585 }
gyorgy@0 1586
gyorgy@0 1587 return entries;
gyorgy@0 1588 },
gyorgy@0 1589
gyorgy@0 1590 translateTrackText: function(trackData, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 1591
gyorgy@0 1592 var
gyorgy@0 1593 entries = {text:[], times:[]},
gyorgy@0 1594 lines,
gyorgy@0 1595 i
gyorgy@0 1596
gyorgy@0 1597 this.translateText( trackData.text.join(' <a></a>'), fromLang, toLang, googleApiKey, function(result) {
gyorgy@0 1598 // split on separators
gyorgy@0 1599 lines = result.split('<a></a>');
gyorgy@0 1600
gyorgy@0 1601 // create new entries
gyorgy@0 1602 for (i=0;i<trackData.text.length; i++) {
gyorgy@0 1603 // add translated line
gyorgy@0 1604 entries.text[i] = lines[i];
gyorgy@0 1605 // copy existing times
gyorgy@0 1606 entries.times[i] = {
gyorgy@0 1607 start: trackData.times[i].start,
gyorgy@0 1608 stop: trackData.times[i].stop,
gyorgy@0 1609 settings: trackData.times[i].settings
gyorgy@0 1610 };
gyorgy@0 1611 }
gyorgy@0 1612
gyorgy@0 1613 callback(entries);
gyorgy@0 1614 });
gyorgy@0 1615 },
gyorgy@0 1616
gyorgy@0 1617 translateText: function(text, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 1618
gyorgy@0 1619 var
gyorgy@0 1620 separatorIndex,
gyorgy@0 1621 chunks = [],
gyorgy@0 1622 chunk,
gyorgy@0 1623 maxlength = 1000,
gyorgy@0 1624 result = '',
gyorgy@0 1625 nextChunk= function() {
gyorgy@0 1626 if (chunks.length > 0) {
gyorgy@0 1627 chunk = chunks.shift();
gyorgy@0 1628 mejs.TrackFormatParser.translateChunk(chunk, fromLang, toLang, googleApiKey, function(r) {
gyorgy@0 1629 if (r != 'undefined') {
gyorgy@0 1630 result += r;
gyorgy@0 1631 }
gyorgy@0 1632 nextChunk();
gyorgy@0 1633 });
gyorgy@0 1634 } else {
gyorgy@0 1635 callback(result);
gyorgy@0 1636 }
gyorgy@0 1637 };
gyorgy@0 1638
gyorgy@0 1639 // split into chunks
gyorgy@0 1640 while (text.length > 0) {
gyorgy@0 1641 if (text.length > maxlength) {
gyorgy@0 1642 separatorIndex = text.lastIndexOf('.', maxlength);
gyorgy@0 1643 chunks.push(text.substring(0, separatorIndex));
gyorgy@0 1644 text = text.substring(separatorIndex+1);
gyorgy@0 1645 } else {
gyorgy@0 1646 chunks.push(text);
gyorgy@0 1647 text = '';
gyorgy@0 1648 }
gyorgy@0 1649 }
gyorgy@0 1650
gyorgy@0 1651 // start handling the chunks
gyorgy@0 1652 nextChunk();
gyorgy@0 1653 },
gyorgy@0 1654 translateChunk: function(text, fromLang, toLang, googleApiKey, callback) {
gyorgy@0 1655
gyorgy@0 1656 var data = {
gyorgy@0 1657 q: text,
gyorgy@0 1658 langpair: fromLang + '|' + toLang,
gyorgy@0 1659 v: '1.0'
gyorgy@0 1660 };
gyorgy@0 1661 if (googleApiKey !== '' && googleApiKey !== null) {
gyorgy@0 1662 data.key = googleApiKey;
gyorgy@0 1663 }
gyorgy@0 1664
gyorgy@0 1665 $.ajax({
gyorgy@0 1666 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 1667 data: data,
gyorgy@0 1668 type: 'GET',
gyorgy@0 1669 dataType: 'jsonp',
gyorgy@0 1670 success: function(d) {
gyorgy@0 1671 callback(d.responseData.translatedText);
gyorgy@0 1672 },
gyorgy@0 1673 error: function(e) {
gyorgy@0 1674 callback(null);
gyorgy@0 1675 }
gyorgy@0 1676 });
gyorgy@0 1677 }
gyorgy@0 1678 };
gyorgy@0 1679 // test for browsers with bad String.split method.
gyorgy@0 1680 if ('x\n\ny'.split(/\n/gi).length != 3) {
gyorgy@0 1681 // add super slow IE8 and below version
gyorgy@0 1682 mejs.TrackFormatParser.split2 = function(text, regex) {
gyorgy@0 1683 var
gyorgy@0 1684 parts = [],
gyorgy@0 1685 chunk = '',
gyorgy@0 1686 i;
gyorgy@0 1687
gyorgy@0 1688 for (i=0; i<text.length; i++) {
gyorgy@0 1689 chunk += text.substring(i,i+1);
gyorgy@0 1690 if (regex.test(chunk)) {
gyorgy@0 1691 parts.push(chunk.replace(regex, ''));
gyorgy@0 1692 chunk = '';
gyorgy@0 1693 }
gyorgy@0 1694 }
gyorgy@0 1695 parts.push(chunk);
gyorgy@0 1696 return parts;
gyorgy@0 1697 }
gyorgy@0 1698 }
gyorgy@0 1699
gyorgy@0 1700 })(mejs.$);
gyorgy@0 1701