diff options
Diffstat (limited to 'javascript/videojs/src/js/control-bar/text-track-controls/chapters-button.js')
| -rw-r--r-- | javascript/videojs/src/js/control-bar/text-track-controls/chapters-button.js | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/javascript/videojs/src/js/control-bar/text-track-controls/chapters-button.js b/javascript/videojs/src/js/control-bar/text-track-controls/chapters-button.js new file mode 100644 index 0000000..8366179 --- /dev/null +++ b/javascript/videojs/src/js/control-bar/text-track-controls/chapters-button.js @@ -0,0 +1,224 @@ +/** + * @file chapters-button.js + */ +import TextTrackButton from './text-track-button.js'; +import Component from '../../component.js'; +import ChaptersTrackMenuItem from './chapters-track-menu-item.js'; +import {toTitleCase} from '../../utils/str.js'; + +/** @import Player from '../../player' */ +/** @import Menu from '../../menu/menu' */ +/** @import TextTrack from '../../tracks/text-track' */ +/** @import TextTrackMenuItem from '../text-track-controls/text-track-menu-item' */ + +/** + * The button component for toggling and selecting chapters + * Chapters act much differently than other text tracks + * Cues are navigation vs. other tracks of alternative languages + * + * @extends TextTrackButton + */ +class ChaptersButton extends TextTrackButton { + + /** + * Creates an instance of this class. + * + * @param {Player} player + * The `Player` that this class should be attached to. + * + * @param {Object} [options] + * The key/value store of player options. + * + * @param {Function} [ready] + * The function to call when this function is ready. + */ + constructor(player, options, ready) { + super(player, options, ready); + + this.setIcon('chapters'); + + this.selectCurrentItem_ = () => { + this.items.forEach(item => { + item.selected(this.track_.activeCues[0] === item.cue); + }); + }; + } + + /** + * Builds the default DOM `className`. + * + * @return {string} + * The DOM `className` for this object. + */ + buildCSSClass() { + return `vjs-chapters-button ${super.buildCSSClass()}`; + } + + buildWrapperCSSClass() { + return `vjs-chapters-button ${super.buildWrapperCSSClass()}`; + } + + /** + * Update the menu based on the current state of its items. + * + * @param {Event} [event] + * An event that triggered this function to run. + * + * @listens TextTrackList#addtrack + * @listens TextTrackList#removetrack + * @listens TextTrackList#change + */ + update(event) { + if (event && event.track && event.track.kind !== 'chapters') { + return; + } + + const track = this.findChaptersTrack(); + + if (track !== this.track_) { + this.setTrack(track); + super.update(); + } else if (!this.items || (track && track.cues && track.cues.length !== this.items.length)) { + // Update the menu initially or if the number of cues has changed since set + super.update(); + } + } + + /** + * Set the currently selected track for the chapters button. + * + * @param {TextTrack} track + * The new track to select. Nothing will change if this is the currently selected + * track. + */ + setTrack(track) { + if (this.track_ === track) { + return; + } + + if (!this.updateHandler_) { + this.updateHandler_ = this.update.bind(this); + } + + // here this.track_ refers to the old track instance + if (this.track_) { + const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); + + if (remoteTextTrackEl) { + remoteTextTrackEl.removeEventListener('load', this.updateHandler_); + } + + this.track_.removeEventListener('cuechange', this.selectCurrentItem_); + + this.track_ = null; + } + + this.track_ = track; + + // here this.track_ refers to the new track instance + if (this.track_) { + this.track_.mode = 'hidden'; + + const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); + + if (remoteTextTrackEl) { + remoteTextTrackEl.addEventListener('load', this.updateHandler_); + } + + this.track_.addEventListener('cuechange', this.selectCurrentItem_); + } + } + + /** + * Find the track object that is currently in use by this ChaptersButton + * + * @return {TextTrack|undefined} + * The current track or undefined if none was found. + */ + findChaptersTrack() { + const tracks = this.player_.textTracks() || []; + + for (let i = tracks.length - 1; i >= 0; i--) { + // We will always choose the last track as our chaptersTrack + const track = tracks[i]; + + if (track.kind === this.kind_) { + return track; + } + } + } + + /** + * Get the caption for the ChaptersButton based on the track label. This will also + * use the current tracks localized kind as a fallback if a label does not exist. + * + * @return {string} + * The tracks current label or the localized track kind. + */ + getMenuCaption() { + if (this.track_ && this.track_.label) { + return this.track_.label; + } + return this.localize(toTitleCase(this.kind_)); + } + + /** + * Create menu from chapter track + * + * @return {Menu} + * New menu for the chapter buttons + */ + createMenu() { + this.options_.title = this.getMenuCaption(); + return super.createMenu(); + } + + /** + * Create a menu item for each text track + * + * @return {TextTrackMenuItem[]} + * Array of menu items + */ + createItems() { + const items = []; + + if (!this.track_) { + return items; + } + + const cues = this.track_.cues; + + if (!cues) { + return items; + } + + for (let i = 0, l = cues.length; i < l; i++) { + const cue = cues[i]; + const mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue }); + + items.push(mi); + } + + return items; + } + +} + +/** + * `kind` of TextTrack to look for to associate it with this menu. + * + * @type {string} + * @private + */ +ChaptersButton.prototype.kind_ = 'chapters'; + +/** + * The text that should display over the `ChaptersButton`s controls. Added for localization. + * + * @type {string} + * @protected + */ +ChaptersButton.prototype.controlText_ = 'Chapters'; + +Component.registerComponent('ChaptersButton', ChaptersButton); +export default ChaptersButton; |
