videojs_rails 4.6.4 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80a4f30faab8c97afa8b8b44242e568a3139cbca
4
- data.tar.gz: 56b077ebc2e96e658bd682030c5350ac8d1c0573
3
+ metadata.gz: c91579dfef47310e8313775c8ce6e3802316052f
4
+ data.tar.gz: b6a514289bfd81adff7796b8d6cf352a62bd2dda
5
5
  SHA512:
6
- metadata.gz: 8d4aac4ce74e012b2fd30731a9378805e04472a02d95a49cb3b3909ea46ce5c08148131a19a0647664672903be1628b9c2ee1263f72b13672818ccd35c92679a
7
- data.tar.gz: 2274ede9d80bfdc4f1d198078d05c0ec51e8f62dad233e047f6cef91d69ada4974707c38bfaaea2de8216e49f911a46f47e55fbb4a2fa65a09e80226b316113e
6
+ metadata.gz: 7edd09f2fe6c10c64475e83a16f65f22349f3b11ddecd213ae23b26e83b0b68f6eb36dfa19925da0a1c3478869c6efd35138a760bc711731aa589e5f828a4281
7
+ data.tar.gz: 340981a79f646701e48ea8a986559ee7c8e6e2a6c102da3cb1bcaf9eeb843c2eebc1f79fde1dc2445d8a2837611b9ad22250b08d093e849cc57e81094c0e9d6e
@@ -1,3 +1,3 @@
1
1
  module VideojsRails
2
- VERSION = '4.6.4'
2
+ VERSION = '4.7.0'
3
3
  end
@@ -60,11 +60,10 @@ var vjs = function(id, options, ready){
60
60
  };
61
61
 
62
62
  // Extended name, also available externally, window.videojs
63
- var videojs = vjs;
64
- window.videojs = window.vjs = vjs;
63
+ var videojs = window['videojs'] = vjs;
65
64
 
66
65
  // CDN Version. Used to target right flash swf.
67
- vjs.CDN_VERSION = '4.6';
66
+ vjs.CDN_VERSION = '4.7';
68
67
  vjs.ACCESS_PROTOCOL = ('https:' == document.location.protocol ? 'https://' : 'http://');
69
68
 
70
69
  /**
@@ -104,12 +103,17 @@ vjs.options = {
104
103
  'errorDisplay': {}
105
104
  },
106
105
 
106
+ 'language': document.getElementsByTagName('html')[0].getAttribute('lang') || navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language || 'en',
107
+
108
+ // locales and their language translations
109
+ 'languages': {},
110
+
107
111
  // Default message to show when a video cannot be played.
108
112
  'notSupportedMessage': 'No compatible source was found for this video.'
109
113
  };
110
114
 
111
115
  // Set CDN Version of swf
112
- // The added (+) blocks the replace from changing this 4.6 string
116
+ // The added (+) blocks the replace from changing this 4.7 string
113
117
  if (vjs.CDN_VERSION !== 'GENERATED'+'_CDN_VSN') {
114
118
  videojs.options['flash']['swf'] = "<%= asset_path('video-js.swf') %>";
115
119
  }
@@ -276,11 +280,15 @@ vjs.CoreObject.create = function(){
276
280
  * and adds a generic handler to the element's event,
277
281
  * along with a unique id (guid) to the element.
278
282
  * @param {Element|Object} elem Element or object to bind listeners to
279
- * @param {String} type Type of event to bind to.
283
+ * @param {String|Array} type Type of event to bind to.
280
284
  * @param {Function} fn Event listener.
281
285
  * @private
282
286
  */
