mediaelement_rails 0.2

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