mediaelement_rails 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +97 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +29 -0
  6. data/Rakefile +39 -0
  7. data/app/assets/flash/mediaelement_rails/flashmediaelement.swf +0 -0
  8. data/app/assets/images/mediaelement_rails/background.png +0 -0
  9. data/app/assets/images/mediaelement_rails/bigplay.png +0 -0
  10. data/app/assets/images/mediaelement_rails/controls-ted.png +0 -0
  11. data/app/assets/images/mediaelement_rails/controls-wmp-bg.png +0 -0
  12. data/app/assets/images/mediaelement_rails/controls-wmp.png +0 -0
  13. data/app/assets/images/mediaelement_rails/controls.png +0 -0
  14. data/app/assets/images/mediaelement_rails/loading.gif +0 -0
  15. data/app/assets/javascripts/mediaelement_rails/index.js +3 -0
  16. data/app/assets/javascripts/mediaelement_rails/mediaelement.js +943 -0
  17. data/app/assets/javascripts/mediaelement_rails/mediaelementplayer.js +1703 -0
  18. data/app/assets/javascripts/mediaelement_rails/rails.js.erb +5 -0
  19. data/app/assets/silverlight/mediaelement_rails/silverlightmediaelement.xap +0 -0
  20. data/app/assets/stylesheets/mediaelement_rails/index.css +1 -0
  21. data/app/assets/stylesheets/mediaelement_rails/mediaelementplayer.css.erb +571 -0
  22. data/app/assets/stylesheets/mediaelement_rails/mejs-skins.css.erb +283 -0
  23. data/app/controllers/mediaelement_rails/application_controller.rb +4 -0
  24. data/app/helpers/mediaelement_rails/application_helper.rb +4 -0
  25. data/app/views/layouts/application.html.erb +14 -0
  26. data/app/views/layouts/mediaelement_rails/application.html.erb +14 -0
  27. data/config/routes.rb +2 -0
  28. data/lib/generators/mediaelement_update/USAGE +7 -0
  29. data/lib/generators/mediaelement_update/mediaelement_update_generator.rb +46 -0
  30. data/lib/mediaelement_rails/engine.rb +5 -0
  31. data/lib/mediaelement_rails/version.rb +3 -0
  32. data/lib/mediaelement_rails.rb +4 -0
  33. data/lib/tasks/mediaelement_rails_tasks.rake +4 -0
  34. data/mediaelement_rails.gemspec +20 -0
  35. data/script/rails +6 -0
  36. data/test/dummy/Rakefile +7 -0
  37. data/test/dummy/app/assets/javascripts/application.js +9 -0
  38. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  39. data/test/dummy/app/controllers/application_controller.rb +3 -0
  40. data/test/dummy/app/helpers/application_helper.rb +2 -0
  41. data/test/dummy/app/mailers/.gitkeep +0 -0
  42. data/test/dummy/app/models/.gitkeep +0 -0
  43. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  44. data/test/dummy/config/application.rb +45 -0
  45. data/test/dummy/config/boot.rb +10 -0
  46. data/test/dummy/config/database.yml +25 -0
  47. data/test/dummy/config/environment.rb +5 -0
  48. data/test/dummy/config/environments/development.rb +24 -0
  49. data/test/dummy/config/environments/production.rb +51 -0
  50. data/test/dummy/config/environments/test.rb +34 -0
  51. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/test/dummy/config/initializers/inflections.rb +10 -0
  53. data/test/dummy/config/initializers/mime_types.rb +5 -0
  54. data/test/dummy/config/initializers/secret_token.rb +7 -0
  55. data/test/dummy/config/initializers/session_store.rb +8 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
  57. data/test/dummy/config/locales/en.yml +5 -0
  58. data/test/dummy/config/routes.rb +4 -0
  59. data/test/dummy/config.ru +4 -0
  60. data/test/dummy/lib/assets/.gitkeep +0 -0
  61. data/test/dummy/log/.gitkeep +0 -0
  62. data/test/dummy/public/404.html +26 -0
  63. data/test/dummy/public/422.html +26 -0
  64. data/test/dummy/public/500.html +26 -0
  65. data/test/dummy/public/favicon.ico +0 -0
  66. data/test/dummy/script/rails +6 -0
  67. data/test/integration/navigation_test.rb +10 -0
  68. data/test/mediaelement_rails_test.rb +7 -0
  69. data/test/test_helper.rb +10 -0
  70. metadata +170 -0
