mediaelement_rails 0.8.0 → 0.8.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23365c544c82660f323c1daabc33b00f6181dffb
4
- data.tar.gz: a8c9ff738d46f26e0ec107d5284e750bef83fadd
3
+ metadata.gz: 2d8652013a3c249f1593fb65ab96035cfceb3e54
4
+ data.tar.gz: 0bf5e23f187444085871307754b558c73cac7350
5
5
  SHA512:
6
- metadata.gz: 7e4207d1706fe455887273959db1e042ae32ec28ab3726bf0828b67443035951b741f9c48d80031eda3488627c85317a31f28018d2b22e84171dce52211bc68f
7
- data.tar.gz: 6462b9298fd2670d65ccd8df2e97efc954f6b0ab6b2ab43cf5bf9f50d267fce94ef89749b6337cb2e9134de9a354dcc332afe04f51cc5b4a6c74ac67a55dad9d
6
+ metadata.gz: 54839803d65a5e955b1964e0289c07d8a080400a088d103cae7a952ece979e94875bcfea9834768f8f2f5f16882b675bb48188f3d9af8bc36422d978b5ff91c3
7
+ data.tar.gz: 36f1ea8770f9d9adeb2a52a1292a837179dbdcea1623f5b84c56aa4cc56f523192b6023d0f7bd079dc19f04c5402edea0ad1073fb63723e666ae6563000dab40
@@ -1,5 +1,9 @@
1
1
  ## Changelog
2
2
 
3
+ ### v0.8.1
4
+
5
+ - Updated MediaElement.js to 2.14.4 (danlopez191)
6
+
3
7
  ### v0.8
4
8
 
5
9
  - Updated MediaElement.js to 2.14.2
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MediaelementRails #
2
2
 
