mediaelement_rails 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
7
7
  *
8
8
  * Copyright 2010-2012, John Dyer (http://j.hn/)
9
- * Dual licensed under the MIT or GPL Version 2 licenses.
9
+ * License: MIT
10
10
  *
11
11
  */
12
12
  if (typeof jQuery != 'undefined') {
@@ -14,1082 +14,1098 @@ if (typeof jQuery != 'undefined') {
14
14
  } else if (typeof ender != 'undefined') {
15
15
  mejs.$ = ender;
16
16
  }
17
- (function ($) {
18
-
19
- // default player values
20
- mejs.MepDefaults = {
21
- // url to poster (to fix iOS 3.x)
22
- poster: '',
23
- // default if the <video width> is not specified
24
- defaultVideoWidth: 480,
25
- // default if the <video height> is not specified
26
- defaultVideoHeight: 270,
27
- // if set, overrides <video width>
28
- videoWidth: -1,
29
- // if set, overrides <video height>
30
- videoHeight: -1,
31
- // default if the user doesn't specify
32
- defaultAudioWidth: 400,
33
- // default if the user doesn't specify
34
- defaultAudioHeight: 30,
35
-
36
- // default amount to move back when back key is pressed
37
- defaultSeekBackwardInterval: function(media) {
38
- return (media.duration * 0.05);
39
- },
40
- // default amount to move forward when forward key is pressed
41
- defaultSeekForwardInterval: function(media) {
42
- return (media.duration * 0.05);
43
- },
44
-
45
- // width of audio player
46
- audioWidth: -1,
47
- // height of audio player
48
- audioHeight: -1,
49
- // initial volume when the player starts (overrided by user cookie)
50
- startVolume: 0.8,
51
- // useful for <audio> player loops
52
- loop: false,
53
- // resize to media dimensions
54
- enableAutosize: true,
55
- // forces the hour marker (##:00:00)
56
- alwaysShowHours: false,
57
-
58
- // show framecount in timecode (##:00:00:00)
59
- showTimecodeFrameCount: false,
60
- // used when showTimecodeFrameCount is set to true
61
- framesPerSecond: 25,
62
-
63
- // automatically calculate the width of the progress bar based on the sizes of other elements
64
- autosizeProgress : true,
65
- // Hide controls when playing and mouse is not over the video
66
- alwaysShowControls: false,
67
- // force iPad's native controls
68
- iPadUseNativeControls: false,
69
- // force iPhone's native controls
70
- iPhoneUseNativeControls: false,
71
- // force Android's native controls
72
- AndroidUseNativeControls: false,
73
- // features to show
74
- features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
75
- // only for dynamic
76
- isVideo: true,
77
-
78
- // turns keyboard support on and off for this instance
79
- enableKeyboard: true,
80
-
81
- // whenthis player starts, it will pause other players
82
- pauseOtherPlayers: true,
83
-
84
- // array of keyboard actions such as play pause
85
- keyActions: [
86
- {
87
- keys: [
88
- 32, // SPACE
89
- 179 // GOOGLE play/pause button
90
- ],
91
- action: function(player, media) {
92
- if (media.paused || media.ended) {
93
- media.play();
94
- } else {
95
- media.pause();
96
- }
97
- }
98
- },
99
- {
100
- keys: [38], // UP
101
- action: function(player, media) {
102
- var newVolume = Math.min(media.volume + 0.1, 1);
103
- media.setVolume(newVolume);
104
- }
105
- },
106
- {
107
- keys: [40], // DOWN
108
- action: function(player, media) {
109
- var newVolume = Math.max(media.volume - 0.1, 0);
110
- media.setVolume(newVolume);
111
- }
112
- },
113
- {
114
- keys: [
115
- 37, // LEFT
116
- 227 // Google TV rewind
117
- ],
118
- action: function(player, media) {
119
- if (!isNaN(media.duration) && media.duration > 0) {
120
- if (player.isVideo) {
121
- player.showControls();
122
- player.startControlsTimer();
123
- }
124
-
125
- // 5%
126
- var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
127
- media.setCurrentTime(newTime);
128
- }
129
- }
130
- },
131
- {
132
- keys: [
133
- 39, // RIGHT
134
- 228 // Google TV forward
135
- ],
136
- action: function(player, media) {
137
- if (!isNaN(media.duration) && media.duration > 0) {
138
- if (player.isVideo) {
139
- player.showControls();
140
- player.startControlsTimer();
141
- }
142
-
143
- // 5%
144
- var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
145
- media.setCurrentTime(newTime);
146
- }
147
- }
148
- },
149
- {
150
- keys: [70], // f
151
- action: function(player, media) {
152
- if (typeof player.enterFullScreen != 'undefined') {
153
- if (player.isFullScreen) {
154
- player.exitFullScreen();
155
- } else {
156
- player.enterFullScreen();
157
- }
158
- }
159
- }
160
- }
161
- ]
162
- };
163
-
164
- mejs.mepIndex = 0;
165
-
166
- mejs.players = [];
167
-
168
- // wraps a MediaElement object in player controls
169
- mejs.MediaElementPlayer = function(node, o) {
170
- // enforce object, even without "new" (via John Resig)
171
- if ( !(this instanceof mejs.MediaElementPlayer) ) {
172
- return new mejs.MediaElementPlayer(node, o);
173
- }
174
-
175
- var t = this;
176
-
177
- // these will be reset after the MediaElement.success fires
178
- t.$media = t.$node = $(node);
179
- t.node = t.media = t.$media[0];
180
-
181
- // check for existing player
182
- if (typeof t.node.player != 'undefined') {
183
- return t.node.player;
184
- } else {
185
- // attach player to DOM node for reference
186
- t.node.player = t;
187
- }
188
-
189
-
190
- // try to get options from data-mejsoptions
191
- if (typeof o == 'undefined') {
192
- o = t.$node.data('mejsoptions');
193
- }
194
-
195
- // extend default options
196
- t.options = $.extend({},mejs.MepDefaults,o);
197
-
198
- // add to player array (for focus events)
199
- mejs.players.push(t);
200
-
201
- // start up
202
- t.init();
203
-
204
- return t;
205
- };
206
-
207
- // actual player
208
- mejs.MediaElementPlayer.prototype = {
209
-
210
- hasFocus: false,
211
-
212
- controlsAreVisible: true,
213
-
214
- init: function() {
215
-
216
- var
217
- t = this,
218
- mf = mejs.MediaFeatures,
219
- // options for MediaElement (shim)
220
- meOptions = $.extend(true, {}, t.options, {
221
- success: function(media, domNode) { t.meReady(media, domNode); },
222
- error: function(e) { t.handleError(e);}
223
- }),
224
- tagName = t.media.tagName.toLowerCase();
225
-
226
- t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
227
-
228
- if (t.isDynamic) {
229
- // get video from src or href?
230
- t.isVideo = t.options.isVideo;
231
- } else {
232
- t.isVideo = (tagName !== 'audio' && t.options.isVideo);
233
- }
234
-
235
- // use native controls in iPad, iPhone, and Android
236
- if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
237
-
238
- // add controls and stop
239
- t.$media.attr('controls', 'controls');
240
-
241
- // attempt to fix iOS 3 bug
242
- //t.$media.removeAttr('poster');
243
- // no Issue found on iOS3 -ttroxell
244
-
245
- // override Apple's autoplay override for iPads
246
- if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
247
- t.media.load();
248
- t.media.play();
249
- }
250
-
251
- } else if (mf.isAndroid && t.AndroidUseNativeControls) {
252
-
253
- // leave default player
254
-
255
- } else {
256
-
257
- // DESKTOP: use MediaElementPlayer controls
258
-
259
- // remove native controls
260
- t.$media.removeAttr('controls');
261
-
262
- // unique ID
263
- t.id = 'mep_' + mejs.mepIndex++;
264
-
265
- // build container
266
- t.container =
267
- $('<div id="' + t.id + '" class="mejs-container">'+
268
- '<div class="mejs-inner">'+
269
- '<div class="mejs-mediaelement"></div>'+
270
- '<div class="mejs-layers"></div>'+
271
- '<div class="mejs-controls"></div>'+
272
- '<div class="mejs-clear"></div>'+
273
- '</div>' +
274
- '</div>')
275
- .addClass(t.$media[0].className)
276
- .insertBefore(t.$media);
277
-
278
- // add classes for user and content
279
- t.container.addClass(
280
- (mf.isAndroid ? 'mejs-android ' : '') +
281
- (mf.isiOS ? 'mejs-ios ' : '') +
282
- (mf.isiPad ? 'mejs-ipad ' : '') +
283
- (mf.isiPhone ? 'mejs-iphone ' : '') +
284
- (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
285
- );
286
-
287
-
288
- // move the <video/video> tag into the right spot
289
- if (mf.isiOS) {
290
-
291
- // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
292
- var $newMedia = t.$media.clone();
293
-
294
- t.container.find('.mejs-mediaelement').append($newMedia);
295
-
296
- t.$media.remove();
297
- t.$node = t.$media = $newMedia;
298
- t.node = t.media = $newMedia[0]
299
-
300
- } else {
301
-
302
- // normal way of moving it into place (doesn't work on iOS)
303
- t.container.find('.mejs-mediaelement').append(t.$media);
304
- }
305
-
306
- // find parts
307
- t.controls = t.container.find('.mejs-controls');
308
- t.layers = t.container.find('.mejs-layers');
309
-
310
- // determine the size
311
-
312
- /* size priority:
313
- (1) videoWidth (forced),
314
- (2) style="width;height;"
315
- (3) width attribute,
316
- (4) defaultVideoWidth (for unspecified cases)
317
- */
318
-
319
- var tagType = (t.isVideo ? 'video' : 'audio'),
320
- capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
321
-
322
-
323
- if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
324
- t.width = t.options[tagType + 'Width'];
325
- } else if (t.media.style.width !== '' && t.media.style.width !== null) {
326
- t.width = t.media.style.width;
327
- } else if (t.media.getAttribute('width') !== null) {
328
- t.width = t.$media.attr('width');
329
- } else {
330
- t.width = t.options['default' + capsTagName + 'Width'];
331
- }
332
-
333
- if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
334
- t.height = t.options[tagType + 'Height'];
335
- } else if (t.media.style.height !== '' && t.media.style.height !== null) {
336
- t.height = t.media.style.height;
337
- } else if (t.$media[0].getAttribute('height') !== null) {
338
- t.height = t.$media.attr('height');
339
- } else {
340
- t.height = t.options['default' + capsTagName + 'Height'];
341
- }
342
-
343
- // set the size, while we wait for the plugins to load below
344
- t.setPlayerSize(t.width, t.height);
345
-
346
- // create MediaElementShim
347
- meOptions.pluginWidth = t.height;
348
- meOptions.pluginHeight = t.width;
349
- }
350
-
351
-
352
-
353
- // create MediaElement shim
354
- mejs.MediaElement(t.$media[0], meOptions);
355
- },
356
-
357
- showControls: function(doAnimation) {
358
- var t = this;
359
-
360
- doAnimation = typeof doAnimation == 'undefined' || doAnimation;
361
-
362
- if (t.controlsAreVisible)
363
- return;
364
-
365
- if (doAnimation) {
366
- t.controls
367
- .css('visibility','visible')
368
- .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
369
-
370
- // any additional controls people might add and want to hide
371
- t.container.find('.mejs-control')
372
- .css('visibility','visible')
373
- .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
374
-
375
- } else {
376
- t.controls
377
- .css('visibility','visible')
378
- .css('display','block');
379
-
380
- // any additional controls people might add and want to hide
381
- t.container.find('.mejs-control')
382
- .css('visibility','visible')
383
- .css('display','block');
384
-
385
- t.controlsAreVisible = true;
386
- }
387
-
388
- t.setControlsSize();
389
-
390
- },
391
-
392
- hideControls: function(doAnimation) {
393
- var t = this;
394
-
395
- doAnimation = typeof doAnimation == 'undefined' || doAnimation;
396
-
397
- if (!t.controlsAreVisible)
398
- return;
399
-
400
- if (doAnimation) {
401
- // fade out main controls
402
- t.controls.stop(true, true).fadeOut(200, function() {
403
- $(this)
404
- .css('visibility','hidden')
405
- .css('display','block');
406
-
407
- t.controlsAreVisible = false;
408
- });
409
-
410
- // any additional controls people might add and want to hide
411
- t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
412
- $(this)
413
- .css('visibility','hidden')
414
- .css('display','block');
415
- });
416
- } else {
417
-
418
- // hide main controls
419
- t.controls
420
- .css('visibility','hidden')
421
- .css('display','block');
422
-
423
- // hide others
424
- t.container.find('.mejs-control')
425
- .css('visibility','hidden')
426
- .css('display','block');
427
-
428
- t.controlsAreVisible = false;
429
- }
430
- },
431
-
432
- controlsTimer: null,
433
-
434
- startControlsTimer: function(timeout) {
435
-
436
- var t = this;
437
-
438
- timeout = typeof timeout != 'undefined' ? timeout : 1500;
439
-
440
- t.killControlsTimer('start');
441
-
442
- t.controlsTimer = setTimeout(function() {
443
- //console.log('timer fired');
444
- t.hideControls();
445
- t.killControlsTimer('hide');
446
- }, timeout);
447
- },
448
-
449
- killControlsTimer: function(src) {
450
-
451
- var t = this;
452
-
453
- if (t.controlsTimer !== null) {
454
- clearTimeout(t.controlsTimer);
455
- delete t.controlsTimer;
456
- t.controlsTimer = null;
457
- }
458
- },
459
-
460
- controlsEnabled: true,
461
-
462
- disableControls: function() {
463
- var t= this;
464
-
465
- t.killControlsTimer();
466
- t.hideControls(false);
467
- this.controlsEnabled = false;
468
- },
469
-
470
- enableControls: function() {
471
- var t= this;
472
-
473
- t.showControls(false);
474
-
475
- t.controlsEnabled = true;
476
- },
477
-
478
-
479
- // Sets up all controls and events
480
- meReady: function(media, domNode) {
481
-
482
-
483
- var t = this,
484
- mf = mejs.MediaFeatures,
485
- autoplayAttr = domNode.getAttribute('autoplay'),
486
- autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
487
- featureIndex,
488
- feature;
489
-
490
- // make sure it can't create itself again if a plugin reloads
491
- if (t.created)
492
- return;
493
- else
494
- t.created = true;
495
-
496
- t.media = media;
497
- t.domNode = domNode;
498
-
499
- if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
500
-
501
- // two built in features
502
- t.buildposter(t, t.controls, t.layers, t.media);
503
- t.buildkeyboard(t, t.controls, t.layers, t.media);
504
- t.buildoverlays(t, t.controls, t.layers, t.media);
505
-
506
- // grab for use by features
507
- t.findTracks();
508
-
509
- // add user-defined features/controls
510
- for (featureIndex in t.options.features) {
511
- feature = t.options.features[featureIndex];
512
- if (t['build' + feature]) {
513
- try {
514
- t['build' + feature](t, t.controls, t.layers, t.media);
515
- } catch (e) {
516
- // TODO: report control error
517
- //throw e;
518
- //console.log('error building ' + feature);
519
- //console.log(e);
520
- }
521
- }
522
- }
523
-
524
- t.container.trigger('controlsready');
525
-
526
- // reset all layers and controls
527
- t.setPlayerSize(t.width, t.height);
528
- t.setControlsSize();
529
-
530
-
531
- // controls fade
532
- if (t.isVideo) {
533
-
534
- if (mejs.MediaFeatures.hasTouch) {
535
-
536
- // for touch devices (iOS, Android)
537
- // show/hide without animation on touch
538
-
539
- t.$media.bind('touchstart', function() {
540
-
541
-
542
- // toggle controls
543
- if (t.controlsAreVisible) {
544
- t.hideControls(false);
545
- } else {
546
- if (t.controlsEnabled) {
547
- t.showControls(false);
548
- }
549
- }
550
- });
551
-
552
- } else {
553
- // click controls
554
- var clickElement = (t.media.pluginType == 'native') ? t.$media : $(t.media.pluginElement);
555
-
556
- // click to play/pause
557
- clickElement.click(function() {
558
- if (media.paused) {
559
- media.play();
560
- } else {
561
- media.pause();
562
- }
563
- });
564
-
565
-
566
- // show/hide controls
567
- t.container
568
- .bind('mouseenter mouseover', function () {
569
- if (t.controlsEnabled) {
570
- if (!t.options.alwaysShowControls) {
571
- t.killControlsTimer('enter');
572
- t.showControls();
573
- t.startControlsTimer(2500);
574
- }
575
- }
576
- })
577
- .bind('mousemove', function() {
578
- if (t.controlsEnabled) {
579
- if (!t.controlsAreVisible) {
580
- t.showControls();
581
- }
582
- //t.killControlsTimer('move');
583
- if (!t.options.alwaysShowControls) {
584
- t.startControlsTimer(2500);
585
- }
586
- }
587
- })
588
- .bind('mouseleave', function () {
589
- if (t.controlsEnabled) {
590
- if (!t.media.paused && !t.options.alwaysShowControls) {
591
- t.startControlsTimer(1000);
592
- }
593
- }
594
- });
595
- }
596
-
597
- // check for autoplay
598
- if (autoplay && !t.options.alwaysShowControls) {
599
- t.hideControls();
600
- }
601
-
602
- // resizer
603
- if (t.options.enableAutosize) {
604
- t.media.addEventListener('loadedmetadata', function(e) {
605
- // if the <video height> was not set and the options.videoHeight was not set
606
- // then resize to the real dimensions
607
- if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
608
- t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
609
- t.setControlsSize();
610
- t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
611
- }
612
- }, false);
613
- }
614
- }
615
-
616
- // EVENTS
617
-
618
- // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
619
- media.addEventListener('play', function() {
620
-
621
- // go through all other players
622
- for (var i=0, il=mejs.players.length; i<il; i++) {
623
- var p = mejs.players[i];
624
- if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
625
- p.pause();
626
- }
627
- p.hasFocus = false;
628
- }
629
-
630
- t.hasFocus = true;
631
- },false);
632
-
633
-
634
- // ended for all
635
- t.media.addEventListener('ended', function (e) {
636
- try{
637
- t.media.setCurrentTime(0);
638
- } catch (exp) {
639
-
640
- }
641
- t.media.pause();
642
-
643
- if (t.setProgressRail)
644
- t.setProgressRail();
645
- if (t.setCurrentRail)
646
- t.setCurrentRail();
647
-
648
- if (t.options.loop) {
649
- t.media.play();
650
- } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
651
- t.showControls();
652
- }
653
- }, false);
654
-
655
- // resize on the first play
656
- t.media.addEventListener('loadedmetadata', function(e) {
657
- if (t.updateDuration) {
658
- t.updateDuration();
659
- }
660
- if (t.updateCurrent) {
661
- t.updateCurrent();
662
- }
663
-
664
- if (!t.isFullScreen) {
665
- t.setPlayerSize(t.width, t.height);
666
- t.setControlsSize();
667
- }
668
- }, false);
669
-
670
-
671
- // webkit has trouble doing this without a delay
672
- setTimeout(function () {
673
- t.setPlayerSize(t.width, t.height);
674
- t.setControlsSize();
675
- }, 50);
676
-
677
- // adjust controls whenever window sizes (used to be in fullscreen only)
678
- $(window).resize(function() {
679
-
680
- // don't resize for fullscreen mode
681
- if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
682
- t.setPlayerSize(t.width, t.height);
683
- }
684
-
685
- // always adjust controls
686
- t.setControlsSize();
687
- });
688
-
689
- // TEMP: needs to be moved somewhere else
690
- if (t.media.pluginType == 'youtube') {
691
- t.container.find('.mejs-overlay-play').hide();
692
- }
693
- }
694
-
695
- // force autoplay for HTML5
696
- if (autoplay && media.pluginType == 'native') {
697
- media.load();
698
- media.play();
699
- }
700
-
701
-
702
- if (t.options.success) {
703
-
704
- if (typeof t.options.success == 'string') {
705
- window[t.options.success](t.media, t.domNode, t);
706
- } else {
707
- t.options.success(t.media, t.domNode, t);
708
- }
709
- }
710
- },
711
-
712
- handleError: function(e) {
713
- var t = this;
714
-
715
- t.controls.hide();
716
-
717
- // Tell user that the file cannot be played
718
- if (t.options.error) {
719
- t.options.error(e);
720
- }
721
- },
722
-
723
- setPlayerSize: function(width,height) {
724
- var t = this;
725
-
726
- if (typeof width != 'undefined')
727
- t.width = width;
728
-
729
- if (typeof height != 'undefined')
730
- t.height = height;
731
-
732
- // detect 100% mode
733
- if (t.height.toString().indexOf('%') > 0) {
734
-
735
- // do we have the native dimensions yet?
736
- var
737
- nativeWidth = (t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth,
738
- nativeHeight = (t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight,
739
- parentWidth = t.container.parent().width(),
740
- newHeight = parseInt(parentWidth * nativeHeight/nativeWidth, 10);
741
-
742
- if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
743
- parentWidth = $(window).width();
744
- newHeight = $(window).height();
745
- }
746
-
747
- if ( newHeight != 0 ) {
748
- // set outer container size
749
- t.container
750
- .width(parentWidth)
751
- .height(newHeight);
752
-
753
- // set native <video>
754
- t.$media
755
- .width('100%')
756
- .height('100%');
757
-
758
- // set shims
759
- t.container.find('object, embed, iframe')
760
- .width('100%')
761
- .height('100%');
762
-
763
- // if shim is ready, send the size to the embeded plugin
764
- if (t.isVideo) {
765
- if (t.media.setVideoSize) {
766
- t.media.setVideoSize(parentWidth, newHeight);
767
- }
768
- }
769
-
770
- // set the layers
771
- t.layers.children('.mejs-layer')
772
- .width('100%')
773
- .height('100%');
774
- }
775
-
776
-
777
- } else {
778
-
779
- t.container
780
- .width(t.width)
781
- .height(t.height);
782
-
783
- t.layers.children('.mejs-layer')
784
- .width(t.width)
785
- .height(t.height);
786
-
787
- }
788
- },
789
-
790
- setControlsSize: function() {
791
- var t = this,
792
- usedWidth = 0,
793
- railWidth = 0,
794
- rail = t.controls.find('.mejs-time-rail'),
795
- total = t.controls.find('.mejs-time-total'),
796
- current = t.controls.find('.mejs-time-current'),
797
- loaded = t.controls.find('.mejs-time-loaded'),
798
- others = rail.siblings();
799
-
800
-
801
- // allow the size to come from custom CSS
802
- if (t.options && !t.options.autosizeProgress) {
803
- // Also, frontends devs can be more flexible
804
- // due the opportunity of absolute positioning.
805
- railWidth = parseInt(rail.css('width'));
806
- }
807
-
808
- // attempt to autosize
809
- if (railWidth === 0 || !railWidth) {
810
-
811
- // find the size of all the other controls besides the rail
812
- others.each(function() {
813
- if ($(this).css('position') != 'absolute') {
814
- usedWidth += $(this).outerWidth(true);
815
- }
816
- });
817
-
818
- // fit the rail into the remaining space
819
- railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
820
- }
821
-
822
- // outer area
823
- rail.width(railWidth);
824
- // dark space
825
- total.width(railWidth - (total.outerWidth(true) - total.width()));
826
-
827
- if (t.setProgressRail)
828
- t.setProgressRail();
829
- if (t.setCurrentRail)
830
- t.setCurrentRail();
831
- },
832
-
833
-
834
- buildposter: function(player, controls, layers, media) {
835
- var t = this,
836
- poster =
837
- $('<div class="mejs-poster mejs-layer">' +
838
- '</div>')
839
- .appendTo(layers),
840
- posterUrl = player.$media.attr('poster');
841
-
842
- // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
843
- if (player.options.poster !== '') {
844
- posterUrl = player.options.poster;
845
- }
846
-
847
- // second, try the real poster
848
- if (posterUrl !== '' && posterUrl != null) {
849
- t.setPoster(posterUrl);
850
- } else {
851
- poster.hide();
852
- }
853
-
854
- media.addEventListener('play',function() {
855
- poster.hide();
856
- }, false);
857
- },
858
-
859
- setPoster: function(url) {
860
- var t = this,
861
- posterDiv = t.container.find('.mejs-poster'),
862
- posterImg = posterDiv.find('img');
863
-
864
- if (posterImg.length == 0) {
865
- posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
866
- }
867
-
868
- posterImg.attr('src', url);
869
- },
870
-
871
- buildoverlays: function(player, controls, layers, media) {
872
- if (!player.isVideo)
873
- return;
874
-
875
- var
876
- loading =
877
- $('<div class="mejs-overlay mejs-layer">'+
878
- '<div class="mejs-overlay-loading"><span></span></div>'+
879
- '</div>')
880
- .hide() // start out hidden
881
- .appendTo(layers),
882
- error =
883
- $('<div class="mejs-overlay mejs-layer">'+
884
- '<div class="mejs-overlay-error"></div>'+
885
- '</div>')
886
- .hide() // start out hidden
887
- .appendTo(layers),
888
- // this needs to come last so it's on top
889
- bigPlay =
890
- $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
891
- '<div class="mejs-overlay-button"></div>'+
892
- '</div>')
893
- .appendTo(layers)
894
- .click(function() {
895
- if (media.paused) {
896
- media.play();
897
- } else {
898
- media.pause();
899
- }
900
- });
901
-
902
- /*
903
- if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
904
- bigPlay.remove();
905
- loading.remove();
906
- }
907
- */
908
-
909
-
910
- // show/hide big play button
911
- media.addEventListener('play',function() {
912
- bigPlay.hide();
913
- loading.hide();
914
- controls.find('.mejs-time-buffering').hide();
915
- error.hide();
916
- }, false);
917
-
918
- media.addEventListener('playing', function() {
919
- bigPlay.hide();
920
- loading.hide();
921
- controls.find('.mejs-time-buffering').hide();
922
- error.hide();
923
- }, false);
924
-
925
- media.addEventListener('seeking', function() {
926
- loading.show();
927
- controls.find('.mejs-time-buffering').show();
928
- }, false);
929
-
930
- media.addEventListener('seeked', function() {
931
- loading.hide();
932
- controls.find('.mejs-time-buffering').hide();
933
- }, false);
934
-
935
- media.addEventListener('pause',function() {
936
- if (!mejs.MediaFeatures.isiPhone) {
937
- bigPlay.show();
938
- }
939
- }, false);
940
-
941
- media.addEventListener('waiting', function() {
942
- loading.show();
943
- controls.find('.mejs-time-buffering').show();
944
- }, false);
945
-
946
-
947
- // show/hide loading
948
- media.addEventListener('loadeddata',function() {
949
- // for some reason Chrome is firing this event
950
- //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
951
- // return;
952
-
953
- loading.show();
954
- controls.find('.mejs-time-buffering').show();
955
- }, false);
956
- media.addEventListener('canplay',function() {
957
- loading.hide();
958
- controls.find('.mejs-time-buffering').hide();
959
- }, false);
960
-
961
- // error handling
962
- media.addEventListener('error',function() {
963
- loading.hide();
964
- controls.find('.mejs-time-buffering').hide();
965
- error.show();
966
- error.find('mejs-overlay-error').html("Error loading this resource");
967
- }, false);
968
- },
969
-
970
- buildkeyboard: function(player, controls, layers, media) {
971
-
972
- var t = this;
973
-
974
- // listen for key presses
975
- $(document).keydown(function(e) {
976
-
977
- if (player.hasFocus && player.options.enableKeyboard) {
978
-
979
- // find a matching key
980
- for (var i=0, il=player.options.keyActions.length; i<il; i++) {
981
- var keyAction = player.options.keyActions[i];
982
-
983
- for (var j=0, jl=keyAction.keys.length; j<jl; j++) {
984
- if (e.keyCode == keyAction.keys[j]) {
985
- e.preventDefault();
986
- keyAction.action(player, media, e.keyCode);
987
- return false;
988
- }
989
- }
990
- }
991
- }
992
-
993
- return true;
994
- });
995
-
996
- // check if someone clicked outside a player region, then kill its focus
997
- $(document).click(function(event) {
998
- if ($(event.target).closest('.mejs-container').length == 0) {
999
- player.hasFocus = false;
1000
- }
1001
- });
1002
-
1003
- },
1004
-
1005
- findTracks: function() {
1006
- var t = this,
1007
- tracktags = t.$media.find('track');
1008
-
1009
- // store for use by plugins
1010
- t.tracks = [];
1011
- tracktags.each(function(index, track) {
1012
-
1013
- track = $(track);
1014
-
1015
- t.tracks.push({
1016
- srclang: track.attr('srclang').toLowerCase(),
1017
- src: track.attr('src'),
1018
- kind: track.attr('kind'),
1019
- label: track.attr('label') || '',
1020
- entries: [],
1021
- isLoaded: false
1022
- });
1023
- });
1024
- },
1025
- changeSkin: function(className) {
1026
- this.container[0].className = 'mejs-container ' + className;
1027
- this.setPlayerSize(this.width, this.height);
1028
- this.setControlsSize();
1029
- },
1030
- play: function() {
1031
- this.media.play();
1032
- },
1033
- pause: function() {
1034
- this.media.pause();
1035
- },
1036
- load: function() {
1037
- this.media.load();
1038
- },
1039
- setMuted: function(muted) {
1040
- this.media.setMuted(muted);
1041
- },
1042
- setCurrentTime: function(time) {
1043
- this.media.setCurrentTime(time);
1044
- },
1045
- getCurrentTime: function() {
1046
- return this.media.currentTime;
1047
- },
1048
- setVolume: function(volume) {
1049
- this.media.setVolume(volume);
1050
- },
1051
- getVolume: function() {
1052
- return this.media.volume;
1053
- },
1054
- setSrc: function(src) {
1055
- this.media.setSrc(src);
1056
- },
1057
- remove: function() {
1058
- var t = this;
1059
-
1060
- if (t.media.pluginType === 'flash') {
1061
- t.media.remove();
1062
- } else if (t.media.pluginType === 'native') {
1063
- t.$media.prop('controls', true);
1064
- }
1065
-
1066
- // grab video and put it back in place
1067
- if (!t.isDynamic) {
1068
- t.$node.insertBefore(t.container)
1069
- }
1070
-
1071
- t.container.remove();
1072
- }
1073
- };
1074
-
1075
- // turn into jQuery plugin
1076
- if (typeof jQuery != 'undefined') {
1077
- jQuery.fn.mediaelementplayer = function (options) {
1078
- return this.each(function () {
1079
- new mejs.MediaElementPlayer(this, options);
1080
- });
1081
- };
1082
- }
1083
-
1084
- $(document).ready(function() {
1085
- // auto enable using JSON attribute
1086
- $('.mejs-player').mediaelementplayer();
1087
- });
1088
-
1089
- // push out to window
1090
- window.MediaElementPlayer = mejs.MediaElementPlayer;
1091
-
1092
- })(mejs.$);
17
+ (function ($) {
18
+
19
+ // default player values
20
+ mejs.MepDefaults = {
21
+ // url to poster (to fix iOS 3.x)
22
+ poster: '',
23
+ // default if the <video width> is not specified
24
+ defaultVideoWidth: 480,
25
+ // default if the <video height> is not specified
26
+ defaultVideoHeight: 270,
27
+ // if set, overrides <video width>
28
+ videoWidth: -1,
29
+ // if set, overrides <video height>
30
+ videoHeight: -1,
31
+ // default if the user doesn't specify
32
+ defaultAudioWidth: 400,
33
+ // default if the user doesn't specify
34
+ defaultAudioHeight: 30,
35
+
36
+ // default amount to move back when back key is pressed
37
+ defaultSeekBackwardInterval: function(media) {
38
+ return (media.duration * 0.05);
39
+ },
40
+ // default amount to move forward when forward key is pressed
41
+ defaultSeekForwardInterval: function(media) {
42
+ return (media.duration * 0.05);
43
+ },
44
+
45
+ // width of audio player
46
+ audioWidth: -1,
47
+ // height of audio player
48
+ audioHeight: -1,
49
+ // initial volume when the player starts (overrided by user cookie)
50
+ startVolume: 0.8,
51
+ // useful for <audio> player loops
52
+ loop: false,
53
+ // rewind to beginning when media ends
54
+ autoRewind: true,
55
+ // resize to media dimensions
56
+ enableAutosize: true,
57
+ // forces the hour marker (##:00:00)
58
+ alwaysShowHours: false,
59
+
60
+ // show framecount in timecode (##:00:00:00)
61
+ showTimecodeFrameCount: false,
62
+ // used when showTimecodeFrameCount is set to true
63
+ framesPerSecond: 25,
64
+
65
+ // automatically calculate the width of the progress bar based on the sizes of other elements
66
+ autosizeProgress : true,
67
+ // Hide controls when playing and mouse is not over the video
68
+ alwaysShowControls: false,
69
+ // Enable click video element to toggle play/pause
70
+ clickToPlayPause: true,
71
+ // force iPad's native controls
72
+ iPadUseNativeControls: false,
73
+ // force iPhone's native controls
74
+ iPhoneUseNativeControls: false,
75
+ // force Android's native controls
76
+ AndroidUseNativeControls: false,
77
+ // features to show
78
+ features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
79
+ // only for dynamic
80
+ isVideo: true,
81
+
82
+ // turns keyboard support on and off for this instance
83
+ enableKeyboard: true,
84
+
85
+ // whenthis player starts, it will pause other players
86
+ pauseOtherPlayers: true,
87
+
88
+ // array of keyboard actions such as play pause
89
+ keyActions: [
90
+ {
91
+ keys: [
92
+ 32, // SPACE
93
+ 179 // GOOGLE play/pause button
94
+ ],
95
+ action: function(player, media) {
96
+ if (media.paused || media.ended) {
97
+ media.play();
98
+ } else {
99
+ media.pause();
100
+ }
101
+ }
102
+ },
103
+ {
104
+ keys: [38], // UP
105
+ action: function(player, media) {
106
+ var newVolume = Math.min(media.volume + 0.1, 1);
107
+ media.setVolume(newVolume);
108
+ }
109
+ },
110
+ {
111
+ keys: [40], // DOWN
112
+ action: function(player, media) {
113
+ var newVolume = Math.max(media.volume - 0.1, 0);
114
+ media.setVolume(newVolume);
115
+ }
116
+ },
117
+ {
118
+ keys: [
119
+ 37, // LEFT
120
+ 227 // Google TV rewind
121
+ ],
122
+ action: function(player, media) {
123
+ if (!isNaN(media.duration) && media.duration > 0) {
124
+ if (player.isVideo) {
125
+ player.showControls();
126
+ player.startControlsTimer();
127
+ }
128
+
129
+ // 5%
130
+ var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
131
+ media.setCurrentTime(newTime);
132
+ }
133
+ }
134
+ },
135
+ {
136
+ keys: [
137
+ 39, // RIGHT
138
+ 228 // Google TV forward
139
+ ],
140
+ action: function(player, media) {
141
+ if (!isNaN(media.duration) && media.duration > 0) {
142
+ if (player.isVideo) {
143
+ player.showControls();
144
+ player.startControlsTimer();
145
+ }
146
+
147
+ // 5%
148
+ var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
149
+ media.setCurrentTime(newTime);
150
+ }
151
+ }
152
+ },
153
+ {
154
+ keys: [70], // f
155
+ action: function(player, media) {
156
+ if (typeof player.enterFullScreen != 'undefined') {
157
+ if (player.isFullScreen) {
158
+ player.exitFullScreen();
159
+ } else {
160
+ player.enterFullScreen();
161
+ }
162
+ }
163
+ }
164
+ }
165
+ ]
166
+ };
167
+
168
+ mejs.mepIndex = 0;
169
+
170
+ mejs.players = [];
171
+
172
+ // wraps a MediaElement object in player controls
173
+ mejs.MediaElementPlayer = function(node, o) {
174
+ // enforce object, even without "new" (via John Resig)
175
+ if ( !(this instanceof mejs.MediaElementPlayer) ) {
176
+ return new mejs.MediaElementPlayer(node, o);
177
+ }
178
+
179
+ var t = this;
180
+
181
+ // these will be reset after the MediaElement.success fires
182
+ t.$media = t.$node = $(node);
183
+ t.node = t.media = t.$media[0];
184
+
185
+ // check for existing player
186
+ if (typeof t.node.player != 'undefined') {
187
+ return t.node.player;
188
+ } else {
189
+ // attach player to DOM node for reference
190
+ t.node.player = t;
191
+ }
192
+
193
+
194
+ // try to get options from data-mejsoptions
195
+ if (typeof o == 'undefined') {
196
+ o = t.$node.data('mejsoptions');
197
+ }
198
+
199
+ // extend default options
200
+ t.options = $.extend({},mejs.MepDefaults,o);
201
+
202
+ // add to player array (for focus events)
203
+ mejs.players.push(t);
204
+
205
+ // start up
206
+ t.init();
207
+
208
+ return t;
209
+ };
210
+
211
+ // actual player
212
+ mejs.MediaElementPlayer.prototype = {
213
+
214
+ hasFocus: false,
215
+
216
+ controlsAreVisible: true,
217
+
218
+ init: function() {
219
+
220
+ var
221
+ t = this,
222
+ mf = mejs.MediaFeatures,
223
+ // options for MediaElement (shim)
224
+ meOptions = $.extend(true, {}, t.options, {
225
+ success: function(media, domNode) { t.meReady(media, domNode); },
226
+ error: function(e) { t.handleError(e);}
227
+ }),
228
+ tagName = t.media.tagName.toLowerCase();
229
+
230
+ t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
231
+
232
+ if (t.isDynamic) {
233
+ // get video from src or href?
234
+ t.isVideo = t.options.isVideo;
235
+ } else {
236
+ t.isVideo = (tagName !== 'audio' && t.options.isVideo);
237
+ }
238
+
239
+ // use native controls in iPad, iPhone, and Android
240
+ if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
241
+
242
+ // add controls and stop
243
+ t.$media.attr('controls', 'controls');
244
+
245
+ // attempt to fix iOS 3 bug
246
+ //t.$media.removeAttr('poster');
247
+ // no Issue found on iOS3 -ttroxell
248
+
249
+ // override Apple's autoplay override for iPads
250
+ if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
251
+ t.media.load();
252
+ t.media.play();
253
+ }
254
+
255
+ } else if (mf.isAndroid && t.AndroidUseNativeControls) {
256
+
257
+ // leave default player
258
+
259
+ } else {
260
+
261
+ // DESKTOP: use MediaElementPlayer controls
262
+
263
+ // remove native controls
264
+ t.$media.removeAttr('controls');
265
+
266
+ // unique ID
267
+ t.id = 'mep_' + mejs.mepIndex++;
268
+
269
+ // build container
270
+ t.container =
271
+ $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') + '">'+
272
+ '<div class="mejs-inner">'+
273
+ '<div class="mejs-mediaelement"></div>'+
274
+ '<div class="mejs-layers"></div>'+
275
+ '<div class="mejs-controls"></div>'+
276
+ '<div class="mejs-clear"></div>'+
277
+ '</div>' +
278
+ '</div>')
279
+ .addClass(t.$media[0].className)
280
+ .insertBefore(t.$media);
281
+
282
+ // add classes for user and content
283
+ t.container.addClass(
284
+ (mf.isAndroid ? 'mejs-android ' : '') +
285
+ (mf.isiOS ? 'mejs-ios ' : '') +
286
+ (mf.isiPad ? 'mejs-ipad ' : '') +
287
+ (mf.isiPhone ? 'mejs-iphone ' : '') +
288
+ (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
289
+ );
290
+
291
+
292
+ // move the <video/video> tag into the right spot
293
+ if (mf.isiOS) {
294
+
295
+ // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
296
+ var $newMedia = t.$media.clone();
297
+
298
+ t.container.find('.mejs-mediaelement').append($newMedia);
299
+
300
+ t.$media.remove();
301
+ t.$node = t.$media = $newMedia;
302
+ t.node = t.media = $newMedia[0]
303
+
304
+ } else {
305
+
306
+ // normal way of moving it into place (doesn't work on iOS)
307
+ t.container.find('.mejs-mediaelement').append(t.$media);
308
+ }
309
+
310
+ // find parts
311
+ t.controls = t.container.find('.mejs-controls');
312
+ t.layers = t.container.find('.mejs-layers');
313
+
314
+ // determine the size
315
+
316
+ /* size priority:
317
+ (1) videoWidth (forced),
318
+ (2) style="width;height;"
319
+ (3) width attribute,
320
+ (4) defaultVideoWidth (for unspecified cases)
321
+ */
322
+
323
+ var tagType = (t.isVideo ? 'video' : 'audio'),
324
+ capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
325
+
326
+
327
+ if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
328
+ t.width = t.options[tagType + 'Width'];
329
+ } else if (t.media.style.width !== '' && t.media.style.width !== null) {
330
+ t.width = t.media.style.width;
331
+ } else if (t.media.getAttribute('width') !== null) {
332
+ t.width = t.$media.attr('width');
333
+ } else {
334
+ t.width = t.options['default' + capsTagName + 'Width'];
335
+ }
336
+
337
+ if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
338
+ t.height = t.options[tagType + 'Height'];
339
+ } else if (t.media.style.height !== '' && t.media.style.height !== null) {
340
+ t.height = t.media.style.height;
341
+ } else if (t.$media[0].getAttribute('height') !== null) {
342
+ t.height = t.$media.attr('height');
343
+ } else {
344
+ t.height = t.options['default' + capsTagName + 'Height'];
345
+ }
346
+
347
+ // set the size, while we wait for the plugins to load below
348
+ t.setPlayerSize(t.width, t.height);
349
+
350
+ // create MediaElementShim
351
+ meOptions.pluginWidth = t.height;
352
+ meOptions.pluginHeight = t.width;
353
+ }
354
+
355
+
356
+
357
+ // create MediaElement shim
358
+ mejs.MediaElement(t.$media[0], meOptions);
359
+
360
+ // controls are shown when loaded
361
+ t.container.trigger('controlsshown');
362
+ },
363
+
364
+ showControls: function(doAnimation) {
365
+ var t = this;
366
+
367
+ doAnimation = typeof doAnimation == 'undefined' || doAnimation;
368
+
369
+ if (t.controlsAreVisible)
370
+ return;
371
+
372
+ if (doAnimation) {
373
+ t.controls
374
+ .css('visibility','visible')
375
+ .stop(true, true).fadeIn(200, function() {
376
+ t.controlsAreVisible = true;
377
+ t.container.trigger('controlsshown');
378
+ });
379
+
380
+ // any additional controls people might add and want to hide
381
+ t.container.find('.mejs-control')
382
+ .css('visibility','visible')
383
+ .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
384
+
385
+ } else {
386
+ t.controls
387
+ .css('visibility','visible')
388
+ .css('display','block');
389
+
390
+ // any additional controls people might add and want to hide
391
+ t.container.find('.mejs-control')
392
+ .css('visibility','visible')
393
+ .css('display','block');
394
+
395
+ t.controlsAreVisible = true;
396
+ t.container.trigger('controlsshown');
397
+ }
398
+
399
+ t.setControlsSize();
400
+
401
+ },
402
+
403
+ hideControls: function(doAnimation) {
404
+ var t = this;
405
+
406
+ doAnimation = typeof doAnimation == 'undefined' || doAnimation;
407
+
408
+ if (!t.controlsAreVisible)
409
+ return;
410
+
411
+ if (doAnimation) {
412
+ // fade out main controls
413
+ t.controls.stop(true, true).fadeOut(200, function() {
414
+ $(this)
415
+ .css('visibility','hidden')
416
+ .css('display','block');
417
+
418
+ t.controlsAreVisible = false;
419
+ t.container.trigger('controlshidden');
420
+ });
421
+
422
+ // any additional controls people might add and want to hide
423
+ t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
424
+ $(this)
425
+ .css('visibility','hidden')
426
+ .css('display','block');
427
+ });
428
+ } else {
429
+
430
+ // hide main controls
431
+ t.controls
432
+ .css('visibility','hidden')
433
+ .css('display','block');
434
+
435
+ // hide others
436
+ t.container.find('.mejs-control')
437
+ .css('visibility','hidden')
438
+ .css('display','block');
439
+
440
+ t.controlsAreVisible = false;
441
+ t.container.trigger('controlshidden');
442
+ }
443
+ },
444
+
445
+ controlsTimer: null,
446
+
447
+ startControlsTimer: function(timeout) {
448
+
449
+ var t = this;
450
+
451
+ timeout = typeof timeout != 'undefined' ? timeout : 1500;
452
+
453
+ t.killControlsTimer('start');
454
+
455
+ t.controlsTimer = setTimeout(function() {
456
+ //console.log('timer fired');
457
+ t.hideControls();
458
+ t.killControlsTimer('hide');
459
+ }, timeout);
460
+ },
461
+
462
+ killControlsTimer: function(src) {
463
+
464
+ var t = this;
465
+
466
+ if (t.controlsTimer !== null) {
467
+ clearTimeout(t.controlsTimer);
468
+ delete t.controlsTimer;
469
+ t.controlsTimer = null;
470
+ }
471
+ },
472
+
473
+ controlsEnabled: true,
474
+
475
+ disableControls: function() {
476
+ var t= this;
477
+
478
+ t.killControlsTimer();
479
+ t.hideControls(false);
480
+ this.controlsEnabled = false;
481
+ },
482
+
483
+ enableControls: function() {
484
+ var t= this;
485
+
486
+ t.showControls(false);
487
+
488
+ t.controlsEnabled = true;
489
+ },
490
+
491
+
492
+ // Sets up all controls and events
493
+ meReady: function(media, domNode) {
494
+
495
+
496
+ var t = this,
497
+ mf = mejs.MediaFeatures,
498
+ autoplayAttr = domNode.getAttribute('autoplay'),
499
+ autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
500
+ featureIndex,
501
+ feature;
502
+
503
+ // make sure it can't create itself again if a plugin reloads
504
+ if (t.created)
505
+ return;
506
+ else
507
+ t.created = true;
508
+
509
+ t.media = media;
510
+ t.domNode = domNode;
511
+
512
+ if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
513
+
514
+ // two built in features
515
+ t.buildposter(t, t.controls, t.layers, t.media);
516
+ t.buildkeyboard(t, t.controls, t.layers, t.media);
517
+ t.buildoverlays(t, t.controls, t.layers, t.media);
518
+
519
+ // grab for use by features
520
+ t.findTracks();
521
+
522
+ // add user-defined features/controls
523
+ for (featureIndex in t.options.features) {
524
+ feature = t.options.features[featureIndex];
525
+ if (t['build' + feature]) {
526
+ try {
527
+ t['build' + feature](t, t.controls, t.layers, t.media);
528
+ } catch (e) {
529
+ // TODO: report control error
530
+ //throw e;
531
+ //console.log('error building ' + feature);
532
+ //console.log(e);
533
+ }
534
+ }
535
+ }
536
+
537
+ t.container.trigger('controlsready');
538
+
539
+ // reset all layers and controls
540
+ t.setPlayerSize(t.width, t.height);
541
+ t.setControlsSize();
542
+
543
+
544
+ // controls fade
545
+ if (t.isVideo) {
546
+
547
+ if (mejs.MediaFeatures.hasTouch) {
548
+
549
+ // for touch devices (iOS, Android)
550
+ // show/hide without animation on touch
551
+
552
+ t.$media.bind('touchstart', function() {
553
+
554
+
555
+ // toggle controls
556
+ if (t.controlsAreVisible) {
557
+ t.hideControls(false);
558
+ } else {
559
+ if (t.controlsEnabled) {
560
+ t.showControls(false);
561
+ }
562
+ }
563
+ });
564
+
565
+ } else {
566
+ // click to play/pause
567
+ t.media.addEventListener('click', function() {
568
+ if (t.options.clickToPlayPause) {
569
+ if (t.media.paused) {
570
+ t.media.play();
571
+ } else {
572
+ t.media.pause();
573
+ }
574
+ }
575
+ });
576
+
577
+ // show/hide controls
578
+ t.container
579
+ .bind('mouseenter mouseover', function () {
580
+ if (t.controlsEnabled) {
581
+ if (!t.options.alwaysShowControls) {
582
+ t.killControlsTimer('enter');
583
+ t.showControls();
584
+ t.startControlsTimer(2500);
585
+ }
586
+ }
587
+ })
588
+ .bind('mousemove', function() {
589
+ if (t.controlsEnabled) {
590
+ if (!t.controlsAreVisible) {
591
+ t.showControls();
592
+ }
593
+ //t.killControlsTimer('move');
594
+ if (!t.options.alwaysShowControls) {
595
+ t.startControlsTimer(2500);
596
+ }
597
+ }
598
+ })
599
+ .bind('mouseleave', function () {
600
+ if (t.controlsEnabled) {
601
+ if (!t.media.paused && !t.options.alwaysShowControls) {
602
+ t.startControlsTimer(1000);
603
+ }
604
+ }
605
+ });
606
+ }
607
+
608
+ // check for autoplay
609
+ if (autoplay && !t.options.alwaysShowControls) {
610
+ t.hideControls();
611
+ }
612
+
613
+ // resizer
614
+ if (t.options.enableAutosize) {
615
+ t.media.addEventListener('loadedmetadata', function(e) {
616
+ // if the <video height> was not set and the options.videoHeight was not set
617
+ // then resize to the real dimensions
618
+ if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
619
+ t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
620
+ t.setControlsSize();
621
+ t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
622
+ }
623
+ }, false);
624
+ }
625
+ }
626
+
627
+ // EVENTS
628
+
629
+ // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
630
+ media.addEventListener('play', function() {
631
+
632
+ // go through all other players
633
+ for (var i=0, il=mejs.players.length; i<il; i++) {
634
+ var p = mejs.players[i];
635
+ if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
636
+ p.pause();
637
+ }
638
+ p.hasFocus = false;
639
+ }
640
+
641
+ t.hasFocus = true;
642
+ },false);
643
+
644
+
645
+ // ended for all
646
+ t.media.addEventListener('ended', function (e) {
647
+ if(t.options.autoRewind) {
648
+ try{
649
+ t.media.setCurrentTime(0);
650
+ } catch (exp) {
651
+
652
+ }
653
+ }
654
+ t.media.pause();
655
+
656
+ if (t.setProgressRail)
657
+ t.setProgressRail();
658
+ if (t.setCurrentRail)
659
+ t.setCurrentRail();
660
+
661
+ if (t.options.loop) {
662
+ t.media.play();
663
+ } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
664
+ t.showControls();
665
+ }
666
+ }, false);
667
+
668
+ // resize on the first play
669
+ t.media.addEventListener('loadedmetadata', function(e) {
670
+ if (t.updateDuration) {
671
+ t.updateDuration();
672
+ }
673
+ if (t.updateCurrent) {
674
+ t.updateCurrent();
675
+ }
676
+
677
+ if (!t.isFullScreen) {
678
+ t.setPlayerSize(t.width, t.height);
679
+ t.setControlsSize();
680
+ }
681
+ }, false);
682
+
683
+
684
+ // webkit has trouble doing this without a delay
685
+ setTimeout(function () {
686
+ t.setPlayerSize(t.width, t.height);
687
+ t.setControlsSize();
688
+ }, 50);
689
+
690
+ // adjust controls whenever window sizes (used to be in fullscreen only)
691
+ $(window).resize(function() {
692
+
693
+ // don't resize for fullscreen mode
694
+ if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
695
+ t.setPlayerSize(t.width, t.height);
696
+ }
697
+
698
+ // always adjust controls
699
+ t.setControlsSize();
700
+ });
701
+
702
+ // TEMP: needs to be moved somewhere else
703
+ if (t.media.pluginType == 'youtube') {
704
+ t.container.find('.mejs-overlay-play').hide();
705
+ }
706
+ }
707
+
708
+ // force autoplay for HTML5
709
+ if (autoplay && media.pluginType == 'native') {
710
+ media.load();
711
+ media.play();
712
+ }
713
+
714
+
715
+ if (t.options.success) {
716
+
717
+ if (typeof t.options.success == 'string') {
718
+ window[t.options.success](t.media, t.domNode, t);
719
+ } else {
720
+ t.options.success(t.media, t.domNode, t);
721
+ }
722
+ }
723
+ },
724
+
725
+ handleError: function(e) {
726
+ var t = this;
727
+
728
+ t.controls.hide();
729
+
730
+ // Tell user that the file cannot be played
731
+ if (t.options.error) {
732
+ t.options.error(e);
733
+ }
734
+ },
735
+
736
+ setPlayerSize: function(width,height) {
737
+ var t = this;
738
+
739
+ if (typeof width != 'undefined')
740
+ t.width = width;
741
+
742
+ if (typeof height != 'undefined')
743
+ t.height = height;
744
+
745
+ // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
746
+ if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
747
+
748
+ // do we have the native dimensions yet?
749
+ var
750
+ nativeWidth = t.isVideo ? ((t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth) : t.options.defaultAudioWidth,
751
+ nativeHeight = t.isVideo ? ((t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight) : t.options.defaultAudioHeight,
752
+ parentWidth = t.container.parent().closest(':visible').width(),
753
+ newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
754
+
755
+ if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
756
+ parentWidth = $(window).width();
757
+ newHeight = $(window).height();
758
+ }
759
+
760
+ if ( newHeight != 0 && parentWidth != 0 ) {
761
+ // set outer container size
762
+ t.container
763
+ .width(parentWidth)
764
+ .height(newHeight);
765
+
766
+ // set native <video> or <audio>
767
+ t.$media
768
+ .width('100%')
769
+ .height('100%');
770
+
771
+ // set shims
772
+ t.container.find('object, embed, iframe')
773
+ .width('100%')
774
+ .height('100%');
775
+
776
+ // if shim is ready, send the size to the embeded plugin
777
+ if (t.isVideo) {
778
+ if (t.media.setVideoSize) {
779
+ t.media.setVideoSize(parentWidth, newHeight);
780
+ }
781
+ }
782
+
783
+ // set the layers
784
+ t.layers.children('.mejs-layer')
785
+ .width('100%')
786
+ .height('100%');
787
+ }
788
+
789
+
790
+ } else {
791
+
792
+ t.container
793
+ .width(t.width)
794
+ .height(t.height);
795
+
796
+ t.layers.children('.mejs-layer')
797
+ .width(t.width)
798
+ .height(t.height);
799
+
800
+ }
801
+ },
802
+
803
+ setControlsSize: function() {
804
+ var t = this,
805
+ usedWidth = 0,
806
+ railWidth = 0,
807
+ rail = t.controls.find('.mejs-time-rail'),
808
+ total = t.controls.find('.mejs-time-total'),
809
+ current = t.controls.find('.mejs-time-current'),
810
+ loaded = t.controls.find('.mejs-time-loaded'),
811
+ others = rail.siblings();
812
+
813
+
814
+ // allow the size to come from custom CSS
815
+ if (t.options && !t.options.autosizeProgress) {
816
+ // Also, frontends devs can be more flexible
817
+ // due the opportunity of absolute positioning.
818
+ railWidth = parseInt(rail.css('width'));
819
+ }
820
+
821
+ // attempt to autosize
822
+ if (railWidth === 0 || !railWidth) {
823
+
824
+ // find the size of all the other controls besides the rail
825
+ others.each(function() {
826
+ if ($(this).css('position') != 'absolute') {
827
+ usedWidth += $(this).outerWidth(true);
828
+ }
829
+ });
830
+
831
+ // fit the rail into the remaining space
832
+ railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
833
+ }
834
+
835
+ // outer area
836
+ rail.width(railWidth);
837
+ // dark space
838
+ total.width(railWidth - (total.outerWidth(true) - total.width()));
839
+
840
+ if (t.setProgressRail)
841
+ t.setProgressRail();
842
+ if (t.setCurrentRail)
843
+ t.setCurrentRail();
844
+ },
845
+
846
+
847
+ buildposter: function(player, controls, layers, media) {
848
+ var t = this,
849
+ poster =
850
+ $('<div class="mejs-poster mejs-layer">' +
851
+ '</div>')
852
+ .appendTo(layers),
853
+ posterUrl = player.$media.attr('poster');
854
+
855
+ // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
856
+ if (player.options.poster !== '') {
857
+ posterUrl = player.options.poster;
858
+ }
859
+
860
+ // second, try the real poster
861
+ if (posterUrl !== '' && posterUrl != null) {
862
+ t.setPoster(posterUrl);
863
+ } else {
864
+ poster.hide();
865
+ }
866
+
867
+ media.addEventListener('play',function() {
868
+ poster.hide();
869
+ }, false);
870
+ },
871
+
872
+ setPoster: function(url) {
873
+ var t = this,
874
+ posterDiv = t.container.find('.mejs-poster'),
875
+ posterImg = posterDiv.find('img');
876
+
877
+ if (posterImg.length == 0) {
878
+ posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
879
+ }
880
+
881
+ posterImg.attr('src', url);
882
+ },
883
+
884
+ buildoverlays: function(player, controls, layers, media) {
885
+ var t = this;
886
+ if (!player.isVideo)
887
+ return;
888
+
889
+ var
890
+ loading =
891
+ $('<div class="mejs-overlay mejs-layer">'+
892
+ '<div class="mejs-overlay-loading"><span></span></div>'+
893
+ '</div>')
894
+ .hide() // start out hidden
895
+ .appendTo(layers),
896
+ error =
897
+ $('<div class="mejs-overlay mejs-layer">'+
898
+ '<div class="mejs-overlay-error"></div>'+
899
+ '</div>')
900
+ .hide() // start out hidden
901
+ .appendTo(layers),
902
+ // this needs to come last so it's on top
903
+ bigPlay =
904
+ $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
905
+ '<div class="mejs-overlay-button"></div>'+
906
+ '</div>')
907
+ .appendTo(layers)
908
+ .click(function() {
909
+ if (t.options.clickToPlayPause) {
910
+ if (media.paused) {
911
+ media.play();
912
+ } else {
913
+ media.pause();
914
+ }
915
+ }
916
+ });
917
+
918
+ /*
919
+ if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
920
+ bigPlay.remove();
921
+ loading.remove();
922
+ }
923
+ */
924
+
925
+
926
+ // show/hide big play button
927
+ media.addEventListener('play',function() {
928
+ bigPlay.hide();
929
+ loading.hide();
930
+ controls.find('.mejs-time-buffering').hide();
931
+ error.hide();
932
+ }, false);
933
+
934
+ media.addEventListener('playing', function() {
935
+ bigPlay.hide();
936
+ loading.hide();
937
+ controls.find('.mejs-time-buffering').hide();
938
+ error.hide();
939
+ }, false);
940
+
941
+ media.addEventListener('seeking', function() {
942
+ loading.show();
943
+ controls.find('.mejs-time-buffering').show();
944
+ }, false);
945
+
946
+ media.addEventListener('seeked', function() {
947
+ loading.hide();
948
+ controls.find('.mejs-time-buffering').hide();
949
+ }, false);
950
+
951
+ media.addEventListener('pause',function() {
952
+ if (!mejs.MediaFeatures.isiPhone) {
953
+ bigPlay.show();
954
+ }
955
+ }, false);
956
+
957
+ media.addEventListener('waiting', function() {
958
+ loading.show();
959
+ controls.find('.mejs-time-buffering').show();
960
+ }, false);
961
+
962
+
963
+ // show/hide loading
964
+ media.addEventListener('loadeddata',function() {
965
+ // for some reason Chrome is firing this event
966
+ //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
967
+ // return;
968
+
969
+ loading.show();
970
+ controls.find('.mejs-time-buffering').show();
971
+ }, false);
972
+ media.addEventListener('canplay',function() {
973
+ loading.hide();
974
+ controls.find('.mejs-time-buffering').hide();
975
+ }, false);
976
+
977
+ // error handling
978
+ media.addEventListener('error',function() {
979
+ loading.hide();
980
+ controls.find('.mejs-time-buffering').hide();
981
+ error.show();
982
+ error.find('mejs-overlay-error').html("Error loading this resource");
983
+ }, false);
984
+ },
985
+
986
+ buildkeyboard: function(player, controls, layers, media) {
987
+
988
+ var t = this;
989
+
990
+ // listen for key presses
991
+ $(document).keydown(function(e) {
992
+
993
+ if (player.hasFocus && player.options.enableKeyboard) {
994
+
995
+ // find a matching key
996
+ for (var i=0, il=player.options.keyActions.length; i<il; i++) {
997
+ var keyAction = player.options.keyActions[i];
998
+
999
+ for (var j=0, jl=keyAction.keys.length; j<jl; j++) {
1000
+ if (e.keyCode == keyAction.keys[j]) {
1001
+ e.preventDefault();
1002
+ keyAction.action(player, media, e.keyCode);
1003
+ return false;
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+
1009
+ return true;
1010
+ });
1011
+
1012
+ // check if someone clicked outside a player region, then kill its focus
1013
+ $(document).click(function(event) {
1014
+ if ($(event.target).closest('.mejs-container').length == 0) {
1015
+ player.hasFocus = false;
1016
+ }
1017
+ });
1018
+
1019
+ },
1020
+
1021
+ findTracks: function() {
1022
+ var t = this,
1023
+ tracktags = t.$media.find('track');
1024
+
1025
+ // store for use by plugins
1026
+ t.tracks = [];
1027
+ tracktags.each(function(index, track) {
1028
+
1029
+ track = $(track);
1030
+
1031
+ t.tracks.push({
1032
+ srclang: track.attr('srclang').toLowerCase(),
1033
+ src: track.attr('src'),
1034
+ kind: track.attr('kind'),
1035
+ label: track.attr('label') || '',
1036
+ entries: [],
1037
+ isLoaded: false
1038
+ });
1039
+ });
1040
+ },
1041
+ changeSkin: function(className) {
1042
+ this.container[0].className = 'mejs-container ' + className;
1043
+ this.setPlayerSize(this.width, this.height);
1044
+ this.setControlsSize();
1045
+ },
1046
+ play: function() {
1047
+ this.media.play();
1048
+ },
1049
+ pause: function() {
1050
+ this.media.pause();
1051
+ },
1052
+ load: function() {
1053
+ this.media.load();
1054
+ },
1055
+ setMuted: function(muted) {
1056
+ this.media.setMuted(muted);
1057
+ },
1058
+ setCurrentTime: function(time) {
1059
+ this.media.setCurrentTime(time);
1060
+ },
1061
+ getCurrentTime: function() {
1062
+ return this.media.currentTime;
1063
+ },
1064
+ setVolume: function(volume) {
1065
+ this.media.setVolume(volume);
1066
+ },
1067
+ getVolume: function() {
1068
+ return this.media.volume;
1069
+ },
1070
+ setSrc: function(src) {
1071
+ this.media.setSrc(src);
1072
+ },
1073
+ remove: function() {
1074
+ var t = this;
1075
+
1076
+ if (t.media.pluginType === 'flash') {
1077
+ t.media.remove();
1078
+ } else if (t.media.pluginType === 'native') {
1079
+ t.$media.prop('controls', true);
1080
+ }
1081
+
1082
+ // grab video and put it back in place
1083
+ if (!t.isDynamic) {
1084
+ t.$node.insertBefore(t.container)
1085
+ }
1086
+
1087
+ t.container.remove();
1088
+ }
1089
+ };
1090
+
1091
+ // turn into jQuery plugin
1092
+ if (typeof jQuery != 'undefined') {
1093
+ jQuery.fn.mediaelementplayer = function (options) {
1094
+ return this.each(function () {
1095
+ new mejs.MediaElementPlayer(this, options);
1096
+ });
1097
+ };
1098
+ }
1099
+
1100
+ $(document).ready(function() {
1101
+ // auto enable using JSON attribute
1102
+ $('.mejs-player').mediaelementplayer();
1103
+ });
1104
+
1105
+ // push out to window
1106
+ window.MediaElementPlayer = mejs.MediaElementPlayer;
1107
+
1108
+ })(mejs.$);
1093
1109
 
1094
1110
  (function($) {
1095
1111
 
@@ -1157,7 +1173,8 @@ if (typeof jQuery != 'undefined') {
1157
1173
  media.pause();
1158
1174
  }
1159
1175
  if (media.currentTime > 0) {
1160
- media.setCurrentTime(0);
1176
+ media.setCurrentTime(0);
1177
+ media.pause();
1161
1178
  controls.find('.mejs-time-current').width('0px');
1162
1179
  controls.find('.mejs-time-handle').css('left', '0px');
1163
1180
  controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
@@ -1201,18 +1218,25 @@ if (typeof jQuery != 'undefined') {
1201
1218
  // mouse position relative to the object
1202
1219
  var x = e.pageX,
1203
1220
  offset = total.offset(),
1204
- width = total.outerWidth(),
1221
+ width = total.outerWidth(true),
1205
1222
  percentage = 0,
1206
1223
  newTime = 0,
1207
- pos = x - offset.left;
1224
+ pos = 0;
1208
1225
 
1209
1226
 
1210
- if (x > offset.left && x <= width + offset.left && media.duration) {
1211
- percentage = ((x - offset.left) / width);
1227
+ if (media.duration) {
1228
+ if (x < offset.left) {
1229
+ x = offset.left;
1230
+ } else if (x > width + offset.left) {
1231
+ x = width + offset.left;
1232
+ }
1233
+
1234
+ pos = x - offset.left;
1235
+ percentage = (pos / width);
1212
1236
  newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
1213
1237
 
1214
1238
  // seek to where the mouse is
1215
- if (mouseIsDown) {
1239
+ if (mouseIsDown && newTime !== media.currentTime) {
1216
1240
  media.setCurrentTime(newTime);
1217
1241
  }
1218
1242
 
@@ -1336,86 +1360,90 @@ if (typeof jQuery != 'undefined') {
1336
1360
  }
1337
1361
  });
1338
1362
  })(mejs.$);
1339
- (function($) {
1340
-
1341
- // options
1342
- $.extend(mejs.MepDefaults, {
1343
- duration: -1,
1344
- timeAndDurationSeparator: ' <span> | </span> '
1345
- });
1346
-
1347
-
1348
- // current and duration 00:00 / 00:00
1349
- $.extend(MediaElementPlayer.prototype, {
1350
- buildcurrent: function(player, controls, layers, media) {
1351
- var t = this;
1352
-
1353
- $('<div class="mejs-time">'+
1354
- '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '')
1355
- + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ '</span>'+
1356
- '</div>')
1357
- .appendTo(controls);
1358
-
1359
- t.currenttime = t.controls.find('.mejs-currenttime');
1360
-
1361
- media.addEventListener('timeupdate',function() {
1362
- player.updateCurrent();
1363
- }, false);
1364
- },
1365
-
1366
-
1367
- buildduration: function(player, controls, layers, media) {
1368
- var t = this;
1369
-
1370
- if (controls.children().last().find('.mejs-currenttime').length > 0) {
1371
- $(t.options.timeAndDurationSeparator +
1372
- '<span class="mejs-duration">' +
1373
- (t.options.duration > 0 ?
1374
- mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1375
- ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1376
- ) +
1377
- '</span>')
1378
- .appendTo(controls.find('.mejs-time'));
1379
- } else {
1380
-
1381
- // add class to current time
1382
- controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
1383
-
1384
- $('<div class="mejs-time mejs-duration-container">'+
1385
- '<span class="mejs-duration">' +
1386
- (t.options.duration > 0 ?
1387
- mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1388
- ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1389
- ) +
1390
- '</span>' +
1391
- '</div>')
1392
- .appendTo(controls);
1393
- }
1394
-
1395
- t.durationD = t.controls.find('.mejs-duration');
1396
-
1397
- media.addEventListener('timeupdate',function() {
1398
- player.updateDuration();
1399
- }, false);
1400
- },
1401
-
1402
- updateCurrent: function() {
1403
- var t = this;
1404
-
1405
- if (t.currenttime) {
1406
- t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
1407
- }
1408
- },
1409
-
1410
- updateDuration: function() {
1411
- var t = this;
1412
-
1413
- if (t.media.duration && t.durationD) {
1414
- t.durationD.html(mejs.Utility.secondsToTimeCode(t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
1415
- }
1416
- }
1417
- });
1418
-
1363
+
1364
+ (function($) {
1365
+
1366
+ // options
1367
+ $.extend(mejs.MepDefaults, {
1368
+ duration: -1,
1369
+ timeAndDurationSeparator: ' <span> | </span> '
1370
+ });
1371
+
1372
+
1373
+ // current and duration 00:00 / 00:00
1374
+ $.extend(MediaElementPlayer.prototype, {
1375
+ buildcurrent: function(player, controls, layers, media) {
1376
+ var t = this;
1377
+
1378
+ $('<div class="mejs-time">'+
1379
+ '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '')
1380
+ + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ '</span>'+
1381
+ '</div>')
1382
+ .appendTo(controls);
1383
+
1384
+ t.currenttime = t.controls.find('.mejs-currenttime');
1385
+
1386
+ media.addEventListener('timeupdate',function() {
1387
+ player.updateCurrent();
1388
+ }, false);
1389
+ },
1390
+
1391
+
1392
+ buildduration: function(player, controls, layers, media) {
1393
+ var t = this;
1394
+
1395
+ if (controls.children().last().find('.mejs-currenttime').length > 0) {
1396
+ $(t.options.timeAndDurationSeparator +
1397
+ '<span class="mejs-duration">' +
1398
+ (t.options.duration > 0 ?
1399
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1400
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1401
+ ) +
1402
+ '</span>')
1403
+ .appendTo(controls.find('.mejs-time'));
1404
+ } else {
1405
+
1406
+ // add class to current time
1407
+ controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
1408
+
1409
+ $('<div class="mejs-time mejs-duration-container">'+
1410
+ '<span class="mejs-duration">' +
1411
+ (t.options.duration > 0 ?
1412
+ mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1413
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1414
+ ) +
1415
+ '</span>' +
1416
+ '</div>')
1417
+ .appendTo(controls);
1418
+ }
1419
+
1420
+ t.durationD = t.controls.find('.mejs-duration');
1421
+
1422
+ media.addEventListener('timeupdate',function() {
1423
+ player.updateDuration();
1424
+ }, false);
1425
+ },
1426
+
1427
+ updateCurrent: function() {
1428
+ var t = this;
1429
+
1430
+ if (t.currenttime) {
1431
+ t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
1432
+ }
1433
+ },
1434
+
1435
+ updateDuration: function() {
1436
+ var t = this;
1437
+
1438
+ //Toggle the long video class if the video is longer than an hour.
1439
+ t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
1440
+
1441
+ if (t.media.duration && t.durationD) {
1442
+ t.durationD.html(mejs.Utility.secondsToTimeCode(t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
1443
+ }
1444
+ }
1445
+ });
1446
+
1419
1447
  })(mejs.$);
1420
1448
  (function($) {
1421
1449
 
@@ -1467,7 +1495,7 @@ if (typeof jQuery != 'undefined') {
1467
1495
 
1468
1496
  positionVolumeHandle = function(volume, secondTry) {
1469
1497
 
1470
- if (!volumeSlider.is(':visible') && typeof secondTry != 'undefined') {
1498
+ if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
1471
1499
  volumeSlider.show();
1472
1500
  positionVolumeHandle(volume, true);
1473
1501
  volumeSlider.hide()
@@ -1500,7 +1528,7 @@ if (typeof jQuery != 'undefined') {
1500
1528
  newTop = totalHeight - (totalHeight * volume);
1501
1529
 
1502
1530
  // handle
1503
- volumeHandle.css('top', totalPosition.top + newTop - (volumeHandle.height() / 2));
1531
+ volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
1504
1532
 
1505
1533
  // show the current visibility
1506
1534
  volumeCurrent.height(totalHeight - newTop );
@@ -1518,10 +1546,10 @@ if (typeof jQuery != 'undefined') {
1518
1546
  newLeft = totalWidth * volume;
1519
1547
 
1520
1548
  // handle
1521
- volumeHandle.css('left', totalPosition.left + newLeft - (volumeHandle.width() / 2));
1549
+ volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
1522
1550
 
1523
1551
  // rezize the current part of the volume bar
1524
- volumeCurrent.width( newLeft );
1552
+ volumeCurrent.width( Math.round(newLeft) );
1525
1553
  }
1526
1554
  },
1527
1555
  handleVolumeMove = function(e) {
@@ -1597,485 +1625,485 @@ if (typeof jQuery != 'undefined') {
1597
1625
  mouseIsDown = false;
1598
1626
  $(document).unbind('.vol');
1599
1627
 
1600
- if (!mouseIsOver && mode == 'vertical') {
1601
- volumeSlider.hide();
1628
+ if (!mouseIsOver && mode == 'vertical') {
1629
+ volumeSlider.hide();
1630
+ }
1631
+ });
1632
+ mouseIsDown = true;
1633
+
1634
+ return false;
1635
+ });
1636
+
1637
+
1638
+ // MUTE button
1639
+ mute.find('button').click(function() {
1640
+ media.setMuted( !media.muted );
1641
+ });
1642
+
1643
+ // listen for volume change events from other sources
1644
+ media.addEventListener('volumechange', function(e) {
1645
+ if (!mouseIsDown) {
1646
+ if (media.muted) {
1647
+ positionVolumeHandle(0);
1648
+ mute.removeClass('mejs-mute').addClass('mejs-unmute');
1649
+ } else {
1650
+ positionVolumeHandle(media.volume);
1651
+ mute.removeClass('mejs-unmute').addClass('mejs-mute');
1652
+ }
1653
+ }
1654
+ }, false);
1655
+
1656
+ if (t.container.is(':visible')) {
1657
+ // set initial volume
1658
+ positionVolumeHandle(player.options.startVolume);
1659
+
1660
+ // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
1661
+ if (media.pluginType === 'native') {
1662
+ media.setVolume(player.options.startVolume);
1663
+ }
1664
+ }
1665
+ }
1666
+ });
1667
+
1668
+ })(mejs.$);
1669
+
1670
+ (function($) {
1671
+
1672
+ $.extend(mejs.MepDefaults, {
1673
+ usePluginFullScreen: true,
1674
+ newWindowCallback: function() { return '';},
1675
+ fullscreenText: mejs.i18n.t('Fullscreen')
1676
+ });
1677
+
1678
+ $.extend(MediaElementPlayer.prototype, {
1679
+
1680
+ isFullScreen: false,
1681
+
1682
+ isNativeFullScreen: false,
1683
+
1684
+ docStyleOverflow: null,
1685
+
1686
+ isInIframe: false,
1687
+
1688
+ buildfullscreen: function(player, controls, layers, media) {
1689
+
1690
+ if (!player.isVideo)
1691
+ return;
1692
+
1693
+ player.isInIframe = (window.location != window.parent.location);
1694
+
1695
+ // native events
1696
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
1697
+
1698
+ // chrome doesn't alays fire this in an iframe
1699
+ var target = null;
1700
+
1701
+ if (mejs.MediaFeatures.hasMozNativeFullScreen) {
1702
+ target = $(document);
1703
+ } else {
1704
+ target = player.container;
1705
+ }
1706
+
1707
+ target.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
1708
+
1709
+ if (mejs.MediaFeatures.isFullScreen()) {
1710
+ player.isNativeFullScreen = true;
1711
+ // reset the controls once we are fully in full screen
1712
+ player.setControlsSize();
1713
+ } else {
1714
+ player.isNativeFullScreen = false;
1715
+ // when a user presses ESC
1716
+ // make sure to put the player back into place
1717
+ player.exitFullScreen();
1718
+ }
1719
+ });
1720
+ }
1721
+
1722
+ var t = this,
1723
+ normalHeight = 0,
1724
+ normalWidth = 0,
1725
+ container = player.container,
1726
+ fullscreenBtn =
1727
+ $('<div class="mejs-button mejs-fullscreen-button">' +
1728
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '"></button>' +
1729
+ '</div>')
1730
+ .appendTo(controls);
1731
+
1732
+ if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
1733
+
1734
+ fullscreenBtn.click(function() {
1735
+ var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
1736
+
1737
+ if (isFullScreen) {
1738
+ player.exitFullScreen();
1739
+ } else {
1740
+ player.enterFullScreen();
1741
+ }
1742
+ });
1743
+
1744
+ } else {
1745
+
1746
+ var hideTimeout = null,
1747
+ supportsPointerEvents = (function() {
1748
+ // TAKEN FROM MODERNIZR
1749
+ var element = document.createElement('x'),
1750
+ documentElement = document.documentElement,
1751
+ getComputedStyle = window.getComputedStyle,
1752
+ supports;
1753
+ if(!('pointerEvents' in element.style)){
1754
+ return false;
1755
+ }
1756
+ element.style.pointerEvents = 'auto';
1757
+ element.style.pointerEvents = 'x';
1758
+ documentElement.appendChild(element);
1759
+ supports = getComputedStyle &&
1760
+ getComputedStyle(element, '').pointerEvents === 'auto';
1761
+ documentElement.removeChild(element);
1762
+ return !!supports;
1763
+ })();
1764
+
1765
+ //console.log('supportsPointerEvents', supportsPointerEvents);
1766
+
1767
+ if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
1768
+
1769
+ // allows clicking through the fullscreen button and controls down directly to Flash
1770
+
1771
+ /*
1772
+ When a user puts his mouse over the fullscreen button, the controls are disabled
1773
+ So we put a div over the video and another one on iether side of the fullscreen button
1774
+ that caputre mouse movement
1775
+ and restore the controls once the mouse moves outside of the fullscreen button
1776
+ */
1777
+
1778
+ var fullscreenIsDisabled = false,
1779
+ restoreControls = function() {
1780
+ if (fullscreenIsDisabled) {
1781
+ // hide the hovers
1782
+ videoHoverDiv.hide();
1783
+ controlsLeftHoverDiv.hide();
1784
+ controlsRightHoverDiv.hide();
1785
+
1786
+ // restore the control bar
1787
+ fullscreenBtn.css('pointer-events', '');
1788
+ t.controls.css('pointer-events', '');
1789
+
1790
+ // store for later
1791
+ fullscreenIsDisabled = false;
1792
+ }
1793
+ },
1794
+ videoHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1795
+ controlsLeftHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1796
+ controlsRightHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1797
+ positionHoverDivs = function() {
1798
+ var style = {position: 'absolute', top: 0, left: 0}; //, backgroundColor: '#f00'};
1799
+ videoHoverDiv.css(style);
1800
+ controlsLeftHoverDiv.css(style);
1801
+ controlsRightHoverDiv.css(style);
1802
+
1803
+ // over video, but not controls
1804
+ videoHoverDiv
1805
+ .width( t.container.width() )
1806
+ .height( t.container.height() - t.controls.height() );
1807
+
1808
+ // over controls, but not the fullscreen button
1809
+ var fullScreenBtnOffset = fullscreenBtn.offset().left - t.container.offset().left;
1810
+ fullScreenBtnWidth = fullscreenBtn.outerWidth(true);
1811
+
1812
+ controlsLeftHoverDiv
1813
+ .width( fullScreenBtnOffset )
1814
+ .height( t.controls.height() )
1815
+ .css({top: t.container.height() - t.controls.height()});
1816
+
1817
+ // after the fullscreen button
1818
+ controlsRightHoverDiv
1819
+ .width( t.container.width() - fullScreenBtnOffset - fullScreenBtnWidth )
1820
+ .height( t.controls.height() )
1821
+ .css({top: t.container.height() - t.controls.height(),
1822
+ left: fullScreenBtnOffset + fullScreenBtnWidth});
1823
+ };
1824
+
1825
+ $(document).resize(function() {
1826
+ positionHoverDivs();
1827
+ });
1828
+
1829
+ // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
1830
+ fullscreenBtn
1831
+ .mouseover(function() {
1832
+
1833
+ if (!t.isFullScreen) {
1834
+
1835
+ var buttonPos = fullscreenBtn.offset(),
1836
+ containerPos = player.container.offset();
1837
+
1838
+ // move the button in Flash into place
1839
+ media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
1840
+
1841
+ // allows click through
1842
+ fullscreenBtn.css('pointer-events', 'none');
1843
+ t.controls.css('pointer-events', 'none');
1844
+
1845
+ // show the divs that will restore things
1846
+ videoHoverDiv.show();
1847
+ controlsRightHoverDiv.show();
1848
+ controlsLeftHoverDiv.show();
1849
+ positionHoverDivs();
1850
+
1851
+ fullscreenIsDisabled = true;
1852
+ }
1853
+
1854
+ });
1855
+
1856
+ // restore controls anytime the user enters or leaves fullscreen
1857
+ media.addEventListener('fullscreenchange', function(e) {
1858
+ restoreControls();
1859
+ });
1860
+
1861
+
1862
+ // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
1863
+ // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
1864
+ /*
1865
+ $(document).mousemove(function(e) {
1866
+
1867
+ // if the mouse is anywhere but the fullsceen button, then restore it all
1868
+ if (fullscreenIsDisabled) {
1869
+
1870
+ var fullscreenBtnPos = fullscreenBtn.offset();
1871
+
1872
+
1873
+ if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
1874
+ e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
1875
+ ) {
1876
+
1877
+ fullscreenBtn.css('pointer-events', '');
1878
+ t.controls.css('pointer-events', '');
1879
+
1880
+ fullscreenIsDisabled = false;
1881
+ }
1602
1882
  }
1603
1883
  });
1604
- mouseIsDown = true;
1605
-
1606
- return false;
1607
- });
1884
+ */
1608
1885
 
1609
1886
 
1610
- // MUTE button
1611
- mute.find('button').click(function() {
1612
- media.setMuted( !media.muted );
1887
+ } else {
1888
+
1889
+ // the hover state will show the fullscreen button in Flash to hover up and click
1890
+
1891
+ fullscreenBtn
1892
+ .mouseover(function() {
1893
+
1894
+ if (hideTimeout !== null) {
1895
+ clearTimeout(hideTimeout);
1896
+ delete hideTimeout;
1897
+ }
1898
+
1899
+ var buttonPos = fullscreenBtn.offset(),
1900
+ containerPos = player.container.offset();
1901
+
1902
+ media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
1903
+
1904
+ })
1905
+ .mouseout(function() {
1906
+
1907
+ if (hideTimeout !== null) {
1908
+ clearTimeout(hideTimeout);
1909
+ delete hideTimeout;
1910
+ }
1911
+
1912
+ hideTimeout = setTimeout(function() {
1913
+ media.hideFullscreenButton();
1914
+ }, 1500);
1915
+
1916
+
1917
+ });
1918
+ }
1919
+ }
1920
+
1921
+ player.fullscreenBtn = fullscreenBtn;
1922
+
1923
+ $(document).bind('keydown',function (e) {
1924
+ if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
1925
+ player.exitFullScreen();
1926
+ }
1613
1927
  });
1614
1928
 
1615
- // listen for volume change events from other sources
1616
- media.addEventListener('volumechange', function(e) {
1617
- if (!mouseIsDown) {
1618
- if (media.muted) {
1619
- positionVolumeHandle(0);
1620
- mute.removeClass('mejs-mute').addClass('mejs-unmute');
1621
- } else {
1622
- positionVolumeHandle(media.volume);
1623
- mute.removeClass('mejs-unmute').addClass('mejs-mute');
1929
+ },
1930
+ enterFullScreen: function() {
1931
+
1932
+ var t = this;
1933
+
1934
+ // firefox+flash can't adjust plugin sizes without resetting :(
1935
+ if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
1936
+ //t.media.setFullscreen(true);
1937
+ //player.isFullScreen = true;
1938
+ return;
1939
+ }
1940
+
1941
+ // store overflow
1942
+ docStyleOverflow = document.documentElement.style.overflow;
1943
+ // set it to not show scroll bars so 100% will work
1944
+ document.documentElement.style.overflow = 'hidden';
1945
+
1946
+ // store sizing
1947
+ normalHeight = t.container.height();
1948
+ normalWidth = t.container.width();
1949
+
1950
+ // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
1951
+ if (t.media.pluginType === 'native') {
1952
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
1953
+
1954
+ mejs.MediaFeatures.requestFullScreen(t.container[0]);
1955
+ //return;
1956
+
1957
+ if (t.isInIframe) {
1958
+ // sometimes exiting from fullscreen doesn't work
1959
+ // notably in Chrome <iframe>. Fixed in version 17
1960
+ setTimeout(function checkFullscreen() {
1961
+
1962
+ if (t.isNativeFullScreen) {
1963
+
1964
+ // check if the video is suddenly not really fullscreen
1965
+ if ($(window).width() !== screen.width) {
1966
+ // manually exit
1967
+ t.exitFullScreen();
1968
+ } else {
1969
+ // test again
1970
+ setTimeout(checkFullscreen, 500);
1971
+ }
1972
+ }
1973
+
1974
+
1975
+ }, 500);
1624
1976
  }
1977
+
1978
+ } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
1979
+ t.media.webkitEnterFullscreen();
1980
+ return;
1625
1981
  }
1626
- }, false);
1982
+ }
1627
1983
 
1628
- if (t.container.is(':visible')) {
1629
- // set initial volume
1630
- positionVolumeHandle(player.options.startVolume);
1631
-
1632
- // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
1633
- if (media.pluginType === 'native') {
1634
- media.setVolume(player.options.startVolume);
1984
+ // check for iframe launch
1985
+ if (t.isInIframe) {
1986
+ var url = t.options.newWindowCallback(this);
1987
+
1988
+
1989
+ if (url !== '') {
1990
+
1991
+ // launch immediately
1992
+ if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
1993
+ t.pause();
1994
+ window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
1995
+ return;
1996
+ } else {
1997
+ setTimeout(function() {
1998
+ if (!t.isNativeFullScreen) {
1999
+ t.pause();
2000
+ window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
2001
+ }
2002
+ }, 250);
2003
+ }
1635
2004
  }
2005
+
2006
+ }
2007
+
2008
+ // full window code
2009
+
2010
+
2011
+
2012
+ // make full size
2013
+ t.container
2014
+ .addClass('mejs-container-fullscreen')
2015
+ .width('100%')
2016
+ .height('100%');
2017
+ //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
2018
+
2019
+ // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
2020
+ // Actually, it seems to be needed for IE8, too
2021
+ //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
2022
+ setTimeout(function() {
2023
+ t.container.css({width: '100%', height: '100%'});
2024
+ t.setControlsSize();
2025
+ }, 500);
2026
+ //}
2027
+
2028
+ if (t.pluginType === 'native') {
2029
+ t.$media
2030
+ .width('100%')
2031
+ .height('100%');
2032
+ } else {
2033
+ t.container.find('object, embed, iframe')
2034
+ .width('100%')
2035
+ .height('100%');
2036
+
2037
+ //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
2038
+ t.media.setVideoSize($(window).width(),$(window).height());
2039
+ //}
2040
+ }
2041
+
2042
+ t.layers.children('div')
2043
+ .width('100%')
2044
+ .height('100%');
2045
+
2046
+ if (t.fullscreenBtn) {
2047
+ t.fullscreenBtn
2048
+ .removeClass('mejs-fullscreen')
2049
+ .addClass('mejs-unfullscreen');
2050
+ }
2051
+
2052
+ t.setControlsSize();
2053
+ t.isFullScreen = true;
2054
+ },
2055
+
2056
+ exitFullScreen: function() {
2057
+
2058
+ var t = this;
2059
+
2060
+ // firefox can't adjust plugins
2061
+ if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
2062
+ t.media.setFullscreen(false);
2063
+ //player.isFullScreen = false;
2064
+ return;
2065
+ }
2066
+
2067
+ // come outo of native fullscreen
2068
+ if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
2069
+ mejs.MediaFeatures.cancelFullScreen();
2070
+ }
2071
+
2072
+ // restore scroll bars to document
2073
+ document.documentElement.style.overflow = docStyleOverflow;
2074
+
2075
+ t.container
2076
+ .removeClass('mejs-container-fullscreen')
2077
+ .width(normalWidth)
2078
+ .height(normalHeight);
2079
+ //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
2080
+
2081
+ if (t.pluginType === 'native') {
2082
+ t.$media
2083
+ .width(normalWidth)
2084
+ .height(normalHeight);
2085
+ } else {
2086
+ t.container.find('object embed')
2087
+ .width(normalWidth)
2088
+ .height(normalHeight);
2089
+
2090
+ t.media.setVideoSize(normalWidth, normalHeight);
1636
2091
  }
2092
+
2093
+ t.layers.children('div')
2094
+ .width(normalWidth)
2095
+ .height(normalHeight);
2096
+
2097
+ t.fullscreenBtn
2098
+ .removeClass('mejs-unfullscreen')
2099
+ .addClass('mejs-fullscreen');
2100
+
2101
+ t.setControlsSize();
2102
+ t.isFullScreen = false;
1637
2103
  }
1638
2104
  });
1639
-
1640
- })(mejs.$);
1641
2105
 
1642
- (function($) {
1643
-
1644
- $.extend(mejs.MepDefaults, {
1645
- usePluginFullScreen: true,
1646
- newWindowCallback: function() { return '';},
1647
- fullscreenText: 'Fullscreen'
1648
- });
1649
-
1650
- $.extend(MediaElementPlayer.prototype, {
1651
-
1652
- isFullScreen: false,
1653
-
1654
- isNativeFullScreen: false,
1655
-
1656
- docStyleOverflow: null,
1657
-
1658
- isInIframe: false,
1659
-
1660
- buildfullscreen: function(player, controls, layers, media) {
1661
-
1662
- if (!player.isVideo)
1663
- return;
1664
-
1665
- player.isInIframe = (window.location != window.parent.location);
1666
-
1667
- // native events
1668
- if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
1669
-
1670
- // chrome doesn't alays fire this in an iframe
1671
- var target = null;
1672
-
1673
- if (mejs.MediaFeatures.hasMozNativeFullScreen) {
1674
- target = $(document);
1675
- } else {
1676
- target = player.container;
1677
- }
1678
-
1679
- target.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
1680
-
1681
- if (mejs.MediaFeatures.isFullScreen()) {
1682
- player.isNativeFullScreen = true;
1683
- // reset the controls once we are fully in full screen
1684
- player.setControlsSize();
1685
- } else {
1686
- player.isNativeFullScreen = false;
1687
- // when a user presses ESC
1688
- // make sure to put the player back into place
1689
- player.exitFullScreen();
1690
- }
1691
- });
1692
- }
1693
-
1694
- var t = this,
1695
- normalHeight = 0,
1696
- normalWidth = 0,
1697
- container = player.container,
1698
- fullscreenBtn =
1699
- $('<div class="mejs-button mejs-fullscreen-button">' +
1700
- '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '"></button>' +
1701
- '</div>')
1702
- .appendTo(controls);
1703
-
1704
- if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
1705
-
1706
- fullscreenBtn.click(function() {
1707
- var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
1708
-
1709
- if (isFullScreen) {
1710
- player.exitFullScreen();
1711
- } else {
1712
- player.enterFullScreen();
1713
- }
1714
- });
1715
-
1716
- } else {
1717
-
1718
- var hideTimeout = null,
1719
- supportsPointerEvents = (function() {
1720
- // TAKEN FROM MODERNIZR
1721
- var element = document.createElement('x'),
1722
- documentElement = document.documentElement,
1723
- getComputedStyle = window.getComputedStyle,
1724
- supports;
1725
- if(!('pointerEvents' in element.style)){
1726
- return false;
1727
- }
1728
- element.style.pointerEvents = 'auto';
1729
- element.style.pointerEvents = 'x';
1730
- documentElement.appendChild(element);
1731
- supports = getComputedStyle &&
1732
- getComputedStyle(element, '').pointerEvents === 'auto';
1733
- documentElement.removeChild(element);
1734
- return !!supports;
1735
- })();
1736
-
1737
- //console.log('supportsPointerEvents', supportsPointerEvents);
1738
-
1739
- if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
1740
-
1741
- // allows clicking through the fullscreen button and controls down directly to Flash
1742
-
1743
- /*
1744
- When a user puts his mouse over the fullscreen button, the controls are disabled
1745
- So we put a div over the video and another one on iether side of the fullscreen button
1746
- that caputre mouse movement
1747
- and restore the controls once the mouse moves outside of the fullscreen button
1748
- */
1749
-
1750
- var fullscreenIsDisabled = false,
1751
- restoreControls = function() {
1752
- if (fullscreenIsDisabled) {
1753
- // hide the hovers
1754
- videoHoverDiv.hide();
1755
- controlsLeftHoverDiv.hide();
1756
- controlsRightHoverDiv.hide();
1757
-
1758
- // restore the control bar
1759
- fullscreenBtn.css('pointer-events', '');
1760
- t.controls.css('pointer-events', '');
1761
-
1762
- // store for later
1763
- fullscreenIsDisabled = false;
1764
- }
1765
- },
1766
- videoHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1767
- controlsLeftHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1768
- controlsRightHoverDiv = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls),
1769
- positionHoverDivs = function() {
1770
- var style = {position: 'absolute', top: 0, left: 0}; //, backgroundColor: '#f00'};
1771
- videoHoverDiv.css(style);
1772
- controlsLeftHoverDiv.css(style);
1773
- controlsRightHoverDiv.css(style);
1774
-
1775
- // over video, but not controls
1776
- videoHoverDiv
1777
- .width( t.container.width() )
1778
- .height( t.container.height() - t.controls.height() );
1779
-
1780
- // over controls, but not the fullscreen button
1781
- var fullScreenBtnOffset = fullscreenBtn.offset().left - t.container.offset().left;
1782
- fullScreenBtnWidth = fullscreenBtn.outerWidth(true);
1783
-
1784
- controlsLeftHoverDiv
1785
- .width( fullScreenBtnOffset )
1786
- .height( t.controls.height() )
1787
- .css({top: t.container.height() - t.controls.height()});
1788
-
1789
- // after the fullscreen button
1790
- controlsRightHoverDiv
1791
- .width( t.container.width() - fullScreenBtnOffset - fullScreenBtnWidth )
1792
- .height( t.controls.height() )
1793
- .css({top: t.container.height() - t.controls.height(),
1794
- left: fullScreenBtnOffset + fullScreenBtnWidth});
1795
- };
1796
-
1797
- $(document).resize(function() {
1798
- positionHoverDivs();
1799
- });
1800
-
1801
- // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
1802
- fullscreenBtn
1803
- .mouseover(function() {
1804
-
1805
- if (!t.isFullScreen) {
1806
-
1807
- var buttonPos = fullscreenBtn.offset(),
1808
- containerPos = player.container.offset();
1809
-
1810
- // move the button in Flash into place
1811
- media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
1812
-
1813
- // allows click through
1814
- fullscreenBtn.css('pointer-events', 'none');
1815
- t.controls.css('pointer-events', 'none');
1816
-
1817
- // show the divs that will restore things
1818
- videoHoverDiv.show();
1819
- controlsRightHoverDiv.show();
1820
- controlsLeftHoverDiv.show();
1821
- positionHoverDivs();
1822
-
1823
- fullscreenIsDisabled = true;
1824
- }
1825
-
1826
- });
1827
-
1828
- // restore controls anytime the user enters or leaves fullscreen
1829
- media.addEventListener('fullscreenchange', function(e) {
1830
- restoreControls();
1831
- });
1832
-
1833
-
1834
- // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
1835
- // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
1836
- /*
1837
- $(document).mousemove(function(e) {
1838
-
1839
- // if the mouse is anywhere but the fullsceen button, then restore it all
1840
- if (fullscreenIsDisabled) {
1841
-
1842
- var fullscreenBtnPos = fullscreenBtn.offset();
1843
-
1844
-
1845
- if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
1846
- e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
1847
- ) {
1848
-
1849
- fullscreenBtn.css('pointer-events', '');
1850
- t.controls.css('pointer-events', '');
1851
-
1852
- fullscreenIsDisabled = false;
1853
- }
1854
- }
1855
- });
1856
- */
1857
-
1858
-
1859
- } else {
1860
-
1861
- // the hover state will show the fullscreen button in Flash to hover up and click
1862
-
1863
- fullscreenBtn
1864
- .mouseover(function() {
1865
-
1866
- if (hideTimeout !== null) {
1867
- clearTimeout(hideTimeout);
1868
- delete hideTimeout;
1869
- }
1870
-
1871
- var buttonPos = fullscreenBtn.offset(),
1872
- containerPos = player.container.offset();
1873
-
1874
- media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
1875
-
1876
- })
1877
- .mouseout(function() {
1878
-
1879
- if (hideTimeout !== null) {
1880
- clearTimeout(hideTimeout);
1881
- delete hideTimeout;
1882
- }
1883
-
1884
- hideTimeout = setTimeout(function() {
1885
- media.hideFullscreenButton();
1886
- }, 1500);
1887
-
1888
-
1889
- });
1890
- }
1891
- }
1892
-
1893
- player.fullscreenBtn = fullscreenBtn;
1894
-
1895
- $(document).bind('keydown',function (e) {
1896
- if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
1897
- player.exitFullScreen();
1898
- }
1899
- });
1900
-
1901
- },
1902
- enterFullScreen: function() {
1903
-
1904
- var t = this;
1905
-
1906
- // firefox+flash can't adjust plugin sizes without resetting :(
1907
- if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
1908
- //t.media.setFullscreen(true);
1909
- //player.isFullScreen = true;
1910
- return;
1911
- }
1912
-
1913
- // store overflow
1914
- docStyleOverflow = document.documentElement.style.overflow;
1915
- // set it to not show scroll bars so 100% will work
1916
- document.documentElement.style.overflow = 'hidden';
1917
-
1918
- // store sizing
1919
- normalHeight = t.container.height();
1920
- normalWidth = t.container.width();
1921
-
1922
- // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
1923
- if (t.media.pluginType === 'native') {
1924
- if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
1925
-
1926
- mejs.MediaFeatures.requestFullScreen(t.container[0]);
1927
- //return;
1928
-
1929
- if (t.isInIframe) {
1930
- // sometimes exiting from fullscreen doesn't work
1931
- // notably in Chrome <iframe>. Fixed in version 17
1932
- setTimeout(function checkFullscreen() {
1933
-
1934
- if (t.isNativeFullScreen) {
1935
-
1936
- // check if the video is suddenly not really fullscreen
1937
- if ($(window).width() !== screen.width) {
1938
- // manually exit
1939
- t.exitFullScreen();
1940
- } else {
1941
- // test again
1942
- setTimeout(checkFullscreen, 500);
1943
- }
1944
- }
1945
-
1946
-
1947
- }, 500);
1948
- }
1949
-
1950
- } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
1951
- t.media.webkitEnterFullscreen();
1952
- return;
1953
- }
1954
- }
1955
-
1956
- // check for iframe launch
1957
- if (t.isInIframe) {
1958
- var url = t.options.newWindowCallback(this);
1959
-
1960
-
1961
- if (url !== '') {
1962
-
1963
- // launch immediately
1964
- if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
1965
- t.pause();
1966
- window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
1967
- return;
1968
- } else {
1969
- setTimeout(function() {
1970
- if (!t.isNativeFullScreen) {
1971
- t.pause();
1972
- window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
1973
- }
1974
- }, 250);
1975
- }
1976
- }
1977
-
1978
- }
1979
-
1980
- // full window code
1981
-
1982
-
1983
-
1984
- // make full size
1985
- t.container
1986
- .addClass('mejs-container-fullscreen')
1987
- .width('100%')
1988
- .height('100%');
1989
- //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
1990
-
1991
- // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
1992
- // Actually, it seems to be needed for IE8, too
1993
- //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
1994
- setTimeout(function() {
1995
- t.container.css({width: '100%', height: '100%'});
1996
- t.setControlsSize();
1997
- }, 500);
1998
- //}
1999
-
2000
- if (t.pluginType === 'native') {
2001
- t.$media
2002
- .width('100%')
2003
- .height('100%');
2004
- } else {
2005
- t.container.find('object, embed, iframe')
2006
- .width('100%')
2007
- .height('100%');
2008
-
2009
- //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
2010
- t.media.setVideoSize($(window).width(),$(window).height());
2011
- //}
2012
- }
2013
-
2014
- t.layers.children('div')
2015
- .width('100%')
2016
- .height('100%');
2017
-
2018
- if (t.fullscreenBtn) {
2019
- t.fullscreenBtn
2020
- .removeClass('mejs-fullscreen')
2021
- .addClass('mejs-unfullscreen');
2022
- }
2023
-
2024
- t.setControlsSize();
2025
- t.isFullScreen = true;
2026
- },
2027
-
2028
- exitFullScreen: function() {
2029
-
2030
- var t = this;
2031
-
2032
- // firefox can't adjust plugins
2033
- if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
2034
- t.media.setFullscreen(false);
2035
- //player.isFullScreen = false;
2036
- return;
2037
- }
2038
-
2039
- // come outo of native fullscreen
2040
- if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
2041
- mejs.MediaFeatures.cancelFullScreen();
2042
- }
2043
-
2044
- // restore scroll bars to document
2045
- document.documentElement.style.overflow = docStyleOverflow;
2046
-
2047
- t.container
2048
- .removeClass('mejs-container-fullscreen')
2049
- .width(normalWidth)
2050
- .height(normalHeight);
2051
- //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
2052
-
2053
- if (t.pluginType === 'native') {
2054
- t.$media
2055
- .width(normalWidth)
2056
- .height(normalHeight);
2057
- } else {
2058
- t.container.find('object embed')
2059
- .width(normalWidth)
2060
- .height(normalHeight);
2061
-
2062
- t.media.setVideoSize(normalWidth, normalHeight);
2063
- }
2064
-
2065
- t.layers.children('div')
2066
- .width(normalWidth)
2067
- .height(normalHeight);
2068
-
2069
- t.fullscreenBtn
2070
- .removeClass('mejs-unfullscreen')
2071
- .addClass('mejs-fullscreen');
2072
-
2073
- t.setControlsSize();
2074
- t.isFullScreen = false;
2075
- }
2076
- });
2077
-
2078
- })(mejs.$);
2106
+ })(mejs.$);
2079
2107
 