283
287
  vjs.on = function(elem, type, fn){
288
+ if (vjs.obj.isArray(type)) {
289
+ return _handleMultipleEvents(vjs.on, elem, type, fn);
290
+ }
291
+
284
292
  var data = vjs.getData(elem);
285
293
 
286
294
  // We need a place to store all our handler data
@@ -318,9 +326,9 @@ vjs.on = function(elem, type, fn){
318
326
  }
319
327
 
320
328
  if (data.handlers[type].length == 1) {
321
- if (document.addEventListener) {
329
+ if (elem.addEventListener) {
322
330
  elem.addEventListener(type, data.dispatcher, false);
323
- } else if (document.attachEvent) {
331
+ } else if (elem.attachEvent) {
324
332
  elem.attachEvent('on' + type, data.dispatcher);
325
333
  }
326
334
  }
@@ -329,7 +337,7 @@ vjs.on = function(elem, type, fn){
329
337
  /**
330
338
  * Removes event listeners from an element
331
339
  * @param {Element|Object} elem Object to remove listeners from
332
- * @param {String=} type Type of listener to remove. Don't include to remove all events from element.
340
+ * @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element.
333
341
  * @param {Function} fn Specific listener to remove. Don't incldue to remove listeners for an event type.
334
342
  * @private
335
343
  */
@@ -342,6 +350,10 @@ vjs.off = function(elem, type, fn) {
342
350
  // If no events exist, nothing to unbind
343
351
  if (!data.handlers) { return; }
344
352
 
353
+ if (vjs.obj.isArray(type)) {
354
+ return _handleMultipleEvents(vjs.off, elem, type, fn);
355
+ }
356
+
345
357
  // Utility function
346
358
  var removeType = function(t){
347
359
  data.handlers[t] = [];
@@ -393,9 +405,9 @@ vjs.cleanUpEvents = function(elem, type) {
393
405
  // Setting to null was causing an error with data.handlers
394
406
 
395
407
  // Remove the meta-handler from the element
396
- if (document.removeEventListener) {
408
+ if (elem.removeEventListener) {
397
409
  elem.removeEventListener(type, data.dispatcher, false);
398
- } else if (document.detachEvent) {
410
+ } else if (elem.detachEvent) {
399
411
  elem.detachEvent('on' + type, data.dispatcher);
400
412
  }
401
413
  }
@@ -529,8 +541,8 @@ vjs.fixEvent = function(event) {
529
541
 
530
542
  /**
531
543
  * Trigger an event for an element
532
- * @param {Element|Object} elem Element to trigger an event on
533
- * @param {String} event Type of event to trigger
544
+ * @param {Element|Object} elem Element to trigger an event on
545
+ * @param {Event|Object|String} event A string (the type) or an event object with a type attribute
534
546
  * @private
535
547
  */
536
548
  vjs.trigger = function(elem, event) {
@@ -602,18 +614,36 @@ vjs.trigger = function(elem, event) {
602
614
  /**
603
615
  * Trigger a listener only once for an event
604
616
  * @param {Element|Object} elem Element or object to
605
- * @param {String} type
617
+ * @param {String|Array} type
606
618
  * @param {Function} fn
607
619
  * @private
608
620
  */
609
621
  vjs.one = function(elem, type, fn) {
622
+ if (vjs.obj.isArray(type)) {
623
+ return _handleMultipleEvents(vjs.one, elem, type, fn);
624
+ }
610
625
  var func = function(){
611
626
  vjs.off(elem, type, func);
612
627
  fn.apply(this, arguments);
613
628
  };
629
+ // copy the guid to the new function so it can removed using the original function's ID
614
630
  func.guid = fn.guid = fn.guid || vjs.guid++;
615
631
  vjs.on(elem, type, func);
616
632
  };
633
+
634
+ /**
635
+ * Loops through an array of event types and calls the requested method for each type.
636
+ * @param {Function} fn The event method we want to use.
637
+ * @param {Element|Object} elem Element or object to bind listeners to
638
+ * @param {String} type Type of event to bind to.
639
+ * @param {Function} callback Event listener.
640
+ * @private
641
+ */
642
+ function _handleMultipleEvents(fn, elem, type, callback) {
643
+ vjs.arr.forEach(type, function(type) {
644
+ fn(elem, type, callback); //Call the event method for each one of the types
645
+ });
646
+ }
617
647
  var hasOwnProp = Object.prototype.hasOwnProperty;
618
648
 
619
649
  /**
@@ -624,29 +654,29 @@ var hasOwnProp = Object.prototype.hasOwnProperty;
624
654
  * @private
625
655
  */
626
656
  vjs.createEl = function(tagName, properties){
627
- var el, propName;
628
-
629
- el = document.createElement(tagName || 'div');
630
-
631
- for (propName in properties){
632
- if (hasOwnProp.call(properties, propName)) {
633
- //el[propName] = properties[propName];
634
- // Not remembering why we were checking for dash
635
- // but using setAttribute means you have to use getAttribute
636
-
637
- // The check for dash checks for the aria-* attributes, like aria-label, aria-valuemin.
638
- // The additional check for "role" is because the default method for adding attributes does not
639
- // add the attribute "role". My guess is because it's not a valid attribute in some namespaces, although
640
- // browsers handle the attribute just fine. The W3C allows for aria-* attributes to be used in pre-HTML5 docs.
641
- // http://www.w3.org/TR/wai-aria-primer/#ariahtml. Using setAttribute gets around this problem.
642
-
643
- if (propName.indexOf('aria-') !== -1 || propName=='role') {
644
- el.setAttribute(propName, properties[propName]);
645
- } else {
646
- el[propName] = properties[propName];
647
- }
657
+ var el;
658
+
659
+ tagName = tagName || 'div';
660
+ properties = properties || {};
661
+
662
+ el = document.createElement(tagName);
663
+
664
+ vjs.obj.each(properties, function(propName, val){
665
+ // Not remembering why we were checking for dash
666
+ // but using setAttribute means you have to use getAttribute
667
+
668
+ // The check for dash checks for the aria-* attributes, like aria-label, aria-valuemin.
669
+ // The additional check for "role" is because the default method for adding attributes does not
670
+ // add the attribute "role". My guess is because it's not a valid attribute in some namespaces, although
671
+ // browsers handle the attribute just fine. The W3C allows for aria-* attributes to be used in pre-HTML5 docs.
672
+ // http://www.w3.org/TR/wai-aria-primer/#ariahtml. Using setAttribute gets around this problem.
673
+ if (propName.indexOf('aria-') !== -1 || propName == 'role') {
674
+ el.setAttribute(propName, val);
675
+ } else {
676
+ el[propName] = val;
648
677
  }
649
- }
678
+ });
679
+
650
680
  return el;
651
681
  };
652
682
 
@@ -776,6 +806,17 @@ vjs.obj.isPlain = function(obj){
776
806
  && obj.constructor === Object;
777
807
  };
778
808
 
809
+ /**
810
+ * Check if an object is Array
811
+ * Since instanceof Array will not work on arrays created in another frame we need to use Array.isArray, but since IE8 does not support Array.isArray we need this shim
812
+ * @param {Object} obj Object to check
813
+ * @return {Boolean} True if plain, false otherwise
814
+ * @private
815
+ */
816
+ vjs.obj.isArray = Array.isArray || function(arr) {
817
+ return Object.prototype.toString.call(arr) === '[object Array]';
818
+ };
819
+
779
820
  /**
780
821
  * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
781
822
  It also stores a unique id on the function so it can be easily removed from events
@@ -996,6 +1037,22 @@ vjs.IS_CHROME = (/Chrome/i).test(vjs.USER_AGENT);
996
1037
 
997
1038
  vjs.TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
998
1039
 
1040
+ /**
1041
+ * Apply attributes to an HTML element.
1042
+ * @param {Element} el Target element.
1043
+ * @param {Object=} attributes Element attributes to be applied.
1044
+ * @private
1045
+ */
1046
+ vjs.setElementAttributes = function(el, attributes){
1047
+ vjs.obj.each(attributes, function(attrName, attrValue) {
1048
+ if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
1049
+ el.removeAttribute(attrName);
1050
+ } else {
1051
+ el.setAttribute(attrName, (attrValue === true ? '' : attrValue));
1052
+ }
1053
+ });
1054
+ };
1055
+
999
1056
  /**
1000
1057
  * Get an element's attribute values, as defined on the HTML tag
1001
1058
  * Attributs are not the same as properties. They're defined on the tag
@@ -1005,7 +1062,7 @@ vjs.TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && doc
1005
1062
  * @return {Object}
1006
1063
  * @private
1007
1064
  */
1008
- vjs.getAttributeValues = function(tag){
1065
+ vjs.getElementAttributes = function(tag){
1009
1066
  var obj, knownBooleans, attrs, attrName, attrVal;
1010
1067
 
1011
1068
  obj = {};
@@ -1448,6 +1505,31 @@ vjs.findPosition = function(el) {
1448
1505
  top: vjs.round(top)
1449
1506
  };
1450
1507
  };
1508
+
1509
+ /**
1510
+ * Array functions container
1511
+ * @type {Object}
1512
+ * @private
1513
+ */
1514
+ vjs.arr = {};
1515
+
1516
+ /*
1517
+ * Loops through an array and runs a function for each item inside it.
1518
+ * @param {Array} array The array
1519
+ * @param {Function} callback The function to be run for each item
1520
+ * @param {*} thisArg The `this` binding of callback
1521
+ * @returns {Array} The array
1522
+ * @private
1523
+ */
1524
+ vjs.arr.forEach = function(array, callback, thisArg) {
1525
+ if (vjs.obj.isArray(array) && callback instanceof Function) {
1526
+ for (var i = 0, len = array.length; i < len; ++i) {
1527
+ callback.call(thisArg || vjs, array[i], i, array);
1528
+ }
1529
+ }
1530
+
1531
+ return array;
1532
+ };
1451
1533
  /**
1452
1534
  * Utility functions namespace
1453
1535
  * @namespace
@@ -1456,10 +1538,9 @@ vjs.findPosition = function(el) {
1456
1538
  vjs.util = {};
1457
1539
 
1458
1540
  /**
1459
- * Merge two options objects,
1460
- * recursively merging any plain object properties as well.
1461
- * Previously `deepMerge`
1462
- *
1541
+ * Merge two options objects, recursively merging any plain object properties as
1542
+ * well. Previously `deepMerge`
1543
+ *
1463
1544
  * @param {Object} obj1 Object to override values in
1464
1545
  * @param {Object} obj2 Overriding object
1465
1546
  * @return {Object} New object -- obj1 and obj2 will be untouched
@@ -1685,6 +1766,15 @@ vjs.Component.prototype.createEl = function(tagName, attributes){
1685
1766
  return vjs.createEl(tagName, attributes);
1686
1767
  };
1687
1768
 
1769
+ vjs.Component.prototype.localize = function(string){
1770
+ var lang = this.player_.language(),
1771
+ languages = this.player_.languages();
1772
+ if (languages && languages[lang] && languages[lang][string]) {
1773
+ return languages[lang][string];
1774
+ }
1775
+ return string;
1776
+ };
1777
+
1688
1778
  /**
1689
1779
  * Get the component's DOM element
1690
1780
  *
@@ -1961,7 +2051,7 @@ vjs.Component.prototype.initChildren = function(){
1961
2051
 
1962
2052
  if (children) {
1963
2053
  // Allow for an array of children details to passed in the options
1964
- if (children instanceof Array) {
2054
+ if (vjs.obj.isArray(children)) {
1965
2055
  for (var i = 0; i < children.length; i++) {
1966
2056
  child = children[i];
1967
2057
 
@@ -2053,13 +2143,13 @@ vjs.Component.prototype.one = function(type, fn) {
2053
2143
  * Trigger an event on an element
2054
2144
  *
2055
2145
  * myComponent.trigger('eventName');
2146
+ * myComponent.trigger({'type':'eventName'});
2056
2147
  *
2057
- * @param {String} type The event type to trigger, e.g. 'click'
2058
- * @param {Event|Object} event The event object to be passed to the listener
2059
- * @return {vjs.Component} self
2148
+ * @param {Event|Object|String} event A string (the type) or an event object with a type attribute
2149
+ * @return {vjs.Component} self
2060
2150
  */
2061
- vjs.Component.prototype.trigger = function(type, event){
2062
- vjs.trigger(this.el_, type, event);
2151
+ vjs.Component.prototype.trigger = function(event){
2152
+ vjs.trigger(this.el_, event);
2063
2153
  return this;
2064
2154
  };
2065
2155
 
@@ -2515,7 +2605,7 @@ vjs.Button.prototype.createEl = function(type, props){
2515
2605
 
2516
2606
  this.controlText_ = vjs.createEl('span', {
2517
2607
  className: 'vjs-control-text',
2518
- innerHTML: this.buttonText || 'Need Text'
2608
+ innerHTML: this.localize(this.buttonText) || 'Need Text'
2519
2609
  });
2520
2610
 
2521
2611
  this.contentEl_.appendChild(this.controlText_);
@@ -2580,6 +2670,10 @@ vjs.Slider = vjs.Component.extend({
2580
2670
  player.on(this.playerEvent, vjs.bind(this, this.update));
2581
2671
 
2582
2672
  this.boundEvents = {};
2673
+
2674
+
2675
+ this.boundEvents.move = vjs.bind(this, this.onMouseMove);
2676
+ this.boundEvents.end = vjs.bind(this, this.onMouseUp);
2583
2677
  }
2584
2678
  });
2585
2679
 
@@ -2601,9 +2695,7 @@ vjs.Slider.prototype.createEl = function(type, props) {
2601
2695
  vjs.Slider.prototype.onMouseDown = function(event){
2602
2696
  event.preventDefault();
2603
2697
  vjs.blockTextSelection();
2604
-
2605
- this.boundEvents.move = vjs.bind(this, this.onMouseMove);
2606
- this.boundEvents.end = vjs.bind(this, this.onMouseUp);
2698
+ this.addClass('vjs-sliding');
2607
2699
 
2608
2700
  vjs.on(document, 'mousemove', this.boundEvents.move);
2609
2701
  vjs.on(document, 'mouseup', this.boundEvents.end);
@@ -2613,8 +2705,13 @@ vjs.Slider.prototype.onMouseDown = function(event){
2613
2705
  this.onMouseMove(event);
2614
2706
  };
2615
2707
 
2708
+ // To be overridden by a subclass
2709
+ vjs.Slider.prototype.onMouseMove = function(){};
2710
+
2616
2711
  vjs.Slider.prototype.onMouseUp = function() {
2617
2712
  vjs.unblockTextSelection();
2713
+ this.removeClass('vjs-sliding');
2714
+
2618
2715
  vjs.off(document, 'mousemove', this.boundEvents.move, false);
2619
2716
  vjs.off(document, 'mouseup', this.boundEvents.end, false);
2620
2717
  vjs.off(document, 'touchmove', this.boundEvents.move, false);
@@ -2670,7 +2767,9 @@ vjs.Slider.prototype.update = function(){
2670
2767
  }
2671
2768
 
2672
2769
  // Set the new bar width
2673
- bar.el().style.width = vjs.round(barProgress * 100, 2) + '%';
2770
+ if (bar) {
2771
+ bar.el().style.width = vjs.round(barProgress * 100, 2) + '%';
2772
+ }
2674
2773
  };
2675
2774
 
2676
2775
  vjs.Slider.prototype.calculateDistance = function(event){
@@ -2681,7 +2780,7 @@ vjs.Slider.prototype.calculateDistance = function(event){
2681
2780
  boxW = boxH = el.offsetWidth;
2682
2781
  handle = this.handle;
2683
2782
 
2684
- if (this.options_.vertical) {
2783
+ if (this.options()['vertical']) {
2685
2784
  boxY = box.top;
2686
2785
 
2687
2786
  if (event.changedTouches) {
@@ -2727,10 +2826,10 @@ vjs.Slider.prototype.onFocus = function(){
2727
2826
  };
2728
2827
 
2729
2828
  vjs.Slider.prototype.onKeyPress = function(event){
2730
- if (event.which == 37) { // Left Arrow
2829
+ if (event.which == 37 || event.which == 40) { // Left and Down Arrows
2731
2830
  event.preventDefault();
2732
2831
  this.stepBack();
2733
- } else if (event.which == 39) { // Right Arrow
2832
+ } else if (event.which == 38 || event.which == 39) { // Up and Right Arrows
2734
2833
  event.preventDefault();
2735
2834
  this.stepForward();
2736
2835
  }
@@ -3152,7 +3251,7 @@ for (var errNum = 0; errNum < vjs.MediaError.errorTypes.length; errNum++) {
3152
3251
  * var myPlayer = videojs('example_video_1');
3153
3252
  * ```
3154
3253
  *
3155
- * In the follwing example, the `data-setup` attribute tells the Video.js library to create a player instance when the library is ready.
3254
+ * In the following example, the `data-setup` attribute tells the Video.js library to create a player instance when the library is ready.
3156
3255
  *
3157
3256
  * ```html
3158
3257
  * <video id="example_video_1" data-setup='{}' controls>
@@ -3182,6 +3281,9 @@ vjs.Player = vjs.Component.extend({
3182
3281
  // Make sure tag ID exists
3183
3282
  tag.id = tag.id || 'vjs_video_' + vjs.guid++;
3184
3283
 
3284
+ // Store the tag attributes used to restore html5 element
3285
+ this.tagAttributes = tag && vjs.getElementAttributes(tag);
3286
+
3185
3287
  // Set Options
3186
3288
  // The options argument overrides options set in the video tag
3187
3289
  // which overrides globally set options.
@@ -3189,6 +3291,12 @@ vjs.Player = vjs.Component.extend({
3189
3291
  // (tag must exist before Player)
3190
3292
  options = vjs.obj.merge(this.getTagSettings(tag), options);
3191
3293
 
3294
+ // Update Current Language
3295
+ this.language_ = options['language'] || vjs.options['language'];
3296
+
3297
+ // Update Supported Languages
3298
+ this.languages_ = options['languages'] || vjs.options['languages'];
3299
+
3192
3300
  // Cache for video property values.
3193
3301
  this.cache_ = {};
3194
3302
 
@@ -3237,6 +3345,41 @@ vjs.Player = vjs.Component.extend({
3237
3345
  }
3238
3346
  });
3239
3347
 
3348
+ /**
3349
+ * The players's stored language code
3350
+ *
3351
+ * @type {String}
3352
+ * @private
3353
+ */
3354
+ vjs.Player.prototype.language_;
3355
+
3356
+ /**
3357
+ * The player's language code
3358
+ * @param {String} languageCode The locale string
3359
+ * @return {String} The locale string when getting
3360
+ * @return {vjs.Player} self, when setting
3361
+ */
3362
+ vjs.Player.prototype.language = function (languageCode) {
3363
+ if (languageCode === undefined) {
3364
+ return this.language_;
3365
+ }
3366
+
3367
+ this.language_ = languageCode;
3368
+ return this;
3369
+ };
3370
+
3371
+ /**
3372
+ * The players's stored language dictionary
3373
+ *
3374
+ * @type {Object}
3375
+ * @private
3376
+ */
3377
+ vjs.Player.prototype.languages_;
3378
+
3379
+ vjs.Player.prototype.languages = function(){
3380
+ return this.languages_;
3381
+ };
3382
+
3240
3383
  /**
3241
3384
  * Player instance options, surfaced using vjs.options
3242
3385
  * vjs.options = vjs.Player.prototype.options_
@@ -3282,7 +3425,7 @@ vjs.Player.prototype.getTagSettings = function(tag){
3282
3425
  'tracks': []
3283
3426
  };
3284
3427
 
3285
- vjs.obj.merge(options, vjs.getAttributeValues(tag));
3428
+ vjs.obj.merge(options, vjs.getElementAttributes(tag));
3286
3429
 
3287
3430
  // Get tag children settings
3288
3431
  if (tag.hasChildNodes()) {
@@ -3295,9 +3438,9 @@ vjs.Player.prototype.getTagSettings = function(tag){
3295
3438
  // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
3296
3439
  childName = child.nodeName.toLowerCase();
3297
3440
  if (childName === 'source') {
3298
- options['sources'].push(vjs.getAttributeValues(child));
3441
+ options['sources'].push(vjs.getElementAttributes(child));
3299
3442
  } else if (childName === 'track') {
3300
- options['tracks'].push(vjs.getAttributeValues(child));
3443
+ options['tracks'].push(vjs.getElementAttributes(child));
3301
3444
  }
3302
3445
  }
3303
3446
  }
@@ -3306,8 +3449,10 @@ vjs.Player.prototype.getTagSettings = function(tag){
3306
3449
  };
3307
3450
 
3308
3451
  vjs.Player.prototype.createEl = function(){
3309
- var el = this.el_ = vjs.Component.prototype.createEl.call(this, 'div');
3310
- var tag = this.tag;
3452
+ var
3453
+ el = this.el_ = vjs.Component.prototype.createEl.call(this, 'div'),
3454
+ tag = this.tag,
3455
+ attrs;
3311
3456
 
3312
3457
  // Remove width/height attrs from tag so CSS can make it 100% width/height
3313
3458
  tag.removeAttribute('width');
@@ -3336,10 +3481,12 @@ vjs.Player.prototype.createEl = function(){
3336
3481
  }
3337
3482
  }
3338
3483
 
3339
- // Give video tag ID and class to player div
3484
+ // Copy over all the attributes from the tag, including ID and class
3340
3485
  // ID will now reference player box, not the video tag
3341
- el.id = tag.id;
3342
- el.className = tag.className;
3486
+ attrs = vjs.getElementAttributes(tag);
3487
+ vjs.obj.each(attrs, function(attr) {
3488
+ el.setAttribute(attr, attrs[attr]);
3489
+ });
3343
3490
 
3344
3491
  // Update tag id/class for use as HTML5 playback tech
3345
3492
  // Might think we should do this after embedding in container so .vjs-tech class
@@ -3371,6 +3518,10 @@ vjs.Player.prototype.createEl = function(){
3371
3518
  // adding children
3372
3519
  this.el_ = el;
3373
3520
  this.on('loadstart', this.onLoadStart);
3521
+ this.on('waiting', this.onWaiting);
3522
+ this.on(['canplay', 'canplaythrough', 'playing', 'ended'], this.onWaitEnd);
3523
+ this.on('seeking', this.onSeeking);
3524
+ this.on('seeked', this.onSeeked);
3374
3525
  this.on('ended', this.onEnded);
3375
3526
  this.on('play', this.onPlay);
3376
3527
  this.on('firstplay', this.onFirstPlay);
@@ -3422,6 +3573,7 @@ vjs.Player.prototype.loadTech = function(techName, source){
3422
3573
  var techOptions = vjs.obj.merge({ 'source': source, 'parentEl': this.el_ }, this.options_[techName.toLowerCase()]);
3423
3574
 
3424
3575
  if (source) {
3576
+ this.currentType_ = source.type;
3425
3577
  if (source.src == this.cache_.src && this.cache_.currentTime > 0) {
3426
3578
  techOptions['startTime'] = this.cache_.currentTime;
3427
3579
  }
@@ -3437,13 +3589,14 @@ vjs.Player.prototype.loadTech = function(techName, source){
3437
3589
 
3438
3590
  vjs.Player.prototype.unloadTech = function(){
3439
3591
  this.isReady_ = false;
3440
- this.tech.dispose();
3441
3592
 
3442
3593
  // Turn off any manual progress or timeupdate tracking
3443
3594
  if (this.manualProgress) { this.manualProgressOff(); }
3444
3595
 
3445
3596
  if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); }
3446
3597
 
3598
+ this.tech.dispose();
3599
+
3447
3600
  this.tech = false;
3448
3601
  };
3449
3602
 
@@ -3496,13 +3649,17 @@ vjs.Player.prototype.trackProgress = function(){
3496
3649
 
3497
3650
  this.progressInterval = setInterval(vjs.bind(this, function(){
3498
3651
  // Don't trigger unless buffered amount is greater than last time
3499
- // log(this.cache_.bufferEnd, this.buffered().end(0), this.duration())
3500
- /* TODO: update for multiple buffered regions */
3501
- if (this.cache_.bufferEnd < this.buffered().end(0)) {
3652
+
3653
+ var bufferedPercent = this.bufferedPercent();
3654
+
3655
+ if (this.cache_.bufferedPercent != bufferedPercent) {
3502
3656
  this.trigger('progress');
3503
- } else if (this.bufferedPercent() == 1) {
3657
+ }
3658
+
3659
+ this.cache_.bufferedPercent = bufferedPercent;
3660
+
3661
+ if (bufferedPercent == 1) {
3504
3662
  this.stopTrackingProgress();
3505
- this.trigger('progress'); // Last update
3506
3663
  }
3507
3664
  }), 500);
3508
3665
  };
@@ -3619,8 +3776,40 @@ vjs.Player.prototype.onLoadedAllData;
3619
3776
  * @event play
3620
3777
  */
3621
3778
  vjs.Player.prototype.onPlay = function(){
3622
- vjs.removeClass(this.el_, 'vjs-paused');
3623
- vjs.addClass(this.el_, 'vjs-playing');
3779
+ this.removeClass('vjs-paused');
3780
+ this.addClass('vjs-playing');
3781
+ };
3782
+
3783
+ /**
3784
+ * Fired whenever the media begins wating
3785
+ * @event waiting
3786
+ */
3787
+ vjs.Player.prototype.onWaiting = function(){
3788
+ this.addClass('vjs-waiting');
3789
+ };
3790
+
3791
+ /**
3792
+ * A handler for events that signal that waiting has eneded
3793
+ * which is not consistent between browsers. See #1351
3794
+ */
3795
+ vjs.Player.prototype.onWaitEnd = function(){
3796
+ this.removeClass('vjs-waiting');
3797
+ };
3798
+
3799
+ /**
3800
+ * Fired whenever the player is jumping to a new time
3801
+ * @event seeking
3802
+ */
3803
+ vjs.Player.prototype.onSeeking = function(){
3804
+ this.addClass('vjs-seeking');
3805
+ };
3806
+
3807
+ /**
3808
+ * Fired when the player has finished jumping to a new time
3809
+ * @event seeked
3810
+ */
3811
+ vjs.Player.prototype.onSeeked = function(){
3812
+ this.removeClass('vjs-seeking');
3624
3813
  };
3625
3814
 
3626
3815
  /**
@@ -3647,8 +3836,8 @@ vjs.Player.prototype.onFirstPlay = function(){
3647
3836
  * @event pause
3648
3837
  */
3649
3838
  vjs.Player.prototype.onPause = function(){
3650
- vjs.removeClass(this.el_, 'vjs-playing');
3651
- vjs.addClass(this.el_, 'vjs-paused');
3839
+ this.removeClass('vjs-playing');
3840
+ this.addClass('vjs-paused');
3652
3841
  };
3653
3842
 
3654
3843
  /**
@@ -3889,7 +4078,6 @@ vjs.Player.prototype.remainingTime = function(){
3889
4078
  // http://dev.w3.org/html5/spec/video.html#dom-media-buffered
3890
4079
  // Buffered returns a timerange object.
3891
4080
  // Kind of like an array of portions of the video that have been downloaded.
3892
- // So far no browsers return more than one range (portion)
3893
4081
 
3894
4082
  /**
3895
4083
  * Get a TimeRange object with the times of the video that have been downloaded
@@ -3912,19 +4100,13 @@ vjs.Player.prototype.remainingTime = function(){
3912
4100
  * @return {Object} A mock TimeRange object (following HTML spec)
3913
4101
  */
3914
4102
  vjs.Player.prototype.buffered = function(){
3915
- var buffered = this.techGet('buffered'),
3916
- start = 0,
3917
- buflast = buffered.length - 1,
3918
- // Default end to 0 and store in values
3919
- end = this.cache_.bufferEnd = this.cache_.bufferEnd || 0;
4103
+ var buffered = this.techGet('buffered');
3920
4104
 
3921
- if (buffered && buflast >= 0 && buffered.end(buflast) !== end) {
3922
- end = buffered.end(buflast);
3923
- // Storing values allows them be overridden by setBufferedFromProgress
3924
- this.cache_.bufferEnd = end;
4105
+ if (!buffered || !buffered.length) {
4106
+ buffered = vjs.createTimeRange(0,0);
3925
4107
  }
3926
4108
 
3927
- return vjs.createTimeRange(start, end);
4109
+ return buffered;
3928
4110
  };
3929
4111
 
3930
4112
  /**
@@ -3938,7 +4120,46 @@ vjs.Player.prototype.buffered = function(){
3938
4120
  * @return {Number} A decimal between 0 and 1 representing the percent
3939
4121
  */
3940
4122
  vjs.Player.prototype.bufferedPercent = function(){
3941
- return (this.duration()) ? this.buffered().end(0) / this.duration() : 0;
4123
+ var duration = this.duration(),
4124
+ buffered = this.buffered(),
4125
+ bufferedDuration = 0,
4126
+ start, end;
4127
+
4128
+ if (!duration) {
4129
+ return 0;
4130
+ }
4131
+
4132
+ for (var i=0; i<buffered.length; i++){
4133
+ start = buffered.start(i);
4134
+ end = buffered.end(i);
4135
+
4136
+ // buffered end can be bigger than duration by a very small fraction
4137
+ if (end > duration) {
4138
+ end = duration;
4139
+ }
4140
+
4141
+ bufferedDuration += end - start;
4142
+ }
4143
+
4144
+ return bufferedDuration / duration;
4145
+ };
4146
+
4147
+ /**
4148
+ * Get the ending time of the last buffered time range
4149
+ *
4150
+ * This is used in the progress bar to encapsulate all time ranges.
4151
+ * @return {Number} The end of the last buffered time range
4152
+ */
4153
+ vjs.Player.prototype.bufferedEnd = function(){
4154
+ var buffered = this.buffered(),
4155
+ duration = this.duration(),
4156
+ end = buffered.end(buffered.length-1);
4157
+
4158
+ if (end > duration) {
4159
+ end = duration;
4160
+ }
4161
+
4162
+ return end;
3942
4163
  };
3943
4164
 
3944
4165
  /**
@@ -4249,64 +4470,69 @@ vjs.Player.prototype.src = function(source){
4249
4470
  return this.techGet('src');
4250
4471
  }
4251
4472
 
4252
- // Case: Array of source objects to choose from and pick the best to play
4253
- if (source instanceof Array) {
4254
-
4255
- var sourceTech = this.selectSource(source),
4256
- techName;
4473
+ // case: Array of source objects to choose from and pick the best to play
4474
+ if (vjs.obj.isArray(source)) {
4475
+ this.sourceList_(source);
4257
4476
 
4258
- if (sourceTech) {
4259
- source = sourceTech.source;
4260
- techName = sourceTech.tech;
4477
+ // case: URL String (http://myvideo...)
4478
+ } else if (typeof source === 'string') {
4479
+ // create a source object from the string
4480
+ this.src({ src: source });
4261
4481
 
4262
- // If this technology is already loaded, set source
4263
- if (techName == this.techName) {
4264
- this.src(source); // Passing the source object
4265
- // Otherwise load this technology with chosen source
4266
- } else {
4267
- this.loadTech(techName, source);
4268
- }
4269
- } else {
4270
- // this.el_.appendChild(vjs.createEl('p', {
4271
- // innerHTML: this.options()['notSupportedMessage']
4272
- // }));
4273
- this.error({ code: 4, message: this.options()['notSupportedMessage'] });
4274
- this.triggerReady(); // we could not find an appropriate tech, but let's still notify the delegate that this is it
4275
- }
4276
-
4277
- // Case: Source object { src: '', type: '' ... }
4482
+ // case: Source object { src: '', type: '' ... }
4278
4483
  } else if (source instanceof Object) {
4279
-
4280
- if (window['videojs'][this.techName]['canPlaySource'](source)) {
4281
- this.src(source.src);
4484
+ // check if the source has a type and the loaded tech cannot play the source
4485
+ // if there's no type we'll just try the current tech
4486
+ if (source.type && !window['videojs'][this.techName]['canPlaySource'](source)) {
4487
+ // create a source list with the current source and send through
4488
+ // the tech loop to check for a compatible technology
4489
+ this.sourceList_([source]);
4282
4490
  } else {
4283
- // Send through tech loop to check for a compatible technology.
4284
- this.src([source]);
4285
- }
4491
+ this.cache_.src = source.src;
4492
+ this.currentType_ = source.type || '';
4286
4493
 
4287
- // Case: URL String (http://myvideo...)
4288
- } else {
4289
- // Cache for getting last set source
4290
- this.cache_.src = source;
4291
-
4292
- if (!this.isReady_) {
4494
+ // wait until the tech is ready to set the source
4293
4495
  this.ready(function(){
4294
- this.src(source);
4496
+ this.techCall('src', source.src);
4497
+
4498
+ if (this.options_['preload'] == 'auto') {
4499
+ this.load();
4500
+ }
4501
+
4502
+ if (this.options_['autoplay']) {
4503
+ this.play();
4504
+ }
4295
4505
  });
4296
- } else {
4297
- this.techCall('src', source);
4298
- if (this.options_['preload'] == 'auto') {
4299
- this.load();
4300
- }
4301
- if (this.options_['autoplay']) {
4302
- this.play();
4303
- }
4304
4506
  }
4305
4507
  }
4306
4508
 
4307
4509
  return this;
4308
4510
  };
4309
4511
 
4512
+ /**
4513
+ * Handle an array of source objects
4514
+ * @param {[type]} sources Array of source objects
4515
+ * @private
4516
+ */
4517
+ vjs.Player.prototype.sourceList_ = function(sources){
4518
+ var sourceTech = this.selectSource(sources);
4519
+
4520
+ if (sourceTech) {
4521
+ if (sourceTech.tech === this.techName) {
4522
+ // if this technology is already loaded, set the source
4523
+ this.src(sourceTech.source);
4524
+ } else {
4525
+ // load this technology with the chosen source
4526
+ this.loadTech(sourceTech.tech, sourceTech.source);
4527
+ }
4528
+ } else {
4529
+ this.error({ code: 4, message: this.options()['notSupportedMessage'] });
4530
+ // we could not find an appropriate tech, but let's still notify the delegate that this is it
4531
+ // this needs a better comment about why this is needed
4532
+ this.triggerReady();
4533
+ }
4534
+ };
4535
+
4310
4536
  // Begin loading the src data
4311
4537
  // http://dev.w3.org/html5/spec/video.html#dom-media-load
4312
4538
  vjs.Player.prototype.load = function(){
@@ -4319,6 +4545,16 @@ vjs.Player.prototype.currentSrc = function(){
4319
4545
  return this.techGet('currentSrc') || this.cache_.src || '';
4320
4546
  };
4321
4547
 
4548
+ /**
4549
+ * Get the current source type e.g. video/mp4
4550
+ * This can allow you rebuild the current source object so that you could load the same
4551
+ * source and tech later
4552
+ * @return {String} The source MIME type
4553
+ */
4554
+ vjs.Player.prototype.currentType = function(){
4555
+ return this.currentType_ || '';
4556
+ };
4557
+
4322
4558
  // Attributes/Options
4323
4559
  vjs.Player.prototype.preload = function(value){
4324
4560
  if (value !== undefined) {
@@ -4731,7 +4967,7 @@ vjs.LiveDisplay.prototype.createEl = function(){
4731
4967
 
4732
4968
  this.contentEl_ = vjs.createEl('div', {
4733
4969
  className: 'vjs-live-display',
4734
- innerHTML: '<span class="vjs-control-text">Stream Type </span>LIVE',
4970
+ innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE'),
4735
4971
  'aria-live': 'off'
4736
4972
  });
4737
4973
 
@@ -4775,14 +5011,14 @@ vjs.PlayToggle.prototype.onClick = function(){
4775
5011
  vjs.PlayToggle.prototype.onPlay = function(){
4776
5012
  vjs.removeClass(this.el_, 'vjs-paused');
4777
5013
  vjs.addClass(this.el_, 'vjs-playing');
4778
- this.el_.children[0].children[0].innerHTML = 'Pause'; // change the button text to "Pause"
5014
+ this.el_.children[0].children[0].innerHTML = this.localize('Pause'); // change the button text to "Pause"
4779
5015
  };
4780
5016
 
4781
5017
  // OnPause - Add the vjs-paused class to the element so it can change appearance
4782
5018
  vjs.PlayToggle.prototype.onPause = function(){
4783
5019
  vjs.removeClass(this.el_, 'vjs-playing');
4784
5020
  vjs.addClass(this.el_, 'vjs-paused');
4785
- this.el_.children[0].children[0].innerHTML = 'Play'; // change the button text to "Play"
5021
+ this.el_.children[0].children[0].innerHTML = this.localize('Play'); // change the button text to "Play"
4786
5022
  };
4787
5023
  /**
4788
5024
  * Displays the current time
@@ -4817,7 +5053,7 @@ vjs.CurrentTimeDisplay.prototype.createEl = function(){
4817
5053
  vjs.CurrentTimeDisplay.prototype.updateContent = function(){
4818
5054
  // Allows for smooth scrubbing, when player can't keep up.
4819
5055
  var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
4820
- this.contentEl_.innerHTML = '<span class="vjs-control-text">Current Time </span>' + vjs.formatTime(time, this.player_.duration());
5056
+ this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Current Time') + '</span> ' + vjs.formatTime(time, this.player_.duration());
4821
5057
  };
4822
5058
 
4823
5059
  /**
@@ -4847,7 +5083,7 @@ vjs.DurationDisplay.prototype.createEl = function(){
4847
5083
 
4848
5084
  this.contentEl_ = vjs.createEl('div', {
4849
5085
  className: 'vjs-duration-display',
4850
- innerHTML: '<span class="vjs-control-text">Duration Time </span>' + '0:00', // label the duration time for screen reader users
5086
+ innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> ' + '0:00', // label the duration time for screen reader users
4851
5087
  'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
4852
5088
  });
4853
5089
 
@@ -4858,7 +5094,7 @@ vjs.DurationDisplay.prototype.createEl = function(){
4858
5094
  vjs.DurationDisplay.prototype.updateContent = function(){
4859
5095
  var duration = this.player_.duration();
4860
5096
  if (duration) {
4861
- this.contentEl_.innerHTML = '<span class="vjs-control-text">Duration Time </span>' + vjs.formatTime(duration); // label the duration time for screen reader users
5097
+ this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> ' + vjs.formatTime(duration); // label the duration time for screen reader users
4862
5098
  }
4863
5099
  };
4864
5100
 
@@ -4907,7 +5143,7 @@ vjs.RemainingTimeDisplay.prototype.createEl = function(){
4907
5143
 
4908
5144
  this.contentEl_ = vjs.createEl('div', {
4909
5145
  className: 'vjs-remaining-time-display',
4910
- innerHTML: '<span class="vjs-control-text">Remaining Time </span>' + '-0:00', // label the remaining time for screen reader users
5146
+ innerHTML: '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> ' + '-0:00', // label the remaining time for screen reader users
4911
5147
  'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
4912
5148
  });
4913
5149
 
@@ -4917,7 +5153,7 @@ vjs.RemainingTimeDisplay.prototype.createEl = function(){
4917
5153
 
4918
5154
  vjs.RemainingTimeDisplay.prototype.updateContent = function(){
4919
5155
  if (this.player_.duration()) {
4920
- this.contentEl_.innerHTML = '<span class="vjs-control-text">Remaining Time </span>' + '-'+ vjs.formatTime(this.player_.remainingTime());
5156
+ this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> ' + '-'+ vjs.formatTime(this.player_.remainingTime());
4921
5157
  }
4922
5158
 
4923
5159
  // Allows for smooth scrubbing, when player can't keep up.
@@ -4951,10 +5187,10 @@ vjs.FullscreenToggle.prototype.buildCSSClass = function(){
4951
5187
  vjs.FullscreenToggle.prototype.onClick = function(){
4952
5188
  if (!this.player_.isFullscreen()) {
4953
5189
  this.player_.requestFullscreen();
4954
- this.controlText_.innerHTML = 'Non-Fullscreen';
5190
+ this.controlText_.innerHTML = this.localize('Non-Fullscreen');
4955
5191
  } else {
4956
5192
  this.player_.exitFullscreen();
4957
- this.controlText_.innerHTML = 'Fullscreen';
5193
+ this.controlText_.innerHTML = this.localize('Fullscreen');
4958
5194
  }
4959
5195
  };
4960
5196
  /**
@@ -5066,7 +5302,6 @@ vjs.SeekBar.prototype.stepBack = function(){
5066
5302
  this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
5067
5303
  };
5068
5304
 
5069
-
5070
5305
  /**
5071
5306
  * Shows load progress
5072
5307
  *
@@ -5085,14 +5320,45 @@ vjs.LoadProgressBar = vjs.Component.extend({
5085
5320
  vjs.LoadProgressBar.prototype.createEl = function(){
5086
5321
  return vjs.Component.prototype.createEl.call(this, 'div', {
5087
5322
  className: 'vjs-load-progress',
5088
- innerHTML: '<span class="vjs-control-text">Loaded: 0%</span>'
5323
+ innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
5089
5324
  });
5090
5325
  };
5091
5326
 
5092
5327
  vjs.LoadProgressBar.prototype.update = function(){
5093
- if (this.el_.style) { this.el_.style.width = vjs.round(this.player_.bufferedPercent() * 100, 2) + '%'; }
5094
- };
5328
+ var i, start, end, part,
5329
+ buffered = this.player_.buffered(),
5330
+ duration = this.player_.duration(),
5331
+ bufferedEnd = this.player_.bufferedEnd(),
5332
+ children = this.el_.children,
5333
+ // get the percent width of a time compared to the total end
5334
+ percentify = function (time, end){
5335
+ var percent = (time / end) || 0; // no NaN
5336
+ return (percent * 100) + '%';
5337
+ };
5338
+
5339
+ // update the width of the progress bar
5340
+ this.el_.style.width = percentify(bufferedEnd, duration);
5095
5341
 
5342
+ // add child elements to represent the individual buffered time ranges
5343
+ for (i = 0; i < buffered.length; i++) {
5344
+ start = buffered.start(i),
5345
+ end = buffered.end(i),
5346
+ part = children[i];
5347
+
5348
+ if (!part) {
5349
+ part = this.el_.appendChild(vjs.createEl())
5350
+ };
5351
+
5352
+ // set the percent based on the width of the progress bar (bufferedEnd)
5353
+ part.style.left = percentify(start, bufferedEnd);
5354
+ part.style.width = percentify(end - start, bufferedEnd);
5355
+ };
5356
+
5357
+ // remove unused buffered range elements
5358
+ for (i = children.length; i > buffered.length; i--) {
5359
+ this.el_.removeChild(children[i-1]);
5360
+ }
5361
+ };
5096
5362
 
5097
5363
  /**
5098
5364
  * Shows play progress
@@ -5111,7 +5377,7 @@ vjs.PlayProgressBar = vjs.Component.extend({
5111
5377
  vjs.PlayProgressBar.prototype.createEl = function(){
5112
5378
  return vjs.Component.prototype.createEl.call(this, 'div', {
5113
5379
  className: 'vjs-play-progress',
5114
- innerHTML: '<span class="vjs-control-text">Progress: 0%</span>'
5380
+ innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
5115
5381
  });
5116
5382
  };
5117
5383
 
@@ -5321,7 +5587,7 @@ vjs.MuteToggle = vjs.Button.extend({
5321
5587
  vjs.MuteToggle.prototype.createEl = function(){
5322
5588
  return vjs.Button.prototype.createEl.call(this, 'div', {
5323
5589
  className: 'vjs-mute-control vjs-control',
5324
- innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
5590
+ innerHTML: '<div><span class="vjs-control-text">' + this.localize('Mute') + '</span></div>'
5325
5591
  });
5326
5592
  };
5327
5593
 
@@ -5345,12 +5611,12 @@ vjs.MuteToggle.prototype.update = function(){
5345
5611
  // This causes unnecessary and confusing information for screen reader users.
5346
5612
  // This check is needed because this function gets called every time the volume level is changed.
5347
5613
  if(this.player_.muted()){
5348
- if(this.el_.children[0].children[0].innerHTML!='Unmute'){
5349
- this.el_.children[0].children[0].innerHTML = 'Unmute'; // change the button text to "Unmute"
5614
+ if(this.el_.children[0].children[0].innerHTML!=this.localize('Unmute')){
5615
+ this.el_.children[0].children[0].innerHTML = this.localize('Unmute'); // change the button text to "Unmute"
5350
5616
  }
5351
5617
  } else {
5352
- if(this.el_.children[0].children[0].innerHTML!='Mute'){
5353
- this.el_.children[0].children[0].innerHTML = 'Mute'; // change the button text to "Mute"
5618
+ if(this.el_.children[0].children[0].innerHTML!=this.localize('Mute')){
5619
+ this.el_.children[0].children[0].innerHTML = this.localize('Mute'); // change the button text to "Mute"
5354
5620
  }
5355
5621
  }
5356
5622
 
@@ -5391,7 +5657,7 @@ vjs.VolumeMenuButton.prototype.createMenu = function(){
5391
5657
  var menu = new vjs.Menu(this.player_, {
5392
5658
  contentElType: 'div'
5393
5659
  });
5394
- var vc = new vjs.VolumeBar(this.player_, vjs.obj.merge({vertical: true}, this.options_.volumeBar));
5660
+ var vc = new vjs.VolumeBar(this.player_, vjs.obj.merge({'vertical': true}, this.options_.volumeBar));
5395
5661
  menu.addChild(vc);
5396
5662
  return menu;
5397
5663
  };
@@ -5404,7 +5670,7 @@ vjs.VolumeMenuButton.prototype.onClick = function(){
5404
5670
  vjs.VolumeMenuButton.prototype.createEl = function(){
5405
5671
  return vjs.Button.prototype.createEl.call(this, 'div', {
5406
5672
  className: 'vjs-volume-menu-button vjs-menu-button vjs-control',
5407
- innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
5673
+ innerHTML: '<div><span class="vjs-control-text">' + this.localize('Mute') + '</span></div>'
5408
5674
  });
5409
5675
  };
5410
5676
  vjs.VolumeMenuButton.prototype.update = vjs.MuteToggle.prototype.update;
@@ -5431,7 +5697,7 @@ vjs.PlaybackRateMenuButton = vjs.MenuButton.extend({
5431
5697
  vjs.PlaybackRateMenuButton.prototype.createEl = function(){
5432
5698
  var el = vjs.Component.prototype.createEl.call(this, 'div', {
5433
5699
  className: 'vjs-playback-rate vjs-menu-button vjs-control',
5434
- innerHTML: '<div class="vjs-control-content"><span class="vjs-control-text">Playback Rate</span></div>'
5700
+ innerHTML: '<div class="vjs-control-content"><span class="vjs-control-text">' + this.localize('Playback Rate') + '</span></div>'
5435
5701
  });
5436
5702
 
5437
5703
  this.labelEl_ = vjs.createEl('div', {
@@ -5629,23 +5895,25 @@ vjs.LoadingSpinner = vjs.Component.extend({
5629
5895
  init: function(player, options){
5630
5896
  vjs.Component.call(this, player, options);
5631
5897
 
5632
- player.on('canplay', vjs.bind(this, this.hide));
5633
- player.on('canplaythrough', vjs.bind(this, this.hide));
5634
- player.on('playing', vjs.bind(this, this.hide));
5635
- player.on('seeking', vjs.bind(this, this.show));
5898
+ // MOVING DISPLAY HANDLING TO CSS
5899
+
5900
+ // player.on('canplay', vjs.bind(this, this.hide));
5901
+ // player.on('canplaythrough', vjs.bind(this, this.hide));
5902
+ // player.on('playing', vjs.bind(this, this.hide));
5903
+ // player.on('seeking', vjs.bind(this, this.show));
5636
5904
 
5637
5905
  // in some browsers seeking does not trigger the 'playing' event,
5638
5906
  // so we also need to trap 'seeked' if we are going to set a
5639
5907
  // 'seeking' event
5640
- player.on('seeked', vjs.bind(this, this.hide));
5908
+ // player.on('seeked', vjs.bind(this, this.hide));
5641
5909
 
5642
- player.on('ended', vjs.bind(this, this.hide));
5910
+ // player.on('ended', vjs.bind(this, this.hide));
5643
5911
 
5644
5912
  // Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner.
5645
5913
  // Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing
5646
5914
  // player.on('stalled', vjs.bind(this, this.show));
5647
5915
 
5648
- player.on('waiting', vjs.bind(this, this.show));
5916
+ // player.on('waiting', vjs.bind(this, this.show));
5649
5917
  }
5650
5918
  });
5651
5919
 
@@ -5705,7 +5973,7 @@ vjs.ErrorDisplay.prototype.createEl = function(){
5705
5973
 
5706
5974
  vjs.ErrorDisplay.prototype.update = function(){
5707
5975
  if (this.player().error()) {
5708
- this.contentEl_.innerHTML = this.player().error().message;
5976
+ this.contentEl_.innerHTML = this.localize(this.player().error().message);
5709
5977
  }
5710
5978
  };
5711
5979
  /**
@@ -5797,8 +6065,6 @@ vjs.MediaTechController.prototype.addControlsListeners = function(){
5797
6065
  // so we'll check if the controls were already showing before reporting user
5798
6066
  // activity
5799
6067
  this.on('touchstart', function(event) {
5800
- // Stop the mouse events from also happening
5801
- event.preventDefault();
5802
6068
  userWasActive = this.player_.userActive();
5803
6069
  });
5804
6070
 
@@ -5808,6 +6074,11 @@ vjs.MediaTechController.prototype.addControlsListeners = function(){
5808
6074
  }
5809
6075
  });
5810
6076
 
6077
+ this.on('touchend', function(event) {
6078
+ // Stop the mouse events from also happening
6079
+ event.preventDefault();
6080
+ });
6081
+
5811
6082
  // Turn on component tap events
5812
6083
  this.emitTapEvents();
5813
6084
 
@@ -5942,6 +6213,7 @@ vjs.Html5 = vjs.MediaTechController.extend({
5942
6213
  });
5943
6214
 
5944
6215
  vjs.Html5.prototype.dispose = function(){
6216
+ vjs.Html5.disposeMediaElement(this.el_);
5945
6217
  vjs.MediaTechController.prototype.dispose.call(this);
5946
6218
  };
5947
6219
 
@@ -5964,10 +6236,13 @@ vjs.Html5.prototype.createEl = function(){
5964
6236
  el = clone;
5965
6237
  player.tag = null;
5966
6238
  } else {
5967
- el = vjs.createEl('video', {
5968
- id:player.id() + '_html5_api',
5969
- className:'vjs-tech'
5970
- });
6239
+ el = vjs.createEl('video');
6240
+ vjs.setElementAttributes(el,
6241
+ vjs.obj.merge(player.tagAttributes || {}, {
6242
+ id:player.id() + '_html5_api',
6243
+ 'class':'vjs-tech'
6244
+ })
6245
+ );
5971
6246
  }
5972
6247
  // associate the player with the new tag
5973
6248
  el['player'] = player;
@@ -5976,12 +6251,14 @@ vjs.Html5.prototype.createEl = function(){
5976
6251
  }
5977
6252
 
5978
6253
  // Update specific tag settings, in case they were overridden
5979
- var attrs = ['autoplay','preload','loop','muted'];
5980
- for (var i = attrs.length - 1; i >= 0; i--) {
5981
- var attr = attrs[i];
5982
- if (player.options_[attr] !== null) {
5983
- el[attr] = player.options_[attr];
6254
+ var settingsAttrs = ['autoplay','preload','loop','muted'];
6255
+ for (var i = settingsAttrs.length - 1; i >= 0; i--) {
6256
+ var attr = settingsAttrs[i];
6257
+ var overwriteAttrs = {};
6258
+ if (typeof player.options_[attr] !== 'undefined') {
6259
+ overwriteAttrs[attr] = player.options_[attr];
5984
6260
  }
6261
+ vjs.setElementAttributes(el, overwriteAttrs);
5985
6262
  }
5986
6263
 
5987
6264
  return el;
@@ -6310,9 +6587,7 @@ vjs.Flash = vjs.MediaTechController.extend({
6310
6587
  'id': objId,
6311
6588
  'name': objId, // Both ID and Name needed or swf to identifty itself
6312
6589
  'class': 'vjs-tech'
6313
- }, options['attributes']),
6314
-
6315
- lastSeekTarget
6590
+ }, options['attributes'])
6316
6591
  ;
6317
6592
 
6318
6593
  // If source was supplied pass as a flash var.
@@ -6327,19 +6602,6 @@ vjs.Flash = vjs.MediaTechController.extend({
6327
6602
  }
6328
6603
  }
6329
6604
 
6330
- this['setCurrentTime'] = function(time){
6331
- lastSeekTarget = time;
6332
- this.el_.vjs_setProperty('currentTime', time);
6333
- };
6334
- this['currentTime'] = function(time){
6335
- // when seeking make the reported time keep up with the requested time
6336
- // by reading the time we're seeking to
6337
- if (this.seeking()) {
6338
- return lastSeekTarget;
6339
- }
6340
- return this.el_.vjs_getProperty('currentTime');
6341
- };
6342
-
6343
6605
  // Add placeholder to player div
6344
6606
  vjs.insertFirst(placeHolder, parentEl);
6345
6607
 
@@ -6349,7 +6611,7 @@ vjs.Flash = vjs.MediaTechController.extend({
6349
6611
  this.ready(function(){
6350
6612
  this.load();
6351
6613
  this.play();
6352
- this.currentTime(options['startTime']);
6614
+ this['currentTime'](options['startTime']);
6353
6615
  });
6354
6616
  }
6355
6617
 
@@ -6364,134 +6626,11 @@ vjs.Flash = vjs.MediaTechController.extend({
6364
6626
  });
6365
6627
  }
6366
6628
 
6367
- // Flash iFrame Mode
6368
- // In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
6369
- // - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
6370
- // - Webkit when hiding the plugin
6371
- // - Webkit and Firefox when using requestFullScreen on a parent element
6372
- // Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
6373
- // Issues that remain include hiding the element and requestFullScreen in Firefox specifically
6374
-
6375
- // There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
6376
- // Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
6377
- // I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
6378
- // Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
6379
- // In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
6380
- // The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
6381
-
6382
- // NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
6383
- // Firefox 9 throws a security error, unleess you call location.href right before doc.write.
6384
- // Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
6385
- // Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
6386
-
6387
- if (options['iFrameMode'] === true && !vjs.IS_FIREFOX) {
6388
-
6389
- // Create iFrame with vjs-tech class so it's 100% width/height
6390
- var iFrm = vjs.createEl('iframe', {
6391
- 'id': objId + '_iframe',
6392
- 'name': objId + '_iframe',
6393
- 'className': 'vjs-tech',
6394
- 'scrolling': 'no',
6395
- 'marginWidth': 0,
6396
- 'marginHeight': 0,
6397
- 'frameBorder': 0
6398
- });
6399
-
6400
- // Update ready function names in flash vars for iframe window
6401
- flashVars['readyFunction'] = 'ready';
6402
- flashVars['eventProxyFunction'] = 'events';
6403
- flashVars['errorEventProxyFunction'] = 'errors';
6404
-
6405
- // Tried multiple methods to get this to work in all browsers
6406
-
6407
- // Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
6408
- // The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
6409
- // var newObj = vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
6410
- // (in onload)
6411
- // var temp = vjs.createEl('a', { id:'asdf', innerHTML: 'asdf' } );
6412
- // iDoc.body.appendChild(temp);
6413
-
6414
- // Tried embedding the flash object through javascript in the iframe source.
6415
- // This works in webkit but still triggers the firefox security error
6416
- // iFrm.src = 'javascript: document.write('"+vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes)+"');";
6417
-
6418
- // Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
6419
- // We should add an option to host the iframe locally though, because it could help a lot of issues.
6420
- // iFrm.src = "iframe.html";
6421
-
6422
- // Wait until iFrame has loaded to write into it.
6423
- vjs.on(iFrm, 'load', vjs.bind(this, function(){
6424
-
6425
- var iDoc,
6426
- iWin = iFrm.contentWindow;
6427
-
6428
- // The one working method I found was to use the iframe's document.write() to create the swf object
6429
- // This got around the security issue in all browsers except firefox.
6430
- // I did find a hack where if I call the iframe's window.location.href='', it would get around the security error
6431
- // However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
6432
- // Plus Firefox 3.6 didn't work no matter what I tried.
6433
- // if (vjs.USER_AGENT.match('Firefox')) {
6434
- // iWin.location.href = '';
6435
- // }
6436
-
6437
- // Get the iFrame's document depending on what the browser supports
6438
- iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
6439
-
6440
- // Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
6441
- // Even tried adding /. that was mentioned in a browser security writeup
6442
- // document.domain = document.domain+'/.';
6443
- // iDoc.domain = document.domain+'/.';
6444
-
6445
- // Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
6446
- // iDoc.body.innerHTML = swfObjectHTML;
6447
-
6448
- // Tried appending the object to the iframe doc's body. Security error in all browsers.
6449
- // iDoc.body.appendChild(swfObject);
6450
-
6451
- // Using document.write actually got around the security error that browsers were throwing.
6452
- // Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
6453
- // Not sure why that's a security issue, but apparently it is.
6454
- iDoc.write(vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes));
6455
-
6456
- // Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
6457
- // So far no issues with swf ready event being called before it's set on the window.
6458
- iWin['player'] = this.player_;
6459
-
6460
- // Create swf ready function for iFrame window
6461
- iWin['ready'] = vjs.bind(this.player_, function(currSwf){
6462
- var el = iDoc.getElementById(currSwf),
6463
- player = this,
6464
- tech = player.tech;
6465
-
6466
- // Update reference to playback technology element
6467
- tech.el_ = el;
6468
-
6469
- // Make sure swf is actually ready. Sometimes the API isn't actually yet.
6470
- vjs.Flash.checkReady(tech);
6471
- });
6472
-
6473
- // Create event listener for all swf events
6474
- iWin['events'] = vjs.bind(this.player_, function(swfID, eventName){
6475
- var player = this;
6476
- if (player && player.techName === 'flash') {
6477
- player.trigger(eventName);
6478
- }
6479
- });
6480
-
6481
- // Create error listener for all swf errors
6482
- iWin['errors'] = vjs.bind(this.player_, function(swfID, eventName){
6483
- vjs.log('Flash Error', eventName);
6484
- });
6485
-
6486
- }));
6487
-
6488
- // Replace placeholder with iFrame (it will load now)
6489
- placeHolder.parentNode.replaceChild(iFrm, placeHolder);
6629
+ // native click events on the SWF aren't triggered on IE11, Win8.1RT
6630
+ // use stageclick events triggered from inside the SWF instead
6631
+ player.on('stageclick', player.reportUserActivity);
6490
6632
 
6491
- // If not using iFrame mode, embed as normal object
6492
- } else {
6493
- vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
6494
- }
6633
+ this.el_ = vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
6495
6634
  }
6496
6635
  });
6497
6636
 
@@ -6509,7 +6648,7 @@ vjs.Flash.prototype.pause = function(){
6509
6648
 
6510
6649
  vjs.Flash.prototype.src = function(src){
6511
6650
  if (src === undefined) {
6512
- return this.currentSrc();
6651
+ return this['currentSrc']();
6513
6652
  }
6514
6653
 
6515
6654
  if (vjs.Flash.isStreamingSrc(src)) {
@@ -6530,7 +6669,21 @@ vjs.Flash.prototype.src = function(src){
6530
6669
  }
6531
6670
  };
6532
6671
 
6533
- vjs.Flash.prototype.currentSrc = function(){
6672
+ vjs.Flash.prototype['setCurrentTime'] = function(time){
6673
+ this.lastSeekTarget_ = time;
6674
+ this.el_.vjs_setProperty('currentTime', time);
6675
+ };
6676
+
6677
+ vjs.Flash.prototype['currentTime'] = function(time){
6678
+ // when seeking make the reported time keep up with the requested time
6679
+ // by reading the time we're seeking to
6680
+ if (this.seeking()) {
6681
+ return this.lastSeekTarget_ || 0;
6682
+ }
6683
+ return this.el_.vjs_getProperty('currentTime');
6684
+ };
6685
+
6686
+ vjs.Flash.prototype['currentSrc'] = function(){
6534
6687
  var src = this.el_.vjs_getProperty('currentSrc');
6535
6688
  // no src, check and see if RTMP
6536
6689
  if (src == null) {
@@ -6551,7 +6704,7 @@ vjs.Flash.prototype.load = function(){
6551
6704
  vjs.Flash.prototype.poster = function(){
6552
6705
  this.el_.vjs_getProperty('poster');
6553
6706
  };
6554
- vjs.Flash.prototype.setPoster = function(){
6707
+ vjs.Flash.prototype['setPoster'] = function(){
6555
6708
  // poster images are not handled by the Flash tech so make this a no-op
6556
6709
  };
6557
6710
 
@@ -6567,31 +6720,22 @@ vjs.Flash.prototype.enterFullScreen = function(){
6567
6720
  return false;
6568
6721
  };
6569
6722
 
6570
- // Create setters and getters for attributes
6571
- var api = vjs.Flash.prototype,
6723
+ (function(){
6724
+ // Create setters and getters for attributes
6725
+ var api = vjs.Flash.prototype,
6572
6726
  readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','),
6573
- readOnly = 'error,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks'.split(',');
6727
+ readOnly = 'error,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks'.split(','),
6574
6728
  // Overridden: buffered, currentTime, currentSrc
6729
+ i;
6575
6730
 
6576
- /**
6577
- * @this {*}
6578
- * @private
6579
- */
6580
- var createSetter = function(attr){
6581
- var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
6582
- api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); };
6583
- };
6584
-
6585
- /**
6586
- * @this {*}
6587
- * @private
6588
- */
6589
- var createGetter = function(attr){
6590
- api[attr] = function(){ return this.el_.vjs_getProperty(attr); };
6591
- };
6731
+ function createSetter(attr){
6732
+ var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
6733
+ api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); };
6734
+ };
6735
+ function createGetter(attr) {
6736
+ api[attr] = function(){ return this.el_.vjs_getProperty(attr); };
6737
+ };
6592
6738
 
6593
- (function(){
6594
- var i;
6595
6739
  // Create getter and setters for all read/write attributes
6596
6740
  for (i = 0; i < readWrite.length; i++) {
6597
6741
  createGetter(readWrite[i]);
@@ -6637,39 +6781,40 @@ vjs.Flash.streamingFormats = {
6637
6781
  };
6638
6782
 
6639
6783
  vjs.Flash['onReady'] = function(currSwf){
6640
- var el = vjs.el(currSwf);
6641
-
6642
- // Get player from box
6643
- // On firefox reloads, el might already have a player
6644
- var player = el['player'] || el.parentNode['player'],
6645
- tech = player.tech;
6784
+ var el, player;
6646
6785
 
6647
- // Reference player on tech element
6648
- el['player'] = player;
6786
+ el = vjs.el(currSwf);
6649
6787
 
6650
- // Update reference to playback technology element
6651
- tech.el_ = el;
6788
+ // get player from the player div property
6789
+ player = el && el.parentNode && el.parentNode['player'];
6652
6790
 
6653
- vjs.Flash.checkReady(tech);
6791
+ // if there is no el or player then the tech has been disposed
6792
+ // and the tech element was removed from the player div
6793
+ if (player) {
6794
+ // reference player on tech element
6795
+ el['player'] = player;
6796
+ // check that the flash object is really ready
6797
+ vjs.Flash['checkReady'](player.tech);
6798
+ }
6654
6799
  };
6655
6800
 
6656
- // The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object.
6801
+ // The SWF isn't always ready when it says it is. Sometimes the API functions still need to be added to the object.
6657
6802
  // If it's not ready, we set a timeout to check again shortly.
6658
- vjs.Flash.checkReady = function(tech){
6803
+ vjs.Flash['checkReady'] = function(tech){
6804
+ // stop worrying if the tech has been disposed
6805
+ if (!tech.el()) {
6806
+ return;
6807
+ }
6659
6808
 
6660
- // Check if API property exists
6809
+ // check if API property exists
6661
6810
  if (tech.el().vjs_getProperty) {
6662
-
6663
- // If so, tell tech it's ready
6811
+ // tell tech it's ready
6664
6812
  tech.triggerReady();
6665
-
6666
- // Otherwise wait longer.
6667
6813
  } else {
6668
-
6814
+ // wait longer
6669
6815
  setTimeout(function(){
6670
- vjs.Flash.checkReady(tech);
6816
+ vjs.Flash['checkReady'](tech);
6671
6817
  }, 50);
6672
-
6673
6818
  }
6674
6819
  };
6675
6820
 
@@ -6925,7 +7070,7 @@ vjs.Player.prototype.addTextTrack = function(kind, label, language, options){
6925
7070
  if (track.dflt()) {
6926
7071
  this.ready(function(){
6927
7072
  setTimeout(function(){
6928
- track.show();
7073
+ track.player().showTextTrack(track.id());
6929
7074
  }, 0);
6930
7075
  });
6931
7076
  }
@@ -7344,9 +7489,9 @@ vjs.TextTrack.prototype.parseCues = function(srcContent) {
7344
7489
  };
7345
7490
 
7346
7491
  // Timing line
7347
- time = line.split(' --> ');
7492
+ time = line.split(/[\t ]+/);
7348
7493
  cue.startTime = this.parseCueTime(time[0]);
7349
- cue.endTime = this.parseCueTime(time[1]);
7494
+ cue.endTime = this.parseCueTime(time[2]);
7350
7495
 
7351
7496
  // Additional lines - Cue Text
7352
7497
  text = [];
@@ -7803,11 +7948,10 @@ vjs.ChaptersButton.prototype.createMenu = function(){
7803
7948
 
7804
7949
  for (;i<j;i++) {
7805
7950
  track = tracks[i];
7806
- if (track.kind() == this.kind_ && track.dflt()) {
7807
- if (track.readyState() < 2) {
7808
- this.chaptersTrack = track;
7951
+ if (track.kind() == this.kind_) {
7952
+ if (track.readyState() === 0) {
7953
+ track.load();
7809
7954
  track.on('loaded', vjs.bind(this, this.createMenu));
7810
- return;
7811
7955
  } else {
7812
7956
  chaptersTrack = track;
7813
7957
  break;
@@ -7815,13 +7959,15 @@ vjs.ChaptersButton.prototype.createMenu = function(){
7815
7959
  }
7816
7960
  }
7817
7961
 
7818
- var menu = this.menu = new vjs.Menu(this.player_);
7819
-
7820
- menu.contentEl().appendChild(vjs.createEl('li', {
7821
- className: 'vjs-menu-title',
7822
- innerHTML: vjs.capitalize(this.kind_),
7823
- tabindex: -1
7824
- }));
7962
+ var menu = this.menu;
7963
+ if (menu === undefined) {
7964
+ menu = new vjs.Menu(this.player_);
7965
+ menu.contentEl().appendChild(vjs.createEl('li', {
7966
+ className: 'vjs-menu-title',
7967
+ innerHTML: vjs.capitalize(this.kind_),
7968
+ tabindex: -1
7969
+ }));
7970
+ }
7825
7971
 
7826
7972
  if (chaptersTrack) {
7827
7973
  var cues = chaptersTrack.cues_, cue, mi;
@@ -7840,6 +7986,7 @@ vjs.ChaptersButton.prototype.createMenu = function(){
7840
7986
 
7841
7987
  menu.addChild(mi);
7842
7988
  }
7989
+ this.addChild(menu);
7843
7990
  }
7844
7991
 
7845
7992
  if (this.items.length > 0) {
@@ -8015,7 +8162,7 @@ vjs.autoSetup = function(){
8015
8162
  }
8016
8163
  }
8017
8164
 
8018
- // No videos were found, so keep looping unless page is finisehd loading.
8165
+ // No videos were found, so keep looping unless page is finished loading.
8019
8166
  } else if (!vjs.windowLoaded) {
8020
8167
  vjs.autoSetupTimeout(1);
8021
8168
  }