rdoroshenko_mediaelement_rails 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +9 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +48 -0
  5. data/Rakefile +12 -0
  6. data/app/assets/images/mediaelement_rails/background.png +0 -0
  7. data/app/assets/images/mediaelement_rails/bigplay.png +0 -0
  8. data/app/assets/images/mediaelement_rails/controls-ted.png +0 -0
  9. data/app/assets/images/mediaelement_rails/controls-wmp-bg.png +0 -0
  10. data/app/assets/images/mediaelement_rails/controls-wmp.png +0 -0
  11. data/app/assets/images/mediaelement_rails/controls.png +0 -0
  12. data/app/assets/images/mediaelement_rails/loading.gif +0 -0
  13. data/app/assets/javascripts/mediaelement_rails/index.js +3 -0
  14. data/app/assets/javascripts/mediaelement_rails/mediaelement.js +1544 -0
  15. data/app/assets/javascripts/mediaelement_rails/mediaelementplayer.js +2757 -0
  16. data/app/assets/javascripts/mediaelement_rails/rails.js.erb +5 -0
  17. data/app/assets/plugins/mediaelement_rails/flashmediaelement.swf +0 -0
  18. data/app/assets/plugins/mediaelement_rails/silverlightmediaelement.xap +0 -0
  19. data/app/assets/stylesheets/mediaelement_rails/index.css +1 -0
  20. data/app/assets/stylesheets/mediaelement_rails/mediaelementplayer.css.erb +801 -0
  21. data/app/assets/stylesheets/mediaelement_rails/mejs-skins.css.erb +283 -0
  22. data/lib/mediaelement_rails.rb +5 -0
  23. data/lib/mediaelement_rails/engine.rb +11 -0
  24. data/lib/mediaelement_rails/version.rb +3 -0
  25. data/mediaelement_rails.gemspec +28 -0
  26. data/mediaelement_rails.thor +83 -0
  27. data/script/rails +5 -0
  28. data/test/dummy/Rakefile +7 -0
  29. data/test/dummy/app/assets/javascripts/mediaelement-and-player.js +1 -0
  30. data/test/dummy/app/assets/javascripts/mediaelement-without-player.js +1 -0
  31. data/test/dummy/app/assets/stylesheets/player-skins.css +3 -0
  32. data/test/dummy/app/assets/stylesheets/player.css +3 -0
  33. data/test/dummy/app/controllers/application_controller.rb +3 -0
  34. data/test/dummy/app/helpers/application_helper.rb +2 -0
  35. data/test/dummy/app/mailers/.gitkeep +0 -0
  36. data/test/dummy/app/models/.gitkeep +0 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/config.ru +4 -0
  39. data/test/dummy/config/application.rb +42 -0
  40. data/test/dummy/config/boot.rb +10 -0
  41. data/test/dummy/config/database.yml +25 -0
  42. data/test/dummy/config/environment.rb +5 -0
  43. data/test/dummy/config/environments/development.rb +24 -0
  44. data/test/dummy/config/environments/production.rb +51 -0
  45. data/test/dummy/config/environments/test.rb +34 -0
  46. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  47. data/test/dummy/config/initializers/inflections.rb +10 -0
  48. data/test/dummy/config/initializers/mime_types.rb +5 -0
  49. data/test/dummy/config/initializers/secret_token.rb +7 -0
  50. data/test/dummy/config/initializers/session_store.rb +8 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
  52. data/test/dummy/config/locales/en.yml +5 -0
  53. data/test/dummy/config/routes.rb +3 -0
  54. data/test/dummy/db/.gitkeep +0 -0
  55. data/test/dummy/lib/assets/.gitkeep +0 -0
  56. data/test/dummy/log/.gitkeep +0 -0
  57. data/test/dummy/public/404.html +26 -0
  58. data/test/dummy/public/422.html +26 -0
  59. data/test/dummy/public/500.html +26 -0
  60. data/test/dummy/public/favicon.ico +0 -0
  61. data/test/dummy/script/rails +6 -0
  62. data/test/integration/assets_test.rb +76 -0
  63. data/test/test_helper.rb +11 -0
  64. data/vendor/.gitkeep +0 -0
  65. metadata +235 -0