2080
2108
  (function($) {
2081
2109
 
@@ -2152,12 +2180,12 @@ if (typeof jQuery != 'undefined') {
2152
2180
  if (!player.options.alwaysShowControls) {
2153
2181
  // move with controls
2154
2182
  player.container
2155
- .bind('mouseenter', function () {
2183
+ .bind('controlsshown', function () {
2156
2184
  // push captions above controls
2157
2185
  player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
2158
2186
 
2159
2187
  })
2160
- .bind('mouseleave', function () {
2188
+ .bind('controlshidden', function () {
2161
2189
  if (!media.paused) {
2162
2190
  // move back to normal place
2163
2191
  player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
@@ -2243,35 +2271,33 @@ if (typeof jQuery != 'undefined') {
2243
2271
 
2244
2272
  };
2245
2273
 
2246
- if (track.isTranslation) {
2247
-
2248
- // translate the first track
2249
- mejs.TrackFormatParser.translateTrackText(t.tracks[0].entries, t.tracks[0].srclang, track.srclang, t.options.googleApiKey, function(newOne) {
2250
2274
 
2251
- // store the new translation
2252
- track.entries = newOne;
2275
+ $.ajax({
2276
+ url: track.src,
2277
+ dataType: "text",
2278
+ success: function(d) {
2253
2279
 
2280
+ // parse the loaded file
2281
+ if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
2282
+ track.entries = mejs.TrackFormatParser.dfxp.parse(d);
2283
+ } else {
2284
+ track.entries = mejs.TrackFormatParser.webvvt.parse(d);
2285
+ }
2286
+
2254
2287
  after();
2255
- });
2256
-
2257
- } else {
2258
- $.ajax({
2259
- url: track.src,
2260
- success: function(d) {
2261
-
2262
- // parse the loaded file
2263
- track.entries = mejs.TrackFormatParser.parse(d);
2264
- after();
2265
2288
 
2266
- if (track.kind == 'chapters' && t.media.duration > 0) {
2267
- t.drawChapters(track);
2268
- }
2269
- },
2270
- error: function() {
2271
- t.loadNextTrack();
2289
+ if (track.kind == 'chapters') {
2290
+ t.media.addEventListener('play', function(e) {
2291
+ if (t.media.duration > 0) {
2292
+ t.displayChapters(track);
2293
+ }
2294
+ }, false);
2272
2295
  }
2273
- });
2274
- }
2296
+ },
2297
+ error: function() {
2298
+ t.loadNextTrack();
2299
+ }
2300
+ });
2275
2301
  },
2276
2302
 
2277
2303
  enableTrackButton: function(lang, label) {
@@ -2489,53 +2515,106 @@ if (typeof jQuery != 'undefined') {
2489
2515
  Adapted from: http://www.delphiki.com/html5/playr
2490
2516
  */
2491
2517
  mejs.TrackFormatParser = {
2492
- // match start "chapter-" (or anythingelse)
2493
- pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/,
2494
- 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})?)(.*)$/,
2495
-
2496
- split2: function (text, regex) {
2497
- // normal version for compliant browsers
2498
- // see below for IE fix
2499
- return text.split(regex);
2500
- },
2501
- parse: function(trackText) {
2502
- var
2503
- i = 0,
2504
- lines = this.split2(trackText, /\r?\n/),
2505
- entries = {text:[], times:[]},
2506
- timecode,
2507
- text;
2508
-
2509
- for(; i<lines.length; i++) {
2510
- // check for the line number
2511
- if (this.pattern_identifier.exec(lines[i])){
2512
- // skip to the next line where the start --> end time code should be
2513
- i++;
2514
- timecode = this.pattern_timecode.exec(lines[i]);
2515
-
2516
- if (timecode && i<lines.length){
2518
+ webvvt: {
2519
+ // match start "chapter-" (or anythingelse)
2520
+ pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/,
2521
+ 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})?)(.*)$/,
2522
+
2523
+ parse: function(trackText) {
2524
+ var
2525
+ i = 0,
2526
+ lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
2527
+ entries = {text:[], times:[]},
2528
+ timecode,
2529
+ text;
2530
+ for(; i<lines.length; i++) {
2531
+ // check for the line number
2532
+ if (this.pattern_identifier.exec(lines[i])){
2533
+ // skip to the next line where the start --> end time code should be
2517
2534
  i++;
2518
- // grab all the (possibly multi-line) text that follows
2519
- text = lines[i];
2520
- i++;
2521
- while(lines[i] !== '' && i<lines.length){
2522
- text = text + '\n' + lines[i];
2535
+ timecode = this.pattern_timecode.exec(lines[i]);
2536
+
2537
+ if (timecode && i<lines.length){
2523
2538
  i++;
2539
+ // grab all the (possibly multi-line) text that follows
2540
+ text = lines[i];
2541
+ i++;
2542
+ while(lines[i] !== '' && i<lines.length){
2543
+ text = text + '\n' + lines[i];
2544
+ i++;
2545
+ }
2546
+ text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
2547
+ // Text is in a different array so I can use .join
2548
+ entries.text.push(text);
2549
+ entries.times.push(
2550
+ {
2551
+ start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) == 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
2552
+ stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
2553
+ settings: timecode[5]
2554
+ });
2524
2555
  }
2525
-
2526
- // Text is in a different array so I can use .join
2527
- entries.text.push(text);
2528
- entries.times.push(
2529
- {
2530
- start: mejs.Utility.timeCodeToSeconds(timecode[1]),
2531
- stop: mejs.Utility.timeCodeToSeconds(timecode[3]),
2532
- settings: timecode[5]
2533
- });
2534
2556
  }
2535
2557
  }
2558
+ return entries;
2536
2559
  }
2560
+ },
2561
+ // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
2562
+ dfxp: {
2563
+ parse: function(trackText) {
2564
+ trackText = $(trackText).filter("tt");
2565
+ var
2566
+ i = 0,
2567
+ container = trackText.children("div").eq(0),
2568
+ lines = container.find("p"),
2569
+ styleNode = trackText.find("#" + container.attr("style")),
2570
+ styles,
2571
+ begin,
2572
+ end,
2573
+ text,
2574
+ entries = {text:[], times:[]};
2575
+
2576
+
2577
+ if (styleNode.length) {
2578
+ var attributes = styleNode.removeAttr("id").get(0).attributes;
2579
+ if (attributes.length) {
2580
+ styles = {};
2581
+ for (i = 0; i < attributes.length; i++) {
2582
+ styles[attributes[i].name.split(":")[1]] = attributes[i].value;
2583
+ }
2584
+ }
2585
+ }
2537
2586
 
2538
- return entries;
2587
+ for(i = 0; i<lines.length; i++) {
2588
+ var style;
2589
+ var _temp_times = {
2590
+ start: null,
2591
+ stop: null,
2592
+ style: null
2593
+ };
2594
+ if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
2595
+ if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
2596
+ if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
2597
+ if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
2598
+ if (styles) {
2599
+ style = "";
2600
+ for (var _style in styles) {
2601
+ style += _style + ":" + styles[_style] + ";";
2602
+ }
2603
+ }
2604
+ if (style) _temp_times.style = style;
2605
+ if (_temp_times.start == 0) _temp_times.start = 0.200;
2606
+ entries.times.push(_temp_times);
2607
+ text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
2608
+ entries.text.push(text);
2609
+ if (entries.times.start == 0) entries.times.start = 2;
2610
+ }
2611
+ return entries;
2612
+ }
2613
+ },
2614
+ split2: function (text, regex) {
2615
+ // normal version for compliant browsers
2616
+ // see below for IE fix
2617
+ return text.split(regex);
2539
2618
  }
2540
2619
  };
2541
2620
 
@@ -2562,196 +2641,231 @@ if (typeof jQuery != 'undefined') {
2562
2641
 
2563
2642
  })(mejs.$);
2564
2643
 
2565
- /*
2566
- * ContextMenu Plugin
2567
- *
2568
- *
2569
- */
2570
-
2571
- (function($) {
2572
-
2573
- $.extend(mejs.MepDefaults,
2574
- { 'contextMenuItems': [
2575
- // demo of a fullscreen option
2576
- {
2577
- render: function(player) {
2578
-
2579
- // check for fullscreen plugin
2580
- if (typeof player.enterFullScreen == 'undefined')
2581
- return null;
2582
-
2583
- if (player.isFullScreen) {
2584
- return "Turn off Fullscreen";
2585
- } else {
2586
- return "Go Fullscreen";
2587
- }
2588
- },
2589
- click: function(player) {
2590
- if (player.isFullScreen) {
2591
- player.exitFullScreen();
2592
- } else {
2593
- player.enterFullScreen();
2594
- }
2595
- }
2596
- }
2597
- ,
2598
- // demo of a mute/unmute button
2599
- {
2600
- render: function(player) {
2601
- if (player.media.muted) {
2602
- return "Unmute";
2603
- } else {
2604
- return "Mute";
2605
- }
2606
- },
2607
- click: function(player) {
2608
- if (player.media.muted) {
2609
- player.setMuted(false);
2610
- } else {
2611
- player.setMuted(true);
2612
- }
2613
- }
2614
- },
2615
- // separator
2616
- {
2617
- isSeparator: true
2618
- }
2619
- ,
2620
- // demo of simple download video
2621
- {
2622
- render: function(player) {
2623
- return "Download Video";
2624
- },
2625
- click: function(player) {
2626
- window.location.href = player.media.currentSrc;
2627
- }
2628
- }
2629
- ]}
2630
- );
2631
-
2632
-
2633
- $.extend(MediaElementPlayer.prototype, {
2634
- buildcontextmenu: function(player, controls, layers, media) {
2635
-
2636
- // create context menu
2637
- player.contextMenu = $('<div class="mejs-contextmenu"></div>')
2638
- .appendTo($('body'))
2639
- .hide();
2640
-
2641
- // create events for showing context menu
2642
- player.container.bind('contextmenu', function(e) {
2643
- if (player.isContextMenuEnabled) {
2644
- e.preventDefault();
2645
- player.renderContextMenu(e.clientX-1, e.clientY-1);
2646
- return false;
2647
- }
2648
- });
2649
- player.container.bind('click', function() {
2650
- player.contextMenu.hide();
2651
- });
2652
- player.contextMenu.bind('mouseleave', function() {
2653
-
2654
- //console.log('context hover out');
2655
- player.startContextMenuTimer();
2656
-
2657
- });
2658
- },
2659
-
2660
- isContextMenuEnabled: true,
2661
- enableContextMenu: function() {
2662
- this.isContextMenuEnabled = true;
2663
- },
2664
- disableContextMenu: function() {
2665
- this.isContextMenuEnabled = false;
2666
- },
2667
-
2668
- contextMenuTimeout: null,
2669
- startContextMenuTimer: function() {
2670
- //console.log('startContextMenuTimer');
2671
-
2672
- var t = this;
2673
-
2674
- t.killContextMenuTimer();
2675
-
2676
- t.contextMenuTimer = setTimeout(function() {
2677
- t.hideContextMenu();
2678
- t.killContextMenuTimer();
2679
- }, 750);
2680
- },
2681
- killContextMenuTimer: function() {
2682
- var timer = this.contextMenuTimer;
2683
-
2684
- //console.log('killContextMenuTimer', timer);
2685
-
2686
- if (timer != null) {
2687
- clearTimeout(timer);
2688
- delete timer;
2689
- timer = null;
2690
- }
2691
- },
2692
-
2693
- hideContextMenu: function() {
2694
- this.contextMenu.hide();
2695
- },
2696
-
2697
- renderContextMenu: function(x,y) {
2698
-
2699
- // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
2700
- var t = this,
2701
- html = '',
2702
- items = t.options.contextMenuItems;
2703
-
2704
- for (var i=0, il=items.length; i<il; i++) {
2705
-
2706
- if (items[i].isSeparator) {
2707
- html += '<div class="mejs-contextmenu-separator"></div>';
2708
- } else {
2709
-
2710
- var rendered = items[i].render(t);
2711
-
2712
- // render can return null if the item doesn't need to be used at the moment
2713
- if (rendered != null) {
2714
- html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
2715
- }
2716
- }
2717
- }
2718
-
2719
- // position and show the context menu
2720
- t.contextMenu
2721
- .empty()
2722
- .append($(html))
2723
- .css({top:y, left:x})
2724
- .show();
2725
-
2726
- // bind events
2727
- t.contextMenu.find('.mejs-contextmenu-item').each(function() {
2728
-
2729
- // which one is this?
2730
- var $dom = $(this),
2731
- itemIndex = parseInt( $dom.data('itemindex'), 10 ),
2732
- item = t.options.contextMenuItems[itemIndex];
2733
-
2734
- // bind extra functionality?
2735
- if (typeof item.show != 'undefined')
2736
- item.show( $dom , t);
2737
-
2738
- // bind click action
2739
- $dom.click(function() {
2740
- // perform click action
2741
- if (typeof item.click != 'undefined')
2742
- item.click(t);
2743
-
2744
- // close
2745
- t.contextMenu.hide();
2746
- });
2747
- });
2748
-
2749
- // stop the controls from hiding
2750
- setTimeout(function() {
2751
- t.killControlsTimer('rev3');
2752
- }, 100);
2753
-
2754
- }
2755
- });
2756
-
2644
+ /*
2645
+ * ContextMenu Plugin
2646
+ *
2647
+ *
2648
+ */
2649
+
2650
+ (function($) {
2651
+
2652
+ $.extend(mejs.MepDefaults,
2653
+ { 'contextMenuItems': [
2654
+ // demo of a fullscreen option
2655
+ {
2656
+ render: function(player) {
2657
+
2658
+ // check for fullscreen plugin
2659
+ if (typeof player.enterFullScreen == 'undefined')
2660
+ return null;
2661
+
2662
+ if (player.isFullScreen) {
2663
+ return "Turn off Fullscreen";
2664
+ } else {
2665
+ return "Go Fullscreen";
2666
+ }
2667
+ },
2668
+ click: function(player) {
2669
+ if (player.isFullScreen) {
2670
+ player.exitFullScreen();
2671
+ } else {
2672
+ player.enterFullScreen();
2673
+ }
2674
+ }
2675
+ }
2676
+ ,
2677
+ // demo of a mute/unmute button
2678
+ {
2679
+ render: function(player) {
2680
+ if (player.media.muted) {
2681
+ return "Unmute";
2682
+ } else {
2683
+ return "Mute";
2684
+ }
2685
+ },
2686
+ click: function(player) {
2687
+ if (player.media.muted) {
2688
+ player.setMuted(false);
2689
+ } else {
2690
+ player.setMuted(true);
2691
+ }
2692
+ }
2693
+ },
2694
+ // separator
2695
+ {
2696
+ isSeparator: true
2697
+ }
2698
+ ,
2699
+ // demo of simple download video
2700
+ {
2701
+ render: function(player) {
2702
+ return "Download Video";
2703
+ },
2704
+ click: function(player) {
2705
+ window.location.href = player.media.currentSrc;
2706
+ }
2707
+ }
2708
+ ]}
2709
+ );
2710
+
2711
+
2712
+ $.extend(MediaElementPlayer.prototype, {
2713
+ buildcontextmenu: function(player, controls, layers, media) {
2714
+
2715
+ // create context menu
2716
+ player.contextMenu = $('<div class="mejs-contextmenu"></div>')
2717
+ .appendTo($('body'))
2718
+ .hide();
2719
+
2720
+ // create events for showing context menu
2721
+ player.container.bind('contextmenu', function(e) {
2722
+ if (player.isContextMenuEnabled) {
2723
+ e.preventDefault();
2724
+ player.renderContextMenu(e.clientX-1, e.clientY-1);
2725
+ return false;
2726
+ }
2727
+ });
2728
+ player.container.bind('click', function() {
2729
+ player.contextMenu.hide();
2730
+ });
2731
+ player.contextMenu.bind('mouseleave', function() {
2732
+
2733
+ //console.log('context hover out');
2734
+ player.startContextMenuTimer();
2735
+
2736
+ });
2737
+ },
2738
+
2739
+ isContextMenuEnabled: true,
2740
+ enableContextMenu: function() {
2741
+ this.isContextMenuEnabled = true;
2742
+ },
2743
+ disableContextMenu: function() {
2744
+ this.isContextMenuEnabled = false;
2745
+ },
2746
+
2747
+ contextMenuTimeout: null,
2748
+ startContextMenuTimer: function() {
2749
+ //console.log('startContextMenuTimer');
2750
+
2751
+ var t = this;
2752
+
2753
+ t.killContextMenuTimer();
2754
+
2755
+ t.contextMenuTimer = setTimeout(function() {
2756
+ t.hideContextMenu();
2757
+ t.killContextMenuTimer();
2758
+ }, 750);
2759
+ },
2760
+ killContextMenuTimer: function() {
2761
+ var timer = this.contextMenuTimer;
2762
+
2763
+ //console.log('killContextMenuTimer', timer);
2764
+
2765
+ if (timer != null) {
2766
+ clearTimeout(timer);
2767
+ delete timer;
2768
+ timer = null;
2769
+ }
2770
+ },
2771
+
2772
+ hideContextMenu: function() {
2773
+ this.contextMenu.hide();
2774
+ },
2775
+
2776
+ renderContextMenu: function(x,y) {
2777
+
2778
+ // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
2779
+ var t = this,
2780
+ html = '',
2781
+ items = t.options.contextMenuItems;
2782
+
2783
+ for (var i=0, il=items.length; i<il; i++) {
2784
+
2785
+ if (items[i].isSeparator) {
2786
+ html += '<div class="mejs-contextmenu-separator"></div>';
2787
+ } else {
2788
+
2789
+ var rendered = items[i].render(t);
2790
+
2791
+ // render can return null if the item doesn't need to be used at the moment
2792
+ if (rendered != null) {
2793
+ html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
2794
+ }
2795
+ }
2796
+ }
2797
+
2798
+ // position and show the context menu
2799
+ t.contextMenu
2800
+ .empty()
2801
+ .append($(html))
2802
+ .css({top:y, left:x})
2803
+ .show();
2804
+
2805
+ // bind events
2806
+ t.contextMenu.find('.mejs-contextmenu-item').each(function() {
2807
+
2808
+ // which one is this?
2809
+ var $dom = $(this),
2810
+ itemIndex = parseInt( $dom.data('itemindex'), 10 ),
2811
+ item = t.options.contextMenuItems[itemIndex];
2812
+
2813
+ // bind extra functionality?
2814
+ if (typeof item.show != 'undefined')
2815
+ item.show( $dom , t);
2816
+
2817
+ // bind click action
2818
+ $dom.click(function() {
2819
+ // perform click action
2820
+ if (typeof item.click != 'undefined')
2821
+ item.click(t);
2822
+
2823
+ // close
2824
+ t.contextMenu.hide();
2825
+ });
2826
+ });
2827
+
2828
+ // stop the controls from hiding
2829
+ setTimeout(function() {
2830
+ t.killControlsTimer('rev3');
2831
+ }, 100);
2832
+
2833
+ }
2834
+ });
2835
+
2836
+ })(mejs.$);
2837
+ /**
2838
+ * Postroll plugin
2839
+ */
2840
+ (function($) {
2841
+
2842
+ $.extend(mejs.MepDefaults, {
2843
+ postrollCloseText: mejs.i18n.t('Close')
2844
+ });
2845
+
2846
+ // Postroll
2847
+ $.extend(MediaElementPlayer.prototype, {
2848
+ buildpostroll: function(player, controls, layers, media) {
2849
+ var
2850
+ t = this,
2851
+ postrollLink = t.container.find('link[rel="postroll"]').attr('href');
2852
+
2853
+ if (typeof postrollLink !== 'undefined') {
2854
+ player.postroll =
2855
+ $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a><div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();
2856
+
2857
+ t.media.addEventListener('ended', function (e) {
2858
+ $.ajax({
2859
+ dataType: 'html',
2860
+ url: postrollLink,
2861
+ success: function (data, textStatus) {
2862
+ layers.find('.mejs-postroll-layer-content').html(data);
2863
+ }
2864
+ });
2865
+ player.postroll.show();
2866
+ }, false);
2867
+ }
2868
+ }
2869
+ });
2870
+
2757
2871
  })(mejs.$);