@@ -0,0 +1,943 @@
1
+ /*!
2
+ * MediaElement.js
3
+ * HTML5 <video> and <audio> shim and player
4
+ * http://mediaelementjs.com/
5
+ *
6
+ * Creates a JavaScript object that mimics HTML5 MediaElement API
7
+ * for browsers that don't understand HTML5 or can't play the provided codec
8
+ * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
9
+ *
10
+ * Copyright 2010-2011, John Dyer (http://j.hn)
11
+ * Dual licensed under the MIT or GPL Version 2 licenses.
12
+ *
13
+ */
14
+ // Namespace
15
+ var mejs = mejs || {};
16
+
17
+ // version number
18
+ mejs.version = '2.1.9';
19
+
20
+ // player number (for missing, same id attr)
21
+ mejs.meIndex = 0;
22
+
23
+ // media types accepted by plugins
24
+ mejs.plugins = {
25
+ silverlight: [
26
+ {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
27
+ ],
28
+ flash: [
29
+ {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg']}
30
+ //,{version: [11,0], types: ['video/webm']} // for future reference
31
+ ]
32
+ };
33
+
34
+ /*
35
+ Utility methods
36
+ */
37
+ mejs.Utility = {
38
+ encodeUrl: function(url) {
39
+ return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
40
+ },
41
+ escapeHTML: function(s) {
42
+ return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
43
+ },
44
+ absolutizeUrl: function(url) {
45
+ var el = document.createElement('div');
46
+ el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
47
+ return el.firstChild.href;
48
+ },
49
+ getScriptPath: function(scriptNames) {
50
+ var
51
+ i = 0,
52
+ j,
53
+ path = '',
54
+ name = '',
55
+ script,
56
+ scripts = document.getElementsByTagName('script');
57
+
58
+ for (; i < scripts.length; i++) {
59
+ script = scripts[i].src;
60
+ for (j = 0; j < scriptNames.length; j++) {
61
+ name = scriptNames[j];
62
+ if (script.indexOf(name) > -1) {
63
+ path = script.substring(0, script.indexOf(name));
64
+ break;
65
+ }
66
+ }
67
+ if (path !== '') {
68
+ break;
69
+ }
70
+ }
71
+ return path;
72
+ },
73
+ secondsToTimeCode: function(seconds,forceHours) {
74
+ seconds = Math.round(seconds);
75
+ var hours,
76
+ minutes = Math.floor(seconds / 60);
77
+ if (minutes >= 60) {
78
+ hours = Math.floor(minutes / 60);
79
+ minutes = minutes % 60;
80
+ }
81
+ hours = hours === undefined ? "00" : (hours >= 10) ? hours : "0" + hours;
82
+ minutes = (minutes >= 10) ? minutes : "0" + minutes;
83
+ seconds = Math.floor(seconds % 60);
84
+ seconds = (seconds >= 10) ? seconds : "0" + seconds;
85
+ return ((hours > 0 || forceHours === true) ? hours + ":" :'') + minutes + ":" + seconds;
86
+ },
87
+ timeCodeToSeconds: function(timecode){
88
+ var tab = timecode.split(':');
89
+ return tab[0]*60*60 + tab[1]*60 + parseFloat(tab[2].replace(',','.'));
90
+ }
91
+ };
92
+
93
+
94
+ // Core detector, plugins are added below
95
+ mejs.PluginDetector = {
96
+
97
+ // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
98
+ hasPluginVersion: function(plugin, v) {
99
+ var pv = this.plugins[plugin];
100
+ v[1] = v[1] || 0;
101
+ v[2] = v[2] || 0;
102
+ return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
103
+ },
104
+
105
+ // cached values
106
+ nav: window.navigator,
107
+ ua: window.navigator.userAgent.toLowerCase(),
108
+
109
+ // stored version numbers
110
+ plugins: [],
111
+
112
+ // runs detectPlugin() and stores the version number
113
+ addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
114
+ this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
115
+ },
116
+
117
+ // get the version number from the mimetype (all but IE) or ActiveX (IE)
118
+ detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
119
+
120
+ var version = [0,0,0],
121
+ description,
122
+ i,
123
+ ax;
124
+
125
+ // Firefox, Webkit, Opera
126
+ if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
127
+ description = this.nav.plugins[pluginName].description;
128
+ if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
129
+ version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
130
+ for (i=0; i<version.length; i++) {
131
+ version[i] = parseInt(version[i].match(/\d+/), 10);
132
+ }
133
+ }
134
+ // Internet Explorer / ActiveX
135
+ } else if (typeof(window.ActiveXObject) != 'undefined') {
136
+ try {
137
+ ax = new ActiveXObject(activeX);
138
+ if (ax) {
139
+ version = axDetect(ax);
140
+ }
141
+ }
142
+ catch (e) { }
143
+ }
144
+ return version;
145
+ }
146
+ };
147
+
148
+ // Add Flash detection
149
+ mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
150
+ // adapted from SWFObject
151
+ var version = [],
152
+ d = ax.GetVariable("$version");
153
+ if (d) {
154
+ d = d.split(" ")[1].split(",");
155
+ version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
156
+ }
157
+ return version;
158
+ });
159
+
160
+ // Add Silverlight detection
161
+ mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
162
+ // Silverlight cannot report its version number to IE
163
+ // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
164
+ // adapted from http://www.silverlightversion.com/
165
+ var v = [0,0,0,0],
166
+ loopMatch = function(ax, v, i, n) {
167
+ while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
168
+ v[i]+=n;
169
+ }
170
+ v[i] -= n;
171
+ };
172
+ loopMatch(ax, v, 0, 1);
173
+ loopMatch(ax, v, 1, 1);
174
+ loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
175
+ loopMatch(ax, v, 2, 1000);
176
+ loopMatch(ax, v, 2, 100);
177
+ loopMatch(ax, v, 2, 10);
178
+ loopMatch(ax, v, 2, 1);
179
+ loopMatch(ax, v, 3, 1);
180
+
181
+ return v;
182
+ });
183
+ // add adobe acrobat
184
+ /*
185
+ PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
186
+ var version = [],
187
+ d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
188
+
189
+ if (d) {
190
+ version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
191
+ }
192
+ return version;
193
+ });
194
+ */
195
+ // necessary detection (fixes for <IE9)
196
+ mejs.MediaFeatures = {
197
+ init: function() {
198
+ var
199
+ nav = mejs.PluginDetector.nav,
200
+ ua = mejs.PluginDetector.ua.toLowerCase(),
201
+ i,
202
+ v,
203
+ html5Elements = ['source','track','audio','video'];
204
+
205
+ // detect browsers (only the ones that have some kind of quirk we need to work around)
206
+ this.isiPad = (ua.match(/ipad/i) !== null);
207
+ this.isiPhone = (ua.match(/iphone/i) !== null);
208
+ this.isAndroid = (ua.match(/android/i) !== null);
209
+ this.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
210
+ this.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1);
211
+ this.isChrome = (ua.match(/chrome/gi) !== null);
212
+ this.isFirefox = (ua.match(/firefox/gi) !== null);
213
+
214
+ // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
215
+ for (i=0; i<html5Elements.length; i++) {
216
+ v = document.createElement(html5Elements[i]);
217
+ }
218
+
219
+ // detect native JavaScript fullscreen (Safari only, Chrome fails)
220
+ this.hasNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
221
+ if (this.isChrome) {
222
+ this.hasNativeFullScreen = false;
223
+ }
224
+ // OS X 10.5 can't do this even if it says it can :(
225
+ if (this.hasNativeFullScreen && ua.match(/mac os x 10_5/i)) {
226
+ this.hasNativeFullScreen = false;
227
+ }
228
+ }
229
+ };
230
+ mejs.MediaFeatures.init();
231
+
232
+
233
+ /*
234
+ extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
235
+ */
236
+ mejs.HtmlMediaElement = {
237
+ pluginType: 'native',
238
+ isFullScreen: false,
239
+
240
+ setCurrentTime: function (time) {
241
+ this.currentTime = time;
242
+ },
243
+
244
+ setMuted: function (muted) {
245
+ this.muted = muted;
246
+ },
247
+
248
+ setVolume: function (volume) {
249
+ this.volume = volume;
250
+ },
251
+
252
+ // for parity with the plugin versions
253
+ stop: function () {
254
+ this.pause();
255
+ },
256
+
257
+ // This can be a url string
258
+ // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
259
+ setSrc: function (url) {
260
+ if (typeof url == 'string') {
261
+ this.src = url;
262
+ } else {
263
+ var i, media;
264
+
265
+ for (i=0; i<url.length; i++) {
266
+ media = url[i];
267
+ if (this.canPlayType(media.type)) {
268
+ this.src = media.src;
269
+ }
270
+ }
271
+ }
272
+ },
273
+
274
+ setVideoSize: function (width, height) {
275
+ this.width = width;
276
+ this.height = height;
277
+ }
278
+ };
279
+
280
+ /*
281
+ Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
282
+ */
283
+ mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
284
+ this.id = pluginid;
285
+ this.pluginType = pluginType;
286
+ this.src = mediaUrl;
287
+ this.events = {};
288
+ };
289
+
290
+ // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
291
+ // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
292
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
293
+ mejs.PluginMediaElement.prototype = {
294
+
295
+ // special
296
+ pluginElement: null,
297
+ pluginType: '',
298
+ isFullScreen: false,
299
+
300
+ // not implemented :(
301
+ playbackRate: -1,
302
+ defaultPlaybackRate: -1,
303
+ seekable: [],
304
+ played: [],
305
+
306
+ // HTML5 read-only properties
307
+ paused: true,
308
+ ended: false,
309
+ seeking: false,
310
+ duration: 0,
311
+ error: null,
312
+
313
+ // HTML5 get/set properties, but only set (updated by event handlers)
314
+ muted: false,
315
+ volume: 1,
316
+ currentTime: 0,
317
+
318
+ // HTML5 methods
319
+ play: function () {
320
+ if (this.pluginApi != null) {
321
+ this.pluginApi.playMedia();
322
+ this.paused = false;
323
+ }
324
+ },
325
+ load: function () {
326
+ if (this.pluginApi != null) {
327
+ this.pluginApi.loadMedia();
328
+ this.paused = false;
329
+ }
330
+ },
331
+ pause: function () {
332
+ if (this.pluginApi != null) {
333
+ this.pluginApi.pauseMedia();
334
+ this.paused = true;
335
+ }
336
+ },
337
+ stop: function () {
338
+ if (this.pluginApi != null) {
339
+ this.pluginApi.stopMedia();
340
+ this.paused = true;
341
+ }
342
+ },
343
+ canPlayType: function(type) {
344
+ var i,
345
+ j,
346
+ pluginInfo,
347
+ pluginVersions = mejs.plugins[this.pluginType];
348
+
349
+ for (i=0; i<pluginVersions.length; i++) {
350
+ pluginInfo = pluginVersions[i];
351
+
352
+ // test if user has the correct plugin version
353
+ if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
354
+
355
+ // test for plugin playback types
356
+ for (j=0; j<pluginInfo.types.length; j++) {
357
+ // find plugin that can play the type
358
+ if (type == pluginInfo.types[j]) {
359
+ return true;
360
+ }
361
+ }
362
+ }
363
+ }
364
+
365
+ return false;
366
+ },
367
+
368
+ // custom methods since not all JavaScript implementations support get/set
369
+
370
+ // This can be a url string
371
+ // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
372
+ setSrc: function (url) {
373
+ if (typeof url == 'string') {
374
+ this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
375
+ this.src = mejs.Utility.absolutizeUrl(url);
376
+ } else {
377
+ var i, media;
378
+
379
+ for (i=0; i<url.length; i++) {
380
+ media = url[i];
381
+ if (this.canPlayType(media.type)) {
382
+ this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
383
+ this.src = mejs.Utility.absolutizeUrl(url);
384
+ }
385
+ }
386
+ }
387
+
388
+ },
389
+ setCurrentTime: function (time) {
390
+ if (this.pluginApi != null) {
391
+ this.pluginApi.setCurrentTime(time);
392
+ this.currentTime = time;
393
+ }
394
+ },
395
+ setVolume: function (volume) {
396
+ if (this.pluginApi != null) {
397
+ this.pluginApi.setVolume(volume);
398
+ this.volume = volume;
399
+ }
400
+ },
401
+ setMuted: function (muted) {
402
+ if (this.pluginApi != null) {
403
+ this.pluginApi.setMuted(muted);
404
+ this.muted = muted;
405
+ }
406
+ },
407
+
408
+ // additional non-HTML5 methods
409
+ setVideoSize: function (width, height) {
410
+ if ( this.pluginElement.style) {
411
+ this.pluginElement.style.width = width + 'px';
412
+ this.pluginElement.style.height = height + 'px';
413
+ }
414
+ if (this.pluginApi != null) {
415
+ this.pluginApi.setVideoSize(width, height);
416
+ }
417
+ },
418
+
419
+ setFullscreen: function (fullscreen) {
420
+ if (this.pluginApi != null) {
421
+ this.pluginApi.setFullscreen(fullscreen);
422
+ }
423
+ },
424
+
425
+ // start: fake events
426
+ addEventListener: function (eventName, callback, bubble) {
427
+ this.events[eventName] = this.events[eventName] || [];
428
+ this.events[eventName].push(callback);
429
+ },
430
+ removeEventListener: function (eventName, callback) {
431
+ if (!eventName) { this.events = {}; return true; }
432
+ var callbacks = this.events[eventName];
433
+ if (!callbacks) return true;
434
+ if (!callback) { this.events[eventName] = []; return true; }
435
+ for (i = 0; i < callbacks.length; i++) {
436
+ if (callbacks[i] === callback) {
437
+ this.events[eventName].splice(i, 1);
438
+ return true;
439
+ }
440
+ }
441
+ return false;
442
+ },
443
+ dispatchEvent: function (eventName) {
444
+ var i,
445
+ args,
446
+ callbacks = this.events[eventName];
447
+
448
+ if (callbacks) {
449
+ args = Array.prototype.slice.call(arguments, 1);
450
+ for (i = 0; i < callbacks.length; i++) {
451
+ callbacks[i].apply(null, args);
452
+ }
453
+ }
454
+ }
455
+ // end: fake events
456
+ };
457
+
458
+
459
+ // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
460
+ mejs.MediaPluginBridge = {
461
+
462
+ pluginMediaElements:{},
463
+ htmlMediaElements:{},
464
+
465
+ registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
466
+ this.pluginMediaElements[id] = pluginMediaElement;
467
+ this.htmlMediaElements[id] = htmlMediaElement;
468
+ },
469
+
470
+ // when Flash/Silverlight is ready, it calls out to this method
471
+ initPlugin: function (id) {
472
+
473
+ var pluginMediaElement = this.pluginMediaElements[id],
474
+ htmlMediaElement = this.htmlMediaElements[id];
475
+
476
+ // find the javascript bridge
477
+ switch (pluginMediaElement.pluginType) {
478
+ case "flash":
479
+ pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
480
+ break;
481
+ case "silverlight":
482
+ pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
483
+ pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
484
+ break;
485
+ }
486
+
487
+ if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
488
+ pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
489
+ }
490
+ },
491
+
492
+ // receives events from Flash/Silverlight and sends them out as HTML5 media events
493
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
494
+ fireEvent: function (id, eventName, values) {
495
+
496
+ var
497
+ e,
498
+ i,
499
+ bufferedTime,
500
+ pluginMediaElement = this.pluginMediaElements[id];
501
+
502
+ pluginMediaElement.ended = false;
503
+ pluginMediaElement.paused = true;
504
+
505
+ // fake event object to mimic real HTML media event.
506
+ e = {
507
+ type: eventName,
508
+ target: pluginMediaElement
509
+ };
510
+
511
+ // attach all values to element and event object
512
+ for (i in values) {
513
+ pluginMediaElement[i] = values[i];
514
+ e[i] = values[i];
515
+ }
516
+
517
+ // fake the newer W3C buffered TimeRange (loaded and total have been removed)
518
+ bufferedTime = values.bufferedTime || 0;
519
+
520
+ e.target.buffered = e.buffered = {
521
+ start: function(index) {
522
+ return 0;
523
+ },
524
+ end: function (index) {
525
+ return bufferedTime;
526
+ },
527
+ length: 1
528
+ };
529
+
530
+ pluginMediaElement.dispatchEvent(e.type, e);
531
+ }
532
+ };
533
+
534
+ /*
535
+ Default options
536
+ */
537
+ mejs.MediaElementDefaults = {
538
+ // allows testing on HTML5, flash, silverlight
539
+ // auto: attempts to detect what the browser can do
540
+ // native: forces HTML5 playback
541
+ // shim: disallows HTML5, will attempt either Flash or Silverlight
542
+ // none: forces fallback view
543
+ mode: 'auto',
544
+ // remove or reorder to change plugin priority and availability
545
+ plugins: ['flash','silverlight'],
546
+ // shows debug errors on screen
547
+ enablePluginDebug: false,
548
+ // overrides the type specified, useful for dynamic instantiation
549
+ type: '',
550
+ // path to Flash and Silverlight plugins
551
+ pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
552
+ // name of flash file
553
+ flashName: 'flashmediaelement.swf',
554
+ // turns on the smoothing filter in Flash
555
+ enablePluginSmoothing: false,
556
+ // name of silverlight file
557
+ silverlightName: 'silverlightmediaelement.xap',
558
+ // default if the <video width> is not specified
559
+ defaultVideoWidth: 480,
560
+ // default if the <video height> is not specified
561
+ defaultVideoHeight: 270,
562
+ // overrides <video width>
563
+ pluginWidth: -1,
564
+ // overrides <video height>
565
+ pluginHeight: -1,
566
+ // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
567
+ // larger number is less accurate, but less strain on plugin->JavaScript bridge
568
+ timerRate: 250,
569
+ success: function () { },
570
+ error: function () { }
571
+ };
572
+
573
+ /*
574
+ Determines if a browser supports the <video> or <audio> element
575
+ and returns either the native element or a Flash/Silverlight version that
576
+ mimics HTML5 MediaElement
577
+ */
578
+ mejs.MediaElement = function (el, o) {
579
+ return mejs.HtmlMediaElementShim.create(el,o);
580
+ };
581
+
582
+ mejs.HtmlMediaElementShim = {
583
+
584
+ create: function(el, o) {
585
+ var
586
+ options = mejs.MediaElementDefaults,
587
+ htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
588
+ isVideo = (htmlMediaElement.tagName.toLowerCase() == 'video'),
589
+ supportsMediaTag = (typeof(htmlMediaElement.canPlayType) != 'undefined'),
590
+ playback = {method:'', url:''},
591
+ poster = htmlMediaElement.getAttribute('poster'),
592
+ autoplay = htmlMediaElement.getAttribute('autoplay'),
593
+ preload = htmlMediaElement.getAttribute('preload'),
594
+ controls = htmlMediaElement.getAttribute('controls'),
595
+ prop;
596
+
597
+ // extend options
598
+ for (prop in o) {
599
+ options[prop] = o[prop];
600
+ }
601
+
602
+ // check for real poster
603
+ poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
604
+ preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
605
+ autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
606
+ controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
607
+
608
+ // test for HTML5 and plugin capabilities
609
+ playback = this.determinePlayback(htmlMediaElement, options, isVideo, supportsMediaTag);
610
+
611
+ if (playback.method == 'native') {
612
+ // second fix for android
613
+ if (mejs.MediaFeatures.isBustedAndroid) {
614
+ htmlMediaElement.src = playback.url;
615
+ htmlMediaElement.addEventListener('click', function() {
616
+ htmlMediaElement.play();
617
+ }, true);
618
+ }
619
+
620
+ // add methods to native HTMLMediaElement
621
+ return this.updateNative( htmlMediaElement, options, autoplay, preload, playback);
622
+ } else if (playback.method !== '') {
623
+ // create plugin to mimic HTMLMediaElement
624
+ return this.createPlugin( htmlMediaElement, options, isVideo, playback.method, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster, autoplay, preload, controls);
625
+ } else {
626
+ // boo, no HTML5, no Flash, no Silverlight.
627
+ this.createErrorMessage( htmlMediaElement, options, (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '', poster );
628
+ }
629
+ },
630
+
631
+ determinePlayback: function(htmlMediaElement, options, isVideo, supportsMediaTag) {
632
+ var
633
+ mediaFiles = [],
634
+ i,
635
+ j,
636
+ k,
637
+ l,
638
+ n,
639
+ type,
640
+ result = { method: '', url: ''},
641
+ src = htmlMediaElement.getAttribute('src'),
642
+ pluginName,
643
+ pluginVersions,
644
+ pluginInfo;
645
+
646
+ // clean up src attr
647
+ if (src == 'undefined' || src == '' || src === null)
648
+ src = null;
649
+
650
+ // STEP 1: Get URL and type from <video src> or <source src>
651
+
652
+ // supplied type overrides all HTML
653
+ if (typeof (options.type) != 'undefined' && options.type !== '') {
654
+ mediaFiles.push({type:options.type, url:src});
655
+
656
+ // test for src attribute first
657
+ } else if (src !== null) {
658
+ type = this.checkType(src, htmlMediaElement.getAttribute('type'), isVideo);
659
+ mediaFiles.push({type:type, url:src});
660
+
661
+ // then test for <source> elements
662
+ } else {
663
+ // test <source> types to see if they are usable
664
+ for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
665
+ n = htmlMediaElement.childNodes[i];
666
+ if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
667
+ src = n.getAttribute('src');
668
+ type = this.checkType(src, n.getAttribute('type'), isVideo);
669
+ mediaFiles.push({type:type, url:src});
670
+ }
671
+ }
672
+ }
673
+
674
+ // STEP 2: Test for playback method
675
+
676
+ // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
677
+ if (mejs.MediaFeatures.isBustedAndroid) {
678
+ htmlMediaElement.canPlayType = function(type) {
679
+ return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
680
+ };
681
+ }
682
+
683
+
684
+ // test for native playback first
685
+ if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'native')) {
686
+ for (i=0; i<mediaFiles.length; i++) {
687
+ // normal check
688
+ if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
689
+ // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
690
+ || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '') {
691
+ result.method = 'native';
692
+ result.url = mediaFiles[i].url;
693
+ return result;
694
+ }
695
+ }
696
+ }
697
+
698
+ // if native playback didn't work, then test plugins
699
+ if (options.mode === 'auto' || options.mode === 'shim') {
700
+ for (i=0; i<mediaFiles.length; i++) {
701
+ type = mediaFiles[i].type;
702
+
703
+ // test all plugins in order of preference [silverlight, flash]
704
+ for (j=0; j<options.plugins.length; j++) {
705
+
706
+ pluginName = options.plugins[j];
707
+
708
+ // test version of plugin (for future features)
709
+ pluginVersions = mejs.plugins[pluginName];
710
+ for (k=0; k<pluginVersions.length; k++) {
711
+ pluginInfo = pluginVersions[k];
712
+
713
+ // test if user has the correct plugin version
714
+ if (mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
715
+
716
+ // test for plugin playback types
717
+ for (l=0; l<pluginInfo.types.length; l++) {
718
+ // find plugin that can play the type
719
+ if (type == pluginInfo.types[l]) {
720
+ result.method = pluginName;
721
+ result.url = mediaFiles[i].url;
722
+ return result;
723
+ }
724
+ }
725
+ }
726
+ }
727
+ }
728
+ }
729
+ }
730
+
731
+ // what if there's nothing to play? just grab the first available
732
+ if (result.method === '') {
733
+ result.url = mediaFiles[0].url;
734
+ }
735
+
736
+ return result;
737
+ },
738
+
739
+ checkType: function(url, type, isVideo) {
740
+ var ext;
741
+
742
+ // if no type is supplied, fake it with the extension
743
+ if (url && !type) {
744
+ ext = url.substring(url.lastIndexOf('.') + 1);
745
+ return ((isVideo) ? 'video' : 'audio') + '/' + ext;
746
+ } else {
747
+ // only return the mime part of the type in case the attribute contains the codec
748
+ // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
749
+ // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
750
+
751
+ if (type && ~type.indexOf(';')) {
752
+ return type.substr(0, type.indexOf(';'));
753
+ } else {
754
+ return type;
755
+ }
756
+ }
757
+ },
758
+
759
+ createErrorMessage: function(htmlMediaElement, options, downloadUrl, poster) {
760
+ var errorContainer = document.createElement('div');
761
+ errorContainer.className = 'me-cannotplay';
762
+
763
+ try {
764
+ errorContainer.style.width = htmlMediaElement.width + 'px';
765
+ errorContainer.style.height = htmlMediaElement.height + 'px';
766
+ } catch (e) {}
767
+
768
+ errorContainer.innerHTML = (poster !== '') ?
769
+ '<a href="' + downloadUrl + '"><img src="' + poster + '" /></a>' :
770
+ '<a href="' + downloadUrl + '"><span>Download File</span></a>';
771
+
772
+ htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
773
+ htmlMediaElement.style.display = 'none';
774
+
775
+ options.error(htmlMediaElement);
776
+ },
777
+
778
+ createPlugin:function(htmlMediaElement, options, isVideo, pluginType, mediaUrl, poster, autoplay, preload, controls) {
779
+ var width = 1,
780
+ height = 1,
781
+ pluginid = 'me_' + pluginType + '_' + (mejs.meIndex++),
782
+ pluginMediaElement = new mejs.PluginMediaElement(pluginid, pluginType, mediaUrl),
783
+ container = document.createElement('div'),
784
+ specialIEContainer,
785
+ node,
786
+ initVars;
787
+
788
+ // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
789
+ node = htmlMediaElement.parentNode;
790
+ while (node !== null && node.tagName.toLowerCase() != 'body') {
791
+ if (node.parentNode.tagName.toLowerCase() == 'p') {
792
+ node.parentNode.parentNode.insertBefore(node, node.parentNode);
793
+ break;
794
+ }
795
+ node = node.parentNode;
796
+ }
797
+
798
+ if (isVideo) {
799
+ width = (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
800
+ height = (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
801
+ } else {
802
+ if (options.enablePluginDebug) {
803
+ width = 320;
804
+ height = 240;
805
+ }
806
+ }
807
+
808
+ // register plugin
809
+ pluginMediaElement.success = options.success;
810
+ mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
811
+
812
+ // add container (must be added to DOM before inserting HTML for IE)
813
+ container.className = 'me-plugin';
814
+ htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
815
+
816
+ // flash/silverlight vars
817
+ initVars = [
818
+ 'id=' + pluginid,
819
+ 'isvideo=' + ((isVideo) ? "true" : "false"),
820
+ 'autoplay=' + ((autoplay) ? "true" : "false"),
821
+ 'preload=' + preload,
822
+ 'width=' + width,
823
+ 'startvolume=' + options.startVolume,
824
+ 'timerrate=' + options.timerRate,
825
+ 'height=' + height];
826
+
827
+ if (mediaUrl !== null) {
828
+ if (pluginType == 'flash') {
829
+ initVars.push('file=' + mejs.Utility.encodeUrl(mediaUrl));
830
+ } else {
831
+ initVars.push('file=' + mediaUrl);
832
+ }
833
+ }
834
+ if (options.enablePluginDebug) {
835
+ initVars.push('debug=true');
836
+ }
837
+ if (options.enablePluginSmoothing) {
838
+ initVars.push('smoothing=true');
839
+ }
840
+ if (controls) {
841
+ initVars.push('controls=true'); // shows controls in the plugin if desired
842
+ }
843
+
844
+ switch (pluginType) {
845
+ case 'silverlight':
846
+ container.innerHTML =
847
+ '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '">' +
848
+ '<param name="initParams" value="' + initVars.join(',') + '" />' +
849
+ '<param name="windowless" value="true" />' +
850
+ '<param name="background" value="black" />' +
851
+ '<param name="minRuntimeVersion" value="3.0.0.0" />' +
852
+ '<param name="autoUpgrade" value="true" />' +
853
+ '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
854
+ '</object>';
855
+ break;
856
+
857
+ case 'flash':
858
+
859
+ if (mejs.MediaFeatures.isIE) {
860
+ specialIEContainer = document.createElement('div');
861
+ container.appendChild(specialIEContainer);
862
+ specialIEContainer.outerHTML =
863
+ '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
864
+ 'id="' + pluginid + '" width="' + width + '" height="' + height + '">' +
865
+ '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
866
+ '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
867
+ '<param name="quality" value="high" />' +
868
+ '<param name="bgcolor" value="#000000" />' +
869
+ '<param name="wmode" value="transparent" />' +
870
+ '<param name="allowScriptAccess" value="always" />' +
871
+ '<param name="allowFullScreen" value="true" />' +
872
+ '</object>';
873
+
874
+ } else {
875
+
876
+ container.innerHTML =
877
+ '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
878
+ 'play="true" ' +
879
+ 'loop="false" ' +
880
+ 'quality="high" ' +
881
+ 'bgcolor="#000000" ' +
882
+ 'wmode="transparent" ' +
883
+ 'allowScriptAccess="always" ' +
884
+ 'allowFullScreen="true" ' +
885
+ 'type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" ' +
886
+ 'src="' + options.pluginPath + options.flashName + '" ' +
887
+ 'flashvars="' + initVars.join('&') + '" ' +
888
+ 'width="' + width + '" ' +
889
+ 'height="' + height + '"></embed>';
890
+ }
891
+ break;
892
+ }
893
+ // hide original element
894
+ htmlMediaElement.style.display = 'none';
895
+
896
+ // FYI: options.success will be fired by the MediaPluginBridge
897
+
898
+ return pluginMediaElement;
899
+ },
900
+
901
+ updateNative: function(htmlMediaElement, options, autoplay, preload, playback) {
902
+ // add methods to video object to bring it into parity with Flash Object
903
+ for (var m in mejs.HtmlMediaElement) {
904
+ htmlMediaElement[m] = mejs.HtmlMediaElement[m];
905
+ }
906
+
907
+ /*
908
+ Chrome now supports preload="none"
909
+ if (mejs.MediaFeatures.isChrome) {
910
+
911
+ // special case to enforce preload attribute (Chrome doesn't respect this)
912
+ if (preload === 'none' && !autoplay) {
913
+
914
+ // forces the browser to stop loading (note: fails in IE9)
915
+ htmlMediaElement.src = '';
916
+ htmlMediaElement.load();
917
+ htmlMediaElement.canceledPreload = true;
918
+
919
+ htmlMediaElement.addEventListener('play',function() {
920
+ if (htmlMediaElement.canceledPreload) {
921
+ htmlMediaElement.src = playback.url;
922
+ htmlMediaElement.load();
923
+ htmlMediaElement.play();
924
+ htmlMediaElement.canceledPreload = false;
925
+ }
926
+ }, false);
927
+ // for some reason Chrome forgets how to autoplay sometimes.
928
+ } else if (autoplay) {
929
+ htmlMediaElement.load();
930
+ htmlMediaElement.play();
931
+ }
932
+ }
933
+ */
934
+
935
+ // fire success code
936
+ options.success(htmlMediaElement, htmlMediaElement);
937
+
938
+ return htmlMediaElement;
939
+ }
940
+ };
941
+
942
+ window.mejs = mejs;
943
+ window.MediaElement = mejs.MediaElement;