3
- This neat project brings the cool [MediaElement.js](http://mediaelementjs.com/) 2.13.2 (HTML5/Flash/Silverlight video player) to the Rails asset pipeline. __*NOTE:*__ This gem requires jquery to be included, which shouldn't be an issue.
3
+ This neat project brings the cool [MediaElement.js](http://mediaelementjs.com/) 2.16.4 (HTML5/Flash/Silverlight video player) to the Rails asset pipeline. __*NOTE:*__ This gem requires jquery to be included, which shouldn't be an issue.
4
4
 
5
5
  ## All you have to do is: ##
6
6
 
@@ -1 +1,14 @@
1
- <?xml version="1.0" standalone="no"?>
1
+ <?xml version="1.0" standalone="no"?>
2
+ <svg id="bigplay" viewBox="0 0 100 200" style="background-color:#ffffff00" version="1.1"
3
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
4
+ x="0px" y="0px" width="100px" height="200px"
5
+ >
6
+ <g id="dark">
7
+ <path id="Polygon" d="M 72.5 49.5 L 38.75 68.9856 L 38.75 30.0144 L 72.5 49.5 Z" fill="#ffffff" opacity="0.75" />
8
+ <path id="Ellipse" d="M 13 50.5 C 13 29.7891 29.7891 13 50.5 13 C 71.2109 13 88 29.7891 88 50.5 C 88 71.2109 71.2109 88 50.5 88 C 29.7891 88 13 71.2109 13 50.5 Z" stroke="#ffffff" stroke-width="5" fill="none" opacity="0.75"/>
9
+ </g>
10
+ <g id="light">
11
+ <path id="Polygon2" d="M 72.5 149.5 L 38.75 168.9856 L 38.75 130.0144 L 72.5 149.5 Z" fill="#ffffff" opacity="1.0" />
12
+ <path id="Ellipse2" d="M 13 150.5 C 13 129.7891 29.7891 113 50.5 113 C 71.2109 113 88 129.7891 88 150.5 C 88 171.211 71.2109 188 50.5 188 C 29.7891 188 13 171.211 13 150.5 Z" stroke="#ffffff" stroke-width="5" fill="none" opacity="1.0"/>
13
+ </g>
14
+ </svg>
@@ -1,21 +1,22 @@
1
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-2014, John Dyer (http://j.hn)
11
- * License: MIT
12
- *
13
- */
2
+ *
3
+ * MediaElement.js
4
+ * HTML5 <video> and <audio> shim and player
5
+ * http://mediaelementjs.com/
6
+ *
7
+ * Creates a JavaScript object that mimics HTML5 MediaElement API
8
+ * for browsers that don't understand HTML5 or can't play the provided codec
9
+ * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
10
+ *
11
+ * Copyright 2010-2014, John Dyer (http://j.hn)
12
+ * License: MIT
13
+ *
14
+ */
14
15
  // Namespace
15
16
  var mejs = mejs || {};
16
17
 
17
18
  // version number
18
- mejs.version = '2.14.2';
19
+ mejs.version = '2.16.4';
19
20
 
20
21
 
21
22
  // player number (for missing, same id attr)
@@ -27,7 +28,7 @@ mejs.plugins = {
27
28
  {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
28
29
  ],
29
30
  flash: [
30
- {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube']}
31
+ {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube', 'application/x-mpegURL']}
31
32
  //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
32
33
  ],
33
34
  youtube: [
@@ -320,6 +321,7 @@ mejs.MediaFeatures = {
320
321
  t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
321
322
  t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
322
323
  t.isChrome = (ua.match(/chrome/gi) !== null);
324
+ t.isChromium = (ua.match(/chromium/gi) !== null);
323
325
  t.isFirefox = (ua.match(/firefox/gi) !== null);
324
326
  t.isWebkit = (ua.match(/webkit/gi) !== null);
325
327
  t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
@@ -385,13 +387,13 @@ mejs.MediaFeatures = {
385
387
  }
386
388
 
387
389
  t.isFullScreen = function() {
388
- if (v.mozRequestFullScreen) {
390
+ if (t.hasMozNativeFullScreen) {
389
391
  return d.mozFullScreen;
390
392
 
391
- } else if (v.webkitRequestFullScreen) {
393
+ } else if (t.hasWebkitNativeFullScreen) {
392
394
  return d.webkitIsFullScreen;
393
395
 
394
- } else if (v.hasMsNativeFullScreen) {
396
+ } else if (t.hasMsNativeFullScreen) {
395
397
  return d.msFullscreenElement !== null;
396
398
  }
397
399
  }
@@ -651,7 +653,7 @@ mejs.PluginMediaElement.prototype = {
651
653
  setVolume: function (volume) {
652
654
  if (this.pluginApi != null) {
653
655
  // same on YouTube and MEjs
654
- if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
656
+ if (this.pluginType == 'youtube') {
655
657
  this.pluginApi.setVolume(volume * 100);
656
658
  } else {
657
659
  this.pluginApi.setVolume(volume);
@@ -661,7 +663,7 @@ mejs.PluginMediaElement.prototype = {
661
663
  },
662
664
  setMuted: function (muted) {
663
665
  if (this.pluginApi != null) {
664
- if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
666
+ if (this.pluginType == 'youtube') {
665
667
  if (muted) {
666
668
  this.pluginApi.mute();
667
669
  } else {
@@ -680,7 +682,7 @@ mejs.PluginMediaElement.prototype = {
680
682
  setVideoSize: function (width, height) {
681
683
 
682
684
  //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
683
- if ( this.pluginElement.style) {
685
+ if (this.pluginElement && this.pluginElement.style) {
684
686
  this.pluginElement.style.width = width + 'px';
685
687
  this.pluginElement.style.height = height + 'px';
686
688
  }
@@ -735,7 +737,7 @@ mejs.PluginMediaElement.prototype = {
735
737
  if (callbacks) {
736
738
  args = Array.prototype.slice.call(arguments, 1);
737
739
  for (i = 0; i < callbacks.length; i++) {
738
- callbacks[i].apply(null, args);
740
+ callbacks[i].apply(this, args);
739
741
  }
740
742
  }
741
743
  },
@@ -1032,6 +1034,12 @@ mejs.HtmlMediaElementShim = {
1032
1034
  };
1033
1035
  }
1034
1036
 
1037
+ // special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora)
1038
+ if (mejs.MediaFeatures.isChromium) {
1039
+ htmlMediaElement.canPlayType = function(type) {
1040
+ return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : '';
1041
+ };
1042
+ }
1035
1043
 
1036
1044
  // test for native playback first
1037
1045
  if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
@@ -1049,7 +1057,7 @@ mejs.HtmlMediaElementShim = {
1049
1057
 
1050
1058
  for (i=0; i<mediaFiles.length; i++) {
1051
1059
  // normal check
1052
- if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
1060
+ if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
1053
1061
  // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
1054
1062
  || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== ''
1055
1063
  // special case for m4a supported by detecting mp4 support
@@ -1146,7 +1154,7 @@ mejs.HtmlMediaElementShim = {
1146
1154
  getTypeFromFile: function(url) {
1147
1155
  url = url.split('?')[0];
1148
1156
  var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
1149
- return (/(mp4|m4v|ogg|ogv|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext);
1157
+ return (/(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext);
1150
1158
  },
1151
1159
 
1152
1160
  getTypeFromExtension: function(ext) {
@@ -1260,6 +1268,8 @@ mejs.HtmlMediaElementShim = {
1260
1268
  // flash/silverlight vars
1261
1269
  initVars = [
1262
1270
  'id=' + pluginid,
1271
+ 'jsinitfunction=' + "mejs.MediaPluginBridge.initPlugin",
1272
+ 'jscallbackfunction=' + "mejs.MediaPluginBridge.fireEvent",
1263
1273
  'isvideo=' + ((playback.isVideo) ? "true" : "false"),
1264
1274
  'autoplay=' + ((autoplay) ? "true" : "false"),
1265
1275
  'preload=' + preload,
@@ -1268,7 +1278,7 @@ mejs.HtmlMediaElementShim = {
1268
1278
  'timerrate=' + options.timerRate,
1269
1279
  'flashstreamer=' + options.flashStreamer,
1270
1280
  'height=' + height,
1271
- 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
1281
+ 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
1272
1282
 
1273
1283
  if (playback.url !== null) {
1274
1284
  if (playback.method == 'flash') {
@@ -1382,19 +1392,37 @@ mejs.HtmlMediaElementShim = {
1382
1392
  var player_id = pluginid + "_player";
1383
1393
  pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
1384
1394
 
1385
- container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '"></iframe>';
1395
+ container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
1386
1396
  if (typeof($f) == 'function') { // froogaloop available
1387
1397
  var player = $f(container.childNodes[0]);
1398
+
1388
1399
  player.addEvent('ready', function() {
1400
+
1389
1401
  player.playVideo = function() {
1390
- player.api('play');
1391
- };
1402
+ player.api( 'play' );
1403
+ }
1404
+ player.stopVideo = function() {
1405
+ player.api( 'unload' );
1406
+ }
1392
1407
  player.pauseVideo = function() {
1393
- player.api('pause');
1394
- };
1395
- player.seekTo = function(seconds) {
1396
- player.api('seekTo', seconds);
1397
- };
1408
+ player.api( 'pause' );
1409
+ }
1410
+ player.seekTo = function( seconds ) {
1411
+ player.api( 'seekTo', seconds );
1412
+ }
1413
+ player.setVolume = function( volume ) {
1414
+ player.api( 'setVolume', volume );
1415
+ }
1416
+ player.setMuted = function( muted ) {
1417
+ if( muted ) {
1418
+ player.lastVolume = player.api( 'getVolume' );
1419
+ player.api( 'setVolume', 0 );
1420
+ } else {
1421
+ player.api( 'setVolume', player.lastVolume );
1422
+ delete player.lastVolume;
1423
+ }
1424
+ }
1425
+
1398
1426
  function createEvent(player, pluginMediaElement, eventName, e) {
1399
1427
  var obj = {
1400
1428
  type: eventName,
@@ -1406,10 +1434,12 @@ mejs.HtmlMediaElementShim = {
1406
1434
  }
1407
1435
  pluginMediaElement.dispatchEvent(obj.type, obj);
1408
1436
  }
1437
+
1409
1438
  player.addEvent('play', function() {
1410
1439
  createEvent(player, pluginMediaElement, 'play');
1411
1440
  createEvent(player, pluginMediaElement, 'playing');
1412
1441
  });
1442
+
1413
1443
  player.addEvent('pause', function() {
1414
1444
  createEvent(player, pluginMediaElement, 'pause');
1415
1445
  });
@@ -1417,14 +1447,16 @@ mejs.HtmlMediaElementShim = {
1417
1447
  player.addEvent('finish', function() {
1418
1448
  createEvent(player, pluginMediaElement, 'ended');
1419
1449
  });
1450
+
1420
1451
  player.addEvent('playProgress', function(e) {
1421
1452
  createEvent(player, pluginMediaElement, 'timeupdate', e);
1422
1453
  });
1454
+
1455
+ pluginMediaElement.pluginElement = container;
1423
1456
  pluginMediaElement.pluginApi = player;
1424
1457
 
1425
1458
  // init mejs
1426
1459
  mejs.MediaPluginBridge.initPlugin(pluginid);
1427
-
1428
1460
  });
1429
1461
  }
1430
1462
  else {
@@ -1668,6 +1700,8 @@ mejs.YouTubeApi = {
1668
1700
  setInterval(function() {
1669
1701
  mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
1670
1702
  }, 250);
1703
+
1704
+ mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
1671
1705
  },
1672
1706
 
1673
1707
  handleStateChange: function(youTubeState, player, pluginMediaElement) {
@@ -1717,11 +1751,11 @@ function onYouTubePlayerReady(id) {
1717
1751
  window.mejs = mejs;
1718
1752
  window.MediaElement = mejs.MediaElement;
1719
1753
 
1720
- /*!
1754
+ /*
1721
1755
  * Adds Internationalization and localization to mediaelement.
1722
1756
  *
1723
- * This file does not contain translations, you have to add the manually.
1724
- * The schema is always the same: me-i18n-locale-[ISO_639-1 Code].js
1757
+ * This file does not contain translations, you have to add them manually.
1758
+ * The schema is always the same: me-i18n-locale-[IETF-language-tag].js
1725
1759
  *
1726
1760
  * Examples are provided both for german and chinese translation.
1727
1761
  *
@@ -1730,7 +1764,8 @@ window.MediaElement = mejs.MediaElement;
1730
1764
  * http://en.wikipedia.org/wiki/Internationalization_and_localization
1731
1765
  *
1732
1766
  * What langcode should i use?
1733
- * http://en.wikipedia.org/wiki/ISO_639-1
1767
+ * http://en.wikipedia.org/wiki/IETF_language_tag
1768
+ * https://tools.ietf.org/html/rfc5646
1734
1769
  *
1735
1770
  *
1736
1771
  * License?
@@ -1756,11 +1791,14 @@ window.MediaElement = mejs.MediaElement;
1756
1791
  */
1757
1792
  ;(function(context, exports, undefined) {
1758
1793
  "use strict";
1794
+
1759
1795
  var i18n = {
1760
1796
  "locale": {
1761
- "language" : '',
1762
- "strings" : {}
1797
+ // Ensure previous values aren't overwritten.
1798
+ "language" : (exports.i18n && exports.i18n.locale.language) || '',
1799
+ "strings" : (exports.i18n && exports.i18n.locale.strings) || {}
1763
1800
  },
1801
+ "ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/,
1764
1802
  "methods" : {}
1765
1803
  };
1766
1804
  // start i18n
@@ -1768,11 +1806,16 @@ window.MediaElement = mejs.MediaElement;
1768
1806
 
1769
1807
  /**
1770
1808
  * Get language, fallback to browser's language if empty
1809
+ *
1810
+ * IETF: RFC 5646, https://tools.ietf.org/html/rfc5646
1811
+ * Examples: en, zh-CN, cmn-Hans-CN, sr-Latn-RS, es-419, x-private
1771
1812
  */
1772
1813
  i18n.getLanguage = function () {
1773
1814
  var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language;
1774
- // convert to iso 639-1 (2-letters, lower case)
1775
- return language.substr(0, 2).toLowerCase();
1815
+ return i18n.ietf_lang_regex.exec(language) ? language : null;
1816
+
1817
+ //(WAS: convert to iso 639-1 (2-letters, lower case))
1818
+ //return language.substr(0, 2).toLowerCase();
1776
1819
  };
1777
1820
 
1778
1821
  // i18n fixes for compatibility with WordPress
@@ -1870,61 +1913,3 @@ window.MediaElement = mejs.MediaElement;
1870
1913
  }
1871
1914
 
1872
1915
  }(mejs.i18n.locale.strings));
1873
-
1874
- /*!
1875
- * This is a i18n.locale language object.
1876
- *
1877
- * German translation by Tim Latz, latz.tim@gmail.com
1878
- *
1879
- * @author
1880
- * Tim Latz (latz.tim@gmail.com)
1881
- *
1882
- * @see
1883
- * me-i18n.js
1884
- *
1885
- * @params
1886
- * - exports - CommonJS, window ..
1887
- */
1888
- ;(function(exports, undefined) {
1889
-
1890
- "use strict";
1891
-
1892
- if (typeof exports.de === 'undefined') {
1893
- exports.de = {
1894
- "Fullscreen" : "Vollbild",
1895
- "Go Fullscreen" : "Vollbild an",
1896
- "Turn off Fullscreen" : "Vollbild aus",
1897
- "Close" : "Schließen"
1898
- };
1899
- }
1900
-
1901
- }(mejs.i18n.locale.strings));
1902
- /*!
1903
- * This is a i18n.locale language object.
1904
- *
1905
- * Traditional chinese translation by Tim Latz, latz.tim@gmail.com
1906
- *
1907
- * @author
1908
- * Tim Latz (latz.tim@gmail.com)
1909
- *
1910
- * @see
1911
- * me-i18n.js
1912
- *
1913
- * @params
1914
- * - exports - CommonJS, window ..
1915
- */
1916
- ;(function(exports, undefined) {
1917
-
1918
- "use strict";
1919
-
1920
- if (typeof exports.zh === 'undefined') {
1921
- exports.zh = {
1922
- "Fullscreen" : "全螢幕",
1923
- "Go Fullscreen" : "全屏模式",
1924
- "Turn off Fullscreen" : "退出全屏模式",
1925
- "Close" : "關閉"
1926
- };
1927
- }
1928
-
1929
- }(mejs.i18n.locale.strings));
1930
-
@@ -1,4 +1,5 @@
1
1
  /*!
2
+ *
2
3
  * MediaElementPlayer
3
4
  * http://mediaelementjs.com/
4
5
  *
@@ -44,6 +45,9 @@ if (typeof jQuery != 'undefined') {
44
45
  return (media.duration * 0.05);
45
46
  },
46
47
 
48
+ // set dimensions via JS instead of CSS
49
+ setDimensions: true,
50
+
47
51
  // width of audio player
48
52
  audioWidth: -1,
49
53
  // height of audio player
@@ -70,8 +74,8 @@ if (typeof jQuery != 'undefined') {
70
74
  alwaysShowControls: false,
71
75
  // Display the video control
72
76
  hideVideoControlsOnLoad: false,
73
- // Enable click video element to toggle play/pause
74
- clickToPlayPause: true,
77
+ // Enable click video element to toggle play/pause
78
+ clickToPlayPause: true,
75
79
  // force iPad's native controls
76
80
  iPadUseNativeControls: false,
77
81
  // force iPhone's native controls
@@ -107,6 +111,12 @@ if (typeof jQuery != 'undefined') {
107
111
  {
108
112
  keys: [38], // UP
109
113
  action: function(player, media) {
114
+ player.container.find('.mejs-volume-slider').css('display','block');
115
+ if (player.isVideo) {
116
+ player.showControls();
117
+ player.startControlsTimer();
118
+ }
119
+
110
120
  var newVolume = Math.min(media.volume + 0.1, 1);
111
121
  media.setVolume(newVolume);
112
122
  }
@@ -114,6 +124,12 @@ if (typeof jQuery != 'undefined') {
114
124
  {
115
125
  keys: [40], // DOWN
116
126
  action: function(player, media) {
127
+ player.container.find('.mejs-volume-slider').css('display','block');
128
+ if (player.isVideo) {
129
+ player.showControls();
130
+ player.startControlsTimer();
131
+ }
132
+
117
133
  var newVolume = Math.max(media.volume - 0.1, 0);
118
134
  media.setVolume(newVolume);
119
135
  }
@@ -155,7 +171,7 @@ if (typeof jQuery != 'undefined') {
155
171
  }
156
172
  },
157
173
  {
158
- keys: [70], // f
174
+ keys: [70], // F
159
175
  action: function(player, media) {
160
176
  if (typeof player.enterFullScreen != 'undefined') {
161
177
  if (player.isFullScreen) {
@@ -165,6 +181,21 @@ if (typeof jQuery != 'undefined') {
165
181
  }
166
182
  }
167
183
  }
184
+ },
185
+ {
186
+ keys: [77], // M
187
+ action: function(player, media) {
188
+ player.container.find('.mejs-volume-slider').css('display','block');
189
+ if (player.isVideo) {
190
+ player.showControls();
191
+ player.startControlsTimer();
192
+ }
193
+ if (player.media.muted) {
194
+ player.setMuted(false);
195
+ } else {
196
+ player.setMuted(true);
197
+ }
198
+ }
168
199
  }
169
200
  ]
170
201
  };
@@ -268,10 +299,14 @@ if (typeof jQuery != 'undefined') {
268
299
 
269
300
  // remove native controls
270
301
  t.$media.removeAttr('controls');
271
-
302
+ var videoPlayerTitle = t.isVideo ?
303
+ mejs.i18n.t('Video Player') : mejs.i18n.t('Audio Player');
304
+ // insert description for screen readers
305
+ $('<span class="mejs-offscreen">' + videoPlayerTitle + '</span>').insertBefore(t.$media);
272
306
  // build container
273
307
  t.container =
274
- $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') + '">'+
308
+ $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') +
309
+ '" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">'+
275
310
  '<div class="mejs-inner">'+
276
311
  '<div class="mejs-mediaelement"></div>'+
277
312
  '<div class="mejs-layers"></div>'+
@@ -280,7 +315,14 @@ if (typeof jQuery != 'undefined') {
280
315
  '</div>' +
281
316
  '</div>')
282
317
  .addClass(t.$media[0].className)
283
- .insertBefore(t.$media);
318
+ .insertBefore(t.$media)
319
+ .focus(function ( e ) {
320
+ if( !t.controlsAreVisible ) {
321
+ t.showControls(true);
322
+ var playButton = t.container.find('.mejs-playpause-button > button');
323
+ playButton.focus();
324
+ }
325
+ });
284
326
 
285
327
  // add classes for user and content
286
328
  t.container.addClass(
@@ -302,7 +344,7 @@ if (typeof jQuery != 'undefined') {
302
344
 
303
345
  t.$media.remove();
304
346
  t.$node = t.$media = $newMedia;
305
- t.node = t.media = $newMedia[0]
347
+ t.node = t.media = $newMedia[0];
306
348
 
307
349
  } else {
308
350
 
@@ -327,6 +369,7 @@ if (typeof jQuery != 'undefined') {
327
369
  capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
328
370
 
329
371
 
372
+
330
373
  if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
331
374
  t.width = t.options[tagType + 'Width'];
332
375
  } else if (t.media.style.width !== '' && t.media.style.width !== null) {
@@ -359,8 +402,8 @@ if (typeof jQuery != 'undefined') {
359
402
  mejs.MediaElement(t.$media[0], meOptions);
360
403
 
361
404
  if (typeof(t.container) != 'undefined' && t.controlsAreVisible){
362
- // controls are shown when loaded
363
- t.container.trigger('controlsshown');
405
+ // controls are shown when loaded
406
+ t.container.trigger('controlsshown');
364
407
  }
365
408
  },
366
409
 
@@ -376,8 +419,8 @@ if (typeof jQuery != 'undefined') {
376
419
  t.controls
377
420
  .css('visibility','visible')
378
421
  .stop(true, true).fadeIn(200, function() {
379
- t.controlsAreVisible = true;
380
- t.container.trigger('controlsshown');
422
+ t.controlsAreVisible = true;
423
+ t.container.trigger('controlsshown');
381
424
  });
382
425
 
383
426
  // any additional controls people might add and want to hide
@@ -408,7 +451,7 @@ if (typeof jQuery != 'undefined') {
408
451
 
409
452
  doAnimation = typeof doAnimation == 'undefined' || doAnimation;
410
453
 
411
- if (!t.controlsAreVisible || t.options.alwaysShowControls)
454
+ if (!t.controlsAreVisible || t.options.alwaysShowControls || t.keyboardAction)
412
455
  return;
413
456
 
414
457
  if (doAnimation) {
@@ -582,14 +625,14 @@ if (typeof jQuery != 'undefined') {
582
625
  }
583
626
  };
584
627
 
585
- // click to play/pause
586
- t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
628
+ // click to play/pause
629
+ t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
587
630
 
588
631
  // show/hide controls
589
632
  t.container
590
633
  .bind('mouseenter mouseover', function () {
591
634
  if (t.controlsEnabled) {
592
- if (!t.options.alwaysShowControls) {
635
+ if (!t.options.alwaysShowControls ) {
593
636
  t.killControlsTimer('enter');
594
637
  t.showControls();
595
638
  t.startControlsTimer(2500);
@@ -601,7 +644,6 @@ if (typeof jQuery != 'undefined') {
601
644
  if (!t.controlsAreVisible) {
602
645
  t.showControls();
603
646
  }
604
- //t.killControlsTimer('move');
605
647
  if (!t.options.alwaysShowControls) {
606
648
  t.startControlsTimer(2500);
607
649
  }
@@ -663,6 +705,10 @@ if (typeof jQuery != 'undefined') {
663
705
  if(t.options.autoRewind) {
664
706
  try{
665
707
  t.media.setCurrentTime(0);
708
+ // Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
709
+ window.setTimeout(function(){
710
+ $(t.container).find('.mejs-overlay-loading').parent().hide();
711
+ }, 20);
666
712
  } catch (exp) {
667
713
 
668
714
  }
@@ -698,6 +744,15 @@ if (typeof jQuery != 'undefined') {
698
744
  }
699
745
  }, false);
700
746
 
747
+ t.container.focusout(function (e) {
748
+ if( e.relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
749
+ var $target = $(e.relatedTarget);
750
+ if (t.keyboardAction && $target.parents('.mejs-container').length === 0) {
751
+ t.keyboardAction = false;
752
+ t.hideControls(true);
753
+ }
754
+ }
755
+ });
701
756
 
702
757
  // webkit has trouble doing this without a delay
703
758
  setTimeout(function () {
@@ -717,8 +772,10 @@ if (typeof jQuery != 'undefined') {
717
772
  t.setControlsSize();
718
773
  });
719
774
 
720
- // TEMP: needs to be moved somewhere else
721
- if (t.media.pluginType == 'youtube') {
775
+ // This is a work-around for a bug in the YouTube iFrame player, which means
776
+ // we can't use the play() API for the initial playback on iOS or Android;
777
+ // user has to start playback directly by tapping on the iFrame.
778
+ if (t.media.pluginType == 'youtube' && ( mf.isiOS || mf.isAndroid ) ) {
722
779
  t.container.find('.mejs-overlay-play').hide();
723
780
  }
724
781
  }
@@ -753,6 +810,10 @@ if (typeof jQuery != 'undefined') {
753
810
  setPlayerSize: function(width,height) {
754
811
  var t = this;
755
812
 
813
+ if( !t.options.setDimensions ) {
814
+ return false;
815
+ }
816
+
756
817
  if (typeof width != 'undefined') {
757
818
  t.width = width;
758
819
  }
@@ -762,18 +823,45 @@ if (typeof jQuery != 'undefined') {
762
823
  }
763
824
 
764
825
  // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
765
- if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || parseInt(t.$node.css('max-width').replace(/px/,''), 10) / t.$node.offsetParent().width() === 1 || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
826
+ if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
766
827
 
767
828
  // do we have the native dimensions yet?
829
+ var nativeWidth = (function() {
830
+ if (t.isVideo) {
831
+ if (t.media.videoWidth && t.media.videoWidth > 0) {
832
+ return t.media.videoWidth;
833
+ } else if (t.media.getAttribute('width') !== null) {
834
+ return t.media.getAttribute('width');
835
+ } else {
836
+ return t.options.defaultVideoWidth;
837
+ }
838
+ } else {
839
+ return t.options.defaultAudioWidth;
840
+ }
841
+ })();
842
+
843
+ var nativeHeight = (function() {
844
+ if (t.isVideo) {
845
+ if (t.media.videoHeight && t.media.videoHeight > 0) {
846
+ return t.media.videoHeight;
847
+ } else if (t.media.getAttribute('height') !== null) {
848
+ return t.media.getAttribute('height');
849
+ } else {
850
+ return t.options.defaultVideoHeight;
851
+ }
852
+ } else {
853
+ return t.options.defaultAudioHeight;
854
+ }
855
+ })();
856
+
768
857
  var
769
- nativeWidth = t.isVideo ? ((t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth) : t.options.defaultAudioWidth,
770
- nativeHeight = t.isVideo ? ((t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight) : t.options.defaultAudioHeight,
771
858
  parentWidth = t.container.parent().closest(':visible').width(),
859
+ parentHeight = t.container.parent().closest(':visible').height(),
772
860
  newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
773
861
 
774
862
  // When we use percent, the newHeight can't be calculated so we get the container height
775
- if(isNaN(newHeight)) {
776
- newHeight = t.container.parent().closest(':visible').height();
863
+ if (isNaN(newHeight)) {
864
+ newHeight = parentHeight;
777
865
  }
778
866
 
779
867
  if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
@@ -781,7 +869,8 @@ if (typeof jQuery != 'undefined') {
781
869
  newHeight = $(window).height();
782
870
  }
783
871
 
784
- if ( newHeight != 0 && parentWidth != 0 ) {
872
+ if ( newHeight && parentWidth ) {
873
+
785
874
  // set outer container size
786
875
  t.container
787
876
  .width(parentWidth)
@@ -838,7 +927,7 @@ if (typeof jQuery != 'undefined') {
838
927
  others = rail.siblings(),
839
928
  lastControl = others.last(),
840
929
  lastControlPosition = null;
841
-
930
+
842
931
  // skip calculation if hidden
843
932
  if (!t.container.is(':visible') || !rail.length || !rail.is(':visible')) {
844
933
  return;
@@ -849,7 +938,7 @@ if (typeof jQuery != 'undefined') {
849
938
  if (t.options && !t.options.autosizeProgress) {
850
939
  // Also, frontends devs can be more flexible
851
940
  // due the opportunity of absolute positioning.
852
- railWidth = parseInt(rail.css('width'));
941
+ railWidth = parseInt(rail.css('width'), 10);
853
942
  }
854
943
 
855
944
  // attempt to autosize
@@ -866,22 +955,22 @@ if (typeof jQuery != 'undefined') {
866
955
  // fit the rail into the remaining space
867
956
  railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
868
957
  }
869
-
958
+
870
959
  // resize the rail,
871
960
  // but then check if the last control (say, the fullscreen button) got pushed down
872
961
  // this often happens when zoomed
873
- do {
962
+ do {
874
963
  // outer area
875
964
  rail.width(railWidth);
876
965
  // dark space
877
- total.width(railWidth - (total.outerWidth(true) - total.width()));
878
-
966
+ total.width(railWidth - (total.outerWidth(true) - total.width()));
967
+
879
968
  if (lastControl.css('position') != 'absolute') {
880
- lastControlPosition = lastControl.position();
881
- railWidth--;
969
+ lastControlPosition = lastControl.position();
970
+ railWidth--;
882
971
  }
883
- } while (lastControlPosition != null && lastControlPosition.top > 0 && railWidth > 0);
884
-
972
+ } while (lastControlPosition !== null && lastControlPosition.top > 0 && railWidth > 0);
973
+
885
974
  if (t.setProgressRail)
886
975
  t.setProgressRail();
887
976
  if (t.setCurrentRail)
@@ -903,7 +992,7 @@ if (typeof jQuery != 'undefined') {
903
992
  }
904
993
 
905
994
  // second, try the real poster
906
- if (posterUrl !== '' && posterUrl != null) {
995
+ if ( posterUrl ) {
907
996
  t.setPoster(posterUrl);
908
997
  } else {
909
998
  poster.hide();
@@ -925,7 +1014,7 @@ if (typeof jQuery != 'undefined') {
925
1014
  posterDiv = t.container.find('.mejs-poster'),
926
1015
  posterImg = posterDiv.find('img');
927
1016
 
928
- if (posterImg.length == 0) {
1017
+ if (posterImg.length === 0) {
929
1018
  posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
930
1019
  }
931
1020
 
@@ -957,7 +1046,7 @@ if (typeof jQuery != 'undefined') {
957
1046
  '<div class="mejs-overlay-button"></div>'+
958
1047
  '</div>')
959
1048
  .appendTo(layers)
960
- .bind('click touchstart', function() {
1049
+ .bind('click', function() { // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
961
1050
  if (t.options.clickToPlayPause) {
962
1051
  if (media.paused) {
963
1052
  media.play();
@@ -1018,10 +1107,23 @@ if (typeof jQuery != 'undefined') {
1018
1107
 
1019
1108
  loading.show();
1020
1109
  controls.find('.mejs-time-buffering').show();
1110
+ // Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
1111
+ if (mejs.MediaFeatures.isAndroid) {
1112
+ media.canplayTimeout = window.setTimeout(
1113
+ function() {
1114
+ if (document.createEvent) {
1115
+ var evt = document.createEvent('HTMLEvents');
1116
+ evt.initEvent('canplay', true, true);
1117
+ return media.dispatchEvent(evt);
1118
+ }
1119
+ }, 300
1120
+ );
1121
+ }
1021
1122
  }, false);
1022
1123
  media.addEventListener('canplay',function() {
1023
1124
  loading.hide();
1024
1125
  controls.find('.mejs-time-buffering').hide();
1126
+ clearTimeout(media.canplayTimeout); // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
1025
1127
  }, false);
1026
1128
 
1027
1129
  // error handling
@@ -1031,40 +1133,50 @@ if (typeof jQuery != 'undefined') {
1031
1133
  error.show();
1032
1134
  error.find('mejs-overlay-error').html("Error loading this resource");
1033
1135
  }, false);
1136
+
1137
+ media.addEventListener('keydown', function(e) {
1138
+ t.onkeydown(player, media, e);
1139
+ }, false);
1034
1140
  },
1035
1141
 
1036
1142
  buildkeyboard: function(player, controls, layers, media) {
1037
1143
 
1038
1144
  var t = this;
1039
1145
 
1146
+ t.container.keydown(function () {
1147
+ t.keyboardAction = true;
1148
+ });
1149
+
1040
1150
  // listen for key presses
1041
1151
  t.globalBind('keydown', function(e) {
1042
-
1043
- if (player.hasFocus && player.options.enableKeyboard) {
1044
-
1045
- // find a matching key
1046
- for (var i=0, il=player.options.keyActions.length; i<il; i++) {
1047
- var keyAction = player.options.keyActions[i];
1048
-
1049
- for (var j=0, jl=keyAction.keys.length; j<jl; j++) {
1050
- if (e.keyCode == keyAction.keys[j]) {
1051
- e.preventDefault();
1052
- keyAction.action(player, media, e.keyCode);
1053
- return false;
1054
- }
1055
- }
1056
- }
1057
- }
1058
-
1059
- return true;
1152
+ return t.onkeydown(player, media, e);
1060
1153
  });
1061
1154
 
1155
+
1062
1156
  // check if someone clicked outside a player region, then kill its focus
1063
1157
  t.globalBind('click', function(event) {
1064
- player.hasFocus = $(event.target).closest('.mejs-container').length != 0;
1158
+ player.hasFocus = $(event.target).closest('.mejs-container').length !== 0;
1065
1159
  });
1066
1160
 
1067
1161
  },
1162
+ onkeydown: function(player, media, e) {
1163
+ if (player.hasFocus && player.options.enableKeyboard) {
1164
+ // find a matching key
1165
+ for (var i = 0, il = player.options.keyActions.length; i < il; i++) {
1166
+ var keyAction = player.options.keyActions[i];
1167
+
1168
+ for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
1169
+ if (e.keyCode == keyAction.keys[j]) {
1170
+ if (typeof(e.preventDefault) == "function") e.preventDefault();
1171
+ keyAction.action(player, media, e.keyCode);
1172
+ return false;
1173
+ }
1174
+ }
1175
+ }
1176
+ }
1177
+
1178
+ return true;
1179
+ },
1068
1180
 
1069
1181
  findTracks: function() {
1070
1182
  var t = this,
@@ -1149,7 +1261,7 @@ if (typeof jQuery != 'undefined') {
1149
1261
  // detach events from the video
1150
1262
  // TODO: detach event listeners better than this;
1151
1263
  // also detach ONLY the events attached by this plugin!
1152
- t.$node.clone().show().insertBefore(t.container);
1264
+ t.$node.clone().insertBefore(t.container).show();
1153
1265
  t.$node.remove();
1154
1266
  } else {
1155
1267
  t.$node.insertBefore(t.container);
@@ -1167,6 +1279,11 @@ if (typeof jQuery != 'undefined') {
1167
1279
  }
1168
1280
  t.globalUnbind();
1169
1281
  delete t.node.player;
1282
+ },
1283
+ rebuildtracks: function(){
1284
+ var t = this;
1285
+ t.findTracks();
1286
+ t.buildtracks(t, t.controls, t.layers, t.media);
1170
1287
  }
1171
1288
  };
1172
1289
 
@@ -1207,30 +1324,31 @@ if (typeof jQuery != 'undefined') {
1207
1324
  })();
1208
1325
 
1209
1326
  // turn into jQuery plugin
1210
- if (typeof jQuery != 'undefined') {
1211
- jQuery.fn.mediaelementplayer = function (options) {
1327
+ if (typeof $ != 'undefined') {
1328
+ $.fn.mediaelementplayer = function (options) {
1212
1329
  if (options === false) {
1213
1330
  this.each(function () {
1214
- var player = jQuery(this).data('mediaelementplayer');
1331
+ var player = $(this).data('mediaelementplayer');
1215
1332
  if (player) {
1216
1333
  player.remove();
1217
1334
  }
1218
- jQuery(this).removeData('mediaelementplayer');
1335
+ $(this).removeData('mediaelementplayer');
1219
1336
  });
1220
1337
  }
1221
1338
  else {
1222
1339
  this.each(function () {
1223
- jQuery(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
1340
+ $(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
1224
1341
  });
1225
1342
  }
1226
1343
  return this;
1227
1344
  };
1228
- }
1229
1345
 
1230
- $(document).ready(function() {
1231
- // auto enable using JSON attribute
1232
- $('.mejs-player').mediaelementplayer();
1233
- });
1346
+
1347
+ $(document).ready(function() {
1348
+ // auto enable using JSON attribute
1349
+ $('.mejs-player').mediaelementplayer();
1350
+ });
1351
+ }
1234
1352
 
1235
1353
  // push out to window
1236
1354
  window.MediaElementPlayer = mejs.MediaElementPlayer;
@@ -1240,7 +1358,8 @@ if (typeof jQuery != 'undefined') {
1240
1358
  (function($) {
1241
1359
 
1242
1360
  $.extend(mejs.MepDefaults, {
1243
- playpauseText: mejs.i18n.t('Play/Pause')
1361
+ playText: mejs.i18n.t('Play'),
1362
+ pauseText: mejs.i18n.t('Pause')
1244
1363
  });
1245
1364
 
1246
1365
  // PLAY/pause BUTTON
@@ -1248,9 +1367,10 @@ if (typeof jQuery != 'undefined') {
1248
1367
  buildplaypause: function(player, controls, layers, media) {
1249
1368
  var
1250
1369
  t = this,
1370
+ op = t.options,
1251
1371
  play =
1252
1372
  $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
1253
- '<button type="button" aria-controls="' + t.id + '" title="' + t.options.playpauseText + '" aria-label="' + t.options.playpauseText + '"></button>' +
1373
+ '<button type="button" aria-controls="' + t.id + '" title="' + op.playText + '" aria-label="' + op.playText + '"></button>' +
1254
1374
  '</div>')
1255
1375
  .appendTo(controls)
1256
1376
  .click(function(e) {
@@ -1263,21 +1383,41 @@ if (typeof jQuery != 'undefined') {
1263
1383
  }
1264
1384
 
1265
1385
  return false;
1266
- });
1386
+ }),
1387
+ play_btn = play.find('button');
1388
+
1389
+
1390
+ function togglePlayPause(which) {
1391
+ if ('play' === which) {
1392
+ play.removeClass('mejs-play').addClass('mejs-pause');
1393
+ play_btn.attr({
1394
+ 'title': op.pauseText,
1395
+ 'aria-label': op.pauseText
1396
+ });
1397
+ } else {
1398
+ play.removeClass('mejs-pause').addClass('mejs-play');
1399
+ play_btn.attr({
1400
+ 'title': op.playText,
1401
+ 'aria-label': op.playText
1402
+ });
1403
+ }
1404
+ };
1405
+ togglePlayPause('pse');
1406
+
1267
1407
 
1268
1408
  media.addEventListener('play',function() {
1269
- play.removeClass('mejs-play').addClass('mejs-pause');
1409
+ togglePlayPause('play');
1270
1410
  }, false);
1271
1411
  media.addEventListener('playing',function() {
1272
- play.removeClass('mejs-play').addClass('mejs-pause');
1412
+ togglePlayPause('play');
1273
1413
  }, false);
1274
1414
 
1275
1415
 
1276
1416
  media.addEventListener('pause',function() {
1277
- play.removeClass('mejs-pause').addClass('mejs-play');
1417
+ togglePlayPause('pse');
1278
1418
  }, false);
1279
1419
  media.addEventListener('paused',function() {
1280
- play.removeClass('mejs-pause').addClass('mejs-play');
1420
+ togglePlayPause('pse');
1281
1421
  }, false);
1282
1422
  }
1283
1423
  });
@@ -1319,24 +1459,30 @@ if (typeof jQuery != 'undefined') {
1319
1459
  })(mejs.$);
1320
1460
 
1321
1461
  (function($) {
1462
+
1463
+ $.extend(mejs.MepDefaults, {
1464
+ progessHelpText: mejs.i18n.t(
1465
+ 'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.')
1466
+ });
1467
+
1322
1468
  // progress/loaded bar
1323
1469
  $.extend(MediaElementPlayer.prototype, {
1324
1470
  buildprogress: function(player, controls, layers, media) {
1325
1471
 
1326
- $('<div class="mejs-time-rail">'+
1327
- '<span class="mejs-time-total">'+
1328
- '<span class="mejs-time-buffering"></span>'+
1329
- '<span class="mejs-time-loaded"></span>'+
1330
- '<span class="mejs-time-current"></span>'+
1331
- '<span class="mejs-time-handle"></span>'+
1332
- '<span class="mejs-time-float">' +
1333
- '<span class="mejs-time-float-current">00:00</span>' +
1334
- '<span class="mejs-time-float-corner"></span>' +
1335
- '</span>'+
1336
- '</span>'+
1337
- '</div>')
1472
+ $('<div class="mejs-time-rail">' +
1473
+ '<span class="mejs-time-total mejs-time-slider">' +
1474
+ //'<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' +
1475
+ '<span class="mejs-time-buffering"></span>' +
1476
+ '<span class="mejs-time-loaded"></span>' +
1477
+ '<span class="mejs-time-current"></span>' +
1478
+ '<span class="mejs-time-handle"></span>' +
1479
+ '<span class="mejs-time-float">' +
1480
+ '<span class="mejs-time-float-current">00:00</span>' +
1481
+ '<span class="mejs-time-float-corner"></span>' +
1482
+ '</span>' +
1483
+ '</div>')
1338
1484
  .appendTo(controls);
1339
- controls.find('.mejs-time-buffering').hide();
1485
+ controls.find('.mejs-time-buffering').hide();
1340
1486
 
1341
1487
  var
1342
1488
  t = this,
@@ -1346,20 +1492,22 @@ if (typeof jQuery != 'undefined') {
1346
1492
  handle = controls.find('.mejs-time-handle'),
1347
1493
  timefloat = controls.find('.mejs-time-float'),
1348
1494
  timefloatcurrent = controls.find('.mejs-time-float-current'),
1495
+ slider = controls.find('.mejs-time-slider'),
1349
1496
  handleMouseMove = function (e) {
1350
- // mouse or touch position relative to the object
1351
- if (e.originalEvent.changedTouches) {
1352
- var x = e.originalEvent.changedTouches[0].pageX;
1353
- }else{
1354
- var x = e.pageX;
1355
- }
1356
1497
 
1357
- var offset = total.offset(),
1498
+ var offset = total.offset(),
1358
1499
  width = total.outerWidth(true),
1359
1500
  percentage = 0,
1360
1501
  newTime = 0,
1361
- pos = 0;
1362
-
1502
+ pos = 0,
1503
+ x;
1504
+
1505
+ // mouse or touch position relative to the object
1506
+ if (e.originalEvent.changedTouches) {
1507
+ x = e.originalEvent.changedTouches[0].pageX;
1508
+ }else{
1509
+ x = e.pageX;
1510
+ }
1363
1511
 
1364
1512
  if (media.duration) {
1365
1513
  if (x < offset.left) {
@@ -1386,7 +1534,101 @@ if (typeof jQuery != 'undefined') {
1386
1534
  }
1387
1535
  },
1388
1536
  mouseIsDown = false,
1389
- mouseIsOver = false;
1537
+ mouseIsOver = false,
1538
+ lastKeyPressTime = 0,
1539
+ startedPaused = false,
1540
+ autoRewindInitial = player.options.autoRewind;
1541
+ // Accessibility for slider
1542
+ var updateSlider = function (e) {
1543
+
1544
+ var seconds = media.currentTime,
1545
+ timeSliderText = mejs.i18n.t('Time Slider'),
1546
+ time = mejs.Utility.secondsToTimeCode(seconds),
1547
+ duration = media.duration;
1548
+
1549
+ slider.attr({
1550
+ 'aria-label': timeSliderText,
1551
+ 'aria-valuemin': 0,
1552
+ 'aria-valuemax': duration,
1553
+ 'aria-valuenow': seconds,
1554
+ 'aria-valuetext': time,
1555
+ 'role': 'slider',
1556
+ 'tabindex': 0
1557
+ });
1558
+
1559
+ };
1560
+
1561
+ var restartPlayer = function () {
1562
+ var now = new Date();
1563
+ if (now - lastKeyPressTime >= 1000) {
1564
+ media.play();
1565
+ }
1566
+ };
1567
+
1568
+ slider.bind('focus', function (e) {
1569
+ player.options.autoRewind = false;
1570
+ });
1571
+
1572
+ slider.bind('blur', function (e) {
1573
+ player.options.autoRewind = autoRewindInitial;
1574
+ });
1575
+
1576
+ slider.bind('keydown', function (e) {
1577
+
1578
+ if ((new Date() - lastKeyPressTime) >= 1000) {
1579
+ startedPaused = media.paused;
1580
+ }
1581
+
1582
+ var keyCode = e.keyCode,
1583
+ duration = media.duration,
1584
+ seekTime = media.currentTime;
1585
+
1586
+ switch (keyCode) {
1587
+ case 37: // left
1588
+ seekTime -= 1;
1589
+ break;
1590
+ case 39: // Right
1591
+ seekTime += 1;
1592
+ break;
1593
+ case 38: // Up
1594
+ seekTime += Math.floor(duration * 0.1);
1595
+ break;
1596
+ case 40: // Down
1597
+ seekTime -= Math.floor(duration * 0.1);
1598
+ break;
1599
+ case 36: // Home
1600
+ seekTime = 0;
1601
+ break;
1602
+ case 35: // end
1603
+ seekTime = duration;
1604
+ break;
1605
+ case 10: // enter
1606
+ media.paused ? media.play() : media.pause();
1607
+ return;
1608
+ case 13: // space
1609
+ media.paused ? media.play() : media.pause();
1610
+ return;
1611
+ default:
1612
+ return;
1613
+ }
1614
+
1615
+ seekTime = seekTime < 0 ? 0 : (seekTime >= duration ? duration : Math.floor(seekTime));
1616
+ lastKeyPressTime = new Date();
1617
+ if (!startedPaused) {
1618
+ media.pause();
1619
+ }
1620
+
1621
+ if (seekTime < media.duration && !startedPaused) {
1622
+ setTimeout(restartPlayer, 1100);
1623
+ }
1624
+
1625
+ media.setCurrentTime(seekTime);
1626
+
1627
+ e.preventDefault();
1628
+ e.stopPropagation();
1629
+ return false;
1630
+ });
1631
+
1390
1632
 
1391
1633
  // handle clicks
1392
1634
  //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
@@ -1404,7 +1646,6 @@ if (typeof jQuery != 'undefined') {
1404
1646
  timefloat.hide();
1405
1647
  t.globalUnbind('.dur');
1406
1648
  });
1407
- return false;
1408
1649
  }
1409
1650
  })
1410
1651
  .bind('mouseenter', function(e) {
@@ -1434,6 +1675,7 @@ if (typeof jQuery != 'undefined') {
1434
1675
  media.addEventListener('timeupdate', function(e) {
1435
1676
  player.setProgressRail(e);
1436
1677
  player.setCurrentRail(e);
1678
+ updateSlider(e);
1437
1679
  }, false);
1438
1680
 
1439
1681
 
@@ -1447,8 +1689,8 @@ if (typeof jQuery != 'undefined') {
1447
1689
 
1448
1690
  var
1449
1691
  t = this,
1450
- target = (e != undefined) ? e.target : t.media,
1451
- percent = null;
1692
+ target = (e !== undefined) ? e.target : t.media,
1693
+ percent = null;
1452
1694
 
1453
1695
  // newest HTML5 spec has buffered array (FF4, Webkit)
1454
1696
  if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
@@ -1459,12 +1701,12 @@ if (typeof jQuery != 'undefined') {
1459
1701
  // to be anything other than 0. If the byte count is available we use this instead.
1460
1702
  // Browsers that support the else if do not seem to have the bufferedBytes value and
1461
1703
  // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
1462
- else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
1704
+ else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
1463
1705
  percent = target.bufferedBytes / target.bytesTotal;
1464
1706
  }
1465
1707
  // Firefox 3 with an Ogg file seems to go this way
1466
- else if (e && e.lengthComputable && e.total != 0) {
1467
- percent = e.loaded/e.total;
1708
+ else if (e && e.lengthComputable && e.total !== 0) {
1709
+ percent = e.loaded / e.total;
1468
1710
  }
1469
1711
 
1470
1712
  // finally update the progress bar
@@ -1480,7 +1722,7 @@ if (typeof jQuery != 'undefined') {
1480
1722
 
1481
1723
  var t = this;
1482
1724
 
1483
- if (t.media.currentTime != undefined && t.media.duration) {
1725
+ if (t.media.currentTime !== undefined && t.media.duration) {
1484
1726
 
1485
1727
  // update bar and handle
1486
1728
  if (t.total && t.handle) {
@@ -1493,10 +1735,9 @@ if (typeof jQuery != 'undefined') {
1493
1735
  }
1494
1736
  }
1495
1737
 
1496
- }
1738
+ }
1497
1739
  });
1498
1740
  })(mejs.$);
1499
-
1500
1741
  (function($) {
1501
1742
 
1502
1743
  // options
@@ -1511,11 +1752,13 @@ if (typeof jQuery != 'undefined') {
1511
1752
  buildcurrent: function(player, controls, layers, media) {
1512
1753
  var t = this;
1513
1754
 
1514
- $('<div class="mejs-time">'+
1515
- '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '')
1516
- + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ '</span>'+
1517
- '</div>')
1518
- .appendTo(controls);
1755
+ $('<div class="mejs-time" role="timer" aria-live="off">' +
1756
+ '<span class="mejs-currenttime">' +
1757
+ (player.options.alwaysShowHours ? '00:' : '') +
1758
+ (player.options.showTimecodeFrameCount? '00:00:00':'00:00') +
1759
+ '</span>'+
1760
+ '</div>')
1761
+ .appendTo(controls);
1519
1762
 
1520
1763
  t.currenttime = t.controls.find('.mejs-currenttime');
1521
1764
 
@@ -1533,8 +1776,8 @@ if (typeof jQuery != 'undefined') {
1533
1776
  '<span class="mejs-duration">' +
1534
1777
  (t.options.duration > 0 ?
1535
1778
  mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1536
- ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1537
- ) +
1779
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1780
+ ) +
1538
1781
  '</span>')
1539
1782
  .appendTo(controls.find('.mejs-time'));
1540
1783
  } else {
@@ -1546,8 +1789,8 @@ if (typeof jQuery != 'undefined') {
1546
1789
  '<span class="mejs-duration">' +
1547
1790
  (t.options.duration > 0 ?
1548
1791
  mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
1549
- ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1550
- ) +
1792
+ ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
1793
+ ) +
1551
1794
  '</span>' +
1552
1795
  '</div>')
1553
1796
  .appendTo(controls);
@@ -1586,6 +1829,7 @@ if (typeof jQuery != 'undefined') {
1586
1829
 
1587
1830
  $.extend(mejs.MepDefaults, {
1588
1831
  muteText: mejs.i18n.t('Mute Toggle'),
1832
+ allyVolumeControlText: mejs.i18n.t('Use Up/Down Arrow keys to increase or decrease volume.'),
1589
1833
  hideVolumeOnTouchDevices: true,
1590
1834
 
1591
1835
  audioVolume: 'horizontal',
@@ -1604,25 +1848,33 @@ if (typeof jQuery != 'undefined') {
1604
1848
  mute = (mode == 'horizontal') ?
1605
1849
 
1606
1850
  // horizontal version
1607
- $('<div class="mejs-button mejs-volume-button mejs-mute">'+
1608
- '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '" aria-label="' + t.options.muteText + '"></button>'+
1851
+ $('<div class="mejs-button mejs-volume-button mejs-mute">' +
1852
+ '<button type="button" aria-controls="' + t.id +
1853
+ '" title="' + t.options.muteText +
1854
+ '" aria-label="' + t.options.muteText +
1855
+ '"></button>'+
1609
1856
  '</div>' +
1610
- '<div class="mejs-horizontal-volume-slider">'+ // outer background
1857
+ '<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
1858
+ '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
1611
1859
  '<div class="mejs-horizontal-volume-total"></div>'+ // line background
1612
1860
  '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
1613
1861
  '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
1614
- '</div>'
1862
+ '</a>'
1615
1863
  )
1616
1864
  .appendTo(controls) :
1617
1865
 
1618
1866
  // vertical version
1619
1867
  $('<div class="mejs-button mejs-volume-button mejs-mute">'+
1620
- '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '" aria-label="' + t.options.muteText + '"></button>'+
1621
- '<div class="mejs-volume-slider">'+ // outer background
1868
+ '<button type="button" aria-controls="' + t.id +
1869
+ '" title="' + t.options.muteText +
1870
+ '" aria-label="' + t.options.muteText +
1871
+ '"></button>'+
1872
+ '<a href="javascript:void(0);" class="mejs-volume-slider">'+ // outer background
1873
+ '<span class="mejs-offscreen">' + t.options.allyVolumeControlText + '</span>' +
1622
1874
  '<div class="mejs-volume-total"></div>'+ // line background
1623
1875
  '<div class="mejs-volume-current"></div>'+ // current volume
1624
1876
  '<div class="mejs-volume-handle"></div>'+ // handle
1625
- '</div>'+
1877
+ '</a>'+
1626
1878
  '</div>')
1627
1879
  .appendTo(controls),
1628
1880
  volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
@@ -1635,32 +1887,30 @@ if (typeof jQuery != 'undefined') {
1635
1887
  if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
1636
1888
  volumeSlider.show();
1637
1889
  positionVolumeHandle(volume, true);
1638
- volumeSlider.hide()
1890
+ volumeSlider.hide();
1639
1891
  return;
1640
1892
  }
1641
-
1893
+
1642
1894
  // correct to 0-1
1643
1895
  volume = Math.max(0,volume);
1644
- volume = Math.min(volume,1);
1645
-
1896
+ volume = Math.min(volume,1);
1897
+
1646
1898
  // ajust mute button style
1647
- if (volume == 0) {
1899
+ if (volume === 0) {
1648
1900
  mute.removeClass('mejs-mute').addClass('mejs-unmute');
1649
1901
  } else {
1650
1902
  mute.removeClass('mejs-unmute').addClass('mejs-mute');
1651
- }
1903
+ }
1652
1904
 
1905
+ // top/left of full size volume slider background
1906
+ var totalPosition = volumeTotal.position();
1653
1907
  // position slider
1654
1908
  if (mode == 'vertical') {
1655
- var
1656
-
1657
- // height of the full size volume slider background
1909
+ var
1910
+ // height of the full size volume slider background
1658
1911
  totalHeight = volumeTotal.height(),
1659
-
1660
- // top/left of full size volume slider background
1661
- totalPosition = volumeTotal.position(),
1662
-
1663
- // the new top position based on the current volume
1912
+
1913
+ // the new top position based on the current volume
1664
1914
  // 70% volume on 100px height == top:30px
1665
1915
  newTop = totalHeight - (totalHeight * volume);
1666
1916
 
@@ -1671,14 +1921,10 @@ if (typeof jQuery != 'undefined') {
1671
1921
  volumeCurrent.height(totalHeight - newTop );
1672
1922
  volumeCurrent.css('top', totalPosition.top + newTop);
1673
1923
  } else {
1674
- var
1675
-
1924
+ var
1676
1925
  // height of the full size volume slider background
1677
1926
  totalWidth = volumeTotal.width(),
1678
1927
 
1679
- // top/left of full size volume slider background
1680
- totalPosition = volumeTotal.position(),
1681
-
1682
1928
  // the new left position based on the current volume
1683
1929
  newLeft = totalWidth * volume;
1684
1930
 
@@ -1695,7 +1941,7 @@ if (typeof jQuery != 'undefined') {
1695
1941
  totalOffset = volumeTotal.offset();
1696
1942
 
1697
1943
  // calculate the new volume based on the moust position
1698
- if (mode == 'vertical') {
1944
+ if (mode === 'vertical') {
1699
1945
 
1700
1946
  var
1701
1947
  railHeight = volumeTotal.height(),
@@ -1705,8 +1951,9 @@ if (typeof jQuery != 'undefined') {
1705
1951
  volume = (railHeight - newY) / railHeight;
1706
1952
 
1707
1953
  // the controls just hide themselves (usually when mouse moves too far up)
1708
- if (totalOffset.top == 0 || totalOffset.left == 0)
1954
+ if (totalOffset.top === 0 || totalOffset.left === 0) {
1709
1955
  return;
1956
+ }
1710
1957
 
1711
1958
  } else {
1712
1959
  var
@@ -1720,16 +1967,16 @@ if (typeof jQuery != 'undefined') {
1720
1967
  volume = Math.max(0,volume);
1721
1968
  volume = Math.min(volume,1);
1722
1969
 
1723
- // position the slider and handle
1970
+ // position the slider and handle
1724
1971
  positionVolumeHandle(volume);
1725
1972
 
1726
1973
  // set the media object (this will trigger the volumechanged event)
1727
- if (volume == 0) {
1974
+ if (volume === 0) {
1728
1975
  media.setMuted(true);
1729
1976
  } else {
1730
1977
  media.setMuted(false);
1731
1978
  }
1732
- media.setVolume(volume);
1979
+ media.setVolume(volume);
1733
1980
  },
1734
1981
  mouseIsDown = false,
1735
1982
  mouseIsOver = false;
@@ -1741,12 +1988,28 @@ if (typeof jQuery != 'undefined') {
1741
1988
  volumeSlider.show();
1742
1989
  mouseIsOver = true;
1743
1990
  }, function() {
1744
- mouseIsOver = false;
1991
+ mouseIsOver = false;
1745
1992
 
1746
1993
  if (!mouseIsDown && mode == 'vertical') {
1747
1994
  volumeSlider.hide();
1748
1995
  }
1749
1996
  });
1997
+
1998
+ var updateVolumeSlider = function (e) {
1999
+
2000
+ var volume = Math.floor(media.volume*100);
2001
+
2002
+ volumeSlider.attr({
2003
+ 'aria-label': mejs.i18n.t('volumeSlider'),
2004
+ 'aria-valuemin': 0,
2005
+ 'aria-valuemax': 100,
2006
+ 'aria-valuenow': volume,
2007
+ 'aria-valuetext': volume+'%',
2008
+ 'role': 'slider',
2009
+ 'tabindex': 0
2010
+ });
2011
+
2012
+ };
1750
2013
 
1751
2014
  volumeSlider
1752
2015
  .bind('mouseover', function() {
@@ -1768,13 +2031,39 @@ if (typeof jQuery != 'undefined') {
1768
2031
  mouseIsDown = true;
1769
2032
 
1770
2033
  return false;
2034
+ })
2035
+ .bind('keydown', function (e) {
2036
+ var keyCode = e.keyCode;
2037
+ var volume = media.volume;
2038
+ switch (keyCode) {
2039
+ case 38: // Up
2040
+ volume += 0.1;
2041
+ break;
2042
+ case 40: // Down
2043
+ volume = volume - 0.1;
2044
+ break;
2045
+ default:
2046
+ return true;
2047
+ }
2048
+
2049
+ mouseIsDown = false;
2050
+ positionVolumeHandle(volume);
2051
+ media.setVolume(volume);
2052
+ return false;
2053
+ })
2054
+ .bind('blur', function () {
2055
+ volumeSlider.hide();
1771
2056
  });
1772
2057
 
1773
-
1774
2058
  // MUTE button
1775
2059
  mute.find('button').click(function() {
1776
2060
  media.setMuted( !media.muted );
1777
2061
  });
2062
+
2063
+ //Keyboard input
2064
+ mute.find('button').bind('focus', function () {
2065
+ volumeSlider.show();
2066
+ });
1778
2067
 
1779
2068
  // listen for volume change events from other sources
1780
2069
  media.addEventListener('volumechange', function(e) {
@@ -1787,6 +2076,7 @@ if (typeof jQuery != 'undefined') {
1787
2076
  mute.removeClass('mejs-unmute').addClass('mejs-mute');
1788
2077
  }
1789
2078
  }
2079
+ updateVolumeSlider(e);
1790
2080
  }, false);
1791
2081
 
1792
2082
  if (t.container.is(':visible')) {
@@ -1794,9 +2084,9 @@ if (typeof jQuery != 'undefined') {
1794
2084
  positionVolumeHandle(player.options.startVolume);
1795
2085
 
1796
2086
  // mutes the media and sets the volume icon muted if the initial volume is set to 0
1797
- if (player.options.startVolume === 0) {
1798
- media.setMuted(true);
1799
- }
2087
+ if (player.options.startVolume === 0) {
2088
+ media.setMuted(true);
2089
+ }
1800
2090
 
1801
2091
  // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
1802
2092
  if (media.pluginType === 'native') {
@@ -1807,7 +2097,6 @@ if (typeof jQuery != 'undefined') {
1807
2097
  });
1808
2098
 
1809
2099
  })(mejs.$);
1810
-
1811
2100
  (function($) {
1812
2101
 
1813
2102
  $.extend(mejs.MepDefaults, {
@@ -1850,11 +2139,7 @@ if (typeof jQuery != 'undefined') {
1850
2139
  }
1851
2140
  };
1852
2141
 
1853
- if (mejs.MediaFeatures.hasMozNativeFullScreen) {
1854
- player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
1855
- } else {
1856
- player.container.bind(mejs.MediaFeatures.fullScreenEventName, func);
1857
- }
2142
+ player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
1858
2143
  }
1859
2144
 
1860
2145
  var t = this,
@@ -2228,6 +2513,9 @@ if (typeof jQuery != 'undefined') {
2228
2513
 
2229
2514
  t.setControlsSize();
2230
2515
  t.isFullScreen = true;
2516
+
2517
+ t.container.find('.mejs-captions-text').css('font-size', screen.width / t.width * 1.00 * 100 + '%');
2518
+ t.container.find('.mejs-captions-position').css('bottom', '45px');
2231
2519
  },
2232
2520
 
2233
2521
  exitFullScreen: function() {
@@ -2280,6 +2568,9 @@ if (typeof jQuery != 'undefined') {
2280
2568
 
2281
2569
  t.setControlsSize();
2282
2570
  t.isFullScreen = false;
2571
+
2572
+ t.container.find('.mejs-captions-text').css('font-size','');
2573
+ t.container.find('.mejs-captions-position').css('bottom', '');
2283
2574
  }
2284
2575
  });
2285
2576
 
@@ -2287,48 +2578,133 @@ if (typeof jQuery != 'undefined') {
2287
2578
 
2288
2579
  (function($) {
2289
2580
 
2290
- // add extra default options
2581
+ // Speed
2582
+ $.extend(mejs.MepDefaults, {
2583
+
2584
+ speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
2585
+
2586
+ defaultSpeed: '1.00',
2587
+
2588
+ speedChar: 'x'
2589
+
2590
+ });
2591
+
2592
+ $.extend(MediaElementPlayer.prototype, {
2593
+
2594
+ buildspeed: function(player, controls, layers, media) {
2595
+ var t = this;
2596
+
2597
+ if (t.media.pluginType == 'native') {
2598
+ var
2599
+ speedButton = null,
2600
+ speedSelector = null,
2601
+ playbackSpeed = null,
2602
+ html = '<div class="mejs-button mejs-speed-button">' +
2603
+ '<button type="button">' + t.options.defaultSpeed + t.options.speedChar + '</button>' +
2604
+ '<div class="mejs-speed-selector">' +
2605
+ '<ul>';
2606
+
2607
+ if ($.inArray(t.options.defaultSpeed, t.options.speeds) === -1) {
2608
+ t.options.speeds.push(t.options.defaultSpeed);
2609
+ }
2610
+
2611
+ t.options.speeds.sort(function(a, b) {
2612
+ return parseFloat(b) - parseFloat(a);
2613
+ });
2614
+
2615
+ for (var i = 0, il = t.options.speeds.length; i<il; i++) {
2616
+ html += '<li>' +
2617
+ '<input type="radio" name="speed" ' +
2618
+ 'value="' + t.options.speeds[i] + '" ' +
2619
+ 'id="' + t.options.speeds[i] + '" ' +
2620
+ (t.options.speeds[i] == t.options.defaultSpeed ? ' checked' : '') +
2621
+ ' />' +
2622
+ '<label for="' + t.options.speeds[i] + '" ' +
2623
+ (t.options.speeds[i] == t.options.defaultSpeed ? ' class="mejs-speed-selected"' : '') +
2624
+ '>' + t.options.speeds[i] + t.options.speedChar + '</label>' +
2625
+ '</li>';
2626
+ }
2627
+ html += '</ul></div></div>';
2628
+
2629
+ speedButton = $(html).appendTo(controls);
2630
+ speedSelector = speedButton.find('.mejs-speed-selector');
2631
+
2632
+ playbackspeed = t.options.defaultSpeed;
2633
+
2634
+ speedSelector
2635
+ .on('click', 'input[type="radio"]', function() {
2636
+ var newSpeed = $(this).attr('value');
2637
+ playbackspeed = newSpeed;
2638
+ media.playbackRate = parseFloat(newSpeed);
2639
+ speedButton.find('button').html(newSpeed + t.options.speedChar);
2640
+ speedButton.find('.mejs-speed-selected').removeClass('mejs-speed-selected');
2641
+ speedButton.find('input[type="radio"]:checked').next().addClass('mejs-speed-selected');
2642
+ });
2643
+
2644
+ speedSelector
2645
+ .height(
2646
+ speedButton.find('.mejs-speed-selector ul').outerHeight(true) +
2647
+ speedButton.find('.mejs-speed-translations').outerHeight(true))
2648
+ .css('top', (-1 * speedSelector.height()) + 'px');
2649
+ }
2650
+ }
2651
+ });
2652
+
2653
+ })(mejs.$);
2654
+
2655
+ (function($) {
2656
+
2657
+ // add extra default options
2291
2658
  $.extend(mejs.MepDefaults, {
2292
2659
  // this will automatically turn on a <track>
2293
2660
  startLanguage: '',
2294
2661
 
2295
2662
  tracksText: mejs.i18n.t('Captions/Subtitles'),
2296
-
2663
+
2297
2664
  // option to remove the [cc] button when no <track kind="subtitles"> are present
2298
2665
  hideCaptionsButtonWhenEmpty: true,
2299
2666
 
2300
2667
  // If true and we only have one track, change captions to popup
2301
2668
  toggleCaptionsButtonWhenOnlyOne: false,
2302
2669
 
2303
- // #id or .class
2670
+ // #id or .class
2304
2671
  slidesSelector: ''
2305
2672
  });
2306
2673
 
2307
2674
  $.extend(MediaElementPlayer.prototype, {
2308
-
2675
+
2309
2676
  hasChapters: false,
2310
2677
 
2678
+ cleartracks: function(player, controls, layers, media){
2679
+ if(player) {
2680
+ if(player.captions) player.captions.remove();
2681
+ if(player.chapters) player.chapters.remove();
2682
+ if(player.captionsText) player.captionsText.remove();
2683
+ if(player.captionsButton) player.captionsButton.remove();
2684
+ }
2685
+ },
2311
2686
  buildtracks: function(player, controls, layers, media) {
2312
- if (player.tracks.length == 0)
2687
+ if (player.tracks.length === 0)
2313
2688
  return;
2314
2689
 
2315
- var t = this,
2316
- i,
2690
+ var t = this,
2691
+ i,
2317
2692
  options = '';
2318
2693
 
2319
2694
  if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
2320
- for (var i = t.domNode.textTracks.length - 1; i >= 0; i--) {
2695
+ for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
2321
2696
  t.domNode.textTracks[i].mode = "hidden";
2322
2697
  }
2323
2698
  }
2324
- player.chapters =
2699
+ t.cleartracks(player, controls, layers, media);
2700
+ player.chapters =
2325
2701
  $('<div class="mejs-chapters mejs-layer"></div>')
2326
2702
  .prependTo(layers).hide();
2327
- player.captions =
2328
- $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover"><span class="mejs-captions-text"></span></div></div>')
2703
+ player.captions =
2704
+ $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" role="log" aria-live="assertive" aria-atomic="false"><span class="mejs-captions-text"></span></div></div>')
2329
2705
  .prependTo(layers).hide();
2330
2706
  player.captionsText = player.captions.find('.mejs-captions-text');
2331
- player.captionsButton =
2707
+ player.captionsButton =
2332
2708
  $('<div class="mejs-button mejs-captions-button">'+
2333
2709
  '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+
2334
2710
  '<div class="mejs-captions-selector">'+
@@ -2341,8 +2717,8 @@ if (typeof jQuery != 'undefined') {
2341
2717
  '</div>'+
2342
2718
  '</div>')
2343
2719
  .appendTo(controls);
2344
-
2345
-
2720
+
2721
+
2346
2722
  var subtitleCount = 0;
2347
2723
  for (i=0; i<player.tracks.length; i++) {
2348
2724
  if (player.tracks[i].kind == 'subtitles') {
@@ -2354,19 +2730,17 @@ if (typeof jQuery != 'undefined') {
2354
2730
  if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
2355
2731
  // click
2356
2732
  player.captionsButton.on('click',function() {
2357
- if (player.selectedTrack == null) {
2358
- var lang = player.tracks[0].srclang;
2733
+ if (player.selectedTrack === null) {
2734
+ lang = player.tracks[0].srclang;
2359
2735
  } else {
2360
- var lang = 'none';
2736
+ lang = 'none';
2361
2737
  }
2362
2738
  player.setTrack(lang);
2363
2739
  });
2364
2740
  } else {
2365
- // hover
2366
- player.captionsButton.hover(function() {
2741
+ // hover or keyboard focus
2742
+ player.captionsButton.on( 'mouseenter focusin', function() {
2367
2743
  $(this).find('.mejs-captions-selector').css('visibility','visible');
2368
- }, function() {
2369
- $(this).find('.mejs-captions-selector').css('visibility','hidden');
2370
2744
  })
2371
2745
 
2372
2746
  // handle clicks to the language radio buttons
@@ -2375,6 +2749,10 @@ if (typeof jQuery != 'undefined') {
2375
2749
  player.setTrack(lang);
2376
2750
  });
2377
2751
 
2752
+ player.captionsButton.on( 'mouseleave focusout', function() {
2753
+ $(this).find(".mejs-captions-selector").css("visibility","hidden");
2754
+ });
2755
+
2378
2756
  }
2379
2757
 
2380
2758
  if (!player.options.alwaysShowControls) {
@@ -2399,8 +2777,6 @@ if (typeof jQuery != 'undefined') {
2399
2777
  player.selectedTrack = null;
2400
2778
  player.isLoadingTrack = false;
2401
2779
 
2402
-
2403
-
2404
2780
  // add to list
2405
2781
  for (i=0; i<player.tracks.length; i++) {
2406
2782
  if (player.tracks[i].kind == 'subtitles') {
@@ -2411,18 +2787,17 @@ if (typeof jQuery != 'undefined') {
2411
2787
  // start loading tracks
2412
2788
  player.loadNextTrack();
2413
2789
 
2414
-
2415
2790
  media.addEventListener('timeupdate',function(e) {
2416
2791
  player.displayCaptions();
2417
2792
  }, false);
2418
-
2419
- if (player.options.slidesSelector != '') {
2793
+
2794
+ if (player.options.slidesSelector !== '') {
2420
2795
  player.slidesContainer = $(player.options.slidesSelector);
2421
2796
 
2422
2797
  media.addEventListener('timeupdate',function(e) {
2423
- player.displaySlides();
2798
+ player.displaySlides();
2424
2799
  }, false);
2425
-
2800
+
2426
2801
  }
2427
2802
 
2428
2803
  media.addEventListener('loadedmetadata', function(e) {
@@ -2445,26 +2820,26 @@ if (typeof jQuery != 'undefined') {
2445
2820
  });
2446
2821
  }
2447
2822
  });
2448
-
2823
+
2449
2824
  // check for autoplay
2450
2825
  if (player.node.getAttribute('autoplay') !== null) {
2451
2826
  player.chapters.css('visibility','hidden');
2452
2827
  }
2453
2828
  },
2454
-
2829
+
2455
2830
  setTrack: function(lang){
2456
-
2831
+
2457
2832
  var t = this,
2458
2833
  i;
2459
-
2834
+
2460
2835
  if (lang == 'none') {
2461
2836
  t.selectedTrack = null;
2462
2837
  t.captionsButton.removeClass('mejs-captions-enabled');
2463
2838
  } else {
2464
2839
  for (i=0; i<t.tracks.length; i++) {
2465
2840
  if (t.tracks[i].srclang == lang) {
2466
- if (t.selectedTrack == null)
2467
- t.captionsButton.addClass('mejs-captions-enabled');
2841
+ if (t.selectedTrack === null)
2842
+ t.captionsButton.addClass('mejs-captions-enabled');
2468
2843
  t.selectedTrack = t.tracks[i];
2469
2844
  t.captions.attr('lang', t.selectedTrack.srclang);
2470
2845
  t.displayCaptions();
@@ -2484,8 +2859,8 @@ if (typeof jQuery != 'undefined') {
2484
2859
  } else {
2485
2860
  // add done?
2486
2861
  t.isLoadingTrack = false;
2487
-
2488
- t.checkForTracks();
2862
+
2863
+ t.checkForTracks();
2489
2864
  }
2490
2865
  },
2491
2866
 
@@ -2513,11 +2888,11 @@ if (typeof jQuery != 'undefined') {
2513
2888
 
2514
2889
  // parse the loaded file
2515
2890
  if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
2516
- track.entries = mejs.TrackFormatParser.dfxp.parse(d);
2517
- } else {
2518
- track.entries = mejs.TrackFormatParser.webvvt.parse(d);
2891
+ track.entries = mejs.TrackFormatParser.dfxp.parse(d);
2892
+ } else {
2893
+ track.entries = mejs.TrackFormatParser.webvtt.parse(d);
2519
2894
  }
2520
-
2895
+
2521
2896
  after();
2522
2897
 
2523
2898
  if (track.kind == 'chapters') {
@@ -2527,10 +2902,10 @@ if (typeof jQuery != 'undefined') {
2527
2902
  }
2528
2903
  }, false);
2529
2904
  }
2530
-
2905
+
2531
2906
  if (track.kind == 'slides') {
2532
2907
  t.setupSlides(track);
2533
- }
2908
+ }
2534
2909
  },
2535
2910
  error: function() {
2536
2911
  t.loadNextTrack();
@@ -2540,10 +2915,10 @@ if (typeof jQuery != 'undefined') {
2540
2915
 
2541
2916
  enableTrackButton: function(lang, label) {
2542
2917
  var t = this;
2543
-
2918
+
2544
2919
  if (label === '') {
2545
2920
  label = mejs.language.codes[lang] || lang;
2546
- }
2921
+ }
2547
2922
 
2548
2923
  t.captionsButton
2549
2924
  .find('input[value=' + lang + ']')
@@ -2553,7 +2928,7 @@ if (typeof jQuery != 'undefined') {
2553
2928
 
2554
2929
  // auto select
2555
2930
  if (t.options.startLanguage == lang) {
2556
- $('#' + t.id + '_captions_' + lang).click();
2931
+ $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
2557
2932
  }
2558
2933
 
2559
2934
  t.adjustLanguageBox();
@@ -2586,12 +2961,12 @@ if (typeof jQuery != 'undefined') {
2586
2961
  t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
2587
2962
  );
2588
2963
  },
2589
-
2964
+
2590
2965
  checkForTracks: function() {
2591
2966
  var
2592
2967
  t = this,
2593
2968
  hasSubtitles = false;
2594
-
2969
+
2595
2970
  // check if any subtitles
2596
2971
  if (t.options.hideCaptionsButtonWhenEmpty) {
2597
2972
  for (i=0; i<t.tracks.length; i++) {
@@ -2599,13 +2974,13 @@ if (typeof jQuery != 'undefined') {
2599
2974
  hasSubtitles = true;
2600
2975
  break;
2601
2976
  }
2602
- }
2603
-
2977
+ }
2978
+
2604
2979
  if (!hasSubtitles) {
2605
2980
  t.captionsButton.hide();
2606
2981
  t.setControlsSize();
2607
- }
2608
- }
2982
+ }
2983
+ }
2609
2984
  },
2610
2985
 
2611
2986
  displayCaptions: function() {
@@ -2618,10 +2993,11 @@ if (typeof jQuery != 'undefined') {
2618
2993
  i,
2619
2994
  track = t.selectedTrack;
2620
2995
 
2621
- if (track != null && track.isLoaded) {
2996
+ if (track !== null && track.isLoaded) {
2622
2997
  for (i=0; i<track.entries.times.length; i++) {
2623
- if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
2624
- t.captionsText.html(track.entries.text[i]);
2998
+ if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
2999
+ // Set the line before the timecode as a class so the cue can be targeted if needed
3000
+ t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
2625
3001
  t.captions.show().height(0);
2626
3002
  return; // exit out if one is visible;
2627
3003
  }
@@ -2631,25 +3007,25 @@ if (typeof jQuery != 'undefined') {
2631
3007
  t.captions.hide();
2632
3008
  }
2633
3009
  },
2634
-
3010
+
2635
3011
  setupSlides: function(track) {
2636
3012
  var t = this;
2637
-
3013
+
2638
3014
  t.slides = track;
2639
3015
  t.slides.entries.imgs = [t.slides.entries.text.length];
2640
3016
  t.showSlide(0);
2641
-
3017
+
2642
3018
  },
2643
-
3019
+
2644
3020
  showSlide: function(index) {
2645
3021
  if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
2646
- return;
3022
+ return;
2647
3023
  }
2648
-
3024
+
2649
3025
  var t = this,
2650
3026
  url = t.slides.entries.text[index],
2651
3027
  img = t.slides.entries.imgs[index];
2652
-
3028
+
2653
3029
  if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
2654
3030
 
2655
3031
  t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
@@ -2658,46 +3034,46 @@ if (typeof jQuery != 'undefined') {
2658
3034
  .hide()
2659
3035
  .fadeIn()
2660
3036
  .siblings(':visible')
2661
- .fadeOut();
2662
-
3037
+ .fadeOut();
3038
+
2663
3039
  });
2664
-
3040
+
2665
3041
  } else {
2666
-
3042
+
2667
3043
  if (!img.is(':visible') && !img.is(':animated')) {
2668
-
2669
- //
2670
-
3044
+
3045
+ //
3046
+
2671
3047
  img.fadeIn()
2672
3048
  .siblings(':visible')
2673
- .fadeOut();
3049
+ .fadeOut();
2674
3050
  }
2675
3051
  }
2676
-
3052
+
2677
3053
  },
2678
-
3054
+
2679
3055
  displaySlides: function() {
2680
-
3056
+
2681
3057
  if (typeof this.slides == 'undefined')
2682
- return;
2683
-
2684
- var
3058
+ return;
3059
+
3060
+ var
2685
3061
  t = this,
2686
3062
  slides = t.slides,
2687
- i;
2688
-
3063
+ i;
3064
+
2689
3065
  for (i=0; i<slides.entries.times.length; i++) {
2690
3066
  if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
2691
-
3067
+
2692
3068
  t.showSlide(i);
2693
-
3069
+
2694
3070
  return; // exit out if one is visible;
2695
3071
  }
2696
3072
  }
2697
3073
  },
2698
3074
 
2699
3075
  displayChapters: function() {
2700
- var
3076
+ var
2701
3077
  t = this,
2702
3078
  i;
2703
3079
 
@@ -2711,7 +3087,7 @@ if (typeof jQuery != 'undefined') {
2711
3087
  },
2712
3088
 
2713
3089
  drawChapters: function(chapters) {
2714
- var
3090
+ var
2715
3091
  t = this,
2716
3092
  i,
2717
3093
  dur,
@@ -2737,10 +3113,10 @@ if (typeof jQuery != 'undefined') {
2737
3113
  //}
2738
3114
 
2739
3115
  t.chapters.append( $(
2740
- '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
2741
- '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
2742
- '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
2743
- '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
3116
+ '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
3117
+ '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
3118
+ '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
3119
+ '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
2744
3120
  '</div>' +
2745
3121
  '</div>'));
2746
3122
  usedPercent += percent;
@@ -2749,7 +3125,7 @@ if (typeof jQuery != 'undefined') {
2749
3125
  t.chapters.find('div.mejs-chapter').click(function() {
2750
3126
  t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
2751
3127
  if (t.media.paused) {
2752
- t.media.play();
3128
+ t.media.play();
2753
3129
  }
2754
3130
  });
2755
3131
 
@@ -2776,7 +3152,7 @@ if (typeof jQuery != 'undefined') {
2776
3152
  nl:'Dutch',
2777
3153
  en:'English',
2778
3154
  et:'Estonian',
2779
- tl:'Filipino',
3155
+ fl:'Filipino',
2780
3156
  fi:'Finnish',
2781
3157
  fr:'French',
2782
3158
  gl:'Galician',
@@ -2801,7 +3177,7 @@ if (typeof jQuery != 'undefined') {
2801
3177
  fa:'Persian',
2802
3178
  pl:'Polish',
2803
3179
  pt:'Portuguese',
2804
- //'pt-pt':'Portuguese (Portugal)',
3180
+ // 'pt-pt':'Portuguese (Portugal)',
2805
3181
  ro:'Romanian',
2806
3182
  ru:'Russian',
2807
3183
  sr:'Serbian',
@@ -2821,10 +3197,10 @@ if (typeof jQuery != 'undefined') {
2821
3197
  };
2822
3198
 
2823
3199
  /*
2824
- Parses WebVVT format which should be formatted as
3200
+ Parses WebVTT format which should be formatted as
2825
3201
  ================================
2826
3202
  WEBVTT
2827
-
3203
+
2828
3204
  1
2829
3205
  00:00:01,1 --> 00:00:05,000
2830
3206
  A line of text
@@ -2832,51 +3208,50 @@ if (typeof jQuery != 'undefined') {
2832
3208
  2
2833
3209
  00:01:15,1 --> 00:02:05,000
2834
3210
  A second line of text
2835
-
3211
+
2836
3212
  ===============================
2837
3213
 
2838
3214
  Adapted from: http://www.delphiki.com/html5/playr
2839
3215
  */
2840
3216
  mejs.TrackFormatParser = {
2841
- webvvt: {
2842
- // match start "chapter-" (or anythingelse)
2843
- pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/,
2844
- pattern_timecode: /^([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
3217
+ webvtt: {
3218
+ pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
2845
3219
 
2846
3220
  parse: function(trackText) {
2847
- var
3221
+ var
2848
3222
  i = 0,
2849
3223
  lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
2850
3224
  entries = {text:[], times:[]},
2851
3225
  timecode,
2852
- text;
3226
+ text,
3227
+ identifier;
2853
3228
  for(; i<lines.length; i++) {
2854
- // check for the line number
2855
- if (this.pattern_identifier.exec(lines[i])){
2856
- // skip to the next line where the start --> end time code should be
2857
- i++;
2858
- timecode = this.pattern_timecode.exec(lines[i]);
3229
+ timecode = this.pattern_timecode.exec(lines[i]);
2859
3230
 
2860
- if (timecode && i<lines.length){
2861
- i++;
2862
- // grab all the (possibly multi-line) text that follows
2863
- text = lines[i];
3231
+ if (timecode && i<lines.length) {
3232
+ if ((i - 1) >= 0 && lines[i - 1] !== '') {
3233
+ identifier = lines[i - 1];
3234
+ }
3235
+ i++;
3236
+ // grab all the (possibly multi-line) text that follows
3237
+ text = lines[i];
3238
+ i++;
3239
+ while(lines[i] !== '' && i<lines.length){
3240
+ text = text + '\n' + lines[i];
2864
3241
  i++;
2865
- while(lines[i] !== '' && i<lines.length){
2866
- text = text + '\n' + lines[i];
2867
- i++;
2868
- }
2869
- text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
2870
- // Text is in a different array so I can use .join
2871
- entries.text.push(text);
2872
- entries.times.push(
2873
- {
2874
- start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) == 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
2875
- stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
2876
- settings: timecode[5]
2877
- });
2878
3242
  }
3243
+ text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
3244
+ // Text is in a different array so I can use .join
3245
+ entries.text.push(text);
3246
+ entries.times.push(
3247
+ {
3248
+ identifier: identifier,
3249
+ start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
3250
+ stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
3251
+ settings: timecode[5]
3252
+ });
2879
3253
  }
3254
+ identifier = '';
2880
3255
  }
2881
3256
  return entries;
2882
3257
  }
@@ -2885,7 +3260,7 @@ if (typeof jQuery != 'undefined') {
2885
3260
  dfxp: {
2886
3261
  parse: function(trackText) {
2887
3262
  trackText = $(trackText).filter("tt");
2888
- var
3263
+ var
2889
3264
  i = 0,
2890
3265
  container = trackText.children("div").eq(0),
2891
3266
  lines = container.find("p"),
@@ -2921,15 +3296,15 @@ if (typeof jQuery != 'undefined') {
2921
3296
  if (styles) {
2922
3297
  style = "";
2923
3298
  for (var _style in styles) {
2924
- style += _style + ":" + styles[_style] + ";";
3299
+ style += _style + ":" + styles[_style] + ";";
2925
3300
  }
2926
3301
  }
2927
3302
  if (style) _temp_times.style = style;
2928
- if (_temp_times.start == 0) _temp_times.start = 0.200;
3303
+ if (_temp_times.start === 0) _temp_times.start = 0.200;
2929
3304
  entries.times.push(_temp_times);
2930
3305
  text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
2931
3306
  entries.text.push(text);
2932
- if (entries.times.start == 0) entries.times.start = 2;
3307
+ if (entries.times.start === 0) entries.times.start = 2;
2933
3308
  }
2934
3309
  return entries;
2935
3310
  }
@@ -2940,13 +3315,13 @@ if (typeof jQuery != 'undefined') {
2940
3315
  return text.split(regex);
2941
3316
  }
2942
3317
  };
2943
-
3318
+
2944
3319
  // test for browsers with bad String.split method.
2945
3320
  if ('x\n\ny'.split(/\n/gi).length != 3) {
2946
3321
  // add super slow IE8 and below version
2947
3322
  mejs.TrackFormatParser.split2 = function(text, regex) {
2948
- var
2949
- parts = [],
3323
+ var
3324
+ parts = [],
2950
3325
  chunk = '',
2951
3326
  i;
2952
3327
 
@@ -2959,8 +3334,8 @@ if (typeof jQuery != 'undefined') {
2959
3334
  }
2960
3335
  parts.push(chunk);
2961
3336
  return parts;
2962
- }
2963
- }
3337
+ };
3338
+ }
2964
3339
 
2965
3340
  })(mejs.$);
2966
3341
 
@@ -3195,4 +3570,4 @@ $.extend(mejs.MepDefaults,
3195
3570
  }
3196
3571
  });
3197
3572
 
3198
- })(mejs.$);
3573
+ })(mejs.$);