videojs_rails 4.10.2 → 4.11.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: ea6386cdc5c6833b596dbb1364360054507070bd
4
- data.tar.gz: aef4b40f159609142b73b2d6f96c1abc0dfb8664
3
+ metadata.gz: c5859566b78fa09a28ae4f320dec8a8de7038789
4
+ data.tar.gz: 42f18794f2a096da9166824bbc587f90ab1f275c
5
5
  SHA512:
6
- metadata.gz: 249b1ad18400aa4e05e9e1b045f2380615615caf709b5041221f7fc8cd11c4e31766fcf1ceabb193e8dbd63358eacee3b00b1f47eadc9a708b86a617207f50d8
7
- data.tar.gz: 2e8e25ae4cbeb17fa3acc4417c51d6cbd69d7b816d9f44d93be7b5235604fa5b62e50189a897163aabe50fc82a05f27442721b71fd894ffbcded263e3424a313
6
+ metadata.gz: f59b5364c73a63688a826865b6eb46edabe5dfe9a1fda58a077a8677075caf110308d837847a65f8f08a086d563cae51858a13589d7e9dd200bf65433f71b260
7
+ data.tar.gz: b327fb577882debbafb08b67c6248fe50c18f7618b492516961a63620bdbdde2077d0713c6ded92b0c6cbda93b5e5af83905b37250f29634242f3be57f0f7477
@@ -1,3 +1,3 @@
1
1
  module VideojsRails
2
- VERSION = '4.10.2'
2
+ VERSION = '4.11.0'
3
3
  end
