rdoroshenko_mediaelement_rails 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/.gitignore +9 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +48 -0
  5. data/Rakefile +12 -0
  6. data/app/assets/images/mediaelement_rails/background.png +0 -0
  7. data/app/assets/images/mediaelement_rails/bigplay.png +0 -0
  8. data/app/assets/images/mediaelement_rails/controls-ted.png +0 -0
  9. data/app/assets/images/mediaelement_rails/controls-wmp-bg.png +0 -0
  10. data/app/assets/images/mediaelement_rails/controls-wmp.png +0 -0
  11. data/app/assets/images/mediaelement_rails/controls.png +0 -0
  12. data/app/assets/images/mediaelement_rails/loading.gif +0 -0
  13. data/app/assets/javascripts/mediaelement_rails/index.js +3 -0
  14. data/app/assets/javascripts/mediaelement_rails/mediaelement.js +1544 -0
  15. data/app/assets/javascripts/mediaelement_rails/mediaelementplayer.js +2757 -0
  16. data/app/assets/javascripts/mediaelement_rails/rails.js.erb +5 -0
  17. data/app/assets/plugins/mediaelement_rails/flashmediaelement.swf +0 -0
  18. data/app/assets/plugins/mediaelement_rails/silverlightmediaelement.xap +0 -0
  19. data/app/assets/stylesheets/mediaelement_rails/index.css +1 -0
  20. data/app/assets/stylesheets/mediaelement_rails/mediaelementplayer.css.erb +801 -0
  21. data/app/assets/stylesheets/mediaelement_rails/mejs-skins.css.erb +283 -0
  22. data/lib/mediaelement_rails.rb +5 -0
  23. data/lib/mediaelement_rails/engine.rb +11 -0
  24. data/lib/mediaelement_rails/version.rb +3 -0
  25. data/mediaelement_rails.gemspec +28 -0
  26. data/mediaelement_rails.thor +83 -0
  27. data/script/rails +5 -0
  28. data/test/dummy/Rakefile +7 -0
  29. data/test/dummy/app/assets/javascripts/mediaelement-and-player.js +1 -0
  30. data/test/dummy/app/assets/javascripts/mediaelement-without-player.js +1 -0
  31. data/test/dummy/app/assets/stylesheets/player-skins.css +3 -0
  32. data/test/dummy/app/assets/stylesheets/player.css +3 -0
  33. data/test/dummy/app/controllers/application_controller.rb +3 -0
  34. data/test/dummy/app/helpers/application_helper.rb +2 -0
  35. data/test/dummy/app/mailers/.gitkeep +0 -0
  36. data/test/dummy/app/models/.gitkeep +0 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/config.ru +4 -0
  39. data/test/dummy/config/application.rb +42 -0
  40. data/test/dummy/config/boot.rb +10 -0
  41. data/test/dummy/config/database.yml +25 -0
  42. data/test/dummy/config/environment.rb +5 -0
  43. data/test/dummy/config/environments/development.rb +24 -0
  44. data/test/dummy/config/environments/production.rb +51 -0
  45. data/test/dummy/config/environments/test.rb +34 -0
  46. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  47. data/test/dummy/config/initializers/inflections.rb +10 -0
  48. data/test/dummy/config/initializers/mime_types.rb +5 -0
  49. data/test/dummy/config/initializers/secret_token.rb +7 -0
  50. data/test/dummy/config/initializers/session_store.rb +8 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
  52. data/test/dummy/config/locales/en.yml +5 -0
  53. data/test/dummy/config/routes.rb +3 -0
  54. data/test/dummy/db/.gitkeep +0 -0
  55. data/test/dummy/lib/assets/.gitkeep +0 -0
  56. data/test/dummy/log/.gitkeep +0 -0
  57. data/test/dummy/public/404.html +26 -0
  58. data/test/dummy/public/422.html +26 -0
  59. data/test/dummy/public/500.html +26 -0
  60. data/test/dummy/public/favicon.ico +0 -0
  61. data/test/dummy/script/rails +6 -0
  62. data/test/integration/assets_test.rb +76 -0
  63. data/test/test_helper.rb +11 -0
  64. data/vendor/.gitkeep +0 -0
  65. metadata +235 -0