@@ -0,0 +1,2757 @@
1
+ /*!
2
+ * MediaElementPlayer
3
+ * http://mediaelementjs.com/
4
+ *
5
+ * Creates a controller bar for HTML5 <video> add <audio> tags
6
+ * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
7
+ *
8
+ * Copyright 2010-2012, John Dyer (http://j.hn/)
9
+ * Dual licensed under the MIT or GPL Version 2 licenses.
10
+ *
11
+ */
12
+ if (typeof jQuery != 'undefined') {
13
+ mejs.$ = jQuery;
14
+ } else if (typeof ender != 'undefined') {
15
+ mejs.$ = ender;
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.$);
1093
+
1094
+ (function($) {
1095
+
1096
+ $.extend(mejs.MepDefaults, {
1097
+ playpauseText: 'Play/Pause'
1098
+ });
1099
+
1100
+ // PLAY/pause BUTTON
1101
+ $.extend(MediaElementPlayer.prototype, {
1102
+ buildplaypause: function(player, controls, layers, media) {
1103
+ var
1104
+ t = this,
1105
+ play =
1106
+ $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
1107
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.playpauseText + '"></button>' +
1108
+ '</div>')
1109
+ .appendTo(controls)
1110
+ .click(function(e) {
1111
+ e.preventDefault();
1112
+
1113
+ if (media.paused) {
1114
+ media.play();
1115
+ } else {
1116
+ media.pause();
1117
+ }
1118
+
1119
+ return false;
1120
+ });
1121
+
1122
+ media.addEventListener('play',function() {
1123
+ play.removeClass('mejs-play').addClass('mejs-pause');
1124
+ }, false);
1125
+ media.addEventListener('playing',function() {
1126
+ play.removeClass('mejs-play').addClass('mejs-pause');
1127
+ }, false);
1128
+
1129
+
1130
+ media.addEventListener('pause',function() {
1131
+ play.removeClass('mejs-pause').addClass('mejs-play');
1132
+ }, false);
1133
+ media.addEventListener('paused',function() {
1134
+ play.removeClass('mejs-pause').addClass('mejs-play');
1135
+ }, false);
1136
+ }
1137
+ });
1138
+
1139
+ })(mejs.$);
1140
+ (function($) {
1141
+
1142
+ $.extend(mejs.MepDefaults, {
1143
+ stopText: 'Stop'
1144
+ });
1145
+
1146
+ // STOP BUTTON
1147
+ $.extend(MediaElementPlayer.prototype, {
1148
+ buildstop: function(player, controls, layers, media) {
1149
+ var t = this,
1150
+ stop =
1151
+ $('<div class="mejs-button mejs-stop-button mejs-stop">' +
1152
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '"></button>' +
1153
+ '</div>')
1154
+ .appendTo(controls)
1155
+ .click(function() {
1156
+ if (!media.paused) {
1157
+ media.pause();
1158
+ }
1159
+ if (media.currentTime > 0) {
1160
+ media.setCurrentTime(0);
1161
+ controls.find('.mejs-time-current').width('0px');
1162
+ controls.find('.mejs-time-handle').css('left', '0px');
1163
+ controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
1164
+ controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );
1165
+ layers.find('.mejs-poster').show();
1166
+ }
1167
+ });
1168
+ }
1169
+ });
1170
+
1171
+ })(mejs.$);
1172
+ (function($) {
1173
+ // progress/loaded bar
1174
+ $.extend(MediaElementPlayer.prototype, {
1175
+ buildprogress: function(player, controls, layers, media) {
1176
+
1177
+ $('<div class="mejs-time-rail">'+
1178
+ '<span class="mejs-time-total">'+
1179
+ '<span class="mejs-time-buffering"></span>'+
1180
+ '<span class="mejs-time-loaded"></span>'+
1181
+ '<span class="mejs-time-current"></span>'+
1182
+ '<span class="mejs-time-handle"></span>'+
1183
+ '<span class="mejs-time-float">' +
1184
+ '<span class="mejs-time-float-current">00:00</span>' +
1185
+ '<span class="mejs-time-float-corner"></span>' +
1186
+ '</span>'+
1187
+ '</span>'+
1188
+ '</div>')
1189
+ .appendTo(controls);
1190
+ controls.find('.mejs-time-buffering').hide();
1191
+
1192
+ var
1193
+ t = this,
1194
+ total = controls.find('.mejs-time-total'),
1195
+ loaded = controls.find('.mejs-time-loaded'),
1196
+ current = controls.find('.mejs-time-current'),
1197
+ handle = controls.find('.mejs-time-handle'),
1198
+ timefloat = controls.find('.mejs-time-float'),
1199
+ timefloatcurrent = controls.find('.mejs-time-float-current'),
1200
+ handleMouseMove = function (e) {
1201
+ // mouse position relative to the object
1202
+ var x = e.pageX,
1203
+ offset = total.offset(),
1204
+ width = total.outerWidth(),
1205
+ percentage = 0,
1206
+ newTime = 0,
1207
+ pos = x - offset.left;
1208
+
1209
+
1210
+ if (x > offset.left && x <= width + offset.left && media.duration) {
1211
+ percentage = ((x - offset.left) / width);
1212
+ newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
1213
+
1214
+ // seek to where the mouse is
1215
+ if (mouseIsDown) {
1216
+ media.setCurrentTime(newTime);
1217
+ }
1218
+
1219
+ // position floating time box
1220
+ if (!mejs.MediaFeatures.hasTouch) {
1221
+ timefloat.css('left', pos);
1222
+ timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
1223
+ timefloat.show();
1224
+ }
1225
+ }
1226
+ },
1227
+ mouseIsDown = false,
1228
+ mouseIsOver = false;
1229
+
1230
+ // handle clicks
1231
+ //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
1232
+ total
1233
+ .bind('mousedown', function (e) {
1234
+ // only handle left clicks
1235
+ if (e.which === 1) {
1236
+ mouseIsDown = true;
1237
+ handleMouseMove(e);
1238
+ $(document)
1239
+ .bind('mousemove.dur', function(e) {
1240
+ handleMouseMove(e);
1241
+ })
1242
+ .bind('mouseup.dur', function (e) {
1243
+ mouseIsDown = false;
1244
+ timefloat.hide();
1245
+ $(document).unbind('.dur');
1246
+ });
1247
+ return false;
1248
+ }
1249
+ })
1250
+ .bind('mouseenter', function(e) {
1251
+ mouseIsOver = true;
1252
+ $(document).bind('mousemove.dur', function(e) {
1253
+ handleMouseMove(e);
1254
+ });
1255
+ if (!mejs.MediaFeatures.hasTouch) {
1256
+ timefloat.show();
1257
+ }
1258
+ })
1259
+ .bind('mouseleave',function(e) {
1260
+ mouseIsOver = false;
1261
+ if (!mouseIsDown) {
1262
+ $(document).unbind('.dur');
1263
+ timefloat.hide();
1264
+ }
1265
+ });
1266
+
1267
+ // loading
1268
+ media.addEventListener('progress', function (e) {
1269
+ player.setProgressRail(e);
1270
+ player.setCurrentRail(e);
1271
+ }, false);
1272
+
1273
+ // current time
1274
+ media.addEventListener('timeupdate', function(e) {
1275
+ player.setProgressRail(e);
1276
+ player.setCurrentRail(e);
1277
+ }, false);
1278
+
1279
+
1280
+ // store for later use
1281
+ t.loaded = loaded;
1282
+ t.total = total;
1283
+ t.current = current;
1284
+ t.handle = handle;
1285
+ },
1286
+ setProgressRail: function(e) {
1287
+
1288
+ var
1289
+ t = this,
1290
+ target = (e != undefined) ? e.target : t.media,
1291
+ percent = null;
1292
+
1293
+ // newest HTML5 spec has buffered array (FF4, Webkit)
1294
+ if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
1295
+ // TODO: account for a real array with multiple values (only Firefox 4 has this so far)
1296
+ percent = target.buffered.end(0) / target.duration;
1297
+ }
1298
+ // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
1299
+ // to be anything other than 0. If the byte count is available we use this instead.
1300
+ // Browsers that support the else if do not seem to have the bufferedBytes value and
1301
+ // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
1302
+ else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
1303
+ percent = target.bufferedBytes / target.bytesTotal;
1304
+ }
1305
+ // Firefox 3 with an Ogg file seems to go this way
1306
+ else if (e && e.lengthComputable && e.total != 0) {
1307
+ percent = e.loaded/e.total;
1308
+ }
1309
+
1310
+ // finally update the progress bar
1311
+ if (percent !== null) {
1312
+ percent = Math.min(1, Math.max(0, percent));
1313
+ // update loaded bar
1314
+ if (t.loaded && t.total) {
1315
+ t.loaded.width(t.total.width() * percent);
1316
+ }
1317
+ }
1318
+ },
1319
+ setCurrentRail: function() {
1320
+
1321
+ var t = this;
1322
+
1323
+ if (t.media.currentTime != undefined && t.media.duration) {
1324
+
1325
+ // update bar and handle
1326
+ if (t.total && t.handle) {
1327
+ var
1328
+ newWidth = t.total.width() * t.media.currentTime / t.media.duration,
1329
+ handlePos = newWidth - (t.handle.outerWidth(true) / 2);
1330
+
1331
+ t.current.width(newWidth);
1332
+ t.handle.css('left', handlePos);
1333
+ }
1334
+ }
1335
+
1336
+ }
1337
+ });
1338
+ })(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
+
1419
+ })(mejs.$);
1420
+ (function($) {
1421
+
1422
+ $.extend(mejs.MepDefaults, {
1423
+ muteText: 'Mute Toggle',
1424
+ hideVolumeOnTouchDevices: true,
1425
+
1426
+ audioVolume: 'horizontal',
1427
+ videoVolume: 'vertical'
1428
+ });
1429
+
1430
+ $.extend(MediaElementPlayer.prototype, {
1431
+ buildvolume: function(player, controls, layers, media) {
1432
+
1433
+ // Android and iOS don't support volume controls
1434
+ if (mejs.MediaFeatures.hasTouch && this.options.hideVolumeOnTouchDevices)
1435
+ return;
1436
+
1437
+ var t = this,
1438
+ mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
1439
+ mute = (mode == 'horizontal') ?
1440
+
1441
+ // horizontal version
1442
+ $('<div class="mejs-button mejs-volume-button mejs-mute">'+
1443
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '"></button>'+
1444
+ '</div>' +
1445
+ '<div class="mejs-horizontal-volume-slider">'+ // outer background
1446
+ '<div class="mejs-horizontal-volume-total"></div>'+ // line background
1447
+ '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
1448
+ '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
1449
+ '</div>'
1450
+ )
1451
+ .appendTo(controls) :
1452
+
1453
+ // vertical version
1454
+ $('<div class="mejs-button mejs-volume-button mejs-mute">'+
1455
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '"></button>'+
1456
+ '<div class="mejs-volume-slider">'+ // outer background
1457
+ '<div class="mejs-volume-total"></div>'+ // line background
1458
+ '<div class="mejs-volume-current"></div>'+ // current volume
1459
+ '<div class="mejs-volume-handle"></div>'+ // handle
1460
+ '</div>'+
1461
+ '</div>')
1462
+ .appendTo(controls),
1463
+ volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
1464
+ volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
1465
+ volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
1466
+ volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
1467
+
1468
+ positionVolumeHandle = function(volume, secondTry) {
1469
+
1470
+ if (!volumeSlider.is(':visible') && typeof secondTry != 'undefined') {
1471
+ volumeSlider.show();
1472
+ positionVolumeHandle(volume, true);
1473
+ volumeSlider.hide()
1474
+ return;
1475
+ }
1476
+
1477
+ // correct to 0-1
1478
+ volume = Math.max(0,volume);
1479
+ volume = Math.min(volume,1);
1480
+
1481
+ // ajust mute button style
1482
+ if (volume == 0) {
1483
+ mute.removeClass('mejs-mute').addClass('mejs-unmute');
1484
+ } else {
1485
+ mute.removeClass('mejs-unmute').addClass('mejs-mute');
1486
+ }
1487
+
1488
+ // position slider
1489
+ if (mode == 'vertical') {
1490
+ var
1491
+
1492
+ // height of the full size volume slider background
1493
+ totalHeight = volumeTotal.height(),
1494
+
1495
+ // top/left of full size volume slider background
1496
+ totalPosition = volumeTotal.position(),
1497
+
1498
+ // the new top position based on the current volume
1499
+ // 70% volume on 100px height == top:30px
1500
+ newTop = totalHeight - (totalHeight * volume);
1501
+
1502
+ // handle
1503
+ volumeHandle.css('top', totalPosition.top + newTop - (volumeHandle.height() / 2));
1504
+
1505
+ // show the current visibility
1506
+ volumeCurrent.height(totalHeight - newTop );
1507
+ volumeCurrent.css('top', totalPosition.top + newTop);
1508
+ } else {
1509
+ var
1510
+
1511
+ // height of the full size volume slider background
1512
+ totalWidth = volumeTotal.width(),
1513
+
1514
+ // top/left of full size volume slider background
1515
+ totalPosition = volumeTotal.position(),
1516
+
1517
+ // the new left position based on the current volume
1518
+ newLeft = totalWidth * volume;
1519
+
1520
+ // handle
1521
+ volumeHandle.css('left', totalPosition.left + newLeft - (volumeHandle.width() / 2));
1522
+
1523
+ // rezize the current part of the volume bar
1524
+ volumeCurrent.width( newLeft );
1525
+ }
1526
+ },
1527
+ handleVolumeMove = function(e) {
1528
+
1529
+ var volume = null,
1530
+ totalOffset = volumeTotal.offset();
1531
+
1532
+ // calculate the new volume based on the moust position
1533
+ if (mode == 'vertical') {
1534
+
1535
+ var
1536
+ railHeight = volumeTotal.height(),
1537
+ totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
1538
+ newY = e.pageY - totalOffset.top;
1539
+
1540
+ volume = (railHeight - newY) / railHeight;
1541
+
1542
+ // the controls just hide themselves (usually when mouse moves too far up)
1543
+ if (totalOffset.top == 0 || totalOffset.left == 0)
1544
+ return;
1545
+
1546
+ } else {
1547
+ var
1548
+ railWidth = volumeTotal.width(),
1549
+ newX = e.pageX - totalOffset.left;
1550
+
1551
+ volume = newX / railWidth;
1552
+ }
1553
+
1554
+ // ensure the volume isn't outside 0-1
1555
+ volume = Math.max(0,volume);
1556
+ volume = Math.min(volume,1);
1557
+
1558
+ // position the slider and handle
1559
+ positionVolumeHandle(volume);
1560
+
1561
+ // set the media object (this will trigger the volumechanged event)
1562
+ if (volume == 0) {
1563
+ media.setMuted(true);
1564
+ } else {
1565
+ media.setMuted(false);
1566
+ }
1567
+ media.setVolume(volume);
1568
+ },
1569
+ mouseIsDown = false,
1570
+ mouseIsOver = false;
1571
+
1572
+ // SLIDER
1573
+
1574
+ mute
1575
+ .hover(function() {
1576
+ volumeSlider.show();
1577
+ mouseIsOver = true;
1578
+ }, function() {
1579
+ mouseIsOver = false;
1580
+
1581
+ if (!mouseIsDown && mode == 'vertical') {
1582
+ volumeSlider.hide();
1583
+ }
1584
+ });
1585
+
1586
+ volumeSlider
1587
+ .bind('mouseover', function() {
1588
+ mouseIsOver = true;
1589
+ })
1590
+ .bind('mousedown', function (e) {
1591
+ handleVolumeMove(e);
1592
+ $(document)
1593
+ .bind('mousemove.vol', function(e) {
1594
+ handleVolumeMove(e);
1595
+ })
1596
+ .bind('mouseup.vol', function () {
1597
+ mouseIsDown = false;
1598
+ $(document).unbind('.vol');
1599
+
1600
+ if (!mouseIsOver && mode == 'vertical') {
1601
+ volumeSlider.hide();
1602
+ }
1603
+ });
1604
+ mouseIsDown = true;
1605
+
1606
+ return false;
1607
+ });
1608
+
1609
+
1610
+ // MUTE button
1611
+ mute.find('button').click(function() {
1612
+ media.setMuted( !media.muted );
1613
+ });
1614
+
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');
1624
+ }
1625
+ }
1626
+ }, false);
1627
+
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);
1635
+ }
1636
+ }
1637
+ }
1638
+ });
1639
+
1640
+ })(mejs.$);
1641
+
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.$);
2079
+
2080
+ (function($) {
2081
+
2082
+ // add extra default options
2083
+ $.extend(mejs.MepDefaults, {
2084
+ // this will automatically turn on a <track>
2085
+ startLanguage: '',
2086
+
2087
+ tracksText: 'Captions/Subtitles'
2088
+ });
2089
+
2090
+ $.extend(MediaElementPlayer.prototype, {
2091
+
2092
+ hasChapters: false,
2093
+
2094
+ buildtracks: function(player, controls, layers, media) {
2095
+ if (!player.isVideo)
2096
+ return;
2097
+
2098
+ if (player.tracks.length == 0)
2099
+ return;
2100
+
2101
+ var t= this, i, options = '';
2102
+
2103
+ player.chapters =
2104
+ $('<div class="mejs-chapters mejs-layer"></div>')
2105
+ .prependTo(layers).hide();
2106
+ player.captions =
2107
+ $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position"><span class="mejs-captions-text"></span></div></div>')
2108
+ .prependTo(layers).hide();
2109
+ player.captionsText = player.captions.find('.mejs-captions-text');
2110
+ player.captionsButton =
2111
+ $('<div class="mejs-button mejs-captions-button">'+
2112
+ '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '"></button>'+
2113
+ '<div class="mejs-captions-selector">'+
2114
+ '<ul>'+
2115
+ '<li>'+
2116
+ '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
2117
+ '<label for="' + player.id + '_captions_none">None</label>'+
2118
+ '</li>' +
2119
+ '</ul>'+
2120
+ '</div>'+
2121
+ '</div>')
2122
+ .appendTo(controls)
2123
+
2124
+ // hover
2125
+ .hover(function() {
2126
+ $(this).find('.mejs-captions-selector').css('visibility','visible');
2127
+ }, function() {
2128
+ $(this).find('.mejs-captions-selector').css('visibility','hidden');
2129
+ })
2130
+
2131
+ // handle clicks to the language radio buttons
2132
+ .delegate('input[type=radio]','click',function() {
2133
+ lang = this.value;
2134
+
2135
+ if (lang == 'none') {
2136
+ player.selectedTrack = null;
2137
+ } else {
2138
+ for (i=0; i<player.tracks.length; i++) {
2139
+ if (player.tracks[i].srclang == lang) {
2140
+ player.selectedTrack = player.tracks[i];
2141
+ player.captions.attr('lang', player.selectedTrack.srclang);
2142
+ player.displayCaptions();
2143
+ break;
2144
+ }
2145
+ }
2146
+ }
2147
+ });
2148
+ //.bind('mouseenter', function() {
2149
+ // player.captionsButton.find('.mejs-captions-selector').css('visibility','visible')
2150
+ //});
2151
+
2152
+ if (!player.options.alwaysShowControls) {
2153
+ // move with controls
2154
+ player.container
2155
+ .bind('mouseenter', function () {
2156
+ // push captions above controls
2157
+ player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
2158
+
2159
+ })
2160
+ .bind('mouseleave', function () {
2161
+ if (!media.paused) {
2162
+ // move back to normal place
2163
+ player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
2164
+ }
2165
+ });
2166
+ } else {
2167
+ player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
2168
+ }
2169
+
2170
+ player.trackToLoad = -1;
2171
+ player.selectedTrack = null;
2172
+ player.isLoadingTrack = false;
2173
+
2174
+
2175
+
2176
+ // add to list
2177
+ for (i=0; i<player.tracks.length; i++) {
2178
+ if (player.tracks[i].kind == 'subtitles') {
2179
+ player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
2180
+ }
2181
+ }
2182
+
2183
+ player.loadNextTrack();
2184
+
2185
+
2186
+ media.addEventListener('timeupdate',function(e) {
2187
+ player.displayCaptions();
2188
+ }, false);
2189
+
2190
+ media.addEventListener('loadedmetadata', function(e) {
2191
+ player.displayChapters();
2192
+ }, false);
2193
+
2194
+ player.container.hover(
2195
+ function () {
2196
+ // chapters
2197
+ if (player.hasChapters) {
2198
+ player.chapters.css('visibility','visible');
2199
+ player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
2200
+ }
2201
+ },
2202
+ function () {
2203
+ if (player.hasChapters && !media.paused) {
2204
+ player.chapters.fadeOut(200, function() {
2205
+ $(this).css('visibility','hidden');
2206
+ $(this).css('display','block');
2207
+ });
2208
+ }
2209
+ });
2210
+
2211
+ // check for autoplay
2212
+ if (player.node.getAttribute('autoplay') !== null) {
2213
+ player.chapters.css('visibility','hidden');
2214
+ }
2215
+ },
2216
+
2217
+ loadNextTrack: function() {
2218
+ var t = this;
2219
+
2220
+ t.trackToLoad++;
2221
+ if (t.trackToLoad < t.tracks.length) {
2222
+ t.isLoadingTrack = true;
2223
+ t.loadTrack(t.trackToLoad);
2224
+ } else {
2225
+ // add done?
2226
+ t.isLoadingTrack = false;
2227
+ }
2228
+ },
2229
+
2230
+ loadTrack: function(index){
2231
+ var
2232
+ t = this,
2233
+ track = t.tracks[index],
2234
+ after = function() {
2235
+
2236
+ track.isLoaded = true;
2237
+
2238
+ // create button
2239
+ //t.addTrackButton(track.srclang);
2240
+ t.enableTrackButton(track.srclang, track.label);
2241
+
2242
+ t.loadNextTrack();
2243
+
2244
+ };
2245
+
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
+
2251
+ // store the new translation
2252
+ track.entries = newOne;
2253
+
2254
+ 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
+
2266
+ if (track.kind == 'chapters' && t.media.duration > 0) {
2267
+ t.drawChapters(track);
2268
+ }
2269
+ },
2270
+ error: function() {
2271
+ t.loadNextTrack();
2272
+ }
2273
+ });
2274
+ }
2275
+ },
2276
+
2277
+ enableTrackButton: function(lang, label) {
2278
+ var t = this;
2279
+
2280
+ if (label === '') {
2281
+ label = mejs.language.codes[lang] || lang;
2282
+ }
2283
+
2284
+ t.captionsButton
2285
+ .find('input[value=' + lang + ']')
2286
+ .prop('disabled',false)
2287
+ .siblings('label')
2288
+ .html( label );
2289
+
2290
+ // auto select
2291
+ if (t.options.startLanguage == lang) {
2292
+ $('#' + t.id + '_captions_' + lang).click();
2293
+ }
2294
+
2295
+ t.adjustLanguageBox();
2296
+ },
2297
+
2298
+ addTrackButton: function(lang, label) {
2299
+ var t = this;
2300
+ if (label === '') {
2301
+ label = mejs.language.codes[lang] || lang;
2302
+ }
2303
+
2304
+ t.captionsButton.find('ul').append(
2305
+ $('<li>'+
2306
+ '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
2307
+ '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
2308
+ '</li>')
2309
+ );
2310
+
2311
+ t.adjustLanguageBox();
2312
+
2313
+ // remove this from the dropdownlist (if it exists)
2314
+ t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
2315
+ },
2316
+
2317
+ adjustLanguageBox:function() {
2318
+ var t = this;
2319
+ // adjust the size of the outer box
2320
+ t.captionsButton.find('.mejs-captions-selector').height(
2321
+ t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
2322
+ t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
2323
+ );
2324
+ },
2325
+
2326
+ displayCaptions: function() {
2327
+
2328
+ if (typeof this.tracks == 'undefined')
2329
+ return;
2330
+
2331
+ var
2332
+ t = this,
2333
+ i,
2334
+ track = t.selectedTrack;
2335
+
2336
+ if (track != null && track.isLoaded) {
2337
+ for (i=0; i<track.entries.times.length; i++) {
2338
+ if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
2339
+ t.captionsText.html(track.entries.text[i]);
2340
+ t.captions.show().height(0);
2341
+ return; // exit out if one is visible;
2342
+ }
2343
+ }
2344
+ t.captions.hide();
2345
+ } else {
2346
+ t.captions.hide();
2347
+ }
2348
+ },
2349
+
2350
+ displayChapters: function() {
2351
+ var
2352
+ t = this,
2353
+ i;
2354
+
2355
+ for (i=0; i<t.tracks.length; i++) {
2356
+ if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
2357
+ t.drawChapters(t.tracks[i]);
2358
+ t.hasChapters = true;
2359
+ break;
2360
+ }
2361
+ }
2362
+ },
2363
+
2364
+ drawChapters: function(chapters) {
2365
+ var
2366
+ t = this,
2367
+ i,
2368
+ dur,
2369
+ //width,
2370
+ //left,
2371
+ percent = 0,
2372
+ usedPercent = 0;
2373
+
2374
+ t.chapters.empty();
2375
+
2376
+ for (i=0; i<chapters.entries.times.length; i++) {
2377
+ dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
2378
+ percent = Math.floor(dur / t.media.duration * 100);
2379
+ if (percent + usedPercent > 100 || // too large
2380
+ i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
2381
+ {
2382
+ percent = 100 - usedPercent;
2383
+ }
2384
+ //width = Math.floor(t.width * dur / t.media.duration);
2385
+ //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
2386
+ //if (left + width > t.width) {
2387
+ // width = t.width - left;
2388
+ //}
2389
+
2390
+ t.chapters.append( $(
2391
+ '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
2392
+ '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
2393
+ '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
2394
+ '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
2395
+ '</div>' +
2396
+ '</div>'));
2397
+ usedPercent += percent;
2398
+ }
2399
+
2400
+ t.chapters.find('div.mejs-chapter').click(function() {
2401
+ t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
2402
+ if (t.media.paused) {
2403
+ t.media.play();
2404
+ }
2405
+ });
2406
+
2407
+ t.chapters.show();
2408
+ }
2409
+ });
2410
+
2411
+
2412
+
2413
+ mejs.language = {
2414
+ codes: {
2415
+ af:'Afrikaans',
2416
+ sq:'Albanian',
2417
+ ar:'Arabic',
2418
+ be:'Belarusian',
2419
+ bg:'Bulgarian',
2420
+ ca:'Catalan',
2421
+ zh:'Chinese',
2422
+ 'zh-cn':'Chinese Simplified',
2423
+ 'zh-tw':'Chinese Traditional',
2424
+ hr:'Croatian',
2425
+ cs:'Czech',
2426
+ da:'Danish',
2427
+ nl:'Dutch',
2428
+ en:'English',
2429
+ et:'Estonian',
2430
+ tl:'Filipino',
2431
+ fi:'Finnish',
2432
+ fr:'French',
2433
+ gl:'Galician',
2434
+ de:'German',
2435
+ el:'Greek',
2436
+ ht:'Haitian Creole',
2437
+ iw:'Hebrew',
2438
+ hi:'Hindi',
2439
+ hu:'Hungarian',
2440
+ is:'Icelandic',
2441
+ id:'Indonesian',
2442
+ ga:'Irish',
2443
+ it:'Italian',
2444
+ ja:'Japanese',
2445
+ ko:'Korean',
2446
+ lv:'Latvian',
2447
+ lt:'Lithuanian',
2448
+ mk:'Macedonian',
2449
+ ms:'Malay',
2450
+ mt:'Maltese',
2451
+ no:'Norwegian',
2452
+ fa:'Persian',
2453
+ pl:'Polish',
2454
+ pt:'Portuguese',
2455
+ //'pt-pt':'Portuguese (Portugal)',
2456
+ ro:'Romanian',
2457
+ ru:'Russian',
2458
+ sr:'Serbian',
2459
+ sk:'Slovak',
2460
+ sl:'Slovenian',
2461
+ es:'Spanish',
2462
+ sw:'Swahili',
2463
+ sv:'Swedish',
2464
+ tl:'Tagalog',
2465
+ th:'Thai',
2466
+ tr:'Turkish',
2467
+ uk:'Ukrainian',
2468
+ vi:'Vietnamese',
2469
+ cy:'Welsh',
2470
+ yi:'Yiddish'
2471
+ }
2472
+ };
2473
+
2474
+ /*
2475
+ Parses WebVVT format which should be formatted as
2476
+ ================================
2477
+ WEBVTT
2478
+
2479
+ 1
2480
+ 00:00:01,1 --> 00:00:05,000
2481
+ A line of text
2482
+
2483
+ 2
2484
+ 00:01:15,1 --> 00:02:05,000
2485
+ A second line of text
2486
+
2487
+ ===============================
2488
+
2489
+ Adapted from: http://www.delphiki.com/html5/playr
2490
+ */
2491
+ 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){
2517
+ 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];
2523
+ i++;
2524
+ }
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
+ }
2535
+ }
2536
+ }
2537
+
2538
+ return entries;
2539
+ }
2540
+ };
2541
+
2542
+ // test for browsers with bad String.split method.
2543
+ if ('x\n\ny'.split(/\n/gi).length != 3) {
2544
+ // add super slow IE8 and below version
2545
+ mejs.TrackFormatParser.split2 = function(text, regex) {
2546
+ var
2547
+ parts = [],
2548
+ chunk = '',
2549
+ i;
2550
+
2551
+ for (i=0; i<text.length; i++) {
2552
+ chunk += text.substring(i,i+1);
2553
+ if (regex.test(chunk)) {
2554
+ parts.push(chunk.replace(regex, ''));
2555
+ chunk = '';
2556
+ }
2557
+ }
2558
+ parts.push(chunk);
2559
+ return parts;
2560
+ }
2561
+ }
2562
+
2563
+ })(mejs.$);
2564
+
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
+
2757
+ })(mejs.$);