rdoroshenko_mediaelement_rails 0.4.0

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