videojs_rails 4.10.2 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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
  }