mediaelement_rails 0.8.0 → 0.8.1

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