rdoroshenko_mediaelement_rails 0.4.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.
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.$);