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.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +97 -0
- data/MIT-LICENSE +20 -0
- data/README.md +29 -0
- data/Rakefile +39 -0
- data/app/assets/flash/mediaelement_rails/flashmediaelement.swf +0 -0
- data/app/assets/images/mediaelement_rails/background.png +0 -0
- data/app/assets/images/mediaelement_rails/bigplay.png +0 -0
- data/app/assets/images/mediaelement_rails/controls-ted.png +0 -0
- data/app/assets/images/mediaelement_rails/controls-wmp-bg.png +0 -0
- data/app/assets/images/mediaelement_rails/controls-wmp.png +0 -0
- data/app/assets/images/mediaelement_rails/controls.png +0 -0
- data/app/assets/images/mediaelement_rails/loading.gif +0 -0
- data/app/assets/javascripts/mediaelement_rails/index.js +3 -0
- data/app/assets/javascripts/mediaelement_rails/mediaelement.js +943 -0
- data/app/assets/javascripts/mediaelement_rails/mediaelementplayer.js +1703 -0
- data/app/assets/javascripts/mediaelement_rails/rails.js.erb +5 -0
- data/app/assets/silverlight/mediaelement_rails/silverlightmediaelement.xap +0 -0
- data/app/assets/stylesheets/mediaelement_rails/index.css +1 -0
- data/app/assets/stylesheets/mediaelement_rails/mediaelementplayer.css.erb +571 -0
- data/app/assets/stylesheets/mediaelement_rails/mejs-skins.css.erb +283 -0
- data/app/controllers/mediaelement_rails/application_controller.rb +4 -0
- data/app/helpers/mediaelement_rails/application_helper.rb +4 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/app/views/layouts/mediaelement_rails/application.html.erb +14 -0
- data/config/routes.rb +2 -0
- data/lib/generators/mediaelement_update/USAGE +7 -0
- data/lib/generators/mediaelement_update/mediaelement_update_generator.rb +46 -0
- data/lib/mediaelement_rails/engine.rb +5 -0
- data/lib/mediaelement_rails/version.rb +3 -0
- data/lib/mediaelement_rails.rb +4 -0
- data/lib/tasks/mediaelement_rails_tasks.rake +4 -0
- data/mediaelement_rails.gemspec +20 -0
- data/script/rails +6 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.gitkeep +0 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +24 -0
- data/test/dummy/config/environments/production.rb +51 -0
- data/test/dummy/config/environments/test.rb +34 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/lib/assets/.gitkeep +0 -0
- data/test/dummy/log/.gitkeep +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/mediaelement_rails_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- 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) + '–' + 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
|
+
|