mediaelement_rails 0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+