@@ -0,0 +1,9 @@
1
+ *.DS_Store
2
+ .bundle/
3
+ pkg/
4
+ Gemfile.lock
5
+ log/*.log
6
+ test/dummy/db/*.sqlite3
7
+ test/dummy/log/*.log
8
+ test/dummy/tmp/
9
+ vendor/mediaelement
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in medialement_rails.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ # MediaelementRails #
2
+
3
+ This neat project brings the cool [MediaElement.js](http://mediaelementjs.com/) (HTML5/Flash/Silverlight video player) to the Rails asset pipeline.
4
+
5
+ ## All you have to do is: ##
6
+
7
+ Add the gem to the list of required gems in your `Gemfile`:
8
+
9
+ ``` ruby
10
+ # ...
11
+ gem "mediaelement_rails"
12
+ # ...
13
+ ```
14
+
15
+ ### Javascript ###
16
+
17
+ Load the Mediaelement Javascript in your `application.js`:
18
+
19
+ ``` javascript
20
+ //= require mediaelement_rails
21
+ ```
22
+
23
+ ### And CSS ###
24
+
25
+ Load the Mediaelement CSS in your `application.css`:
26
+
27
+ ``` css
28
+ /*
29
+ *= require mediaelement_rails
30
+ * and optionally:
31
+ *= require mediaelement_rails/mejs-skins
32
+ */
33
+ ```
34
+
35
+ ## Wanna use MediaElement (not the player) only? ##
36
+
37
+ This is easy as hell too!
38
+ Don't include any CSS and include the following in your `application.js` to get it working:
39
+
40
+ ``` javascript
41
+ //= require mediaelement_rails/rails
42
+ ```
43
+
44
+ ## Anything else I should know? ##
45
+
46
+ Nothing special! This project includes all assets you might need.
47
+
48
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require "rake/testtask"
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << "lib"
7
+ t.libs << "test"
8
+ t.pattern = "test/**/*_test.rb"
9
+ t.verbose = false
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ //= require jquery
2
+ //= require ./rails
3
+ //= require ./mediaelementplayer
@@ -0,0 +1,1544 @@
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-2012, 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.9.1';
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', 'video/youtube', 'video/x-youtube']}
30
+ //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
31
+ ],
32
+ youtube: [
33
+ {version: null, types: ['video/youtube', 'video/x-youtube']}
34
+ ],
35
+ vimeo: [
36
+ {version: null, types: ['video/vimeo']}
37
+ ]
38
+ };
39
+
40
+ /*
41
+ Utility methods
42
+ */
43
+ mejs.Utility = {
44
+ encodeUrl: function(url) {
45
+ return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
46
+ },
47
+ escapeHTML: function(s) {
48
+ return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
49
+ },
50
+ absolutizeUrl: function(url) {
51
+ var el = document.createElement('div');
52
+ el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
53
+ return el.firstChild.href;
54
+ },
55
+ getScriptPath: function(scriptNames) {
56
+ var
57
+ i = 0,
58
+ j,
59
+ path = '',
60
+ name = '',
61
+ script,
62
+ scripts = document.getElementsByTagName('script'),
63
+ il = scripts.length,
64
+ jl = scriptNames.length;
65
+
66
+ for (; i < il; i++) {
67
+ script = scripts[i].src;
68
+ for (j = 0; j < jl; j++) {
69
+ name = scriptNames[j];
70
+ if (script.indexOf(name) > -1) {
71
+ path = script.substring(0, script.indexOf(name));
72
+ break;
73
+ }
74
+ }
75
+ if (path !== '') {
76
+ break;
77
+ }
78
+ }
79
+ return path;
80
+ },
81
+ secondsToTimeCode: function(time, forceHours, showFrameCount, fps) {
82
+ //add framecount
83
+ if (typeof showFrameCount == 'undefined') {
84
+ showFrameCount=false;
85
+ } else if(typeof fps == 'undefined') {
86
+ fps = 25;
87
+ }
88
+
89
+ var hours = Math.floor(time / 3600) % 24,
90
+ minutes = Math.floor(time / 60) % 60,
91
+ seconds = Math.floor(time % 60),
92
+ frames = Math.floor(((time % 1)*fps).toFixed(3)),
93
+ result =
94
+ ( (forceHours || hours > 0) ? (hours < 10 ? '0' + hours : hours) + ':' : '')
95
+ + (minutes < 10 ? '0' + minutes : minutes) + ':'
96
+ + (seconds < 10 ? '0' + seconds : seconds)
97
+ + ((showFrameCount) ? ':' + (frames < 10 ? '0' + frames : frames) : '');
98
+
99
+ return result;
100
+ },
101
+
102
+ timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
103
+ if (typeof showFrameCount == 'undefined') {
104
+ showFrameCount=false;
105
+ } else if(typeof fps == 'undefined') {
106
+ fps = 25;
107
+ }
108
+
109
+ var tc_array = hh_mm_ss_ff.split(":"),
110
+ tc_hh = parseInt(tc_array[0], 10),
111
+ tc_mm = parseInt(tc_array[1], 10),
112
+ tc_ss = parseInt(tc_array[2], 10),
113
+ tc_ff = 0,
114
+ tc_in_seconds = 0;
115
+
116
+ if (showFrameCount) {
117
+ tc_ff = parseInt(tc_array[3])/fps;
118
+ }
119
+
120
+ tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
121
+
122
+ return tc_in_seconds;
123
+ },
124
+
125
+ /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
126
+ removeSwf: function(id) {
127
+ var obj = document.getElementById(id);
128
+ if (obj && obj.nodeName == "OBJECT") {
129
+ if (mejs.MediaFeatures.isIE) {
130
+ obj.style.display = "none";
131
+ (function(){
132
+ if (obj.readyState == 4) {
133
+ mejs.Utility.removeObjectInIE(id);
134
+ } else {
135
+ setTimeout(arguments.callee, 10);
136
+ }
137
+ })();
138
+ } else {
139
+ obj.parentNode.removeChild(obj);
140
+ }
141
+ }
142
+ },
143
+ removeObjectInIE: function(id) {
144
+ var obj = document.getElementById(id);
145
+ if (obj) {
146
+ for (var i in obj) {
147
+ if (typeof obj[i] == "function") {
148
+ obj[i] = null;
149
+ }
150
+ }
151
+ obj.parentNode.removeChild(obj);
152
+ }
153
+ }
154
+ };
155
+
156
+
157
+ // Core detector, plugins are added below
158
+ mejs.PluginDetector = {
159
+
160
+ // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
161
+ hasPluginVersion: function(plugin, v) {
162
+ var pv = this.plugins[plugin];
163
+ v[1] = v[1] || 0;
164
+ v[2] = v[2] || 0;
165
+ 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;
166
+ },
167
+
168
+ // cached values
169
+ nav: window.navigator,
170
+ ua: window.navigator.userAgent.toLowerCase(),
171
+
172
+ // stored version numbers
173
+ plugins: [],
174
+
175
+ // runs detectPlugin() and stores the version number
176
+ addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
177
+ this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
178
+ },
179
+
180
+ // get the version number from the mimetype (all but IE) or ActiveX (IE)
181
+ detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
182
+
183
+ var version = [0,0,0],
184
+ description,
185
+ i,
186
+ ax;
187
+
188
+ // Firefox, Webkit, Opera
189
+ if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
190
+ description = this.nav.plugins[pluginName].description;
191
+ if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
192
+ version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
193
+ for (i=0; i<version.length; i++) {
194
+ version[i] = parseInt(version[i].match(/\d+/), 10);
195
+ }
196
+ }
197
+ // Internet Explorer / ActiveX
198
+ } else if (typeof(window.ActiveXObject) != 'undefined') {
199
+ try {
200
+ ax = new ActiveXObject(activeX);
201
+ if (ax) {
202
+ version = axDetect(ax);
203
+ }
204
+ }
205
+ catch (e) { }
206
+ }
207
+ return version;
208
+ }
209
+ };
210
+
211
+ // Add Flash detection
212
+ mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
213
+ // adapted from SWFObject
214
+ var version = [],
215
+ d = ax.GetVariable("$version");
216
+ if (d) {
217
+ d = d.split(" ")[1].split(",");
218
+ version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
219
+ }
220
+ return version;
221
+ });
222
+
223
+ // Add Silverlight detection
224
+ mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
225
+ // Silverlight cannot report its version number to IE
226
+ // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
227
+ // adapted from http://www.silverlightversion.com/
228
+ var v = [0,0,0,0],
229
+ loopMatch = function(ax, v, i, n) {
230
+ while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
231
+ v[i]+=n;
232
+ }
233
+ v[i] -= n;
234
+ };
235
+ loopMatch(ax, v, 0, 1);
236
+ loopMatch(ax, v, 1, 1);
237
+ loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
238
+ loopMatch(ax, v, 2, 1000);
239
+ loopMatch(ax, v, 2, 100);
240
+ loopMatch(ax, v, 2, 10);
241
+ loopMatch(ax, v, 2, 1);
242
+ loopMatch(ax, v, 3, 1);
243
+
244
+ return v;
245
+ });
246
+ // add adobe acrobat
247
+ /*
248
+ PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
249
+ var version = [],
250
+ d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
251
+
252
+ if (d) {
253
+ version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
254
+ }
255
+ return version;
256
+ });
257
+ */
258
+ // necessary detection (fixes for <IE9)
259
+ mejs.MediaFeatures = {
260
+ init: function() {
261
+ var
262
+ t = this,
263
+ d = document,
264
+ nav = mejs.PluginDetector.nav,
265
+ ua = mejs.PluginDetector.ua.toLowerCase(),
266
+ i,
267
+ v,
268
+ html5Elements = ['source','track','audio','video'];
269
+
270
+ // detect browsers (only the ones that have some kind of quirk we need to work around)
271
+ t.isiPad = (ua.match(/ipad/i) !== null);
272
+ t.isiPhone = (ua.match(/iphone/i) !== null);
273
+ t.isiOS = t.isiPhone || t.isiPad;
274
+ t.isAndroid = (ua.match(/android/i) !== null);
275
+ t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
276
+ t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1);
277
+ t.isChrome = (ua.match(/chrome/gi) !== null);
278
+ t.isFirefox = (ua.match(/firefox/gi) !== null);
279
+ t.isWebkit = (ua.match(/webkit/gi) !== null);
280
+ t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit;
281
+ t.isOpera = (ua.match(/opera/gi) !== null);
282
+ t.hasTouch = ('ontouchstart' in window);
283
+
284
+ // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
285
+ for (i=0; i<html5Elements.length; i++) {
286
+ v = document.createElement(html5Elements[i]);
287
+ }
288
+
289
+ t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
290
+
291
+ // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
292
+
293
+ // iOS
294
+ t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
295
+
296
+ // Webkit/firefox
297
+ t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
298
+ t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
299
+
300
+ t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen);
301
+ t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
302
+ if (t.hasMozNativeFullScreen) {
303
+ t.nativeFullScreenEnabled = v.mozFullScreenEnabled;
304
+ }
305
+
306
+
307
+ if (this.isChrome) {
308
+ t.hasSemiNativeFullScreen = false;
309
+ }
310
+
311
+ if (t.hasTrueNativeFullScreen) {
312
+ t.fullScreenEventName = (t.hasWebkitNativeFullScreen) ? 'webkitfullscreenchange' : 'mozfullscreenchange';
313
+
314
+
315
+ t.isFullScreen = function() {
316
+ if (v.mozRequestFullScreen) {
317
+ return d.mozFullScreen;
318
+ } else if (v.webkitRequestFullScreen) {
319
+ return d.webkitIsFullScreen;
320
+ }
321
+ }
322
+
323
+ t.requestFullScreen = function(el) {
324
+
325
+ if (t.hasWebkitNativeFullScreen) {
326
+ el.webkitRequestFullScreen();
327
+ } else if (t.hasMozNativeFullScreen) {
328
+ el.mozRequestFullScreen();
329
+ }
330
+ }
331
+
332
+ t.cancelFullScreen = function() {
333
+ if (t.hasWebkitNativeFullScreen) {
334
+ document.webkitCancelFullScreen();
335
+ } else if (t.hasMozNativeFullScreen) {
336
+ document.mozCancelFullScreen();
337
+ }
338
+ }
339
+
340
+ }
341
+
342
+
343
+ // OS X 10.5 can't do this even if it says it can :(
344
+ if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
345
+ t.hasNativeFullScreen = false;
346
+ t.hasSemiNativeFullScreen = false;
347
+ }
348
+
349
+ }
350
+ };
351
+ mejs.MediaFeatures.init();
352
+
353
+
354
+ /*
355
+ extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
356
+ */
357
+ mejs.HtmlMediaElement = {
358
+ pluginType: 'native',
359
+ isFullScreen: false,
360
+
361
+ setCurrentTime: function (time) {
362
+ this.currentTime = time;
363
+ },
364
+
365
+ setMuted: function (muted) {
366
+ this.muted = muted;
367
+ },
368
+
369
+ setVolume: function (volume) {
370
+ this.volume = volume;
371
+ },
372
+
373
+ // for parity with the plugin versions
374
+ stop: function () {
375
+ this.pause();
376
+ },
377
+
378
+ // This can be a url string
379
+ // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
380
+ setSrc: function (url) {
381
+
382
+ // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
383
+ var
384
+ existingSources = this.getElementsByTagName('source');
385
+ while (existingSources.length > 0){
386
+ this.removeChild(existingSources[0]);
387
+ }
388
+
389
+ if (typeof url == 'string') {
390
+ this.src = url;
391
+ } else {
392
+ var i, media;
393
+
394
+ for (i=0; i<url.length; i++) {
395
+ media = url[i];
396
+ if (this.canPlayType(media.type)) {
397
+ this.src = media.src;
398
+ }
399
+ }
400
+ }
401
+ },
402
+
403
+ setVideoSize: function (width, height) {
404
+ this.width = width;
405
+ this.height = height;
406
+ }
407
+ };
408
+
409
+ /*
410
+ Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
411
+ */
412
+ mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
413
+ this.id = pluginid;
414
+ this.pluginType = pluginType;
415
+ this.src = mediaUrl;
416
+ this.events = {};
417
+ };
418
+
419
+ // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
420
+ // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
421
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
422
+ mejs.PluginMediaElement.prototype = {
423
+
424
+ // special
425
+ pluginElement: null,
426
+ pluginType: '',
427
+ isFullScreen: false,
428
+
429
+ // not implemented :(
430
+ playbackRate: -1,
431
+ defaultPlaybackRate: -1,
432
+ seekable: [],
433
+ played: [],
434
+
435
+ // HTML5 read-only properties
436
+ paused: true,
437
+ ended: false,
438
+ seeking: false,
439
+ duration: 0,
440
+ error: null,
441
+ tagName: '',
442
+
443
+ // HTML5 get/set properties, but only set (updated by event handlers)
444
+ muted: false,
445
+ volume: 1,
446
+ currentTime: 0,
447
+
448
+ // HTML5 methods
449
+ play: function () {
450
+ if (this.pluginApi != null) {
451
+ if (this.pluginType == 'youtube') {
452
+ this.pluginApi.playVideo();
453
+ } else {
454
+ this.pluginApi.playMedia();
455
+ }
456
+ this.paused = false;
457
+ }
458
+ },
459
+ load: function () {
460
+ if (this.pluginApi != null) {
461
+ if (this.pluginType == 'youtube') {
462
+ } else {
463
+ this.pluginApi.loadMedia();
464
+ }
465
+
466
+ this.paused = false;
467
+ }
468
+ },
469
+ pause: function () {
470
+ if (this.pluginApi != null) {
471
+ if (this.pluginType == 'youtube') {
472
+ this.pluginApi.pauseVideo();
473
+ } else {
474
+ this.pluginApi.pauseMedia();
475
+ }
476
+
477
+
478
+ this.paused = true;
479
+ }
480
+ },
481
+ stop: function () {
482
+ if (this.pluginApi != null) {
483
+ if (this.pluginType == 'youtube') {
484
+ this.pluginApi.stopVideo();
485
+ } else {
486
+ this.pluginApi.stopMedia();
487
+ }
488
+ this.paused = true;
489
+ }
490
+ },
491
+ canPlayType: function(type) {
492
+ var i,
493
+ j,
494
+ pluginInfo,
495
+ pluginVersions = mejs.plugins[this.pluginType];
496
+
497
+ for (i=0; i<pluginVersions.length; i++) {
498
+ pluginInfo = pluginVersions[i];
499
+
500
+ // test if user has the correct plugin version
501
+ if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
502
+
503
+ // test for plugin playback types
504
+ for (j=0; j<pluginInfo.types.length; j++) {
505
+ // find plugin that can play the type
506
+ if (type == pluginInfo.types[j]) {
507
+ return true;
508
+ }
509
+ }
510
+ }
511
+ }
512
+
513
+ return false;
514
+ },
515
+
516
+ positionFullscreenButton: function(x,y,visibleAndAbove) {
517
+ if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
518
+ this.pluginApi.positionFullscreenButton(x,y,visibleAndAbove);
519
+ }
520
+ },
521
+
522
+ hideFullscreenButton: function() {
523
+ if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
524
+ this.pluginApi.hideFullscreenButton();
525
+ }
526
+ },
527
+
528
+
529
+ // custom methods since not all JavaScript implementations support get/set
530
+
531
+ // This can be a url string
532
+ // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
533
+ setSrc: function (url) {
534
+ if (typeof url == 'string') {
535
+ this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
536
+ this.src = mejs.Utility.absolutizeUrl(url);
537
+ } else {
538
+ var i, media;
539
+
540
+ for (i=0; i<url.length; i++) {
541
+ media = url[i];
542
+ if (this.canPlayType(media.type)) {
543
+ this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
544
+ this.src = mejs.Utility.absolutizeUrl(url);
545
+ }
546
+ }
547
+ }
548
+
549
+ },
550
+ setCurrentTime: function (time) {
551
+ if (this.pluginApi != null) {
552
+ if (this.pluginType == 'youtube') {
553
+ this.pluginApi.seekTo(time);
554
+ } else {
555
+ this.pluginApi.setCurrentTime(time);
556
+ }
557
+
558
+
559
+
560
+ this.currentTime = time;
561
+ }
562
+ },
563
+ setVolume: function (volume) {
564
+ if (this.pluginApi != null) {
565
+ // same on YouTube and MEjs
566
+ if (this.pluginType == 'youtube') {
567
+ this.pluginApi.setVolume(volume * 100);
568
+ } else {
569
+ this.pluginApi.setVolume(volume);
570
+ }
571
+ this.volume = volume;
572
+ }
573
+ },
574
+ setMuted: function (muted) {
575
+ if (this.pluginApi != null) {
576
+ if (this.pluginType == 'youtube') {
577
+ if (muted) {
578
+ this.pluginApi.mute();
579
+ } else {
580
+ this.pluginApi.unMute();
581
+ }
582
+ this.muted = muted;
583
+ this.dispatchEvent('volumechange');
584
+ } else {
585
+ this.pluginApi.setMuted(muted);
586
+ }
587
+ this.muted = muted;
588
+ }
589
+ },
590
+
591
+ // additional non-HTML5 methods
592
+ setVideoSize: function (width, height) {
593
+
594
+ //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
595
+ if ( this.pluginElement.style) {
596
+ this.pluginElement.style.width = width + 'px';
597
+ this.pluginElement.style.height = height + 'px';
598
+ }
599
+ if (this.pluginApi != null && this.pluginApi.setVideoSize) {
600
+ this.pluginApi.setVideoSize(width, height);
601
+ }
602
+ //}
603
+ },
604
+
605
+ setFullscreen: function (fullscreen) {
606
+ if (this.pluginApi != null && this.pluginApi.setFullscreen) {
607
+ this.pluginApi.setFullscreen(fullscreen);
608
+ }
609
+ },
610
+
611
+ enterFullScreen: function() {
612
+ if (this.pluginApi != null && this.pluginApi.setFullscreen) {
613
+ this.setFullscreen(true);
614
+ }
615
+
616
+ },
617
+
618
+ exitFullScreen: function() {
619
+ if (this.pluginApi != null && this.pluginApi.setFullscreen) {
620
+ this.setFullscreen(false);
621
+ }
622
+ },
623
+
624
+ // start: fake events
625
+ addEventListener: function (eventName, callback, bubble) {
626
+ this.events[eventName] = this.events[eventName] || [];
627
+ this.events[eventName].push(callback);
628
+ },
629
+ removeEventListener: function (eventName, callback) {
630
+ if (!eventName) { this.events = {}; return true; }
631
+ var callbacks = this.events[eventName];
632
+ if (!callbacks) return true;
633
+ if (!callback) { this.events[eventName] = []; return true; }
634
+ for (i = 0; i < callbacks.length; i++) {
635
+ if (callbacks[i] === callback) {
636
+ this.events[eventName].splice(i, 1);
637
+ return true;
638
+ }
639
+ }
640
+ return false;
641
+ },
642
+ dispatchEvent: function (eventName) {
643
+ var i,
644
+ args,
645
+ callbacks = this.events[eventName];
646
+
647
+ if (callbacks) {
648
+ args = Array.prototype.slice.call(arguments, 1);
649
+ for (i = 0; i < callbacks.length; i++) {
650
+ callbacks[i].apply(null, args);
651
+ }
652
+ }
653
+ },
654
+ // end: fake events
655
+
656
+ // fake DOM attribute methods
657
+ attributes: {},
658
+ hasAttribute: function(name){
659
+ return (name in this.attributes);
660
+ },
661
+ removeAttribute: function(name){
662
+ delete this.attributes[name];
663
+ },
664
+ getAttribute: function(name){
665
+ if (this.hasAttribute(name)) {
666
+ return this.attributes[name];
667
+ }
668
+ return '';
669
+ },
670
+ setAttribute: function(name, value){
671
+ this.attributes[name] = value;
672
+ },
673
+
674
+ remove: function() {
675
+ mejs.Utility.removeSwf(this.pluginElement.id);
676
+ }
677
+ };
678
+
679
+ // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
680
+ mejs.MediaPluginBridge = {
681
+
682
+ pluginMediaElements:{},
683
+ htmlMediaElements:{},
684
+
685
+ registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
686
+ this.pluginMediaElements[id] = pluginMediaElement;
687
+ this.htmlMediaElements[id] = htmlMediaElement;
688
+ },
689
+
690
+ // when Flash/Silverlight is ready, it calls out to this method
691
+ initPlugin: function (id) {
692
+
693
+ var pluginMediaElement = this.pluginMediaElements[id],
694
+ htmlMediaElement = this.htmlMediaElements[id];
695
+
696
+ if (pluginMediaElement) {
697
+ // find the javascript bridge
698
+ switch (pluginMediaElement.pluginType) {
699
+ case "flash":
700
+ pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
701
+ break;
702
+ case "silverlight":
703
+ pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
704
+ pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
705
+ break;
706
+ }
707
+
708
+ if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
709
+ pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
710
+ }
711
+ }
712
+ },
713
+
714
+ // receives events from Flash/Silverlight and sends them out as HTML5 media events
715
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
716
+ fireEvent: function (id, eventName, values) {
717
+
718
+ var
719
+ e,
720
+ i,
721
+ bufferedTime,
722
+ pluginMediaElement = this.pluginMediaElements[id];
723
+
724
+ pluginMediaElement.ended = false;
725
+ pluginMediaElement.paused = true;
726
+
727
+ // fake event object to mimic real HTML media event.
728
+ e = {
729
+ type: eventName,
730
+ target: pluginMediaElement
731
+ };
732
+
733
+ // attach all values to element and event object
734
+ for (i in values) {
735
+ pluginMediaElement[i] = values[i];
736
+ e[i] = values[i];
737
+ }
738
+
739
+ // fake the newer W3C buffered TimeRange (loaded and total have been removed)
740
+ bufferedTime = values.bufferedTime || 0;
741
+
742
+ e.target.buffered = e.buffered = {
743
+ start: function(index) {
744
+ return 0;
745
+ },
746
+ end: function (index) {
747
+ return bufferedTime;
748
+ },
749
+ length: 1
750
+ };
751
+
752
+ pluginMediaElement.dispatchEvent(e.type, e);
753
+ }
754
+ };
755
+
756
+ /*
757
+ Default options
758
+ */
759
+ mejs.MediaElementDefaults = {
760
+ // allows testing on HTML5, flash, silverlight
761
+ // auto: attempts to detect what the browser can do
762
+ // auto_plugin: prefer plugins and then attempt native HTML5
763
+ // native: forces HTML5 playback
764
+ // shim: disallows HTML5, will attempt either Flash or Silverlight
765
+ // none: forces fallback view
766
+ mode: 'auto',
767
+ // remove or reorder to change plugin priority and availability
768
+ plugins: ['flash','silverlight','youtube','vimeo'],
769
+ // shows debug errors on screen
770
+ enablePluginDebug: false,
771
+ // overrides the type specified, useful for dynamic instantiation
772
+ type: '',
773
+ // path to Flash and Silverlight plugins
774
+ pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
775
+ // name of flash file
776
+ flashName: 'flashmediaelement.swf',
777
+ // turns on the smoothing filter in Flash
778
+ enablePluginSmoothing: false,
779
+ // name of silverlight file
780
+ silverlightName: 'silverlightmediaelement.xap',
781
+ // default if the <video width> is not specified
782
+ defaultVideoWidth: 480,
783
+ // default if the <video height> is not specified
784
+ defaultVideoHeight: 270,
785
+ // overrides <video width>
786
+ pluginWidth: -1,
787
+ // overrides <video height>
788
+ pluginHeight: -1,
789
+ // additional plugin variables in 'key=value' form
790
+ pluginVars: [],
791
+ // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
792
+ // larger number is less accurate, but less strain on plugin->JavaScript bridge
793
+ timerRate: 250,
794
+ // initial volume for player
795
+ startVolume: 0.8,
796
+ success: function () { },
797
+ error: function () { }
798
+ };
799
+
800
+ /*
801
+ Determines if a browser supports the <video> or <audio> element
802
+ and returns either the native element or a Flash/Silverlight version that
803
+ mimics HTML5 MediaElement
804
+ */
805
+ mejs.MediaElement = function (el, o) {
806
+ return mejs.HtmlMediaElementShim.create(el,o);
807
+ };
808
+
809
+ mejs.HtmlMediaElementShim = {
810
+
811
+ create: function(el, o) {
812
+ var
813
+ options = mejs.MediaElementDefaults,
814
+ htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
815
+ tagName = htmlMediaElement.tagName.toLowerCase(),
816
+ isMediaTag = (tagName === 'audio' || tagName === 'video'),
817
+ src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
818
+ poster = htmlMediaElement.getAttribute('poster'),
819
+ autoplay = htmlMediaElement.getAttribute('autoplay'),
820
+ preload = htmlMediaElement.getAttribute('preload'),
821
+ controls = htmlMediaElement.getAttribute('controls'),
822
+ playback,
823
+ prop;
824
+
825
+ // extend options
826
+ for (prop in o) {
827
+ options[prop] = o[prop];
828
+ }
829
+
830
+ // clean up attributes
831
+ src = (typeof src == 'undefined' || src === null || src == '') ? null : src;
832
+ poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
833
+ preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
834
+ autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
835
+ controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
836
+
837
+ // test for HTML5 and plugin capabilities
838
+ playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
839
+ playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
840
+
841
+ if (playback.method == 'native') {
842
+ // second fix for android
843
+ if (mejs.MediaFeatures.isBustedAndroid) {
844
+ htmlMediaElement.src = playback.url;
845
+ htmlMediaElement.addEventListener('click', function() {
846
+ htmlMediaElement.play();
847
+ }, false);
848
+ }
849
+
850
+ // add methods to native HTMLMediaElement
851
+ return this.updateNative(playback, options, autoplay, preload);
852
+ } else if (playback.method !== '') {
853
+ // create plugin to mimic HTMLMediaElement
854
+
855
+ return this.createPlugin( playback, options, poster, autoplay, preload, controls);
856
+ } else {
857
+ // boo, no HTML5, no Flash, no Silverlight.
858
+ this.createErrorMessage( playback, options, poster );
859
+
860
+ return this;
861
+ }
862
+ },
863
+
864
+ determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
865
+ var
866
+ mediaFiles = [],
867
+ i,
868
+ j,
869
+ k,
870
+ l,
871
+ n,
872
+ type,
873
+ result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
874
+ pluginName,
875
+ pluginVersions,
876
+ pluginInfo,
877
+ dummy;
878
+
879
+ // STEP 1: Get URL and type from <video src> or <source src>
880
+
881
+ // supplied type overrides <video type> and <source type>
882
+ if (typeof options.type != 'undefined' && options.type !== '') {
883
+
884
+ // accept either string or array of types
885
+ if (typeof options.type == 'string') {
886
+ mediaFiles.push({type:options.type, url:src});
887
+ } else {
888
+
889
+ for (i=0; i<options.type.length; i++) {
890
+ mediaFiles.push({type:options.type[i], url:src});
891
+ }
892
+ }
893
+
894
+ // test for src attribute first
895
+ } else if (src !== null) {
896
+ type = this.formatType(src, htmlMediaElement.getAttribute('type'));
897
+ mediaFiles.push({type:type, url:src});
898
+
899
+ // then test for <source> elements
900
+ } else {
901
+ // test <source> types to see if they are usable
902
+ for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
903
+ n = htmlMediaElement.childNodes[i];
904
+ if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
905
+ src = n.getAttribute('src');
906
+ type = this.formatType(src, n.getAttribute('type'));
907
+ mediaFiles.push({type:type, url:src});
908
+ }
909
+ }
910
+ }
911
+
912
+ // in the case of dynamicly created players
913
+ // check for audio types
914
+ if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
915
+ result.isVideo = false;
916
+ }
917
+
918
+
919
+ // STEP 2: Test for playback method
920
+
921
+ // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
922
+ if (mejs.MediaFeatures.isBustedAndroid) {
923
+ htmlMediaElement.canPlayType = function(type) {
924
+ return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
925
+ };
926
+ }
927
+
928
+
929
+ // test for native playback first
930
+ if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native')) {
931
+
932
+ if (!isMediaTag) {
933
+
934
+ // create a real HTML5 Media Element
935
+ dummy = document.createElement( result.isVideo ? 'video' : 'audio');
936
+ htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
937
+ htmlMediaElement.style.display = 'none';
938
+
939
+ // use this one from now on
940
+ result.htmlMediaElement = htmlMediaElement = dummy;
941
+ }
942
+
943
+ for (i=0; i<mediaFiles.length; i++) {
944
+ // normal check
945
+ if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
946
+ // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
947
+ || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '') {
948
+ result.method = 'native';
949
+ result.url = mediaFiles[i].url;
950
+ break;
951
+ }
952
+ }
953
+
954
+ if (result.method === 'native') {
955
+ if (result.url !== null) {
956
+ htmlMediaElement.src = result.url;
957
+ }
958
+
959
+ // if `auto_plugin` mode, then cache the native result but try plugins.
960
+ if (options.mode !== 'auto_plugin') {
961
+ return result;
962
+ }
963
+ }
964
+ }
965
+
966
+ // if native playback didn't work, then test plugins
967
+ if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
968
+ for (i=0; i<mediaFiles.length; i++) {
969
+ type = mediaFiles[i].type;
970
+
971
+ // test all plugins in order of preference [silverlight, flash]
972
+ for (j=0; j<options.plugins.length; j++) {
973
+
974
+ pluginName = options.plugins[j];
975
+
976
+ // test version of plugin (for future features)
977
+ pluginVersions = mejs.plugins[pluginName];
978
+
979
+ for (k=0; k<pluginVersions.length; k++) {
980
+ pluginInfo = pluginVersions[k];
981
+
982
+ // test if user has the correct plugin version
983
+
984
+ // for youtube/vimeo
985
+ if (pluginInfo.version == null ||
986
+
987
+ mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
988
+
989
+ // test for plugin playback types
990
+ for (l=0; l<pluginInfo.types.length; l++) {
991
+ // find plugin that can play the type
992
+ if (type == pluginInfo.types[l]) {
993
+ result.method = pluginName;
994
+ result.url = mediaFiles[i].url;
995
+ return result;
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
1005
+ // if we have native support then return that.
1006
+ if (options.mode === 'auto_plugin' && result.method === 'native') {
1007
+ return result;
1008
+ }
1009
+
1010
+ // what if there's nothing to play? just grab the first available
1011
+ if (result.method === '' && mediaFiles.length > 0) {
1012
+ result.url = mediaFiles[0].url;
1013
+ }
1014
+
1015
+ return result;
1016
+ },
1017
+
1018
+ formatType: function(url, type) {
1019
+ var ext;
1020
+
1021
+ // if no type is supplied, fake it with the extension
1022
+ if (url && !type) {
1023
+ return this.getTypeFromFile(url);
1024
+ } else {
1025
+ // only return the mime part of the type in case the attribute contains the codec
1026
+ // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
1027
+ // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
1028
+
1029
+ if (type && ~type.indexOf(';')) {
1030
+ return type.substr(0, type.indexOf(';'));
1031
+ } else {
1032
+ return type;
1033
+ }
1034
+ }
1035
+ },
1036
+
1037
+ getTypeFromFile: function(url) {
1038
+ var ext = url.substring(url.lastIndexOf('.') + 1);
1039
+ return (/(mp4|m4v|ogg|ogv|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext);
1040
+ },
1041
+
1042
+ getTypeFromExtension: function(ext) {
1043
+ var ext_types = {
1044
+ 'mp4': ['mp4','m4v'],
1045
+ 'ogg': ['ogg','ogv','oga'],
1046
+ 'webm': ['webm','webmv','webma']
1047
+ };
1048
+ var r = ext;
1049
+ $.each(ext_types, function(key, value) {
1050
+ if (value.indexOf(ext) > -1) {
1051
+ r = key;
1052
+ return;
1053
+ }
1054
+ });
1055
+ return r;
1056
+ },
1057
+
1058
+ createErrorMessage: function(playback, options, poster) {
1059
+ var
1060
+ htmlMediaElement = playback.htmlMediaElement,
1061
+ errorContainer = document.createElement('div');
1062
+
1063
+ errorContainer.className = 'me-cannotplay';
1064
+
1065
+ try {
1066
+ errorContainer.style.width = htmlMediaElement.width + 'px';
1067
+ errorContainer.style.height = htmlMediaElement.height + 'px';
1068
+ } catch (e) {}
1069
+
1070
+ errorContainer.innerHTML = (poster !== '') ?
1071
+ '<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' :
1072
+ '<a href="' + playback.url + '"><span>Download File</span></a>';
1073
+
1074
+ htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
1075
+ htmlMediaElement.style.display = 'none';
1076
+
1077
+ options.error(htmlMediaElement);
1078
+ },
1079
+
1080
+ createPlugin:function(playback, options, poster, autoplay, preload, controls) {
1081
+ var
1082
+ htmlMediaElement = playback.htmlMediaElement,
1083
+ width = 1,
1084
+ height = 1,
1085
+ pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
1086
+ pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
1087
+ container = document.createElement('div'),
1088
+ specialIEContainer,
1089
+ node,
1090
+ initVars;
1091
+
1092
+ // copy tagName from html media element
1093
+ pluginMediaElement.tagName = htmlMediaElement.tagName
1094
+
1095
+ // copy attributes from html media element to plugin media element
1096
+ for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
1097
+ var attribute = htmlMediaElement.attributes[i];
1098
+ if (attribute.specified == true) {
1099
+ pluginMediaElement.setAttribute(attribute.name, attribute.value);
1100
+ }
1101
+ }
1102
+
1103
+ // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
1104
+ node = htmlMediaElement.parentNode;
1105
+ while (node !== null && node.tagName.toLowerCase() != 'body') {
1106
+ if (node.parentNode.tagName.toLowerCase() == 'p') {
1107
+ node.parentNode.parentNode.insertBefore(node, node.parentNode);
1108
+ break;
1109
+ }
1110
+ node = node.parentNode;
1111
+ }
1112
+
1113
+ if (playback.isVideo) {
1114
+ width = (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
1115
+ height = (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
1116
+
1117
+ // in case of '%' make sure it's encoded
1118
+ width = mejs.Utility.encodeUrl(width);
1119
+ height = mejs.Utility.encodeUrl(height);
1120
+
1121
+ } else {
1122
+ if (options.enablePluginDebug) {
1123
+ width = 320;
1124
+ height = 240;
1125
+ }
1126
+ }
1127
+
1128
+ // register plugin
1129
+ pluginMediaElement.success = options.success;
1130
+ mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
1131
+
1132
+ // add container (must be added to DOM before inserting HTML for IE)
1133
+ container.className = 'me-plugin';
1134
+ container.id = pluginid + '_container';
1135
+
1136
+ if (playback.isVideo) {
1137
+ htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
1138
+ } else {
1139
+ document.body.insertBefore(container, document.body.childNodes[0]);
1140
+ }
1141
+
1142
+ // flash/silverlight vars
1143
+ initVars = [
1144
+ 'id=' + pluginid,
1145
+ 'isvideo=' + ((playback.isVideo) ? "true" : "false"),
1146
+ 'autoplay=' + ((autoplay) ? "true" : "false"),
1147
+ 'preload=' + preload,
1148
+ 'width=' + width,
1149
+ 'startvolume=' + options.startVolume,
1150
+ 'timerrate=' + options.timerRate,
1151
+ 'height=' + height];
1152
+
1153
+ if (playback.url !== null) {
1154
+ if (playback.method == 'flash') {
1155
+ initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
1156
+ } else {
1157
+ initVars.push('file=' + playback.url);
1158
+ }
1159
+ }
1160
+ if (options.enablePluginDebug) {
1161
+ initVars.push('debug=true');
1162
+ }
1163
+ if (options.enablePluginSmoothing) {
1164
+ initVars.push('smoothing=true');
1165
+ }
1166
+ if (controls) {
1167
+ initVars.push('controls=true'); // shows controls in the plugin if desired
1168
+ }
1169
+ if (options.pluginVars) {
1170
+ initVars = initVars.concat(options.pluginVars);
1171
+ }
1172
+
1173
+ switch (playback.method) {
1174
+ case 'silverlight':
1175
+ container.innerHTML =
1176
+ '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '">' +
1177
+ '<param name="initParams" value="' + initVars.join(',') + '" />' +
1178
+ '<param name="windowless" value="true" />' +
1179
+ '<param name="background" value="black" />' +
1180
+ '<param name="minRuntimeVersion" value="3.0.0.0" />' +
1181
+ '<param name="autoUpgrade" value="true" />' +
1182
+ '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
1183
+ '</object>';
1184
+ break;
1185
+
1186
+ case 'flash':
1187
+
1188
+ if (mejs.MediaFeatures.isIE) {
1189
+ specialIEContainer = document.createElement('div');
1190
+ container.appendChild(specialIEContainer);
1191
+ specialIEContainer.outerHTML =
1192
+ '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
1193
+ 'id="' + pluginid + '" width="' + width + '" height="' + height + '">' +
1194
+ '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
1195
+ '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
1196
+ '<param name="quality" value="high" />' +
1197
+ '<param name="bgcolor" value="#000000" />' +
1198
+ '<param name="wmode" value="transparent" />' +
1199
+ '<param name="allowScriptAccess" value="always" />' +
1200
+ '<param name="allowFullScreen" value="true" />' +
1201
+ '</object>';
1202
+
1203
+ } else {
1204
+
1205
+ container.innerHTML =
1206
+ '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
1207
+ 'play="true" ' +
1208
+ 'loop="false" ' +
1209
+ 'quality="high" ' +
1210
+ 'bgcolor="#000000" ' +
1211
+ 'wmode="transparent" ' +
1212
+ 'allowScriptAccess="always" ' +
1213
+ 'allowFullScreen="true" ' +
1214
+ 'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
1215
+ 'src="' + options.pluginPath + options.flashName + '" ' +
1216
+ 'flashvars="' + initVars.join('&') + '" ' +
1217
+ 'width="' + width + '" ' +
1218
+ 'height="' + height + '"></embed>';
1219
+ }
1220
+ break;
1221
+
1222
+ case 'youtube':
1223
+
1224
+
1225
+ var
1226
+ videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
1227
+ youtubeSettings = {
1228
+ container: container,
1229
+ containerId: container.id,
1230
+ pluginMediaElement: pluginMediaElement,
1231
+ pluginId: pluginid,
1232
+ videoId: videoId,
1233
+ height: height,
1234
+ width: width
1235
+ };
1236
+
1237
+ if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
1238
+ mejs.YouTubeApi.createFlash(youtubeSettings);
1239
+ } else {
1240
+ mejs.YouTubeApi.enqueueIframe(youtubeSettings);
1241
+ }
1242
+
1243
+ break;
1244
+
1245
+ // DEMO Code. Does NOT work.
1246
+ case 'vimeo':
1247
+ //console.log('vimeoid');
1248
+
1249
+ pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
1250
+
1251
+ container.innerHTML =
1252
+ '<object width="' + width + '" height="' + height + '">' +
1253
+ '<param name="allowfullscreen" value="true" />' +
1254
+ '<param name="allowscriptaccess" value="always" />' +
1255
+ '<param name="flashvars" value="api=1" />' +
1256
+ '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=' + pluginMediaElement.vimeoid + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" />' +
1257
+ '<embed src="//vimeo.com/moogaloop.swf?api=1&amp;clip_id=' + pluginMediaElement.vimeoid + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="' + width + '" height="' + height + '"></embed>' +
1258
+ '</object>';
1259
+
1260
+ break;
1261
+ }
1262
+ // hide original element
1263
+ htmlMediaElement.style.display = 'none';
1264
+
1265
+ // FYI: options.success will be fired by the MediaPluginBridge
1266
+
1267
+ return pluginMediaElement;
1268
+ },
1269
+
1270
+ updateNative: function(playback, options, autoplay, preload) {
1271
+
1272
+ var htmlMediaElement = playback.htmlMediaElement,
1273
+ m;
1274
+
1275
+
1276
+ // add methods to video object to bring it into parity with Flash Object
1277
+ for (m in mejs.HtmlMediaElement) {
1278
+ htmlMediaElement[m] = mejs.HtmlMediaElement[m];
1279
+ }
1280
+
1281
+ /*
1282
+ Chrome now supports preload="none"
1283
+ if (mejs.MediaFeatures.isChrome) {
1284
+
1285
+ // special case to enforce preload attribute (Chrome doesn't respect this)
1286
+ if (preload === 'none' && !autoplay) {
1287
+
1288
+ // forces the browser to stop loading (note: fails in IE9)
1289
+ htmlMediaElement.src = '';
1290
+ htmlMediaElement.load();
1291
+ htmlMediaElement.canceledPreload = true;
1292
+
1293
+ htmlMediaElement.addEventListener('play',function() {
1294
+ if (htmlMediaElement.canceledPreload) {
1295
+ htmlMediaElement.src = playback.url;
1296
+ htmlMediaElement.load();
1297
+ htmlMediaElement.play();
1298
+ htmlMediaElement.canceledPreload = false;
1299
+ }
1300
+ }, false);
1301
+ // for some reason Chrome forgets how to autoplay sometimes.
1302
+ } else if (autoplay) {
1303
+ htmlMediaElement.load();
1304
+ htmlMediaElement.play();
1305
+ }
1306
+ }
1307
+ */
1308
+
1309
+ // fire success code
1310
+ options.success(htmlMediaElement, htmlMediaElement);
1311
+
1312
+ return htmlMediaElement;
1313
+ }
1314
+ };
1315
+
1316
+ /*
1317
+ - test on IE (object vs. embed)
1318
+ - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
1319
+ - fullscreen?
1320
+ */
1321
+
1322
+ // YouTube Flash and Iframe API
1323
+ mejs.YouTubeApi = {
1324
+ isIframeStarted: false,
1325
+ isIframeLoaded: false,
1326
+ loadIframeApi: function() {
1327
+ if (!this.isIframeStarted) {
1328
+ var tag = document.createElement('script');
1329
+ tag.src = "http://www.youtube.com/player_api";
1330
+ var firstScriptTag = document.getElementsByTagName('script')[0];
1331
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
1332
+ this.isIframeStarted = true;
1333
+ }
1334
+ },
1335
+ iframeQueue: [],
1336
+ enqueueIframe: function(yt) {
1337
+
1338
+ if (this.isLoaded) {
1339
+ this.createIframe(yt);
1340
+ } else {
1341
+ this.loadIframeApi();
1342
+ this.iframeQueue.push(yt);
1343
+ }
1344
+ },
1345
+ createIframe: function(settings) {
1346
+
1347
+ var
1348
+ pluginMediaElement = settings.pluginMediaElement,
1349
+ player = new YT.Player(settings.containerId, {
1350
+ height: settings.height,
1351
+ width: settings.width,
1352
+ videoId: settings.videoId,
1353
+ playerVars: {controls:0},
1354
+ events: {
1355
+ 'onReady': function() {
1356
+
1357
+ // hook up iframe object to MEjs
1358
+ settings.pluginMediaElement.pluginApi = player;
1359
+
1360
+ // init mejs
1361
+ mejs.MediaPluginBridge.initPlugin(settings.pluginId);
1362
+
1363
+ // create timer
1364
+ setInterval(function() {
1365
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1366
+ }, 250);
1367
+ },
1368
+ 'onStateChange': function(e) {
1369
+
1370
+ mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
1371
+
1372
+ }
1373
+ }
1374
+ });
1375
+ },
1376
+
1377
+ createEvent: function (player, pluginMediaElement, eventName) {
1378
+ var obj = {
1379
+ type: eventName,
1380
+ target: pluginMediaElement
1381
+ };
1382
+
1383
+ if (player && player.getDuration) {
1384
+
1385
+ // time
1386
+ pluginMediaElement.currentTime = obj.currentTime = player.getCurrentTime();
1387
+ pluginMediaElement.duration = obj.duration = player.getDuration();
1388
+
1389
+ // state
1390
+ obj.paused = pluginMediaElement.paused;
1391
+ obj.ended = pluginMediaElement.ended;
1392
+
1393
+ // sound
1394
+ obj.muted = player.isMuted();
1395
+ obj.volume = player.getVolume() / 100;
1396
+
1397
+ // progress
1398
+ obj.bytesTotal = player.getVideoBytesTotal();
1399
+ obj.bufferedBytes = player.getVideoBytesLoaded();
1400
+
1401
+ // fake the W3C buffered TimeRange
1402
+ var bufferedTime = obj.bufferedBytes / obj.bytesTotal * obj.duration;
1403
+
1404
+ obj.target.buffered = obj.buffered = {
1405
+ start: function(index) {
1406
+ return 0;
1407
+ },
1408
+ end: function (index) {
1409
+ return bufferedTime;
1410
+ },
1411
+ length: 1
1412
+ };
1413
+
1414
+ }
1415
+
1416
+ // send event up the chain
1417
+ pluginMediaElement.dispatchEvent(obj.type, obj);
1418
+ },
1419
+
1420
+ iFrameReady: function() {
1421
+
1422
+ this.isLoaded = true;
1423
+ this.isIframeLoaded = true;
1424
+
1425
+ while (this.iframeQueue.length > 0) {
1426
+ var settings = this.iframeQueue.pop();
1427
+ this.createIframe(settings);
1428
+ }
1429
+ },
1430
+
1431
+ // FLASH!
1432
+ flashPlayers: {},
1433
+ createFlash: function(settings) {
1434
+
1435
+ this.flashPlayers[settings.pluginId] = settings;
1436
+
1437
+ /*
1438
+ settings.container.innerHTML =
1439
+ '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
1440
+ 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; ">' +
1441
+ '<param name="allowScriptAccess" value="always">' +
1442
+ '<param name="wmode" value="transparent">' +
1443
+ '</object>';
1444
+ */
1445
+
1446
+ var specialIEContainer,
1447
+ youtubeUrl = 'http://www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
1448
+
1449
+ if (mejs.MediaFeatures.isIE) {
1450
+
1451
+ specialIEContainer = document.createElement('div');
1452
+ settings.container.appendChild(specialIEContainer);
1453
+ specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
1454
+ 'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '">' +
1455
+ '<param name="movie" value="' + youtubeUrl + '" />' +
1456
+ '<param name="wmode" value="transparent" />' +
1457
+ '<param name="allowScriptAccess" value="always" />' +
1458
+ '<param name="allowFullScreen" value="true" />' +
1459
+ '</object>';
1460
+ } else {
1461
+ settings.container.innerHTML =
1462
+ '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
1463
+ 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; ">' +
1464
+ '<param name="allowScriptAccess" value="always">' +
1465
+ '<param name="wmode" value="transparent">' +
1466
+ '</object>';
1467
+ }
1468
+
1469
+ },
1470
+
1471
+ flashReady: function(id) {
1472
+ var
1473
+ settings = this.flashPlayers[id],
1474
+ player = document.getElementById(id),
1475
+ pluginMediaElement = settings.pluginMediaElement;
1476
+
1477
+ // hook up and return to MediaELementPlayer.success
1478
+ pluginMediaElement.pluginApi =
1479
+ pluginMediaElement.pluginElement = player;
1480
+ mejs.MediaPluginBridge.initPlugin(id);
1481
+
1482
+ // load the youtube video
1483
+ player.cueVideoById(settings.videoId);
1484
+
1485
+ var callbackName = settings.containerId + '_callback'
1486
+
1487
+ window[callbackName] = function(e) {
1488
+ mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
1489
+ }
1490
+
1491
+ player.addEventListener('onStateChange', callbackName);
1492
+
1493
+ setInterval(function() {
1494
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1495
+ }, 250);
1496
+ },
1497
+
1498
+ handleStateChange: function(youTubeState, player, pluginMediaElement) {
1499
+ switch (youTubeState) {
1500
+ case -1: // not started
1501
+ pluginMediaElement.paused = true;
1502
+ pluginMediaElement.ended = true;
1503
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
1504
+ //createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
1505
+ break;
1506
+ case 0:
1507
+ pluginMediaElement.paused = false;
1508
+ pluginMediaElement.ended = true;
1509
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
1510
+ break;
1511
+ case 1:
1512
+ pluginMediaElement.paused = false;
1513
+ pluginMediaElement.ended = false;
1514
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
1515
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
1516
+ break;
1517
+ case 2:
1518
+ pluginMediaElement.paused = true;
1519
+ pluginMediaElement.ended = false;
1520
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
1521
+ break;
1522
+ case 3: // buffering
1523
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
1524
+ break;
1525
+ case 5:
1526
+ // cued?
1527
+ break;
1528
+
1529
+ }
1530
+
1531
+ }
1532
+ }
1533
+ // IFRAME
1534
+ function onYouTubePlayerAPIReady() {
1535
+ mejs.YouTubeApi.iFrameReady();
1536
+ }
1537
+ // FLASH
1538
+ function onYouTubePlayerReady(id) {
1539
+ mejs.YouTubeApi.flashReady(id);
1540
+ }
1541
+
1542
+ window.mejs = mejs;
1543
+ window.MediaElement = mejs.MediaElement;
1544
+