@@ -63,7 +63,7 @@ var vjs = function(id, options, ready){
63
63
  var videojs = window['videojs'] = vjs;
64
64
 
65
65
  // CDN Version. Used to target right flash swf.
66
- vjs.CDN_VERSION = '4.10';
66
+ vjs.CDN_VERSION = '4.11';
67
67
  vjs.ACCESS_PROTOCOL = ('https:' == document.location.protocol ? 'https://' : 'http://');
68
68
 
69
69
  /**
@@ -116,7 +116,7 @@ vjs.options = {
116
116
  };
117
117
 
118
118
  // Set CDN Version of swf
119
- // The added (+) blocks the replace from changing this 4.10 string
119
+ // The added (+) blocks the replace from changing this 4.11 string
120
120
  if (vjs.CDN_VERSION !== 'GENERATED'+'_CDN_VSN') {
121
121
  videojs.options['flash']['swf'] = "<%= asset_path('video-js.swf') %>";
122
122
  }
@@ -161,7 +161,7 @@ if (typeof define === 'function' && define['amd']) {
161
161
  module['exports'] = videojs;
162
162
  }
163
163
  /**
164
- * Core Object/Class for objects that use inheritance + contstructors
164
+ * Core Object/Class for objects that use inheritance + constructors
165
165
  *
166
166
  * To create a class that can be subclassed itself, extend the CoreObject class.
167
167
  *
@@ -214,7 +214,7 @@ if (typeof define === 'function' && define['amd']) {
214
214
  vjs.CoreObject = vjs['CoreObject'] = function(){};
215
215
  // Manually exporting vjs['CoreObject'] here for Closure Compiler
216
216
  // because of the use of the extend/create class methods
217
- // If we didn't do this, those functions would get flattend to something like
217
+ // If we didn't do this, those functions would get flattened to something like
218
218
  // `a = ...` and `this.prototype` would refer to the global object instead of
219
219
  // CoreObject
220
220
 
@@ -240,10 +240,10 @@ vjs.CoreObject.extend = function(props){
240
240
  // In Resig's simple class inheritance (previously used) the constructor
241
241
  // is a function that calls `this.init.apply(arguments)`
242
242
  // However that would prevent us from using `ParentObject.call(this);`
243
- // in a Child constuctor because the `this` in `this.init`
244
- // would still refer to the Child and cause an inifinite loop.
243
+ // in a Child constructor because the `this` in `this.init`
244
+ // would still refer to the Child and cause an infinite loop.
245
245
  // We would instead have to do
246
- // `ParentObject.prototype.init.apply(this, argumnents);`
246
+ // `ParentObject.prototype.init.apply(this, arguments);`
247
247
  // Bleh. We're not creating a _super() function, so it's good to keep
248
248
  // the parent constructor reference simple.
249
249
  subObj = function(){
@@ -272,7 +272,7 @@ vjs.CoreObject.extend = function(props){
272
272
  };
273
273
 
274
274
  /**
275
- * Create a new instace of this Object class
275
+ * Create a new instance of this Object class
276
276
  *
277
277
  * var myAnimal = Animal.create();
278
278
  *
@@ -616,7 +616,7 @@ vjs.trigger = function(elem, event) {
616
616
  * We've since updated to the latest version, but keeping this around
617
617
  * for now just in case.
618
618
  */
619
- // // Added in attion to book. Book code was broke.
619
+ // // Added in addition to book. Book code was broke.
620
620
  // event = typeof event === 'object' ?
621
621
  // event[vjs.expando] ?
622
622
  // event :
@@ -785,7 +785,7 @@ vjs.obj.merge = function(obj1, obj2){
785
785
  vjs.obj.deepMerge = function(obj1, obj2){
786
786
  var key, val1, val2;
787
787
 
788
- // make a copy of obj1 so we're not ovewriting original values.
788
+ // make a copy of obj1 so we're not overwriting original values.
789
789
  // like prototype.options_ and all sub options objects
790
790
  obj1 = vjs.obj.copy(obj1);
791
791
 
@@ -881,7 +881,7 @@ vjs.bind = function(context, fn, uid) {
881
881
 
882
882
  /**
883
883
  * Element Data Store. Allows for binding data to an element without putting it directly on the element.
884
- * Ex. Event listneres are stored here.
884
+ * Ex. Event listeners are stored here.
885
885
  * (also from jsninja.com, slightly modified and updated for closure compiler)
886
886
  * @type {Object}
887
887
  * @private
@@ -1100,7 +1100,7 @@ vjs.setElementAttributes = function(el, attributes){
1100
1100
 
1101
1101
  /**
1102
1102
  * Get an element's attribute values, as defined on the HTML tag
1103
- * Attributs are not the same as properties. They're defined on the tag
1103
+ * Attributes are not the same as properties. They're defined on the tag
1104
1104
  * or with setAttribute (which shouldn't be used with HTML)
1105
1105
  * This will return true or false for boolean attributes.
1106
1106
  * @param {Element} tag Element from which to get tag attributes
@@ -1283,87 +1283,7 @@ vjs.createTimeRange = function(start, end){
1283
1283
  };
1284
1284
 
1285
1285
  /**
1286
- * Simple http request for retrieving external files (e.g. text tracks)
1287
- * @param {String} url URL of resource
1288
- * @param {Function} onSuccess Success callback
1289
- * @param {Function=} onError Error callback
1290
- * @param {Boolean=} withCredentials Flag which allow credentials
1291
- * @private
1292
- */
1293
- vjs.get = function(url, onSuccess, onError, withCredentials){
1294
- var fileUrl, request, urlInfo, winLoc, crossOrigin;
1295
-
1296
- onError = onError || function(){};
1297
-
1298
- if (typeof XMLHttpRequest === 'undefined') {
1299
- // Shim XMLHttpRequest for older IEs
1300
- window.XMLHttpRequest = function () {
1301
- try { return new window.ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch (e) {}
1302
- try { return new window.ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch (f) {}
1303
- try { return new window.ActiveXObject('Msxml2.XMLHTTP'); } catch (g) {}
1304
- throw new Error('This browser does not support XMLHttpRequest.');
1305
- };
1306
- }
1307
-
1308
- request = new XMLHttpRequest();
1309
-
1310
- urlInfo = vjs.parseUrl(url);
1311
- winLoc = window.location;
1312
- // check if url is for another domain/origin
1313
- // ie8 doesn't know location.origin, so we won't rely on it here
1314
- crossOrigin = (urlInfo.protocol + urlInfo.host) !== (winLoc.protocol + winLoc.host);
1315
-
1316
- // Use XDomainRequest for IE if XMLHTTPRequest2 isn't available
1317
- // 'withCredentials' is only available in XMLHTTPRequest2
1318
- // Also XDomainRequest has a lot of gotchas, so only use if cross domain
1319
- if(crossOrigin && window.XDomainRequest && !('withCredentials' in request)) {
1320
- request = new window.XDomainRequest();
1321
- request.onload = function() {
1322
- onSuccess(request.responseText);
1323
- };
1324
- request.onerror = onError;
1325
- // these blank handlers need to be set to fix ie9 http://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/
1326
- request.onprogress = function() {};
1327
- request.ontimeout = onError;
1328
-
1329
- // XMLHTTPRequest
1330
- } else {
1331
- fileUrl = (urlInfo.protocol == 'file:' || winLoc.protocol == 'file:');
1332
-
1333
- request.onreadystatechange = function() {
1334
- if (request.readyState === 4) {
1335
- if (request.status === 200 || fileUrl && request.status === 0) {
1336
- onSuccess(request.responseText);
1337
- } else {
1338
- onError(request.responseText);
1339
- }
1340
- }
1341
- };
1342
- }
1343
-
1344
- // open the connection
1345
- try {
1346
- // Third arg is async, or ignored by XDomainRequest
1347
- request.open('GET', url, true);
1348
- // withCredentials only supported by XMLHttpRequest2
1349
- if(withCredentials) {
1350
- request.withCredentials = true;
1351
- }
1352
- } catch(e) {
1353
- onError(e);
1354
- return;
1355
- }
1356
-
1357
- // send the request
1358
- try {
1359
- request.send();
1360
- } catch(e) {
1361
- onError(e);
1362
- }
1363
- };
1364
-
1365
- /**
1366
- * Add to local storage (may removeable)
1286
+ * Add to local storage (may removable)
1367
1287
  * @private
1368
1288
  */
1369
1289
  vjs.setLocalStorage = function(key, value){
@@ -1386,7 +1306,7 @@ vjs.setLocalStorage = function(key, value){
1386
1306
  };
1387
1307
 
1388
1308
  /**
1389
- * Get abosolute version of relative URL. Used to tell flash correct URL.
1309
+ * Get absolute version of relative URL. Used to tell flash correct URL.
1390
1310
  * http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
1391
1311
  * @param {String} url URL to make absolute
1392
1312
  * @return {String} Absolute URL
@@ -1448,7 +1368,7 @@ vjs.parseUrl = function(url) {
1448
1368
  };
1449
1369
 
1450
1370
  /**
1451
- * Log messags to the console and history based on the type of message
1371
+ * Log messages to the console and history based on the type of message
1452
1372
  *
1453
1373
  * @param {String} type The type of message, or `null` for `log`
1454
1374
  * @param {[type]} args The args to be passed to the log
@@ -1578,6 +1498,157 @@ vjs.arr.forEach = function(array, callback, thisArg) {
1578
1498
 
1579
1499
  return array;
1580
1500
  };
1501
+ /**
1502
+ * Simple http request for retrieving external files (e.g. text tracks)
1503
+ *
1504
+ * ##### Example
1505
+ *
1506
+ * // using url string
1507
+ * videojs.xhr('http://example.com/myfile.vtt', function(error, response, responseBody){});
1508
+ *
1509
+ * // or options block
1510
+ * videojs.xhr({
1511
+ * uri: 'http://example.com/myfile.vtt',
1512
+ * method: 'GET',
1513
+ * responseType: 'text'
1514
+ * }, function(error, response, responseBody){
1515
+ * if (error) {
1516
+ * // log the error
1517
+ * } else {
1518
+ * // successful, do something with the response
1519
+ * }
1520
+ * });
1521
+ *
1522
+ *
1523
+ * API is modeled after the Raynos/xhr, which we hope to use after
1524
+ * getting browserify implemented.
1525
+ * https://github.com/Raynos/xhr/blob/master/index.js
1526
+ *
1527
+ * @param {Object|String} options Options block or URL string
1528
+ * @param {Function} callback The callback function
1529
+ * @returns {Object} The request
1530
+ */
1531
+ vjs.xhr = function(options, callback){
1532
+ var XHR, request, urlInfo, winLoc, fileUrl, crossOrigin, abortTimeout, successHandler, errorHandler;
1533
+
1534
+ // If options is a string it's the url
1535
+ if (typeof options === 'string') {
1536
+ options = {
1537
+ uri: options
1538
+ };
1539
+ }
1540
+
1541
+ // Merge with default options
1542
+ videojs.util.mergeOptions({
1543
+ method: 'GET',
1544
+ timeout: 45 * 1000
1545
+ }, options);
1546
+
1547
+ callback = callback || function(){};
1548
+
1549
+ successHandler = function(){
1550
+ window.clearTimeout(abortTimeout);
1551
+ callback(null, request, request.response || request.responseText);
1552
+ };
1553
+
1554
+ errorHandler = function(err){
1555
+ window.clearTimeout(abortTimeout);
1556
+
1557
+ if (!err || typeof err === 'string') {
1558
+ err = new Error(err);
1559
+ }
1560
+
1561
+ callback(err, request);
1562
+ };
1563
+
1564
+ XHR = window.XMLHttpRequest;
1565
+
1566
+ if (typeof XHR === 'undefined') {
1567
+ // Shim XMLHttpRequest for older IEs
1568
+ XHR = function () {
1569
+ try { return new window.ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch (e) {}
1570
+ try { return new window.ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch (f) {}
1571
+ try { return new window.ActiveXObject('Msxml2.XMLHTTP'); } catch (g) {}
1572
+ throw new Error('This browser does not support XMLHttpRequest.');
1573
+ };
1574
+ }
1575
+
1576
+ request = new XHR();
1577
+ // Store a reference to the url on the request instance
1578
+ request.uri = options.uri;
1579
+
1580
+ urlInfo = vjs.parseUrl(options.uri);
1581
+ winLoc = window.location;
1582
+ // Check if url is for another domain/origin
1583
+ // IE8 doesn't know location.origin, so we won't rely on it here
1584
+ crossOrigin = (urlInfo.protocol + urlInfo.host) !== (winLoc.protocol + winLoc.host);
1585
+
1586
+ // XDomainRequest -- Use for IE if XMLHTTPRequest2 isn't available
1587
+ // 'withCredentials' is only available in XMLHTTPRequest2
1588
+ // Also XDomainRequest has a lot of gotchas, so only use if cross domain
1589
+ if (crossOrigin && window.XDomainRequest && !('withCredentials' in request)) {
1590
+ request = new window.XDomainRequest();
1591
+ request.onload = successHandler;
1592
+ request.onerror = errorHandler;
1593
+ // These blank handlers need to be set to fix ie9
1594
+ // http://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/
1595
+ request.onprogress = function(){};
1596
+ request.ontimeout = function(){};
1597
+
1598
+ // XMLHTTPRequest
1599
+ } else {
1600
+ fileUrl = (urlInfo.protocol == 'file:' || winLoc.protocol == 'file:');
1601
+
1602
+ request.onreadystatechange = function() {
1603
+ if (request.readyState === 4) {
1604
+ if (request.timedout) {
1605
+ return errorHandler('timeout');
1606
+ }
1607
+
1608
+ if (request.status === 200 || fileUrl && request.status === 0) {
1609
+ successHandler();
1610
+ } else {
1611
+ errorHandler();
1612
+ }
1613
+ }
1614
+ };
1615
+
1616
+ if (options.timeout) {
1617
+ abortTimeout = window.setTimeout(function() {
1618
+ if (request.readyState !== 4) {
1619
+ request.timedout = true;
1620
+ request.abort();
1621
+ }
1622
+ }, options.timeout);
1623
+ }
1624
+ }
1625
+
1626
+ // open the connection
1627
+ try {
1628
+ // Third arg is async, or ignored by XDomainRequest
1629
+ request.open(options.method || 'GET', options.uri, true);
1630
+ } catch(err) {
1631
+ return errorHandler(err);
1632
+ }
1633
+
1634
+ // withCredentials only supported by XMLHttpRequest2
1635
+ if(options.withCredentials) {
1636
+ request.withCredentials = true;
1637
+ }
1638
+
1639
+ if (options.responseType) {
1640
+ request.responseType = options.responseType;
1641
+ }
1642
+
1643
+ // send the request
1644
+ try {
1645
+ request.send();
1646
+ } catch(err) {
1647
+ return errorHandler(err);
1648
+ }
1649
+
1650
+ return request;
1651
+ };
1581
1652
  /**
1582
1653
  * Utility functions namespace
1583
1654
  * @namespace
@@ -2177,7 +2248,7 @@ vjs.Component.prototype.buildCSSClass = function(){
2177
2248
  *
2178
2249
  * The benefit of using this over `vjs.on(otherElement, 'eventName', myFunc)`
2179
2250
  * and `otherComponent.on('eventName', myFunc)` is that this way the listeners
2180
- * will be automatically cleaned up when either component is diposed.
2251
+ * will be automatically cleaned up when either component is disposed.
2181
2252
  * It will also bind myComponent as the context of myFunc.
2182
2253
  *
2183
2254
  * **NOTE**: When using this on elements in the page other than window
@@ -2358,7 +2429,7 @@ vjs.Component.prototype.isReady_;
2358
2429
  *
2359
2430
  * Allows for delaying ready. Override on a sub class prototype.
2360
2431
  * If you set this.isReadyOnInitFinish_ it will affect all components.
2361
- * Specially used when waiting for the Flash player to asynchrnously load.
2432
+ * Specially used when waiting for the Flash player to asynchronously load.
2362
2433
  *
2363
2434
  * @type {Boolean}
2364
2435
  * @private
@@ -2376,7 +2447,7 @@ vjs.Component.prototype.readyQueue_;
2376
2447
  /**
2377
2448
  * Bind a listener to the component's ready state
2378
2449
  *
2379
- * Different from event listeners in that if the ready event has already happend
2450
+ * Different from event listeners in that if the ready event has already happened
2380
2451
  * it will trigger the function immediately.
2381
2452
  *
2382
2453
  * @param {Function} fn Ready listener
@@ -2502,7 +2573,7 @@ vjs.Component.prototype.unlockShowing = function(){
2502
2573
  /**
2503
2574
  * Disable component by making it unshowable
2504
2575
  *
2505
- * Currently private because we're movign towards more css-based states.
2576
+ * Currently private because we're moving towards more css-based states.
2506
2577
  * @private
2507
2578
  */
2508
2579
  vjs.Component.prototype.disable = function(){
@@ -2640,7 +2711,7 @@ vjs.Component.prototype.onResize;
2640
2711
  *
2641
2712
  * This is used to support toggling the controls through a tap on the video.
2642
2713
  *
2643
- * We're requireing them to be enabled because otherwise every component would
2714
+ * We're requiring them to be enabled because otherwise every component would
2644
2715
  * have this extra overhead unnecessarily, on mobile devices where extra
2645
2716
  * overhead is especially bad.
2646
2717
  * @private
@@ -2749,15 +2820,15 @@ vjs.Component.prototype.enableTouchActivity = function() {
2749
2820
  // For as long as the they are touching the device or have their mouse down,
2750
2821
  // we consider them active even if they're not moving their finger or mouse.
2751
2822
  // So we want to continue to update that they are active
2752
- clearInterval(touchHolding);
2823
+ this.clearInterval(touchHolding);
2753
2824
  // report at the same interval as activityCheck
2754
- touchHolding = setInterval(report, 250);
2825
+ touchHolding = this.setInterval(report, 250);
2755
2826
  });
2756
2827
 
2757
2828
  touchEnd = function(event) {
2758
2829
  report();
2759
2830
  // stop the interval that maintains activity if the touch is holding
2760
- clearInterval(touchHolding);
2831
+ this.clearInterval(touchHolding);
2761
2832
  };
2762
2833
 
2763
2834
  this.on('touchmove', report);
@@ -2765,6 +2836,83 @@ vjs.Component.prototype.enableTouchActivity = function() {
2765
2836
  this.on('touchcancel', touchEnd);
2766
2837
  };
2767
2838
 
2839
+ /**
2840
+ * Creates timeout and sets up disposal automatically.
2841
+ * @param {Function} fn The function to run after the timeout.
2842
+ * @param {Number} timeout Number of ms to delay before executing specified function.
2843
+ * @return {Number} Returns the timeout ID
2844
+ */
2845
+ vjs.Component.prototype.setTimeout = function(fn, timeout) {
2846
+ fn = vjs.bind(this, fn);
2847
+
2848
+ // window.setTimeout would be preferable here, but due to some bizarre issue with Sinon and/or Phantomjs, we can't.
2849
+ var timeoutId = setTimeout(fn, timeout);
2850
+
2851
+ var disposeFn = function() {
2852
+ this.clearTimeout(timeoutId);
2853
+ };
2854
+
2855
+ disposeFn.guid = 'vjs-timeout-'+ timeoutId;
2856
+
2857
+ this.on('dispose', disposeFn);
2858
+
2859
+ return timeoutId;
2860
+ };
2861
+
2862
+
2863
+ /**
2864
+ * Clears a timeout and removes the associated dispose listener
2865
+ * @param {Number} timeoutId The id of the timeout to clear
2866
+ * @return {Number} Returns the timeout ID
2867
+ */
2868
+ vjs.Component.prototype.clearTimeout = function(timeoutId) {
2869
+ clearTimeout(timeoutId);
2870
+
2871
+ var disposeFn = function(){};
2872
+ disposeFn.guid = 'vjs-timeout-'+ timeoutId;
2873
+
2874
+ this.off('dispose', disposeFn);
2875
+
2876
+ return timeoutId;
2877
+ };
2878
+
2879
+ /**
2880
+ * Creates an interval and sets up disposal automatically.
2881
+ * @param {Function} fn The function to run every N seconds.
2882
+ * @param {Number} interval Number of ms to delay before executing specified function.
2883
+ * @return {Number} Returns the interval ID
2884
+ */
2885
+ vjs.Component.prototype.setInterval = function(fn, interval) {
2886
+ fn = vjs.bind(this, fn);
2887
+
2888
+ var intervalId = setInterval(fn, interval);
2889
+
2890
+ var disposeFn = function() {
2891
+ this.clearInterval(intervalId);
2892
+ };
2893
+
2894
+ disposeFn.guid = 'vjs-interval-'+ intervalId;
2895
+
2896
+ this.on('dispose', disposeFn);
2897
+
2898
+ return intervalId;
2899
+ };
2900
+
2901
+ /**
2902
+ * Clears an interval and removes the associated dispose listener
2903
+ * @param {Number} intervalId The id of the interval to clear
2904
+ * @return {Number} Returns the interval ID
2905
+ */
2906
+ vjs.Component.prototype.clearInterval = function(intervalId) {
2907
+ clearInterval(intervalId);
2908
+
2909
+ var disposeFn = function(){};
2910
+ disposeFn.guid = 'vjs-interval-'+ intervalId;
2911
+
2912
+ this.off('dispose', disposeFn);
2913
+
2914
+ return intervalId;
2915
+ };
2768
2916
  /* Button - Base class for all buttons
2769
2917
  ================================================================================ */
2770
2918
  /**
@@ -2874,24 +3022,9 @@ vjs.Slider = vjs.Component.extend({
2874
3022
 
2875
3023
  this.on(player, 'controlsvisible', this.update);
2876
3024
  this.on(player, this.playerEvent, this.update);
2877
-
2878
- this.boundEvents = {};
2879
- this.boundEvents.move = vjs.bind(this, this.onMouseMove);
2880
- this.boundEvents.end = vjs.bind(this, this.onMouseUp);
2881
3025
  }
2882
3026
  });
2883
3027
 
2884
- vjs.Slider.prototype.dispose = function() {
2885
- vjs.off(document, 'mousemove', this.boundEvents.move, false);
2886
- vjs.off(document, 'mouseup', this.boundEvents.end, false);
2887
- vjs.off(document, 'touchmove', this.boundEvents.move, false);
2888
- vjs.off(document, 'touchend', this.boundEvents.end, false);
2889
-
2890
- vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress));
2891
-
2892
- vjs.Component.prototype.dispose.call(this);
2893
- };
2894
-
2895
3028
  vjs.Slider.prototype.createEl = function(type, props) {
2896
3029
  props = props || {};
2897
3030
  // Add the slider element class to all sub classes
@@ -2912,10 +3045,10 @@ vjs.Slider.prototype.onMouseDown = function(event){
2912
3045
  vjs.blockTextSelection();
2913
3046
  this.addClass('vjs-sliding');
2914
3047
 
2915
- vjs.on(document, 'mousemove', this.boundEvents.move);
2916
- vjs.on(document, 'mouseup', this.boundEvents.end);
2917
- vjs.on(document, 'touchmove', this.boundEvents.move);
2918
- vjs.on(document, 'touchend', this.boundEvents.end);
3048
+ this.on(document, 'mousemove', this.onMouseMove);
3049
+ this.on(document, 'mouseup', this.onMouseUp);
3050
+ this.on(document, 'touchmove', this.onMouseMove);
3051
+ this.on(document, 'touchend', this.onMouseUp);
2919
3052
 
2920
3053
  this.onMouseMove(event);
2921
3054
  };
@@ -2927,10 +3060,10 @@ vjs.Slider.prototype.onMouseUp = function() {
2927
3060
  vjs.unblockTextSelection();
2928
3061
  this.removeClass('vjs-sliding');
2929
3062
 
2930
- vjs.off(document, 'mousemove', this.boundEvents.move, false);
2931
- vjs.off(document, 'mouseup', this.boundEvents.end, false);
2932
- vjs.off(document, 'touchmove', this.boundEvents.move, false);
2933
- vjs.off(document, 'touchend', this.boundEvents.end, false);
3063
+ this.off(document, 'mousemove', this.onMouseMove);
3064
+ this.off(document, 'mouseup', this.onMouseUp);
3065
+ this.off(document, 'touchmove', this.onMouseMove);
3066
+ this.off(document, 'touchend', this.onMouseUp);
2934
3067
 
2935
3068
  this.update();
2936
3069
  };
@@ -3037,7 +3170,7 @@ vjs.Slider.prototype.calculateDistance = function(event){
3037
3170
  };
3038
3171
 
3039
3172
  vjs.Slider.prototype.onFocus = function(){
3040
- vjs.on(document, 'keyup', vjs.bind(this, this.onKeyPress));
3173
+ this.on(document, 'keydown', this.onKeyPress);
3041
3174
  };
3042
3175
 
3043
3176
  vjs.Slider.prototype.onKeyPress = function(event){
@@ -3051,7 +3184,7 @@ vjs.Slider.prototype.onKeyPress = function(event){
3051
3184
  };
3052
3185
 
3053
3186
  vjs.Slider.prototype.onBlur = function(){
3054
- vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress));
3187
+ this.off(document, 'keydown', this.onKeyPress);
3055
3188
  };
3056
3189
 
3057
3190
  /**
@@ -3205,7 +3338,7 @@ vjs.MenuButton = vjs.Button.extend({
3205
3338
  this.hide();
3206
3339
  }
3207
3340
 
3208
- this.on('keyup', this.onKeyPress);
3341
+ this.on('keydown', this.onKeyPress);
3209
3342
  this.el_.setAttribute('aria-haspopup', true);
3210
3343
  this.el_.setAttribute('role', 'button');
3211
3344
  }
@@ -3380,7 +3513,7 @@ for (var errNum = 0; errNum < vjs.MediaError.errorTypes.length; errNum++) {
3380
3513
  var apiMap, specApi, browserApi, i;
3381
3514
 
3382
3515
  /**
3383
- * Store the browser-specifc methods for the fullscreen API
3516
+ * Store the browser-specific methods for the fullscreen API
3384
3517
  * @type {Object|undefined}
3385
3518
  * @private
3386
3519
  */
@@ -3568,7 +3701,7 @@ vjs.Player = vjs.Component.extend({
3568
3701
  });
3569
3702
 
3570
3703
  /**
3571
- * The players's stored language code
3704
+ * The player's stored language code
3572
3705
  *
3573
3706
  * @type {String}
3574
3707
  * @private
@@ -3591,7 +3724,7 @@ vjs.Player.prototype.language = function (languageCode) {
3591
3724
  };
3592
3725
 
3593
3726
  /**
3594
- * The players's stored language dictionary
3727
+ * The player's stored language dictionary
3595
3728
  *
3596
3729
  * @type {Object}
3597
3730
  * @private
@@ -3775,7 +3908,7 @@ vjs.Player.prototype.createEl = function(){
3775
3908
 
3776
3909
  // /* Media Technology (tech)
3777
3910
  // ================================================================================ */
3778
- // Load/Create an instance of playback technlogy including element and API methods
3911
+ // Load/Create an instance of playback technology including element and API methods
3779
3912
  // And append playback element in player div.
3780
3913
  vjs.Player.prototype.loadTech = function(techName, source){
3781
3914
 
@@ -3914,7 +4047,7 @@ vjs.Player.prototype.onPlay = function(){
3914
4047
  };
3915
4048
 
3916
4049
  /**
3917
- * Fired whenever the media begins wating
4050
+ * Fired whenever the media begins waiting
3918
4051
  * @event waiting
3919
4052
  */
3920
4053
  vjs.Player.prototype.onWaiting = function(){
@@ -3922,7 +4055,7 @@ vjs.Player.prototype.onWaiting = function(){
3922
4055
  };
3923
4056
 
3924
4057
  /**
3925
- * A handler for events that signal that waiting has eneded
4058
+ * A handler for events that signal that waiting has ended
3926
4059
  * which is not consistent between browsers. See #1351
3927
4060
  * @private
3928
4061
  */
@@ -4012,7 +4145,7 @@ vjs.Player.prototype.onEnded = function(){
4012
4145
  * @event durationchange
4013
4146
  */
4014
4147
  vjs.Player.prototype.onDurationChange = function(){
4015
- // Allows for cacheing value instead of asking player each time.
4148
+ // Allows for caching value instead of asking player each time.
4016
4149
  // We need to get the techGet response and check for a value so we don't
4017
4150
  // accidentally cause the stack to blow up.
4018
4151
  var duration = this.techGet('duration');
@@ -4170,7 +4303,7 @@ vjs.Player.prototype.currentTime = function(seconds){
4170
4303
  // cache last currentTime and return. default to 0 seconds
4171
4304
  //
4172
4305
  // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
4173
- // currentTime when scrubbing, but may not provide much performace benefit afterall.
4306
+ // currentTime when scrubbing, but may not provide much performance benefit afterall.
4174
4307
  // Should be tested. Also something has to read the actual current time or the cache will
4175
4308
  // never get updated.
4176
4309
  return this.cache_.currentTime = (this.techGet('currentTime') || 0);
@@ -4190,7 +4323,7 @@ vjs.Player.prototype.currentTime = function(seconds){
4190
4323
  vjs.Player.prototype.duration = function(seconds){
4191
4324
  if (seconds !== undefined) {
4192
4325
 
4193
- // cache the last set value for optimiized scrubbing (esp. Flash)
4326
+ // cache the last set value for optimized scrubbing (esp. Flash)
4194
4327
  this.cache_.duration = parseFloat(seconds);
4195
4328
 
4196
4329
  return this;
@@ -4356,7 +4489,7 @@ vjs.Player.prototype.muted = function(muted){
4356
4489
  };
4357
4490
 
4358
4491
  // Check if current tech can support native fullscreen
4359
- // (e.g. with built in controls lik iOS, so not our flash swf)
4492
+ // (e.g. with built in controls like iOS, so not our flash swf)
4360
4493
  vjs.Player.prototype.supportsFullScreen = function(){
4361
4494
  return this.techGet('supportsFullScreen') || false;
4362
4495
  };
@@ -4427,7 +4560,7 @@ vjs.Player.prototype.requestFullscreen = function(){
4427
4560
 
4428
4561
  // Trigger fullscreenchange event after change
4429
4562
  // We have to specifically add this each time, and remove
4430
- // when cancelling fullscreen. Otherwise if there's multiple
4563
+ // when canceling fullscreen. Otherwise if there's multiple
4431
4564
  // players on a page, they would all be reacting to the same fullscreen
4432
4565
  // events
4433
4566
  vjs.on(document, fsApi['fullscreenchange'], vjs.bind(this, function(e){
@@ -4544,7 +4677,6 @@ vjs.Player.prototype.exitFullWindow = function(){
4544
4677
  };
4545
4678
 
4546
4679
  vjs.Player.prototype.selectSource = function(sources){
4547
-
4548
4680
  // Loop through each playback technology in the options order
4549
4681
  for (var i=0,j=this.options_['techOrder'];i<j.length;i++) {
4550
4682
  var techName = vjs.capitalize(j[i]),
@@ -4633,7 +4765,14 @@ vjs.Player.prototype.src = function(source){
4633
4765
 
4634
4766
  // wait until the tech is ready to set the source
4635
4767
  this.ready(function(){
4636
- this.techCall('src', source.src);
4768
+
4769
+ // The setSource tech method was added with source handlers
4770
+ // so older techs won't support it
4771
+ if (this.tech['setSource']) {
4772
+ this.techCall('setSource', source);
4773
+ } else {
4774
+ this.techCall('src', source.src);
4775
+ }
4637
4776
 
4638
4777
  if (this.options_['preload'] == 'auto') {
4639
4778
  this.load();
@@ -4655,8 +4794,7 @@ vjs.Player.prototype.src = function(source){
4655
4794
  * @private
4656
4795
  */
4657
4796
  vjs.Player.prototype.sourceList_ = function(sources){
4658
- var sourceTech = this.selectSource(sources),
4659
- errorTimeout;
4797
+ var sourceTech = this.selectSource(sources);
4660
4798
 
4661
4799
  if (sourceTech) {
4662
4800
  if (sourceTech.tech === this.techName) {
@@ -4668,17 +4806,13 @@ vjs.Player.prototype.sourceList_ = function(sources){
4668
4806
  }
4669
4807
  } else {
4670
4808
  // We need to wrap this in a timeout to give folks a chance to add error event handlers
4671
- errorTimeout = setTimeout(vjs.bind(this, function() {
4809
+ this.setTimeout( function() {
4672
4810
  this.error({ code: 4, message: this.localize(this.options()['notSupportedMessage']) });
4673
- }), 0);
4811
+ }, 0);
4674
4812
 
4675
4813
  // we could not find an appropriate tech, but let's still notify the delegate that this is it
4676
4814
  // this needs a better comment about why this is needed
4677
4815
  this.triggerReady();
4678
-
4679
- this.on('dispose', function() {
4680
- clearTimeout(errorTimeout);
4681
- });
4682
4816
  }
4683
4817
  };
4684
4818
 
@@ -5009,17 +5143,17 @@ vjs.Player.prototype.listenForUserActivity = function(){
5009
5143
  // For as long as the they are touching the device or have their mouse down,
5010
5144
  // we consider them active even if they're not moving their finger or mouse.
5011
5145
  // So we want to continue to update that they are active
5012
- clearInterval(mouseInProgress);
5146
+ this.clearInterval(mouseInProgress);
5013
5147
  // Setting userActivity=true now and setting the interval to the same time
5014
5148
  // as the activityCheck interval (250) should ensure we never miss the
5015
5149
  // next activityCheck
5016
- mouseInProgress = setInterval(onActivity, 250);
5150
+ mouseInProgress = this.setInterval(onActivity, 250);
5017
5151
  };
5018
5152
 
5019
5153
  onMouseUp = function(event) {
5020
5154
  onActivity();
5021
5155
  // Stop the interval that maintains activity if the mouse/touch is down
5022
- clearInterval(mouseInProgress);
5156
+ this.clearInterval(mouseInProgress);
5023
5157
  };
5024
5158
 
5025
5159
  // Any mouse movement will be considered user activity
@@ -5037,7 +5171,7 @@ vjs.Player.prototype.listenForUserActivity = function(){
5037
5171
  // `this.reportUserActivity` simply sets this.userActivity_ to true, which
5038
5172
  // then gets picked up by this loop
5039
5173
  // http://ejohn.org/blog/learning-from-twitter/
5040
- activityCheck = setInterval(vjs.bind(this, function() {
5174
+ activityCheck = this.setInterval(function() {
5041
5175
  // Check to see if mouse/touch activity has happened
5042
5176
  if (this.userActivity_) {
5043
5177
  // Reset the activity tracker
@@ -5047,29 +5181,23 @@ vjs.Player.prototype.listenForUserActivity = function(){
5047
5181
  this.userActive(true);
5048
5182
 
5049
5183
  // Clear any existing inactivity timeout to start the timer over
5050
- clearTimeout(inactivityTimeout);
5184
+ this.clearTimeout(inactivityTimeout);
5051
5185
 
5052
5186
  var timeout = this.options()['inactivityTimeout'];
5053
5187
  if (timeout > 0) {
5054
5188
  // In <timeout> milliseconds, if no more activity has occurred the
5055
5189
  // user will be considered inactive
5056
- inactivityTimeout = setTimeout(vjs.bind(this, function () {
5190
+ inactivityTimeout = this.setTimeout(function () {
5057
5191
  // Protect against the case where the inactivityTimeout can trigger just
5058
5192
  // before the next user activity is picked up by the activityCheck loop
5059
5193
  // causing a flicker
5060
5194
  if (!this.userActivity_) {
5061
5195
  this.userActive(false);
5062
5196
  }
5063
- }), timeout);
5197
+ }, timeout);
5064
5198
  }
5065
5199
  }
5066
- }), 250);
5067
-
5068
- // Clean up the intervals when we kill the player
5069
- this.on('dispose', function(){
5070
- clearInterval(activityCheck);
5071
- clearTimeout(inactivityTimeout);
5072
- });
5200
+ }, 250);
5073
5201
  };
5074
5202
 
5075
5203
  /**
@@ -5567,13 +5695,13 @@ vjs.LoadProgressBar.prototype.update = function(){
5567
5695
  part = children[i];
5568
5696
 
5569
5697
  if (!part) {
5570
- part = this.el_.appendChild(vjs.createEl())
5571
- };
5698
+ part = this.el_.appendChild(vjs.createEl());
5699
+ }
5572
5700
 
5573
5701
  // set the percent based on the width of the progress bar (bufferedEnd)
5574
5702
  part.style.left = percentify(start, bufferedEnd);
5575
5703
  part.style.width = percentify(end - start, bufferedEnd);
5576
- };
5704
+ }
5577
5705
 
5578
5706
  // remove unused buffered range elements
5579
5707
  for (i = children.length; i > buffered.length; i--) {
@@ -5948,7 +6076,7 @@ vjs.PlaybackRateMenuButton.prototype.createMenu = function(){
5948
6076
  menu.addChild(
5949
6077
  new vjs.PlaybackRateMenuItem(this.player(), { 'rate': rates[i] + 'x'})
5950
6078
  );
5951
- };
6079
+ }
5952
6080
  }
5953
6081
 
5954
6082
  return menu;
@@ -5970,7 +6098,7 @@ vjs.PlaybackRateMenuButton.prototype.onClick = function(){
5970
6098
  newRate = rates[i];
5971
6099
  break;
5972
6100
  }
5973
- };
6101
+ }
5974
6102
  this.player().playbackRate(newRate);
5975
6103
  };
5976
6104
 
@@ -6250,7 +6378,7 @@ vjs.MediaTechController = vjs.Component.extend({
6250
6378
  this.manualProgressOn();
6251
6379
  }
6252
6380
 
6253
- // Manually track timeudpates in cases where the browser/flash player doesn't report it.
6381
+ // Manually track timeupdates in cases where the browser/flash player doesn't report it.
6254
6382
  if (!this['featuresTimeupdateEvents']) {
6255
6383
  this.manualTimeUpdatesOn();
6256
6384
  }
@@ -6404,8 +6532,7 @@ vjs.MediaTechController.prototype.manualProgressOff = function(){
6404
6532
  };
6405
6533
 
6406
6534
  vjs.MediaTechController.prototype.trackProgress = function(){
6407
-
6408
- this.progressInterval = setInterval(vjs.bind(this, function(){
6535
+ this.progressInterval = this.setInterval(function(){
6409
6536
  // Don't trigger unless buffered amount is greater than last time
6410
6537
 
6411
6538
  var bufferedPercent = this.player().bufferedPercent();
@@ -6419,9 +6546,9 @@ vjs.MediaTechController.prototype.trackProgress = function(){
6419
6546
  if (bufferedPercent === 1) {
6420
6547
  this.stopTrackingProgress();
6421
6548
  }
6422
- }), 500);
6549
+ }, 500);
6423
6550
  };
6424
- vjs.MediaTechController.prototype.stopTrackingProgress = function(){ clearInterval(this.progressInterval); };
6551
+ vjs.MediaTechController.prototype.stopTrackingProgress = function(){ this.clearInterval(this.progressInterval); };
6425
6552
 
6426
6553
  /*! Time Tracking -------------------------------------------------------------- */
6427
6554
  vjs.MediaTechController.prototype.manualTimeUpdatesOn = function(){
@@ -6451,14 +6578,14 @@ vjs.MediaTechController.prototype.manualTimeUpdatesOff = function(){
6451
6578
 
6452
6579
  vjs.MediaTechController.prototype.trackCurrentTime = function(){
6453
6580
  if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); }
6454
- this.currentTimeInterval = setInterval(vjs.bind(this, function(){
6581
+ this.currentTimeInterval = this.setInterval(function(){
6455
6582
  this.player().trigger('timeupdate');
6456
- }), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
6583
+ }, 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
6457
6584
  };
6458
6585
 
6459
6586
  // Turn off play progress tracking (when paused or dragging)
6460
6587
  vjs.MediaTechController.prototype.stopTrackingCurrentTime = function(){
6461
- clearInterval(this.currentTimeInterval);
6588
+ this.clearInterval(this.currentTimeInterval);
6462
6589
 
6463
6590
  // #1002 - if the video ends right before the next timeupdate would happen,
6464
6591
  // the progress bar won't make it all the way to the end
@@ -6498,7 +6625,106 @@ vjs.MediaTechController.prototype['featuresPlaybackRate'] = false;
6498
6625
  vjs.MediaTechController.prototype['featuresProgressEvents'] = false;
6499
6626
  vjs.MediaTechController.prototype['featuresTimeupdateEvents'] = false;
6500
6627
 
6501
- vjs.media = {};
6628
+ /**
6629
+ * A functional mixin for techs that want to use the Source Handler pattern.
6630
+ *
6631
+ * ##### EXAMPLE:
6632
+ *
6633
+ * videojs.MediaTechController.withSourceHandlers.call(MyTech);
6634
+ *
6635
+ */
6636
+ vjs.MediaTechController.withSourceHandlers = function(Tech){
6637
+ /**
6638
+ * Register a source handler
6639
+ * Source handlers are scripts for handling specific formats.
6640
+ * The source handler pattern is used for adaptive formats (HLS, DASH) that
6641
+ * manually load video data and feed it into a Source Buffer (Media Source Extensions)
6642
+ * @param {Function} handler The source handler
6643
+ * @param {Boolean} first Register it before any existing handlers
6644
+ */
6645
+ Tech.registerSourceHandler = function(handler, index){
6646
+ var handlers = Tech.sourceHandlers;
6647
+
6648
+ if (!handlers) {
6649
+ handlers = Tech.sourceHandlers = [];
6650
+ }
6651
+
6652
+ if (index === undefined) {
6653
+ // add to the end of the list
6654
+ index = handlers.length;
6655
+ }
6656
+
6657
+ handlers.splice(index, 0, handler);
6658
+ };
6659
+
6660
+ /**
6661
+ * Return the first source handler that supports the source
6662
+ * TODO: Answer question: should 'probably' be prioritized over 'maybe'
6663
+ * @param {Object} source The source object
6664
+ * @returns {Object} The first source handler that supports the source
6665
+ * @returns {null} Null if no source handler is found
6666
+ */
6667
+ Tech.selectSourceHandler = function(source){
6668
+ var handlers = Tech.sourceHandlers || [],
6669
+ can;
6670
+
6671
+ for (var i = 0; i < handlers.length; i++) {
6672
+ can = handlers[i].canHandleSource(source);
6673
+
6674
+ if (can) {
6675
+ return handlers[i];
6676
+ }
6677
+ }
6678
+
6679
+ return null;
6680
+ };
6681
+
6682
+ /**
6683
+ * Check if the tech can support the given source
6684
+ * @param {Object} srcObj The source object
6685
+ * @return {String} 'probably', 'maybe', or '' (empty string)
6686
+ */
6687
+ Tech.canPlaySource = function(srcObj){
6688
+ var sh = Tech.selectSourceHandler(srcObj);
6689
+
6690
+ if (sh) {
6691
+ return sh.canHandleSource(srcObj);
6692
+ }
6693
+
6694
+ return '';
6695
+ };
6696
+
6697
+ /**
6698
+ * Create a function for setting the source using a source object
6699
+ * and source handlers.
6700
+ * Should never be called unless a source handler was found.
6701
+ * @param {Object} source A source object with src and type keys
6702
+ * @return {vjs.MediaTechController} self
6703
+ */
6704
+ Tech.prototype.setSource = function(source){
6705
+ var sh = Tech.selectSourceHandler(source);
6706
+
6707
+ // Dispose any existing source handler
6708
+ this.disposeSourceHandler();
6709
+ this.off('dispose', this.disposeSourceHandler);
6710
+
6711
+ this.currentSource_ = source;
6712
+ this.sourceHandler_ = sh.handleSource(source, this);
6713
+ this.on('dispose', this.disposeSourceHandler);
6714
+
6715
+ return this;
6716
+ };
6717
+
6718
+ /**
6719
+ * Clean up any existing source handler
6720
+ */
6721
+ Tech.prototype.disposeSourceHandler = function(){
6722
+ if (this.sourceHandler_ && this.sourceHandler_.dispose) {
6723
+ this.sourceHandler_.dispose();
6724
+ }
6725
+ };
6726
+
6727
+ };
6502
6728
  /**
6503
6729
  * @fileoverview HTML5 Media Controller - Wrapper for HTML5 Media API
6504
6730
  */
@@ -6513,22 +6739,8 @@ vjs.media = {};
6513
6739
  vjs.Html5 = vjs.MediaTechController.extend({
6514
6740
  /** @constructor */
6515
6741
  init: function(player, options, ready){
6516
- // volume cannot be changed from 1 on iOS
6517
- this['featuresVolumeControl'] = vjs.Html5.canControlVolume();
6518
-
6519
- // just in case; or is it excessively...
6520
- this['featuresPlaybackRate'] = vjs.Html5.canControlPlaybackRate();
6521
-
6522
- // In iOS, if you move a video element in the DOM, it breaks video playback.
6523
- this['movingMediaElementInDOM'] = !vjs.IS_IOS;
6524
-
6525
- // HTML video is able to automatically resize when going to fullscreen
6526
- this['featuresFullscreenResize'] = true;
6527
-
6528
- // HTML video supports progress events
6529
- this['featuresProgressEvents'] = true;
6530
-
6531
6742
  vjs.MediaTechController.call(this, player, options, ready);
6743
+
6532
6744
  this.setupTriggers();
6533
6745
 
6534
6746
  var source = options['source'];
@@ -6537,8 +6749,8 @@ vjs.Html5 = vjs.MediaTechController.extend({
6537
6749
  // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
6538
6750
  // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
6539
6751
  // anyway so the error gets fired.
6540
- if (source && (this.el_.currentSrc !== source.src) || (player.tag && player.tag.initNetworkState_ === 3)) {
6541
- this.el_.src = source.src;
6752
+ if (source && (this.el_.currentSrc !== source.src || (player.tag && player.tag.initNetworkState_ === 3))) {
6753
+ this.setSource(source);
6542
6754
  }
6543
6755
 
6544
6756
  // Determine if native controls should be used
@@ -6734,7 +6946,7 @@ vjs.Html5.prototype.enterFullScreen = function(){
6734
6946
 
6735
6947
  // playing and pausing synchronously during the transition to fullscreen
6736
6948
  // can get iOS ~6.1 devices into a play/pause loop
6737
- setTimeout(function(){
6949
+ this.setTimeout(function(){
6738
6950
  video.pause();
6739
6951
  video.webkitEnterFullScreen();
6740
6952
  }, 0);
@@ -6742,16 +6954,25 @@ vjs.Html5.prototype.enterFullScreen = function(){
6742
6954
  video.webkitEnterFullScreen();
6743
6955
  }
6744
6956
  };
6957
+
6745
6958
  vjs.Html5.prototype.exitFullScreen = function(){
6746
6959
  this.el_.webkitExitFullScreen();
6747
6960
  };
6961
+
6962
+
6748
6963
  vjs.Html5.prototype.src = function(src) {
6749
6964
  if (src === undefined) {
6750
6965
  return this.el_.src;
6751
6966
  } else {
6752
- this.el_.src = src;
6967
+ // Setting src through `src` instead of `setSrc` will be deprecated
6968
+ this.setSrc(src);
6753
6969
  }
6754
6970
  };
6971
+
6972
+ vjs.Html5.prototype.setSrc = function(src) {
6973
+ this.el_.src = src;
6974
+ };
6975
+
6755
6976
  vjs.Html5.prototype.load = function(){ this.el_.load(); };
6756
6977
  vjs.Html5.prototype.currentSrc = function(){ return this.el_.currentSrc; };
6757
6978
 
@@ -6780,10 +7001,13 @@ vjs.Html5.prototype.setPlaybackRate = function(val){ this.el_.playbackRate = val
6780
7001
 
6781
7002
  vjs.Html5.prototype.networkState = function(){ return this.el_.networkState; };
6782
7003
 
6783
- /* HTML5 Support Testing ---------------------------------------------------- */
6784
7004
 
7005
+ /**
7006
+ * Check if HTML5 video is supported by this browser/device
7007
+ * @return {Boolean}
7008
+ */
6785
7009
  vjs.Html5.isSupported = function(){
6786
- // ie9 with no Media Player is a LIAR! (#984)
7010
+ // IE9 with no Media Player is a LIAR! (#984)
6787
7011
  try {
6788
7012
  vjs.TEST_VID['volume'] = 0.5;
6789
7013
  } catch (e) {
@@ -6793,31 +7017,119 @@ vjs.Html5.isSupported = function(){
6793
7017
  return !!vjs.TEST_VID.canPlayType;
6794
7018
  };
6795
7019
 
6796
- vjs.Html5.canPlaySource = function(srcObj){
6797
- // IE9 on Windows 7 without MediaPlayer throws an error here
6798
- // https://github.com/videojs/video.js/issues/519
6799
- try {
6800
- return !!vjs.TEST_VID.canPlayType(srcObj.type);
6801
- } catch(e) {
6802
- return '';
7020
+ // Add Source Handler pattern functions to this tech
7021
+ vjs.MediaTechController.withSourceHandlers(vjs.Html5);
7022
+
7023
+ /**
7024
+ * The default native source handler.
7025
+ * This simply passes the source to the video element. Nothing fancy.
7026
+ * @param {Object} source The source object
7027
+ * @param {vjs.Html5} tech The instance of the HTML5 tech
7028
+ */
7029
+ vjs.Html5.nativeSourceHandler = {};
7030
+
7031
+ /**
7032
+ * Check if the video element can handle the source natively
7033
+ * @param {Object} source The source object
7034
+ * @return {String} 'probably', 'maybe', or '' (empty string)
7035
+ */
7036
+ vjs.Html5.nativeSourceHandler.canHandleSource = function(source){
7037
+ var ext;
7038
+
7039
+ function canPlayType(type){
7040
+ // IE9 on Windows 7 without MediaPlayer throws an error here
7041
+ // https://github.com/videojs/video.js/issues/519
7042
+ try {
7043
+ return !!vjs.TEST_VID.canPlayType(type);
7044
+ } catch(e) {
7045
+ return '';
7046
+ }
7047
+ }
7048
+
7049
+ // If a type was provided we should rely on that
7050
+ if (source.type) {
7051
+ return canPlayType(source.type);
7052
+ } else {
7053
+ // If no type, fall back to checking 'video/[EXTENSION]'
7054
+ ext = source.src.match(/\.([^\/\?]+)(\?[^\/]+)?$/i)[1];
7055
+ return canPlayType('video/'+ext);
6803
7056
  }
6804
- // TODO: Check Type
6805
- // If no Type, check ext
6806
- // Check Media Type
6807
7057
  };
6808
7058
 
7059
+ /**
7060
+ * Pass the source to the video element
7061
+ * Adaptive source handlers will have more complicated workflows before passing
7062
+ * video data to the video element
7063
+ * @param {Object} source The source object
7064
+ * @param {vjs.Html5} tech The instance of the Html5 tech
7065
+ */
7066
+ vjs.Html5.nativeSourceHandler.handleSource = function(source, tech){
7067
+ tech.setSrc(source.src);
7068
+ };
7069
+
7070
+ /**
7071
+ * Clean up the source handler when disposing the player or switching sources..
7072
+ * (no cleanup is needed when supporting the format natively)
7073
+ */
7074
+ vjs.Html5.nativeSourceHandler.dispose = function(){};
7075
+
7076
+ // Register the native source handler
7077
+ vjs.Html5.registerSourceHandler(vjs.Html5.nativeSourceHandler);
7078
+
7079
+ /**
7080
+ * Check if the volume can be changed in this browser/device.
7081
+ * Volume cannot be changed in a lot of mobile devices.
7082
+ * Specifically, it can't be changed from 1 on iOS.
7083
+ * @return {Boolean}
7084
+ */
6809
7085
  vjs.Html5.canControlVolume = function(){
6810
7086
  var volume = vjs.TEST_VID.volume;
6811
7087
  vjs.TEST_VID.volume = (volume / 2) + 0.1;
6812
7088
  return volume !== vjs.TEST_VID.volume;
6813
7089
  };
6814
7090
 
7091
+ /**
7092
+ * Check if playbackRate is supported in this browser/device.
7093
+ * @return {[type]} [description]
7094
+ */
6815
7095
  vjs.Html5.canControlPlaybackRate = function(){
6816
7096
  var playbackRate = vjs.TEST_VID.playbackRate;
6817
7097
  vjs.TEST_VID.playbackRate = (playbackRate / 2) + 0.1;
6818
7098
  return playbackRate !== vjs.TEST_VID.playbackRate;
6819
7099
  };
6820
7100
 
7101
+ /**
7102
+ * Set the tech's volume control support status
7103
+ * @type {Boolean}
7104
+ */
7105
+ vjs.Html5.prototype['featuresVolumeControl'] = vjs.Html5.canControlVolume();
7106
+
7107
+ /**
7108
+ * Set the tech's playbackRate support status
7109
+ * @type {Boolean}
7110
+ */
7111
+ vjs.Html5.prototype['featuresPlaybackRate'] = vjs.Html5.canControlPlaybackRate();
7112
+
7113
+ /**
7114
+ * Set the tech's status on moving the video element.
7115
+ * In iOS, if you move a video element in the DOM, it breaks video playback.
7116
+ * @type {Boolean}
7117
+ */
7118
+ vjs.Html5.prototype['movingMediaElementInDOM'] = !vjs.IS_IOS;
7119
+
7120
+ /**
7121
+ * Set the the tech's fullscreen resize support status.
7122
+ * HTML video is able to automatically resize when going to fullscreen.
7123
+ * (No longer appears to be used. Can probably be removed.)
7124
+ */
7125
+ vjs.Html5.prototype['featuresFullscreenResize'] = true;
7126
+
7127
+ /**
7128
+ * Set the tech's progress event support status
7129
+ * (this disables the manual progress events of the MediaTechController)
7130
+ */
7131
+ vjs.Html5.prototype['featuresProgressEvents'] = true;
7132
+
6821
7133
  // HTML5 Feature detection and Device Fixes --------------------------------- //
6822
7134
  (function() {
6823
7135
  var canPlayType,
@@ -6959,21 +7271,16 @@ vjs.Flash = vjs.MediaTechController.extend({
6959
7271
  // Merge default attributes with ones passed in
6960
7272
  attributes = vjs.obj.merge({
6961
7273
  'id': objId,
6962
- 'name': objId, // Both ID and Name needed or swf to identifty itself
7274
+ 'name': objId, // Both ID and Name needed or swf to identify itself
6963
7275
  'class': 'vjs-tech'
6964
7276
  }, options['attributes'])
6965
7277
  ;
6966
7278
 
6967
7279
  // If source was supplied pass as a flash var.
6968
7280
  if (source) {
6969
- if (source.type && vjs.Flash.isStreamingType(source.type)) {
6970
- var parts = vjs.Flash.streamToParts(source.src);
6971
- flashVars['rtmpConnection'] = encodeURIComponent(parts.connection);
6972
- flashVars['rtmpStream'] = encodeURIComponent(parts.stream);
6973
- }
6974
- else {
6975
- flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
6976
- }
7281
+ this.ready(function(){
7282
+ this.setSource(source);
7283
+ });
6977
7284
  }
6978
7285
 
6979
7286
  // Add placeholder to player div
@@ -7025,21 +7332,20 @@ vjs.Flash.prototype.src = function(src){
7025
7332
  return this['currentSrc']();
7026
7333
  }
7027
7334
 
7028
- if (vjs.Flash.isStreamingSrc(src)) {
7029
- src = vjs.Flash.streamToParts(src);
7030
- this.setRtmpConnection(src.connection);
7031
- this.setRtmpStream(src.stream);
7032
- } else {
7033
- // Make sure source URL is abosolute.
7034
- src = vjs.getAbsoluteURL(src);
7035
- this.el_.vjs_src(src);
7036
- }
7335
+ // Setting src through `src` not `setSrc` will be deprecated
7336
+ return this.setSrc(src);
7337
+ };
7338
+
7339
+ vjs.Flash.prototype.setSrc = function(src){
7340
+ // Make sure source URL is absolute.
7341
+ src = vjs.getAbsoluteURL(src);
7342
+ this.el_.vjs_src(src);
7037
7343
 
7038
7344
  // Currently the SWF doesn't autoplay if you load a source later.
7039
7345
  // e.g. Load player w/ no source, wait 2s, set src.
7040
7346
  if (this.player_.autoplay()) {
7041
7347
  var tech = this;
7042
- setTimeout(function(){ tech.play(); }, 0);
7348
+ this.setTimeout(function(){ tech.play(); }, 0);
7043
7349
  }
7044
7350
  };
7045
7351
 
@@ -7059,17 +7365,11 @@ vjs.Flash.prototype['currentTime'] = function(time){
7059
7365
  };
7060
7366
 
7061
7367
  vjs.Flash.prototype['currentSrc'] = function(){
7062
- var src = this.el_.vjs_getProperty('currentSrc');
7063
- // no src, check and see if RTMP
7064
- if (src == null) {
7065
- var connection = this['rtmpConnection'](),
7066
- stream = this['rtmpStream']();
7067
-
7068
- if (connection && stream) {
7069
- src = vjs.Flash.streamFromParts(connection, stream);
7070
- }
7368
+ if (this.currentSource_) {
7369
+ return this.currentSource_.src;
7370
+ } else {
7371
+ return this.el_.vjs_getProperty('currentSrc');
7071
7372
  }
7072
- return src;
7073
7373
  };
7074
7374
 
7075
7375
  vjs.Flash.prototype.load = function(){
@@ -7106,10 +7406,10 @@ vjs.Flash.prototype.enterFullScreen = function(){
7106
7406
  function createSetter(attr){
7107
7407
  var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
7108
7408
  api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); };
7109
- };
7409
+ }
7110
7410
  function createGetter(attr) {
7111
7411
  api[attr] = function(){ return this.el_.vjs_getProperty(attr); };
7112
- };
7412
+ }
7113
7413
 
7114
7414
  // Create getter and setters for all read/write attributes
7115
7415
  for (i = 0; i < readWrite.length; i++) {
@@ -7130,19 +7430,59 @@ vjs.Flash.isSupported = function(){
7130
7430
  // return swfobject.hasFlashPlayerVersion('10');
7131
7431
  };
7132
7432
 
7133
- vjs.Flash.canPlaySource = function(srcObj){
7433
+ // Add Source Handler pattern functions to this tech
7434
+ vjs.MediaTechController.withSourceHandlers(vjs.Flash);
7435
+
7436
+ /**
7437
+ * The default native source handler.
7438
+ * This simply passes the source to the video element. Nothing fancy.
7439
+ * @param {Object} source The source object
7440
+ * @param {vjs.Flash} tech The instance of the Flash tech
7441
+ */
7442
+ vjs.Flash.nativeSourceHandler = {};
7443
+
7444
+ /**
7445
+ * Check Flash can handle the source natively
7446
+ * @param {Object} source The source object
7447
+ * @return {String} 'probably', 'maybe', or '' (empty string)
7448
+ */
7449
+ vjs.Flash.nativeSourceHandler.canHandleSource = function(source){
7134
7450
  var type;
7135
7451
 
7136
- if (!srcObj.type) {
7452
+ if (!source.type) {
7137
7453
  return '';
7138
7454
  }
7139
7455
 
7140
- type = srcObj.type.replace(/;.*/,'').toLowerCase();
7141
- if (type in vjs.Flash.formats || type in vjs.Flash.streamingFormats) {
7456
+ // Strip code information from the type because we don't get that specific
7457
+ type = source.type.replace(/;.*/,'').toLowerCase();
7458
+
7459
+ if (type in vjs.Flash.formats) {
7142
7460
  return 'maybe';
7143
7461
  }
7462
+
7463
+ return '';
7144
7464
  };
7145
7465
 
7466
+ /**
7467
+ * Pass the source to the flash object
7468
+ * Adaptive source handlers will have more complicated workflows before passing
7469
+ * video data to the video element
7470
+ * @param {Object} source The source object
7471
+ * @param {vjs.Flash} tech The instance of the Flash tech
7472
+ */
7473
+ vjs.Flash.nativeSourceHandler.handleSource = function(source, tech){
7474
+ tech.setSrc(source.src);
7475
+ };
7476
+
7477
+ /**
7478
+ * Clean up the source handler when disposing the player or switching sources..
7479
+ * (no cleanup is needed when supporting the format natively)
7480
+ */
7481
+ vjs.Flash.nativeSourceHandler.dispose = function(){};
7482
+
7483
+ // Register the native source handler
7484
+ vjs.Flash.registerSourceHandler(vjs.Flash.nativeSourceHandler);
7485
+
7146
7486
  vjs.Flash.formats = {
7147
7487
  'video/flv': 'FLV',
7148
7488
  'video/x-flv': 'FLV',
@@ -7150,11 +7490,6 @@ vjs.Flash.formats = {
7150
7490
  'video/m4v': 'MP4'
7151
7491
  };
7152
7492
 
7153
- vjs.Flash.streamingFormats = {
7154
- 'rtmp/mp4': 'MP4',
7155
- 'rtmp/flv': 'FLV'
7156
- };
7157
-
7158
7493
  vjs.Flash['onReady'] = function(currSwf){
7159
7494
  var el, player;
7160
7495
 
@@ -7187,7 +7522,7 @@ vjs.Flash['checkReady'] = function(tech){
7187
7522
  tech.triggerReady();
7188
7523
  } else {
7189
7524
  // wait longer
7190
- setTimeout(function(){
7525
+ this.setTimeout(function(){
7191
7526
  vjs.Flash['checkReady'](tech);
7192
7527
  }, 50);
7193
7528
  }
@@ -7257,7 +7592,7 @@ vjs.Flash.embed = function(swf, placeHolder, flashVars, params, attributes){
7257
7592
 
7258
7593
  vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){
7259
7594
 
7260
- var objTag = '<object type="application/x-shockwave-flash"',
7595
+ var objTag = '<object type="application/x-shockwave-flash" ',
7261
7596
  flashVarsString = '',
7262
7597
  paramsString = '',
7263
7598
  attrsString = '';
@@ -7299,6 +7634,10 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){
7299
7634
 
7300
7635
  return objTag + attrsString + '>' + paramsString + '</object>';
7301
7636
  };
7637
+ vjs.Flash.streamingFormats = {
7638
+ 'rtmp/mp4': 'MP4',
7639
+ 'rtmp/flv': 'FLV'
7640
+ };
7302
7641
 
7303
7642
  vjs.Flash.streamFromParts = function(connection, stream) {
7304
7643
  return connection + '&' + stream;
@@ -7347,6 +7686,42 @@ vjs.Flash.RTMP_RE = /^rtmp[set]?:\/\//i;
7347
7686
  vjs.Flash.isStreamingSrc = function(src) {
7348
7687
  return vjs.Flash.RTMP_RE.test(src);
7349
7688
  };
7689
+
7690
+ /**
7691
+ * A source handler for RTMP urls
7692
+ * @type {Object}
7693
+ */
7694
+ vjs.Flash.rtmpSourceHandler = {};
7695
+
7696
+ /**
7697
+ * Check Flash can handle the source natively
7698
+ * @param {Object} source The source object
7699
+ * @return {String} 'probably', 'maybe', or '' (empty string)
7700
+ */
7701
+ vjs.Flash.rtmpSourceHandler.canHandleSource = function(source){
7702
+ if (vjs.Flash.isStreamingType(source.type) || vjs.Flash.isStreamingSrc(source.src)) {
7703
+ return 'maybe';
7704
+ }
7705
+
7706
+ return '';
7707
+ };
7708
+
7709
+ /**
7710
+ * Pass the source to the flash object
7711
+ * Adaptive source handlers will have more complicated workflows before passing
7712
+ * video data to the video element
7713
+ * @param {Object} source The source object
7714
+ * @param {vjs.Flash} tech The instance of the Flash tech
7715
+ */
7716
+ vjs.Flash.rtmpSourceHandler.handleSource = function(source, tech){
7717
+ var srcParts = vjs.Flash.streamToParts(source.src);
7718
+
7719
+ tech.setRtmpConnection(srcParts.connection);
7720
+ tech.setRtmpStream(srcParts.stream);
7721
+ };
7722
+
7723
+ // Register the native source handler
7724
+ vjs.Flash.registerSourceHandler(vjs.Flash.rtmpSourceHandler);
7350
7725
  /**
7351
7726
  * The Media Loader is the component that decides which playback technology to load
7352
7727
  * when the player is initialized.
@@ -7383,8 +7758,8 @@ vjs.MediaLoader = vjs.Component.extend({
7383
7758
  /**
7384
7759
  * @fileoverview Text Tracks
7385
7760
  * Text tracks are tracks of timed text events.
7386
- * Captions - text displayed over the video for the hearing impared
7387
- * Subtitles - text displayed over the video for those who don't understand langauge in the video
7761
+ * Captions - text displayed over the video for the hearing impaired
7762
+ * Subtitles - text displayed over the video for those who don't understand language in the video
7388
7763
  * Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video
7389
7764
  * Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device
7390
7765
  */
@@ -7437,14 +7812,14 @@ vjs.Player.prototype.addTextTrack = function(kind, label, language, options){
7437
7812
  tracks.push(track);
7438
7813
 
7439
7814
  // If track.dflt() is set, start showing immediately
7440
- // TODO: Add a process to deterime the best track to show for the specific kind
7441
- // Incase there are mulitple defaulted tracks of the same kind
7815
+ // TODO: Add a process to determine the best track to show for the specific kind
7816
+ // In case there are multiple defaulted tracks of the same kind
7442
7817
  // Or the user has a set preference of a specific language that should override the default
7443
7818
  // Note: The setTimeout is a workaround because with the html5 tech, the player is 'ready'
7444
7819
  // before it's child components (including the textTrackDisplay) have finished loading.
7445
7820
  if (track.dflt()) {
7446
7821
  this.ready(function(){
7447
- setTimeout(function(){
7822
+ this.setTimeout(function(){
7448
7823
  track.player().showTextTrack(track.id());
7449
7824
  }, 0);
7450
7825
  });
@@ -7531,6 +7906,8 @@ vjs.TextTrack = vjs.Component.extend({
7531
7906
  this.activeCues_ = [];
7532
7907
  this.readyState_ = 0;
7533
7908
  this.mode_ = 0;
7909
+
7910
+ player.on('dispose', vjs.bind(this, this.deactivate, this.id_));
7534
7911
  }
7535
7912
  });
7536
7913
 
@@ -7804,7 +8181,13 @@ vjs.TextTrack.prototype.load = function(){
7804
8181
  // Only load if not loaded yet.
7805
8182
  if (this.readyState_ === 0) {
7806
8183
  this.readyState_ = 1;
7807
- vjs.get(this.src_, vjs.bind(this, this.parseCues), vjs.bind(this, this.onError));
8184
+ vjs.xhr(this.src_, vjs.bind(function(err, response, responseBody){
8185
+ if (err) {
8186
+ return this.onError(err);
8187
+ }
8188
+
8189
+ this.parseCues(responseBody);
8190
+ }));
7808
8191
  }
7809
8192
 
7810
8193
  };
@@ -7854,7 +8237,7 @@ vjs.TextTrack.prototype.parseCues = function(srcContent) {
7854
8237
  text = [];
7855
8238
 
7856
8239
  // Loop until a blank line or end of lines
7857
- // Assumeing trim('') returns false for blank lines
8240
+ // Assuming trim('') returns false for blank lines
7858
8241
  while (lines[++i] && (line = vjs.trim(lines[i]))) {
7859
8242
  text.push(line);
7860
8243
  }