videojs_rails 4.11.4 → 4.12.0
Sign up to get free protection for your applications and to get access to all the features.
@@ -37,6 +37,16 @@ var vjs = function(id, options, ready){
|
|
37
37
|
|
38
38
|
// If a player instance has already been created for this ID return it.
|
39
39
|
if (vjs.players[id]) {
|
40
|
+
|
41
|
+
// If options or ready funtion are passed, warn
|
42
|
+
if (options) {
|
43
|
+
vjs.log.warn ('Player "' + id + '" is already initialised. Options will not be applied.');
|
44
|
+
}
|
45
|
+
|
46
|
+
if (ready) {
|
47
|
+
vjs.players[id].ready(ready);
|
48
|
+
}
|
49
|
+
|
40
50
|
return vjs.players[id];
|
41
51
|
|
42
52
|
// Otherwise get element for ID
|
@@ -63,9 +73,15 @@ var vjs = function(id, options, ready){
|
|
63
73
|
var videojs = window['videojs'] = vjs;
|
64
74
|
|
65
75
|
// CDN Version. Used to target right flash swf.
|
66
|
-
vjs.CDN_VERSION = '4.
|
76
|
+
vjs.CDN_VERSION = '4.12';
|
67
77
|
vjs.ACCESS_PROTOCOL = ('https:' == document.location.protocol ? 'https://' : 'http://');
|
68
78
|
|
79
|
+
/**
|
80
|
+
* Full player version
|
81
|
+
* @type {string}
|
82
|
+
*/
|
83
|
+
vjs['VERSION'] = '4.12.0';
|
84
|
+
|
69
85
|
/**
|
70
86
|
* Global Player instance options, surfaced from vjs.Player.prototype.options_
|
71
87
|
* vjs.options = vjs.Player.prototype.options_
|
@@ -99,11 +115,12 @@ vjs.options = {
|
|
99
115
|
'children': {
|
100
116
|
'mediaLoader': {},
|
101
117
|
'posterImage': {},
|
102
|
-
'textTrackDisplay': {},
|
103
118
|
'loadingSpinner': {},
|
119
|
+
'textTrackDisplay': {},
|
104
120
|
'bigPlayButton': {},
|
105
121
|
'controlBar': {},
|
106
|
-
'errorDisplay': {}
|
122
|
+
'errorDisplay': {},
|
123
|
+
'textTrackSettings': {}
|
107
124
|
},
|
108
125
|
|
109
126
|
'language': document.getElementsByTagName('html')[0].getAttribute('lang') || navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language || 'en',
|
@@ -116,7 +133,7 @@ vjs.options = {
|
|
116
133
|
};
|
117
134
|
|
118
135
|
// Set CDN Version of swf
|
119
|
-
// The added (+) blocks the replace from changing this 4.
|
136
|
+
// The added (+) blocks the replace from changing this 4.12 string
|
120
137
|
if (vjs.CDN_VERSION !== 'GENERATED'+'_CDN_VSN') {
|
121
138
|
videojs.options['flash']['swf'] = "<%= asset_path('video-js.swf') %>";
|
122
139
|
}
|
@@ -154,7 +171,7 @@ vjs.players = {};
|
|
154
171
|
* compiler compatible, so string keys are used.
|
155
172
|
*/
|
156
173
|
if (typeof define === 'function' && define['amd']) {
|
157
|
-
define([], function(){ return videojs; });
|
174
|
+
define('videojs', [], function(){ return videojs; });
|
158
175
|
|
159
176
|
// checking that module is an object too because of umdjs/umd#35
|
160
177
|
} else if (typeof exports === 'object' && typeof module === 'object') {
|
@@ -913,6 +930,8 @@ vjs.getData = function(el){
|
|
913
930
|
var id = el[vjs.expando];
|
914
931
|
if (!id) {
|
915
932
|
id = el[vjs.expando] = vjs.guid++;
|
933
|
+
}
|
934
|
+
if (!vjs.cache[id]) {
|
916
935
|
vjs.cache[id] = {};
|
917
936
|
}
|
918
937
|
return vjs.cache[id];
|
@@ -1025,6 +1044,13 @@ vjs.removeClass = function(element, classToRemove){
|
|
1025
1044
|
* @private
|
1026
1045
|
*/
|
1027
1046
|
vjs.TEST_VID = vjs.createEl('video');
|
1047
|
+
(function() {
|
1048
|
+
var track = document.createElement('track');
|
1049
|
+
track.kind = 'captions';
|
1050
|
+
track.srclang = 'en';
|
1051
|
+
track.label = 'English';
|
1052
|
+
vjs.TEST_VID.appendChild(track);
|
1053
|
+
})();
|
1028
1054
|
|
1029
1055
|
/**
|
1030
1056
|
* Useragent for browser testing.
|
@@ -1078,6 +1104,7 @@ vjs.IS_OLD_ANDROID = vjs.IS_ANDROID && (/webkit/i).test(vjs.USER_AGENT) && vjs.A
|
|
1078
1104
|
|
1079
1105
|
vjs.IS_FIREFOX = (/Firefox/i).test(vjs.USER_AGENT);
|
1080
1106
|
vjs.IS_CHROME = (/Chrome/i).test(vjs.USER_AGENT);
|
1107
|
+
vjs.IS_IE8 = (/MSIE\s8\.0/).test(vjs.USER_AGENT);
|
1081
1108
|
|
1082
1109
|
vjs.TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
|
1083
1110
|
vjs.BACKGROUND_SIZE_SUPPORTED = 'backgroundSize' in vjs.TEST_VID.style;
|
@@ -1360,6 +1387,15 @@ vjs.parseUrl = function(url) {
|
|
1360
1387
|
details[props[i]] = a[props[i]];
|
1361
1388
|
}
|
1362
1389
|
|
1390
|
+
// IE9 adds the port to the host property unlike everyone else. If
|
1391
|
+
// a port identifier is added for standard ports, strip it.
|
1392
|
+
if (details.protocol === 'http:') {
|
1393
|
+
details.host = details.host.replace(/:80$/, '');
|
1394
|
+
}
|
1395
|
+
if (details.protocol === 'https:') {
|
1396
|
+
details.host = details.host.replace(/:443$/, '');
|
1397
|
+
}
|
1398
|
+
|
1363
1399
|
if (addToBody) {
|
1364
1400
|
document.body.removeChild(div);
|
1365
1401
|
}
|
@@ -1685,7 +1721,50 @@ vjs.util.mergeOptions = function(obj1, obj2){
|
|
1685
1721
|
}
|
1686
1722
|
}
|
1687
1723
|
return obj1;
|
1688
|
-
}
|
1724
|
+
};vjs.EventEmitter = function() {
|
1725
|
+
};
|
1726
|
+
|
1727
|
+
vjs.EventEmitter.prototype.allowedEvents_ = {
|
1728
|
+
};
|
1729
|
+
|
1730
|
+
vjs.EventEmitter.prototype.on = function(type, fn) {
|
1731
|
+
// Remove the addEventListener alias before calling vjs.on
|
1732
|
+
// so we don't get into an infinite type loop
|
1733
|
+
var ael = this.addEventListener;
|
1734
|
+
this.addEventListener = Function.prototype;
|
1735
|
+
vjs.on(this, type, fn);
|
1736
|
+
this.addEventListener = ael;
|
1737
|
+
};
|
1738
|
+
vjs.EventEmitter.prototype.addEventListener = vjs.EventEmitter.prototype.on;
|
1739
|
+
|
1740
|
+
vjs.EventEmitter.prototype.off = function(type, fn) {
|
1741
|
+
vjs.off(this, type, fn);
|
1742
|
+
};
|
1743
|
+
vjs.EventEmitter.prototype.removeEventListener = vjs.EventEmitter.prototype.off;
|
1744
|
+
|
1745
|
+
vjs.EventEmitter.prototype.one = function(type, fn) {
|
1746
|
+
vjs.one(this, type, fn);
|
1747
|
+
};
|
1748
|
+
|
1749
|
+
vjs.EventEmitter.prototype.trigger = function(event) {
|
1750
|
+
var type = event.type || event;
|
1751
|
+
|
1752
|
+
if (typeof event === 'string') {
|
1753
|
+
event = {
|
1754
|
+
type: type
|
1755
|
+
};
|
1756
|
+
}
|
1757
|
+
event = vjs.fixEvent(event);
|
1758
|
+
|
1759
|
+
if (this.allowedEvents_[type] && this['on' + type]) {
|
1760
|
+
this['on' + type](event);
|
1761
|
+
}
|
1762
|
+
|
1763
|
+
vjs.trigger(this, event);
|
1764
|
+
};
|
1765
|
+
// The standard DOM EventTarget.dispatchEvent() is aliased to trigger()
|
1766
|
+
vjs.EventEmitter.prototype.dispatchEvent = vjs.EventEmitter.prototype.trigger;
|
1767
|
+
/**
|
1689
1768
|
* @fileoverview Player Component - Base class for all UI objects
|
1690
1769
|
*
|
1691
1770
|
*/
|
@@ -2532,7 +2611,7 @@ vjs.Component.prototype.removeClass = function(classToRemove){
|
|
2532
2611
|
* @return {vjs.Component}
|
2533
2612
|
*/
|
2534
2613
|
vjs.Component.prototype.show = function(){
|
2535
|
-
this.
|
2614
|
+
this.removeClass('vjs-hidden');
|
2536
2615
|
return this;
|
2537
2616
|
};
|
2538
2617
|
|
@@ -2542,7 +2621,7 @@ vjs.Component.prototype.show = function(){
|
|
2542
2621
|
* @return {vjs.Component}
|
2543
2622
|
*/
|
2544
2623
|
vjs.Component.prototype.hide = function(){
|
2545
|
-
this.
|
2624
|
+
this.addClass('vjs-hidden');
|
2546
2625
|
return this;
|
2547
2626
|
};
|
2548
2627
|
|
@@ -2718,19 +2797,23 @@ vjs.Component.prototype.onResize;
|
|
2718
2797
|
*/
|
2719
2798
|
vjs.Component.prototype.emitTapEvents = function(){
|
2720
2799
|
var touchStart, firstTouch, touchTime, couldBeTap, noTap,
|
2721
|
-
xdiff, ydiff, touchDistance, tapMovementThreshold;
|
2800
|
+
xdiff, ydiff, touchDistance, tapMovementThreshold, touchTimeThreshold;
|
2722
2801
|
|
2723
2802
|
// Track the start time so we can determine how long the touch lasted
|
2724
2803
|
touchStart = 0;
|
2725
2804
|
firstTouch = null;
|
2726
2805
|
|
2727
2806
|
// Maximum movement allowed during a touch event to still be considered a tap
|
2728
|
-
|
2807
|
+
// Other popular libs use anywhere from 2 (hammer.js) to 15, so 10 seems like a nice, round number.
|
2808
|
+
tapMovementThreshold = 10;
|
2809
|
+
|
2810
|
+
// The maximum length a touch can be while still being considered a tap
|
2811
|
+
touchTimeThreshold = 200;
|
2729
2812
|
|
2730
2813
|
this.on('touchstart', function(event) {
|
2731
2814
|
// If more than one finger, don't consider treating this as a click
|
2732
2815
|
if (event.touches.length === 1) {
|
2733
|
-
firstTouch = event.touches[0];
|
2816
|
+
firstTouch = vjs.obj.copy(event.touches[0]);
|
2734
2817
|
// Record start time so we can detect a tap vs. "touch and hold"
|
2735
2818
|
touchStart = new Date().getTime();
|
2736
2819
|
// Reset couldBeTap tracking
|
@@ -2769,8 +2852,8 @@ vjs.Component.prototype.emitTapEvents = function(){
|
|
2769
2852
|
if (couldBeTap === true) {
|
2770
2853
|
// Measure how long the touch lasted
|
2771
2854
|
touchTime = new Date().getTime() - touchStart;
|
2772
|
-
//
|
2773
|
-
if (touchTime <
|
2855
|
+
// Make sure the touch was less than the threshold to be considered a tap
|
2856
|
+
if (touchTime < touchTimeThreshold) {
|
2774
2857
|
event.preventDefault(); // Don't let browser turn this into a click
|
2775
2858
|
this.trigger('tap');
|
2776
2859
|
// It may be good to copy the touchend event object and change the
|
@@ -3083,7 +3166,12 @@ vjs.Slider.prototype.update = function(){
|
|
3083
3166
|
bar = this.bar;
|
3084
3167
|
|
3085
3168
|
// Protect against no duration and other division issues
|
3086
|
-
if (
|
3169
|
+
if (typeof progress !== 'number' ||
|
3170
|
+
progress !== progress ||
|
3171
|
+
progress < 0 ||
|
3172
|
+
progress === Infinity) {
|
3173
|
+
progress = 0;
|
3174
|
+
}
|
3087
3175
|
|
3088
3176
|
barProgress = progress;
|
3089
3177
|
|
@@ -3328,15 +3416,7 @@ vjs.MenuButton = vjs.Button.extend({
|
|
3328
3416
|
init: function(player, options){
|
3329
3417
|
vjs.Button.call(this, player, options);
|
3330
3418
|
|
3331
|
-
this.
|
3332
|
-
|
3333
|
-
// Add list to element
|
3334
|
-
this.addChild(this.menu);
|
3335
|
-
|
3336
|
-
// Automatically hide empty menu buttons
|
3337
|
-
if (this.items && this.items.length === 0) {
|
3338
|
-
this.hide();
|
3339
|
-
}
|
3419
|
+
this.update();
|
3340
3420
|
|
3341
3421
|
this.on('keydown', this.onKeyPress);
|
3342
3422
|
this.el_.setAttribute('aria-haspopup', true);
|
@@ -3344,6 +3424,23 @@ vjs.MenuButton = vjs.Button.extend({
|
|
3344
3424
|
}
|
3345
3425
|
});
|
3346
3426
|
|
3427
|
+
vjs.MenuButton.prototype.update = function() {
|
3428
|
+
var menu = this.createMenu();
|
3429
|
+
|
3430
|
+
if (this.menu) {
|
3431
|
+
this.removeChild(this.menu);
|
3432
|
+
}
|
3433
|
+
|
3434
|
+
this.menu = menu;
|
3435
|
+
this.addChild(menu);
|
3436
|
+
|
3437
|
+
if (this.items && this.items.length === 0) {
|
3438
|
+
this.hide();
|
3439
|
+
} else if (this.items && this.items.length > 1) {
|
3440
|
+
this.show();
|
3441
|
+
}
|
3442
|
+
};
|
3443
|
+
|
3347
3444
|
/**
|
3348
3445
|
* Track the state of the menu button
|
3349
3446
|
* @type {Boolean}
|
@@ -3821,29 +3918,6 @@ vjs.Player.prototype.createEl = function(){
|
|
3821
3918
|
// Remove width/height attrs from tag so CSS can make it 100% width/height
|
3822
3919
|
tag.removeAttribute('width');
|
3823
3920
|
tag.removeAttribute('height');
|
3824
|
-
// Empty video tag tracks so the built-in player doesn't use them also.
|
3825
|
-
// This may not be fast enough to stop HTML5 browsers from reading the tags
|
3826
|
-
// so we'll need to turn off any default tracks if we're manually doing
|
3827
|
-
// captions and subtitles. videoElement.textTracks
|
3828
|
-
if (tag.hasChildNodes()) {
|
3829
|
-
var nodes, nodesLength, i, node, nodeName, removeNodes;
|
3830
|
-
|
3831
|
-
nodes = tag.childNodes;
|
3832
|
-
nodesLength = nodes.length;
|
3833
|
-
removeNodes = [];
|
3834
|
-
|
3835
|
-
while (nodesLength--) {
|
3836
|
-
node = nodes[nodesLength];
|
3837
|
-
nodeName = node.nodeName.toLowerCase();
|
3838
|
-
if (nodeName === 'track') {
|
3839
|
-
removeNodes.push(node);
|
3840
|
-
}
|
3841
|
-
}
|
3842
|
-
|
3843
|
-
for (i=0; i<removeNodes.length; i++) {
|
3844
|
-
tag.removeChild(removeNodes[i]);
|
3845
|
-
}
|
3846
|
-
}
|
3847
3921
|
|
3848
3922
|
// Copy over all the attributes from the tag, including ID and class
|
3849
3923
|
// ID will now reference player box, not the video tag
|
@@ -3983,6 +4057,8 @@ vjs.Player.prototype.unloadTech = function(){
|
|
3983
4057
|
vjs.Player.prototype.onLoadStart = function() {
|
3984
4058
|
// TODO: Update to use `emptied` event instead. See #1277.
|
3985
4059
|
|
4060
|
+
this.removeClass('vjs-ended');
|
4061
|
+
|
3986
4062
|
// reset the error state
|
3987
4063
|
this.error(null);
|
3988
4064
|
|
@@ -3994,9 +4070,6 @@ vjs.Player.prototype.onLoadStart = function() {
|
|
3994
4070
|
} else {
|
3995
4071
|
// reset the hasStarted state
|
3996
4072
|
this.hasStarted(false);
|
3997
|
-
this.one('play', function(){
|
3998
|
-
this.hasStarted(true);
|
3999
|
-
});
|
4000
4073
|
}
|
4001
4074
|
};
|
4002
4075
|
|
@@ -4043,8 +4116,13 @@ vjs.Player.prototype.onLoadedAllData;
|
|
4043
4116
|
* @event play
|
4044
4117
|
*/
|
4045
4118
|
vjs.Player.prototype.onPlay = function(){
|
4119
|
+
this.removeClass('vjs-ended');
|
4046
4120
|
this.removeClass('vjs-paused');
|
4047
4121
|
this.addClass('vjs-playing');
|
4122
|
+
|
4123
|
+
// hide the poster when the user hits play
|
4124
|
+
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play
|
4125
|
+
this.hasStarted(true);
|
4048
4126
|
};
|
4049
4127
|
|
4050
4128
|
/**
|
@@ -4133,6 +4211,7 @@ vjs.Player.prototype.onProgress = function(){
|
|
4133
4211
|
* @event ended
|
4134
4212
|
*/
|
4135
4213
|
vjs.Player.prototype.onEnded = function(){
|
4214
|
+
this.addClass('vjs-ended');
|
4136
4215
|
if (this.options_['loop']) {
|
4137
4216
|
this.currentTime(0);
|
4138
4217
|
this.play();
|
@@ -4182,6 +4261,12 @@ vjs.Player.prototype.onFullscreenChange = function() {
|
|
4182
4261
|
}
|
4183
4262
|
};
|
4184
4263
|
|
4264
|
+
/**
|
4265
|
+
* Fired when an error occurs
|
4266
|
+
* @event error
|
4267
|
+
*/
|
4268
|
+
vjs.Player.prototype.onError;
|
4269
|
+
|
4185
4270
|
// /* Player API
|
4186
4271
|
// ================================================================================ */
|
4187
4272
|
|
@@ -5247,9 +5332,97 @@ vjs.Player.prototype.isAudio = function(bool) {
|
|
5247
5332
|
return this.isAudio_;
|
5248
5333
|
};
|
5249
5334
|
|
5335
|
+
/**
|
5336
|
+
* Returns the current state of network activity for the element, from
|
5337
|
+
* the codes in the list below.
|
5338
|
+
* - NETWORK_EMPTY (numeric value 0)
|
5339
|
+
* The element has not yet been initialised. All attributes are in
|
5340
|
+
* their initial states.
|
5341
|
+
* - NETWORK_IDLE (numeric value 1)
|
5342
|
+
* The element's resource selection algorithm is active and has
|
5343
|
+
* selected a resource, but it is not actually using the network at
|
5344
|
+
* this time.
|
5345
|
+
* - NETWORK_LOADING (numeric value 2)
|
5346
|
+
* The user agent is actively trying to download data.
|
5347
|
+
* - NETWORK_NO_SOURCE (numeric value 3)
|
5348
|
+
* The element's resource selection algorithm is active, but it has
|
5349
|
+
* not yet found a resource to use.
|
5350
|
+
* @return {Number} the current network activity state
|
5351
|
+
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
|
5352
|
+
*/
|
5353
|
+
vjs.Player.prototype.networkState = function(){
|
5354
|
+
return this.techGet('networkState');
|
5355
|
+
};
|
5356
|
+
|
5357
|
+
/**
|
5358
|
+
* Returns a value that expresses the current state of the element
|
5359
|
+
* with respect to rendering the current playback position, from the
|
5360
|
+
* codes in the list below.
|
5361
|
+
* - HAVE_NOTHING (numeric value 0)
|
5362
|
+
* No information regarding the media resource is available.
|
5363
|
+
* - HAVE_METADATA (numeric value 1)
|
5364
|
+
* Enough of the resource has been obtained that the duration of the
|
5365
|
+
* resource is available.
|
5366
|
+
* - HAVE_CURRENT_DATA (numeric value 2)
|
5367
|
+
* Data for the immediate current playback position is available.
|
5368
|
+
* - HAVE_FUTURE_DATA (numeric value 3)
|
5369
|
+
* Data for the immediate current playback position is available, as
|
5370
|
+
* well as enough data for the user agent to advance the current
|
5371
|
+
* playback position in the direction of playback.
|
5372
|
+
* - HAVE_ENOUGH_DATA (numeric value 4)
|
5373
|
+
* The user agent estimates that enough data is available for
|
5374
|
+
* playback to proceed uninterrupted.
|
5375
|
+
* @return {Number} the current playback rendering state
|
5376
|
+
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
|
5377
|
+
*/
|
5378
|
+
vjs.Player.prototype.readyState = function(){
|
5379
|
+
return this.techGet('readyState');
|
5380
|
+
};
|
5381
|
+
|
5382
|
+
/**
|
5383
|
+
* Text tracks are tracks of timed text events.
|
5384
|
+
* Captions - text displayed over the video for the hearing impaired
|
5385
|
+
* Subtitles - text displayed over the video for those who don't understand language in the video
|
5386
|
+
* Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video
|
5387
|
+
* Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device
|
5388
|
+
*/
|
5389
|
+
|
5390
|
+
/**
|
5391
|
+
* Get an array of associated text tracks. captions, subtitles, chapters, descriptions
|
5392
|
+
* http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
|
5393
|
+
* @return {Array} Array of track objects
|
5394
|
+
*/
|
5395
|
+
vjs.Player.prototype.textTracks = function(){
|
5396
|
+
// cannot use techGet directly because it checks to see whether the tech is ready.
|
5397
|
+
// Flash is unlikely to be ready in time but textTracks should still work.
|
5398
|
+
return this.tech && this.tech['textTracks']();
|
5399
|
+
};
|
5400
|
+
|
5401
|
+
vjs.Player.prototype.remoteTextTracks = function() {
|
5402
|
+
return this.tech && this.tech['remoteTextTracks']();
|
5403
|
+
};
|
5404
|
+
|
5405
|
+
/**
|
5406
|
+
* Add a text track
|
5407
|
+
* In addition to the W3C settings we allow adding additional info through options.
|
5408
|
+
* http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
|
5409
|
+
* @param {String} kind Captions, subtitles, chapters, descriptions, or metadata
|
5410
|
+
* @param {String=} label Optional label
|
5411
|
+
* @param {String=} language Optional language
|
5412
|
+
*/
|
5413
|
+
vjs.Player.prototype.addTextTrack = function(kind, label, language) {
|
5414
|
+
return this.tech && this.tech['addTextTrack'](kind, label, language);
|
5415
|
+
};
|
5416
|
+
|
5417
|
+
vjs.Player.prototype.addRemoteTextTrack = function(options) {
|
5418
|
+
return this.tech && this.tech['addRemoteTextTrack'](options);
|
5419
|
+
};
|
5420
|
+
|
5421
|
+
vjs.Player.prototype.removeRemoteTextTrack = function(track) {
|
5422
|
+
this.tech && this.tech['removeRemoteTextTrack'](track);
|
5423
|
+
};
|
5424
|
+
|
5250
5425
|
// Methods to add support for
|
5251
|
-
// networkState: function(){ return this.techCall('networkState'); },
|
5252
|
-
// readyState: function(){ return this.techCall('readyState'); },
|
5253
5426
|
// initialTime: function(){ return this.techCall('initialTime'); },
|
5254
5427
|
// startOffsetTime: function(){ return this.techCall('startOffsetTime'); },
|
5255
5428
|
// played: function(){ return this.techCall('played'); },
|
@@ -5290,7 +5463,10 @@ vjs.ControlBar.prototype.options_ = {
|
|
5290
5463
|
'volumeControl': {},
|
5291
5464
|
'muteToggle': {},
|
5292
5465
|
// 'volumeMenuButton': {},
|
5293
|
-
'playbackRateMenuButton': {}
|
5466
|
+
'playbackRateMenuButton': {},
|
5467
|
+
'subtitlesButton': {},
|
5468
|
+
'captionsButton': {},
|
5469
|
+
'chaptersButton': {}
|
5294
5470
|
}
|
5295
5471
|
};
|
5296
5472
|
|
@@ -5622,6 +5798,7 @@ vjs.SeekBar.prototype.onMouseDown = function(event){
|
|
5622
5798
|
vjs.Slider.prototype.onMouseDown.call(this, event);
|
5623
5799
|
|
5624
5800
|
this.player_.scrubbing = true;
|
5801
|
+
this.player_.addClass('vjs-scrubbing');
|
5625
5802
|
|
5626
5803
|
this.videoWasPlaying = !this.player_.paused();
|
5627
5804
|
this.player_.pause();
|
@@ -5641,6 +5818,7 @@ vjs.SeekBar.prototype.onMouseUp = function(event){
|
|
5641
5818
|
vjs.Slider.prototype.onMouseUp.call(this, event);
|
5642
5819
|
|
5643
5820
|
this.player_.scrubbing = false;
|
5821
|
+
this.player_.removeClass('vjs-scrubbing');
|
5644
5822
|
if (this.videoWasPlaying) {
|
5645
5823
|
this.player_.play();
|
5646
5824
|
}
|
@@ -5989,7 +6167,7 @@ vjs.VolumeMenuButton = vjs.MenuButton.extend({
|
|
5989
6167
|
vjs.MenuButton.call(this, player, options);
|
5990
6168
|
|
5991
6169
|
// Same listeners as MuteToggle
|
5992
|
-
this.on(player, 'volumechange', this.
|
6170
|
+
this.on(player, 'volumechange', this.volumeUpdate);
|
5993
6171
|
|
5994
6172
|
// hide mute toggle if the current tech doesn't support volume control
|
5995
6173
|
if (player.tech && player.tech['featuresVolumeControl'] === false) {
|
@@ -6032,7 +6210,7 @@ vjs.VolumeMenuButton.prototype.createEl = function(){
|
|
6032
6210
|
innerHTML: '<div><span class="vjs-control-text">' + this.localize('Mute') + '</span></div>'
|
6033
6211
|
});
|
6034
6212
|
};
|
6035
|
-
vjs.VolumeMenuButton.prototype.
|
6213
|
+
vjs.VolumeMenuButton.prototype.volumeUpdate = vjs.MuteToggle.prototype.update;
|
6036
6214
|
/**
|
6037
6215
|
* The component for controlling the playback rate
|
6038
6216
|
*
|
@@ -6224,10 +6402,7 @@ vjs.PosterImage.prototype.update = function(){
|
|
6224
6402
|
// If there's no poster source we should display:none on this component
|
6225
6403
|
// so it's not still clickable or right-clickable
|
6226
6404
|
if (url) {
|
6227
|
-
|
6228
|
-
// as opposed to show() which sets display to block
|
6229
|
-
// In the future it might be worth creating an `unhide` component method
|
6230
|
-
this.el_.style.display = '';
|
6405
|
+
this.show();
|
6231
6406
|
} else {
|
6232
6407
|
this.hide();
|
6233
6408
|
}
|
@@ -6356,6 +6531,8 @@ vjs.ErrorDisplay.prototype.update = function(){
|
|
6356
6531
|
this.contentEl_.innerHTML = this.localize(this.player().error().message);
|
6357
6532
|
}
|
6358
6533
|
};
|
6534
|
+
(function() {
|
6535
|
+
var createTrackHelper;
|
6359
6536
|
/**
|
6360
6537
|
* @fileoverview Media Technology Controller - Base class for media playback
|
6361
6538
|
* technology controllers like Flash and HTML5
|
@@ -6387,6 +6564,12 @@ vjs.MediaTechController = vjs.Component.extend({
|
|
6387
6564
|
}
|
6388
6565
|
|
6389
6566
|
this.initControlsListeners();
|
6567
|
+
|
6568
|
+
if (!this['featuresNativeTextTracks']) {
|
6569
|
+
this.emulateTextTracks();
|
6570
|
+
}
|
6571
|
+
|
6572
|
+
this.initTextTrackListeners();
|
6390
6573
|
}
|
6391
6574
|
});
|
6392
6575
|
|
@@ -6573,10 +6756,12 @@ vjs.MediaTechController.prototype.manualTimeUpdatesOn = function(){
|
|
6573
6756
|
};
|
6574
6757
|
|
6575
6758
|
vjs.MediaTechController.prototype.manualTimeUpdatesOff = function(){
|
6759
|
+
var player = this.player_;
|
6760
|
+
|
6576
6761
|
this.manualTimeUpdates = false;
|
6577
6762
|
this.stopTrackingCurrentTime();
|
6578
|
-
this.off('play', this.trackCurrentTime);
|
6579
|
-
this.off('pause', this.stopTrackingCurrentTime);
|
6763
|
+
this.off(player, 'play', this.trackCurrentTime);
|
6764
|
+
this.off(player, 'pause', this.stopTrackingCurrentTime);
|
6580
6765
|
};
|
6581
6766
|
|
6582
6767
|
vjs.MediaTechController.prototype.trackCurrentTime = function(){
|
@@ -6609,6 +6794,141 @@ vjs.MediaTechController.prototype.setCurrentTime = function() {
|
|
6609
6794
|
if (this.manualTimeUpdates) { this.player().trigger('timeupdate'); }
|
6610
6795
|
};
|
6611
6796
|
|
6797
|
+
// TODO: Consider looking at moving this into the text track display directly
|
6798
|
+
// https://github.com/videojs/video.js/issues/1863
|
6799
|
+
vjs.MediaTechController.prototype.initTextTrackListeners = function() {
|
6800
|
+
var player = this.player_,
|
6801
|
+
tracks,
|
6802
|
+
textTrackListChanges = function() {
|
6803
|
+
var textTrackDisplay = player.getChild('textTrackDisplay'),
|
6804
|
+
controlBar;
|
6805
|
+
|
6806
|
+
if (textTrackDisplay) {
|
6807
|
+
textTrackDisplay.updateDisplay();
|
6808
|
+
}
|
6809
|
+
};
|
6810
|
+
|
6811
|
+
tracks = this.textTracks();
|
6812
|
+
|
6813
|
+
if (!tracks) {
|
6814
|
+
return;
|
6815
|
+
}
|
6816
|
+
|
6817
|
+
tracks.addEventListener('removetrack', textTrackListChanges);
|
6818
|
+
tracks.addEventListener('addtrack', textTrackListChanges);
|
6819
|
+
|
6820
|
+
this.on('dispose', vjs.bind(this, function() {
|
6821
|
+
tracks.removeEventListener('removetrack', textTrackListChanges);
|
6822
|
+
tracks.removeEventListener('addtrack', textTrackListChanges);
|
6823
|
+
}));
|
6824
|
+
};
|
6825
|
+
|
6826
|
+
vjs.MediaTechController.prototype.emulateTextTracks = function() {
|
6827
|
+
var player = this.player_,
|
6828
|
+
textTracksChanges,
|
6829
|
+
tracks,
|
6830
|
+
script;
|
6831
|
+
|
6832
|
+
if (!window['WebVTT']) {
|
6833
|
+
script = document.createElement('script');
|
6834
|
+
script.src = player.options()['vtt.js'] || '../node_modules/vtt.js/dist/vtt.js';
|
6835
|
+
player.el().appendChild(script);
|
6836
|
+
window['WebVTT'] = true;
|
6837
|
+
}
|
6838
|
+
|
6839
|
+
tracks = this.textTracks();
|
6840
|
+
if (!tracks) {
|
6841
|
+
return;
|
6842
|
+
}
|
6843
|
+
|
6844
|
+
textTracksChanges = function() {
|
6845
|
+
var i, track, textTrackDisplay;
|
6846
|
+
|
6847
|
+
textTrackDisplay = player.getChild('textTrackDisplay'),
|
6848
|
+
|
6849
|
+
textTrackDisplay.updateDisplay();
|
6850
|
+
|
6851
|
+
for (i = 0; i < this.length; i++) {
|
6852
|
+
track = this[i];
|
6853
|
+
track.removeEventListener('cuechange', vjs.bind(textTrackDisplay, textTrackDisplay.updateDisplay));
|
6854
|
+
if (track.mode === 'showing') {
|
6855
|
+
track.addEventListener('cuechange', vjs.bind(textTrackDisplay, textTrackDisplay.updateDisplay));
|
6856
|
+
}
|
6857
|
+
}
|
6858
|
+
};
|
6859
|
+
|
6860
|
+
tracks.addEventListener('change', textTracksChanges);
|
6861
|
+
|
6862
|
+
this.on('dispose', vjs.bind(this, function() {
|
6863
|
+
tracks.removeEventListener('change', textTracksChanges);
|
6864
|
+
}));
|
6865
|
+
};
|
6866
|
+
|
6867
|
+
/**
|
6868
|
+
* Provide default methods for text tracks.
|
6869
|
+
*
|
6870
|
+
* Html5 tech overrides these.
|
6871
|
+
*/
|
6872
|
+
|
6873
|
+
/**
|
6874
|
+
* List of associated text tracks
|
6875
|
+
* @type {Array}
|
6876
|
+
* @private
|
6877
|
+
*/
|
6878
|
+
vjs.MediaTechController.prototype.textTracks_;
|
6879
|
+
|
6880
|
+
vjs.MediaTechController.prototype.textTracks = function() {
|
6881
|
+
this.textTracks_ = this.textTracks_ || new vjs.TextTrackList();
|
6882
|
+
return this.textTracks_;
|
6883
|
+
};
|
6884
|
+
|
6885
|
+
vjs.MediaTechController.prototype.remoteTextTracks = function() {
|
6886
|
+
this.remoteTextTracks_ = this.remoteTextTracks_ || new vjs.TextTrackList();
|
6887
|
+
return this.remoteTextTracks_;
|
6888
|
+
};
|
6889
|
+
|
6890
|
+
createTrackHelper = function(self, kind, label, language, options) {
|
6891
|
+
var tracks = self.textTracks(),
|
6892
|
+
track;
|
6893
|
+
|
6894
|
+
options = options || {};
|
6895
|
+
|
6896
|
+
options['kind'] = kind;
|
6897
|
+
if (label) {
|
6898
|
+
options['label'] = label;
|
6899
|
+
}
|
6900
|
+
if (language) {
|
6901
|
+
options['language'] = language;
|
6902
|
+
}
|
6903
|
+
options['player'] = self.player_;
|
6904
|
+
|
6905
|
+
track = new vjs.TextTrack(options);
|
6906
|
+
tracks.addTrack_(track);
|
6907
|
+
|
6908
|
+
return track;
|
6909
|
+
};
|
6910
|
+
|
6911
|
+
vjs.MediaTechController.prototype.addTextTrack = function(kind, label, language) {
|
6912
|
+
if (!kind) {
|
6913
|
+
throw new Error('TextTrack kind is required but was not provided');
|
6914
|
+
}
|
6915
|
+
|
6916
|
+
return createTrackHelper(this, kind, label, language);
|
6917
|
+
};
|
6918
|
+
|
6919
|
+
vjs.MediaTechController.prototype.addRemoteTextTrack = function(options) {
|
6920
|
+
var track = createTrackHelper(this, options['kind'], options['label'], options['language'], options);
|
6921
|
+
this.remoteTextTracks().addTrack_(track);
|
6922
|
+
return {
|
6923
|
+
track: track
|
6924
|
+
};
|
6925
|
+
};
|
6926
|
+
|
6927
|
+
vjs.MediaTechController.prototype.removeRemoteTextTrack = function(track) {
|
6928
|
+
this.textTracks().removeTrack_(track);
|
6929
|
+
this.remoteTextTracks().removeTrack_(track);
|
6930
|
+
};
|
6931
|
+
|
6612
6932
|
/**
|
6613
6933
|
* Provide a default setPoster method for techs
|
6614
6934
|
*
|
@@ -6628,6 +6948,8 @@ vjs.MediaTechController.prototype['featuresPlaybackRate'] = false;
|
|
6628
6948
|
vjs.MediaTechController.prototype['featuresProgressEvents'] = false;
|
6629
6949
|
vjs.MediaTechController.prototype['featuresTimeupdateEvents'] = false;
|
6630
6950
|
|
6951
|
+
vjs.MediaTechController.prototype['featuresNativeTextTracks'] = false;
|
6952
|
+
|
6631
6953
|
/**
|
6632
6954
|
* A functional mixin for techs that want to use the Source Handler pattern.
|
6633
6955
|
*
|
@@ -6728,6 +7050,10 @@ vjs.MediaTechController.withSourceHandlers = function(Tech){
|
|
6728
7050
|
};
|
6729
7051
|
|
6730
7052
|
};
|
7053
|
+
|
7054
|
+
vjs.media = {};
|
7055
|
+
|
7056
|
+
})();
|
6731
7057
|
/**
|
6732
7058
|
* @fileoverview HTML5 Media Controller - Wrapper for HTML5 Media API
|
6733
7059
|
*/
|
@@ -6742,6 +7068,12 @@ vjs.MediaTechController.withSourceHandlers = function(Tech){
|
|
6742
7068
|
vjs.Html5 = vjs.MediaTechController.extend({
|
6743
7069
|
/** @constructor */
|
6744
7070
|
init: function(player, options, ready){
|
7071
|
+
var nodes, nodesLength, i, node, nodeName, removeNodes;
|
7072
|
+
|
7073
|
+
if (options['nativeCaptions'] === false || options['nativeTextTracks'] === false) {
|
7074
|
+
this['featuresNativeTextTracks'] = false;
|
7075
|
+
}
|
7076
|
+
|
6745
7077
|
vjs.MediaTechController.call(this, player, options, ready);
|
6746
7078
|
|
6747
7079
|
this.setupTriggers();
|
@@ -6756,6 +7088,37 @@ vjs.Html5 = vjs.MediaTechController.extend({
|
|
6756
7088
|
this.setSource(source);
|
6757
7089
|
}
|
6758
7090
|
|
7091
|
+
if (this.el_.hasChildNodes()) {
|
7092
|
+
|
7093
|
+
nodes = this.el_.childNodes;
|
7094
|
+
nodesLength = nodes.length;
|
7095
|
+
removeNodes = [];
|
7096
|
+
|
7097
|
+
while (nodesLength--) {
|
7098
|
+
node = nodes[nodesLength];
|
7099
|
+
nodeName = node.nodeName.toLowerCase();
|
7100
|
+
if (nodeName === 'track') {
|
7101
|
+
if (!this['featuresNativeTextTracks']) {
|
7102
|
+
// Empty video tag tracks so the built-in player doesn't use them also.
|
7103
|
+
// This may not be fast enough to stop HTML5 browsers from reading the tags
|
7104
|
+
// so we'll need to turn off any default tracks if we're manually doing
|
7105
|
+
// captions and subtitles. videoElement.textTracks
|
7106
|
+
removeNodes.push(node);
|
7107
|
+
} else {
|
7108
|
+
this.remoteTextTracks().addTrack_(node['track']);
|
7109
|
+
}
|
7110
|
+
}
|
7111
|
+
}
|
7112
|
+
|
7113
|
+
for (i=0; i<removeNodes.length; i++) {
|
7114
|
+
this.el_.removeChild(removeNodes[i]);
|
7115
|
+
}
|
7116
|
+
}
|
7117
|
+
|
7118
|
+
if (this['featuresNativeTextTracks']) {
|
7119
|
+
this.on('loadstart', vjs.bind(this, this.hideCaptions));
|
7120
|
+
}
|
7121
|
+
|
6759
7122
|
// Determine if native controls should be used
|
6760
7123
|
// Our goal should be to get the custom controls on mobile solid everywhere
|
6761
7124
|
// so we can remove this all together. Right now this will block custom
|
@@ -6786,6 +7149,9 @@ vjs.Html5.prototype.dispose = function(){
|
|
6786
7149
|
|
6787
7150
|
vjs.Html5.prototype.createEl = function(){
|
6788
7151
|
var player = this.player_,
|
7152
|
+
track,
|
7153
|
+
trackEl,
|
7154
|
+
i,
|
6789
7155
|
// If possible, reuse original tag for HTML5 playback technology element
|
6790
7156
|
el = player.tag,
|
6791
7157
|
attributes,
|
@@ -6822,12 +7188,27 @@ vjs.Html5.prototype.createEl = function(){
|
|
6822
7188
|
// associate the player with the new tag
|
6823
7189
|
el['player'] = player;
|
6824
7190
|
|
7191
|
+
if (player.options_.tracks) {
|
7192
|
+
for (i = 0; i < player.options_.tracks.length; i++) {
|
7193
|
+
track = player.options_.tracks[i];
|
7194
|
+
trackEl = document.createElement('track');
|
7195
|
+
trackEl.kind = track.kind;
|
7196
|
+
trackEl.label = track.label;
|
7197
|
+
trackEl.srclang = track.srclang;
|
7198
|
+
trackEl.src = track.src;
|
7199
|
+
if ('default' in track) {
|
7200
|
+
trackEl.setAttribute('default', 'default');
|
7201
|
+
}
|
7202
|
+
el.appendChild(trackEl);
|
7203
|
+
}
|
7204
|
+
}
|
7205
|
+
|
6825
7206
|
vjs.insertFirst(el, player.el());
|
6826
7207
|
}
|
6827
7208
|
|
6828
7209
|
// Update specific tag settings, in case they were overridden
|
6829
7210
|
var settingsAttrs = ['autoplay','preload','loop','muted'];
|
6830
|
-
for (
|
7211
|
+
for (i = settingsAttrs.length - 1; i >= 0; i--) {
|
6831
7212
|
var attr = settingsAttrs[i];
|
6832
7213
|
var overwriteAttrs = {};
|
6833
7214
|
if (typeof player.options_[attr] !== 'undefined') {
|
@@ -6840,6 +7221,24 @@ vjs.Html5.prototype.createEl = function(){
|
|
6840
7221
|
// jenniisawesome = true;
|
6841
7222
|
};
|
6842
7223
|
|
7224
|
+
|
7225
|
+
vjs.Html5.prototype.hideCaptions = function() {
|
7226
|
+
var tracks = this.el_.textTracks,
|
7227
|
+
track,
|
7228
|
+
i = tracks.length,
|
7229
|
+
kinds = {
|
7230
|
+
'captions': 1,
|
7231
|
+
'subtitles': 1
|
7232
|
+
};
|
7233
|
+
|
7234
|
+
while (i--) {
|
7235
|
+
track = tracks[i];
|
7236
|
+
if (track && track['kind'] in kinds) {
|
7237
|
+
track.mode = 'disabled';
|
7238
|
+
}
|
7239
|
+
}
|
7240
|
+
};
|
7241
|
+
|
6843
7242
|
// Make video events trigger player events
|
6844
7243
|
// May seem verbose here, but makes other APIs possible.
|
6845
7244
|
// Triggers removed using this.off when disposed
|
@@ -7011,7 +7410,95 @@ vjs.Html5.prototype.playbackRate = function(){ return this.el_.playbackRate; };
|
|
7011
7410
|
vjs.Html5.prototype.setPlaybackRate = function(val){ this.el_.playbackRate = val; };
|
7012
7411
|
|
7013
7412
|
vjs.Html5.prototype.networkState = function(){ return this.el_.networkState; };
|
7413
|
+
vjs.Html5.prototype.readyState = function(){ return this.el_.readyState; };
|
7414
|
+
|
7415
|
+
vjs.Html5.prototype.textTracks = function() {
|
7416
|
+
if (!this['featuresNativeTextTracks']) {
|
7417
|
+
return vjs.MediaTechController.prototype.textTracks.call(this);
|
7418
|
+
}
|
7419
|
+
|
7420
|
+
return this.el_.textTracks;
|
7421
|
+
};
|
7422
|
+
vjs.Html5.prototype.addTextTrack = function(kind, label, language) {
|
7423
|
+
if (!this['featuresNativeTextTracks']) {
|
7424
|
+
return vjs.MediaTechController.prototype.addTextTrack.call(this, kind, label, language);
|
7425
|
+
}
|
7426
|
+
|
7427
|
+
return this.el_.addTextTrack(kind, label, language);
|
7428
|
+
};
|
7429
|
+
|
7430
|
+
vjs.Html5.prototype.addRemoteTextTrack = function(options) {
|
7431
|
+
if (!this['featuresNativeTextTracks']) {
|
7432
|
+
return vjs.MediaTechController.prototype.addRemoteTextTrack.call(this, options);
|
7433
|
+
}
|
7434
|
+
|
7435
|
+
var track = document.createElement('track');
|
7436
|
+
options = options || {};
|
7437
|
+
|
7438
|
+
if (options['kind']) {
|
7439
|
+
track['kind'] = options['kind'];
|
7440
|
+
}
|
7441
|
+
if (options['label']) {
|
7442
|
+
track['label'] = options['label'];
|
7443
|
+
}
|
7444
|
+
if (options['language'] || options['srclang']) {
|
7445
|
+
track['srclang'] = options['language'] || options['srclang'];
|
7446
|
+
}
|
7447
|
+
if (options['default']) {
|
7448
|
+
track['default'] = options['default'];
|
7449
|
+
}
|
7450
|
+
if (options['id']) {
|
7451
|
+
track['id'] = options['id'];
|
7452
|
+
}
|
7453
|
+
if (options['src']) {
|
7454
|
+
track['src'] = options['src'];
|
7455
|
+
}
|
7456
|
+
|
7457
|
+
this.el().appendChild(track);
|
7014
7458
|
|
7459
|
+
if (track.track['kind'] === 'metadata') {
|
7460
|
+
track['track']['mode'] = 'hidden';
|
7461
|
+
} else {
|
7462
|
+
track['track']['mode'] = 'disabled';
|
7463
|
+
}
|
7464
|
+
|
7465
|
+
track['onload'] = function() {
|
7466
|
+
var tt = track['track'];
|
7467
|
+
if (track.readyState >= 2) {
|
7468
|
+
if (tt['kind'] === 'metadata' && tt['mode'] !== 'hidden') {
|
7469
|
+
tt['mode'] = 'hidden';
|
7470
|
+
} else if (tt['kind'] !== 'metadata' && tt['mode'] !== 'disabled') {
|
7471
|
+
tt['mode'] = 'disabled';
|
7472
|
+
}
|
7473
|
+
track['onload'] = null;
|
7474
|
+
}
|
7475
|
+
};
|
7476
|
+
|
7477
|
+
this.remoteTextTracks().addTrack_(track.track);
|
7478
|
+
|
7479
|
+
return track;
|
7480
|
+
};
|
7481
|
+
|
7482
|
+
vjs.Html5.prototype.removeRemoteTextTrack = function(track) {
|
7483
|
+
if (!this['featuresNativeTextTracks']) {
|
7484
|
+
return vjs.MediaTechController.prototype.removeRemoteTextTrack.call(this, track);
|
7485
|
+
}
|
7486
|
+
|
7487
|
+
var tracks, i;
|
7488
|
+
|
7489
|
+
this.remoteTextTracks().removeTrack_(track);
|
7490
|
+
|
7491
|
+
tracks = this.el()['querySelectorAll']('track');
|
7492
|
+
|
7493
|
+
for (i = 0; i < tracks.length; i++) {
|
7494
|
+
if (tracks[i] === track || tracks[i]['track'] === track) {
|
7495
|
+
tracks[i]['parentNode']['removeChild'](tracks[i]);
|
7496
|
+
break;
|
7497
|
+
}
|
7498
|
+
}
|
7499
|
+
};
|
7500
|
+
|
7501
|
+
/* HTML5 Support Testing ---------------------------------------------------- */
|
7015
7502
|
|
7016
7503
|
/**
|
7017
7504
|
* Check if HTML5 video is supported by this browser/device
|
@@ -7113,6 +7600,29 @@ vjs.Html5.canControlPlaybackRate = function(){
|
|
7113
7600
|
return playbackRate !== vjs.TEST_VID.playbackRate;
|
7114
7601
|
};
|
7115
7602
|
|
7603
|
+
/**
|
7604
|
+
* Check to see if native text tracks are supported by this browser/device
|
7605
|
+
* @return {Boolean}
|
7606
|
+
*/
|
7607
|
+
vjs.Html5.supportsNativeTextTracks = function() {
|
7608
|
+
var supportsTextTracks;
|
7609
|
+
|
7610
|
+
// Figure out native text track support
|
7611
|
+
// If mode is a number, we cannot change it because it'll disappear from view.
|
7612
|
+
// Browsers with numeric modes include IE10 and older (<=2013) samsung android models.
|
7613
|
+
// Firefox isn't playing nice either with modifying the mode
|
7614
|
+
// TODO: Investigate firefox: https://github.com/videojs/video.js/issues/1862
|
7615
|
+
supportsTextTracks = !!vjs.TEST_VID.textTracks;
|
7616
|
+
if (supportsTextTracks && vjs.TEST_VID.textTracks.length > 0) {
|
7617
|
+
supportsTextTracks = typeof vjs.TEST_VID.textTracks[0]['mode'] !== 'number';
|
7618
|
+
}
|
7619
|
+
if (supportsTextTracks && vjs.IS_FIREFOX) {
|
7620
|
+
supportsTextTracks = false;
|
7621
|
+
}
|
7622
|
+
|
7623
|
+
return supportsTextTracks;
|
7624
|
+
};
|
7625
|
+
|
7116
7626
|
/**
|
7117
7627
|
* Set the tech's volume control support status
|
7118
7628
|
* @type {Boolean}
|
@@ -7145,8 +7655,14 @@ vjs.Html5.prototype['featuresFullscreenResize'] = true;
|
|
7145
7655
|
*/
|
7146
7656
|
vjs.Html5.prototype['featuresProgressEvents'] = true;
|
7147
7657
|
|
7148
|
-
|
7149
|
-
|
7658
|
+
/**
|
7659
|
+
* Sets the tech's status on native text track support
|
7660
|
+
* @type {Boolean}
|
7661
|
+
*/
|
7662
|
+
vjs.Html5.prototype['featuresNativeTextTracks'] = vjs.Html5.supportsNativeTextTracks();
|
7663
|
+
|
7664
|
+
// HTML5 Feature detection and Device Fixes --------------------------------- //
|
7665
|
+
(function() {
|
7150
7666
|
var canPlayType,
|
7151
7667
|
mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i,
|
7152
7668
|
mp4RE = /^video\/mp4/i;
|
@@ -7414,7 +7930,7 @@ vjs.Flash.prototype.enterFullScreen = function(){
|
|
7414
7930
|
// Create setters and getters for attributes
|
7415
7931
|
var api = vjs.Flash.prototype,
|
7416
7932
|
readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','),
|
7417
|
-
readOnly = 'error,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight
|
7933
|
+
readOnly = 'error,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight'.split(','),
|
7418
7934
|
// Overridden: buffered, currentTime, currentSrc
|
7419
7935
|
i;
|
7420
7936
|
|
@@ -7770,702 +8286,538 @@ vjs.MediaLoader = vjs.Component.extend({
|
|
7770
8286
|
}
|
7771
8287
|
}
|
7772
8288
|
});
|
7773
|
-
|
7774
|
-
*
|
7775
|
-
*
|
7776
|
-
*
|
7777
|
-
* Subtitles - text displayed over the video for those who don't understand language in the video
|
7778
|
-
* Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video
|
7779
|
-
* Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device
|
7780
|
-
*/
|
7781
|
-
|
7782
|
-
// Player Additions - Functions add to the player object for easier access to tracks
|
7783
|
-
|
7784
|
-
/**
|
7785
|
-
* List of associated text tracks
|
7786
|
-
* @type {Array}
|
7787
|
-
* @private
|
8289
|
+
/*
|
8290
|
+
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
|
8291
|
+
*
|
8292
|
+
* enum TextTrackMode { "disabled", "hidden", "showing" };
|
7788
8293
|
*/
|
7789
|
-
vjs.
|
8294
|
+
vjs.TextTrackMode = {
|
8295
|
+
'disabled': 'disabled',
|
8296
|
+
'hidden': 'hidden',
|
8297
|
+
'showing': 'showing'
|
8298
|
+
};
|
7790
8299
|
|
7791
|
-
|
7792
|
-
*
|
7793
|
-
*
|
7794
|
-
*
|
7795
|
-
* @private
|
8300
|
+
/*
|
8301
|
+
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackkind
|
8302
|
+
*
|
8303
|
+
* enum TextTrackKind { "subtitles", "captions", "descriptions", "chapters", "metadata" };
|
7796
8304
|
*/
|
7797
|
-
vjs.
|
7798
|
-
|
7799
|
-
|
8305
|
+
vjs.TextTrackKind = {
|
8306
|
+
'subtitles': 'subtitles',
|
8307
|
+
'captions': 'captions',
|
8308
|
+
'descriptions': 'descriptions',
|
8309
|
+
'chapters': 'chapters',
|
8310
|
+
'metadata': 'metadata'
|
7800
8311
|
};
|
7801
|
-
|
7802
|
-
|
7803
|
-
*
|
7804
|
-
*
|
7805
|
-
*
|
7806
|
-
*
|
7807
|
-
*
|
7808
|
-
*
|
7809
|
-
*
|
7810
|
-
*
|
8312
|
+
(function() {
|
8313
|
+
/*
|
8314
|
+
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack
|
8315
|
+
*
|
8316
|
+
* interface TextTrack : EventTarget {
|
8317
|
+
* readonly attribute TextTrackKind kind;
|
8318
|
+
* readonly attribute DOMString label;
|
8319
|
+
* readonly attribute DOMString language;
|
8320
|
+
*
|
8321
|
+
* readonly attribute DOMString id;
|
8322
|
+
* readonly attribute DOMString inBandMetadataTrackDispatchType;
|
8323
|
+
*
|
8324
|
+
* attribute TextTrackMode mode;
|
8325
|
+
*
|
8326
|
+
* readonly attribute TextTrackCueList? cues;
|
8327
|
+
* readonly attribute TextTrackCueList? activeCues;
|
8328
|
+
*
|
8329
|
+
* void addCue(TextTrackCue cue);
|
8330
|
+
* void removeCue(TextTrackCue cue);
|
8331
|
+
*
|
8332
|
+
* attribute EventHandler oncuechange;
|
8333
|
+
* };
|
7811
8334
|
*/
|
7812
|
-
|
7813
|
-
|
8335
|
+
|
8336
|
+
vjs.TextTrack = function(options) {
|
8337
|
+
var tt, id, mode, kind, label, language, cues, activeCues, timeupdateHandler, changed, prop;
|
8338
|
+
|
7814
8339
|
options = options || {};
|
7815
8340
|
|
7816
|
-
options['
|
7817
|
-
|
7818
|
-
options['language'] = language;
|
7819
|
-
|
7820
|
-
// HTML5 Spec says default to subtitles.
|
7821
|
-
// Uppercase first letter to match class names
|
7822
|
-
var Kind = vjs.capitalize(kind || 'subtitles');
|
7823
|
-
|
7824
|
-
// Create correct texttrack class. CaptionsTrack, etc.
|
7825
|
-
var track = new window['videojs'][Kind + 'Track'](this, options);
|
7826
|
-
|
7827
|
-
tracks.push(track);
|
7828
|
-
|
7829
|
-
// If track.dflt() is set, start showing immediately
|
7830
|
-
// TODO: Add a process to determine the best track to show for the specific kind
|
7831
|
-
// In case there are multiple defaulted tracks of the same kind
|
7832
|
-
// Or the user has a set preference of a specific language that should override the default
|
7833
|
-
// Note: The setTimeout is a workaround because with the html5 tech, the player is 'ready'
|
7834
|
-
// before it's child components (including the textTrackDisplay) have finished loading.
|
7835
|
-
if (track.dflt()) {
|
7836
|
-
this.ready(function(){
|
7837
|
-
this.setTimeout(function(){
|
7838
|
-
track.player().showTextTrack(track.id());
|
7839
|
-
}, 0);
|
7840
|
-
});
|
8341
|
+
if (!options['player']) {
|
8342
|
+
throw new Error('A player was not provided.');
|
7841
8343
|
}
|
7842
8344
|
|
7843
|
-
|
7844
|
-
|
7845
|
-
|
7846
|
-
/**
|
7847
|
-
* Add an array of text tracks. captions, subtitles, chapters, descriptions
|
7848
|
-
* Track objects will be stored in the player.textTracks() array
|
7849
|
-
* @param {Array} trackList Array of track elements or objects (fake track elements)
|
7850
|
-
* @private
|
7851
|
-
*/
|
7852
|
-
vjs.Player.prototype.addTextTracks = function(trackList){
|
7853
|
-
var trackObj;
|
8345
|
+
tt = this;
|
8346
|
+
if (vjs.IS_IE8) {
|
8347
|
+
tt = document.createElement('custom');
|
7854
8348
|
|
7855
|
-
|
7856
|
-
|
7857
|
-
|
8349
|
+
for (prop in vjs.TextTrack.prototype) {
|
8350
|
+
tt[prop] = vjs.TextTrack.prototype[prop];
|
8351
|
+
}
|
7858
8352
|
}
|
7859
8353
|
|
7860
|
-
|
7861
|
-
};
|
8354
|
+
tt.player_ = options['player'];
|
7862
8355
|
|
7863
|
-
|
7864
|
-
|
7865
|
-
|
7866
|
-
|
7867
|
-
|
7868
|
-
j = tracks.length,
|
7869
|
-
track, showTrack, kind;
|
8356
|
+
mode = vjs.TextTrackMode[options['mode']] || 'disabled';
|
8357
|
+
kind = vjs.TextTrackKind[options['kind']] || 'subtitles';
|
8358
|
+
label = options['label'] || '';
|
8359
|
+
language = options['language'] || options['srclang'] || '';
|
8360
|
+
id = options['id'] || 'vjs_text_track_' + vjs.guid++;
|
7870
8361
|
|
7871
|
-
|
7872
|
-
|
7873
|
-
|
7874
|
-
if (track.id() === id) {
|
7875
|
-
track.show();
|
7876
|
-
showTrack = track;
|
8362
|
+
if (kind === 'metadata' || kind === 'chapters') {
|
8363
|
+
mode = 'hidden';
|
8364
|
+
}
|
7877
8365
|
|
7878
|
-
|
7879
|
-
|
7880
|
-
|
8366
|
+
tt.cues_ = [];
|
8367
|
+
tt.activeCues_ = [];
|
8368
|
+
|
8369
|
+
cues = new vjs.TextTrackCueList(tt.cues_);
|
8370
|
+
activeCues = new vjs.TextTrackCueList(tt.activeCues_);
|
8371
|
+
|
8372
|
+
changed = false;
|
8373
|
+
timeupdateHandler = vjs.bind(tt, function() {
|
8374
|
+
this['activeCues'];
|
8375
|
+
if (changed) {
|
8376
|
+
this['trigger']('cuechange');
|
8377
|
+
changed = false;
|
7881
8378
|
}
|
8379
|
+
});
|
8380
|
+
if (mode !== 'disabled') {
|
8381
|
+
tt.player_.on('timeupdate', timeupdateHandler);
|
7882
8382
|
}
|
7883
8383
|
|
7884
|
-
|
7885
|
-
|
8384
|
+
Object.defineProperty(tt, 'kind', {
|
8385
|
+
get: function() {
|
8386
|
+
return kind;
|
8387
|
+
},
|
8388
|
+
set: Function.prototype
|
8389
|
+
});
|
7886
8390
|
|
7887
|
-
|
7888
|
-
|
7889
|
-
|
7890
|
-
|
8391
|
+
Object.defineProperty(tt, 'label', {
|
8392
|
+
get: function() {
|
8393
|
+
return label;
|
8394
|
+
},
|
8395
|
+
set: Function.prototype
|
8396
|
+
});
|
7891
8397
|
|
7892
|
-
|
7893
|
-
|
8398
|
+
Object.defineProperty(tt, 'language', {
|
8399
|
+
get: function() {
|
8400
|
+
return language;
|
8401
|
+
},
|
8402
|
+
set: Function.prototype
|
8403
|
+
});
|
7894
8404
|
|
7895
|
-
|
7896
|
-
|
7897
|
-
|
7898
|
-
|
7899
|
-
|
7900
|
-
|
7901
|
-
* @param {Object=} options
|
7902
|
-
* @constructor
|
7903
|
-
*/
|
7904
|
-
vjs.TextTrack = vjs.Component.extend({
|
7905
|
-
/** @constructor */
|
7906
|
-
init: function(player, options){
|
7907
|
-
vjs.Component.call(this, player, options);
|
8405
|
+
Object.defineProperty(tt, 'id', {
|
8406
|
+
get: function() {
|
8407
|
+
return id;
|
8408
|
+
},
|
8409
|
+
set: Function.prototype
|
8410
|
+
});
|
7908
8411
|
|
7909
|
-
|
7910
|
-
|
8412
|
+
Object.defineProperty(tt, 'mode', {
|
8413
|
+
get: function() {
|
8414
|
+
return mode;
|
8415
|
+
},
|
8416
|
+
set: function(newMode) {
|
8417
|
+
if (!vjs.TextTrackMode[newMode]) {
|
8418
|
+
return;
|
8419
|
+
}
|
8420
|
+
mode = newMode;
|
8421
|
+
if (mode === 'showing') {
|
8422
|
+
this.player_.on('timeupdate', timeupdateHandler);
|
8423
|
+
}
|
8424
|
+
this.trigger('modechange');
|
8425
|
+
}
|
8426
|
+
});
|
7911
8427
|
|
7912
|
-
|
7913
|
-
|
7914
|
-
|
7915
|
-
|
7916
|
-
|
7917
|
-
this.title_ = options['title'];
|
7918
|
-
this.language_ = options['srclang'];
|
7919
|
-
this.label_ = options['label'];
|
7920
|
-
this.cues_ = [];
|
7921
|
-
this.activeCues_ = [];
|
7922
|
-
this.readyState_ = 0;
|
7923
|
-
this.mode_ = 0;
|
8428
|
+
Object.defineProperty(tt, 'cues', {
|
8429
|
+
get: function() {
|
8430
|
+
if (!this.loaded_) {
|
8431
|
+
return null;
|
8432
|
+
}
|
7924
8433
|
|
7925
|
-
|
7926
|
-
|
7927
|
-
|
8434
|
+
return cues;
|
8435
|
+
},
|
8436
|
+
set: Function.prototype
|
8437
|
+
});
|
7928
8438
|
|
7929
|
-
|
7930
|
-
|
7931
|
-
|
7932
|
-
*/
|
7933
|
-
vjs.TextTrack.prototype.kind_;
|
8439
|
+
Object.defineProperty(tt, 'activeCues', {
|
8440
|
+
get: function() {
|
8441
|
+
var i, l, active, ct, cue;
|
7934
8442
|
|
7935
|
-
|
7936
|
-
|
7937
|
-
|
7938
|
-
*/
|
7939
|
-
vjs.TextTrack.prototype.kind = function(){
|
7940
|
-
return this.kind_;
|
7941
|
-
};
|
8443
|
+
if (!this.loaded_) {
|
8444
|
+
return null;
|
8445
|
+
}
|
7942
8446
|
|
7943
|
-
|
7944
|
-
|
7945
|
-
|
7946
|
-
*/
|
7947
|
-
vjs.TextTrack.prototype.src_;
|
8447
|
+
if (this['cues'].length === 0) {
|
8448
|
+
return activeCues; // nothing to do
|
8449
|
+
}
|
7948
8450
|
|
7949
|
-
|
7950
|
-
|
7951
|
-
|
7952
|
-
|
7953
|
-
|
7954
|
-
|
7955
|
-
|
8451
|
+
ct = this.player_.currentTime();
|
8452
|
+
i = 0;
|
8453
|
+
l = this['cues'].length;
|
8454
|
+
active = [];
|
8455
|
+
|
8456
|
+
for (; i < l; i++) {
|
8457
|
+
cue = this['cues'][i];
|
8458
|
+
if (cue['startTime'] <= ct && cue['endTime'] >= ct) {
|
8459
|
+
active.push(cue);
|
8460
|
+
} else if (cue['startTime'] === cue['endTime'] && cue['startTime'] <= ct && cue['startTime'] + 0.5 >= ct) {
|
8461
|
+
active.push(cue);
|
8462
|
+
}
|
8463
|
+
}
|
7956
8464
|
|
7957
|
-
|
7958
|
-
* Track default value
|
7959
|
-
* If default is used, subtitles/captions to start showing
|
7960
|
-
* @private
|
7961
|
-
*/
|
7962
|
-
vjs.TextTrack.prototype.dflt_;
|
8465
|
+
changed = false;
|
7963
8466
|
|
7964
|
-
|
7965
|
-
|
7966
|
-
|
7967
|
-
|
7968
|
-
|
7969
|
-
|
7970
|
-
}
|
8467
|
+
if (active.length !== this.activeCues_.length) {
|
8468
|
+
changed = true;
|
8469
|
+
} else {
|
8470
|
+
for (i = 0; i < active.length; i++) {
|
8471
|
+
if (indexOf.call(this.activeCues_, active[i]) === -1) {
|
8472
|
+
changed = true;
|
8473
|
+
}
|
8474
|
+
}
|
8475
|
+
}
|
7971
8476
|
|
7972
|
-
|
7973
|
-
|
7974
|
-
* @private
|
7975
|
-
*/
|
7976
|
-
vjs.TextTrack.prototype.title_;
|
8477
|
+
this.activeCues_ = active;
|
8478
|
+
activeCues.setCues_(this.activeCues_);
|
7977
8479
|
|
7978
|
-
|
7979
|
-
|
7980
|
-
|
7981
|
-
|
7982
|
-
vjs.TextTrack.prototype.title = function(){
|
7983
|
-
return this.title_;
|
7984
|
-
};
|
8480
|
+
return activeCues;
|
8481
|
+
},
|
8482
|
+
set: Function.prototype
|
8483
|
+
});
|
7985
8484
|
|
7986
|
-
|
7987
|
-
|
7988
|
-
|
7989
|
-
|
7990
|
-
|
7991
|
-
vjs.TextTrack.prototype.language_;
|
8485
|
+
if (options.src) {
|
8486
|
+
loadTrack(options.src, tt);
|
8487
|
+
} else {
|
8488
|
+
tt.loaded_ = true;
|
8489
|
+
}
|
7992
8490
|
|
7993
|
-
|
7994
|
-
|
7995
|
-
|
7996
|
-
*/
|
7997
|
-
vjs.TextTrack.prototype.language = function(){
|
7998
|
-
return this.language_;
|
8491
|
+
if (vjs.IS_IE8) {
|
8492
|
+
return tt;
|
8493
|
+
}
|
7999
8494
|
};
|
8000
8495
|
|
8001
|
-
|
8002
|
-
|
8003
|
-
* Spec def: readonly attribute DOMString label;
|
8004
|
-
* @private
|
8005
|
-
*/
|
8006
|
-
vjs.TextTrack.prototype.label_;
|
8496
|
+
vjs.TextTrack.prototype = vjs.obj.create(vjs.EventEmitter.prototype);
|
8497
|
+
vjs.TextTrack.prototype.constructor = vjs.TextTrack;
|
8007
8498
|
|
8008
|
-
|
8009
|
-
*
|
8010
|
-
* @return {String}
|
8499
|
+
/*
|
8500
|
+
* cuechange - One or more cues in the track have become active or stopped being active.
|
8011
8501
|
*/
|
8012
|
-
vjs.TextTrack.prototype.
|
8013
|
-
|
8502
|
+
vjs.TextTrack.prototype.allowedEvents_ = {
|
8503
|
+
'cuechange': 'cuechange'
|
8014
8504
|
};
|
8015
8505
|
|
8016
|
-
|
8017
|
-
|
8018
|
-
|
8019
|
-
* @private
|
8020
|
-
*/
|
8021
|
-
vjs.TextTrack.prototype.cues_;
|
8506
|
+
vjs.TextTrack.prototype.addCue = function(cue) {
|
8507
|
+
var tracks = this.player_.textTracks(),
|
8508
|
+
i = 0;
|
8022
8509
|
|
8023
|
-
|
8024
|
-
|
8025
|
-
|
8026
|
-
|
8027
|
-
|
8028
|
-
|
8510
|
+
if (tracks) {
|
8511
|
+
for (; i < tracks.length; i++) {
|
8512
|
+
if (tracks[i] !== this) {
|
8513
|
+
tracks[i].removeCue(cue);
|
8514
|
+
}
|
8515
|
+
}
|
8516
|
+
}
|
8517
|
+
|
8518
|
+
this.cues_.push(cue);
|
8519
|
+
this['cues'].setCues_(this.cues_);
|
8029
8520
|
};
|
8030
8521
|
|
8031
|
-
|
8032
|
-
|
8033
|
-
|
8034
|
-
|
8035
|
-
|
8036
|
-
vjs.TextTrack.prototype.activeCues_;
|
8522
|
+
vjs.TextTrack.prototype.removeCue = function(removeCue) {
|
8523
|
+
var i = 0,
|
8524
|
+
l = this.cues_.length,
|
8525
|
+
cue,
|
8526
|
+
removed = false;
|
8037
8527
|
|
8038
|
-
|
8039
|
-
|
8040
|
-
|
8041
|
-
|
8042
|
-
|
8043
|
-
|
8528
|
+
for (; i < l; i++) {
|
8529
|
+
cue = this.cues_[i];
|
8530
|
+
if (cue === removeCue) {
|
8531
|
+
this.cues_.splice(i, 1);
|
8532
|
+
removed = true;
|
8533
|
+
}
|
8534
|
+
}
|
8535
|
+
|
8536
|
+
if (removed) {
|
8537
|
+
this.cues.setCues_(this.cues_);
|
8538
|
+
}
|
8044
8539
|
};
|
8045
8540
|
|
8046
|
-
|
8047
|
-
*
|
8048
|
-
* const unsigned short NONE = 0;
|
8049
|
-
* const unsigned short LOADING = 1;
|
8050
|
-
* const unsigned short LOADED = 2;
|
8051
|
-
* const unsigned short ERROR = 3;
|
8052
|
-
* readonly attribute unsigned short readyState;
|
8053
|
-
* @private
|
8541
|
+
/*
|
8542
|
+
* Downloading stuff happens below this point
|
8054
8543
|
*/
|
8055
|
-
|
8544
|
+
var loadTrack, parseCues, indexOf;
|
8056
8545
|
|
8057
|
-
|
8058
|
-
|
8059
|
-
|
8060
|
-
|
8061
|
-
|
8062
|
-
return this.readyState_;
|
8063
|
-
};
|
8546
|
+
loadTrack = function(src, track) {
|
8547
|
+
vjs.xhr(src, vjs.bind(this, function(err, response, responseBody){
|
8548
|
+
if (err) {
|
8549
|
+
return vjs.log.error(err);
|
8550
|
+
}
|
8064
8551
|
|
8065
|
-
/**
|
8066
|
-
* Mode describes if the track is showing, hidden, or disabled
|
8067
|
-
* const unsigned short OFF = 0;
|
8068
|
-
* const unsigned short HIDDEN = 1; (still triggering cuechange events, but not visible)
|
8069
|
-
* const unsigned short SHOWING = 2;
|
8070
|
-
* attribute unsigned short mode;
|
8071
|
-
* @private
|
8072
|
-
*/
|
8073
|
-
vjs.TextTrack.prototype.mode_;
|
8074
8552
|
|
8075
|
-
|
8076
|
-
|
8077
|
-
|
8078
|
-
*/
|
8079
|
-
vjs.TextTrack.prototype.mode = function(){
|
8080
|
-
return this.mode_;
|
8553
|
+
track.loaded_ = true;
|
8554
|
+
parseCues(responseBody, track);
|
8555
|
+
}));
|
8081
8556
|
};
|
8082
8557
|
|
8083
|
-
|
8084
|
-
|
8085
|
-
|
8086
|
-
|
8087
|
-
|
8088
|
-
|
8089
|
-
|
8090
|
-
});
|
8091
|
-
};
|
8558
|
+
parseCues = function(srcContent, track) {
|
8559
|
+
if (typeof window['WebVTT'] !== 'function') {
|
8560
|
+
//try again a bit later
|
8561
|
+
return window.setTimeout(function() {
|
8562
|
+
parseCues(srcContent, track);
|
8563
|
+
}, 25);
|
8564
|
+
}
|
8092
8565
|
|
8093
|
-
|
8094
|
-
* Show: Mode Showing (2)
|
8095
|
-
* Indicates that the text track is active. If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily.
|
8096
|
-
* The user agent is maintaining a list of which cues are active, and events are being fired accordingly.
|
8097
|
-
* In addition, for text tracks whose kind is subtitles or captions, the cues are being displayed over the video as appropriate;
|
8098
|
-
* for text tracks whose kind is descriptions, the user agent is making the cues available to the user in a non-visual fashion;
|
8099
|
-
* and for text tracks whose kind is chapters, the user agent is making available to the user a mechanism by which the user can navigate to any point in the media resource by selecting a cue.
|
8100
|
-
* The showing by default state is used in conjunction with the default attribute on track elements to indicate that the text track was enabled due to that attribute.
|
8101
|
-
* This allows the user agent to override the state if a later track is discovered that is more appropriate per the user's preferences.
|
8102
|
-
*/
|
8103
|
-
vjs.TextTrack.prototype.show = function(){
|
8104
|
-
this.activate();
|
8566
|
+
var parser = new window['WebVTT']['Parser'](window, window['vttjs'], window['WebVTT']['StringDecoder']());
|
8105
8567
|
|
8106
|
-
|
8568
|
+
parser['oncue'] = function(cue) {
|
8569
|
+
track.addCue(cue);
|
8570
|
+
};
|
8571
|
+
parser['onparsingerror'] = function(error) {
|
8572
|
+
vjs.log.error(error);
|
8573
|
+
};
|
8107
8574
|
|
8108
|
-
|
8109
|
-
|
8575
|
+
parser['parse'](srcContent);
|
8576
|
+
parser['flush']();
|
8110
8577
|
};
|
8111
8578
|
|
8112
|
-
|
8113
|
-
* Hide: Mode Hidden (1)
|
8114
|
-
* Indicates that the text track is active, but that the user agent is not actively displaying the cues.
|
8115
|
-
* If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily.
|
8116
|
-
* The user agent is maintaining a list of which cues are active, and events are being fired accordingly.
|
8117
|
-
*/
|
8118
|
-
vjs.TextTrack.prototype.hide = function(){
|
8119
|
-
// When hidden, cues are still triggered. Disable to stop triggering.
|
8120
|
-
this.activate();
|
8579
|
+
indexOf = function(searchElement, fromIndex) {
|
8121
8580
|
|
8122
|
-
|
8581
|
+
var k;
|
8123
8582
|
|
8124
|
-
|
8125
|
-
|
8126
|
-
}
|
8583
|
+
if (this == null) {
|
8584
|
+
throw new TypeError('"this" is null or not defined');
|
8585
|
+
}
|
8127
8586
|
|
8128
|
-
|
8129
|
-
* Disable: Mode Off/Disable (0)
|
8130
|
-
* Indicates that the text track is not active. Other than for the purposes of exposing the track in the DOM, the user agent is ignoring the text track.
|
8131
|
-
* No cues are active, no events are fired, and the user agent will not attempt to obtain the track's cues.
|
8132
|
-
*/
|
8133
|
-
vjs.TextTrack.prototype.disable = function(){
|
8134
|
-
// If showing, hide.
|
8135
|
-
if (this.mode_ == 2) { this.hide(); }
|
8587
|
+
var O = Object(this);
|
8136
8588
|
|
8137
|
-
|
8138
|
-
this.deactivate();
|
8589
|
+
var len = O.length >>> 0;
|
8139
8590
|
|
8140
|
-
|
8141
|
-
|
8142
|
-
}
|
8591
|
+
if (len === 0) {
|
8592
|
+
return -1;
|
8593
|
+
}
|
8143
8594
|
|
8144
|
-
|
8145
|
-
|
8146
|
-
|
8147
|
-
|
8148
|
-
|
8149
|
-
if (this.readyState_ === 0) { this.load(); }
|
8595
|
+
var n = +fromIndex || 0;
|
8596
|
+
|
8597
|
+
if (Math.abs(n) === Infinity) {
|
8598
|
+
n = 0;
|
8599
|
+
}
|
8150
8600
|
|
8151
|
-
|
8152
|
-
|
8153
|
-
|
8154
|
-
// Using unique ID for bind function so other tracks don't remove listener
|
8155
|
-
this.player_.on('timeupdate', vjs.bind(this, this.update, this.id_));
|
8601
|
+
if (n >= len) {
|
8602
|
+
return -1;
|
8603
|
+
}
|
8156
8604
|
|
8157
|
-
|
8158
|
-
this.player_.on('ended', vjs.bind(this, this.reset, this.id_));
|
8605
|
+
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
8159
8606
|
|
8160
|
-
|
8161
|
-
if (
|
8162
|
-
|
8607
|
+
while (k < len) {
|
8608
|
+
if (k in O && O[k] === searchElement) {
|
8609
|
+
return k;
|
8163
8610
|
}
|
8611
|
+
k++;
|
8164
8612
|
}
|
8613
|
+
return -1;
|
8165
8614
|
};
|
8166
8615
|
|
8167
|
-
|
8168
|
-
|
8169
|
-
|
8170
|
-
|
8171
|
-
|
8172
|
-
|
8173
|
-
|
8174
|
-
|
8175
|
-
|
8176
|
-
|
8177
|
-
|
8178
|
-
|
8616
|
+
})();
|
8617
|
+
/*
|
8618
|
+
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist
|
8619
|
+
*
|
8620
|
+
* interface TextTrackList : EventTarget {
|
8621
|
+
* readonly attribute unsigned long length;
|
8622
|
+
* getter TextTrack (unsigned long index);
|
8623
|
+
* TextTrack? getTrackById(DOMString id);
|
8624
|
+
*
|
8625
|
+
* attribute EventHandler onchange;
|
8626
|
+
* attribute EventHandler onaddtrack;
|
8627
|
+
* attribute EventHandler onremovetrack;
|
8628
|
+
* };
|
8629
|
+
*/
|
8630
|
+
vjs.TextTrackList = function(tracks) {
|
8631
|
+
var list = this,
|
8632
|
+
prop,
|
8633
|
+
i = 0;
|
8634
|
+
|
8635
|
+
if (vjs.IS_IE8) {
|
8636
|
+
list = document.createElement('custom');
|
8637
|
+
|
8638
|
+
for (prop in vjs.TextTrackList.prototype) {
|
8639
|
+
list[prop] = vjs.TextTrackList.prototype[prop];
|
8640
|
+
}
|
8641
|
+
}
|
8179
8642
|
|
8180
|
-
|
8181
|
-
|
8182
|
-
//
|
8183
|
-
// Not loaded
|
8184
|
-
// Indicates that the text track is known to exist (e.g. it has been declared with a track element), but its cues have not been obtained.
|
8185
|
-
//
|
8186
|
-
// Loading
|
8187
|
-
// Indicates that the text track is loading and there have been no fatal errors encountered so far. Further cues might still be added to the track.
|
8188
|
-
//
|
8189
|
-
// Loaded
|
8190
|
-
// Indicates that the text track has been loaded with no fatal errors. No new cues will be added to the track except if the text track corresponds to a MutableTextTrack object.
|
8191
|
-
//
|
8192
|
-
// Failed to load
|
8193
|
-
// Indicates that the text track was enabled, but when the user agent attempted to obtain it, this failed in some way (e.g. URL could not be resolved, network error, unknown text track format). Some or all of the cues are likely missing and will not be obtained.
|
8194
|
-
vjs.TextTrack.prototype.load = function(){
|
8643
|
+
tracks = tracks || [];
|
8644
|
+
list.tracks_ = [];
|
8195
8645
|
|
8196
|
-
|
8197
|
-
|
8198
|
-
|
8199
|
-
|
8200
|
-
|
8201
|
-
return this.onError(err);
|
8202
|
-
}
|
8646
|
+
Object.defineProperty(list, 'length', {
|
8647
|
+
get: function() {
|
8648
|
+
return this.tracks_.length;
|
8649
|
+
}
|
8650
|
+
});
|
8203
8651
|
|
8204
|
-
|
8205
|
-
|
8652
|
+
for (; i < tracks.length; i++) {
|
8653
|
+
list.addTrack_(tracks[i]);
|
8206
8654
|
}
|
8207
8655
|
|
8656
|
+
if (vjs.IS_IE8) {
|
8657
|
+
return list;
|
8658
|
+
}
|
8208
8659
|
};
|
8209
8660
|
|
8210
|
-
vjs.
|
8211
|
-
|
8212
|
-
this.readyState_ = 3;
|
8213
|
-
this.trigger('error');
|
8214
|
-
};
|
8215
|
-
|
8216
|
-
// Parse the WebVTT text format for cue times.
|
8217
|
-
// TODO: Separate parser into own class so alternative timed text formats can be used. (TTML, DFXP)
|
8218
|
-
vjs.TextTrack.prototype.parseCues = function(srcContent) {
|
8219
|
-
var cue, time, text,
|
8220
|
-
lines = srcContent.split('\n'),
|
8221
|
-
line = '', id;
|
8661
|
+
vjs.TextTrackList.prototype = vjs.obj.create(vjs.EventEmitter.prototype);
|
8662
|
+
vjs.TextTrackList.prototype.constructor = vjs.TextTrackList;
|
8222
8663
|
|
8223
|
-
|
8224
|
-
|
8664
|
+
/*
|
8665
|
+
* change - One or more tracks in the track list have been enabled or disabled.
|
8666
|
+
* addtrack - A track has been added to the track list.
|
8667
|
+
* removetrack - A track has been removed from the track list.
|
8668
|
+
*/
|
8669
|
+
vjs.TextTrackList.prototype.allowedEvents_ = {
|
8670
|
+
'change': 'change',
|
8671
|
+
'addtrack': 'addtrack',
|
8672
|
+
'removetrack': 'removetrack'
|
8673
|
+
};
|
8225
8674
|
|
8226
|
-
|
8675
|
+
// emulate attribute EventHandler support to allow for feature detection
|
8676
|
+
(function() {
|
8677
|
+
var event;
|
8227
8678
|
|
8228
|
-
|
8679
|
+
for (event in vjs.TextTrackList.prototype.allowedEvents_) {
|
8680
|
+
vjs.TextTrackList.prototype['on' + event] = null;
|
8681
|
+
}
|
8682
|
+
})();
|
8229
8683
|
|
8230
|
-
|
8231
|
-
|
8232
|
-
|
8233
|
-
|
8234
|
-
|
8235
|
-
|
8236
|
-
} else {
|
8237
|
-
id = this.cues_.length;
|
8684
|
+
vjs.TextTrackList.prototype.addTrack_ = function(track) {
|
8685
|
+
var index = this.tracks_.length;
|
8686
|
+
if (!(''+index in this)) {
|
8687
|
+
Object.defineProperty(this, index, {
|
8688
|
+
get: function() {
|
8689
|
+
return this.tracks_[index];
|
8238
8690
|
}
|
8691
|
+
});
|
8692
|
+
}
|
8239
8693
|
|
8240
|
-
|
8241
|
-
|
8242
|
-
|
8243
|
-
|
8244
|
-
};
|
8245
|
-
|
8246
|
-
// Timing line
|
8247
|
-
time = line.split(/[\t ]+/);
|
8248
|
-
cue.startTime = this.parseCueTime(time[0]);
|
8249
|
-
cue.endTime = this.parseCueTime(time[2]);
|
8250
|
-
|
8251
|
-
// Additional lines - Cue Text
|
8252
|
-
text = [];
|
8694
|
+
track.addEventListener('modechange', vjs.bind(this, function() {
|
8695
|
+
this.trigger('change');
|
8696
|
+
}));
|
8697
|
+
this.tracks_.push(track);
|
8253
8698
|
|
8254
|
-
|
8255
|
-
|
8256
|
-
|
8257
|
-
|
8258
|
-
|
8699
|
+
this.trigger({
|
8700
|
+
type: 'addtrack',
|
8701
|
+
track: track
|
8702
|
+
});
|
8703
|
+
};
|
8259
8704
|
|
8260
|
-
|
8705
|
+
vjs.TextTrackList.prototype.removeTrack_ = function(rtrack) {
|
8706
|
+
var i = 0,
|
8707
|
+
l = this.length,
|
8708
|
+
result = null,
|
8709
|
+
track;
|
8261
8710
|
|
8262
|
-
|
8263
|
-
|
8711
|
+
for (; i < l; i++) {
|
8712
|
+
track = this[i];
|
8713
|
+
if (track === rtrack) {
|
8714
|
+
this.tracks_.splice(i, 1);
|
8715
|
+
break;
|
8264
8716
|
}
|
8265
8717
|
}
|
8266
8718
|
|
8267
|
-
this.
|
8268
|
-
|
8719
|
+
this.trigger({
|
8720
|
+
type: 'removetrack',
|
8721
|
+
track: rtrack
|
8722
|
+
});
|
8269
8723
|
};
|
8270
8724
|
|
8725
|
+
vjs.TextTrackList.prototype.getTrackById = function(id) {
|
8726
|
+
var i = 0,
|
8727
|
+
l = this.length,
|
8728
|
+
result = null,
|
8729
|
+
track;
|
8271
8730
|
|
8272
|
-
|
8273
|
-
|
8274
|
-
|
8275
|
-
|
8276
|
-
|
8277
|
-
|
8278
|
-
|
8279
|
-
if (parts.length == 3) {
|
8280
|
-
hours = parts[0];
|
8281
|
-
minutes = parts[1];
|
8282
|
-
other = parts[2];
|
8283
|
-
} else {
|
8284
|
-
hours = 0;
|
8285
|
-
minutes = parts[0];
|
8286
|
-
other = parts[1];
|
8287
|
-
}
|
8288
|
-
|
8289
|
-
// Break other (seconds, milliseconds, and flags) by spaces
|
8290
|
-
// TODO: Make additional cue layout settings work with flags
|
8291
|
-
other = other.split(/\s+/);
|
8292
|
-
// Remove seconds. Seconds is the first part before any spaces.
|
8293
|
-
seconds = other.splice(0,1)[0];
|
8294
|
-
// Could use either . or , for decimal
|
8295
|
-
seconds = seconds.split(/\.|,/);
|
8296
|
-
// Get milliseconds
|
8297
|
-
ms = parseFloat(seconds[1]);
|
8298
|
-
seconds = seconds[0];
|
8299
|
-
|
8300
|
-
// hours => seconds
|
8301
|
-
time += parseFloat(hours) * 3600;
|
8302
|
-
// minutes => seconds
|
8303
|
-
time += parseFloat(minutes) * 60;
|
8304
|
-
// Add seconds
|
8305
|
-
time += parseFloat(seconds);
|
8306
|
-
// Add milliseconds
|
8307
|
-
if (ms) { time += ms/1000; }
|
8308
|
-
|
8309
|
-
return time;
|
8310
|
-
};
|
8311
|
-
|
8312
|
-
// Update active cues whenever timeupdate events are triggered on the player.
|
8313
|
-
vjs.TextTrack.prototype.update = function(){
|
8314
|
-
if (this.cues_.length > 0) {
|
8315
|
-
|
8316
|
-
// Get current player time, adjust for track offset
|
8317
|
-
var offset = this.player_.options()['trackTimeOffset'] || 0;
|
8318
|
-
var time = this.player_.currentTime() + offset;
|
8319
|
-
|
8320
|
-
// Check if the new time is outside the time box created by the the last update.
|
8321
|
-
if (this.prevChange === undefined || time < this.prevChange || this.nextChange <= time) {
|
8322
|
-
var cues = this.cues_,
|
8323
|
-
|
8324
|
-
// Create a new time box for this state.
|
8325
|
-
newNextChange = this.player_.duration(), // Start at beginning of the timeline
|
8326
|
-
newPrevChange = 0, // Start at end
|
8327
|
-
|
8328
|
-
reverse = false, // Set the direction of the loop through the cues. Optimized the cue check.
|
8329
|
-
newCues = [], // Store new active cues.
|
8330
|
-
|
8331
|
-
// Store where in the loop the current active cues are, to provide a smart starting point for the next loop.
|
8332
|
-
firstActiveIndex, lastActiveIndex,
|
8333
|
-
cue, i; // Loop vars
|
8334
|
-
|
8335
|
-
// Check if time is going forwards or backwards (scrubbing/rewinding)
|
8336
|
-
// If we know the direction we can optimize the starting position and direction of the loop through the cues array.
|
8337
|
-
if (time >= this.nextChange || this.nextChange === undefined) { // NextChange should happen
|
8338
|
-
// Forwards, so start at the index of the first active cue and loop forward
|
8339
|
-
i = (this.firstActiveIndex !== undefined) ? this.firstActiveIndex : 0;
|
8340
|
-
} else {
|
8341
|
-
// Backwards, so start at the index of the last active cue and loop backward
|
8342
|
-
reverse = true;
|
8343
|
-
i = (this.lastActiveIndex !== undefined) ? this.lastActiveIndex : cues.length - 1;
|
8344
|
-
}
|
8731
|
+
for (; i < l; i++) {
|
8732
|
+
track = this[i];
|
8733
|
+
if (track.id === id) {
|
8734
|
+
result = track;
|
8735
|
+
break;
|
8736
|
+
}
|
8737
|
+
}
|
8345
8738
|
|
8346
|
-
|
8347
|
-
|
8739
|
+
return result;
|
8740
|
+
};
|
8741
|
+
/*
|
8742
|
+
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist
|
8743
|
+
*
|
8744
|
+
* interface TextTrackCueList {
|
8745
|
+
* readonly attribute unsigned long length;
|
8746
|
+
* getter TextTrackCue (unsigned long index);
|
8747
|
+
* TextTrackCue? getCueById(DOMString id);
|
8748
|
+
* };
|
8749
|
+
*/
|
8348
8750
|
|
8349
|
-
|
8350
|
-
|
8351
|
-
|
8751
|
+
vjs.TextTrackCueList = function(cues) {
|
8752
|
+
var list = this,
|
8753
|
+
prop;
|
8352
8754
|
|
8353
|
-
|
8354
|
-
|
8355
|
-
}
|
8755
|
+
if (vjs.IS_IE8) {
|
8756
|
+
list = document.createElement('custom');
|
8356
8757
|
|
8357
|
-
|
8358
|
-
|
8359
|
-
|
8360
|
-
|
8758
|
+
for (prop in vjs.TextTrackCueList.prototype) {
|
8759
|
+
list[prop] = vjs.TextTrackCueList.prototype[prop];
|
8760
|
+
}
|
8761
|
+
}
|
8361
8762
|
|
8362
|
-
|
8363
|
-
} else if (time < cue.startTime) {
|
8364
|
-
newNextChange = Math.min(newNextChange, cue.startTime);
|
8763
|
+
vjs.TextTrackCueList.prototype.setCues_.call(list, cues);
|
8365
8764
|
|
8366
|
-
|
8367
|
-
|
8368
|
-
|
8765
|
+
Object.defineProperty(list, 'length', {
|
8766
|
+
get: function() {
|
8767
|
+
return this.length_;
|
8768
|
+
}
|
8769
|
+
});
|
8369
8770
|
|
8370
|
-
|
8371
|
-
|
8771
|
+
if (vjs.IS_IE8) {
|
8772
|
+
return list;
|
8773
|
+
}
|
8774
|
+
};
|
8372
8775
|
|
8373
|
-
|
8374
|
-
|
8776
|
+
vjs.TextTrackCueList.prototype.setCues_ = function(cues) {
|
8777
|
+
var oldLength = this.length || 0,
|
8778
|
+
i = 0,
|
8779
|
+
l = cues.length,
|
8780
|
+
defineProp;
|
8375
8781
|
|
8376
|
-
|
8377
|
-
|
8378
|
-
newCues.splice(0,0,cue);
|
8782
|
+
this.cues_ = cues;
|
8783
|
+
this.length_ = cues.length;
|
8379
8784
|
|
8380
|
-
|
8381
|
-
|
8382
|
-
|
8383
|
-
|
8384
|
-
|
8385
|
-
newCues.push(cue);
|
8386
|
-
|
8387
|
-
// If forward, the first current cue is our firstActiveIndex
|
8388
|
-
if (firstActiveIndex === undefined) { firstActiveIndex = i; }
|
8389
|
-
lastActiveIndex = i;
|
8390
|
-
}
|
8391
|
-
|
8392
|
-
newNextChange = Math.min(newNextChange, cue.endTime);
|
8393
|
-
newPrevChange = Math.max(newPrevChange, cue.startTime);
|
8394
|
-
|
8395
|
-
cue.active = true;
|
8785
|
+
defineProp = function(i) {
|
8786
|
+
if (!(''+i in this)) {
|
8787
|
+
Object.defineProperty(this, '' + i, {
|
8788
|
+
get: function() {
|
8789
|
+
return this.cues_[i];
|
8396
8790
|
}
|
8791
|
+
});
|
8792
|
+
}
|
8793
|
+
};
|
8397
8794
|
|
8398
|
-
|
8399
|
-
|
8400
|
-
|
8401
|
-
|
8402
|
-
// Walk up the array fo cues, break if at last
|
8403
|
-
if (i === cues.length - 1) { break; } else { i++; }
|
8404
|
-
}
|
8405
|
-
|
8406
|
-
}
|
8407
|
-
|
8408
|
-
this.activeCues_ = newCues;
|
8409
|
-
this.nextChange = newNextChange;
|
8410
|
-
this.prevChange = newPrevChange;
|
8411
|
-
this.firstActiveIndex = firstActiveIndex;
|
8412
|
-
this.lastActiveIndex = lastActiveIndex;
|
8413
|
-
|
8414
|
-
this.updateDisplay();
|
8415
|
-
|
8416
|
-
this.trigger('cuechange');
|
8795
|
+
if (oldLength < l) {
|
8796
|
+
i = oldLength;
|
8797
|
+
for(; i < l; i++) {
|
8798
|
+
defineProp.call(this, i);
|
8417
8799
|
}
|
8418
8800
|
}
|
8419
8801
|
};
|
8420
8802
|
|
8421
|
-
|
8422
|
-
|
8423
|
-
|
8424
|
-
|
8425
|
-
|
8803
|
+
vjs.TextTrackCueList.prototype.getCueById = function(id) {
|
8804
|
+
var i = 0,
|
8805
|
+
l = this.length,
|
8806
|
+
result = null,
|
8807
|
+
cue;
|
8426
8808
|
|
8427
|
-
for (;i<
|
8428
|
-
|
8809
|
+
for (; i < l; i++) {
|
8810
|
+
cue = this[i];
|
8811
|
+
if (cue.id === id) {
|
8812
|
+
result = cue;
|
8813
|
+
break;
|
8814
|
+
}
|
8429
8815
|
}
|
8430
8816
|
|
8431
|
-
|
8432
|
-
};
|
8433
|
-
|
8434
|
-
// Set all loop helper values back
|
8435
|
-
vjs.TextTrack.prototype.reset = function(){
|
8436
|
-
this.nextChange = 0;
|
8437
|
-
this.prevChange = this.player_.duration();
|
8438
|
-
this.firstActiveIndex = 0;
|
8439
|
-
this.lastActiveIndex = 0;
|
8817
|
+
return result;
|
8440
8818
|
};
|
8441
|
-
|
8442
|
-
|
8443
|
-
/**
|
8444
|
-
* The track component for managing the hiding and showing of captions
|
8445
|
-
*
|
8446
|
-
* @constructor
|
8447
|
-
*/
|
8448
|
-
vjs.CaptionsTrack = vjs.TextTrack.extend();
|
8449
|
-
vjs.CaptionsTrack.prototype.kind_ = 'captions';
|
8450
|
-
// Exporting here because Track creation requires the track kind
|
8451
|
-
// to be available on global object. e.g. new window['videojs'][Kind + 'Track']
|
8452
|
-
|
8453
|
-
/**
|
8454
|
-
* The track component for managing the hiding and showing of subtitles
|
8455
|
-
*
|
8456
|
-
* @constructor
|
8457
|
-
*/
|
8458
|
-
vjs.SubtitlesTrack = vjs.TextTrack.extend();
|
8459
|
-
vjs.SubtitlesTrack.prototype.kind_ = 'subtitles';
|
8460
|
-
|
8461
|
-
/**
|
8462
|
-
* The track component for managing the hiding and showing of chapters
|
8463
|
-
*
|
8464
|
-
* @constructor
|
8465
|
-
*/
|
8466
|
-
vjs.ChaptersTrack = vjs.TextTrack.extend();
|
8467
|
-
vjs.ChaptersTrack.prototype.kind_ = 'chapters';
|
8468
|
-
|
8819
|
+
(function() {
|
8820
|
+
'use strict';
|
8469
8821
|
|
8470
8822
|
/* Text Track Display
|
8471
8823
|
============================================================================= */
|
@@ -8481,22 +8833,176 @@ vjs.TextTrackDisplay = vjs.Component.extend({
|
|
8481
8833
|
init: function(player, options, ready){
|
8482
8834
|
vjs.Component.call(this, player, options, ready);
|
8483
8835
|
|
8836
|
+
player.on('loadstart', vjs.bind(this, this.toggleDisplay));
|
8837
|
+
|
8484
8838
|
// This used to be called during player init, but was causing an error
|
8485
8839
|
// if a track should show by default and the display hadn't loaded yet.
|
8486
8840
|
// Should probably be moved to an external track loader when we support
|
8487
8841
|
// tracks that don't need a display.
|
8488
|
-
|
8489
|
-
|
8490
|
-
|
8842
|
+
player.ready(vjs.bind(this, function() {
|
8843
|
+
if (player.tech && player.tech['featuresNativeTextTracks']) {
|
8844
|
+
this.hide();
|
8845
|
+
return;
|
8846
|
+
}
|
8847
|
+
|
8848
|
+
var i, tracks, track;
|
8849
|
+
|
8850
|
+
player.on('fullscreenchange', vjs.bind(this, this.updateDisplay));
|
8851
|
+
|
8852
|
+
tracks = player.options_['tracks'] || [];
|
8853
|
+
for (i = 0; i < tracks.length; i++) {
|
8854
|
+
track = tracks[i];
|
8855
|
+
this.player_.addRemoteTextTrack(track);
|
8856
|
+
}
|
8857
|
+
}));
|
8491
8858
|
}
|
8492
8859
|
});
|
8493
8860
|
|
8861
|
+
vjs.TextTrackDisplay.prototype.toggleDisplay = function() {
|
8862
|
+
if (this.player_.tech && this.player_.tech['featuresNativeTextTracks']) {
|
8863
|
+
this.hide();
|
8864
|
+
} else {
|
8865
|
+
this.show();
|
8866
|
+
}
|
8867
|
+
};
|
8868
|
+
|
8494
8869
|
vjs.TextTrackDisplay.prototype.createEl = function(){
|
8495
8870
|
return vjs.Component.prototype.createEl.call(this, 'div', {
|
8496
8871
|
className: 'vjs-text-track-display'
|
8497
8872
|
});
|
8498
8873
|
};
|
8499
8874
|
|
8875
|
+
vjs.TextTrackDisplay.prototype.clearDisplay = function() {
|
8876
|
+
if (typeof window['WebVTT'] === 'function') {
|
8877
|
+
window['WebVTT']['processCues'](window, [], this.el_);
|
8878
|
+
}
|
8879
|
+
};
|
8880
|
+
|
8881
|
+
// Add cue HTML to display
|
8882
|
+
var constructColor = function(color, opacity) {
|
8883
|
+
return 'rgba(' +
|
8884
|
+
// color looks like "#f0e"
|
8885
|
+
parseInt(color[1] + color[1], 16) + ',' +
|
8886
|
+
parseInt(color[2] + color[2], 16) + ',' +
|
8887
|
+
parseInt(color[3] + color[3], 16) + ',' +
|
8888
|
+
opacity + ')';
|
8889
|
+
};
|
8890
|
+
var darkGray = '#222';
|
8891
|
+
var lightGray = '#ccc';
|
8892
|
+
var fontMap = {
|
8893
|
+
monospace: 'monospace',
|
8894
|
+
sansSerif: 'sans-serif',
|
8895
|
+
serif: 'serif',
|
8896
|
+
monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
|
8897
|
+
monospaceSerif: '"Courier New", monospace',
|
8898
|
+
proportionalSansSerif: 'sans-serif',
|
8899
|
+
proportionalSerif: 'serif',
|
8900
|
+
casual: '"Comic Sans MS", Impact, fantasy',
|
8901
|
+
script: '"Monotype Corsiva", cursive',
|
8902
|
+
smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
|
8903
|
+
};
|
8904
|
+
var tryUpdateStyle = function(el, style, rule) {
|
8905
|
+
// some style changes will throw an error, particularly in IE8. Those should be noops.
|
8906
|
+
try {
|
8907
|
+
el.style[style] = rule;
|
8908
|
+
} catch (e) {}
|
8909
|
+
};
|
8910
|
+
|
8911
|
+
vjs.TextTrackDisplay.prototype.updateDisplay = function() {
|
8912
|
+
var tracks = this.player_.textTracks(),
|
8913
|
+
i = 0,
|
8914
|
+
track;
|
8915
|
+
|
8916
|
+
this.clearDisplay();
|
8917
|
+
|
8918
|
+
if (!tracks) {
|
8919
|
+
return;
|
8920
|
+
}
|
8921
|
+
|
8922
|
+
for (; i < tracks.length; i++) {
|
8923
|
+
track = tracks[i];
|
8924
|
+
if (track['mode'] === 'showing') {
|
8925
|
+
this.updateForTrack(track);
|
8926
|
+
}
|
8927
|
+
}
|
8928
|
+
};
|
8929
|
+
|
8930
|
+
vjs.TextTrackDisplay.prototype.updateForTrack = function(track) {
|
8931
|
+
if (typeof window['WebVTT'] !== 'function' || !track['activeCues']) {
|
8932
|
+
return;
|
8933
|
+
}
|
8934
|
+
|
8935
|
+
var i = 0,
|
8936
|
+
property,
|
8937
|
+
cueDiv,
|
8938
|
+
overrides = this.player_['textTrackSettings'].getValues(),
|
8939
|
+
fontSize,
|
8940
|
+
cues = [];
|
8941
|
+
|
8942
|
+
for (; i < track['activeCues'].length; i++) {
|
8943
|
+
cues.push(track['activeCues'][i]);
|
8944
|
+
}
|
8945
|
+
|
8946
|
+
window['WebVTT']['processCues'](window, track['activeCues'], this.el_);
|
8947
|
+
|
8948
|
+
i = cues.length;
|
8949
|
+
while (i--) {
|
8950
|
+
cueDiv = cues[i].displayState;
|
8951
|
+
if (overrides.color) {
|
8952
|
+
cueDiv.firstChild.style.color = overrides.color;
|
8953
|
+
}
|
8954
|
+
if (overrides.textOpacity) {
|
8955
|
+
tryUpdateStyle(cueDiv.firstChild,
|
8956
|
+
'color',
|
8957
|
+
constructColor(overrides.color || '#fff',
|
8958
|
+
overrides.textOpacity));
|
8959
|
+
}
|
8960
|
+
if (overrides.backgroundColor) {
|
8961
|
+
cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
|
8962
|
+
}
|
8963
|
+
if (overrides.backgroundOpacity) {
|
8964
|
+
tryUpdateStyle(cueDiv.firstChild,
|
8965
|
+
'backgroundColor',
|
8966
|
+
constructColor(overrides.backgroundColor || '#000',
|
8967
|
+
overrides.backgroundOpacity));
|
8968
|
+
}
|
8969
|
+
if (overrides.windowColor) {
|
8970
|
+
if (overrides.windowOpacity) {
|
8971
|
+
tryUpdateStyle(cueDiv,
|
8972
|
+
'backgroundColor',
|
8973
|
+
constructColor(overrides.windowColor, overrides.windowOpacity));
|
8974
|
+
} else {
|
8975
|
+
cueDiv.style.backgroundColor = overrides.windowColor;
|
8976
|
+
}
|
8977
|
+
}
|
8978
|
+
if (overrides.edgeStyle) {
|
8979
|
+
if (overrides.edgeStyle === 'dropshadow') {
|
8980
|
+
cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
|
8981
|
+
} else if (overrides.edgeStyle === 'raised') {
|
8982
|
+
cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
|
8983
|
+
} else if (overrides.edgeStyle === 'depressed') {
|
8984
|
+
cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
|
8985
|
+
} else if (overrides.edgeStyle === 'uniform') {
|
8986
|
+
cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
|
8987
|
+
}
|
8988
|
+
}
|
8989
|
+
if (overrides.fontPercent && overrides.fontPercent !== 1) {
|
8990
|
+
fontSize = window.parseFloat(cueDiv.style.fontSize);
|
8991
|
+
cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px';
|
8992
|
+
cueDiv.style.height = 'auto';
|
8993
|
+
cueDiv.style.top = 'auto';
|
8994
|
+
cueDiv.style.bottom = '2px';
|
8995
|
+
}
|
8996
|
+
if (overrides.fontFamily && overrides.fontFamily !== 'default') {
|
8997
|
+
if (overrides.fontFamily === 'small-caps') {
|
8998
|
+
cueDiv.firstChild.style.fontVariant = 'small-caps';
|
8999
|
+
} else {
|
9000
|
+
cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
|
9001
|
+
}
|
9002
|
+
}
|
9003
|
+
}
|
9004
|
+
};
|
9005
|
+
|
8500
9006
|
|
8501
9007
|
/**
|
8502
9008
|
* The specific menu item type for selecting a language within a text track kind
|
@@ -8506,24 +9012,98 @@ vjs.TextTrackDisplay.prototype.createEl = function(){
|
|
8506
9012
|
vjs.TextTrackMenuItem = vjs.MenuItem.extend({
|
8507
9013
|
/** @constructor */
|
8508
9014
|
init: function(player, options){
|
8509
|
-
var track = this.track = options['track']
|
9015
|
+
var track = this.track = options['track'],
|
9016
|
+
tracks = player.textTracks(),
|
9017
|
+
changeHandler,
|
9018
|
+
event;
|
9019
|
+
|
9020
|
+
if (tracks) {
|
9021
|
+
changeHandler = vjs.bind(this, function() {
|
9022
|
+
var selected = this.track['mode'] === 'showing',
|
9023
|
+
track,
|
9024
|
+
i,
|
9025
|
+
l;
|
9026
|
+
|
9027
|
+
if (this instanceof vjs.OffTextTrackMenuItem) {
|
9028
|
+
selected = true;
|
9029
|
+
|
9030
|
+
i = 0,
|
9031
|
+
l = tracks.length;
|
9032
|
+
|
9033
|
+
for (; i < l; i++) {
|
9034
|
+
track = tracks[i];
|
9035
|
+
if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') {
|
9036
|
+
selected = false;
|
9037
|
+
break;
|
9038
|
+
}
|
9039
|
+
}
|
9040
|
+
}
|
9041
|
+
|
9042
|
+
this.selected(selected);
|
9043
|
+
});
|
9044
|
+
tracks.addEventListener('change', changeHandler);
|
9045
|
+
player.on('dispose', function() {
|
9046
|
+
tracks.removeEventListener('change', changeHandler);
|
9047
|
+
});
|
9048
|
+
}
|
8510
9049
|
|
8511
9050
|
// Modify options for parent MenuItem class's init.
|
8512
|
-
options['label'] = track
|
8513
|
-
options['selected'] = track
|
9051
|
+
options['label'] = track['label'] || track['language'] || 'Unknown';
|
9052
|
+
options['selected'] = track['default'] || track['mode'] === 'showing';
|
8514
9053
|
vjs.MenuItem.call(this, player, options);
|
8515
9054
|
|
8516
|
-
|
9055
|
+
// iOS7 doesn't dispatch change events to TextTrackLists when an
|
9056
|
+
// associated track's mode changes. Without something like
|
9057
|
+
// Object.observe() (also not present on iOS7), it's not
|
9058
|
+
// possible to detect changes to the mode attribute and polyfill
|
9059
|
+
// the change event. As a poor substitute, we manually dispatch
|
9060
|
+
// change events whenever the controls modify the mode.
|
9061
|
+
if (tracks && tracks.onchange === undefined) {
|
9062
|
+
this.on(['tap', 'click'], function() {
|
9063
|
+
if (typeof window.Event !== 'object') {
|
9064
|
+
// Android 2.3 throws an Illegal Constructor error for window.Event
|
9065
|
+
try {
|
9066
|
+
event = new window.Event('change');
|
9067
|
+
} catch(err){}
|
9068
|
+
}
|
9069
|
+
|
9070
|
+
if (!event) {
|
9071
|
+
event = document.createEvent('Event');
|
9072
|
+
event.initEvent('change', true, true);
|
9073
|
+
}
|
9074
|
+
|
9075
|
+
tracks.dispatchEvent(event);
|
9076
|
+
});
|
9077
|
+
}
|
8517
9078
|
}
|
8518
9079
|
});
|
8519
9080
|
|
8520
9081
|
vjs.TextTrackMenuItem.prototype.onClick = function(){
|
9082
|
+
var kind = this.track['kind'],
|
9083
|
+
tracks = this.player_.textTracks(),
|
9084
|
+
mode,
|
9085
|
+
track,
|
9086
|
+
i = 0;
|
9087
|
+
|
8521
9088
|
vjs.MenuItem.prototype.onClick.call(this);
|
8522
|
-
this.player_.showTextTrack(this.track.id_, this.track.kind());
|
8523
|
-
};
|
8524
9089
|
|
8525
|
-
|
8526
|
-
|
9090
|
+
if (!tracks) {
|
9091
|
+
return;
|
9092
|
+
}
|
9093
|
+
|
9094
|
+
for (; i < tracks.length; i++) {
|
9095
|
+
track = tracks[i];
|
9096
|
+
|
9097
|
+
if (track['kind'] !== kind) {
|
9098
|
+
continue;
|
9099
|
+
}
|
9100
|
+
|
9101
|
+
if (track === this.track) {
|
9102
|
+
track['mode'] = 'showing';
|
9103
|
+
} else {
|
9104
|
+
track['mode'] = 'disabled';
|
9105
|
+
}
|
9106
|
+
}
|
8527
9107
|
};
|
8528
9108
|
|
8529
9109
|
/**
|
@@ -8537,35 +9117,34 @@ vjs.OffTextTrackMenuItem = vjs.TextTrackMenuItem.extend({
|
|
8537
9117
|
// Create pseudo track info
|
8538
9118
|
// Requires options['kind']
|
8539
9119
|
options['track'] = {
|
8540
|
-
kind:
|
8541
|
-
player: player,
|
8542
|
-
label:
|
8543
|
-
|
8544
|
-
mode:
|
9120
|
+
'kind': options['kind'],
|
9121
|
+
'player': player,
|
9122
|
+
'label': options['kind'] + ' off',
|
9123
|
+
'default': false,
|
9124
|
+
'mode': 'disabled'
|
8545
9125
|
};
|
8546
9126
|
vjs.TextTrackMenuItem.call(this, player, options);
|
8547
9127
|
this.selected(true);
|
8548
9128
|
}
|
8549
9129
|
});
|
8550
9130
|
|
8551
|
-
vjs.
|
8552
|
-
|
8553
|
-
|
8554
|
-
|
8555
|
-
|
8556
|
-
|
8557
|
-
|
8558
|
-
|
8559
|
-
|
9131
|
+
vjs.CaptionSettingsMenuItem = vjs.TextTrackMenuItem.extend({
|
9132
|
+
init: function(player, options) {
|
9133
|
+
options['track'] = {
|
9134
|
+
'kind': options['kind'],
|
9135
|
+
'player': player,
|
9136
|
+
'label': options['kind'] + ' settings',
|
9137
|
+
'default': false,
|
9138
|
+
mode: 'disabled'
|
9139
|
+
};
|
8560
9140
|
|
8561
|
-
|
8562
|
-
|
8563
|
-
if (track.kind() == this.track.kind() && track.mode() == 2) {
|
8564
|
-
off = false;
|
8565
|
-
}
|
9141
|
+
vjs.TextTrackMenuItem.call(this, player, options);
|
9142
|
+
this.addClass('vjs-texttrack-settings');
|
8566
9143
|
}
|
9144
|
+
});
|
8567
9145
|
|
8568
|
-
|
9146
|
+
vjs.CaptionSettingsMenuItem.prototype.onClick = function() {
|
9147
|
+
this.player().getChild('textTrackSettings').show();
|
8569
9148
|
};
|
8570
9149
|
|
8571
9150
|
/**
|
@@ -8576,49 +9155,53 @@ vjs.OffTextTrackMenuItem.prototype.update = function(){
|
|
8576
9155
|
vjs.TextTrackButton = vjs.MenuButton.extend({
|
8577
9156
|
/** @constructor */
|
8578
9157
|
init: function(player, options){
|
9158
|
+
var tracks, updateHandler;
|
9159
|
+
|
8579
9160
|
vjs.MenuButton.call(this, player, options);
|
8580
9161
|
|
9162
|
+
tracks = this.player_.textTracks();
|
9163
|
+
|
8581
9164
|
if (this.items.length <= 1) {
|
8582
9165
|
this.hide();
|
8583
9166
|
}
|
8584
|
-
}
|
8585
|
-
});
|
8586
|
-
|
8587
|
-
// vjs.TextTrackButton.prototype.buttonPressed = false;
|
8588
|
-
|
8589
|
-
// vjs.TextTrackButton.prototype.createMenu = function(){
|
8590
|
-
// var menu = new vjs.Menu(this.player_);
|
8591
|
-
|
8592
|
-
// // Add a title list item to the top
|
8593
|
-
// // menu.el().appendChild(vjs.createEl('li', {
|
8594
|
-
// // className: 'vjs-menu-title',
|
8595
|
-
// // innerHTML: vjs.capitalize(this.kind_),
|
8596
|
-
// // tabindex: -1
|
8597
|
-
// // }));
|
8598
|
-
|
8599
|
-
// this.items = this.createItems();
|
8600
9167
|
|
8601
|
-
|
8602
|
-
|
8603
|
-
|
8604
|
-
// }
|
9168
|
+
if (!tracks) {
|
9169
|
+
return;
|
9170
|
+
}
|
8605
9171
|
|
8606
|
-
|
8607
|
-
|
9172
|
+
updateHandler = vjs.bind(this, this.update);
|
9173
|
+
tracks.addEventListener('removetrack', updateHandler);
|
9174
|
+
tracks.addEventListener('addtrack', updateHandler);
|
8608
9175
|
|
8609
|
-
|
8610
|
-
|
9176
|
+
this.player_.on('dispose', function() {
|
9177
|
+
tracks.removeEventListener('removetrack', updateHandler);
|
9178
|
+
tracks.removeEventListener('addtrack', updateHandler);
|
9179
|
+
});
|
9180
|
+
}
|
9181
|
+
});
|
8611
9182
|
|
8612
9183
|
// Create a menu item for each text track
|
8613
9184
|
vjs.TextTrackButton.prototype.createItems = function(){
|
8614
|
-
var items = [], track;
|
9185
|
+
var items = [], track, tracks;
|
9186
|
+
|
9187
|
+
if (this instanceof vjs.CaptionsButton && !(this.player().tech && this.player().tech['featuresNativeTextTracks'])) {
|
9188
|
+
items.push(new vjs.CaptionSettingsMenuItem(this.player_, { 'kind': this.kind_ }));
|
9189
|
+
}
|
8615
9190
|
|
8616
9191
|
// Add an OFF menu item to turn all tracks off
|
8617
9192
|
items.push(new vjs.OffTextTrackMenuItem(this.player_, { 'kind': this.kind_ }));
|
8618
9193
|
|
8619
|
-
|
8620
|
-
|
8621
|
-
|
9194
|
+
tracks = this.player_.textTracks();
|
9195
|
+
|
9196
|
+
if (!tracks) {
|
9197
|
+
return items;
|
9198
|
+
}
|
9199
|
+
|
9200
|
+
for (var i = 0; i < tracks.length; i++) {
|
9201
|
+
track = tracks[i];
|
9202
|
+
|
9203
|
+
// only add tracks that are of the appropriate kind and have a label
|
9204
|
+
if (track['kind'] === this.kind_) {
|
8622
9205
|
items.push(new vjs.TextTrackMenuItem(this.player_, {
|
8623
9206
|
'track': track
|
8624
9207
|
}));
|
@@ -8644,6 +9227,22 @@ vjs.CaptionsButton.prototype.kind_ = 'captions';
|
|
8644
9227
|
vjs.CaptionsButton.prototype.buttonText = 'Captions';
|
8645
9228
|
vjs.CaptionsButton.prototype.className = 'vjs-captions-button';
|
8646
9229
|
|
9230
|
+
vjs.CaptionsButton.prototype.update = function() {
|
9231
|
+
var threshold = 2;
|
9232
|
+
vjs.TextTrackButton.prototype.update.call(this);
|
9233
|
+
|
9234
|
+
// if native, then threshold is 1 because no settings button
|
9235
|
+
if (this.player().tech && this.player().tech['featuresNativeTextTracks']) {
|
9236
|
+
threshold = 1;
|
9237
|
+
}
|
9238
|
+
|
9239
|
+
if (this.items && this.items.length > threshold) {
|
9240
|
+
this.show();
|
9241
|
+
} else {
|
9242
|
+
this.hide();
|
9243
|
+
}
|
9244
|
+
};
|
9245
|
+
|
8647
9246
|
/**
|
8648
9247
|
* The button component for toggling and selecting subtitles
|
8649
9248
|
*
|
@@ -8680,11 +9279,17 @@ vjs.ChaptersButton.prototype.className = 'vjs-chapters-button';
|
|
8680
9279
|
|
8681
9280
|
// Create a menu item for each text track
|
8682
9281
|
vjs.ChaptersButton.prototype.createItems = function(){
|
8683
|
-
var items = [], track;
|
9282
|
+
var items = [], track, tracks;
|
8684
9283
|
|
8685
|
-
|
8686
|
-
|
8687
|
-
|
9284
|
+
tracks = this.player_.textTracks();
|
9285
|
+
|
9286
|
+
if (!tracks) {
|
9287
|
+
return items;
|
9288
|
+
}
|
9289
|
+
|
9290
|
+
for (var i = 0; i < tracks.length; i++) {
|
9291
|
+
track = tracks[i];
|
9292
|
+
if (track['kind'] === this.kind_) {
|
8688
9293
|
items.push(new vjs.TextTrackMenuItem(this.player_, {
|
8689
9294
|
'track': track
|
8690
9295
|
}));
|
@@ -8695,18 +9300,23 @@ vjs.ChaptersButton.prototype.createItems = function(){
|
|
8695
9300
|
};
|
8696
9301
|
|
8697
9302
|
vjs.ChaptersButton.prototype.createMenu = function(){
|
8698
|
-
var tracks = this.player_.textTracks(),
|
9303
|
+
var tracks = this.player_.textTracks() || [],
|
8699
9304
|
i = 0,
|
8700
|
-
|
9305
|
+
l = tracks.length,
|
8701
9306
|
track, chaptersTrack,
|
8702
9307
|
items = this.items = [];
|
8703
9308
|
|
8704
|
-
for (;i<
|
9309
|
+
for (; i < l; i++) {
|
8705
9310
|
track = tracks[i];
|
8706
|
-
if (track
|
8707
|
-
if (track.
|
8708
|
-
track
|
8709
|
-
|
9311
|
+
if (track['kind'] == this.kind_) {
|
9312
|
+
if (!track.cues) {
|
9313
|
+
track['mode'] = 'hidden';
|
9314
|
+
/* jshint loopfunc:true */
|
9315
|
+
// TODO see if we can figure out a better way of doing this https://github.com/videojs/video.js/issues/1864
|
9316
|
+
window.setTimeout(vjs.bind(this, function() {
|
9317
|
+
this.createMenu();
|
9318
|
+
}), 100);
|
9319
|
+
/* jshint loopfunc:false */
|
8710
9320
|
} else {
|
8711
9321
|
chaptersTrack = track;
|
8712
9322
|
break;
|
@@ -8725,11 +9335,11 @@ vjs.ChaptersButton.prototype.createMenu = function(){
|
|
8725
9335
|
}
|
8726
9336
|
|
8727
9337
|
if (chaptersTrack) {
|
8728
|
-
var cues = chaptersTrack
|
9338
|
+
var cues = chaptersTrack['cues'], cue, mi;
|
8729
9339
|
i = 0;
|
8730
|
-
|
9340
|
+
l = cues.length;
|
8731
9341
|
|
8732
|
-
for (;i<
|
9342
|
+
for (; i < l; i++) {
|
8733
9343
|
cue = cues[i];
|
8734
9344
|
|
8735
9345
|
mi = new vjs.ChaptersTrackMenuItem(this.player_, {
|
@@ -8764,10 +9374,10 @@ vjs.ChaptersTrackMenuItem = vjs.MenuItem.extend({
|
|
8764
9374
|
|
8765
9375
|
// Modify options for parent MenuItem class's init.
|
8766
9376
|
options['label'] = cue.text;
|
8767
|
-
options['selected'] = (cue
|
9377
|
+
options['selected'] = (cue['startTime'] <= currentTime && currentTime < cue['endTime']);
|
8768
9378
|
vjs.MenuItem.call(this, player, options);
|
8769
9379
|
|
8770
|
-
track.
|
9380
|
+
track.addEventListener('cuechange', vjs.bind(this, this.update));
|
8771
9381
|
}
|
8772
9382
|
});
|
8773
9383
|
|
@@ -8782,22 +9392,297 @@ vjs.ChaptersTrackMenuItem.prototype.update = function(){
|
|
8782
9392
|
currentTime = this.player_.currentTime();
|
8783
9393
|
|
8784
9394
|
// vjs.log(currentTime, cue.startTime);
|
8785
|
-
this.selected(cue
|
9395
|
+
this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']);
|
8786
9396
|
};
|
9397
|
+
})();
|
9398
|
+
(function() {
|
9399
|
+
'use strict';
|
8787
9400
|
|
8788
|
-
|
8789
|
-
|
8790
|
-
|
8791
|
-
|
8792
|
-
|
8793
|
-
|
9401
|
+
vjs.TextTrackSettings = vjs.Component.extend({
|
9402
|
+
init: function(player, options) {
|
9403
|
+
vjs.Component.call(this, player, options);
|
9404
|
+
this.hide();
|
9405
|
+
|
9406
|
+
vjs.on(this.el().querySelector('.vjs-done-button'), 'click', vjs.bind(this, function() {
|
9407
|
+
this.saveSettings();
|
9408
|
+
this.hide();
|
9409
|
+
}));
|
9410
|
+
|
9411
|
+
vjs.on(this.el().querySelector('.vjs-default-button'), 'click', vjs.bind(this, function() {
|
9412
|
+
this.el().querySelector('.vjs-fg-color > select').selectedIndex = 0;
|
9413
|
+
this.el().querySelector('.vjs-bg-color > select').selectedIndex = 0;
|
9414
|
+
this.el().querySelector('.window-color > select').selectedIndex = 0;
|
9415
|
+
this.el().querySelector('.vjs-text-opacity > select').selectedIndex = 0;
|
9416
|
+
this.el().querySelector('.vjs-bg-opacity > select').selectedIndex = 0;
|
9417
|
+
this.el().querySelector('.vjs-window-opacity > select').selectedIndex = 0;
|
9418
|
+
this.el().querySelector('.vjs-edge-style select').selectedIndex = 0;
|
9419
|
+
this.el().querySelector('.vjs-font-family select').selectedIndex = 0;
|
9420
|
+
this.el().querySelector('.vjs-font-percent select').selectedIndex = 2;
|
9421
|
+
this.updateDisplay();
|
9422
|
+
}));
|
9423
|
+
|
9424
|
+
vjs.on(this.el().querySelector('.vjs-fg-color > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9425
|
+
vjs.on(this.el().querySelector('.vjs-bg-color > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9426
|
+
vjs.on(this.el().querySelector('.window-color > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9427
|
+
vjs.on(this.el().querySelector('.vjs-text-opacity > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9428
|
+
vjs.on(this.el().querySelector('.vjs-bg-opacity > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9429
|
+
vjs.on(this.el().querySelector('.vjs-window-opacity > select'), 'change', vjs.bind(this, this.updateDisplay));
|
9430
|
+
vjs.on(this.el().querySelector('.vjs-font-percent select'), 'change', vjs.bind(this, this.updateDisplay));
|
9431
|
+
vjs.on(this.el().querySelector('.vjs-edge-style select'), 'change', vjs.bind(this, this.updateDisplay));
|
9432
|
+
vjs.on(this.el().querySelector('.vjs-font-family select'), 'change', vjs.bind(this, this.updateDisplay));
|
9433
|
+
|
9434
|
+
if (player.options()['persistTextTrackSettings']) {
|
9435
|
+
this.restoreSettings();
|
9436
|
+
}
|
9437
|
+
}
|
9438
|
+
});
|
9439
|
+
|
9440
|
+
vjs.TextTrackSettings.prototype.createEl = function() {
|
9441
|
+
return vjs.Component.prototype.createEl.call(this, 'div', {
|
9442
|
+
className: 'vjs-caption-settings vjs-modal-overlay',
|
9443
|
+
innerHTML: captionOptionsMenuTemplate()
|
9444
|
+
});
|
9445
|
+
};
|
9446
|
+
|
9447
|
+
vjs.TextTrackSettings.prototype.getValues = function() {
|
9448
|
+
var el, bgOpacity, textOpacity, windowOpacity, textEdge, fontFamily, fgColor, bgColor, windowColor, result, name, fontPercent;
|
9449
|
+
|
9450
|
+
el = this.el();
|
9451
|
+
|
9452
|
+
textEdge = getSelectedOptionValue(el.querySelector('.vjs-edge-style select'));
|
9453
|
+
fontFamily = getSelectedOptionValue(el.querySelector('.vjs-font-family select'));
|
9454
|
+
fgColor = getSelectedOptionValue(el.querySelector('.vjs-fg-color > select'));
|
9455
|
+
textOpacity = getSelectedOptionValue(el.querySelector('.vjs-text-opacity > select'));
|
9456
|
+
bgColor = getSelectedOptionValue(el.querySelector('.vjs-bg-color > select'));
|
9457
|
+
bgOpacity = getSelectedOptionValue(el.querySelector('.vjs-bg-opacity > select'));
|
9458
|
+
windowColor = getSelectedOptionValue(el.querySelector('.window-color > select'));
|
9459
|
+
windowOpacity = getSelectedOptionValue(el.querySelector('.vjs-window-opacity > select'));
|
9460
|
+
fontPercent = window['parseFloat'](getSelectedOptionValue(el.querySelector('.vjs-font-percent > select')));
|
9461
|
+
|
9462
|
+
result = {
|
9463
|
+
'backgroundOpacity': bgOpacity,
|
9464
|
+
'textOpacity': textOpacity,
|
9465
|
+
'windowOpacity': windowOpacity,
|
9466
|
+
'edgeStyle': textEdge,
|
9467
|
+
'fontFamily': fontFamily,
|
9468
|
+
'color': fgColor,
|
9469
|
+
'backgroundColor': bgColor,
|
9470
|
+
'windowColor': windowColor,
|
9471
|
+
'fontPercent': fontPercent
|
9472
|
+
};
|
9473
|
+
for (name in result) {
|
9474
|
+
if (result[name] === '' || result[name] === 'none' || (name === 'fontPercent' && result[name] === 1.00)) {
|
9475
|
+
delete result[name];
|
9476
|
+
}
|
9477
|
+
}
|
9478
|
+
return result;
|
9479
|
+
};
|
9480
|
+
|
9481
|
+
vjs.TextTrackSettings.prototype.setValues = function(values) {
|
9482
|
+
var el = this.el(), fontPercent;
|
9483
|
+
|
9484
|
+
setSelectedOption(el.querySelector('.vjs-edge-style select'), values.edgeStyle);
|
9485
|
+
setSelectedOption(el.querySelector('.vjs-font-family select'), values.fontFamily);
|
9486
|
+
setSelectedOption(el.querySelector('.vjs-fg-color > select'), values.color);
|
9487
|
+
setSelectedOption(el.querySelector('.vjs-text-opacity > select'), values.textOpacity);
|
9488
|
+
setSelectedOption(el.querySelector('.vjs-bg-color > select'), values.backgroundColor);
|
9489
|
+
setSelectedOption(el.querySelector('.vjs-bg-opacity > select'), values.backgroundOpacity);
|
9490
|
+
setSelectedOption(el.querySelector('.window-color > select'), values.windowColor);
|
9491
|
+
setSelectedOption(el.querySelector('.vjs-window-opacity > select'), values.windowOpacity);
|
9492
|
+
|
9493
|
+
fontPercent = values.fontPercent;
|
9494
|
+
|
9495
|
+
if (fontPercent) {
|
9496
|
+
fontPercent = fontPercent.toFixed(2);
|
9497
|
+
}
|
9498
|
+
|
9499
|
+
setSelectedOption(el.querySelector('.vjs-font-percent > select'), fontPercent);
|
9500
|
+
};
|
9501
|
+
|
9502
|
+
vjs.TextTrackSettings.prototype.restoreSettings = function() {
|
9503
|
+
var values;
|
9504
|
+
try {
|
9505
|
+
values = JSON.parse(window.localStorage.getItem('vjs-text-track-settings'));
|
9506
|
+
} catch (e) {}
|
9507
|
+
|
9508
|
+
if (values) {
|
9509
|
+
this.setValues(values);
|
9510
|
+
}
|
9511
|
+
};
|
9512
|
+
|
9513
|
+
vjs.TextTrackSettings.prototype.saveSettings = function() {
|
9514
|
+
var values;
|
9515
|
+
|
9516
|
+
if (!this.player_.options()['persistTextTrackSettings']) {
|
9517
|
+
return;
|
9518
|
+
}
|
9519
|
+
|
9520
|
+
values = this.getValues();
|
9521
|
+
try {
|
9522
|
+
if (!vjs.isEmpty(values)) {
|
9523
|
+
window.localStorage.setItem('vjs-text-track-settings', JSON.stringify(values));
|
9524
|
+
} else {
|
9525
|
+
window.localStorage.removeItem('vjs-text-track-settings');
|
9526
|
+
}
|
9527
|
+
} catch (e) {}
|
9528
|
+
};
|
9529
|
+
|
9530
|
+
vjs.TextTrackSettings.prototype.updateDisplay = function() {
|
9531
|
+
var ttDisplay = this.player_.getChild('textTrackDisplay');
|
9532
|
+
if (ttDisplay) {
|
9533
|
+
ttDisplay.updateDisplay();
|
9534
|
+
}
|
9535
|
+
};
|
9536
|
+
|
9537
|
+
function getSelectedOptionValue(target) {
|
9538
|
+
var selectedOption;
|
9539
|
+
// not all browsers support selectedOptions, so, fallback to options
|
9540
|
+
if (target.selectedOptions) {
|
9541
|
+
selectedOption = target.selectedOptions[0];
|
9542
|
+
} else if (target.options) {
|
9543
|
+
selectedOption = target.options[target.options.selectedIndex];
|
9544
|
+
}
|
9545
|
+
|
9546
|
+
return selectedOption.value;
|
9547
|
+
}
|
8794
9548
|
|
8795
|
-
|
8796
|
-
|
8797
|
-
|
8798
|
-
|
8799
|
-
|
8800
|
-
|
9549
|
+
function setSelectedOption(target, value) {
|
9550
|
+
var i, option;
|
9551
|
+
|
9552
|
+
if (!value) {
|
9553
|
+
return;
|
9554
|
+
}
|
9555
|
+
|
9556
|
+
for (i = 0; i < target.options.length; i++) {
|
9557
|
+
option = target.options[i];
|
9558
|
+
if (option.value === value) {
|
9559
|
+
break;
|
9560
|
+
}
|
9561
|
+
}
|
9562
|
+
|
9563
|
+
if (target.selectedOptions) {
|
9564
|
+
target.selectedOptions[0] = option;
|
9565
|
+
}
|
9566
|
+
|
9567
|
+
target.selectedIndex = i;
|
9568
|
+
}
|
9569
|
+
|
9570
|
+
function captionOptionsMenuTemplate() {
|
9571
|
+
return '<div class="vjs-tracksettings">' +
|
9572
|
+
'<div class="vjs-tracksettings-colors">' +
|
9573
|
+
'<div class="vjs-fg-color vjs-tracksetting">' +
|
9574
|
+
'<label class="vjs-label">Foreground</label>' +
|
9575
|
+
'<select>' +
|
9576
|
+
'<option value="">---</option>' +
|
9577
|
+
'<option value="#FFF">White</option>' +
|
9578
|
+
'<option value="#000">Black</option>' +
|
9579
|
+
'<option value="#F00">Red</option>' +
|
9580
|
+
'<option value="#0F0">Green</option>' +
|
9581
|
+
'<option value="#00F">Blue</option>' +
|
9582
|
+
'<option value="#FF0">Yellow</option>' +
|
9583
|
+
'<option value="#F0F">Magenta</option>' +
|
9584
|
+
'<option value="#0FF">Cyan</option>' +
|
9585
|
+
'</select>' +
|
9586
|
+
'<span class="vjs-text-opacity vjs-opacity">' +
|
9587
|
+
'<select>' +
|
9588
|
+
'<option value="">---</option>' +
|
9589
|
+
'<option value="1">Opaque</option>' +
|
9590
|
+
'<option value="0.5">Semi-Opaque</option>' +
|
9591
|
+
'</select>' +
|
9592
|
+
'</span>' +
|
9593
|
+
'</div>' + // vjs-fg-color
|
9594
|
+
'<div class="vjs-bg-color vjs-tracksetting">' +
|
9595
|
+
'<label class="vjs-label">Background</label>' +
|
9596
|
+
'<select>' +
|
9597
|
+
'<option value="">---</option>' +
|
9598
|
+
'<option value="#FFF">White</option>' +
|
9599
|
+
'<option value="#000">Black</option>' +
|
9600
|
+
'<option value="#F00">Red</option>' +
|
9601
|
+
'<option value="#0F0">Green</option>' +
|
9602
|
+
'<option value="#00F">Blue</option>' +
|
9603
|
+
'<option value="#FF0">Yellow</option>' +
|
9604
|
+
'<option value="#F0F">Magenta</option>' +
|
9605
|
+
'<option value="#0FF">Cyan</option>' +
|
9606
|
+
'</select>' +
|
9607
|
+
'<span class="vjs-bg-opacity vjs-opacity">' +
|
9608
|
+
'<select>' +
|
9609
|
+
'<option value="">---</option>' +
|
9610
|
+
'<option value="1">Opaque</option>' +
|
9611
|
+
'<option value="0.5">Semi-Transparent</option>' +
|
9612
|
+
'<option value="0">Transparent</option>' +
|
9613
|
+
'</select>' +
|
9614
|
+
'</span>' +
|
9615
|
+
'</div>' + // vjs-bg-color
|
9616
|
+
'<div class="window-color vjs-tracksetting">' +
|
9617
|
+
'<label class="vjs-label">Window</label>' +
|
9618
|
+
'<select>' +
|
9619
|
+
'<option value="">---</option>' +
|
9620
|
+
'<option value="#FFF">White</option>' +
|
9621
|
+
'<option value="#000">Black</option>' +
|
9622
|
+
'<option value="#F00">Red</option>' +
|
9623
|
+
'<option value="#0F0">Green</option>' +
|
9624
|
+
'<option value="#00F">Blue</option>' +
|
9625
|
+
'<option value="#FF0">Yellow</option>' +
|
9626
|
+
'<option value="#F0F">Magenta</option>' +
|
9627
|
+
'<option value="#0FF">Cyan</option>' +
|
9628
|
+
'</select>' +
|
9629
|
+
'<span class="vjs-window-opacity vjs-opacity">' +
|
9630
|
+
'<select>' +
|
9631
|
+
'<option value="">---</option>' +
|
9632
|
+
'<option value="1">Opaque</option>' +
|
9633
|
+
'<option value="0.5">Semi-Transparent</option>' +
|
9634
|
+
'<option value="0">Transparent</option>' +
|
9635
|
+
'</select>' +
|
9636
|
+
'</span>' +
|
9637
|
+
'</div>' + // vjs-window-color
|
9638
|
+
'</div>' + // vjs-tracksettings
|
9639
|
+
'<div class="vjs-tracksettings-font">' +
|
9640
|
+
'<div class="vjs-font-percent vjs-tracksetting">' +
|
9641
|
+
'<label class="vjs-label">Font Size</label>' +
|
9642
|
+
'<select>' +
|
9643
|
+
'<option value="0.50">50%</option>' +
|
9644
|
+
'<option value="0.75">75%</option>' +
|
9645
|
+
'<option value="1.00" selected>100%</option>' +
|
9646
|
+
'<option value="1.25">125%</option>' +
|
9647
|
+
'<option value="1.50">150%</option>' +
|
9648
|
+
'<option value="1.75">175%</option>' +
|
9649
|
+
'<option value="2.00">200%</option>' +
|
9650
|
+
'<option value="3.00">300%</option>' +
|
9651
|
+
'<option value="4.00">400%</option>' +
|
9652
|
+
'</select>' +
|
9653
|
+
'</div>' + // vjs-font-percent
|
9654
|
+
'<div class="vjs-edge-style vjs-tracksetting">' +
|
9655
|
+
'<label class="vjs-label">Text Edge Style</label>' +
|
9656
|
+
'<select>' +
|
9657
|
+
'<option value="none">None</option>' +
|
9658
|
+
'<option value="raised">Raised</option>' +
|
9659
|
+
'<option value="depressed">Depressed</option>' +
|
9660
|
+
'<option value="uniform">Uniform</option>' +
|
9661
|
+
'<option value="dropshadow">Dropshadow</option>' +
|
9662
|
+
'</select>' +
|
9663
|
+
'</div>' + // vjs-edge-style
|
9664
|
+
'<div class="vjs-font-family vjs-tracksetting">' +
|
9665
|
+
'<label class="vjs-label">Font Family</label>' +
|
9666
|
+
'<select>' +
|
9667
|
+
'<option value="">Default</option>' +
|
9668
|
+
'<option value="monospaceSerif">Monospace Serif</option>' +
|
9669
|
+
'<option value="proportionalSerif">Proportional Serif</option>' +
|
9670
|
+
'<option value="monospaceSansSerif">Monospace Sans-Serif</option>' +
|
9671
|
+
'<option value="proportionalSansSerif">Proportional Sans-Serif</option>' +
|
9672
|
+
'<option value="casual">Casual</option>' +
|
9673
|
+
'<option value="script">Script</option>' +
|
9674
|
+
'<option value="small-caps">Small Caps</option>' +
|
9675
|
+
'</select>' +
|
9676
|
+
'</div>' + // vjs-font-family
|
9677
|
+
'</div>' +
|
9678
|
+
'</div>' +
|
9679
|
+
'<div class="vjs-tracksettings-controls">' +
|
9680
|
+
'<button class="vjs-default-button">Defaults</button>' +
|
9681
|
+
'<button class="vjs-done-button">Done</button>' +
|
9682
|
+
'</div>';
|
9683
|
+
}
|
9684
|
+
|
9685
|
+
})();
|
8801
9686
|
/**
|
8802
9687
|
* @fileoverview Add JSON support
|
8803
9688
|
* @suppress {undefinedVars}
|
@@ -8963,3 +9848,1954 @@ vjs.autoSetupTimeout(1);
|
|
8963
9848
|
vjs.plugin = function(name, init){
|
8964
9849
|
vjs.Player.prototype[name] = init;
|
8965
9850
|
};
|
9851
|
+
|
9852
|
+
/* vtt.js - v0.11.11 (https://github.com/mozilla/vtt.js) built on 22-01-2015 */
|
9853
|
+
|
9854
|
+
(function(root) {
|
9855
|
+
var vttjs = root.vttjs = {};
|
9856
|
+
var cueShim = vttjs.VTTCue;
|
9857
|
+
var regionShim = vttjs.VTTRegion;
|
9858
|
+
var oldVTTCue = root.VTTCue;
|
9859
|
+
var oldVTTRegion = root.VTTRegion;
|
9860
|
+
|
9861
|
+
vttjs.shim = function() {
|
9862
|
+
vttjs.VTTCue = cueShim;
|
9863
|
+
vttjs.VTTRegion = regionShim;
|
9864
|
+
};
|
9865
|
+
|
9866
|
+
vttjs.restore = function() {
|
9867
|
+
vttjs.VTTCue = oldVTTCue;
|
9868
|
+
vttjs.VTTRegion = oldVTTRegion;
|
9869
|
+
};
|
9870
|
+
}(this));
|
9871
|
+
|
9872
|
+
/**
|
9873
|
+
* Copyright 2013 vtt.js Contributors
|
9874
|
+
*
|
9875
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
9876
|
+
* you may not use this file except in compliance with the License.
|
9877
|
+
* You may obtain a copy of the License at
|
9878
|
+
*
|
9879
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9880
|
+
*
|
9881
|
+
* Unless required by applicable law or agreed to in writing, software
|
9882
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
9883
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
9884
|
+
* See the License for the specific language governing permissions and
|
9885
|
+
* limitations under the License.
|
9886
|
+
*/
|
9887
|
+
|
9888
|
+
(function(root, vttjs) {
|
9889
|
+
|
9890
|
+
var autoKeyword = "auto";
|
9891
|
+
var directionSetting = {
|
9892
|
+
"": true,
|
9893
|
+
"lr": true,
|
9894
|
+
"rl": true
|
9895
|
+
};
|
9896
|
+
var alignSetting = {
|
9897
|
+
"start": true,
|
9898
|
+
"middle": true,
|
9899
|
+
"end": true,
|
9900
|
+
"left": true,
|
9901
|
+
"right": true
|
9902
|
+
};
|
9903
|
+
|
9904
|
+
function findDirectionSetting(value) {
|
9905
|
+
if (typeof value !== "string") {
|
9906
|
+
return false;
|
9907
|
+
}
|
9908
|
+
var dir = directionSetting[value.toLowerCase()];
|
9909
|
+
return dir ? value.toLowerCase() : false;
|
9910
|
+
}
|
9911
|
+
|
9912
|
+
function findAlignSetting(value) {
|
9913
|
+
if (typeof value !== "string") {
|
9914
|
+
return false;
|
9915
|
+
}
|
9916
|
+
var align = alignSetting[value.toLowerCase()];
|
9917
|
+
return align ? value.toLowerCase() : false;
|
9918
|
+
}
|
9919
|
+
|
9920
|
+
function extend(obj) {
|
9921
|
+
var i = 1;
|
9922
|
+
for (; i < arguments.length; i++) {
|
9923
|
+
var cobj = arguments[i];
|
9924
|
+
for (var p in cobj) {
|
9925
|
+
obj[p] = cobj[p];
|
9926
|
+
}
|
9927
|
+
}
|
9928
|
+
|
9929
|
+
return obj;
|
9930
|
+
}
|
9931
|
+
|
9932
|
+
function VTTCue(startTime, endTime, text) {
|
9933
|
+
var cue = this;
|
9934
|
+
var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
|
9935
|
+
var baseObj = {};
|
9936
|
+
|
9937
|
+
if (isIE8) {
|
9938
|
+
cue = document.createElement('custom');
|
9939
|
+
} else {
|
9940
|
+
baseObj.enumerable = true;
|
9941
|
+
}
|
9942
|
+
|
9943
|
+
/**
|
9944
|
+
* Shim implementation specific properties. These properties are not in
|
9945
|
+
* the spec.
|
9946
|
+
*/
|
9947
|
+
|
9948
|
+
// Lets us know when the VTTCue's data has changed in such a way that we need
|
9949
|
+
// to recompute its display state. This lets us compute its display state
|
9950
|
+
// lazily.
|
9951
|
+
cue.hasBeenReset = false;
|
9952
|
+
|
9953
|
+
/**
|
9954
|
+
* VTTCue and TextTrackCue properties
|
9955
|
+
* http://dev.w3.org/html5/webvtt/#vttcue-interface
|
9956
|
+
*/
|
9957
|
+
|
9958
|
+
var _id = "";
|
9959
|
+
var _pauseOnExit = false;
|
9960
|
+
var _startTime = startTime;
|
9961
|
+
var _endTime = endTime;
|
9962
|
+
var _text = text;
|
9963
|
+
var _region = null;
|
9964
|
+
var _vertical = "";
|
9965
|
+
var _snapToLines = true;
|
9966
|
+
var _line = "auto";
|
9967
|
+
var _lineAlign = "start";
|
9968
|
+
var _position = 50;
|
9969
|
+
var _positionAlign = "middle";
|
9970
|
+
var _size = 50;
|
9971
|
+
var _align = "middle";
|
9972
|
+
|
9973
|
+
Object.defineProperty(cue,
|
9974
|
+
"id", extend({}, baseObj, {
|
9975
|
+
get: function() {
|
9976
|
+
return _id;
|
9977
|
+
},
|
9978
|
+
set: function(value) {
|
9979
|
+
_id = "" + value;
|
9980
|
+
}
|
9981
|
+
}));
|
9982
|
+
|
9983
|
+
Object.defineProperty(cue,
|
9984
|
+
"pauseOnExit", extend({}, baseObj, {
|
9985
|
+
get: function() {
|
9986
|
+
return _pauseOnExit;
|
9987
|
+
},
|
9988
|
+
set: function(value) {
|
9989
|
+
_pauseOnExit = !!value;
|
9990
|
+
}
|
9991
|
+
}));
|
9992
|
+
|
9993
|
+
Object.defineProperty(cue,
|
9994
|
+
"startTime", extend({}, baseObj, {
|
9995
|
+
get: function() {
|
9996
|
+
return _startTime;
|
9997
|
+
},
|
9998
|
+
set: function(value) {
|
9999
|
+
if (typeof value !== "number") {
|
10000
|
+
throw new TypeError("Start time must be set to a number.");
|
10001
|
+
}
|
10002
|
+
_startTime = value;
|
10003
|
+
this.hasBeenReset = true;
|
10004
|
+
}
|
10005
|
+
}));
|
10006
|
+
|
10007
|
+
Object.defineProperty(cue,
|
10008
|
+
"endTime", extend({}, baseObj, {
|
10009
|
+
get: function() {
|
10010
|
+
return _endTime;
|
10011
|
+
},
|
10012
|
+
set: function(value) {
|
10013
|
+
if (typeof value !== "number") {
|
10014
|
+
throw new TypeError("End time must be set to a number.");
|
10015
|
+
}
|
10016
|
+
_endTime = value;
|
10017
|
+
this.hasBeenReset = true;
|
10018
|
+
}
|
10019
|
+
}));
|
10020
|
+
|
10021
|
+
Object.defineProperty(cue,
|
10022
|
+
"text", extend({}, baseObj, {
|
10023
|
+
get: function() {
|
10024
|
+
return _text;
|
10025
|
+
},
|
10026
|
+
set: function(value) {
|
10027
|
+
_text = "" + value;
|
10028
|
+
this.hasBeenReset = true;
|
10029
|
+
}
|
10030
|
+
}));
|
10031
|
+
|
10032
|
+
Object.defineProperty(cue,
|
10033
|
+
"region", extend({}, baseObj, {
|
10034
|
+
get: function() {
|
10035
|
+
return _region;
|
10036
|
+
},
|
10037
|
+
set: function(value) {
|
10038
|
+
_region = value;
|
10039
|
+
this.hasBeenReset = true;
|
10040
|
+
}
|
10041
|
+
}));
|
10042
|
+
|
10043
|
+
Object.defineProperty(cue,
|
10044
|
+
"vertical", extend({}, baseObj, {
|
10045
|
+
get: function() {
|
10046
|
+
return _vertical;
|
10047
|
+
},
|
10048
|
+
set: function(value) {
|
10049
|
+
var setting = findDirectionSetting(value);
|
10050
|
+
// Have to check for false because the setting an be an empty string.
|
10051
|
+
if (setting === false) {
|
10052
|
+
throw new SyntaxError("An invalid or illegal string was specified.");
|
10053
|
+
}
|
10054
|
+
_vertical = setting;
|
10055
|
+
this.hasBeenReset = true;
|
10056
|
+
}
|
10057
|
+
}));
|
10058
|
+
|
10059
|
+
Object.defineProperty(cue,
|
10060
|
+
"snapToLines", extend({}, baseObj, {
|
10061
|
+
get: function() {
|
10062
|
+
return _snapToLines;
|
10063
|
+
},
|
10064
|
+
set: function(value) {
|
10065
|
+
_snapToLines = !!value;
|
10066
|
+
this.hasBeenReset = true;
|
10067
|
+
}
|
10068
|
+
}));
|
10069
|
+
|
10070
|
+
Object.defineProperty(cue,
|
10071
|
+
"line", extend({}, baseObj, {
|
10072
|
+
get: function() {
|
10073
|
+
return _line;
|
10074
|
+
},
|
10075
|
+
set: function(value) {
|
10076
|
+
if (typeof value !== "number" && value !== autoKeyword) {
|
10077
|
+
throw new SyntaxError("An invalid number or illegal string was specified.");
|
10078
|
+
}
|
10079
|
+
_line = value;
|
10080
|
+
this.hasBeenReset = true;
|
10081
|
+
}
|
10082
|
+
}));
|
10083
|
+
|
10084
|
+
Object.defineProperty(cue,
|
10085
|
+
"lineAlign", extend({}, baseObj, {
|
10086
|
+
get: function() {
|
10087
|
+
return _lineAlign;
|
10088
|
+
},
|
10089
|
+
set: function(value) {
|
10090
|
+
var setting = findAlignSetting(value);
|
10091
|
+
if (!setting) {
|
10092
|
+
throw new SyntaxError("An invalid or illegal string was specified.");
|
10093
|
+
}
|
10094
|
+
_lineAlign = setting;
|
10095
|
+
this.hasBeenReset = true;
|
10096
|
+
}
|
10097
|
+
}));
|
10098
|
+
|
10099
|
+
Object.defineProperty(cue,
|
10100
|
+
"position", extend({}, baseObj, {
|
10101
|
+
get: function() {
|
10102
|
+
return _position;
|
10103
|
+
},
|
10104
|
+
set: function(value) {
|
10105
|
+
if (value < 0 || value > 100) {
|
10106
|
+
throw new Error("Position must be between 0 and 100.");
|
10107
|
+
}
|
10108
|
+
_position = value;
|
10109
|
+
this.hasBeenReset = true;
|
10110
|
+
}
|
10111
|
+
}));
|
10112
|
+
|
10113
|
+
Object.defineProperty(cue,
|
10114
|
+
"positionAlign", extend({}, baseObj, {
|
10115
|
+
get: function() {
|
10116
|
+
return _positionAlign;
|
10117
|
+
},
|
10118
|
+
set: function(value) {
|
10119
|
+
var setting = findAlignSetting(value);
|
10120
|
+
if (!setting) {
|
10121
|
+
throw new SyntaxError("An invalid or illegal string was specified.");
|
10122
|
+
}
|
10123
|
+
_positionAlign = setting;
|
10124
|
+
this.hasBeenReset = true;
|
10125
|
+
}
|
10126
|
+
}));
|
10127
|
+
|
10128
|
+
Object.defineProperty(cue,
|
10129
|
+
"size", extend({}, baseObj, {
|
10130
|
+
get: function() {
|
10131
|
+
return _size;
|
10132
|
+
},
|
10133
|
+
set: function(value) {
|
10134
|
+
if (value < 0 || value > 100) {
|
10135
|
+
throw new Error("Size must be between 0 and 100.");
|
10136
|
+
}
|
10137
|
+
_size = value;
|
10138
|
+
this.hasBeenReset = true;
|
10139
|
+
}
|
10140
|
+
}));
|
10141
|
+
|
10142
|
+
Object.defineProperty(cue,
|
10143
|
+
"align", extend({}, baseObj, {
|
10144
|
+
get: function() {
|
10145
|
+
return _align;
|
10146
|
+
},
|
10147
|
+
set: function(value) {
|
10148
|
+
var setting = findAlignSetting(value);
|
10149
|
+
if (!setting) {
|
10150
|
+
throw new SyntaxError("An invalid or illegal string was specified.");
|
10151
|
+
}
|
10152
|
+
_align = setting;
|
10153
|
+
this.hasBeenReset = true;
|
10154
|
+
}
|
10155
|
+
}));
|
10156
|
+
|
10157
|
+
/**
|
10158
|
+
* Other <track> spec defined properties
|
10159
|
+
*/
|
10160
|
+
|
10161
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
|
10162
|
+
cue.displayState = undefined;
|
10163
|
+
|
10164
|
+
if (isIE8) {
|
10165
|
+
return cue;
|
10166
|
+
}
|
10167
|
+
}
|
10168
|
+
|
10169
|
+
/**
|
10170
|
+
* VTTCue methods
|
10171
|
+
*/
|
10172
|
+
|
10173
|
+
VTTCue.prototype.getCueAsHTML = function() {
|
10174
|
+
// Assume WebVTT.convertCueToDOMTree is on the global.
|
10175
|
+
return WebVTT.convertCueToDOMTree(window, this.text);
|
10176
|
+
};
|
10177
|
+
|
10178
|
+
root.VTTCue = root.VTTCue || VTTCue;
|
10179
|
+
vttjs.VTTCue = VTTCue;
|
10180
|
+
}(this, (this.vttjs || {})));
|
10181
|
+
|
10182
|
+
/**
|
10183
|
+
* Copyright 2013 vtt.js Contributors
|
10184
|
+
*
|
10185
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
10186
|
+
* you may not use this file except in compliance with the License.
|
10187
|
+
* You may obtain a copy of the License at
|
10188
|
+
*
|
10189
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10190
|
+
*
|
10191
|
+
* Unless required by applicable law or agreed to in writing, software
|
10192
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
10193
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10194
|
+
* See the License for the specific language governing permissions and
|
10195
|
+
* limitations under the License.
|
10196
|
+
*/
|
10197
|
+
|
10198
|
+
(function(root, vttjs) {
|
10199
|
+
|
10200
|
+
var scrollSetting = {
|
10201
|
+
"": true,
|
10202
|
+
"up": true,
|
10203
|
+
};
|
10204
|
+
|
10205
|
+
function findScrollSetting(value) {
|
10206
|
+
if (typeof value !== "string") {
|
10207
|
+
return false;
|
10208
|
+
}
|
10209
|
+
var scroll = scrollSetting[value.toLowerCase()];
|
10210
|
+
return scroll ? value.toLowerCase() : false;
|
10211
|
+
}
|
10212
|
+
|
10213
|
+
function isValidPercentValue(value) {
|
10214
|
+
return typeof value === "number" && (value >= 0 && value <= 100);
|
10215
|
+
}
|
10216
|
+
|
10217
|
+
// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
|
10218
|
+
function VTTRegion() {
|
10219
|
+
var _width = 100;
|
10220
|
+
var _lines = 3;
|
10221
|
+
var _regionAnchorX = 0;
|
10222
|
+
var _regionAnchorY = 100;
|
10223
|
+
var _viewportAnchorX = 0;
|
10224
|
+
var _viewportAnchorY = 100;
|
10225
|
+
var _scroll = "";
|
10226
|
+
|
10227
|
+
Object.defineProperties(this, {
|
10228
|
+
"width": {
|
10229
|
+
enumerable: true,
|
10230
|
+
get: function() {
|
10231
|
+
return _width;
|
10232
|
+
},
|
10233
|
+
set: function(value) {
|
10234
|
+
if (!isValidPercentValue(value)) {
|
10235
|
+
throw new Error("Width must be between 0 and 100.");
|
10236
|
+
}
|
10237
|
+
_width = value;
|
10238
|
+
}
|
10239
|
+
},
|
10240
|
+
"lines": {
|
10241
|
+
enumerable: true,
|
10242
|
+
get: function() {
|
10243
|
+
return _lines;
|
10244
|
+
},
|
10245
|
+
set: function(value) {
|
10246
|
+
if (typeof value !== "number") {
|
10247
|
+
throw new TypeError("Lines must be set to a number.");
|
10248
|
+
}
|
10249
|
+
_lines = value;
|
10250
|
+
}
|
10251
|
+
},
|
10252
|
+
"regionAnchorY": {
|
10253
|
+
enumerable: true,
|
10254
|
+
get: function() {
|
10255
|
+
return _regionAnchorY;
|
10256
|
+
},
|
10257
|
+
set: function(value) {
|
10258
|
+
if (!isValidPercentValue(value)) {
|
10259
|
+
throw new Error("RegionAnchorX must be between 0 and 100.");
|
10260
|
+
}
|
10261
|
+
_regionAnchorY = value;
|
10262
|
+
}
|
10263
|
+
},
|
10264
|
+
"regionAnchorX": {
|
10265
|
+
enumerable: true,
|
10266
|
+
get: function() {
|
10267
|
+
return _regionAnchorX;
|
10268
|
+
},
|
10269
|
+
set: function(value) {
|
10270
|
+
if(!isValidPercentValue(value)) {
|
10271
|
+
throw new Error("RegionAnchorY must be between 0 and 100.");
|
10272
|
+
}
|
10273
|
+
_regionAnchorX = value;
|
10274
|
+
}
|
10275
|
+
},
|
10276
|
+
"viewportAnchorY": {
|
10277
|
+
enumerable: true,
|
10278
|
+
get: function() {
|
10279
|
+
return _viewportAnchorY;
|
10280
|
+
},
|
10281
|
+
set: function(value) {
|
10282
|
+
if (!isValidPercentValue(value)) {
|
10283
|
+
throw new Error("ViewportAnchorY must be between 0 and 100.");
|
10284
|
+
}
|
10285
|
+
_viewportAnchorY = value;
|
10286
|
+
}
|
10287
|
+
},
|
10288
|
+
"viewportAnchorX": {
|
10289
|
+
enumerable: true,
|
10290
|
+
get: function() {
|
10291
|
+
return _viewportAnchorX;
|
10292
|
+
},
|
10293
|
+
set: function(value) {
|
10294
|
+
if (!isValidPercentValue(value)) {
|
10295
|
+
throw new Error("ViewportAnchorX must be between 0 and 100.");
|
10296
|
+
}
|
10297
|
+
_viewportAnchorX = value;
|
10298
|
+
}
|
10299
|
+
},
|
10300
|
+
"scroll": {
|
10301
|
+
enumerable: true,
|
10302
|
+
get: function() {
|
10303
|
+
return _scroll;
|
10304
|
+
},
|
10305
|
+
set: function(value) {
|
10306
|
+
var setting = findScrollSetting(value);
|
10307
|
+
// Have to check for false as an empty string is a legal value.
|
10308
|
+
if (setting === false) {
|
10309
|
+
throw new SyntaxError("An invalid or illegal string was specified.");
|
10310
|
+
}
|
10311
|
+
_scroll = setting;
|
10312
|
+
}
|
10313
|
+
}
|
10314
|
+
});
|
10315
|
+
}
|
10316
|
+
|
10317
|
+
root.VTTRegion = root.VTTRegion || VTTRegion;
|
10318
|
+
vttjs.VTTRegion = VTTRegion;
|
10319
|
+
}(this, (this.vttjs || {})));
|
10320
|
+
|
10321
|
+
/**
|
10322
|
+
* Copyright 2013 vtt.js Contributors
|
10323
|
+
*
|
10324
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
10325
|
+
* you may not use this file except in compliance with the License.
|
10326
|
+
* You may obtain a copy of the License at
|
10327
|
+
*
|
10328
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10329
|
+
*
|
10330
|
+
* Unless required by applicable law or agreed to in writing, software
|
10331
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
10332
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10333
|
+
* See the License for the specific language governing permissions and
|
10334
|
+
* limitations under the License.
|
10335
|
+
*/
|
10336
|
+
|
10337
|
+
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
10338
|
+
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
10339
|
+
|
10340
|
+
(function(global) {
|
10341
|
+
|
10342
|
+
var _objCreate = Object.create || (function() {
|
10343
|
+
function F() {}
|
10344
|
+
return function(o) {
|
10345
|
+
if (arguments.length !== 1) {
|
10346
|
+
throw new Error('Object.create shim only accepts one parameter.');
|
10347
|
+
}
|
10348
|
+
F.prototype = o;
|
10349
|
+
return new F();
|
10350
|
+
};
|
10351
|
+
})();
|
10352
|
+
|
10353
|
+
// Creates a new ParserError object from an errorData object. The errorData
|
10354
|
+
// object should have default code and message properties. The default message
|
10355
|
+
// property can be overriden by passing in a message parameter.
|
10356
|
+
// See ParsingError.Errors below for acceptable errors.
|
10357
|
+
function ParsingError(errorData, message) {
|
10358
|
+
this.name = "ParsingError";
|
10359
|
+
this.code = errorData.code;
|
10360
|
+
this.message = message || errorData.message;
|
10361
|
+
}
|
10362
|
+
ParsingError.prototype = _objCreate(Error.prototype);
|
10363
|
+
ParsingError.prototype.constructor = ParsingError;
|
10364
|
+
|
10365
|
+
// ParsingError metadata for acceptable ParsingErrors.
|
10366
|
+
ParsingError.Errors = {
|
10367
|
+
BadSignature: {
|
10368
|
+
code: 0,
|
10369
|
+
message: "Malformed WebVTT signature."
|
10370
|
+
},
|
10371
|
+
BadTimeStamp: {
|
10372
|
+
code: 1,
|
10373
|
+
message: "Malformed time stamp."
|
10374
|
+
}
|
10375
|
+
};
|
10376
|
+
|
10377
|
+
// Try to parse input as a time stamp.
|
10378
|
+
function parseTimeStamp(input) {
|
10379
|
+
|
10380
|
+
function computeSeconds(h, m, s, f) {
|
10381
|
+
return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
|
10382
|
+
}
|
10383
|
+
|
10384
|
+
var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
|
10385
|
+
if (!m) {
|
10386
|
+
return null;
|
10387
|
+
}
|
10388
|
+
|
10389
|
+
if (m[3]) {
|
10390
|
+
// Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
|
10391
|
+
return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
|
10392
|
+
} else if (m[1] > 59) {
|
10393
|
+
// Timestamp takes the form of [hours]:[minutes].[milliseconds]
|
10394
|
+
// First position is hours as it's over 59.
|
10395
|
+
return computeSeconds(m[1], m[2], 0, m[4]);
|
10396
|
+
} else {
|
10397
|
+
// Timestamp takes the form of [minutes]:[seconds].[milliseconds]
|
10398
|
+
return computeSeconds(0, m[1], m[2], m[4]);
|
10399
|
+
}
|
10400
|
+
}
|
10401
|
+
|
10402
|
+
// A settings object holds key/value pairs and will ignore anything but the first
|
10403
|
+
// assignment to a specific key.
|
10404
|
+
function Settings() {
|
10405
|
+
this.values = _objCreate(null);
|
10406
|
+
}
|
10407
|
+
|
10408
|
+
Settings.prototype = {
|
10409
|
+
// Only accept the first assignment to any key.
|
10410
|
+
set: function(k, v) {
|
10411
|
+
if (!this.get(k) && v !== "") {
|
10412
|
+
this.values[k] = v;
|
10413
|
+
}
|
10414
|
+
},
|
10415
|
+
// Return the value for a key, or a default value.
|
10416
|
+
// If 'defaultKey' is passed then 'dflt' is assumed to be an object with
|
10417
|
+
// a number of possible default values as properties where 'defaultKey' is
|
10418
|
+
// the key of the property that will be chosen; otherwise it's assumed to be
|
10419
|
+
// a single value.
|
10420
|
+
get: function(k, dflt, defaultKey) {
|
10421
|
+
if (defaultKey) {
|
10422
|
+
return this.has(k) ? this.values[k] : dflt[defaultKey];
|
10423
|
+
}
|
10424
|
+
return this.has(k) ? this.values[k] : dflt;
|
10425
|
+
},
|
10426
|
+
// Check whether we have a value for a key.
|
10427
|
+
has: function(k) {
|
10428
|
+
return k in this.values;
|
10429
|
+
},
|
10430
|
+
// Accept a setting if its one of the given alternatives.
|
10431
|
+
alt: function(k, v, a) {
|
10432
|
+
for (var n = 0; n < a.length; ++n) {
|
10433
|
+
if (v === a[n]) {
|
10434
|
+
this.set(k, v);
|
10435
|
+
break;
|
10436
|
+
}
|
10437
|
+
}
|
10438
|
+
},
|
10439
|
+
// Accept a setting if its a valid (signed) integer.
|
10440
|
+
integer: function(k, v) {
|
10441
|
+
if (/^-?\d+$/.test(v)) { // integer
|
10442
|
+
this.set(k, parseInt(v, 10));
|
10443
|
+
}
|
10444
|
+
},
|
10445
|
+
// Accept a setting if its a valid percentage.
|
10446
|
+
percent: function(k, v) {
|
10447
|
+
var m;
|
10448
|
+
if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
|
10449
|
+
v = parseFloat(v);
|
10450
|
+
if (v >= 0 && v <= 100) {
|
10451
|
+
this.set(k, v);
|
10452
|
+
return true;
|
10453
|
+
}
|
10454
|
+
}
|
10455
|
+
return false;
|
10456
|
+
}
|
10457
|
+
};
|
10458
|
+
|
10459
|
+
// Helper function to parse input into groups separated by 'groupDelim', and
|
10460
|
+
// interprete each group as a key/value pair separated by 'keyValueDelim'.
|
10461
|
+
function parseOptions(input, callback, keyValueDelim, groupDelim) {
|
10462
|
+
var groups = groupDelim ? input.split(groupDelim) : [input];
|
10463
|
+
for (var i in groups) {
|
10464
|
+
if (typeof groups[i] !== "string") {
|
10465
|
+
continue;
|
10466
|
+
}
|
10467
|
+
var kv = groups[i].split(keyValueDelim);
|
10468
|
+
if (kv.length !== 2) {
|
10469
|
+
continue;
|
10470
|
+
}
|
10471
|
+
var k = kv[0];
|
10472
|
+
var v = kv[1];
|
10473
|
+
callback(k, v);
|
10474
|
+
}
|
10475
|
+
}
|
10476
|
+
|
10477
|
+
function parseCue(input, cue, regionList) {
|
10478
|
+
// Remember the original input if we need to throw an error.
|
10479
|
+
var oInput = input;
|
10480
|
+
// 4.1 WebVTT timestamp
|
10481
|
+
function consumeTimeStamp() {
|
10482
|
+
var ts = parseTimeStamp(input);
|
10483
|
+
if (ts === null) {
|
10484
|
+
throw new ParsingError(ParsingError.Errors.BadTimeStamp,
|
10485
|
+
"Malformed timestamp: " + oInput);
|
10486
|
+
}
|
10487
|
+
// Remove time stamp from input.
|
10488
|
+
input = input.replace(/^[^\sa-zA-Z-]+/, "");
|
10489
|
+
return ts;
|
10490
|
+
}
|
10491
|
+
|
10492
|
+
// 4.4.2 WebVTT cue settings
|
10493
|
+
function consumeCueSettings(input, cue) {
|
10494
|
+
var settings = new Settings();
|
10495
|
+
|
10496
|
+
parseOptions(input, function (k, v) {
|
10497
|
+
switch (k) {
|
10498
|
+
case "region":
|
10499
|
+
// Find the last region we parsed with the same region id.
|
10500
|
+
for (var i = regionList.length - 1; i >= 0; i--) {
|
10501
|
+
if (regionList[i].id === v) {
|
10502
|
+
settings.set(k, regionList[i].region);
|
10503
|
+
break;
|
10504
|
+
}
|
10505
|
+
}
|
10506
|
+
break;
|
10507
|
+
case "vertical":
|
10508
|
+
settings.alt(k, v, ["rl", "lr"]);
|
10509
|
+
break;
|
10510
|
+
case "line":
|
10511
|
+
var vals = v.split(","),
|
10512
|
+
vals0 = vals[0];
|
10513
|
+
settings.integer(k, vals0);
|
10514
|
+
settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
|
10515
|
+
settings.alt(k, vals0, ["auto"]);
|
10516
|
+
if (vals.length === 2) {
|
10517
|
+
settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
|
10518
|
+
}
|
10519
|
+
break;
|
10520
|
+
case "position":
|
10521
|
+
vals = v.split(",");
|
10522
|
+
settings.percent(k, vals[0]);
|
10523
|
+
if (vals.length === 2) {
|
10524
|
+
settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
|
10525
|
+
}
|
10526
|
+
break;
|
10527
|
+
case "size":
|
10528
|
+
settings.percent(k, v);
|
10529
|
+
break;
|
10530
|
+
case "align":
|
10531
|
+
settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
|
10532
|
+
break;
|
10533
|
+
}
|
10534
|
+
}, /:/, /\s/);
|
10535
|
+
|
10536
|
+
// Apply default values for any missing fields.
|
10537
|
+
cue.region = settings.get("region", null);
|
10538
|
+
cue.vertical = settings.get("vertical", "");
|
10539
|
+
cue.line = settings.get("line", "auto");
|
10540
|
+
cue.lineAlign = settings.get("lineAlign", "start");
|
10541
|
+
cue.snapToLines = settings.get("snapToLines", true);
|
10542
|
+
cue.size = settings.get("size", 100);
|
10543
|
+
cue.align = settings.get("align", "middle");
|
10544
|
+
cue.position = settings.get("position", {
|
10545
|
+
start: 0,
|
10546
|
+
left: 0,
|
10547
|
+
middle: 50,
|
10548
|
+
end: 100,
|
10549
|
+
right: 100
|
10550
|
+
}, cue.align);
|
10551
|
+
cue.positionAlign = settings.get("positionAlign", {
|
10552
|
+
start: "start",
|
10553
|
+
left: "start",
|
10554
|
+
middle: "middle",
|
10555
|
+
end: "end",
|
10556
|
+
right: "end"
|
10557
|
+
}, cue.align);
|
10558
|
+
}
|
10559
|
+
|
10560
|
+
function skipWhitespace() {
|
10561
|
+
input = input.replace(/^\s+/, "");
|
10562
|
+
}
|
10563
|
+
|
10564
|
+
// 4.1 WebVTT cue timings.
|
10565
|
+
skipWhitespace();
|
10566
|
+
cue.startTime = consumeTimeStamp(); // (1) collect cue start time
|
10567
|
+
skipWhitespace();
|
10568
|
+
if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->"
|
10569
|
+
throw new ParsingError(ParsingError.Errors.BadTimeStamp,
|
10570
|
+
"Malformed time stamp (time stamps must be separated by '-->'): " +
|
10571
|
+
oInput);
|
10572
|
+
}
|
10573
|
+
input = input.substr(3);
|
10574
|
+
skipWhitespace();
|
10575
|
+
cue.endTime = consumeTimeStamp(); // (5) collect cue end time
|
10576
|
+
|
10577
|
+
// 4.1 WebVTT cue settings list.
|
10578
|
+
skipWhitespace();
|
10579
|
+
consumeCueSettings(input, cue);
|
10580
|
+
}
|
10581
|
+
|
10582
|
+
var ESCAPE = {
|
10583
|
+
"&": "&",
|
10584
|
+
"<": "<",
|
10585
|
+
">": ">",
|
10586
|
+
"‎": "\u200e",
|
10587
|
+
"‏": "\u200f",
|
10588
|
+
" ": "\u00a0"
|
10589
|
+
};
|
10590
|
+
|
10591
|
+
var TAG_NAME = {
|
10592
|
+
c: "span",
|
10593
|
+
i: "i",
|
10594
|
+
b: "b",
|
10595
|
+
u: "u",
|
10596
|
+
ruby: "ruby",
|
10597
|
+
rt: "rt",
|
10598
|
+
v: "span",
|
10599
|
+
lang: "span"
|
10600
|
+
};
|
10601
|
+
|
10602
|
+
var TAG_ANNOTATION = {
|
10603
|
+
v: "title",
|
10604
|
+
lang: "lang"
|
10605
|
+
};
|
10606
|
+
|
10607
|
+
var NEEDS_PARENT = {
|
10608
|
+
rt: "ruby"
|
10609
|
+
};
|
10610
|
+
|
10611
|
+
// Parse content into a document fragment.
|
10612
|
+
function parseContent(window, input) {
|
10613
|
+
function nextToken() {
|
10614
|
+
// Check for end-of-string.
|
10615
|
+
if (!input) {
|
10616
|
+
return null;
|
10617
|
+
}
|
10618
|
+
|
10619
|
+
// Consume 'n' characters from the input.
|
10620
|
+
function consume(result) {
|
10621
|
+
input = input.substr(result.length);
|
10622
|
+
return result;
|
10623
|
+
}
|
10624
|
+
|
10625
|
+
var m = input.match(/^([^<]*)(<[^>]+>?)?/);
|
10626
|
+
// If there is some text before the next tag, return it, otherwise return
|
10627
|
+
// the tag.
|
10628
|
+
return consume(m[1] ? m[1] : m[2]);
|
10629
|
+
}
|
10630
|
+
|
10631
|
+
// Unescape a string 's'.
|
10632
|
+
function unescape1(e) {
|
10633
|
+
return ESCAPE[e];
|
10634
|
+
}
|
10635
|
+
function unescape(s) {
|
10636
|
+
while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
|
10637
|
+
s = s.replace(m[0], unescape1);
|
10638
|
+
}
|
10639
|
+
return s;
|
10640
|
+
}
|
10641
|
+
|
10642
|
+
function shouldAdd(current, element) {
|
10643
|
+
return !NEEDS_PARENT[element.localName] ||
|
10644
|
+
NEEDS_PARENT[element.localName] === current.localName;
|
10645
|
+
}
|
10646
|
+
|
10647
|
+
// Create an element for this tag.
|
10648
|
+
function createElement(type, annotation) {
|
10649
|
+
var tagName = TAG_NAME[type];
|
10650
|
+
if (!tagName) {
|
10651
|
+
return null;
|
10652
|
+
}
|
10653
|
+
var element = window.document.createElement(tagName);
|
10654
|
+
element.localName = tagName;
|
10655
|
+
var name = TAG_ANNOTATION[type];
|
10656
|
+
if (name && annotation) {
|
10657
|
+
element[name] = annotation.trim();
|
10658
|
+
}
|
10659
|
+
return element;
|
10660
|
+
}
|
10661
|
+
|
10662
|
+
var rootDiv = window.document.createElement("div"),
|
10663
|
+
current = rootDiv,
|
10664
|
+
t,
|
10665
|
+
tagStack = [];
|
10666
|
+
|
10667
|
+
while ((t = nextToken()) !== null) {
|
10668
|
+
if (t[0] === '<') {
|
10669
|
+
if (t[1] === "/") {
|
10670
|
+
// If the closing tag matches, move back up to the parent node.
|
10671
|
+
if (tagStack.length &&
|
10672
|
+
tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
|
10673
|
+
tagStack.pop();
|
10674
|
+
current = current.parentNode;
|
10675
|
+
}
|
10676
|
+
// Otherwise just ignore the end tag.
|
10677
|
+
continue;
|
10678
|
+
}
|
10679
|
+
var ts = parseTimeStamp(t.substr(1, t.length - 2));
|
10680
|
+
var node;
|
10681
|
+
if (ts) {
|
10682
|
+
// Timestamps are lead nodes as well.
|
10683
|
+
node = window.document.createProcessingInstruction("timestamp", ts);
|
10684
|
+
current.appendChild(node);
|
10685
|
+
continue;
|
10686
|
+
}
|
10687
|
+
var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
|
10688
|
+
// If we can't parse the tag, skip to the next tag.
|
10689
|
+
if (!m) {
|
10690
|
+
continue;
|
10691
|
+
}
|
10692
|
+
// Try to construct an element, and ignore the tag if we couldn't.
|
10693
|
+
node = createElement(m[1], m[3]);
|
10694
|
+
if (!node) {
|
10695
|
+
continue;
|
10696
|
+
}
|
10697
|
+
// Determine if the tag should be added based on the context of where it
|
10698
|
+
// is placed in the cuetext.
|
10699
|
+
if (!shouldAdd(current, node)) {
|
10700
|
+
continue;
|
10701
|
+
}
|
10702
|
+
// Set the class list (as a list of classes, separated by space).
|
10703
|
+
if (m[2]) {
|
10704
|
+
node.className = m[2].substr(1).replace('.', ' ');
|
10705
|
+
}
|
10706
|
+
// Append the node to the current node, and enter the scope of the new
|
10707
|
+
// node.
|
10708
|
+
tagStack.push(m[1]);
|
10709
|
+
current.appendChild(node);
|
10710
|
+
current = node;
|
10711
|
+
continue;
|
10712
|
+
}
|
10713
|
+
|
10714
|
+
// Text nodes are leaf nodes.
|
10715
|
+
current.appendChild(window.document.createTextNode(unescape(t)));
|
10716
|
+
}
|
10717
|
+
|
10718
|
+
return rootDiv;
|
10719
|
+
}
|
10720
|
+
|
10721
|
+
// This is a list of all the Unicode characters that have a strong
|
10722
|
+
// right-to-left category. What this means is that these characters are
|
10723
|
+
// written right-to-left for sure. It was generated by pulling all the strong
|
10724
|
+
// right-to-left characters out of the Unicode data table. That table can
|
10725
|
+
// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
|
10726
|
+
var strongRTLChars = [0x05BE, 0x05C0, 0x05C3, 0x05C6, 0x05D0, 0x05D1,
|
10727
|
+
0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA,
|
10728
|
+
0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3,
|
10729
|
+
0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05F0, 0x05F1,
|
10730
|
+
0x05F2, 0x05F3, 0x05F4, 0x0608, 0x060B, 0x060D, 0x061B, 0x061E, 0x061F,
|
10731
|
+
0x0620, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628,
|
10732
|
+
0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631,
|
10733
|
+
0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A,
|
10734
|
+
0x063B, 0x063C, 0x063D, 0x063E, 0x063F, 0x0640, 0x0641, 0x0642, 0x0643,
|
10735
|
+
0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x066D, 0x066E,
|
10736
|
+
0x066F, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676, 0x0677, 0x0678,
|
10737
|
+
0x0679, 0x067A, 0x067B, 0x067C, 0x067D, 0x067E, 0x067F, 0x0680, 0x0681,
|
10738
|
+
0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687, 0x0688, 0x0689, 0x068A,
|
10739
|
+
0x068B, 0x068C, 0x068D, 0x068E, 0x068F, 0x0690, 0x0691, 0x0692, 0x0693,
|
10740
|
+
0x0694, 0x0695, 0x0696, 0x0697, 0x0698, 0x0699, 0x069A, 0x069B, 0x069C,
|
10741
|
+
0x069D, 0x069E, 0x069F, 0x06A0, 0x06A1, 0x06A2, 0x06A3, 0x06A4, 0x06A5,
|
10742
|
+
0x06A6, 0x06A7, 0x06A8, 0x06A9, 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE,
|
10743
|
+
0x06AF, 0x06B0, 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 0x06B7,
|
10744
|
+
0x06B8, 0x06B9, 0x06BA, 0x06BB, 0x06BC, 0x06BD, 0x06BE, 0x06BF, 0x06C0,
|
10745
|
+
0x06C1, 0x06C2, 0x06C3, 0x06C4, 0x06C5, 0x06C6, 0x06C7, 0x06C8, 0x06C9,
|
10746
|
+
0x06CA, 0x06CB, 0x06CC, 0x06CD, 0x06CE, 0x06CF, 0x06D0, 0x06D1, 0x06D2,
|
10747
|
+
0x06D3, 0x06D4, 0x06D5, 0x06E5, 0x06E6, 0x06EE, 0x06EF, 0x06FA, 0x06FB,
|
10748
|
+
0x06FC, 0x06FD, 0x06FE, 0x06FF, 0x0700, 0x0701, 0x0702, 0x0703, 0x0704,
|
10749
|
+
0x0705, 0x0706, 0x0707, 0x0708, 0x0709, 0x070A, 0x070B, 0x070C, 0x070D,
|
10750
|
+
0x070F, 0x0710, 0x0712, 0x0713, 0x0714, 0x0715, 0x0716, 0x0717, 0x0718,
|
10751
|
+
0x0719, 0x071A, 0x071B, 0x071C, 0x071D, 0x071E, 0x071F, 0x0720, 0x0721,
|
10752
|
+
0x0722, 0x0723, 0x0724, 0x0725, 0x0726, 0x0727, 0x0728, 0x0729, 0x072A,
|
10753
|
+
0x072B, 0x072C, 0x072D, 0x072E, 0x072F, 0x074D, 0x074E, 0x074F, 0x0750,
|
10754
|
+
0x0751, 0x0752, 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, 0x0758, 0x0759,
|
10755
|
+
0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 0x075F, 0x0760, 0x0761, 0x0762,
|
10756
|
+
0x0763, 0x0764, 0x0765, 0x0766, 0x0767, 0x0768, 0x0769, 0x076A, 0x076B,
|
10757
|
+
0x076C, 0x076D, 0x076E, 0x076F, 0x0770, 0x0771, 0x0772, 0x0773, 0x0774,
|
10758
|
+
0x0775, 0x0776, 0x0777, 0x0778, 0x0779, 0x077A, 0x077B, 0x077C, 0x077D,
|
10759
|
+
0x077E, 0x077F, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786,
|
10760
|
+
0x0787, 0x0788, 0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F,
|
10761
|
+
0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796, 0x0797, 0x0798,
|
10762
|
+
0x0799, 0x079A, 0x079B, 0x079C, 0x079D, 0x079E, 0x079F, 0x07A0, 0x07A1,
|
10763
|
+
0x07A2, 0x07A3, 0x07A4, 0x07A5, 0x07B1, 0x07C0, 0x07C1, 0x07C2, 0x07C3,
|
10764
|
+
0x07C4, 0x07C5, 0x07C6, 0x07C7, 0x07C8, 0x07C9, 0x07CA, 0x07CB, 0x07CC,
|
10765
|
+
0x07CD, 0x07CE, 0x07CF, 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x07D4, 0x07D5,
|
10766
|
+
0x07D6, 0x07D7, 0x07D8, 0x07D9, 0x07DA, 0x07DB, 0x07DC, 0x07DD, 0x07DE,
|
10767
|
+
0x07DF, 0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x07E4, 0x07E5, 0x07E6, 0x07E7,
|
10768
|
+
0x07E8, 0x07E9, 0x07EA, 0x07F4, 0x07F5, 0x07FA, 0x0800, 0x0801, 0x0802,
|
10769
|
+
0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, 0x080A, 0x080B,
|
10770
|
+
0x080C, 0x080D, 0x080E, 0x080F, 0x0810, 0x0811, 0x0812, 0x0813, 0x0814,
|
10771
|
+
0x0815, 0x081A, 0x0824, 0x0828, 0x0830, 0x0831, 0x0832, 0x0833, 0x0834,
|
10772
|
+
0x0835, 0x0836, 0x0837, 0x0838, 0x0839, 0x083A, 0x083B, 0x083C, 0x083D,
|
10773
|
+
0x083E, 0x0840, 0x0841, 0x0842, 0x0843, 0x0844, 0x0845, 0x0846, 0x0847,
|
10774
|
+
0x0848, 0x0849, 0x084A, 0x084B, 0x084C, 0x084D, 0x084E, 0x084F, 0x0850,
|
10775
|
+
0x0851, 0x0852, 0x0853, 0x0854, 0x0855, 0x0856, 0x0857, 0x0858, 0x085E,
|
10776
|
+
0x08A0, 0x08A2, 0x08A3, 0x08A4, 0x08A5, 0x08A6, 0x08A7, 0x08A8, 0x08A9,
|
10777
|
+
0x08AA, 0x08AB, 0x08AC, 0x200F, 0xFB1D, 0xFB1F, 0xFB20, 0xFB21, 0xFB22,
|
10778
|
+
0xFB23, 0xFB24, 0xFB25, 0xFB26, 0xFB27, 0xFB28, 0xFB2A, 0xFB2B, 0xFB2C,
|
10779
|
+
0xFB2D, 0xFB2E, 0xFB2F, 0xFB30, 0xFB31, 0xFB32, 0xFB33, 0xFB34, 0xFB35,
|
10780
|
+
0xFB36, 0xFB38, 0xFB39, 0xFB3A, 0xFB3B, 0xFB3C, 0xFB3E, 0xFB40, 0xFB41,
|
10781
|
+
0xFB43, 0xFB44, 0xFB46, 0xFB47, 0xFB48, 0xFB49, 0xFB4A, 0xFB4B, 0xFB4C,
|
10782
|
+
0xFB4D, 0xFB4E, 0xFB4F, 0xFB50, 0xFB51, 0xFB52, 0xFB53, 0xFB54, 0xFB55,
|
10783
|
+
0xFB56, 0xFB57, 0xFB58, 0xFB59, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D, 0xFB5E,
|
10784
|
+
0xFB5F, 0xFB60, 0xFB61, 0xFB62, 0xFB63, 0xFB64, 0xFB65, 0xFB66, 0xFB67,
|
10785
|
+
0xFB68, 0xFB69, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D, 0xFB6E, 0xFB6F, 0xFB70,
|
10786
|
+
0xFB71, 0xFB72, 0xFB73, 0xFB74, 0xFB75, 0xFB76, 0xFB77, 0xFB78, 0xFB79,
|
10787
|
+
0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81, 0xFB82,
|
10788
|
+
0xFB83, 0xFB84, 0xFB85, 0xFB86, 0xFB87, 0xFB88, 0xFB89, 0xFB8A, 0xFB8B,
|
10789
|
+
0xFB8C, 0xFB8D, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91, 0xFB92, 0xFB93, 0xFB94,
|
10790
|
+
0xFB95, 0xFB96, 0xFB97, 0xFB98, 0xFB99, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D,
|
10791
|
+
0xFB9E, 0xFB9F, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3, 0xFBA4, 0xFBA5, 0xFBA6,
|
10792
|
+
0xFBA7, 0xFBA8, 0xFBA9, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD, 0xFBAE, 0xFBAF,
|
10793
|
+
0xFBB0, 0xFBB1, 0xFBB2, 0xFBB3, 0xFBB4, 0xFBB5, 0xFBB6, 0xFBB7, 0xFBB8,
|
10794
|
+
0xFBB9, 0xFBBA, 0xFBBB, 0xFBBC, 0xFBBD, 0xFBBE, 0xFBBF, 0xFBC0, 0xFBC1,
|
10795
|
+
0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6, 0xFBD7, 0xFBD8, 0xFBD9, 0xFBDA, 0xFBDB,
|
10796
|
+
0xFBDC, 0xFBDD, 0xFBDE, 0xFBDF, 0xFBE0, 0xFBE1, 0xFBE2, 0xFBE3, 0xFBE4,
|
10797
|
+
0xFBE5, 0xFBE6, 0xFBE7, 0xFBE8, 0xFBE9, 0xFBEA, 0xFBEB, 0xFBEC, 0xFBED,
|
10798
|
+
0xFBEE, 0xFBEF, 0xFBF0, 0xFBF1, 0xFBF2, 0xFBF3, 0xFBF4, 0xFBF5, 0xFBF6,
|
10799
|
+
0xFBF7, 0xFBF8, 0xFBF9, 0xFBFA, 0xFBFB, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF,
|
10800
|
+
0xFC00, 0xFC01, 0xFC02, 0xFC03, 0xFC04, 0xFC05, 0xFC06, 0xFC07, 0xFC08,
|
10801
|
+
0xFC09, 0xFC0A, 0xFC0B, 0xFC0C, 0xFC0D, 0xFC0E, 0xFC0F, 0xFC10, 0xFC11,
|
10802
|
+
0xFC12, 0xFC13, 0xFC14, 0xFC15, 0xFC16, 0xFC17, 0xFC18, 0xFC19, 0xFC1A,
|
10803
|
+
0xFC1B, 0xFC1C, 0xFC1D, 0xFC1E, 0xFC1F, 0xFC20, 0xFC21, 0xFC22, 0xFC23,
|
10804
|
+
0xFC24, 0xFC25, 0xFC26, 0xFC27, 0xFC28, 0xFC29, 0xFC2A, 0xFC2B, 0xFC2C,
|
10805
|
+
0xFC2D, 0xFC2E, 0xFC2F, 0xFC30, 0xFC31, 0xFC32, 0xFC33, 0xFC34, 0xFC35,
|
10806
|
+
0xFC36, 0xFC37, 0xFC38, 0xFC39, 0xFC3A, 0xFC3B, 0xFC3C, 0xFC3D, 0xFC3E,
|
10807
|
+
0xFC3F, 0xFC40, 0xFC41, 0xFC42, 0xFC43, 0xFC44, 0xFC45, 0xFC46, 0xFC47,
|
10808
|
+
0xFC48, 0xFC49, 0xFC4A, 0xFC4B, 0xFC4C, 0xFC4D, 0xFC4E, 0xFC4F, 0xFC50,
|
10809
|
+
0xFC51, 0xFC52, 0xFC53, 0xFC54, 0xFC55, 0xFC56, 0xFC57, 0xFC58, 0xFC59,
|
10810
|
+
0xFC5A, 0xFC5B, 0xFC5C, 0xFC5D, 0xFC5E, 0xFC5F, 0xFC60, 0xFC61, 0xFC62,
|
10811
|
+
0xFC63, 0xFC64, 0xFC65, 0xFC66, 0xFC67, 0xFC68, 0xFC69, 0xFC6A, 0xFC6B,
|
10812
|
+
0xFC6C, 0xFC6D, 0xFC6E, 0xFC6F, 0xFC70, 0xFC71, 0xFC72, 0xFC73, 0xFC74,
|
10813
|
+
0xFC75, 0xFC76, 0xFC77, 0xFC78, 0xFC79, 0xFC7A, 0xFC7B, 0xFC7C, 0xFC7D,
|
10814
|
+
0xFC7E, 0xFC7F, 0xFC80, 0xFC81, 0xFC82, 0xFC83, 0xFC84, 0xFC85, 0xFC86,
|
10815
|
+
0xFC87, 0xFC88, 0xFC89, 0xFC8A, 0xFC8B, 0xFC8C, 0xFC8D, 0xFC8E, 0xFC8F,
|
10816
|
+
0xFC90, 0xFC91, 0xFC92, 0xFC93, 0xFC94, 0xFC95, 0xFC96, 0xFC97, 0xFC98,
|
10817
|
+
0xFC99, 0xFC9A, 0xFC9B, 0xFC9C, 0xFC9D, 0xFC9E, 0xFC9F, 0xFCA0, 0xFCA1,
|
10818
|
+
0xFCA2, 0xFCA3, 0xFCA4, 0xFCA5, 0xFCA6, 0xFCA7, 0xFCA8, 0xFCA9, 0xFCAA,
|
10819
|
+
0xFCAB, 0xFCAC, 0xFCAD, 0xFCAE, 0xFCAF, 0xFCB0, 0xFCB1, 0xFCB2, 0xFCB3,
|
10820
|
+
0xFCB4, 0xFCB5, 0xFCB6, 0xFCB7, 0xFCB8, 0xFCB9, 0xFCBA, 0xFCBB, 0xFCBC,
|
10821
|
+
0xFCBD, 0xFCBE, 0xFCBF, 0xFCC0, 0xFCC1, 0xFCC2, 0xFCC3, 0xFCC4, 0xFCC5,
|
10822
|
+
0xFCC6, 0xFCC7, 0xFCC8, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE,
|
10823
|
+
0xFCCF, 0xFCD0, 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD4, 0xFCD5, 0xFCD6, 0xFCD7,
|
10824
|
+
0xFCD8, 0xFCD9, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFCDE, 0xFCDF, 0xFCE0,
|
10825
|
+
0xFCE1, 0xFCE2, 0xFCE3, 0xFCE4, 0xFCE5, 0xFCE6, 0xFCE7, 0xFCE8, 0xFCE9,
|
10826
|
+
0xFCEA, 0xFCEB, 0xFCEC, 0xFCED, 0xFCEE, 0xFCEF, 0xFCF0, 0xFCF1, 0xFCF2,
|
10827
|
+
0xFCF3, 0xFCF4, 0xFCF5, 0xFCF6, 0xFCF7, 0xFCF8, 0xFCF9, 0xFCFA, 0xFCFB,
|
10828
|
+
0xFCFC, 0xFCFD, 0xFCFE, 0xFCFF, 0xFD00, 0xFD01, 0xFD02, 0xFD03, 0xFD04,
|
10829
|
+
0xFD05, 0xFD06, 0xFD07, 0xFD08, 0xFD09, 0xFD0A, 0xFD0B, 0xFD0C, 0xFD0D,
|
10830
|
+
0xFD0E, 0xFD0F, 0xFD10, 0xFD11, 0xFD12, 0xFD13, 0xFD14, 0xFD15, 0xFD16,
|
10831
|
+
0xFD17, 0xFD18, 0xFD19, 0xFD1A, 0xFD1B, 0xFD1C, 0xFD1D, 0xFD1E, 0xFD1F,
|
10832
|
+
0xFD20, 0xFD21, 0xFD22, 0xFD23, 0xFD24, 0xFD25, 0xFD26, 0xFD27, 0xFD28,
|
10833
|
+
0xFD29, 0xFD2A, 0xFD2B, 0xFD2C, 0xFD2D, 0xFD2E, 0xFD2F, 0xFD30, 0xFD31,
|
10834
|
+
0xFD32, 0xFD33, 0xFD34, 0xFD35, 0xFD36, 0xFD37, 0xFD38, 0xFD39, 0xFD3A,
|
10835
|
+
0xFD3B, 0xFD3C, 0xFD3D, 0xFD50, 0xFD51, 0xFD52, 0xFD53, 0xFD54, 0xFD55,
|
10836
|
+
0xFD56, 0xFD57, 0xFD58, 0xFD59, 0xFD5A, 0xFD5B, 0xFD5C, 0xFD5D, 0xFD5E,
|
10837
|
+
0xFD5F, 0xFD60, 0xFD61, 0xFD62, 0xFD63, 0xFD64, 0xFD65, 0xFD66, 0xFD67,
|
10838
|
+
0xFD68, 0xFD69, 0xFD6A, 0xFD6B, 0xFD6C, 0xFD6D, 0xFD6E, 0xFD6F, 0xFD70,
|
10839
|
+
0xFD71, 0xFD72, 0xFD73, 0xFD74, 0xFD75, 0xFD76, 0xFD77, 0xFD78, 0xFD79,
|
10840
|
+
0xFD7A, 0xFD7B, 0xFD7C, 0xFD7D, 0xFD7E, 0xFD7F, 0xFD80, 0xFD81, 0xFD82,
|
10841
|
+
0xFD83, 0xFD84, 0xFD85, 0xFD86, 0xFD87, 0xFD88, 0xFD89, 0xFD8A, 0xFD8B,
|
10842
|
+
0xFD8C, 0xFD8D, 0xFD8E, 0xFD8F, 0xFD92, 0xFD93, 0xFD94, 0xFD95, 0xFD96,
|
10843
|
+
0xFD97, 0xFD98, 0xFD99, 0xFD9A, 0xFD9B, 0xFD9C, 0xFD9D, 0xFD9E, 0xFD9F,
|
10844
|
+
0xFDA0, 0xFDA1, 0xFDA2, 0xFDA3, 0xFDA4, 0xFDA5, 0xFDA6, 0xFDA7, 0xFDA8,
|
10845
|
+
0xFDA9, 0xFDAA, 0xFDAB, 0xFDAC, 0xFDAD, 0xFDAE, 0xFDAF, 0xFDB0, 0xFDB1,
|
10846
|
+
0xFDB2, 0xFDB3, 0xFDB4, 0xFDB5, 0xFDB6, 0xFDB7, 0xFDB8, 0xFDB9, 0xFDBA,
|
10847
|
+
0xFDBB, 0xFDBC, 0xFDBD, 0xFDBE, 0xFDBF, 0xFDC0, 0xFDC1, 0xFDC2, 0xFDC3,
|
10848
|
+
0xFDC4, 0xFDC5, 0xFDC6, 0xFDC7, 0xFDF0, 0xFDF1, 0xFDF2, 0xFDF3, 0xFDF4,
|
10849
|
+
0xFDF5, 0xFDF6, 0xFDF7, 0xFDF8, 0xFDF9, 0xFDFA, 0xFDFB, 0xFDFC, 0xFE70,
|
10850
|
+
0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A,
|
10851
|
+
0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, 0xFE80, 0xFE81, 0xFE82, 0xFE83,
|
10852
|
+
0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C,
|
10853
|
+
0xFE8D, 0xFE8E, 0xFE8F, 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95,
|
10854
|
+
0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E,
|
10855
|
+
0xFE9F, 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
|
10856
|
+
0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, 0xFEB0,
|
10857
|
+
0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9,
|
10858
|
+
0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0, 0xFEC1, 0xFEC2,
|
10859
|
+
0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB,
|
10860
|
+
0xFECC, 0xFECD, 0xFECE, 0xFECF, 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4,
|
10861
|
+
0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD,
|
10862
|
+
0xFEDE, 0xFEDF, 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6,
|
10863
|
+
0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
|
10864
|
+
0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8,
|
10865
|
+
0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0x10800, 0x10801, 0x10802, 0x10803,
|
10866
|
+
0x10804, 0x10805, 0x10808, 0x1080A, 0x1080B, 0x1080C, 0x1080D, 0x1080E,
|
10867
|
+
0x1080F, 0x10810, 0x10811, 0x10812, 0x10813, 0x10814, 0x10815, 0x10816,
|
10868
|
+
0x10817, 0x10818, 0x10819, 0x1081A, 0x1081B, 0x1081C, 0x1081D, 0x1081E,
|
10869
|
+
0x1081F, 0x10820, 0x10821, 0x10822, 0x10823, 0x10824, 0x10825, 0x10826,
|
10870
|
+
0x10827, 0x10828, 0x10829, 0x1082A, 0x1082B, 0x1082C, 0x1082D, 0x1082E,
|
10871
|
+
0x1082F, 0x10830, 0x10831, 0x10832, 0x10833, 0x10834, 0x10835, 0x10837,
|
10872
|
+
0x10838, 0x1083C, 0x1083F, 0x10840, 0x10841, 0x10842, 0x10843, 0x10844,
|
10873
|
+
0x10845, 0x10846, 0x10847, 0x10848, 0x10849, 0x1084A, 0x1084B, 0x1084C,
|
10874
|
+
0x1084D, 0x1084E, 0x1084F, 0x10850, 0x10851, 0x10852, 0x10853, 0x10854,
|
10875
|
+
0x10855, 0x10857, 0x10858, 0x10859, 0x1085A, 0x1085B, 0x1085C, 0x1085D,
|
10876
|
+
0x1085E, 0x1085F, 0x10900, 0x10901, 0x10902, 0x10903, 0x10904, 0x10905,
|
10877
|
+
0x10906, 0x10907, 0x10908, 0x10909, 0x1090A, 0x1090B, 0x1090C, 0x1090D,
|
10878
|
+
0x1090E, 0x1090F, 0x10910, 0x10911, 0x10912, 0x10913, 0x10914, 0x10915,
|
10879
|
+
0x10916, 0x10917, 0x10918, 0x10919, 0x1091A, 0x1091B, 0x10920, 0x10921,
|
10880
|
+
0x10922, 0x10923, 0x10924, 0x10925, 0x10926, 0x10927, 0x10928, 0x10929,
|
10881
|
+
0x1092A, 0x1092B, 0x1092C, 0x1092D, 0x1092E, 0x1092F, 0x10930, 0x10931,
|
10882
|
+
0x10932, 0x10933, 0x10934, 0x10935, 0x10936, 0x10937, 0x10938, 0x10939,
|
10883
|
+
0x1093F, 0x10980, 0x10981, 0x10982, 0x10983, 0x10984, 0x10985, 0x10986,
|
10884
|
+
0x10987, 0x10988, 0x10989, 0x1098A, 0x1098B, 0x1098C, 0x1098D, 0x1098E,
|
10885
|
+
0x1098F, 0x10990, 0x10991, 0x10992, 0x10993, 0x10994, 0x10995, 0x10996,
|
10886
|
+
0x10997, 0x10998, 0x10999, 0x1099A, 0x1099B, 0x1099C, 0x1099D, 0x1099E,
|
10887
|
+
0x1099F, 0x109A0, 0x109A1, 0x109A2, 0x109A3, 0x109A4, 0x109A5, 0x109A6,
|
10888
|
+
0x109A7, 0x109A8, 0x109A9, 0x109AA, 0x109AB, 0x109AC, 0x109AD, 0x109AE,
|
10889
|
+
0x109AF, 0x109B0, 0x109B1, 0x109B2, 0x109B3, 0x109B4, 0x109B5, 0x109B6,
|
10890
|
+
0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A10, 0x10A11, 0x10A12, 0x10A13,
|
10891
|
+
0x10A15, 0x10A16, 0x10A17, 0x10A19, 0x10A1A, 0x10A1B, 0x10A1C, 0x10A1D,
|
10892
|
+
0x10A1E, 0x10A1F, 0x10A20, 0x10A21, 0x10A22, 0x10A23, 0x10A24, 0x10A25,
|
10893
|
+
0x10A26, 0x10A27, 0x10A28, 0x10A29, 0x10A2A, 0x10A2B, 0x10A2C, 0x10A2D,
|
10894
|
+
0x10A2E, 0x10A2F, 0x10A30, 0x10A31, 0x10A32, 0x10A33, 0x10A40, 0x10A41,
|
10895
|
+
0x10A42, 0x10A43, 0x10A44, 0x10A45, 0x10A46, 0x10A47, 0x10A50, 0x10A51,
|
10896
|
+
0x10A52, 0x10A53, 0x10A54, 0x10A55, 0x10A56, 0x10A57, 0x10A58, 0x10A60,
|
10897
|
+
0x10A61, 0x10A62, 0x10A63, 0x10A64, 0x10A65, 0x10A66, 0x10A67, 0x10A68,
|
10898
|
+
0x10A69, 0x10A6A, 0x10A6B, 0x10A6C, 0x10A6D, 0x10A6E, 0x10A6F, 0x10A70,
|
10899
|
+
0x10A71, 0x10A72, 0x10A73, 0x10A74, 0x10A75, 0x10A76, 0x10A77, 0x10A78,
|
10900
|
+
0x10A79, 0x10A7A, 0x10A7B, 0x10A7C, 0x10A7D, 0x10A7E, 0x10A7F, 0x10B00,
|
10901
|
+
0x10B01, 0x10B02, 0x10B03, 0x10B04, 0x10B05, 0x10B06, 0x10B07, 0x10B08,
|
10902
|
+
0x10B09, 0x10B0A, 0x10B0B, 0x10B0C, 0x10B0D, 0x10B0E, 0x10B0F, 0x10B10,
|
10903
|
+
0x10B11, 0x10B12, 0x10B13, 0x10B14, 0x10B15, 0x10B16, 0x10B17, 0x10B18,
|
10904
|
+
0x10B19, 0x10B1A, 0x10B1B, 0x10B1C, 0x10B1D, 0x10B1E, 0x10B1F, 0x10B20,
|
10905
|
+
0x10B21, 0x10B22, 0x10B23, 0x10B24, 0x10B25, 0x10B26, 0x10B27, 0x10B28,
|
10906
|
+
0x10B29, 0x10B2A, 0x10B2B, 0x10B2C, 0x10B2D, 0x10B2E, 0x10B2F, 0x10B30,
|
10907
|
+
0x10B31, 0x10B32, 0x10B33, 0x10B34, 0x10B35, 0x10B40, 0x10B41, 0x10B42,
|
10908
|
+
0x10B43, 0x10B44, 0x10B45, 0x10B46, 0x10B47, 0x10B48, 0x10B49, 0x10B4A,
|
10909
|
+
0x10B4B, 0x10B4C, 0x10B4D, 0x10B4E, 0x10B4F, 0x10B50, 0x10B51, 0x10B52,
|
10910
|
+
0x10B53, 0x10B54, 0x10B55, 0x10B58, 0x10B59, 0x10B5A, 0x10B5B, 0x10B5C,
|
10911
|
+
0x10B5D, 0x10B5E, 0x10B5F, 0x10B60, 0x10B61, 0x10B62, 0x10B63, 0x10B64,
|
10912
|
+
0x10B65, 0x10B66, 0x10B67, 0x10B68, 0x10B69, 0x10B6A, 0x10B6B, 0x10B6C,
|
10913
|
+
0x10B6D, 0x10B6E, 0x10B6F, 0x10B70, 0x10B71, 0x10B72, 0x10B78, 0x10B79,
|
10914
|
+
0x10B7A, 0x10B7B, 0x10B7C, 0x10B7D, 0x10B7E, 0x10B7F, 0x10C00, 0x10C01,
|
10915
|
+
0x10C02, 0x10C03, 0x10C04, 0x10C05, 0x10C06, 0x10C07, 0x10C08, 0x10C09,
|
10916
|
+
0x10C0A, 0x10C0B, 0x10C0C, 0x10C0D, 0x10C0E, 0x10C0F, 0x10C10, 0x10C11,
|
10917
|
+
0x10C12, 0x10C13, 0x10C14, 0x10C15, 0x10C16, 0x10C17, 0x10C18, 0x10C19,
|
10918
|
+
0x10C1A, 0x10C1B, 0x10C1C, 0x10C1D, 0x10C1E, 0x10C1F, 0x10C20, 0x10C21,
|
10919
|
+
0x10C22, 0x10C23, 0x10C24, 0x10C25, 0x10C26, 0x10C27, 0x10C28, 0x10C29,
|
10920
|
+
0x10C2A, 0x10C2B, 0x10C2C, 0x10C2D, 0x10C2E, 0x10C2F, 0x10C30, 0x10C31,
|
10921
|
+
0x10C32, 0x10C33, 0x10C34, 0x10C35, 0x10C36, 0x10C37, 0x10C38, 0x10C39,
|
10922
|
+
0x10C3A, 0x10C3B, 0x10C3C, 0x10C3D, 0x10C3E, 0x10C3F, 0x10C40, 0x10C41,
|
10923
|
+
0x10C42, 0x10C43, 0x10C44, 0x10C45, 0x10C46, 0x10C47, 0x10C48, 0x1EE00,
|
10924
|
+
0x1EE01, 0x1EE02, 0x1EE03, 0x1EE05, 0x1EE06, 0x1EE07, 0x1EE08, 0x1EE09,
|
10925
|
+
0x1EE0A, 0x1EE0B, 0x1EE0C, 0x1EE0D, 0x1EE0E, 0x1EE0F, 0x1EE10, 0x1EE11,
|
10926
|
+
0x1EE12, 0x1EE13, 0x1EE14, 0x1EE15, 0x1EE16, 0x1EE17, 0x1EE18, 0x1EE19,
|
10927
|
+
0x1EE1A, 0x1EE1B, 0x1EE1C, 0x1EE1D, 0x1EE1E, 0x1EE1F, 0x1EE21, 0x1EE22,
|
10928
|
+
0x1EE24, 0x1EE27, 0x1EE29, 0x1EE2A, 0x1EE2B, 0x1EE2C, 0x1EE2D, 0x1EE2E,
|
10929
|
+
0x1EE2F, 0x1EE30, 0x1EE31, 0x1EE32, 0x1EE34, 0x1EE35, 0x1EE36, 0x1EE37,
|
10930
|
+
0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE4D, 0x1EE4E,
|
10931
|
+
0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D,
|
10932
|
+
0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE67, 0x1EE68, 0x1EE69, 0x1EE6A,
|
10933
|
+
0x1EE6C, 0x1EE6D, 0x1EE6E, 0x1EE6F, 0x1EE70, 0x1EE71, 0x1EE72, 0x1EE74,
|
10934
|
+
0x1EE75, 0x1EE76, 0x1EE77, 0x1EE79, 0x1EE7A, 0x1EE7B, 0x1EE7C, 0x1EE7E,
|
10935
|
+
0x1EE80, 0x1EE81, 0x1EE82, 0x1EE83, 0x1EE84, 0x1EE85, 0x1EE86, 0x1EE87,
|
10936
|
+
0x1EE88, 0x1EE89, 0x1EE8B, 0x1EE8C, 0x1EE8D, 0x1EE8E, 0x1EE8F, 0x1EE90,
|
10937
|
+
0x1EE91, 0x1EE92, 0x1EE93, 0x1EE94, 0x1EE95, 0x1EE96, 0x1EE97, 0x1EE98,
|
10938
|
+
0x1EE99, 0x1EE9A, 0x1EE9B, 0x1EEA1, 0x1EEA2, 0x1EEA3, 0x1EEA5, 0x1EEA6,
|
10939
|
+
0x1EEA7, 0x1EEA8, 0x1EEA9, 0x1EEAB, 0x1EEAC, 0x1EEAD, 0x1EEAE, 0x1EEAF,
|
10940
|
+
0x1EEB0, 0x1EEB1, 0x1EEB2, 0x1EEB3, 0x1EEB4, 0x1EEB5, 0x1EEB6, 0x1EEB7,
|
10941
|
+
0x1EEB8, 0x1EEB9, 0x1EEBA, 0x1EEBB, 0x10FFFD];
|
10942
|
+
|
10943
|
+
function determineBidi(cueDiv) {
|
10944
|
+
var nodeStack = [],
|
10945
|
+
text = "",
|
10946
|
+
charCode;
|
10947
|
+
|
10948
|
+
if (!cueDiv || !cueDiv.childNodes) {
|
10949
|
+
return "ltr";
|
10950
|
+
}
|
10951
|
+
|
10952
|
+
function pushNodes(nodeStack, node) {
|
10953
|
+
for (var i = node.childNodes.length - 1; i >= 0; i--) {
|
10954
|
+
nodeStack.push(node.childNodes[i]);
|
10955
|
+
}
|
10956
|
+
}
|
10957
|
+
|
10958
|
+
function nextTextNode(nodeStack) {
|
10959
|
+
if (!nodeStack || !nodeStack.length) {
|
10960
|
+
return null;
|
10961
|
+
}
|
10962
|
+
|
10963
|
+
var node = nodeStack.pop(),
|
10964
|
+
text = node.textContent || node.innerText;
|
10965
|
+
if (text) {
|
10966
|
+
// TODO: This should match all unicode type B characters (paragraph
|
10967
|
+
// separator characters). See issue #115.
|
10968
|
+
var m = text.match(/^.*(\n|\r)/);
|
10969
|
+
if (m) {
|
10970
|
+
nodeStack.length = 0;
|
10971
|
+
return m[0];
|
10972
|
+
}
|
10973
|
+
return text;
|
10974
|
+
}
|
10975
|
+
if (node.tagName === "ruby") {
|
10976
|
+
return nextTextNode(nodeStack);
|
10977
|
+
}
|
10978
|
+
if (node.childNodes) {
|
10979
|
+
pushNodes(nodeStack, node);
|
10980
|
+
return nextTextNode(nodeStack);
|
10981
|
+
}
|
10982
|
+
}
|
10983
|
+
|
10984
|
+
pushNodes(nodeStack, cueDiv);
|
10985
|
+
while ((text = nextTextNode(nodeStack))) {
|
10986
|
+
for (var i = 0; i < text.length; i++) {
|
10987
|
+
charCode = text.charCodeAt(i);
|
10988
|
+
for (var j = 0; j < strongRTLChars.length; j++) {
|
10989
|
+
if (strongRTLChars[j] === charCode) {
|
10990
|
+
return "rtl";
|
10991
|
+
}
|
10992
|
+
}
|
10993
|
+
}
|
10994
|
+
}
|
10995
|
+
return "ltr";
|
10996
|
+
}
|
10997
|
+
|
10998
|
+
function computeLinePos(cue) {
|
10999
|
+
if (typeof cue.line === "number" &&
|
11000
|
+
(cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
|
11001
|
+
return cue.line;
|
11002
|
+
}
|
11003
|
+
if (!cue.track || !cue.track.textTrackList ||
|
11004
|
+
!cue.track.textTrackList.mediaElement) {
|
11005
|
+
return -1;
|
11006
|
+
}
|
11007
|
+
var track = cue.track,
|
11008
|
+
trackList = track.textTrackList,
|
11009
|
+
count = 0;
|
11010
|
+
for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
|
11011
|
+
if (trackList[i].mode === "showing") {
|
11012
|
+
count++;
|
11013
|
+
}
|
11014
|
+
}
|
11015
|
+
return ++count * -1;
|
11016
|
+
}
|
11017
|
+
|
11018
|
+
function StyleBox() {
|
11019
|
+
}
|
11020
|
+
|
11021
|
+
// Apply styles to a div. If there is no div passed then it defaults to the
|
11022
|
+
// div on 'this'.
|
11023
|
+
StyleBox.prototype.applyStyles = function(styles, div) {
|
11024
|
+
div = div || this.div;
|
11025
|
+
for (var prop in styles) {
|
11026
|
+
if (styles.hasOwnProperty(prop)) {
|
11027
|
+
div.style[prop] = styles[prop];
|
11028
|
+
}
|
11029
|
+
}
|
11030
|
+
};
|
11031
|
+
|
11032
|
+
StyleBox.prototype.formatStyle = function(val, unit) {
|
11033
|
+
return val === 0 ? 0 : val + unit;
|
11034
|
+
};
|
11035
|
+
|
11036
|
+
// Constructs the computed display state of the cue (a div). Places the div
|
11037
|
+
// into the overlay which should be a block level element (usually a div).
|
11038
|
+
function CueStyleBox(window, cue, styleOptions) {
|
11039
|
+
var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
|
11040
|
+
var color = "rgba(255, 255, 255, 1)";
|
11041
|
+
var backgroundColor = "rgba(0, 0, 0, 0.8)";
|
11042
|
+
|
11043
|
+
if (isIE8) {
|
11044
|
+
color = "rgb(255, 255, 255)";
|
11045
|
+
backgroundColor = "rgb(0, 0, 0)";
|
11046
|
+
}
|
11047
|
+
|
11048
|
+
StyleBox.call(this);
|
11049
|
+
this.cue = cue;
|
11050
|
+
|
11051
|
+
// Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
|
11052
|
+
// have inline positioning and will function as the cue background box.
|
11053
|
+
this.cueDiv = parseContent(window, cue.text);
|
11054
|
+
var styles = {
|
11055
|
+
color: color,
|
11056
|
+
backgroundColor: backgroundColor,
|
11057
|
+
position: "relative",
|
11058
|
+
left: 0,
|
11059
|
+
right: 0,
|
11060
|
+
top: 0,
|
11061
|
+
bottom: 0,
|
11062
|
+
display: "inline"
|
11063
|
+
};
|
11064
|
+
|
11065
|
+
if (!isIE8) {
|
11066
|
+
styles.writingMode = cue.vertical === "" ? "horizontal-tb"
|
11067
|
+
: cue.vertical === "lr" ? "vertical-lr"
|
11068
|
+
: "vertical-rl";
|
11069
|
+
styles.unicodeBidi = "plaintext";
|
11070
|
+
}
|
11071
|
+
this.applyStyles(styles, this.cueDiv);
|
11072
|
+
|
11073
|
+
// Create an absolutely positioned div that will be used to position the cue
|
11074
|
+
// div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
|
11075
|
+
// mirrors of them except "middle" which is "center" in CSS.
|
11076
|
+
this.div = window.document.createElement("div");
|
11077
|
+
styles = {
|
11078
|
+
textAlign: cue.align === "middle" ? "center" : cue.align,
|
11079
|
+
font: styleOptions.font,
|
11080
|
+
whiteSpace: "pre-line",
|
11081
|
+
position: "absolute"
|
11082
|
+
};
|
11083
|
+
|
11084
|
+
if (!isIE8) {
|
11085
|
+
styles.direction = determineBidi(this.cueDiv);
|
11086
|
+
styles.writingMode = cue.vertical === "" ? "horizontal-tb"
|
11087
|
+
: cue.vertical === "lr" ? "vertical-lr"
|
11088
|
+
: "vertical-rl".
|
11089
|
+
stylesunicodeBidi = "plaintext";
|
11090
|
+
}
|
11091
|
+
|
11092
|
+
this.applyStyles(styles);
|
11093
|
+
|
11094
|
+
this.div.appendChild(this.cueDiv);
|
11095
|
+
|
11096
|
+
// Calculate the distance from the reference edge of the viewport to the text
|
11097
|
+
// position of the cue box. The reference edge will be resolved later when
|
11098
|
+
// the box orientation styles are applied.
|
11099
|
+
var textPos = 0;
|
11100
|
+
switch (cue.positionAlign) {
|
11101
|
+
case "start":
|
11102
|
+
textPos = cue.position;
|
11103
|
+
break;
|
11104
|
+
case "middle":
|
11105
|
+
textPos = cue.position - (cue.size / 2);
|
11106
|
+
break;
|
11107
|
+
case "end":
|
11108
|
+
textPos = cue.position - cue.size;
|
11109
|
+
break;
|
11110
|
+
}
|
11111
|
+
|
11112
|
+
// Horizontal box orientation; textPos is the distance from the left edge of the
|
11113
|
+
// area to the left edge of the box and cue.size is the distance extending to
|
11114
|
+
// the right from there.
|
11115
|
+
if (cue.vertical === "") {
|
11116
|
+
this.applyStyles({
|
11117
|
+
left: this.formatStyle(textPos, "%"),
|
11118
|
+
width: this.formatStyle(cue.size, "%"),
|
11119
|
+
});
|
11120
|
+
// Vertical box orientation; textPos is the distance from the top edge of the
|
11121
|
+
// area to the top edge of the box and cue.size is the height extending
|
11122
|
+
// downwards from there.
|
11123
|
+
} else {
|
11124
|
+
this.applyStyles({
|
11125
|
+
top: this.formatStyle(textPos, "%"),
|
11126
|
+
height: this.formatStyle(cue.size, "%")
|
11127
|
+
});
|
11128
|
+
}
|
11129
|
+
|
11130
|
+
this.move = function(box) {
|
11131
|
+
this.applyStyles({
|
11132
|
+
top: this.formatStyle(box.top, "px"),
|
11133
|
+
bottom: this.formatStyle(box.bottom, "px"),
|
11134
|
+
left: this.formatStyle(box.left, "px"),
|
11135
|
+
right: this.formatStyle(box.right, "px"),
|
11136
|
+
height: this.formatStyle(box.height, "px"),
|
11137
|
+
width: this.formatStyle(box.width, "px"),
|
11138
|
+
});
|
11139
|
+
};
|
11140
|
+
}
|
11141
|
+
CueStyleBox.prototype = _objCreate(StyleBox.prototype);
|
11142
|
+
CueStyleBox.prototype.constructor = CueStyleBox;
|
11143
|
+
|
11144
|
+
// Represents the co-ordinates of an Element in a way that we can easily
|
11145
|
+
// compute things with such as if it overlaps or intersects with another Element.
|
11146
|
+
// Can initialize it with either a StyleBox or another BoxPosition.
|
11147
|
+
function BoxPosition(obj) {
|
11148
|
+
var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
|
11149
|
+
|
11150
|
+
// Either a BoxPosition was passed in and we need to copy it, or a StyleBox
|
11151
|
+
// was passed in and we need to copy the results of 'getBoundingClientRect'
|
11152
|
+
// as the object returned is readonly. All co-ordinate values are in reference
|
11153
|
+
// to the viewport origin (top left).
|
11154
|
+
var lh, height, width, top;
|
11155
|
+
if (obj.div) {
|
11156
|
+
height = obj.div.offsetHeight;
|
11157
|
+
width = obj.div.offsetWidth;
|
11158
|
+
top = obj.div.offsetTop;
|
11159
|
+
|
11160
|
+
var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
|
11161
|
+
rects.getClientRects && rects.getClientRects();
|
11162
|
+
obj = obj.div.getBoundingClientRect();
|
11163
|
+
// In certain cases the outter div will be slightly larger then the sum of
|
11164
|
+
// the inner div's lines. This could be due to bold text, etc, on some platforms.
|
11165
|
+
// In this case we should get the average line height and use that. This will
|
11166
|
+
// result in the desired behaviour.
|
11167
|
+
lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
|
11168
|
+
: 0;
|
11169
|
+
|
11170
|
+
}
|
11171
|
+
this.left = obj.left;
|
11172
|
+
this.right = obj.right;
|
11173
|
+
this.top = obj.top || top;
|
11174
|
+
this.height = obj.height || height;
|
11175
|
+
this.bottom = obj.bottom || (top + (obj.height || height));
|
11176
|
+
this.width = obj.width || width;
|
11177
|
+
this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
|
11178
|
+
|
11179
|
+
if (isIE8 && !this.lineHeight) {
|
11180
|
+
this.lineHeight = 13;
|
11181
|
+
}
|
11182
|
+
}
|
11183
|
+
|
11184
|
+
// Move the box along a particular axis. Optionally pass in an amount to move
|
11185
|
+
// the box. If no amount is passed then the default is the line height of the
|
11186
|
+
// box.
|
11187
|
+
BoxPosition.prototype.move = function(axis, toMove) {
|
11188
|
+
toMove = toMove !== undefined ? toMove : this.lineHeight;
|
11189
|
+
switch (axis) {
|
11190
|
+
case "+x":
|
11191
|
+
this.left += toMove;
|
11192
|
+
this.right += toMove;
|
11193
|
+
break;
|
11194
|
+
case "-x":
|
11195
|
+
this.left -= toMove;
|
11196
|
+
this.right -= toMove;
|
11197
|
+
break;
|
11198
|
+
case "+y":
|
11199
|
+
this.top += toMove;
|
11200
|
+
this.bottom += toMove;
|
11201
|
+
break;
|
11202
|
+
case "-y":
|
11203
|
+
this.top -= toMove;
|
11204
|
+
this.bottom -= toMove;
|
11205
|
+
break;
|
11206
|
+
}
|
11207
|
+
};
|
11208
|
+
|
11209
|
+
// Check if this box overlaps another box, b2.
|
11210
|
+
BoxPosition.prototype.overlaps = function(b2) {
|
11211
|
+
return this.left < b2.right &&
|
11212
|
+
this.right > b2.left &&
|
11213
|
+
this.top < b2.bottom &&
|
11214
|
+
this.bottom > b2.top;
|
11215
|
+
};
|
11216
|
+
|
11217
|
+
// Check if this box overlaps any other boxes in boxes.
|
11218
|
+
BoxPosition.prototype.overlapsAny = function(boxes) {
|
11219
|
+
for (var i = 0; i < boxes.length; i++) {
|
11220
|
+
if (this.overlaps(boxes[i])) {
|
11221
|
+
return true;
|
11222
|
+
}
|
11223
|
+
}
|
11224
|
+
return false;
|
11225
|
+
};
|
11226
|
+
|
11227
|
+
// Check if this box is within another box.
|
11228
|
+
BoxPosition.prototype.within = function(container) {
|
11229
|
+
return this.top >= container.top &&
|
11230
|
+
this.bottom <= container.bottom &&
|
11231
|
+
this.left >= container.left &&
|
11232
|
+
this.right <= container.right;
|
11233
|
+
};
|
11234
|
+
|
11235
|
+
// Check if this box is entirely within the container or it is overlapping
|
11236
|
+
// on the edge opposite of the axis direction passed. For example, if "+x" is
|
11237
|
+
// passed and the box is overlapping on the left edge of the container, then
|
11238
|
+
// return true.
|
11239
|
+
BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
|
11240
|
+
switch (axis) {
|
11241
|
+
case "+x":
|
11242
|
+
return this.left < container.left;
|
11243
|
+
case "-x":
|
11244
|
+
return this.right > container.right;
|
11245
|
+
case "+y":
|
11246
|
+
return this.top < container.top;
|
11247
|
+
case "-y":
|
11248
|
+
return this.bottom > container.bottom;
|
11249
|
+
}
|
11250
|
+
};
|
11251
|
+
|
11252
|
+
// Find the percentage of the area that this box is overlapping with another
|
11253
|
+
// box.
|
11254
|
+
BoxPosition.prototype.intersectPercentage = function(b2) {
|
11255
|
+
var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
|
11256
|
+
y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
|
11257
|
+
intersectArea = x * y;
|
11258
|
+
return intersectArea / (this.height * this.width);
|
11259
|
+
};
|
11260
|
+
|
11261
|
+
// Convert the positions from this box to CSS compatible positions using
|
11262
|
+
// the reference container's positions. This has to be done because this
|
11263
|
+
// box's positions are in reference to the viewport origin, whereas, CSS
|
11264
|
+
// values are in referecne to their respective edges.
|
11265
|
+
BoxPosition.prototype.toCSSCompatValues = function(reference) {
|
11266
|
+
return {
|
11267
|
+
top: this.top - reference.top,
|
11268
|
+
bottom: reference.bottom - this.bottom,
|
11269
|
+
left: this.left - reference.left,
|
11270
|
+
right: reference.right - this.right,
|
11271
|
+
height: this.height,
|
11272
|
+
width: this.width
|
11273
|
+
};
|
11274
|
+
};
|
11275
|
+
|
11276
|
+
// Get an object that represents the box's position without anything extra.
|
11277
|
+
// Can pass a StyleBox, HTMLElement, or another BoxPositon.
|
11278
|
+
BoxPosition.getSimpleBoxPosition = function(obj) {
|
11279
|
+
var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
|
11280
|
+
var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
|
11281
|
+
var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
|
11282
|
+
|
11283
|
+
obj = obj.div ? obj.div.getBoundingClientRect() :
|
11284
|
+
obj.tagName ? obj.getBoundingClientRect() : obj;
|
11285
|
+
var ret = {
|
11286
|
+
left: obj.left,
|
11287
|
+
right: obj.right,
|
11288
|
+
top: obj.top || top,
|
11289
|
+
height: obj.height || height,
|
11290
|
+
bottom: obj.bottom || (top + (obj.height || height)),
|
11291
|
+
width: obj.width || width
|
11292
|
+
};
|
11293
|
+
return ret;
|
11294
|
+
};
|
11295
|
+
|
11296
|
+
// Move a StyleBox to its specified, or next best, position. The containerBox
|
11297
|
+
// is the box that contains the StyleBox, such as a div. boxPositions are
|
11298
|
+
// a list of other boxes that the styleBox can't overlap with.
|
11299
|
+
function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
|
11300
|
+
|
11301
|
+
// Find the best position for a cue box, b, on the video. The axis parameter
|
11302
|
+
// is a list of axis, the order of which, it will move the box along. For example:
|
11303
|
+
// Passing ["+x", "-x"] will move the box first along the x axis in the positive
|
11304
|
+
// direction. If it doesn't find a good position for it there it will then move
|
11305
|
+
// it along the x axis in the negative direction.
|
11306
|
+
function findBestPosition(b, axis) {
|
11307
|
+
var bestPosition,
|
11308
|
+
specifiedPosition = new BoxPosition(b),
|
11309
|
+
percentage = 1; // Highest possible so the first thing we get is better.
|
11310
|
+
|
11311
|
+
for (var i = 0; i < axis.length; i++) {
|
11312
|
+
while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
|
11313
|
+
(b.within(containerBox) && b.overlapsAny(boxPositions))) {
|
11314
|
+
b.move(axis[i]);
|
11315
|
+
}
|
11316
|
+
// We found a spot where we aren't overlapping anything. This is our
|
11317
|
+
// best position.
|
11318
|
+
if (b.within(containerBox)) {
|
11319
|
+
return b;
|
11320
|
+
}
|
11321
|
+
var p = b.intersectPercentage(containerBox);
|
11322
|
+
// If we're outside the container box less then we were on our last try
|
11323
|
+
// then remember this position as the best position.
|
11324
|
+
if (percentage > p) {
|
11325
|
+
bestPosition = new BoxPosition(b);
|
11326
|
+
percentage = p;
|
11327
|
+
}
|
11328
|
+
// Reset the box position to the specified position.
|
11329
|
+
b = new BoxPosition(specifiedPosition);
|
11330
|
+
}
|
11331
|
+
return bestPosition || specifiedPosition;
|
11332
|
+
}
|
11333
|
+
|
11334
|
+
var boxPosition = new BoxPosition(styleBox),
|
11335
|
+
cue = styleBox.cue,
|
11336
|
+
linePos = computeLinePos(cue),
|
11337
|
+
axis = [];
|
11338
|
+
|
11339
|
+
// If we have a line number to align the cue to.
|
11340
|
+
if (cue.snapToLines) {
|
11341
|
+
var size;
|
11342
|
+
switch (cue.vertical) {
|
11343
|
+
case "":
|
11344
|
+
axis = [ "+y", "-y" ];
|
11345
|
+
size = "height";
|
11346
|
+
break;
|
11347
|
+
case "rl":
|
11348
|
+
axis = [ "+x", "-x" ];
|
11349
|
+
size = "width";
|
11350
|
+
break;
|
11351
|
+
case "lr":
|
11352
|
+
axis = [ "-x", "+x" ];
|
11353
|
+
size = "width";
|
11354
|
+
break;
|
11355
|
+
}
|
11356
|
+
|
11357
|
+
var step = boxPosition.lineHeight,
|
11358
|
+
position = step * Math.round(linePos),
|
11359
|
+
maxPosition = containerBox[size] + step,
|
11360
|
+
initialAxis = axis[0];
|
11361
|
+
|
11362
|
+
// If the specified intial position is greater then the max position then
|
11363
|
+
// clamp the box to the amount of steps it would take for the box to
|
11364
|
+
// reach the max position.
|
11365
|
+
if (Math.abs(position) > maxPosition) {
|
11366
|
+
position = position < 0 ? -1 : 1;
|
11367
|
+
position *= Math.ceil(maxPosition / step) * step;
|
11368
|
+
}
|
11369
|
+
|
11370
|
+
// If computed line position returns negative then line numbers are
|
11371
|
+
// relative to the bottom of the video instead of the top. Therefore, we
|
11372
|
+
// need to increase our initial position by the length or width of the
|
11373
|
+
// video, depending on the writing direction, and reverse our axis directions.
|
11374
|
+
if (linePos < 0) {
|
11375
|
+
position += cue.vertical === "" ? containerBox.height : containerBox.width;
|
11376
|
+
axis = axis.reverse();
|
11377
|
+
}
|
11378
|
+
|
11379
|
+
// Move the box to the specified position. This may not be its best
|
11380
|
+
// position.
|
11381
|
+
boxPosition.move(initialAxis, position);
|
11382
|
+
|
11383
|
+
} else {
|
11384
|
+
// If we have a percentage line value for the cue.
|
11385
|
+
var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
|
11386
|
+
|
11387
|
+
switch (cue.lineAlign) {
|
11388
|
+
case "middle":
|
11389
|
+
linePos -= (calculatedPercentage / 2);
|
11390
|
+
break;
|
11391
|
+
case "end":
|
11392
|
+
linePos -= calculatedPercentage;
|
11393
|
+
break;
|
11394
|
+
}
|
11395
|
+
|
11396
|
+
// Apply initial line position to the cue box.
|
11397
|
+
switch (cue.vertical) {
|
11398
|
+
case "":
|
11399
|
+
styleBox.applyStyles({
|
11400
|
+
top: styleBox.formatStyle(linePos, "%")
|
11401
|
+
});
|
11402
|
+
break;
|
11403
|
+
case "rl":
|
11404
|
+
styleBox.applyStyles({
|
11405
|
+
left: styleBox.formatStyle(linePos, "%")
|
11406
|
+
});
|
11407
|
+
break;
|
11408
|
+
case "lr":
|
11409
|
+
styleBox.applyStyles({
|
11410
|
+
right: styleBox.formatStyle(linePos, "%")
|
11411
|
+
});
|
11412
|
+
break;
|
11413
|
+
}
|
11414
|
+
|
11415
|
+
axis = [ "+y", "-x", "+x", "-y" ];
|
11416
|
+
|
11417
|
+
// Get the box position again after we've applied the specified positioning
|
11418
|
+
// to it.
|
11419
|
+
boxPosition = new BoxPosition(styleBox);
|
11420
|
+
}
|
11421
|
+
|
11422
|
+
var bestPosition = findBestPosition(boxPosition, axis);
|
11423
|
+
styleBox.move(bestPosition.toCSSCompatValues(containerBox));
|
11424
|
+
}
|
11425
|
+
|
11426
|
+
function WebVTT() {
|
11427
|
+
// Nothing
|
11428
|
+
}
|
11429
|
+
|
11430
|
+
// Helper to allow strings to be decoded instead of the default binary utf8 data.
|
11431
|
+
WebVTT.StringDecoder = function() {
|
11432
|
+
return {
|
11433
|
+
decode: function(data) {
|
11434
|
+
if (!data) {
|
11435
|
+
return "";
|
11436
|
+
}
|
11437
|
+
if (typeof data !== "string") {
|
11438
|
+
throw new Error("Error - expected string data.");
|
11439
|
+
}
|
11440
|
+
return decodeURIComponent(encodeURIComponent(data));
|
11441
|
+
}
|
11442
|
+
};
|
11443
|
+
};
|
11444
|
+
|
11445
|
+
WebVTT.convertCueToDOMTree = function(window, cuetext) {
|
11446
|
+
if (!window || !cuetext) {
|
11447
|
+
return null;
|
11448
|
+
}
|
11449
|
+
return parseContent(window, cuetext);
|
11450
|
+
};
|
11451
|
+
|
11452
|
+
var FONT_SIZE_PERCENT = 0.05;
|
11453
|
+
var FONT_STYLE = "sans-serif";
|
11454
|
+
var CUE_BACKGROUND_PADDING = "1.5%";
|
11455
|
+
|
11456
|
+
// Runs the processing model over the cues and regions passed to it.
|
11457
|
+
// @param overlay A block level element (usually a div) that the computed cues
|
11458
|
+
// and regions will be placed into.
|
11459
|
+
WebVTT.processCues = function(window, cues, overlay) {
|
11460
|
+
if (!window || !cues || !overlay) {
|
11461
|
+
return null;
|
11462
|
+
}
|
11463
|
+
|
11464
|
+
// Remove all previous children.
|
11465
|
+
while (overlay.firstChild) {
|
11466
|
+
overlay.removeChild(overlay.firstChild);
|
11467
|
+
}
|
11468
|
+
|
11469
|
+
var paddedOverlay = window.document.createElement("div");
|
11470
|
+
paddedOverlay.style.position = "absolute";
|
11471
|
+
paddedOverlay.style.left = "0";
|
11472
|
+
paddedOverlay.style.right = "0";
|
11473
|
+
paddedOverlay.style.top = "0";
|
11474
|
+
paddedOverlay.style.bottom = "0";
|
11475
|
+
paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
|
11476
|
+
overlay.appendChild(paddedOverlay);
|
11477
|
+
|
11478
|
+
// Determine if we need to compute the display states of the cues. This could
|
11479
|
+
// be the case if a cue's state has been changed since the last computation or
|
11480
|
+
// if it has not been computed yet.
|
11481
|
+
function shouldCompute(cues) {
|
11482
|
+
for (var i = 0; i < cues.length; i++) {
|
11483
|
+
if (cues[i].hasBeenReset || !cues[i].displayState) {
|
11484
|
+
return true;
|
11485
|
+
}
|
11486
|
+
}
|
11487
|
+
return false;
|
11488
|
+
}
|
11489
|
+
|
11490
|
+
// We don't need to recompute the cues' display states. Just reuse them.
|
11491
|
+
if (!shouldCompute(cues)) {
|
11492
|
+
for (var i = 0; i < cues.length; i++) {
|
11493
|
+
paddedOverlay.appendChild(cues[i].displayState);
|
11494
|
+
}
|
11495
|
+
return;
|
11496
|
+
}
|
11497
|
+
|
11498
|
+
var boxPositions = [],
|
11499
|
+
containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
|
11500
|
+
fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
|
11501
|
+
var styleOptions = {
|
11502
|
+
font: fontSize + "px " + FONT_STYLE
|
11503
|
+
};
|
11504
|
+
|
11505
|
+
(function() {
|
11506
|
+
var styleBox, cue;
|
11507
|
+
|
11508
|
+
for (var i = 0; i < cues.length; i++) {
|
11509
|
+
cue = cues[i];
|
11510
|
+
|
11511
|
+
// Compute the intial position and styles of the cue div.
|
11512
|
+
styleBox = new CueStyleBox(window, cue, styleOptions);
|
11513
|
+
paddedOverlay.appendChild(styleBox.div);
|
11514
|
+
|
11515
|
+
// Move the cue div to it's correct line position.
|
11516
|
+
moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
|
11517
|
+
|
11518
|
+
// Remember the computed div so that we don't have to recompute it later
|
11519
|
+
// if we don't have too.
|
11520
|
+
cue.displayState = styleBox.div;
|
11521
|
+
|
11522
|
+
boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
|
11523
|
+
}
|
11524
|
+
})();
|
11525
|
+
};
|
11526
|
+
|
11527
|
+
WebVTT.Parser = function(window, vttjs, decoder) {
|
11528
|
+
if (!decoder) {
|
11529
|
+
decoder = vttjs;
|
11530
|
+
vttjs = {};
|
11531
|
+
}
|
11532
|
+
if (!vttjs) {
|
11533
|
+
vttjs = {};
|
11534
|
+
}
|
11535
|
+
|
11536
|
+
this.window = window;
|
11537
|
+
this.vttjs = vttjs;
|
11538
|
+
this.state = "INITIAL";
|
11539
|
+
this.buffer = "";
|
11540
|
+
this.decoder = decoder || new TextDecoder("utf8");
|
11541
|
+
this.regionList = [];
|
11542
|
+
};
|
11543
|
+
|
11544
|
+
WebVTT.Parser.prototype = {
|
11545
|
+
// If the error is a ParsingError then report it to the consumer if
|
11546
|
+
// possible. If it's not a ParsingError then throw it like normal.
|
11547
|
+
reportOrThrowError: function(e) {
|
11548
|
+
if (e instanceof ParsingError) {
|
11549
|
+
this.onparsingerror && this.onparsingerror(e);
|
11550
|
+
} else {
|
11551
|
+
throw e;
|
11552
|
+
}
|
11553
|
+
},
|
11554
|
+
parse: function (data) {
|
11555
|
+
var self = this;
|
11556
|
+
|
11557
|
+
// If there is no data then we won't decode it, but will just try to parse
|
11558
|
+
// whatever is in buffer already. This may occur in circumstances, for
|
11559
|
+
// example when flush() is called.
|
11560
|
+
if (data) {
|
11561
|
+
// Try to decode the data that we received.
|
11562
|
+
self.buffer += self.decoder.decode(data, {stream: true});
|
11563
|
+
}
|
11564
|
+
|
11565
|
+
function collectNextLine() {
|
11566
|
+
var buffer = self.buffer;
|
11567
|
+
var pos = 0;
|
11568
|
+
while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
|
11569
|
+
++pos;
|
11570
|
+
}
|
11571
|
+
var line = buffer.substr(0, pos);
|
11572
|
+
// Advance the buffer early in case we fail below.
|
11573
|
+
if (buffer[pos] === '\r') {
|
11574
|
+
++pos;
|
11575
|
+
}
|
11576
|
+
if (buffer[pos] === '\n') {
|
11577
|
+
++pos;
|
11578
|
+
}
|
11579
|
+
self.buffer = buffer.substr(pos);
|
11580
|
+
return line;
|
11581
|
+
}
|
11582
|
+
|
11583
|
+
// 3.4 WebVTT region and WebVTT region settings syntax
|
11584
|
+
function parseRegion(input) {
|
11585
|
+
var settings = new Settings();
|
11586
|
+
|
11587
|
+
parseOptions(input, function (k, v) {
|
11588
|
+
switch (k) {
|
11589
|
+
case "id":
|
11590
|
+
settings.set(k, v);
|
11591
|
+
break;
|
11592
|
+
case "width":
|
11593
|
+
settings.percent(k, v);
|
11594
|
+
break;
|
11595
|
+
case "lines":
|
11596
|
+
settings.integer(k, v);
|
11597
|
+
break;
|
11598
|
+
case "regionanchor":
|
11599
|
+
case "viewportanchor":
|
11600
|
+
var xy = v.split(',');
|
11601
|
+
if (xy.length !== 2) {
|
11602
|
+
break;
|
11603
|
+
}
|
11604
|
+
// We have to make sure both x and y parse, so use a temporary
|
11605
|
+
// settings object here.
|
11606
|
+
var anchor = new Settings();
|
11607
|
+
anchor.percent("x", xy[0]);
|
11608
|
+
anchor.percent("y", xy[1]);
|
11609
|
+
if (!anchor.has("x") || !anchor.has("y")) {
|
11610
|
+
break;
|
11611
|
+
}
|
11612
|
+
settings.set(k + "X", anchor.get("x"));
|
11613
|
+
settings.set(k + "Y", anchor.get("y"));
|
11614
|
+
break;
|
11615
|
+
case "scroll":
|
11616
|
+
settings.alt(k, v, ["up"]);
|
11617
|
+
break;
|
11618
|
+
}
|
11619
|
+
}, /=/, /\s/);
|
11620
|
+
|
11621
|
+
// Create the region, using default values for any values that were not
|
11622
|
+
// specified.
|
11623
|
+
if (settings.has("id")) {
|
11624
|
+
var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
|
11625
|
+
region.width = settings.get("width", 100);
|
11626
|
+
region.lines = settings.get("lines", 3);
|
11627
|
+
region.regionAnchorX = settings.get("regionanchorX", 0);
|
11628
|
+
region.regionAnchorY = settings.get("regionanchorY", 100);
|
11629
|
+
region.viewportAnchorX = settings.get("viewportanchorX", 0);
|
11630
|
+
region.viewportAnchorY = settings.get("viewportanchorY", 100);
|
11631
|
+
region.scroll = settings.get("scroll", "");
|
11632
|
+
// Register the region.
|
11633
|
+
self.onregion && self.onregion(region);
|
11634
|
+
// Remember the VTTRegion for later in case we parse any VTTCues that
|
11635
|
+
// reference it.
|
11636
|
+
self.regionList.push({
|
11637
|
+
id: settings.get("id"),
|
11638
|
+
region: region
|
11639
|
+
});
|
11640
|
+
}
|
11641
|
+
}
|
11642
|
+
|
11643
|
+
// 3.2 WebVTT metadata header syntax
|
11644
|
+
function parseHeader(input) {
|
11645
|
+
parseOptions(input, function (k, v) {
|
11646
|
+
switch (k) {
|
11647
|
+
case "Region":
|
11648
|
+
// 3.3 WebVTT region metadata header syntax
|
11649
|
+
parseRegion(v);
|
11650
|
+
break;
|
11651
|
+
}
|
11652
|
+
}, /:/);
|
11653
|
+
}
|
11654
|
+
|
11655
|
+
// 5.1 WebVTT file parsing.
|
11656
|
+
try {
|
11657
|
+
var line;
|
11658
|
+
if (self.state === "INITIAL") {
|
11659
|
+
// We can't start parsing until we have the first line.
|
11660
|
+
if (!/\r\n|\n/.test(self.buffer)) {
|
11661
|
+
return this;
|
11662
|
+
}
|
11663
|
+
|
11664
|
+
line = collectNextLine();
|
11665
|
+
|
11666
|
+
var m = line.match(/^WEBVTT([ \t].*)?$/);
|
11667
|
+
if (!m || !m[0]) {
|
11668
|
+
throw new ParsingError(ParsingError.Errors.BadSignature);
|
11669
|
+
}
|
11670
|
+
|
11671
|
+
self.state = "HEADER";
|
11672
|
+
}
|
11673
|
+
|
11674
|
+
var alreadyCollectedLine = false;
|
11675
|
+
while (self.buffer) {
|
11676
|
+
// We can't parse a line until we have the full line.
|
11677
|
+
if (!/\r\n|\n/.test(self.buffer)) {
|
11678
|
+
return this;
|
11679
|
+
}
|
11680
|
+
|
11681
|
+
if (!alreadyCollectedLine) {
|
11682
|
+
line = collectNextLine();
|
11683
|
+
} else {
|
11684
|
+
alreadyCollectedLine = false;
|
11685
|
+
}
|
11686
|
+
|
11687
|
+
switch (self.state) {
|
11688
|
+
case "HEADER":
|
11689
|
+
// 13-18 - Allow a header (metadata) under the WEBVTT line.
|
11690
|
+
if (/:/.test(line)) {
|
11691
|
+
parseHeader(line);
|
11692
|
+
} else if (!line) {
|
11693
|
+
// An empty line terminates the header and starts the body (cues).
|
11694
|
+
self.state = "ID";
|
11695
|
+
}
|
11696
|
+
continue;
|
11697
|
+
case "NOTE":
|
11698
|
+
// Ignore NOTE blocks.
|
11699
|
+
if (!line) {
|
11700
|
+
self.state = "ID";
|
11701
|
+
}
|
11702
|
+
continue;
|
11703
|
+
case "ID":
|
11704
|
+
// Check for the start of NOTE blocks.
|
11705
|
+
if (/^NOTE($|[ \t])/.test(line)) {
|
11706
|
+
self.state = "NOTE";
|
11707
|
+
break;
|
11708
|
+
}
|
11709
|
+
// 19-29 - Allow any number of line terminators, then initialize new cue values.
|
11710
|
+
if (!line) {
|
11711
|
+
continue;
|
11712
|
+
}
|
11713
|
+
self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
|
11714
|
+
self.state = "CUE";
|
11715
|
+
// 30-39 - Check if self line contains an optional identifier or timing data.
|
11716
|
+
if (line.indexOf("-->") === -1) {
|
11717
|
+
self.cue.id = line;
|
11718
|
+
continue;
|
11719
|
+
}
|
11720
|
+
// Process line as start of a cue.
|
11721
|
+
/*falls through*/
|
11722
|
+
case "CUE":
|
11723
|
+
// 40 - Collect cue timings and settings.
|
11724
|
+
try {
|
11725
|
+
parseCue(line, self.cue, self.regionList);
|
11726
|
+
} catch (e) {
|
11727
|
+
self.reportOrThrowError(e);
|
11728
|
+
// In case of an error ignore rest of the cue.
|
11729
|
+
self.cue = null;
|
11730
|
+
self.state = "BADCUE";
|
11731
|
+
continue;
|
11732
|
+
}
|
11733
|
+
self.state = "CUETEXT";
|
11734
|
+
continue;
|
11735
|
+
case "CUETEXT":
|
11736
|
+
var hasSubstring = line.indexOf("-->") !== -1;
|
11737
|
+
// 34 - If we have an empty line then report the cue.
|
11738
|
+
// 35 - If we have the special substring '-->' then report the cue,
|
11739
|
+
// but do not collect the line as we need to process the current
|
11740
|
+
// one as a new cue.
|
11741
|
+
if (!line || hasSubstring && (alreadyCollectedLine = true)) {
|
11742
|
+
// We are done parsing self cue.
|
11743
|
+
self.oncue && self.oncue(self.cue);
|
11744
|
+
self.cue = null;
|
11745
|
+
self.state = "ID";
|
11746
|
+
continue;
|
11747
|
+
}
|
11748
|
+
if (self.cue.text) {
|
11749
|
+
self.cue.text += "\n";
|
11750
|
+
}
|
11751
|
+
self.cue.text += line;
|
11752
|
+
continue;
|
11753
|
+
case "BADCUE": // BADCUE
|
11754
|
+
// 54-62 - Collect and discard the remaining cue.
|
11755
|
+
if (!line) {
|
11756
|
+
self.state = "ID";
|
11757
|
+
}
|
11758
|
+
continue;
|
11759
|
+
}
|
11760
|
+
}
|
11761
|
+
} catch (e) {
|
11762
|
+
self.reportOrThrowError(e);
|
11763
|
+
|
11764
|
+
// If we are currently parsing a cue, report what we have.
|
11765
|
+
if (self.state === "CUETEXT" && self.cue && self.oncue) {
|
11766
|
+
self.oncue(self.cue);
|
11767
|
+
}
|
11768
|
+
self.cue = null;
|
11769
|
+
// Enter BADWEBVTT state if header was not parsed correctly otherwise
|
11770
|
+
// another exception occurred so enter BADCUE state.
|
11771
|
+
self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
|
11772
|
+
}
|
11773
|
+
return this;
|
11774
|
+
},
|
11775
|
+
flush: function () {
|
11776
|
+
var self = this;
|
11777
|
+
try {
|
11778
|
+
// Finish decoding the stream.
|
11779
|
+
self.buffer += self.decoder.decode();
|
11780
|
+
// Synthesize the end of the current cue or region.
|
11781
|
+
if (self.cue || self.state === "HEADER") {
|
11782
|
+
self.buffer += "\n\n";
|
11783
|
+
self.parse();
|
11784
|
+
}
|
11785
|
+
// If we've flushed, parsed, and we're still on the INITIAL state then
|
11786
|
+
// that means we don't have enough of the stream to parse the first
|
11787
|
+
// line.
|
11788
|
+
if (self.state === "INITIAL") {
|
11789
|
+
throw new ParsingError(ParsingError.Errors.BadSignature);
|
11790
|
+
}
|
11791
|
+
} catch(e) {
|
11792
|
+
self.reportOrThrowError(e);
|
11793
|
+
}
|
11794
|
+
self.onflush && self.onflush();
|
11795
|
+
return this;
|
11796
|
+
}
|
11797
|
+
};
|
11798
|
+
|
11799
|
+
global.WebVTT = WebVTT;
|
11800
|
+
|
11801
|
+
}(this, (this.vttjs || {})));
|