3 exports.__esModule = true;
5 var _component = require('../component');
7 var _component2 = _interopRequireDefault(_component);
9 var _fn = require('../utils/fn.js');
11 var Fn = _interopRequireWildcard(_fn);
13 var _window = require('global/window');
15 var _window2 = _interopRequireDefault(_window);
17 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
19 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
21 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
23 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
25 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
26 * @file text-track-display.js
30 var darkGray = '#222';
31 var lightGray = '#ccc';
33 monospace: 'monospace',
34 sansSerif: 'sans-serif',
36 monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
37 monospaceSerif: '"Courier New", monospace',
38 proportionalSansSerif: 'sans-serif',
39 proportionalSerif: 'serif',
40 casual: '"Comic Sans MS", Impact, fantasy',
41 script: '"Monotype Corsiva", cursive',
42 smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
46 * Construct an rgba color from a given hex color code.
48 * @param {number} color
49 * Hex number for color, like #f0e.
51 * @param {number} opacity
52 * Value for opacity, 0.0 - 1.0.
55 * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
59 function constructColor(color, opacity) {
61 // color looks like "#f0e"
62 parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
66 * Try to update the style of a DOM element. Some style changes will throw an error,
67 * particularly in IE8. Those should be noops.
70 * The DOM element to be styled.
72 * @param {string} style
73 * The CSS property on the element that should be styled.
75 * @param {string} rule
76 * The style rule that should be applied to the property.
78 function tryUpdateStyle(el, style, rule) {
80 el.style[style] = rule;
89 * The component for displaying text track cues.
94 var TextTrackDisplay = function (_Component) {
95 _inherits(TextTrackDisplay, _Component);
98 * Creates an instance of this class.
100 * @param {Player} player
101 * The `Player` that this class should be attached to.
103 * @param {Object} [options]
104 * The key/value store of player options.
106 * @param {Component~ReadyCallback} [ready]
107 * The function to call when `TextTrackDisplay` is ready.
109 function TextTrackDisplay(player, options, ready) {
110 _classCallCheck(this, TextTrackDisplay);
112 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready));
114 player.on('loadstart', Fn.bind(_this, _this.toggleDisplay));
115 player.on('texttrackchange', Fn.bind(_this, _this.updateDisplay));
117 // This used to be called during player init, but was causing an error
118 // if a track should show by default and the display hadn't loaded yet.
119 // Should probably be moved to an external track loader when we support
120 // tracks that don't need a display.
121 player.ready(Fn.bind(_this, function () {
122 if (player.tech_ && player.tech_.featuresNativeTextTracks) {
127 player.on('fullscreenchange', Fn.bind(this, this.updateDisplay));
129 var tracks = this.options_.playerOptions.tracks || [];
131 for (var i = 0; i < tracks.length; i++) {
132 this.player_.addRemoteTextTrack(tracks[i], true);
135 var modes = { captions: 1, subtitles: 1 };
136 var trackList = this.player_.textTracks();
137 var firstDesc = void 0;
138 var firstCaptions = void 0;
141 for (var _i = 0; _i < trackList.length; _i++) {
142 var track = trackList[_i];
144 if (track['default']) {
145 if (track.kind === 'descriptions' && !firstDesc) {
147 } else if (track.kind in modes && !firstCaptions) {
148 firstCaptions = track;
153 // We want to show the first default track but captions and subtitles
154 // take precedence over descriptions.
155 // So, display the first default captions or subtitles track
156 // and otherwise the first default descriptions track.
158 firstCaptions.mode = 'showing';
159 } else if (firstDesc) {
160 firstDesc.mode = 'showing';
168 * Turn display of {@link TextTrack}'s from the current state into the other state.
169 * There are only two states:
173 * @listens Player#loadstart
177 TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
178 if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
186 * Create the {@link Component}'s DOM element.
189 * The element that was created.
193 TextTrackDisplay.prototype.createEl = function createEl() {
194 return _Component.prototype.createEl.call(this, 'div', {
195 className: 'vjs-text-track-display'
198 'aria-atomic': 'true'
203 * Clear all displayed {@link TextTrack}s.
207 TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
208 if (typeof _window2['default'].WebVTT === 'function') {
209 _window2['default'].WebVTT.processCues(_window2['default'], [], this.el_);
214 * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
215 * a {@link Player#fullscreenchange} is fired.
217 * @listens Player#texttrackchange
218 * @listens Player#fullscreenchange
222 TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
223 var tracks = this.player_.textTracks();
231 // Track display prioritization model: if multiple tracks are 'showing',
232 // display the first 'subtitles' or 'captions' track which is 'showing',
233 // otherwise display the first 'descriptions' track which is 'showing'
235 var descriptionsTrack = null;
236 var captionsSubtitlesTrack = null;
238 var i = tracks.length;
241 var track = tracks[i];
243 if (track.mode === 'showing') {
244 if (track.kind === 'descriptions') {
245 descriptionsTrack = track;
247 captionsSubtitlesTrack = track;
252 if (captionsSubtitlesTrack) {
253 if (this.getAttribute('aria-live') !== 'off') {
254 this.setAttribute('aria-live', 'off');
256 this.updateForTrack(captionsSubtitlesTrack);
257 } else if (descriptionsTrack) {
258 if (this.getAttribute('aria-live') !== 'assertive') {
259 this.setAttribute('aria-live', 'assertive');
261 this.updateForTrack(descriptionsTrack);
266 * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
268 * @param {TextTrack} track
269 * Text track object to be added to the list.
273 TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
274 if (typeof _window2['default'].WebVTT !== 'function' || !track.activeCues) {
278 var overrides = this.player_.textTrackSettings.getValues();
281 for (var _i2 = 0; _i2 < track.activeCues.length; _i2++) {
282 cues.push(track.activeCues[_i2]);
285 _window2['default'].WebVTT.processCues(_window2['default'], cues, this.el_);
296 var cueDiv = cue.displayState;
298 if (overrides.color) {
299 cueDiv.firstChild.style.color = overrides.color;
301 if (overrides.textOpacity) {
302 tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
304 if (overrides.backgroundColor) {
305 cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
307 if (overrides.backgroundOpacity) {
308 tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
310 if (overrides.windowColor) {
311 if (overrides.windowOpacity) {
312 tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
314 cueDiv.style.backgroundColor = overrides.windowColor;
317 if (overrides.edgeStyle) {
318 if (overrides.edgeStyle === 'dropshadow') {
319 cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
320 } else if (overrides.edgeStyle === 'raised') {
321 cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
322 } else if (overrides.edgeStyle === 'depressed') {
323 cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
324 } else if (overrides.edgeStyle === 'uniform') {
325 cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
328 if (overrides.fontPercent && overrides.fontPercent !== 1) {
329 var fontSize = _window2['default'].parseFloat(cueDiv.style.fontSize);
331 cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
332 cueDiv.style.height = 'auto';
333 cueDiv.style.top = 'auto';
334 cueDiv.style.bottom = '2px';
336 if (overrides.fontFamily && overrides.fontFamily !== 'default') {
337 if (overrides.fontFamily === 'small-caps') {
338 cueDiv.firstChild.style.fontVariant = 'small-caps';
340 cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
346 return TextTrackDisplay;
347 }(_component2['default']);
349 _component2['default'].registerComponent('TextTrackDisplay', TextTrackDisplay);
350 exports['default'] = TextTrackDisplay;