diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 826ec9ffb2..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,47 +0,0 @@ -{ - "evil" : true, - "validthis": true, - "node" : true, - "debug" : true, - "boss" : true, - "expr" : true, - "eqnull" : true, - "quotmark" : "single", - "sub" : true, - "trailing" : true, - "undef" : true, - "laxbreak" : true, - "esnext" : true, - "eqeqeq" : true, - "predef" : [ - "_V_", - "goog", - "console", - - "require", - "define", - "module", - "exports", - "process", - - "q", - "asyncTest", - "deepEqual", - "equal", - "expect", - "module", - "notDeepEqual", - "notEqual", - "notStrictEqual", - "ok", - "throws", - "QUnit", - "raises", - "start", - "stop", - "strictEqual", - "test", - "throws", - "sinon" - ] -} diff --git a/build/grunt.js b/build/grunt.js index 379a815ac2..7372c7530b 100644 --- a/build/grunt.js +++ b/build/grunt.js @@ -114,14 +114,6 @@ module.exports = function(grunt) { build: ['build/temp/*'], dist: ['dist/*'] }, - jshint: { - src: { - src: ['src/js/**/*.js', 'Gruntfile.js', 'test/unit/**/*.js'], - options: { - jshintrc: '.jshintrc' - } - } - }, uglify: { options: { sourceMap: true, @@ -160,10 +152,6 @@ module.exports = function(grunt) { skin: { files: ['src/css/**/*'], tasks: ['sass'] - }, - jshint: { - files: ['src/**/*', 'test/unit/**/*.js', 'Gruntfile.js'], - tasks: 'jshint' } }, connect: { @@ -443,6 +431,14 @@ module.exports = function(grunt) { src: ['build/temp/video.js'] } } + }, + shell: { + lint: { + command: 'vjsstandard', + options: { + preferLocal: true + } + } } }); @@ -455,7 +451,6 @@ module.exports = function(grunt) { const buildDependents = [ 'clean:build', - 'jshint', 'browserify:build', 'exorcise:build', 'concat:novtt', diff --git a/package.json b/package.json index 892c4090cf..522f631712 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "homepage": "http://videojs.com", "author": "Steve Heffernan", "scripts": { + "lint": "vjsstandard", "test": "grunt test" }, "repository": { @@ -45,6 +46,7 @@ "css": "^2.2.0", "es5-shim": "^4.1.3", "es6-shim": "^0.35.1", + "ghooks": "^1.3.2", "gkatsev-grunt-sass": "^1.1.1", "grunt": "^0.4.4", "grunt-aws-s3": "^0.12.1", @@ -57,7 +59,6 @@ "grunt-contrib-connect": "~0.7.1", "grunt-contrib-copy": "^0.8.0", "grunt-contrib-cssmin": "~0.6.0", - "grunt-contrib-jshint": "~0.11.3", "grunt-contrib-less": "~0.6.4", "grunt-contrib-uglify": "^0.8.0", "grunt-contrib-watch": "~0.1.4", @@ -66,6 +67,7 @@ "grunt-fastly": "^0.1.3", "grunt-github-releaser": "^0.1.17", "grunt-karma": "^0.8.3", + "grunt-shell": "^1.3.0", "grunt-version": "~0.3.0", "grunt-videojs-languages": "0.0.4", "grunt-zip": "0.10.2", @@ -87,9 +89,10 @@ "sinon": "^1.16.1", "time-grunt": "^1.1.1", "uglify-js": "~2.3.6", - "videojs-doc-generator": "0.0.1" + "videojs-doc-generator": "0.0.1", + "videojs-standard": "^5.0.0" }, - "standard": { + "vjsstandard": { "ignore": [ "**/Gruntfile.js", "**/build/**", @@ -97,7 +100,14 @@ "**/docs/**", "**/lang/**", "**/sandbox/**", - "**/test/**" + "**/test/api/**", + "**/test/coverage/**", + "**/test/karma.conf.js" ] + }, + "config": { + "ghooks": { + "pre-push": "npm run lint" + } } } diff --git a/src/js/base-styles.js b/src/js/base-styles.js index be6e302393..cd6ff7a128 100644 --- a/src/js/base-styles.js +++ b/src/js/base-styles.js @@ -6,13 +6,17 @@ import window from 'global/window'; import document from 'global/document'; -if (window.VIDEOJS_NO_BASE_THEME) return; +if (window.VIDEOJS_NO_BASE_THEME) { + return; +} const styles = '{{GENERATED_STYLES}}'; -if (styles === '{{GENERATED'+'_STYLES}}'); +// Don't think we need this as it's a noop? +// if (styles === '{{GENERATED'+'_STYLES}}'); const styleNode = document.createElement('style'); + styleNode.innerHTML = styles; document.head.insertBefore(styleNode, document.head.firstChild); diff --git a/src/js/button.js b/src/js/button.js index 54067cc02c..5583d9541a 100644 --- a/src/js/button.js +++ b/src/js/button.js @@ -3,10 +3,7 @@ */ import ClickableComponent from './clickable-component.js'; import Component from './component'; -import * as Events from './utils/events.js'; -import * as Fn from './utils/fn.js'; import log from './utils/log.js'; -import document from 'global/document'; import assign from 'object.assign'; /** @@ -32,7 +29,7 @@ class Button extends ClickableComponent { * @return {Element} * @method createEl */ - createEl(tag='button', props={}, attributes={}) { + createEl(tag = 'button', props = {}, attributes = {}) { props = assign({ className: this.buildCSSClass() }, props); @@ -53,11 +50,15 @@ class Button extends ClickableComponent { // Add attributes for button element attributes = assign({ - type: 'button', // Necessary since the default button type is "submit" - 'aria-live': 'polite' // let the screen reader user know that the text of the button may change + + // Necessary since the default button type is "submit" + 'type': 'button', + + // let the screen reader user know that the text of the button may change + 'aria-live': 'polite' }, attributes); - let el = Component.prototype.createEl.call(this, tag, props, attributes); + const el = Component.prototype.createEl.call(this, tag, props, attributes); this.createControlTextEl(el); @@ -73,8 +74,9 @@ class Button extends ClickableComponent { * @deprecated * @method addChild */ - addChild(child, options={}) { - let className = this.constructor.name; + addChild(child, options = {}) { + const className = this.constructor.name; + log.warn(`Adding an actionable (user controllable) child to a Button (${className}) is not supported; use a ClickableComponent instead.`); // Avoid the error message generated by ClickableComponent's addChild method @@ -87,13 +89,15 @@ class Button extends ClickableComponent { * @method handleKeyPress */ handleKeyPress(event) { + // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button. if (event.which === 32 || event.which === 13) { - } else { - super.handleKeyPress(event); // Pass keypress handling up for unsupported keys + return; } - } + // Pass keypress handling up for unsupported keys + super.handleKeyPress(event); + } } Component.registerComponent('Button', Button); diff --git a/src/js/clickable-component.js b/src/js/clickable-component.js index 2861b3a228..4b4c830165 100644 --- a/src/js/clickable-component.js +++ b/src/js/clickable-component.js @@ -39,7 +39,7 @@ class ClickableComponent extends Component { * @return {Element} * @method createEl */ - createEl(tag='div', props={}, attributes={}) { + createEl(tag = 'div', props = {}, attributes = {}) { props = assign({ className: this.buildCSSClass(), tabIndex: 0 @@ -51,11 +51,13 @@ class ClickableComponent extends Component { // Add ARIA attributes for clickable element which is not a native HTML button attributes = assign({ - role: 'button', - 'aria-live': 'polite' // let the screen reader user know that the text of the element may change + 'role': 'button', + + // let the screen reader user know that the text of the element may change + 'aria-live': 'polite' }, attributes); - let el = super.createEl(tag, props, attributes); + const el = super.createEl(tag, props, attributes); this.createControlTextEl(el); @@ -91,9 +93,11 @@ class ClickableComponent extends Component { * @return {String} * @method controlText */ - controlText(text, el=this.el()) { - if (!text) return this.controlText_ || 'Need Text'; - + controlText(text, el = this.el()) { + if (!text) { + return this.controlText_ || 'Need Text'; + } + const localizedText = this.localize(text); this.controlText_ = text; @@ -121,14 +125,14 @@ class ClickableComponent extends Component { * @return {Component} The child component (created by this process if a string was used) * @method addChild */ - addChild(child, options={}) { + addChild(child, options = {}) { // TODO: Fix adding an actionable child to a ClickableComponent; currently // it will cause issues with assistive technology (e.g. screen readers) // which support ARIA, since an element with role="button" cannot have // actionable child elements. - //let className = this.constructor.name; - //log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`); + // let className = this.constructor.name; + // log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`); return super.addChild(child, options); } @@ -179,12 +183,15 @@ class ClickableComponent extends Component { * @method handleKeyPress */ handleKeyPress(event) { + // Support Space (32) or Enter (13) key operation to fire a click event if (event.which === 32 || event.which === 13) { event.preventDefault(); this.handleClick(event); } else if (super.handleKeyPress) { - super.handleKeyPress(event); // Pass keypress handling up for unsupported keys + + // Pass keypress handling up for unsupported keys + super.handleKeyPress(event); } } diff --git a/src/js/component.js b/src/js/component.js index 9ef9d0caf3..3e1362b34d 100644 --- a/src/js/component.js +++ b/src/js/component.js @@ -3,7 +3,6 @@ * * Player Component - Base class for all UI objects */ - import window from 'global/window'; import * as Dom from './utils/dom.js'; import * as Fn from './utils/fn.js'; @@ -14,7 +13,6 @@ import toTitleCase from './utils/to-title-case.js'; import assign from 'object.assign'; import mergeOptions from './utils/merge-options.js'; - /** * Base UI Component class * Components are embeddable UI objects that are represented by both a @@ -32,7 +30,7 @@ import mergeOptions from './utils/merge-options.js'; * ``` * Components are also event targets. * ```js - * button.on('click', function(){ + * button.on('click', function() { * console.log('Button Clicked!'); * }); * button.trigger('customevent'); @@ -66,7 +64,7 @@ class Component { // If there was no ID from the options, generate one if (!this.id_) { // Don't require the player ID function in the case of mock players - let id = player && player.id && player.id() || 'no_player'; + const id = player && player.id && player.id() || 'no_player'; this.id_ = `${id}_component_${Guid.newGUID()}`; } @@ -219,21 +217,21 @@ class Component { } localize(string) { - let code = this.player_.language && this.player_.language(); - let languages = this.player_.languages && this.player_.languages(); + const code = this.player_.language && this.player_.language(); + const languages = this.player_.languages && this.player_.languages(); if (!code || !languages) { return string; } - let language = languages[code]; + const language = languages[code]; if (language && language[string]) { return language[string]; } - let primaryCode = code.split('-')[0]; - let primaryLang = languages[primaryCode]; + const primaryCode = code.split('-')[0]; + const primaryLang = languages[primaryCode]; if (primaryLang && primaryLang[string]) { return primaryLang[string]; @@ -340,7 +338,7 @@ class Component { * @return {Component} The child component (created by this process if a string was used) * @method addChild */ - addChild(child, options={}, index=this.children_.length) { + addChild(child, options = {}, index = this.children_.length) { let component; let componentName; @@ -361,14 +359,14 @@ class Component { // If no componentClass in options, assume componentClass is the name lowercased // (e.g. playButton) - let componentClassName = options.componentClass || toTitleCase(componentName); + const componentClassName = options.componentClass || toTitleCase(componentName); // Set name through options options.name = componentName; // Create a new object & element for this controls set // If there's no .player_, this is a player - let ComponentClass = Component.getComponent(componentClassName); + const ComponentClass = Component.getComponent(componentClassName); if (!ComponentClass) { throw new Error(`Component ${componentClassName} does not exist`); @@ -406,8 +404,9 @@ class Component { // Add the UI object's element to the container div (box) // Having an element is not required if (typeof component.el === 'function' && component.el()) { - let childNodes = this.contentEl().children; - let refNode = childNodes[index] || null; + const childNodes = this.contentEl().children; + const refNode = childNodes[index] || null; + this.contentEl().insertBefore(component.el(), refNode); } @@ -448,7 +447,7 @@ class Component { this.childIndex_[component.id()] = null; this.childNameIndex_[component.name()] = null; - let compEl = component.el(); + const compEl = component.el(); if (compEl && compEl.parentNode === this.contentEl()) { this.contentEl().removeChild(component.el()); @@ -502,14 +501,14 @@ class Component { * @method initChildren */ initChildren() { - let children = this.options_.children; + const children = this.options_.children; if (children) { // `this` is `parent` - let parentOptions = this.options_; + const parentOptions = this.options_; - let handleAdd = (child) => { - let name = child.name; + const handleAdd = (child) => { + const name = child.name; let opts = child.opts; // Allow options for children to be set at the parent options @@ -539,7 +538,8 @@ class Component { // Add a direct reference to the child by name on the parent instance. // If two of the same component are used, different names should be supplied // for each - let newChild = this.addChild(name, opts); + const newChild = this.addChild(name, opts); + if (newChild) { this[name] = newChild; } @@ -547,7 +547,7 @@ class Component { // Allow for an array of children details to passed in the options let workingChildren; - let Tech = Component.getComponent('Tech'); + const Tech = Component.getComponent('Tech'); if (Array.isArray(children)) { workingChildren = children; @@ -563,13 +563,13 @@ class Component { return !workingChildren.some(function(wchild) { if (typeof wchild === 'string') { return child === wchild; - } else { - return child === wchild.name; } + return child === wchild.name; }); })) .map((child) => { - let name, opts; + let name; + let opts; if (typeof child === 'string') { name = child; @@ -585,8 +585,9 @@ class Component { // we have to make sure that child.name isn't in the techOrder since // techs are registerd as Components but can't aren't compatible // See https://github.com/videojs/video.js/issues/2772 - let c = Component.getComponent(child.opts.componentClass || + const c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name)); + return c && !Tech.isTech(c); }) .forEach(handleAdd); @@ -608,7 +609,7 @@ class Component { /** * Add an event listener to this component's element * ```js - * var myFunc = function(){ + * var myFunc = function() { * var myComponent = this; * // Do something when the event is fired * }; @@ -797,7 +798,7 @@ class Component { * @return {Component} * @method ready */ - ready(fn, sync=false) { + ready(fn, sync = false) { if (fn) { if (this.isReady_) { if (sync) { @@ -824,14 +825,14 @@ class Component { this.isReady_ = true; // Ensure ready is triggerd asynchronously - this.setTimeout(function(){ - let readyQueue = this.readyQueue_; + this.setTimeout(function() { + const readyQueue = this.readyQueue_; // Reset Ready Queue this.readyQueue_ = []; if (readyQueue && readyQueue.length > 0) { - readyQueue.forEach(function(fn){ + readyQueue.forEach(function(fn) { fn.call(this); }, this); } @@ -1077,8 +1078,8 @@ class Component { } // Get dimension value from style - let val = this.el_.style[widthOrHeight]; - let pxIndex = val.indexOf('px'); + const val = this.el_.style[widthOrHeight]; + const pxIndex = val.indexOf('px'); if (pxIndex !== -1) { // Return the pixel value with no 'px' @@ -1106,11 +1107,13 @@ class Component { if (typeof window.getComputedStyle === 'function') { const computedStyle = window.getComputedStyle(this.el_); + computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight]; } else if (this.el_.currentStyle) { // ie 8 doesn't support computed style, shim it // return clientWidth or clientHeight instead for better accuracy const rule = `offset${toTitleCase(widthOrHeight)}`; + computedWidthOrHeight = this.el_[rule]; } @@ -1194,7 +1197,7 @@ class Component { // So, if we moved only a small distance, this could still be a tap const xdiff = event.touches[0].pageX - firstTouch.pageX; const ydiff = event.touches[0].pageY - firstTouch.pageY; - const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff); + const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff); if (touchDistance > tapMovementThreshold) { couldBeTap = false; @@ -1297,7 +1300,7 @@ class Component { fn = Fn.bind(this, fn); // window.setTimeout would be preferable here, but due to some bizarre issue with Sinon and/or Phantomjs, we can't. - let timeoutId = window.setTimeout(fn, timeout); + const timeoutId = window.setTimeout(fn, timeout); const disposeFn = function() { this.clearTimeout(timeoutId); @@ -1340,7 +1343,7 @@ class Component { setInterval(fn, interval) { fn = Fn.bind(this, fn); - let intervalId = window.setInterval(fn, interval); + const intervalId = window.setInterval(fn, interval); const disposeFn = function() { this.clearInterval(intervalId); @@ -1425,7 +1428,7 @@ class Component { // Set up the constructor using the supplied init method // or using the init of the parent object // Make sure to check the unobfuscated version for external libs - let init = props.init || props.init || this.prototype.init || this.prototype.init || function() {}; + const init = props.init || props.init || this.prototype.init || this.prototype.init || function() {}; // In Resig's simple class inheritance (previously used) the constructor // is a function that calls `this.init.apply(arguments)` // However that would prevent us from using `ParentObject.call(this);` @@ -1435,7 +1438,7 @@ class Component { // `ParentObject.prototype.init.apply(this, arguments);` // Bleh. We're not creating a _super() function, so it's good to keep // the parent constructor reference simple. - let subObj = function() { + const subObj = function() { init.apply(this, arguments); }; @@ -1449,7 +1452,7 @@ class Component { subObj.extend = Component.extend; // Extend subObj's prototype with functions and other properties from props - for (let name in props) { + for (const name in props) { if (props.hasOwnProperty(name)) { subObj.prototype[name] = props[name]; } diff --git a/src/js/control-bar/audio-track-controls/audio-track-button.js b/src/js/control-bar/audio-track-controls/audio-track-button.js index b4f7d5575a..3b84c3db6b 100644 --- a/src/js/control-bar/audio-track-controls/audio-track-button.js +++ b/src/js/control-bar/audio-track-controls/audio-track-button.js @@ -3,7 +3,6 @@ */ import TrackButton from '../track-button.js'; import Component from '../../component.js'; -import * as Fn from '../../utils/fn.js'; import AudioTrackMenuItem from './audio-track-menu-item.js'; /** @@ -40,19 +39,19 @@ class AudioTrackButton extends TrackButton { * @method createItems */ createItems(items = []) { - let tracks = this.player_.audioTracks && this.player_.audioTracks(); + const tracks = this.player_.audioTracks && this.player_.audioTracks(); if (!tracks) { return items; } for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; + const track = tracks[i]; items.push(new AudioTrackMenuItem(this.player_, { + track, // MenuItem is selectable - 'selectable': true, - 'track': track + selectable: true })); } diff --git a/src/js/control-bar/audio-track-controls/audio-track-menu-item.js b/src/js/control-bar/audio-track-controls/audio-track-menu-item.js index fcaf428a43..b97f44a22f 100644 --- a/src/js/control-bar/audio-track-controls/audio-track-menu-item.js +++ b/src/js/control-bar/audio-track-controls/audio-track-menu-item.js @@ -15,8 +15,8 @@ import * as Fn from '../../utils/fn.js'; */ class AudioTrackMenuItem extends MenuItem { constructor(player, options) { - let track = options.track; - let tracks = player.audioTracks(); + const track = options.track; + const tracks = player.audioTracks(); // Modify options for parent MenuItem class's init. options.label = track.label || track.language || 'Unknown'; @@ -27,7 +27,7 @@ class AudioTrackMenuItem extends MenuItem { this.track = track; if (tracks) { - let changeHandler = Fn.bind(this, this.handleTracksChange); + const changeHandler = Fn.bind(this, this.handleTracksChange); tracks.addEventListener('change', changeHandler); this.on('dispose', () => { @@ -42,14 +42,16 @@ class AudioTrackMenuItem extends MenuItem { * @method handleClick */ handleClick(event) { - let tracks = this.player_.audioTracks(); + const tracks = this.player_.audioTracks(); super.handleClick(event); - if (!tracks) return; + if (!tracks) { + return; + } for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; + const track = tracks[i]; if (track === this.track) { track.enabled = true; diff --git a/src/js/control-bar/control-bar.js b/src/js/control-bar/control-bar.js index 1fbc82abd6..90d110f696 100644 --- a/src/js/control-bar/control-bar.js +++ b/src/js/control-bar/control-bar.js @@ -4,24 +4,24 @@ import Component from '../component.js'; // Required children -import PlayToggle from './play-toggle.js'; -import CurrentTimeDisplay from './time-controls/current-time-display.js'; -import DurationDisplay from './time-controls/duration-display.js'; -import TimeDivider from './time-controls/time-divider.js'; -import RemainingTimeDisplay from './time-controls/remaining-time-display.js'; -import LiveDisplay from './live-display.js'; -import ProgressControl from './progress-control/progress-control.js'; -import FullscreenToggle from './fullscreen-toggle.js'; -import VolumeControl from './volume-control/volume-control.js'; -import VolumeMenuButton from './volume-menu-button.js'; -import MuteToggle from './mute-toggle.js'; -import ChaptersButton from './text-track-controls/chapters-button.js'; -import DescriptionsButton from './text-track-controls/descriptions-button.js'; -import SubtitlesButton from './text-track-controls/subtitles-button.js'; -import CaptionsButton from './text-track-controls/captions-button.js'; -import AudioTrackButton from './audio-track-controls/audio-track-button.js'; -import PlaybackRateMenuButton from './playback-rate-menu/playback-rate-menu-button.js'; -import CustomControlSpacer from './spacer-controls/custom-control-spacer.js'; +import './play-toggle.js'; +import './time-controls/current-time-display.js'; +import './time-controls/duration-display.js'; +import './time-controls/time-divider.js'; +import './time-controls/remaining-time-display.js'; +import './live-display.js'; +import './progress-control/progress-control.js'; +import './fullscreen-toggle.js'; +import './volume-control/volume-control.js'; +import './volume-menu-button.js'; +import './mute-toggle.js'; +import './text-track-controls/chapters-button.js'; +import './text-track-controls/descriptions-button.js'; +import './text-track-controls/subtitles-button.js'; +import './text-track-controls/captions-button.js'; +import './audio-track-controls/audio-track-button.js'; +import './playback-rate-menu/playback-rate-menu-button.js'; +import './spacer-controls/custom-control-spacer.js'; /** * Container of main controls @@ -42,7 +42,8 @@ class ControlBar extends Component { className: 'vjs-control-bar', dir: 'ltr' }, { - 'role': 'group' // The control bar is a group, so it can contain menuitems + // The control bar is a group, so it can contain menuitems + role: 'group' }); } } diff --git a/src/js/control-bar/live-display.js b/src/js/control-bar/live-display.js index 25fd01710b..017b5d0576 100644 --- a/src/js/control-bar/live-display.js +++ b/src/js/control-bar/live-display.js @@ -27,7 +27,7 @@ class LiveDisplay extends Component { * @method createEl */ createEl() { - var el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-live-control vjs-control' }); diff --git a/src/js/control-bar/mute-toggle.js b/src/js/control-bar/mute-toggle.js index aa063ecc23..16adb66b1b 100644 --- a/src/js/control-bar/mute-toggle.js +++ b/src/js/control-bar/mute-toggle.js @@ -21,14 +21,15 @@ class MuteToggle extends Button { this.on(player, 'volumechange', this.update); // hide mute toggle if the current tech doesn't support volume control - if (player.tech_ && player.tech_['featuresVolumeControl'] === false) { + if (player.tech_ && player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } this.on(player, 'loadstart', function() { - this.update(); // We need to update the button to account for a default muted state. + // We need to update the button to account for a default muted state. + this.update(); - if (player.tech_['featuresVolumeControl'] === false) { + if (player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } else { this.removeClass('vjs-hidden'); @@ -52,7 +53,7 @@ class MuteToggle extends Button { * @method handleClick */ handleClick() { - this.player_.muted( this.player_.muted() ? false : true ); + this.player_.muted(this.player_.muted() ? false : true); } /** @@ -61,8 +62,8 @@ class MuteToggle extends Button { * @method update */ update() { - var vol = this.player_.volume(), - level = 3; + const vol = this.player_.volume(); + let level = 3; if (vol === 0 || this.player_.muted()) { level = 0; @@ -75,13 +76,14 @@ class MuteToggle extends Button { // Don't rewrite the button text if the actual text doesn't change. // This causes unnecessary and confusing information for screen reader users. // This check is needed because this function gets called every time the volume level is changed. - let toMute = this.player_.muted() ? 'Unmute' : 'Mute'; + const toMute = this.player_.muted() ? 'Unmute' : 'Mute'; + if (this.controlText() !== toMute) { this.controlText(toMute); } - /* TODO improve muted icon classes */ - for (var i = 0; i < 4; i++) { + // TODO improve muted icon classes + for (let i = 0; i < 4; i++) { Dom.removeElClass(this.el_, `vjs-vol-${i}`); } Dom.addElClass(this.el_, `vjs-vol-${level}`); diff --git a/src/js/control-bar/play-toggle.js b/src/js/control-bar/play-toggle.js index 5849d7808a..3781d135d4 100644 --- a/src/js/control-bar/play-toggle.js +++ b/src/js/control-bar/play-toggle.js @@ -14,7 +14,7 @@ import Component from '../component.js'; */ class PlayToggle extends Button { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'play', this.handlePlay); @@ -52,7 +52,8 @@ class PlayToggle extends Button { handlePlay() { this.removeClass('vjs-paused'); this.addClass('vjs-playing'); - this.controlText('Pause'); // change the button text to "Pause" + // change the button text to "Pause" + this.controlText('Pause'); } /** @@ -63,7 +64,8 @@ class PlayToggle extends Button { handlePause() { this.removeClass('vjs-playing'); this.addClass('vjs-paused'); - this.controlText('Play'); // change the button text to "Play" + // change the button text to "Play" + this.controlText('Play'); } } diff --git a/src/js/control-bar/playback-rate-menu/playback-rate-menu-button.js b/src/js/control-bar/playback-rate-menu/playback-rate-menu-button.js index 342c0aec16..993e94fdf2 100644 --- a/src/js/control-bar/playback-rate-menu/playback-rate-menu-button.js +++ b/src/js/control-bar/playback-rate-menu/playback-rate-menu-button.js @@ -17,7 +17,7 @@ import * as Dom from '../../utils/dom.js'; */ class PlaybackRateMenuButton extends MenuButton { - constructor(player, options){ + constructor(player, options) { super(player, options); this.updateVisibility(); @@ -34,7 +34,7 @@ class PlaybackRateMenuButton extends MenuButton { * @method createEl */ createEl() { - let el = super.createEl(); + const el = super.createEl(); this.labelEl_ = Dom.createEl('div', { className: 'vjs-playback-rate-value', @@ -63,13 +63,13 @@ class PlaybackRateMenuButton extends MenuButton { * @method createMenu */ createMenu() { - let menu = new Menu(this.player()); - let rates = this.playbackRates(); + const menu = new Menu(this.player()); + const rates = this.playbackRates(); if (rates) { for (let i = rates.length - 1; i >= 0; i--) { menu.addChild( - new PlaybackRateMenuItem(this.player(), { 'rate': rates[i] + 'x'}) + new PlaybackRateMenuItem(this.player(), {rate: rates[i] + 'x'}) ); } } @@ -94,12 +94,13 @@ class PlaybackRateMenuButton extends MenuButton { */ handleClick() { // select next rate option - let currentRate = this.player().playbackRate(); - let rates = this.playbackRates(); + const currentRate = this.player().playbackRate(); + const rates = this.playbackRates(); // this will select first one if the last one currently selected let newRate = rates[0]; - for (let i = 0; i < rates.length ; i++) { + + for (let i = 0; i < rates.length; i++) { if (rates[i] > currentRate) { newRate = rates[i]; break; @@ -115,7 +116,7 @@ class PlaybackRateMenuButton extends MenuButton { * @method playbackRates */ playbackRates() { - return this.options_['playbackRates'] || (this.options_.playerOptions && this.options_.playerOptions['playbackRates']); + return this.options_.playbackRates || (this.options_.playerOptions && this.options_.playerOptions.playbackRates); } /** @@ -126,10 +127,10 @@ class PlaybackRateMenuButton extends MenuButton { * @method playbackRateSupported */ playbackRateSupported() { - return this.player().tech_ - && this.player().tech_['featuresPlaybackRate'] - && this.playbackRates() - && this.playbackRates().length > 0 + return this.player().tech_ && + this.player().tech_.featuresPlaybackRate && + this.playbackRates() && + this.playbackRates().length > 0 ; } diff --git a/src/js/control-bar/playback-rate-menu/playback-rate-menu-item.js b/src/js/control-bar/playback-rate-menu/playback-rate-menu-item.js index 614d8c1cdb..6ec35dac4f 100644 --- a/src/js/control-bar/playback-rate-menu/playback-rate-menu-item.js +++ b/src/js/control-bar/playback-rate-menu/playback-rate-menu-item.js @@ -14,13 +14,13 @@ import Component from '../../component.js'; */ class PlaybackRateMenuItem extends MenuItem { - constructor(player, options){ - let label = options['rate']; - let rate = parseFloat(label, 10); + constructor(player, options) { + const label = options.rate; + const rate = parseFloat(label, 10); // Modify options for parent MenuItem class's init. - options['label'] = label; - options['selected'] = rate === 1; + options.label = label; + options.selected = rate === 1; super(player, options); this.label = label; diff --git a/src/js/control-bar/progress-control/load-progress-bar.js b/src/js/control-bar/progress-control/load-progress-bar.js index ec20e8afff..2e1a8aae1c 100644 --- a/src/js/control-bar/progress-control/load-progress-bar.js +++ b/src/js/control-bar/progress-control/load-progress-bar.js @@ -14,7 +14,7 @@ import * as Dom from '../../utils/dom.js'; */ class LoadProgressBar extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'progress', this.update); } @@ -38,14 +38,16 @@ class LoadProgressBar extends Component { * @method update */ update() { - let buffered = this.player_.buffered(); - let duration = this.player_.duration(); - let bufferedEnd = this.player_.bufferedEnd(); - let children = this.el_.children; + const buffered = this.player_.buffered(); + const duration = this.player_.duration(); + const bufferedEnd = this.player_.bufferedEnd(); + const children = this.el_.children; // get the percent width of a time compared to the total end - let percentify = function (time, end){ - let percent = (time / end) || 0; // no NaN + const percentify = function(time, end) { + // no NaN + const percent = (time / end) || 0; + return ((percent >= 1 ? 1 : percent) * 100) + '%'; }; @@ -54,8 +56,8 @@ class LoadProgressBar extends Component { // add child elements to represent the individual buffered time ranges for (let i = 0; i < buffered.length; i++) { - let start = buffered.start(i); - let end = buffered.end(i); + const start = buffered.start(i); + const end = buffered.end(i); let part = children[i]; if (!part) { @@ -69,7 +71,7 @@ class LoadProgressBar extends Component { // remove unused buffered range elements for (let i = children.length; i > buffered.length; i--) { - this.el_.removeChild(children[i-1]); + this.el_.removeChild(children[i - 1]); } } diff --git a/src/js/control-bar/progress-control/mouse-time-display.js b/src/js/control-bar/progress-control/mouse-time-display.js index e59be2caa4..c72d132adf 100644 --- a/src/js/control-bar/progress-control/mouse-time-display.js +++ b/src/js/control-bar/progress-control/mouse-time-display.js @@ -55,24 +55,24 @@ class MouseTimeDisplay extends Component { } handleMouseMove(event) { - let duration = this.player_.duration(); - let newTime = this.calculateDistance(event) * duration; - let position = event.pageX - Dom.findElPosition(this.el().parentNode).left; + const duration = this.player_.duration(); + const newTime = this.calculateDistance(event) * duration; + const position = event.pageX - Dom.findElPosition(this.el().parentNode).left; this.update(newTime, position); } update(newTime, position) { - let time = formatTime(newTime, this.player_.duration()); + const time = formatTime(newTime, this.player_.duration()); this.el().style.left = position + 'px'; this.el().setAttribute('data-current-time', time); if (this.keepTooltipsInside) { - let clampedPosition = this.clampPosition_(position); - let difference = position - clampedPosition + 1; - let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width); - let tooltipWidthHalf = tooltipWidth / 2; + const clampedPosition = this.clampPosition_(position); + const difference = position - clampedPosition + 1; + const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width); + const tooltipWidthHalf = tooltipWidth / 2; this.tooltip.innerHTML = time; this.tooltip.style.right = `-${tooltipWidthHalf - difference}px`; @@ -98,9 +98,9 @@ class MouseTimeDisplay extends Component { return position; } - let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width); - let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width); - let tooltipWidthHalf = tooltipWidth / 2; + const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width); + const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width); + const tooltipWidthHalf = tooltipWidth / 2; let actualPosition = position; if (position < tooltipWidthHalf) { diff --git a/src/js/control-bar/progress-control/play-progress-bar.js b/src/js/control-bar/progress-control/play-progress-bar.js index 7ddea6cf45..5b29ba862a 100644 --- a/src/js/control-bar/progress-control/play-progress-bar.js +++ b/src/js/control-bar/progress-control/play-progress-bar.js @@ -3,7 +3,6 @@ */ import Component from '../../component.js'; import * as Fn from '../../utils/fn.js'; -import * as Dom from '../../utils/dom.js'; import formatTime from '../../utils/format-time.js'; /** @@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js'; */ class PlayProgressBar extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.updateDataAttr(); this.on(player, 'timeupdate', this.updateDataAttr); @@ -48,7 +47,8 @@ class PlayProgressBar extends Component { } updateDataAttr() { - let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); + const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); + this.el_.setAttribute('data-current-time', formatTime(time, this.player_.duration())); } diff --git a/src/js/control-bar/progress-control/progress-control.js b/src/js/control-bar/progress-control/progress-control.js index 703989e4db..e142f42e3d 100644 --- a/src/js/control-bar/progress-control/progress-control.js +++ b/src/js/control-bar/progress-control/progress-control.js @@ -2,8 +2,9 @@ * @file progress-control.js */ import Component from '../../component.js'; -import SeekBar from './seek-bar.js'; -import MouseTimeDisplay from './mouse-time-display.js'; + +import './seek-bar.js'; +import './mouse-time-display.js'; /** * The Progress Control component contains the seek bar, load progress, diff --git a/src/js/control-bar/progress-control/seek-bar.js b/src/js/control-bar/progress-control/seek-bar.js index 21fdc15adb..8fa34694a6 100644 --- a/src/js/control-bar/progress-control/seek-bar.js +++ b/src/js/control-bar/progress-control/seek-bar.js @@ -4,12 +4,12 @@ import window from 'global/window'; import Slider from '../../slider/slider.js'; import Component from '../../component.js'; -import LoadProgressBar from './load-progress-bar.js'; -import PlayProgressBar from './play-progress-bar.js'; -import TooltipProgressBar from './tooltip-progress-bar.js'; import * as Fn from '../../utils/fn.js'; import formatTime from '../../utils/format-time.js'; -import assign from 'object.assign'; + +import './load-progress-bar.js'; +import './play-progress-bar.js'; +import './tooltip-progress-bar.js'; /** * Seek Bar and holder for the progress bars @@ -21,7 +21,7 @@ import assign from 'object.assign'; */ class SeekBar extends Slider { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'timeupdate', this.updateProgress); this.on(player, 'ended', this.updateProgress); @@ -65,9 +65,10 @@ class SeekBar extends Slider { this.updateAriaAttributes(this.tooltipProgressBar.el_); this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width; - let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width); - let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width); - let tooltipStyle = this.tooltipProgressBar.el().style; + const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width); + const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width); + const tooltipStyle = this.tooltipProgressBar.el().style; + tooltipStyle.maxWidth = Math.floor(playerWidth - (tooltipWidth / 2)) + 'px'; tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px'; tooltipStyle.right = `-${tooltipWidth / 2}px`; @@ -76,9 +77,12 @@ class SeekBar extends Slider { updateAriaAttributes(el) { // Allows for smooth scrubbing, when player can't keep up. - let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); - el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete) - el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete) + const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); + + // machine readable value of progress bar (percentage complete) + el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); + // human readable value of progress bar (time complete) + el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); } /** @@ -88,7 +92,8 @@ class SeekBar extends Slider { * @method getPercent */ getPercent() { - let percent = this.player_.currentTime() / this.player_.duration(); + const percent = this.player_.currentTime() / this.player_.duration(); + return percent >= 1 ? 1 : percent; } @@ -115,7 +120,9 @@ class SeekBar extends Slider { let newTime = this.calculateDistance(event) * this.player_.duration(); // Don't let video end while scrubbing. - if (newTime === this.player_.duration()) { newTime = newTime - 0.1; } + if (newTime === this.player_.duration()) { + newTime = newTime - 0.1; + } // Set new time (tell player to seek to new time) this.player_.currentTime(newTime); @@ -141,7 +148,8 @@ class SeekBar extends Slider { * @method stepForward */ stepForward() { - this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users + // more quickly fast forward for keyboard-only users + this.player_.currentTime(this.player_.currentTime() + 5); } /** @@ -150,7 +158,8 @@ class SeekBar extends Slider { * @method stepBack */ stepBack() { - this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users + // more quickly rewind for keyboard-only users + this.player_.currentTime(this.player_.currentTime() - 5); } } @@ -161,7 +170,7 @@ SeekBar.prototype.options_ = { 'mouseTimeDisplay', 'playProgressBar' ], - 'barName': 'playProgressBar' + barName: 'playProgressBar' }; SeekBar.prototype.playerEvent = 'timeupdate'; diff --git a/src/js/control-bar/progress-control/tooltip-progress-bar.js b/src/js/control-bar/progress-control/tooltip-progress-bar.js index f1e092dad3..4a6db351ba 100644 --- a/src/js/control-bar/progress-control/tooltip-progress-bar.js +++ b/src/js/control-bar/progress-control/tooltip-progress-bar.js @@ -3,7 +3,6 @@ */ import Component from '../../component.js'; import * as Fn from '../../utils/fn.js'; -import * as Dom from '../../utils/dom.js'; import formatTime from '../../utils/format-time.js'; /** @@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js'; */ class TooltipProgressBar extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.updateDataAttr(); this.on(player, 'timeupdate', this.updateDataAttr); @@ -30,7 +29,7 @@ class TooltipProgressBar extends Component { * @method createEl */ createEl() { - let el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-tooltip-progress-bar vjs-slider-bar', innerHTML: `
${this.localize('Progress')}: 0%` @@ -42,8 +41,9 @@ class TooltipProgressBar extends Component { } updateDataAttr() { - let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); - let formattedTime = formatTime(time, this.player_.duration()); + const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); + const formattedTime = formatTime(time, this.player_.duration()); + this.el_.setAttribute('data-current-time', formattedTime); this.tooltip.innerHTML = formattedTime; } diff --git a/src/js/control-bar/spacer-controls/custom-control-spacer.js b/src/js/control-bar/spacer-controls/custom-control-spacer.js index 7327442b4a..a5c5a21f34 100644 --- a/src/js/control-bar/spacer-controls/custom-control-spacer.js +++ b/src/js/control-bar/spacer-controls/custom-control-spacer.js @@ -29,8 +29,8 @@ class CustomControlSpacer extends Spacer { * @method createEl */ createEl() { - let el = super.createEl({ - className: this.buildCSSClass(), + const el = super.createEl({ + className: this.buildCSSClass() }); // No-flex/table-cell mode requires there be some content diff --git a/src/js/control-bar/text-track-controls/caption-settings-menu-item.js b/src/js/control-bar/text-track-controls/caption-settings-menu-item.js index 712543f20b..7632802cb8 100644 --- a/src/js/control-bar/text-track-controls/caption-settings-menu-item.js +++ b/src/js/control-bar/text-track-controls/caption-settings-menu-item.js @@ -12,24 +12,24 @@ import Component from '../../component.js'; * @extends TextTrackMenuItem * @class CaptionSettingsMenuItem */ - class CaptionSettingsMenuItem extends TextTrackMenuItem { +class CaptionSettingsMenuItem extends TextTrackMenuItem { constructor(player, options) { - options['track'] = { - 'kind': options['kind'], - 'player': player, - 'label': options['kind'] + ' settings', - 'selectable': false, - 'default': false, + options.track = { + player, + kind: options.kind, + label: options.kind + ' settings', + selectable: false, + default: false, mode: 'disabled' }; // CaptionSettingsMenuItem has no concept of 'selected' - options['selectable'] = false; + options.selectable = false; super(player, options); this.addClass('vjs-texttrack-settings'); - this.controlText(', opens ' + options['kind'] + ' settings dialog'); + this.controlText(', opens ' + options.kind + ' settings dialog'); } /** diff --git a/src/js/control-bar/text-track-controls/captions-button.js b/src/js/control-bar/text-track-controls/captions-button.js index b5ee37cfc9..929c841022 100644 --- a/src/js/control-bar/text-track-controls/captions-button.js +++ b/src/js/control-bar/text-track-controls/captions-button.js @@ -16,9 +16,9 @@ import CaptionSettingsMenuItem from './caption-settings-menu-item.js'; */ class CaptionsButton extends TextTrackButton { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); - this.el_.setAttribute('aria-label','Captions Menu'); + this.el_.setAttribute('aria-label', 'Captions Menu'); } /** @@ -38,10 +38,11 @@ class CaptionsButton extends TextTrackButton { */ update() { let threshold = 2; + super.update(); // if native, then threshold is 1 because no settings button - if (this.player().tech_ && this.player().tech_['featuresNativeTextTracks']) { + if (this.player().tech_ && this.player().tech_.featuresNativeTextTracks) { threshold = 1; } @@ -59,10 +60,10 @@ class CaptionsButton extends TextTrackButton { * @method createItems */ createItems() { - let items = []; + const items = []; - if (!(this.player().tech_ && this.player().tech_['featuresNativeTextTracks'])) { - items.push(new CaptionSettingsMenuItem(this.player_, { 'kind': this.kind_ })); + if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) { + items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.kind_})); } return super.createItems(items); diff --git a/src/js/control-bar/text-track-controls/chapters-button.js b/src/js/control-bar/text-track-controls/chapters-button.js index 0cc8606ea8..004be740d2 100644 --- a/src/js/control-bar/text-track-controls/chapters-button.js +++ b/src/js/control-bar/text-track-controls/chapters-button.js @@ -7,9 +7,7 @@ import TextTrackMenuItem from './text-track-menu-item.js'; import ChaptersTrackMenuItem from './chapters-track-menu-item.js'; import Menu from '../../menu/menu.js'; import * as Dom from '../../utils/dom.js'; -import * as Fn from '../../utils/fn.js'; import toTitleCase from '../../utils/to-title-case.js'; -import window from 'global/window'; /** * The button component for toggling and selecting chapters @@ -24,9 +22,9 @@ import window from 'global/window'; */ class ChaptersButton extends TextTrackButton { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); - this.el_.setAttribute('aria-label','Chapters Menu'); + this.el_.setAttribute('aria-label', 'Chapters Menu'); } /** @@ -46,20 +44,18 @@ class ChaptersButton extends TextTrackButton { * @method createItems */ createItems() { - let items = []; - - let tracks = this.player_.textTracks(); + const items = []; + const tracks = this.player_.textTracks(); if (!tracks) { return items; } for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; - if (track['kind'] === this.kind_) { - items.push(new TextTrackMenuItem(this.player_, { - 'track': track - })); + const track = tracks[i]; + + if (track.kind === this.kind_) { + items.push(new TextTrackMenuItem(this.player_, {track})); } } @@ -73,16 +69,16 @@ class ChaptersButton extends TextTrackButton { * @method createMenu */ createMenu() { - let tracks = this.player_.textTracks() || []; + const tracks = this.player_.textTracks() || []; let chaptersTrack; let items = this.items || []; for (let i = tracks.length - 1; i >= 0; i--) { // We will always choose the last track as our chaptersTrack - let track = tracks[i]; + const track = tracks[i]; - if (track['kind'] === this.kind_) { + if (track.kind === this.kind_) { chaptersTrack = track; break; @@ -90,27 +86,30 @@ class ChaptersButton extends TextTrackButton { } let menu = this.menu; + if (menu === undefined) { menu = new Menu(this.player_); - let title = Dom.createEl('li', { + + const title = Dom.createEl('li', { className: 'vjs-menu-title', innerHTML: toTitleCase(this.kind_), tabIndex: -1 }); + menu.children_.unshift(title); Dom.insertElFirst(title, menu.contentEl()); } else { - // We will empty out the menu children each time because we want a - // fresh new menu child list each time - items.forEach(item => menu.removeChild(item)); - // Empty out the ChaptersButton menu items because we no longer need them - items = []; + // We will empty out the menu children each time because we want a + // fresh new menu child list each time + items.forEach(item => menu.removeChild(item)); + // Empty out the ChaptersButton menu items because we no longer need them + items = []; } - if (chaptersTrack && chaptersTrack.cues == null) { - chaptersTrack['mode'] = 'hidden'; + if (chaptersTrack && (chaptersTrack.cues === null || chaptersTrack.cues === undefined)) { + chaptersTrack.mode = 'hidden'; - let remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack); + const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack); if (remoteTextTrackEl) { remoteTextTrackEl.addEventListener('load', (event) => this.update()); @@ -118,14 +117,14 @@ class ChaptersButton extends TextTrackButton { } if (chaptersTrack && chaptersTrack.cues && chaptersTrack.cues.length > 0) { - let cues = chaptersTrack['cues'], cue; + const cues = chaptersTrack.cues; for (let i = 0, l = cues.length; i < l; i++) { - cue = cues[i]; + const cue = cues[i]; - let mi = new ChaptersTrackMenuItem(this.player_, { - 'track': chaptersTrack, - 'cue': cue + const mi = new ChaptersTrackMenuItem(this.player_, { + cue, + track: chaptersTrack }); items.push(mi); diff --git a/src/js/control-bar/text-track-controls/chapters-track-menu-item.js b/src/js/control-bar/text-track-controls/chapters-track-menu-item.js index 40b717a0e4..2291da4c30 100644 --- a/src/js/control-bar/text-track-controls/chapters-track-menu-item.js +++ b/src/js/control-bar/text-track-controls/chapters-track-menu-item.js @@ -15,14 +15,14 @@ import * as Fn from '../../utils/fn.js'; */ class ChaptersTrackMenuItem extends MenuItem { - constructor(player, options){ - let track = options['track']; - let cue = options['cue']; - let currentTime = player.currentTime(); + constructor(player, options) { + const track = options.track; + const cue = options.cue; + const currentTime = player.currentTime(); // Modify options for parent MenuItem class's init. - options['label'] = cue.text; - options['selected'] = (cue['startTime'] <= currentTime && currentTime < cue['endTime']); + options.label = cue.text; + options.selected = (cue.startTime <= currentTime && currentTime < cue.endTime); super(player, options); this.track = track; @@ -47,11 +47,11 @@ class ChaptersTrackMenuItem extends MenuItem { * @method update */ update() { - let cue = this.cue; - let currentTime = this.player_.currentTime(); + const cue = this.cue; + const currentTime = this.player_.currentTime(); // vjs.log(currentTime, cue.startTime); - this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']); + this.selected(cue.startTime <= currentTime && currentTime < cue.endTime); } } diff --git a/src/js/control-bar/text-track-controls/descriptions-button.js b/src/js/control-bar/text-track-controls/descriptions-button.js index 671c620c56..0b7bd02b45 100644 --- a/src/js/control-bar/text-track-controls/descriptions-button.js +++ b/src/js/control-bar/text-track-controls/descriptions-button.js @@ -16,14 +16,14 @@ import * as Fn from '../../utils/fn.js'; */ class DescriptionsButton extends TextTrackButton { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); this.el_.setAttribute('aria-label', 'Descriptions Menu'); - let tracks = player.textTracks(); + const tracks = player.textTracks(); if (tracks) { - let changeHandler = Fn.bind(this, this.handleTracksChange); + const changeHandler = Fn.bind(this, this.handleTracksChange); tracks.addEventListener('change', changeHandler); this.on('dispose', function() { @@ -37,14 +37,15 @@ class DescriptionsButton extends TextTrackButton { * * @method handleTracksChange */ - handleTracksChange(event){ - let tracks = this.player().textTracks(); + handleTracksChange(event) { + const tracks = this.player().textTracks(); let disabled = false; // Check whether a track of a different kind is showing for (let i = 0, l = tracks.length; i < l; i++) { - let track = tracks[i]; - if (track['kind'] !== this.kind_ && track['mode'] === 'showing') { + const track = tracks[i]; + + if (track.kind !== this.kind_ && track.mode === 'showing') { disabled = true; break; } diff --git a/src/js/control-bar/text-track-controls/off-text-track-menu-item.js b/src/js/control-bar/text-track-controls/off-text-track-menu-item.js index 9cc124b9df..a23b82bd4d 100644 --- a/src/js/control-bar/text-track-controls/off-text-track-menu-item.js +++ b/src/js/control-bar/text-track-controls/off-text-track-menu-item.js @@ -14,19 +14,19 @@ import Component from '../../component.js'; */ class OffTextTrackMenuItem extends TextTrackMenuItem { - constructor(player, options){ + constructor(player, options) { // Create pseudo track info // Requires options['kind'] - options['track'] = { - 'kind': options['kind'], - 'player': player, - 'label': options['kind'] + ' off', - 'default': false, - 'mode': 'disabled' + options.track = { + player, + kind: options.kind, + label: options.kind + ' off', + default: false, + mode: 'disabled' }; // MenuItem is selectable - options['selectable'] = true; + options.selectable = true; super(player, options); this.selected(true); @@ -38,13 +38,14 @@ class OffTextTrackMenuItem extends TextTrackMenuItem { * @param {Object} event Event object * @method handleTracksChange */ - handleTracksChange(event){ - let tracks = this.player().textTracks(); + handleTracksChange(event) { + const tracks = this.player().textTracks(); let selected = true; for (let i = 0, l = tracks.length; i < l; i++) { - let track = tracks[i]; - if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') { + const track = tracks[i]; + + if (track.kind === this.track.kind && track.mode === 'showing') { selected = false; break; } diff --git a/src/js/control-bar/text-track-controls/subtitles-button.js b/src/js/control-bar/text-track-controls/subtitles-button.js index c56731496e..207e8179d2 100644 --- a/src/js/control-bar/text-track-controls/subtitles-button.js +++ b/src/js/control-bar/text-track-controls/subtitles-button.js @@ -15,9 +15,9 @@ import Component from '../../component.js'; */ class SubtitlesButton extends TextTrackButton { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); - this.el_.setAttribute('aria-label','Subtitles Menu'); + this.el_.setAttribute('aria-label', 'Subtitles Menu'); } /** diff --git a/src/js/control-bar/text-track-controls/text-track-button.js b/src/js/control-bar/text-track-controls/text-track-button.js index 1decbeec24..fced8fb3ce 100644 --- a/src/js/control-bar/text-track-controls/text-track-button.js +++ b/src/js/control-bar/text-track-controls/text-track-button.js @@ -3,7 +3,6 @@ */ import TrackButton from '../track-button.js'; import Component from '../../component.js'; -import * as Fn from '../../utils/fn.js'; import TextTrackMenuItem from './text-track-menu-item.js'; import OffTextTrackMenuItem from './off-text-track-menu-item.js'; @@ -17,7 +16,7 @@ import OffTextTrackMenuItem from './off-text-track-menu-item.js'; */ class TextTrackButton extends TrackButton { - constructor(player, options = {}){ + constructor(player, options = {}) { options.tracks = player.textTracks(); super(player, options); @@ -29,25 +28,25 @@ class TextTrackButton extends TrackButton { * @return {Array} Array of menu items * @method createItems */ - createItems(items=[]) { + createItems(items = []) { // Add an OFF menu item to turn all tracks off - items.push(new OffTextTrackMenuItem(this.player_, { 'kind': this.kind_ })); + items.push(new OffTextTrackMenuItem(this.player_, {kind: this.kind_})); - let tracks = this.player_.textTracks(); + const tracks = this.player_.textTracks(); if (!tracks) { return items; } for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; + const track = tracks[i]; // only add tracks that are of the appropriate kind and have a label - if (track['kind'] === this.kind_) { + if (track.kind === this.kind_) { items.push(new TextTrackMenuItem(this.player_, { + track, // MenuItem is selectable - 'selectable': true, - 'track': track + selectable: true })); } } diff --git a/src/js/control-bar/text-track-controls/text-track-menu-item.js b/src/js/control-bar/text-track-controls/text-track-menu-item.js index 8b4c73bdad..ccb5b02010 100644 --- a/src/js/control-bar/text-track-controls/text-track-menu-item.js +++ b/src/js/control-bar/text-track-controls/text-track-menu-item.js @@ -17,20 +17,20 @@ import document from 'global/document'; */ class TextTrackMenuItem extends MenuItem { - constructor(player, options){ - let track = options['track']; - let tracks = player.textTracks(); + constructor(player, options) { + const track = options.track; + const tracks = player.textTracks(); // Modify options for parent MenuItem class's init. - options['label'] = track['label'] || track['language'] || 'Unknown'; - options['selected'] = track['default'] || track['mode'] === 'showing'; + options.label = track.label || track.language || 'Unknown'; + options.selected = track.default || track.mode === 'showing'; super(player, options); this.track = track; if (tracks) { - let changeHandler = Fn.bind(this, this.handleTracksChange); + const changeHandler = Fn.bind(this, this.handleTracksChange); tracks.addEventListener('change', changeHandler); this.on('dispose', function() { @@ -52,7 +52,9 @@ class TextTrackMenuItem extends MenuItem { // Android 2.3 throws an Illegal Constructor error for window.Event try { event = new window.Event('change'); - } catch(err){} + } catch (err) { + // continue regardless of error + } } if (!event) { @@ -71,24 +73,26 @@ class TextTrackMenuItem extends MenuItem { * @method handleClick */ handleClick(event) { - let kind = this.track['kind']; - let tracks = this.player_.textTracks(); + const kind = this.track.kind; + const tracks = this.player_.textTracks(); super.handleClick(event); - if (!tracks) return; + if (!tracks) { + return; + } for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; + const track = tracks[i]; - if (track['kind'] !== kind) { + if (track.kind !== kind) { continue; } if (track === this.track) { - track['mode'] = 'showing'; + track.mode = 'showing'; } else { - track['mode'] = 'disabled'; + track.mode = 'disabled'; } } } @@ -98,8 +102,8 @@ class TextTrackMenuItem extends MenuItem { * * @method handleTracksChange */ - handleTracksChange(event){ - this.selected(this.track['mode'] === 'showing'); + handleTracksChange(event) { + this.selected(this.track.mode === 'showing'); } } diff --git a/src/js/control-bar/time-controls/current-time-display.js b/src/js/control-bar/time-controls/current-time-display.js index ff99fc54e6..66a102aeea 100644 --- a/src/js/control-bar/time-controls/current-time-display.js +++ b/src/js/control-bar/time-controls/current-time-display.js @@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js'; */ class CurrentTimeDisplay extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'timeupdate', this.updateContent); @@ -28,14 +28,14 @@ class CurrentTimeDisplay extends Component { * @method createEl */ createEl() { - let el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-current-time vjs-time-control vjs-control' }); this.contentEl_ = Dom.createEl('div', { className: 'vjs-current-time-display', // label the current time for screen reader users - innerHTML: 'Current Time ' + '0:00', + innerHTML: 'Current Time ' + '0:00' }, { // tell screen readers not to automatically read the time as it changes 'aria-live': 'off' @@ -52,9 +52,10 @@ class CurrentTimeDisplay extends Component { */ updateContent() { // Allows for smooth scrubbing, when player can't keep up. - let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); - let localizedText = this.localize('Current Time'); - let formattedTime = formatTime(time, this.player_.duration()); + const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); + const localizedText = this.localize('Current Time'); + const formattedTime = formatTime(time, this.player_.duration()); + if (formattedTime !== this.formattedTime_) { this.formattedTime_ = formattedTime; this.contentEl_.innerHTML = `${localizedText} ${formattedTime}`; diff --git a/src/js/control-bar/time-controls/duration-display.js b/src/js/control-bar/time-controls/duration-display.js index 0496e3cd50..1b2cb27f50 100644 --- a/src/js/control-bar/time-controls/duration-display.js +++ b/src/js/control-bar/time-controls/duration-display.js @@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js'; */ class DurationDisplay extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'durationchange', this.updateContent); @@ -28,7 +28,7 @@ class DurationDisplay extends Component { * @method createEl */ createEl() { - let el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-duration vjs-time-control vjs-control' }); @@ -51,12 +51,15 @@ class DurationDisplay extends Component { * @method updateContent */ updateContent() { - let duration = this.player_.duration(); + const duration = this.player_.duration(); + if (duration && this.duration_ !== duration) { this.duration_ = duration; - let localizedText = this.localize('Duration Time'); - let formattedTime = formatTime(duration); - this.contentEl_.innerHTML = `${localizedText} ${formattedTime}`; // label the duration time for screen reader users + const localizedText = this.localize('Duration Time'); + const formattedTime = formatTime(duration); + + // label the duration time for screen reader users + this.contentEl_.innerHTML = `${localizedText} ${formattedTime}`; } } diff --git a/src/js/control-bar/time-controls/remaining-time-display.js b/src/js/control-bar/time-controls/remaining-time-display.js index 8ac701f6b1..40914b7cd1 100644 --- a/src/js/control-bar/time-controls/remaining-time-display.js +++ b/src/js/control-bar/time-controls/remaining-time-display.js @@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js'; */ class RemainingTimeDisplay extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'timeupdate', this.updateContent); @@ -29,14 +29,14 @@ class RemainingTimeDisplay extends Component { * @method createEl */ createEl() { - let el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-remaining-time vjs-time-control vjs-control' }); this.contentEl_ = Dom.createEl('div', { className: 'vjs-remaining-time-display', // label the remaining time for screen reader users - innerHTML: `${this.localize('Remaining Time')} -0:00`, + innerHTML: `${this.localize('Remaining Time')} -0:00` }, { // tell screen readers not to automatically read the time as it changes 'aria-live': 'off' @@ -55,6 +55,7 @@ class RemainingTimeDisplay extends Component { if (this.player_.duration()) { const localizedText = this.localize('Remaining Time'); const formattedTime = formatTime(this.player_.remainingTime()); + if (formattedTime !== this.formattedTime_) { this.formattedTime_ = formattedTime; this.contentEl_.innerHTML = `${localizedText} -${formattedTime}`; diff --git a/src/js/control-bar/track-button.js b/src/js/control-bar/track-button.js index f482465fa1..4b3056b1a8 100644 --- a/src/js/control-bar/track-button.js +++ b/src/js/control-bar/track-button.js @@ -15,8 +15,8 @@ import * as Fn from '../utils/fn.js'; */ class TrackButton extends MenuButton { - constructor(player, options){ - let tracks = options.tracks; + constructor(player, options) { + const tracks = options.tracks; super(player, options); @@ -28,7 +28,8 @@ class TrackButton extends MenuButton { return; } - let updateHandler = Fn.bind(this, this.update); + const updateHandler = Fn.bind(this, this.update); + tracks.addEventListener('removetrack', updateHandler); tracks.addEventListener('addtrack', updateHandler); diff --git a/src/js/control-bar/volume-control/volume-bar.js b/src/js/control-bar/volume-control/volume-bar.js index bf775cd6cc..db44eb9e59 100644 --- a/src/js/control-bar/volume-control/volume-bar.js +++ b/src/js/control-bar/volume-control/volume-bar.js @@ -6,7 +6,7 @@ import Component from '../../component.js'; import * as Fn from '../../utils/fn.js'; // Required children -import VolumeLevel from './volume-level.js'; +import './volume-level.js'; /** * The bar that contains the volume level and can be clicked on to adjust the level @@ -18,7 +18,7 @@ import VolumeLevel from './volume-level.js'; */ class VolumeBar extends Slider { - constructor(player, options){ + constructor(player, options) { super(player, options); this.on(player, 'volumechange', this.updateARIAAttributes); player.ready(Fn.bind(this, this.updateARIAAttributes)); @@ -63,9 +63,8 @@ class VolumeBar extends Slider { getPercent() { if (this.player_.muted()) { return 0; - } else { - return this.player_.volume(); } + return this.player_.volume(); } /** @@ -95,7 +94,8 @@ class VolumeBar extends Slider { */ updateARIAAttributes() { // Current value of volume bar as a percentage - let volume = (this.player_.volume() * 100).toFixed(2); + const volume = (this.player_.volume() * 100).toFixed(2); + this.el_.setAttribute('aria-valuenow', volume); this.el_.setAttribute('aria-valuetext', volume + '%'); } @@ -106,7 +106,7 @@ VolumeBar.prototype.options_ = { children: [ 'volumeLevel' ], - 'barName': 'volumeLevel' + barName: 'volumeLevel' }; VolumeBar.prototype.playerEvent = 'volumechange'; diff --git a/src/js/control-bar/volume-control/volume-control.js b/src/js/control-bar/volume-control/volume-control.js index 2095b3dfac..741b403c17 100644 --- a/src/js/control-bar/volume-control/volume-control.js +++ b/src/js/control-bar/volume-control/volume-control.js @@ -4,7 +4,7 @@ import Component from '../../component.js'; // Required children -import VolumeBar from './volume-bar.js'; +import './volume-bar.js'; /** * The component for controlling the volume level @@ -16,15 +16,15 @@ import VolumeBar from './volume-bar.js'; */ class VolumeControl extends Component { - constructor(player, options){ + constructor(player, options) { super(player, options); // hide volume controls when they're not supported by the current tech - if (player.tech_ && player.tech_['featuresVolumeControl'] === false) { + if (player.tech_ && player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } - this.on(player, 'loadstart', function(){ - if (player.tech_['featuresVolumeControl'] === false) { + this.on(player, 'loadstart', function() { + if (player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } else { this.removeClass('vjs-hidden'); diff --git a/src/js/control-bar/volume-menu-button.js b/src/js/control-bar/volume-menu-button.js index 83879ab5f9..d650a245e4 100644 --- a/src/js/control-bar/volume-menu-button.js +++ b/src/js/control-bar/volume-menu-button.js @@ -18,7 +18,7 @@ import VolumeBar from './volume-control/volume-bar.js'; */ class VolumeMenuButton extends PopupButton { - constructor(player, options={}){ + constructor(player, options = {}) { // Default to inline if (options.inline === undefined) { options.inline = true; @@ -48,7 +48,7 @@ class VolumeMenuButton extends PopupButton { // hide mute toggle if the current tech doesn't support volume control function updateVisibility() { - if (player.tech_ && player.tech_['featuresVolumeControl'] === false) { + if (player.tech_ && player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } else { this.removeClass('vjs-hidden'); @@ -58,19 +58,19 @@ class VolumeMenuButton extends PopupButton { updateVisibility.call(this); this.on(player, 'loadstart', updateVisibility); - this.on(this.volumeBar, ['slideractive', 'focus'], function(){ + this.on(this.volumeBar, ['slideractive', 'focus'], function() { this.addClass('vjs-slider-active'); }); - this.on(this.volumeBar, ['sliderinactive', 'blur'], function(){ + this.on(this.volumeBar, ['sliderinactive', 'blur'], function() { this.removeClass('vjs-slider-active'); }); - this.on(this.volumeBar, ['focus'], function(){ + this.on(this.volumeBar, ['focus'], function() { this.addClass('vjs-lock-showing'); }); - this.on(this.volumeBar, ['blur'], function(){ + this.on(this.volumeBar, ['blur'], function() { this.removeClass('vjs-lock-showing'); }); } @@ -83,7 +83,8 @@ class VolumeMenuButton extends PopupButton { */ buildCSSClass() { let orientationClass = ''; - if (!!this.options_.vertical) { + + if (this.options_.vertical) { orientationClass = 'vjs-volume-menu-button-vertical'; } else { orientationClass = 'vjs-volume-menu-button-horizontal'; @@ -99,11 +100,11 @@ class VolumeMenuButton extends PopupButton { * @method createPopup */ createPopup() { - let popup = new Popup(this.player_, { + const popup = new Popup(this.player_, { contentElType: 'div' }); - let vb = new VolumeBar(this.player_, this.options_.volumeBar); + const vb = new VolumeBar(this.player_, this.options_.volumeBar); popup.addChild(vb); diff --git a/src/js/error-display.js b/src/js/error-display.js index 36cde55267..0959040288 100644 --- a/src/js/error-display.js +++ b/src/js/error-display.js @@ -3,8 +3,6 @@ */ import Component from './component'; import ModalDialog from './modal-dialog'; - -import * as Dom from './utils/dom'; import mergeOptions from './utils/merge-options'; /** @@ -45,7 +43,8 @@ class ErrorDisplay extends ModalDialog { * @return {String|Null} */ content() { - let error = this.player().error(); + const error = this.player().error(); + return error ? this.localize(error.message) : ''; } } diff --git a/src/js/event-target.js b/src/js/event-target.js index 281d2b99c2..2c738df3cd 100644 --- a/src/js/event-target.js +++ b/src/js/event-target.js @@ -3,41 +3,43 @@ */ import * as Events from './utils/events.js'; -var EventTarget = function() {}; +const EventTarget = function() {}; EventTarget.prototype.allowedEvents_ = {}; EventTarget.prototype.on = function(type, fn) { // Remove the addEventListener alias before calling Events.on // so we don't get into an infinite type loop - let ael = this.addEventListener; + const ael = this.addEventListener; + this.addEventListener = () => {}; Events.on(this, type, fn); this.addEventListener = ael; }; + EventTarget.prototype.addEventListener = EventTarget.prototype.on; EventTarget.prototype.off = function(type, fn) { Events.off(this, type, fn); }; + EventTarget.prototype.removeEventListener = EventTarget.prototype.off; EventTarget.prototype.one = function(type, fn) { // Remove the addEventListener alias before calling Events.on // so we don't get into an infinite type loop - let ael = this.addEventListener; + const ael = this.addEventListener; + this.addEventListener = () => {}; Events.one(this, type, fn); this.addEventListener = ael; }; EventTarget.prototype.trigger = function(event) { - let type = event.type || event; + const type = event.type || event; if (typeof event === 'string') { - event = { - type: type - }; + event = {type}; } event = Events.fixEvent(event); @@ -47,6 +49,7 @@ EventTarget.prototype.trigger = function(event) { Events.trigger(this, event); }; + // The standard DOM EventTarget.dispatchEvent() is aliased to trigger() EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger; diff --git a/src/js/extend.js b/src/js/extend.js index 963ca2eda2..aa2df7baa6 100644 --- a/src/js/extend.js +++ b/src/js/extend.js @@ -7,7 +7,7 @@ import log from './utils/log'; * Both work the same but node adds `super_` to the subClass * and Bable adds the superClass as __proto__. Both seem useful. */ -const _inherits = function (subClass, superClass) { +const _inherits = function(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } @@ -44,10 +44,11 @@ const _inherits = function (subClass, superClass) { * }); * ``` */ -const extendFn = function(superClass, subClassMethods={}) { +const extendFn = function(superClass, subClassMethods = {}) { let subClass = function() { superClass.apply(this, arguments); }; + let methods = {}; if (typeof subClassMethods === 'object') { @@ -66,7 +67,7 @@ const extendFn = function(superClass, subClassMethods={}) { _inherits(subClass, superClass); // Extend subObj's prototype with functions and other properties from props - for (var name in methods) { + for (const name in methods) { if (methods.hasOwnProperty(name)) { subClass.prototype[name] = methods[name]; } diff --git a/src/js/fullscreen-api.js b/src/js/fullscreen-api.js index b594541ab1..cd15def3db 100644 --- a/src/js/fullscreen-api.js +++ b/src/js/fullscreen-api.js @@ -8,7 +8,7 @@ import document from 'global/document'; * @type {Object|undefined} * @private */ -let FullscreenApi = {}; +const FullscreenApi = {}; // browser API methods // map approach from Screenful.js - https://github.com/sindresorhus/screenfull.js @@ -60,7 +60,7 @@ const apiMap = [ ] ]; -let specApi = apiMap[0]; +const specApi = apiMap[0]; let browserApi; // determine the supported set of functions @@ -74,7 +74,7 @@ for (let i = 0; i < apiMap.length; i++) { // map the browser API names to the spec API names if (browserApi) { - for (let i=0; i= 0) { @@ -497,8 +515,8 @@ class Player extends Component { } // Get the ratio as a decimal we can use to calculate dimensions - let ratioParts = aspectRatio.split(':'); - let ratioMultiplier = ratioParts[1] / ratioParts[0]; + const ratioParts = aspectRatio.split(':'); + const ratioMultiplier = ratioParts[1] / ratioParts[0]; if (this.width_ !== undefined) { // Use any width that's been specifically set @@ -516,14 +534,14 @@ class Player extends Component { height = this.height_; } else { // Otherwise calculate the height from the ratio and the width - height = width * ratioMultiplier; + height = width * ratioMultiplier; } // Ensure the CSS class is valid by starting with an alpha character - if (/^[^a-zA-Z]/.test(this.id())) { - idClass = 'dimensions-'+this.id(); + if ((/^[^a-zA-Z]/).test(this.id())) { + idClass = 'dimensions-' + this.id(); } else { - idClass = this.id()+'-dimensions'; + idClass = this.id() + '-dimensions'; } // Ensure the right class is still on the player for the style element @@ -571,9 +589,9 @@ class Player extends Component { this.isReady_ = false; // Grab tech-specific options from player options and add source and parent element to use. - var techOptions = assign({ + const techOptions = assign({ + source, 'nativeControlsForTouch': this.options_.nativeControlsForTouch, - 'source': source, 'playerId': this.id(), 'techId': `${this.id()}_${techName}_api`, 'videoTracks': this.videoTracks_, @@ -602,13 +620,14 @@ class Player extends Component { } // Initialize tech instance - let techComponent = Tech.getTech(techName); + let TechComponent = Tech.getTech(techName); + // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. - if (!techComponent) { - techComponent = Component.getComponent(techName); + if (!TechComponent) { + TechComponent = Component.getComponent(techName); } - this.tech_ = new techComponent(techOptions); + this.tech_ = new TechComponent(techOptions); // player.triggerReady is always async, so don't need this to be async this.tech_.ready(Fn.bind(this, this.handleTechReady_), true); @@ -698,12 +717,13 @@ class Player extends Component { if (safety && safety.IWillNotUseThisInPlugins) { return this.tech_; } - let errorText = ` + const errorText = ` Please make sure that you are not using this inside of a plugin. To disable this alert and error, please pass in an object with \`IWillNotUseThisInPlugins\` to the \`tech\` method. See https://github.com/videojs/video.js/issues/2617 for more info. `; + window.alert(errorText); throw new Error(errorText); } @@ -794,9 +814,9 @@ class Player extends Component { // This fixes both issues. Need to wait for API, so it updates displays correctly if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) { try { - delete this.tag.poster; // Chrome Fix. Fixed in Chrome v16. - } - catch (e) { + // Chrome Fix. Fixed in Chrome v16. + delete this.tag.poster; + } catch (e) { log('deleting tag.poster throws in some browsers', e); } this.play(); @@ -954,9 +974,9 @@ class Player extends Component { * @method handleTechFirstPlay_ */ handleTechFirstPlay_() { - //If the first starttime attribute is specified - //then we will start at the given offset in seconds - if(this.options_.starttime){ + // If the first starttime attribute is specified + // then we will start at the given offset in seconds + if (this.options_.starttime) { this.currentTime(this.options_.starttime); } @@ -1024,7 +1044,9 @@ class Player extends Component { handleTechClick_(event) { // We're using mousedown to detect clicks thanks to Flash, but mousedown // will also be triggered with right-clicks, so we need to prevent that - if (event.button !== 0) return; + if (event.button !== 0) { + return; + } // When controls are disabled a click should not toggle playback because // the click is considered a control @@ -1065,7 +1087,7 @@ class Player extends Component { * @method handleTechTouchMove_ */ handleTechTouchMove_() { - if (this.userWasActive){ + if (this.userWasActive) { this.reportUserActivity(); } } @@ -1126,7 +1148,8 @@ class Player extends Component { * @method handleTechError_ */ handleTechError_() { - let error = this.tech_.error(); + const error = this.tech_.error(); + this.error(error); } @@ -1181,7 +1204,8 @@ class Player extends Component { } handleTechTextData_() { - var data = null; + let data = null; + if (arguments.length > 1) { data = arguments[1]; } @@ -1259,15 +1283,17 @@ class Player extends Component { techCall_(method, arg) { // If it's not ready yet, call method when it is if (this.tech_ && !this.tech_.isReady_) { - this.tech_.ready(function(){ + this.tech_.ready(function() { this[method](arg); }, true); // Otherwise call method now } else { try { - this.tech_ && this.tech_[method](arg); - } catch(e) { + if (this.tech_) { + this.tech_[method](arg); + } + } catch (e) { log(e); throw e; } @@ -1290,18 +1316,17 @@ class Player extends Component { // When that happens we'll catch the errors and inform tech that it's not ready any more. try { return this.tech_[method](); - } catch(e) { + } catch (e) { // When building additional tech libs, an expected method may not be defined yet if (this.tech_[method] === undefined) { log(`Video.js: ${method} method not defined for ${this.techName_} playback technology.`, e); + + // When a method isn't available on the object it throws a TypeError + } else if (e.name === 'TypeError') { + log(`Video.js: ${method} unavailable on ${this.techName_} playback technology element.`, e); + this.tech_.isReady_ = false; } else { - // When a method isn't available on the object it throws a TypeError - if (e.name === 'TypeError') { - log(`Video.js: ${method} unavailable on ${this.techName_} playback technology element.`, e); - this.tech_.isReady_ = false; - } else { - log(e); - } + log(e); } throw e; } @@ -1414,7 +1439,8 @@ class Player extends Component { // currentTime when scrubbing, but may not provide much performance benefit afterall. // Should be tested. Also something has to read the actual current time or the cache will // never get updated. - return this.cache_.currentTime = (this.techGet_('currentTime') || 0); + this.cache_.currentTime = (this.techGet_('currentTime') || 0); + return this.cache_.currentTime; } /** @@ -1495,10 +1521,10 @@ class Player extends Component { * @method buffered */ buffered() { - var buffered = this.techGet_('buffered'); + let buffered = this.techGet_('buffered'); if (!buffered || !buffered.length) { - buffered = createTimeRange(0,0); + buffered = createTimeRange(0, 0); } return buffered; @@ -1527,9 +1553,9 @@ class Player extends Component { * @method bufferedEnd */ bufferedEnd() { - var buffered = this.buffered(), - duration = this.duration(), - end = buffered.end(buffered.length-1); + const buffered = this.buffered(); + const duration = this.duration(); + let end = buffered.end(buffered.length - 1); if (end > duration) { end = duration; @@ -1557,7 +1583,8 @@ class Player extends Component { let vol; if (percentAsDecimal !== undefined) { - vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1 + // Force value to between 0 and 1 + vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); this.cache_.volume = vol; this.techCall_('setVolume', vol); @@ -1569,7 +1596,6 @@ class Player extends Component { return (isNaN(vol)) ? 1 : vol; } - /** * Get the current muted state, or turn mute on or off * ```js @@ -1589,7 +1615,7 @@ class Player extends Component { this.techCall_('setMuted', muted); return this; } - return this.techGet_('muted') || false; // Default to false + return this.techGet_('muted') || false; } // Check if current tech can support native fullscreen @@ -1645,7 +1671,7 @@ class Player extends Component { * @method requestFullscreen */ requestFullscreen() { - var fsApi = FullscreenApi; + const fsApi = FullscreenApi; this.isFullscreen(true); @@ -1658,7 +1684,7 @@ class Player extends Component { // when canceling fullscreen. Otherwise if there's multiple // players on a page, they would all be reacting to the same fullscreen // events - Events.on(document, fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e){ + Events.on(document, fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) { this.isFullscreen(document[fsApi.fullscreenElement]); // If cancelling fullscreen, remove event listener. @@ -1695,17 +1721,18 @@ class Player extends Component { * @method exitFullscreen */ exitFullscreen() { - var fsApi = FullscreenApi; + const fsApi = FullscreenApi; + this.isFullscreen(false); // Check for browser element fullscreen support if (fsApi.requestFullscreen) { document[fsApi.exitFullscreen](); } else if (this.tech_.supportsFullScreen()) { - this.techCall_('exitFullScreen'); + this.techCall_('exitFullScreen'); } else { - this.exitFullWindow(); - this.trigger('fullscreenchange'); + this.exitFullWindow(); + this.trigger('fullscreenchange'); } return this; @@ -1782,7 +1809,7 @@ class Player extends Component { // Loop through each playback technology in the options order for (let i = 0, j = this.options_.techOrder; i < j.length; i++) { - let techName = toTitleCase(j[i]); + const techName = toTitleCase(j[i]); let tech = Tech.getTech(techName); // Support old behavior of techs being registered as components. @@ -1822,7 +1849,7 @@ class Player extends Component { selectSource(sources) { // Get only the techs specified in `techOrder` that exist and are supported by the // current platform - let techs = + const techs = this.options_.techOrder .map(toTitleCase) .map((techName) => { @@ -1845,7 +1872,7 @@ class Player extends Component { // Iterate over each `innerArray` element once per `outerArray` element and execute // `tester` with both. If `tester` returns a non-falsy value, exit early and return // that value. - let findFirstPassingTechSourcePair = function (outerArray, innerArray, tester) { + const findFirstPassingTechSourcePair = function(outerArray, innerArray, tester) { let found; outerArray.some((outerChoice) => { @@ -1862,10 +1889,10 @@ class Player extends Component { }; let foundSourceAndTech; - let flip = (fn) => (a, b) => fn(b, a); - let finder = ([techName, tech], source) => { + const flip = (fn) => (a, b) => fn(b, a); + const finder = ([techName, tech], source) => { if (tech.canPlaySource(source, this.options_[techName.toLowerCase()])) { - return {source: source, tech: techName}; + return {source, tech: techName}; } }; @@ -1920,6 +1947,7 @@ class Player extends Component { } let currentTech = Tech.getTech(this.techName_); + // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. if (!currentTech) { @@ -1948,7 +1976,7 @@ class Player extends Component { this.currentType_ = source.type || ''; // wait until the tech is ready to set the source - this.ready(function(){ + this.ready(function() { // The setSource tech method was added with source handlers // so older techs won't support it @@ -1984,7 +2012,7 @@ class Player extends Component { * @method sourceList_ */ sourceList_(sources) { - var sourceTech = this.selectSource(sources); + const sourceTech = this.selectSource(sources); if (sourceTech) { if (sourceTech.tech === this.techName_) { @@ -1996,7 +2024,7 @@ class Player extends Component { } } else { // We need to wrap this in a timeout to give folks a chance to add error event handlers - this.setTimeout( function() { + this.setTimeout(function() { this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) }); }, 0); @@ -2098,7 +2126,7 @@ class Player extends Component { loop(value) { if (value !== undefined) { this.techCall_('setLoop', value); - this.options_['loop'] = value; + this.options_.loop = value; return this; } return this.techGet_('loop'); @@ -2172,7 +2200,8 @@ class Player extends Component { */ controls(bool) { if (bool !== undefined) { - bool = !!bool; // force boolean + bool = !!bool; + // Don't trigger a change event unless it actually changed if (this.controls_ !== bool) { this.controls_ = bool; @@ -2218,7 +2247,8 @@ class Player extends Component { */ usingNativeControls(bool) { if (bool !== undefined) { - bool = !!bool; // force boolean + bool = !!bool; + // Don't trigger a change event unless it actually changed if (this.usingNativeControls_ !== bool) { this.usingNativeControls_ = bool; @@ -2302,7 +2332,9 @@ class Player extends Component { * @return {Boolean} True if the player is in the ended state, false if not. * @method ended */ - ended() { return this.techGet_('ended'); } + ended() { + return this.techGet_('ended'); + } /** * Returns whether or not the player is in the "seeking" state. @@ -2310,7 +2342,9 @@ class Player extends Component { * @return {Boolean} True if the player is in the seeking state, false if not. * @method seeking */ - seeking() { return this.techGet_('seeking'); } + seeking() { + return this.techGet_('seeking'); + } /** * Returns the TimeRanges of the media that are currently available @@ -2319,7 +2353,9 @@ class Player extends Component { * @return {TimeRanges} the seekable intervals of the media timeline * @method seekable */ - seekable() { return this.techGet_('seekable'); } + seekable() { + return this.techGet_('seekable'); + } /** * Report user activity @@ -2363,8 +2399,8 @@ class Player extends Component { // // When this gets resolved in ALL browsers it can be removed // https://code.google.com/p/chromium/issues/detail?id=103041 - if(this.tech_) { - this.tech_.one('mousemove', function(e){ + if (this.tech_) { + this.tech_.one('mousemove', function(e) { e.stopPropagation(); e.preventDefault(); }); @@ -2387,21 +2423,22 @@ class Player extends Component { * @method listenForUserActivity_ */ listenForUserActivity_() { - let mouseInProgress, lastMoveX, lastMoveY; - - let handleActivity = Fn.bind(this, this.reportUserActivity); + let mouseInProgress; + let lastMoveX; + let lastMoveY; + const handleActivity = Fn.bind(this, this.reportUserActivity); - let handleMouseMove = function(e) { + const handleMouseMove = function(e) { // #1068 - Prevent mousemove spamming // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970 - if(e.screenX !== lastMoveX || e.screenY !== lastMoveY) { + if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) { lastMoveX = e.screenX; lastMoveY = e.screenY; handleActivity(); } }; - let handleMouseDown = function() { + const handleMouseDown = function() { handleActivity(); // For as long as the they are touching the device or have their mouse down, // we consider them active even if they're not moving their finger or mouse. @@ -2413,7 +2450,7 @@ class Player extends Component { mouseInProgress = this.setInterval(handleActivity, 250); }; - let handleMouseUp = function(event) { + const handleMouseUp = function(event) { handleActivity(); // Stop the interval that maintains activity if the mouse/touch is down this.clearInterval(mouseInProgress); @@ -2435,7 +2472,8 @@ class Player extends Component { // then gets picked up by this loop // http://ejohn.org/blog/learning-from-twitter/ let inactivityTimeout; - let activityCheck = this.setInterval(function() { + + this.setInterval(function() { // Check to see if mouse/touch activity has happened if (this.userActivity_) { // Reset the activity tracker @@ -2447,16 +2485,17 @@ class Player extends Component { // Clear any existing inactivity timeout to start the timer over this.clearTimeout(inactivityTimeout); - var timeout = this.options_['inactivityTimeout']; + const timeout = this.options_.inactivityTimeout; + if (timeout > 0) { // In milliseconds, if no more activity has occurred the // user will be considered inactive - inactivityTimeout = this.setTimeout(function () { + inactivityTimeout = this.setTimeout(function() { // Protect against the case where the inactivityTimeout can trigger just - // before the next user activity is picked up by the activityCheck loop + // before the next user activity is picked up by the activity check loop // causing a flicker if (!this.userActivity_) { - this.userActive(false); + this.userActive(false); } }, timeout); } @@ -2481,11 +2520,10 @@ class Player extends Component { return this; } - if (this.tech_ && this.tech_['featuresPlaybackRate']) { + if (this.tech_ && this.tech_.featuresPlaybackRate) { return this.techGet_('playbackRate'); - } else { - return 1.0; } + return 1.0; } /** @@ -2611,7 +2649,9 @@ class Player extends Component { textTracks() { // cannot use techGet_ directly because it checks to see whether the tech is ready. // Flash is unlikely to be ready in time but textTracks should still work. - return this.tech_ && this.tech_['textTracks'](); + if (this.tech_) { + return this.tech_.textTracks(); + } } /** @@ -2621,7 +2661,9 @@ class Player extends Component { * @method remoteTextTracks */ remoteTextTracks() { - return this.tech_ && this.tech_['remoteTextTracks'](); + if (this.tech_) { + return this.tech_.remoteTextTracks(); + } } /** @@ -2631,7 +2673,9 @@ class Player extends Component { * @method remoteTextTrackEls */ remoteTextTrackEls() { - return this.tech_ && this.tech_['remoteTextTrackEls'](); + if (this.tech_) { + return this.tech_.remoteTextTrackEls(); + } } /** @@ -2645,7 +2689,9 @@ class Player extends Component { * @method addTextTrack */ addTextTrack(kind, label, language) { - return this.tech_ && this.tech_['addTextTrack'](kind, label, language); + if (this.tech_) { + return this.tech_.addTextTrack(kind, label, language); + } } /** @@ -2655,7 +2701,9 @@ class Player extends Component { * @method addRemoteTextTrack */ addRemoteTextTrack(options) { - return this.tech_ && this.tech_['addRemoteTextTrack'](options); + if (this.tech_) { + return this.tech_.addRemoteTextTrack(options); + } } /** @@ -2666,8 +2714,10 @@ class Player extends Component { */ // destructure the input into an object with a track argument, defaulting to arguments[0] // default the whole argument to an empty object if nothing was passed in - removeRemoteTextTrack({track = arguments[0]} = {}) { // jshint ignore:line - this.tech_ && this.tech_['removeRemoteTextTrack'](track); + removeRemoteTextTrack({track = arguments[0]} = {}) { + if (this.tech_) { + return this.tech_.removeRemoteTextTrack(track); + } } /** @@ -2691,11 +2741,11 @@ class Player extends Component { } // Methods to add support for - // initialTime: function(){ return this.techCall_('initialTime'); }, - // startOffsetTime: function(){ return this.techCall_('startOffsetTime'); }, - // played: function(){ return this.techCall_('played'); }, - // defaultPlaybackRate: function(){ return this.techCall_('defaultPlaybackRate'); }, - // defaultMuted: function(){ return this.techCall_('defaultMuted'); } + // initialTime: function() { return this.techCall_('initialTime'); }, + // startOffsetTime: function() { return this.techCall_('startOffsetTime'); }, + // played: function() { return this.techCall_('played'); }, + // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); }, + // defaultMuted: function() { return this.techCall_('defaultMuted'); } /** * The player's language code @@ -2713,7 +2763,7 @@ class Player extends Component { return this.language_; } - this.language_ = (''+code).toLowerCase(); + this.language_ = String(code).toLowerCase(); return this; } @@ -2726,7 +2776,7 @@ class Player extends Component { * @method languages */ languages() { - return mergeOptions(Player.prototype.options_.languages, this.languages_); + return mergeOptions(Player.prototype.options_.languages, this.languages_); } /** @@ -2736,8 +2786,8 @@ class Player extends Component { * @method toJSON */ toJSON() { - let options = mergeOptions(this.options_); - let tracks = options.tracks; + const options = mergeOptions(this.options_); + const tracks = options.tracks; options.tracks = []; @@ -2770,16 +2820,14 @@ class Player extends Component { * @return {ModalDialog} */ createModal(content, options) { - let player = this; - options = options || {}; options.content = content || ''; - let modal = new ModalDialog(player, options); + const modal = new ModalDialog(this, options); - player.addChild(modal); - modal.on('dispose', function() { - player.removeChild(modal); + this.addChild(modal); + modal.on('dispose', () => { + this.removeChild(modal); }); return modal.open(); @@ -2794,19 +2842,20 @@ class Player extends Component { * @method getTagSettings */ static getTagSettings(tag) { - let baseOptions = { - 'sources': [], - 'tracks': [] + const baseOptions = { + sources: [], + tracks: [] }; const tagOptions = Dom.getElAttributes(tag); const dataSetup = tagOptions['data-setup']; // Check if data-setup attr exists. - if (dataSetup !== null){ + if (dataSetup !== null) { // Parse options JSON // If empty string, make it a parsable json object. const [err, data] = safeParseTuple(dataSetup || '{}'); + if (err) { log.error(err); } @@ -2819,10 +2868,11 @@ class Player extends Component { if (tag.hasChildNodes()) { const children = tag.childNodes; - for (let i=0, j=children.length; i 0) { - for(let i=0, e=vids.length; i 0) { - for(let i=0, e=audios.length; i 0) { - for (let i=0, e=mediaEls.length; i this.play(), 0); } } @@ -187,7 +190,8 @@ class Flash extends Tech { * @method setCurrentTime */ setCurrentTime(time) { - let seekable = this.seekable(); + const seekable = this.seekable(); + if (seekable.length) { // clamp to the current seekable range time = time > seekable.start(0) ? time : seekable.start(0); @@ -224,9 +228,8 @@ class Flash extends Tech { currentSrc() { if (this.currentSource_) { return this.currentSource_.src; - } else { - return this.el_.vjs_getProperty('currentSrc'); } + return this.el_.vjs_getProperty('currentSrc'); } /** @@ -237,10 +240,10 @@ class Flash extends Tech { duration() { if (this.readyState() === 0) { return NaN; - } else { - let duration = this.el_.vjs_getProperty('duration'); - return duration >= 0 ? duration : Infinity; } + const duration = this.el_.vjs_getProperty('duration'); + + return duration >= 0 ? duration : Infinity; } /** @@ -276,6 +279,7 @@ class Flash extends Tech { */ seekable() { const duration = this.duration(); + if (duration === 0) { return createTimeRange(); } @@ -289,7 +293,8 @@ class Flash extends Tech { * @method buffered */ buffered() { - let ranges = this.el_.vjs_getProperty('buffered'); + const ranges = this.el_.vjs_getProperty('buffered'); + if (ranges.length === 0) { return createTimeRange(); } @@ -305,7 +310,8 @@ class Flash extends Tech { * @method supportsFullScreen */ supportsFullScreen() { - return false; // Flash does not allow fullscreen through javascript + // Flash does not allow fullscreen through javascript + return false; } /** @@ -322,18 +328,23 @@ class Flash extends Tech { } - // Create setters and getters for attributes const _api = Flash.prototype; const _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','); const _readOnly = 'networkState,readyState,initialTime,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(','); -function _createSetter(attr){ - var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1); - _api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); }; +function _createSetter(attr) { + const attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1); + + _api['set' + attrUpper] = function(val) { + return this.el_.vjs_setProperty(attr, val); + }; } + function _createGetter(attr) { - _api[attr] = function(){ return this.el_.vjs_getProperty(attr); }; + _api[attr] = function() { + return this.el_.vjs_getProperty(attr); + }; } // Create getter and setters for all read/write attributes @@ -349,7 +360,7 @@ for (let i = 0; i < _readOnly.length; i++) { /* Flash Support Testing -------------------------------------------------------- */ -Flash.isSupported = function(){ +Flash.isSupported = function() { return Flash.version()[0] >= 10; // return swfobject.hasFlashPlayerVersion('10'); }; @@ -371,7 +382,7 @@ Flash.nativeSourceHandler = {}; * @param {String} type The mimetype to check * @return {String} 'probably', 'maybe', or '' (empty string) */ -Flash.nativeSourceHandler.canPlayType = function(type){ +Flash.nativeSourceHandler.canPlayType = function(type) { if (type in Flash.formats) { return 'maybe'; } @@ -386,11 +397,12 @@ Flash.nativeSourceHandler.canPlayType = function(type){ * @param {Object} options The options passed to the tech * @return {String} 'probably', 'maybe', or '' (empty string) */ -Flash.nativeSourceHandler.canHandleSource = function(source, options){ - var type; +Flash.nativeSourceHandler.canHandleSource = function(source, options) { + let type; function guessMimeType(src) { - var ext = Url.getFileExtension(src); + const ext = Url.getFileExtension(src); + if (ext) { return `video/${ext}`; } @@ -416,7 +428,7 @@ Flash.nativeSourceHandler.canHandleSource = function(source, options){ * @param {Flash} tech The instance of the Flash tech * @param {Object} options The options to pass to the source */ -Flash.nativeSourceHandler.handleSource = function(source, tech, options){ +Flash.nativeSourceHandler.handleSource = function(source, tech, options) { tech.setSrc(source.src); }; @@ -424,7 +436,7 @@ Flash.nativeSourceHandler.handleSource = function(source, tech, options){ * Clean up the source handler when disposing the player or switching sources.. * (no cleanup is needed when supporting the format natively) */ -Flash.nativeSourceHandler.dispose = function(){}; +Flash.nativeSourceHandler.dispose = function() {}; // Register the native source handler Flash.registerSourceHandler(Flash.nativeSourceHandler); @@ -436,9 +448,9 @@ Flash.formats = { 'video/m4v': 'MP4' }; -Flash.onReady = function(currSwf){ - let el = Dom.getEl(currSwf); - let tech = el && el.tech; +Flash.onReady = function(currSwf) { + const el = Dom.getEl(currSwf); + const tech = el && el.tech; // if there is no el then the tech has been disposed // and the tech element was removed from the player div @@ -450,7 +462,7 @@ Flash.onReady = function(currSwf){ // The SWF isn't always ready when it says it is. Sometimes the API functions still need to be added to the object. // If it's not ready, we set a timeout to check again shortly. -Flash.checkReady = function(tech){ +Flash.checkReady = function(tech) { // stop worrying if the tech has been disposed if (!tech.el()) { return; @@ -462,20 +474,21 @@ Flash.checkReady = function(tech){ tech.triggerReady(); } else { // wait longer - this.setTimeout(function(){ - Flash['checkReady'](tech); + this.setTimeout(function() { + Flash.checkReady(tech); }, 50); } }; // Trigger events from the swf on the player -Flash.onEvent = function(swfID, eventName){ - let tech = Dom.getEl(swfID).tech; +Flash.onEvent = function(swfID, eventName) { + const tech = Dom.getEl(swfID).tech; + tech.trigger(eventName, Array.prototype.slice.call(arguments, 2)); }; // Log errors from the swf -Flash.onError = function(swfID, err){ +Flash.onError = function(swfID, err) { const tech = Dom.getEl(swfID).tech; // trigger MEDIA_ERR_SRC_NOT_SUPPORTED @@ -488,7 +501,7 @@ Flash.onError = function(swfID, err){ }; // Flash Version Check -Flash.version = function(){ +Flash.version = function() { let version = '0,0,0'; // IE @@ -496,18 +509,20 @@ Flash.version = function(){ version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; // other browsers - } catch(e) { + } catch (e) { try { - if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin){ + if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) { version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; } - } catch(err) {} + } catch (err) { + // satisfy linter + } } return version.split(','); }; // Flash embedding method. Only used in non-iframe mode -Flash.embed = function(swf, flashVars, params, attributes){ +Flash.embed = function(swf, flashVars, params, attributes) { const code = Flash.getEmbedCode(swf, flashVars, params, attributes); // Get element by embedding code and retrieving created element @@ -516,7 +531,7 @@ Flash.embed = function(swf, flashVars, params, attributes){ return obj; }; -Flash.getEmbedCode = function(swf, flashVars, params, attributes){ +Flash.getEmbedCode = function(swf, flashVars, params, attributes) { const objTag = '`; }); attributes = assign({ // Add swf to attributes (need both for IE and Others to work) - 'data': swf, + data: swf, // Default to 100% width/height - 'width': '100%', - 'height': '100%' + width: '100%', + height: '100%' }, attributes); // Create Attributes string - Object.getOwnPropertyNames(attributes).forEach(function(key){ + Object.getOwnPropertyNames(attributes).forEach(function(key) { attrsString += `${key}="${attributes[key]}" `; }); diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js index 8d72e042c9..2d64cb9680 100644 --- a/src/js/tech/html5.js +++ b/src/js/tech/html5.js @@ -10,7 +10,6 @@ import * as Url from '../utils/url.js'; import * as Fn from '../utils/fn.js'; import log from '../utils/log.js'; import tsml from 'tsml'; -import TextTrack from '../../../src/js/tracks/text-track.js'; import * as browser from '../utils/browser.js'; import document from 'global/document'; import window from 'global/window'; @@ -28,7 +27,7 @@ import toTitleCase from '../utils/to-title-case.js'; */ class Html5 extends Tech { - constructor(options, ready){ + constructor(options, ready) { super(options, ready); const source = options.source; @@ -46,13 +45,13 @@ class Html5 extends Tech { if (this.el_.hasChildNodes()) { - let nodes = this.el_.childNodes; + const nodes = this.el_.childNodes; let nodesLength = nodes.length; - let removeNodes = []; + const removeNodes = []; while (nodesLength--) { - let node = nodes[nodesLength]; - let nodeName = node.nodeName.toLowerCase(); + const node = nodes[nodesLength]; + const nodeName = node.nodeName.toLowerCase(); if (nodeName === 'track') { if (!this.featuresNativeTextTracks) { @@ -74,21 +73,21 @@ class Html5 extends Tech { } } - for (let i=0; i { - let capitalType = toTitleCase(type); + const capitalType = toTitleCase(type); if (!this[`featuresNative${capitalType}Tracks`]) { return; } - let tl = this.el()[`${type}Tracks`]; + const tl = this.el()[`${type}Tracks`]; if (tl && tl.addEventListener) { tl.addEventListener('change', Fn.bind(this, this[`handle${capitalType}TrackChange_`])); @@ -102,7 +101,7 @@ class Html5 extends Tech { if (this.featuresNativeTextTracks) { if (crossoriginTracks) { - log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used. + log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used. This may prevent text tracks from loading.`); } @@ -132,15 +131,15 @@ class Html5 extends Tech { dispose() { // Un-ProxyNativeTracks ['audio', 'video', 'text'].forEach((type) => { - let capitalType = toTitleCase(type); - let tl = this.el_[`${type}Tracks`]; + const capitalType = toTitleCase(type); + const tl = this.el_[`${type}Tracks`]; if (tl && tl.removeEventListener) { tl.removeEventListener('change', this[`handle${capitalType}TrackChange_`]); tl.removeEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]); tl.removeEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]); } - + // Stop removing old text tracks if (tl) { this.off('loadstart', this[`removeOld${capitalType}Tracks_`]); @@ -164,11 +163,12 @@ class Html5 extends Tech { // Check if this browser supports moving the element into the box. // On the iPhone video will break if you move the element, // So we have to create a brand new element. - if (!el || this['movingMediaElementInDOM'] === false) { + if (!el || this.movingMediaElementInDOM === false) { // If the original tag is still there, clone and remove it. if (el) { const clone = el.cloneNode(true); + el.parentNode.insertBefore(clone, el); Html5.disposeMediaElement(el); el = clone; @@ -176,8 +176,9 @@ class Html5 extends Tech { el = document.createElement('video'); // determine if native controls should be used - let tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag); - let attributes = mergeOptions({}, tagAttributes); + const tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag); + const attributes = mergeOptions({}, tagAttributes); + if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) { delete attributes.controls; } @@ -192,10 +193,12 @@ class Html5 extends Tech { } // Update specific tag settings, in case they were overridden - let settingsAttrs = ['autoplay','preload','loop','muted']; + const settingsAttrs = ['autoplay', 'preload', 'loop', 'muted']; + for (let i = settingsAttrs.length - 1; i >= 0; i--) { const attr = settingsAttrs[i]; - let overwriteAttrs = {}; + const overwriteAttrs = {}; + if (typeof this.options_[attr] !== 'undefined') { overwriteAttrs[attr] = this.options_[attr]; } @@ -229,21 +232,23 @@ class Html5 extends Tech { // which could also happen between now and the next loop, so we'll // watch for that also. let loadstartFired = false; - let setLoadstartFired = function() { + const setLoadstartFired = function() { loadstartFired = true; }; + this.on('loadstart', setLoadstartFired); - let triggerLoadstart = function() { + const triggerLoadstart = function() { // We did miss the original loadstart. Make sure the player // sees loadstart before loadedmetadata if (!loadstartFired) { this.trigger('loadstart'); } }; + this.on('loadedmetadata', triggerLoadstart); - this.ready(function(){ + this.ready(function() { this.off('loadstart', setLoadstartFired); this.off('loadedmetadata', triggerLoadstart); @@ -260,7 +265,7 @@ class Html5 extends Tech { // The other readyState events aren't as much of a problem if we double // them, so not going to go to as much trouble as loadstart to prevent // that unless we find reason to. - let eventsToTrigger = ['loadstart']; + const eventsToTrigger = ['loadstart']; // loadedmetadata: newly equal to HAVE_METADATA (1) or greater eventsToTrigger.push('loadedmetadata'); @@ -281,15 +286,15 @@ class Html5 extends Tech { } // We still need to give the player time to add event listeners - this.ready(function(){ - eventsToTrigger.forEach(function(type){ + this.ready(function() { + eventsToTrigger.forEach(function(type) { this.trigger(type); }, this); }); } proxyNativeTextTracks_() { - let tt = this.el().textTracks; + const tt = this.el().textTracks; if (tt) { // Add tracks - if player is initialised after DOM loaded, textTracks @@ -303,14 +308,15 @@ class Html5 extends Tech { tt.addEventListener('addtrack', this.handleTextTrackAdd_); tt.addEventListener('removetrack', this.handleTextTrackRemove_); } - + // Remove (native) texttracks that are not used anymore this.on('loadstart', this.removeOldTextTracks_); } } handleTextTrackChange(e) { - let tt = this.textTracks(); + const tt = this.textTracks(); + this.textTracks().trigger({ type: 'change', target: tt, @@ -328,7 +334,8 @@ class Html5 extends Tech { } handleVideoTrackChange_(e) { - let vt = this.videoTracks(); + const vt = this.videoTracks(); + this.videoTracks().trigger({ type: 'change', target: vt, @@ -346,7 +353,8 @@ class Html5 extends Tech { } handleAudioTrackChange_(e) { - let audioTrackList = this.audioTracks(); + const audioTrackList = this.audioTracks(); + this.audioTracks().trigger({ type: 'change', target: audioTrackList, @@ -373,15 +381,16 @@ class Html5 extends Tech { removeOldTracks_(techTracks, elTracks) { // This will loop over the techTracks and check if they are still used by the HTML5 video element // If not, they will be removed from the emulated list - let removeTracks = []; + const removeTracks = []; + if (!elTracks) { return; } for (let i = 0; i < techTracks.length; i++) { - let techTrack = techTracks[i]; - + const techTrack = techTracks[i]; let found = false; + for (let j = 0; j < elTracks.length; j++) { if (elTracks[j] === techTrack) { found = true; @@ -396,6 +405,7 @@ class Html5 extends Tech { for (let i = 0; i < removeTracks.length; i++) { const track = removeTracks[i]; + techTracks.removeTrack_(track); } } @@ -403,18 +413,21 @@ class Html5 extends Tech { removeOldTextTracks_() { const techTracks = this.textTracks(); const elTracks = this.el().textTracks; + this.removeOldTracks_(techTracks, elTracks); } removeOldAudioTracks_() { const techTracks = this.audioTracks(); const elTracks = this.el().audioTracks; + this.removeOldTracks_(techTracks, elTracks); } removeOldVideoTracks_() { const techTracks = this.videoTracks(); const elTracks = this.el().videoTracks; + this.removeOldTracks_(techTracks, elTracks); } @@ -423,14 +436,18 @@ class Html5 extends Tech { * * @method play */ - play() { this.el_.play(); } + play() { + this.el_.play(); + } /** * Pause for html5 tech * * @method pause */ - pause() { this.el_.pause(); } + pause() { + this.el_.pause(); + } /** * Paused for html5 tech @@ -438,7 +455,9 @@ class Html5 extends Tech { * @return {Boolean} * @method paused */ - paused() { return this.el_.paused; } + paused() { + return this.el_.paused; + } /** * Get current time @@ -446,7 +465,9 @@ class Html5 extends Tech { * @return {Number} * @method currentTime */ - currentTime() { return this.el_.currentTime; } + currentTime() { + return this.el_.currentTime; + } /** * Set current time @@ -457,7 +478,7 @@ class Html5 extends Tech { setCurrentTime(seconds) { try { this.el_.currentTime = seconds; - } catch(e) { + } catch (e) { log(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady); } @@ -469,7 +490,9 @@ class Html5 extends Tech { * @return {Number} * @method duration */ - duration() { return this.el_.duration || 0; } + duration() { + return this.el_.duration || 0; + } /** * Get a TimeRange object that represents the intersection @@ -479,7 +502,9 @@ class Html5 extends Tech { * @return {TimeRangeObject} * @method buffered */ - buffered() { return this.el_.buffered; } + buffered() { + return this.el_.buffered; + } /** * Get volume level @@ -487,7 +512,9 @@ class Html5 extends Tech { * @return {Number} * @method volume */ - volume() { return this.el_.volume; } + volume() { + return this.el_.volume; + } /** * Set volume level @@ -495,7 +522,9 @@ class Html5 extends Tech { * @param {Number} percentAsDecimal Volume percent as a decimal * @method setVolume */ - setVolume(percentAsDecimal) { this.el_.volume = percentAsDecimal; } + setVolume(percentAsDecimal) { + this.el_.volume = percentAsDecimal; + } /** * Get if muted @@ -503,7 +532,9 @@ class Html5 extends Tech { * @return {Boolean} * @method muted */ - muted() { return this.el_.muted; } + muted() { + return this.el_.muted; + } /** * Set muted @@ -511,7 +542,9 @@ class Html5 extends Tech { * @param {Boolean} If player is to be muted or note * @method setMuted */ - setMuted(muted) { this.el_.muted = muted; } + setMuted(muted) { + this.el_.muted = muted; + } /** * Get player width @@ -519,7 +552,9 @@ class Html5 extends Tech { * @return {Number} * @method width */ - width() { return this.el_.offsetWidth; } + width() { + return this.el_.offsetWidth; + } /** * Get player height @@ -527,7 +562,9 @@ class Html5 extends Tech { * @return {Number} * @method height */ - height() { return this.el_.offsetHeight; } + height() { + return this.el_.offsetHeight; + } /** * Get if there is fullscreen support @@ -537,9 +574,10 @@ class Html5 extends Tech { */ supportsFullScreen() { if (typeof this.el_.webkitEnterFullScreen === 'function') { - let userAgent = window.navigator.userAgent; + const userAgent = window.navigator.userAgent; + // Seems to be broken in Chromium/Chrome && Safari in Leopard - if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) { + if ((/Android/).test(userAgent) || !(/Chrome|Mac OS X 10.5/).test(userAgent)) { return true; } } @@ -552,7 +590,7 @@ class Html5 extends Tech { * @method enterFullScreen */ enterFullScreen() { - var video = this.el_; + const video = this.el_; if ('webkitDisplayingFullscreen' in video) { this.one('webkitbeginfullscreen', function() { @@ -571,7 +609,7 @@ class Html5 extends Tech { // playing and pausing synchronously during the transition to fullscreen // can get iOS ~6.1 devices into a play/pause loop - this.setTimeout(function(){ + this.setTimeout(function() { video.pause(); video.webkitEnterFullScreen(); }, 0); @@ -599,10 +637,10 @@ class Html5 extends Tech { src(src) { if (src === undefined) { return this.el_.src; - } else { - // Setting src through `src` instead of `setSrc` will be deprecated - this.setSrc(src); } + + // Setting src through `src` instead of `setSrc` will be deprecated + this.setSrc(src); } /** @@ -621,7 +659,7 @@ class Html5 extends Tech { * * @method load */ - load(){ + load() { this.el_.load(); } @@ -643,9 +681,8 @@ class Html5 extends Tech { currentSrc() { if (this.currentSource_) { return this.currentSource_.src; - } else { - return this.el_.currentSrc; } + return this.el_.currentSrc; } /** @@ -654,7 +691,9 @@ class Html5 extends Tech { * @return {String} * @method poster */ - poster() { return this.el_.poster; } + poster() { + return this.el_.poster; + } /** * Set poster @@ -662,7 +701,9 @@ class Html5 extends Tech { * @param {String} val URL to poster image * @method */ - setPoster(val) { this.el_.poster = val; } + setPoster(val) { + this.el_.poster = val; + } /** * Get preload attribute @@ -670,7 +711,9 @@ class Html5 extends Tech { * @return {String} * @method preload */ - preload() { return this.el_.preload; } + preload() { + return this.el_.preload; + } /** * Set preload attribute @@ -678,7 +721,9 @@ class Html5 extends Tech { * @param {String} val Value for preload attribute * @method setPreload */ - setPreload(val) { this.el_.preload = val; } + setPreload(val) { + this.el_.preload = val; + } /** * Get autoplay attribute @@ -686,7 +731,9 @@ class Html5 extends Tech { * @return {String} * @method autoplay */ - autoplay() { return this.el_.autoplay; } + autoplay() { + return this.el_.autoplay; + } /** * Set autoplay attribute @@ -694,7 +741,9 @@ class Html5 extends Tech { * @param {String} val Value for preload attribute * @method setAutoplay */ - setAutoplay(val) { this.el_.autoplay = val; } + setAutoplay(val) { + this.el_.autoplay = val; + } /** * Get controls attribute @@ -702,7 +751,9 @@ class Html5 extends Tech { * @return {String} * @method controls */ - controls() { return this.el_.controls; } + controls() { + return this.el_.controls; + } /** * Set controls attribute @@ -710,7 +761,9 @@ class Html5 extends Tech { * @param {String} val Value for controls attribute * @method setControls */ - setControls(val) { this.el_.controls = !!val; } + setControls(val) { + this.el_.controls = !!val; + } /** * Get loop attribute @@ -718,7 +771,9 @@ class Html5 extends Tech { * @return {String} * @method loop */ - loop() { return this.el_.loop; } + loop() { + return this.el_.loop; + } /** * Set loop attribute @@ -726,7 +781,9 @@ class Html5 extends Tech { * @param {String} val Value for loop attribute * @method setLoop */ - setLoop(val) { this.el_.loop = val; } + setLoop(val) { + this.el_.loop = val; + } /** * Get error value @@ -734,7 +791,9 @@ class Html5 extends Tech { * @return {String} * @method error */ - error() { return this.el_.error; } + error() { + return this.el_.error; + } /** * Get whether or not the player is in the "seeking" state @@ -742,7 +801,9 @@ class Html5 extends Tech { * @return {Boolean} * @method seeking */ - seeking() { return this.el_.seeking; } + seeking() { + return this.el_.seeking; + } /** * Get a TimeRanges object that represents the @@ -752,7 +813,9 @@ class Html5 extends Tech { * @return {TimeRangeObject} * @method seekable */ - seekable() { return this.el_.seekable; } + seekable() { + return this.el_.seekable; + } /** * Get if video ended @@ -760,7 +823,9 @@ class Html5 extends Tech { * @return {Boolean} * @method ended */ - ended() { return this.el_.ended; } + ended() { + return this.el_.ended; + } /** * Get the value of the muted content attribute @@ -770,7 +835,9 @@ class Html5 extends Tech { * @return {Boolean} * @method defaultMuted */ - defaultMuted() { return this.el_.defaultMuted; } + defaultMuted() { + return this.el_.defaultMuted; + } /** * Get desired speed at which the media resource is to play @@ -778,7 +845,9 @@ class Html5 extends Tech { * @return {Number} * @method playbackRate */ - playbackRate() { return this.el_.playbackRate; } + playbackRate() { + return this.el_.playbackRate; + } /** * Returns a TimeRanges object that represents the ranges of the @@ -787,7 +856,9 @@ class Html5 extends Tech { * timeline that has been reached through normal playback * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-played */ - played() { return this.el_.played; } + played() { + return this.el_.played; + } /** * Set desired speed at which the media resource is to play @@ -795,7 +866,9 @@ class Html5 extends Tech { * @param {Number} val Speed at which the media resource is to play * @method setPlaybackRate */ - setPlaybackRate(val) { this.el_.playbackRate = val; } + setPlaybackRate(val) { + this.el_.playbackRate = val; + } /** * Get the current state of network activity for the element, from @@ -808,7 +881,9 @@ class Html5 extends Tech { * @return {Number} * @method networkState */ - networkState() { return this.el_.networkState; } + networkState() { + return this.el_.networkState; + } /** * Get a value that expresses the current state of the element @@ -823,7 +898,9 @@ class Html5 extends Tech { * @return {Number} * @method readyState */ - readyState() { return this.el_.readyState; } + readyState() { + return this.el_.readyState; + } /** * Get width of video @@ -831,7 +908,9 @@ class Html5 extends Tech { * @return {Number} * @method videoWidth */ - videoWidth() { return this.el_.videoWidth; } + videoWidth() { + return this.el_.videoWidth; + } /** * Get height of video @@ -839,7 +918,9 @@ class Html5 extends Tech { * @return {Number} * @method videoHeight */ - videoHeight() { return this.el_.videoHeight; } + videoHeight() { + return this.el_.videoHeight; + } /** * Get text tracks @@ -862,7 +943,7 @@ class Html5 extends Tech { * @method addTextTrack */ addTextTrack(kind, label, language) { - if (!this['featuresNativeTextTracks']) { + if (!this.featuresNativeTextTracks) { return super.addTextTrack(kind, label, language); } @@ -877,12 +958,12 @@ class Html5 extends Tech { * @return {HTMLTrackElement} * @method addRemoteTextTrack */ - addRemoteTextTrack(options={}) { - if (!this['featuresNativeTextTracks']) { + addRemoteTextTrack(options = {}) { + if (!this.featuresNativeTextTracks) { return super.addRemoteTextTrack(options); } - let htmlTrackElement = document.createElement('track'); + const htmlTrackElement = document.createElement('track'); if (options.kind) { htmlTrackElement.kind = options.kind; @@ -919,21 +1000,20 @@ class Html5 extends Tech { * @method removeRemoteTextTrack */ removeRemoteTextTrack(track) { - if (!this['featuresNativeTextTracks']) { + if (!this.featuresNativeTextTracks) { return super.removeRemoteTextTrack(track); } - let tracks, i; - - let trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); + const trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list this.remoteTextTrackEls().removeTrackElement_(trackElement); this.remoteTextTracks().removeTrack_(track); - tracks = this.$$('track'); + const tracks = this.$$('track'); + + let i = tracks.length; - i = tracks.length; while (i--) { if (track === tracks[i] || track === tracks[i].track) { this.el().removeChild(tracks[i]); @@ -943,7 +1023,6 @@ class Html5 extends Tech { } - /* HTML5 Support Testing ---------------------------------------------------- */ /* @@ -954,7 +1033,8 @@ class Html5 extends Tech { * @private */ Html5.TEST_VID = document.createElement('video'); -let track = document.createElement('track'); +const track = document.createElement('track'); + track.kind = 'captions'; track.srclang = 'en'; track.label = 'English'; @@ -965,10 +1045,10 @@ Html5.TEST_VID.appendChild(track); * * @return {Boolean} */ -Html5.isSupported = function(){ +Html5.isSupported = function() { // IE9 with no Media Player is a LIAR! (#984) try { - Html5.TEST_VID['volume'] = 0.5; + Html5.TEST_VID.volume = 0.5; } catch (e) { return false; } @@ -994,12 +1074,12 @@ Html5.nativeSourceHandler = {}; * @param {String} type The mimetype to check * @return {String} 'probably', 'maybe', or '' (empty string) */ -Html5.nativeSourceHandler.canPlayType = function(type){ +Html5.nativeSourceHandler.canPlayType = function(type) { // IE9 on Windows 7 without MediaPlayer throws an error here // https://github.com/videojs/video.js/issues/519 try { return Html5.TEST_VID.canPlayType(type); - } catch(e) { + } catch (e) { return ''; } }; @@ -1011,15 +1091,15 @@ Html5.nativeSourceHandler.canPlayType = function(type){ * @param {Object} options The options passed to the tech * @return {String} 'probably', 'maybe', or '' (empty string) */ -Html5.nativeSourceHandler.canHandleSource = function(source, options){ - var match, ext; +Html5.nativeSourceHandler.canHandleSource = function(source, options) { // If a type was provided we should rely on that if (source.type) { return Html5.nativeSourceHandler.canPlayType(source.type); + + // If no type, fall back to checking 'video/[EXTENSION]' } else if (source.src) { - // If no type, fall back to checking 'video/[EXTENSION]' - ext = Url.getFileExtension(source.src); + const ext = Url.getFileExtension(source.src); return Html5.nativeSourceHandler.canPlayType(`video/${ext}`); } @@ -1036,7 +1116,7 @@ Html5.nativeSourceHandler.canHandleSource = function(source, options){ * @param {Html5} tech The instance of the Html5 tech * @param {Object} options The options to pass to the source */ -Html5.nativeSourceHandler.handleSource = function(source, tech, options){ +Html5.nativeSourceHandler.handleSource = function(source, tech, options) { tech.setSrc(source.src); }; @@ -1044,7 +1124,7 @@ Html5.nativeSourceHandler.handleSource = function(source, tech, options){ * Clean up the source handler when disposing the player or switching sources.. * (no cleanup is needed when supporting the format natively) */ -Html5.nativeSourceHandler.dispose = function(){}; +Html5.nativeSourceHandler.dispose = function() {}; // Register the native source handler Html5.registerSourceHandler(Html5.nativeSourceHandler); @@ -1056,13 +1136,14 @@ Html5.registerSourceHandler(Html5.nativeSourceHandler); * * @return {Boolean} */ -Html5.canControlVolume = function(){ +Html5.canControlVolume = function() { // IE will error if Windows Media Player not installed #3315 try { - var volume = Html5.TEST_VID.volume; + const volume = Html5.TEST_VID.volume; + Html5.TEST_VID.volume = (volume / 2) + 0.1; return volume !== Html5.TEST_VID.volume; - } catch(e) { + } catch (e) { return false; } }; @@ -1072,7 +1153,7 @@ Html5.canControlVolume = function(){ * * @return {Boolean} */ -Html5.canControlPlaybackRate = function(){ +Html5.canControlPlaybackRate = function() { // Playback rate API is implemented in Android Chrome, but doesn't do anything // https://github.com/videojs/video.js/issues/3180 if (browser.IS_ANDROID && browser.IS_CHROME) { @@ -1080,10 +1161,11 @@ Html5.canControlPlaybackRate = function(){ } // IE will error if Windows Media Player not installed #3315 try { - var playbackRate = Html5.TEST_VID.playbackRate; + const playbackRate = Html5.TEST_VID.playbackRate; + Html5.TEST_VID.playbackRate = (playbackRate / 2) + 0.1; return playbackRate !== Html5.TEST_VID.playbackRate; - } catch(e) { + } catch (e) { return false; } }; @@ -1094,7 +1176,7 @@ Html5.canControlPlaybackRate = function(){ * @return {Boolean} */ Html5.supportsNativeTextTracks = function() { - var supportsTextTracks; + let supportsTextTracks; // Figure out native text track support // If mode is a number, we cannot change it because it'll disappear from view. @@ -1103,7 +1185,7 @@ Html5.supportsNativeTextTracks = function() { // TODO: Investigate firefox: https://github.com/videojs/video.js/issues/1862 supportsTextTracks = !!Html5.TEST_VID.textTracks; if (supportsTextTracks && Html5.TEST_VID.textTracks.length > 0) { - supportsTextTracks = typeof Html5.TEST_VID.textTracks[0]['mode'] !== 'number'; + supportsTextTracks = typeof Html5.TEST_VID.textTracks[0].mode !== 'number'; } if (supportsTextTracks && browser.IS_FIREFOX) { supportsTextTracks = false; @@ -1121,7 +1203,8 @@ Html5.supportsNativeTextTracks = function() { * @return {Boolean} */ Html5.supportsNativeVideoTracks = function() { - let supportsVideoTracks = !!Html5.TEST_VID.videoTracks; + const supportsVideoTracks = !!Html5.TEST_VID.videoTracks; + return supportsVideoTracks; }; @@ -1131,11 +1214,11 @@ Html5.supportsNativeVideoTracks = function() { * @return {Boolean} */ Html5.supportsNativeAudioTracks = function() { - let supportsAudioTracks = !!Html5.TEST_VID.audioTracks; + const supportsAudioTracks = !!Html5.TEST_VID.audioTracks; + return supportsAudioTracks; }; - /** * An array of events available on the Html5 tech. * @@ -1172,14 +1255,14 @@ Html5.Events = [ * * @type {Boolean} */ -Html5.prototype['featuresVolumeControl'] = Html5.canControlVolume(); +Html5.prototype.featuresVolumeControl = Html5.canControlVolume(); /* * Set the tech's playbackRate support status * * @type {Boolean} */ -Html5.prototype['featuresPlaybackRate'] = Html5.canControlPlaybackRate(); +Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate(); /* * Set the tech's status on moving the video element. @@ -1187,41 +1270,41 @@ Html5.prototype['featuresPlaybackRate'] = Html5.canControlPlaybackRate(); * * @type {Boolean} */ -Html5.prototype['movingMediaElementInDOM'] = !browser.IS_IOS; +Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS; /* * Set the the tech's fullscreen resize support status. * HTML video is able to automatically resize when going to fullscreen. * (No longer appears to be used. Can probably be removed.) */ -Html5.prototype['featuresFullscreenResize'] = true; +Html5.prototype.featuresFullscreenResize = true; /* * Set the tech's progress event support status * (this disables the manual progress events of the Tech) */ -Html5.prototype['featuresProgressEvents'] = true; +Html5.prototype.featuresProgressEvents = true; /* * Sets the tech's status on native text track support * * @type {Boolean} */ -Html5.prototype['featuresNativeTextTracks'] = Html5.supportsNativeTextTracks(); +Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks(); /** * Sets the tech's status on native text track support * * @type {Boolean} */ -Html5.prototype['featuresNativeVideoTracks'] = Html5.supportsNativeVideoTracks(); +Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks(); /** * Sets the tech's status on native audio track support * * @type {Boolean} */ -Html5.prototype['featuresNativeAudioTracks'] = Html5.supportsNativeAudioTracks(); +Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); // HTML5 Feature detection and Device Fixes --------------------------------- // let canPlayType; @@ -1249,7 +1332,7 @@ Html5.patchCanPlayType = function() { canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType; } - Html5.TEST_VID.constructor.prototype.canPlayType = function(type){ + Html5.TEST_VID.constructor.prototype.canPlayType = function(type) { if (type && mp4RE.test(type)) { return 'maybe'; } @@ -1259,7 +1342,8 @@ Html5.patchCanPlayType = function() { }; Html5.unpatchCanPlayType = function() { - var r = Html5.TEST_VID.constructor.prototype.canPlayType; + const r = Html5.TEST_VID.constructor.prototype.canPlayType; + Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType; canPlayType = null; return r; @@ -1268,15 +1352,17 @@ Html5.unpatchCanPlayType = function() { // by default, patch the video element Html5.patchCanPlayType(); -Html5.disposeMediaElement = function(el){ - if (!el) { return; } +Html5.disposeMediaElement = function(el) { + if (!el) { + return; + } if (el.parentNode) { el.parentNode.removeChild(el); } // remove any child track or source nodes to prevent their loading - while(el.hasChildNodes()) { + while (el.hasChildNodes()) { el.removeChild(el.firstChild); } @@ -1294,15 +1380,18 @@ Html5.disposeMediaElement = function(el){ } catch (e) { // not supported } - })(); + }()); } }; -Html5.resetMediaElement = function(el){ - if (!el) { return; } +Html5.resetMediaElement = function(el) { + if (!el) { + return; + } - let sources = el.querySelectorAll('source'); + const sources = el.querySelectorAll('source'); let i = sources.length; + while (i--) { el.removeChild(sources[i]); } @@ -1316,8 +1405,10 @@ Html5.resetMediaElement = function(el){ (function() { try { el.load(); - } catch (e) {} - })(); + } catch (e) { + // satisfy linter + } + }()); } }; diff --git a/src/js/tech/loader.js b/src/js/tech/loader.js index 14bc28c184..0675e4abcb 100644 --- a/src/js/tech/loader.js +++ b/src/js/tech/loader.js @@ -3,7 +3,6 @@ */ import Component from '../component.js'; import Tech from './tech.js'; -import window from 'global/window'; import toTitleCase from '../utils/to-title-case.js'; /** @@ -18,16 +17,17 @@ import toTitleCase from '../utils/to-title-case.js'; */ class MediaLoader extends Component { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); // If there are no sources when the player is initialized, // load the first supported playback technology. - if (!options.playerOptions['sources'] || options.playerOptions['sources'].length === 0) { - for (let i=0, j=options.playerOptions['techOrder']; i { - let list = this[`${type}Tracks`]() || []; + const list = this[`${type}Tracks`]() || []; let i = list.length; + while (i--) { - let track = list[i]; + const track = list[i]; + if (type === 'text') { this.removeRemoteTextTrack(track); } @@ -315,7 +342,9 @@ class Tech extends Component { */ setCurrentTime() { // improve the accuracy of manual timeupdates - if (this.manualTimeUpdates) { this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); } + if (this.manualTimeUpdates) { + this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); + } } /** @@ -324,13 +353,15 @@ class Tech extends Component { * @method initTextTrackListeners */ initTextTrackListeners() { - let textTrackListChanges = Fn.bind(this, function() { + const textTrackListChanges = Fn.bind(this, function() { this.trigger('texttrackchange'); }); - let tracks = this.textTracks(); + const tracks = this.textTracks(); - if (!tracks) return; + if (!tracks) { + return; + } tracks.addEventListener('removetrack', textTrackListChanges); tracks.addEventListener('addtrack', textTrackListChanges); @@ -341,7 +372,6 @@ class Tech extends Component { })); } - /** * Initialize audio and video track listeners * @@ -351,11 +381,11 @@ class Tech extends Component { const trackTypes = ['video', 'audio']; trackTypes.forEach((type) => { - let trackListChanges = () => { + const trackListChanges = () => { this.trigger(`${type}trackchange`); }; - let tracks = this[`${type}Tracks`](); + const tracks = this[`${type}Tracks`](); tracks.addEventListener('removetrack', trackListChanges); tracks.addEventListener('addtrack', trackListChanges); @@ -373,13 +403,15 @@ class Tech extends Component { * @method emulateTextTracks */ emulateTextTracks() { - let tracks = this.textTracks(); + const tracks = this.textTracks(); + if (!tracks) { return; } - if (!window['WebVTT'] && this.el().parentNode != null) { - let script = document.createElement('script'); + if (!window.WebVTT && this.el().parentNode !== null && this.el().parentNode !== undefined) { + const script = document.createElement('script'); + script.src = this.options_['vtt.js'] || '../node_modules/videojs-vtt.js/dist/vtt.js'; script.onload = () => { this.trigger('vttjsloaded'); @@ -393,16 +425,17 @@ class Tech extends Component { }); // but have not loaded yet and we set it to true before the inject so that // we don't overwrite the injected window.WebVTT if it loads right away - window['WebVTT'] = true; + window.WebVTT = true; this.el().parentNode.appendChild(script); } - let updateDisplay = () => this.trigger('texttrackchange'); - let textTracksChanges = () => { + const updateDisplay = () => this.trigger('texttrackchange'); + const textTracksChanges = () => { updateDisplay(); for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; + const track = tracks[i]; + track.removeEventListener('cuechange', updateDisplay); if (track.mode === 'showing') { track.addEventListener('cuechange', updateDisplay); @@ -506,11 +539,11 @@ class Tech extends Component { * @method addRemoteTextTrack */ addRemoteTextTrack(options) { - let track = mergeOptions(options, { + const track = mergeOptions(options, { tech: this }); - let htmlTrackElement = new HTMLTrackElement(track); + const htmlTrackElement = new HTMLTrackElement(track); // store HTMLTrackElement and TextTrack to remote list this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); @@ -531,7 +564,7 @@ class Tech extends Component { removeRemoteTextTrack(track) { this.textTracks().removeTrack_(track); - let trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); + const trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list this.remoteTextTrackEls().removeTrackElement_(trackElement); @@ -620,7 +653,7 @@ class Tech extends Component { * @type {TextTrackList} * @private */ -Tech.prototype.textTracks_; +Tech.prototype.textTracks_; // eslint-disable-line /** * List of associated audio tracks @@ -628,7 +661,7 @@ Tech.prototype.textTracks_; * @type {AudioTrackList} * @private */ -Tech.prototype.audioTracks_; +Tech.prototype.audioTracks_; // eslint-disable-line /** * List of associated video tracks @@ -636,27 +669,7 @@ Tech.prototype.audioTracks_; * @type {VideoTrackList} * @private */ -Tech.prototype.videoTracks_; - - -var createTrackHelper = function(self, kind, label, language, options={}) { - let tracks = self.textTracks(); - - options.kind = kind; - - if (label) { - options.label = label; - } - if (language) { - options.language = language; - } - options.tech = self; - - let track = new TextTrack(options); - tracks.addTrack_(track); - - return track; -}; +Tech.prototype.videoTracks_; // eslint-disable-line Tech.prototype.featuresVolumeControl = true; @@ -671,7 +684,7 @@ Tech.prototype.featuresTimeupdateEvents = false; Tech.prototype.featuresNativeTextTracks = false; -/* +/** * A functional mixin for techs that want to use the Source Handler pattern. * * ##### EXAMPLE: @@ -679,16 +692,17 @@ Tech.prototype.featuresNativeTextTracks = false; * Tech.withSourceHandlers.call(MyTech); * */ -Tech.withSourceHandlers = function(_Tech){ - /* - * Register a source handler - * Source handlers are scripts for handling specific formats. - * The source handler pattern is used for adaptive formats (HLS, DASH) that - * manually load video data and feed it into a Source Buffer (Media Source Extensions) - * @param {Function} handler The source handler - * @param {Boolean} first Register it before any existing handlers - */ - _Tech.registerSourceHandler = function(handler, index){ +Tech.withSourceHandlers = function(_Tech) { + + /** + * Register a source handler + * Source handlers are scripts for handling specific formats. + * The source handler pattern is used for adaptive formats (HLS, DASH) that + * manually load video data and feed it into a Source Buffer (Media Source Extensions) + * @param {Function} handler The source handler + * @param {Boolean} first Register it before any existing handlers + */ + _Tech.registerSourceHandler = function(handler, index) { let handlers = _Tech.sourceHandlers; if (!handlers) { @@ -703,13 +717,13 @@ Tech.withSourceHandlers = function(_Tech){ handlers.splice(index, 0, handler); }; - /* + /** * Check if the tech can support the given type * @param {String} type The mimetype to check * @return {String} 'probably', 'maybe', or '' (empty string) */ - _Tech.canPlayType = function(type){ - let handlers = _Tech.sourceHandlers || []; + _Tech.canPlayType = function(type) { + const handlers = _Tech.sourceHandlers || []; let can; for (let i = 0; i < handlers.length; i++) { @@ -723,16 +737,16 @@ Tech.withSourceHandlers = function(_Tech){ return ''; }; - /* - * Return the first source handler that supports the source - * TODO: Answer question: should 'probably' be prioritized over 'maybe' - * @param {Object} source The source object - * @param {Object} options The options passed to the tech - * @returns {Object} The first source handler that supports the source - * @returns {null} Null if no source handler is found - */ - _Tech.selectSourceHandler = function(source, options){ - let handlers = _Tech.sourceHandlers || []; + /** + * Return the first source handler that supports the source + * TODO: Answer question: should 'probably' be prioritized over 'maybe' + * @param {Object} source The source object + * @param {Object} options The options passed to the tech + * @returns {Object} The first source handler that supports the source + * @returns {null} Null if no source handler is found + */ + _Tech.selectSourceHandler = function(source, options) { + const handlers = _Tech.sourceHandlers || []; let can; for (let i = 0; i < handlers.length; i++) { @@ -746,14 +760,14 @@ Tech.withSourceHandlers = function(_Tech){ return null; }; - /* + /** * Check if the tech can support the given source * @param {Object} srcObj The source object * @param {Object} options The options passed to the tech * @return {String} 'probably', 'maybe', or '' (empty string) */ - _Tech.canPlaySource = function(srcObj, options){ - let sh = _Tech.selectSourceHandler(srcObj, options); + _Tech.canPlaySource = function(srcObj, options) { + const sh = _Tech.selectSourceHandler(srcObj, options); if (sh) { return sh.canHandleSource(srcObj, options); @@ -762,17 +776,17 @@ Tech.withSourceHandlers = function(_Tech){ return ''; }; - /* + /** * When using a source handler, prefer its implementation of * any function normally provided by the tech. */ - let deferrable = [ - 'seekable', - 'duration' - ]; + const deferrable = [ + 'seekable', + 'duration' + ]; - deferrable.forEach(function (fnName) { - let originalFn = this[fnName]; + deferrable.forEach(function(fnName) { + const originalFn = this[fnName]; if (typeof originalFn !== 'function') { return; @@ -786,14 +800,14 @@ Tech.withSourceHandlers = function(_Tech){ }; }, _Tech.prototype); - /* - * Create a function for setting the source using a source object - * and source handlers. - * Should never be called unless a source handler was found. - * @param {Object} source A source object with src and type keys - * @return {Tech} self - */ - _Tech.prototype.setSource = function(source){ + /** + * Create a function for setting the source using a source object + * and source handlers. + * Should never be called unless a source handler was found. + * @param {Object} source A source object with src and type keys + * @return {Tech} self + */ + _Tech.prototype.setSource = function(source) { let sh = _Tech.selectSourceHandler(source, this.options_); if (!sh) { diff --git a/src/js/tracks/audio-track-list.js b/src/js/tracks/audio-track-list.js index d1499bec29..07c072c24d 100644 --- a/src/js/tracks/audio-track-list.js +++ b/src/js/tracks/audio-track-list.js @@ -21,6 +21,7 @@ const disableOthers = function(list, track) { list[i].enabled = false; } }; + /** * A list of possible audio tracks. All functionality is in the * base class Tracklist and the spec for AudioTrackList is located at: @@ -57,12 +58,12 @@ class AudioTrackList extends TrackList { // as it does not support Object.defineProperty properly if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in TrackList.prototype) { + for (const prop in TrackList.prototype) { if (prop !== 'constructor') { list[prop] = TrackList.prototype[prop]; } } - for (let prop in AudioTrackList.prototype) { + for (const prop in AudioTrackList.prototype) { if (prop !== 'constructor') { list[prop] = AudioTrackList.prototype[prop]; } diff --git a/src/js/tracks/audio-track.js b/src/js/tracks/audio-track.js index e5f0f7854a..aa8892c18b 100644 --- a/src/js/tracks/audio-track.js +++ b/src/js/tracks/audio-track.js @@ -20,16 +20,16 @@ import * as browser from '../utils/browser.js'; */ class AudioTrack extends Track { constructor(options = {}) { - let settings = merge(options, { + const settings = merge(options, { kind: AudioTrackKind[options.kind] || '' }); // on IE8 this will be a document element // for every other browser this will be a normal object - let track = super(settings); + const track = super(settings); let enabled = false; if (browser.IS_IE8) { - for (let prop in AudioTrack.prototype) { + for (const prop in AudioTrack.prototype) { if (prop !== 'constructor') { track[prop] = AudioTrack.prototype[prop]; } @@ -37,7 +37,9 @@ class AudioTrack extends Track { } Object.defineProperty(track, 'enabled', { - get() { return enabled; }, + get() { + return enabled; + }, set(newEnabled) { // an invalid or unchanged value if (typeof newEnabled !== 'boolean' || newEnabled === enabled) { diff --git a/src/js/tracks/html-track-element-list.js b/src/js/tracks/html-track-element-list.js index 9b38d05820..cbc0ef85c8 100644 --- a/src/js/tracks/html-track-element-list.js +++ b/src/js/tracks/html-track-element-list.js @@ -7,12 +7,12 @@ import document from 'global/document'; class HtmlTrackElementList { constructor(trackElements = []) { - let list = this; + let list = this; // eslint-disable-line if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in HtmlTrackElementList.prototype) { + for (const prop in HtmlTrackElementList.prototype) { if (prop !== 'constructor') { list[prop] = HtmlTrackElementList.prototype[prop]; } diff --git a/src/js/tracks/html-track-element.js b/src/js/tracks/html-track-element.js index d799375efb..c3e3d393d4 100644 --- a/src/js/tracks/html-track-element.js +++ b/src/js/tracks/html-track-element.js @@ -39,20 +39,20 @@ class HTMLTrackElement extends EventTarget { constructor(options = {}) { super(); - let readyState, - trackElement = this; + let readyState; + let trackElement = this; // eslint-disable-line if (browser.IS_IE8) { trackElement = document.createElement('custom'); - for (let prop in HTMLTrackElement.prototype) { + for (const prop in HTMLTrackElement.prototype) { if (prop !== 'constructor') { trackElement[prop] = HTMLTrackElement.prototype[prop]; } } } - let track = new TextTrack(options); + const track = new TextTrack(options); trackElement.kind = track.kind; trackElement.src = track.src; diff --git a/src/js/tracks/text-track-cue-list.js b/src/js/tracks/text-track-cue-list.js index 3abd7ed069..eef9d559a0 100644 --- a/src/js/tracks/text-track-cue-list.js +++ b/src/js/tracks/text-track-cue-list.js @@ -20,12 +20,12 @@ import document from 'global/document'; class TextTrackCueList { constructor(cues) { - let list = this; + let list = this; // eslint-disable-line if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in TextTrackCueList.prototype) { + for (const prop in TextTrackCueList.prototype) { if (prop !== 'constructor') { list[prop] = TextTrackCueList.prototype[prop]; } @@ -53,14 +53,14 @@ class TextTrackCueList { * @private */ setCues_(cues) { - let oldLength = this.length || 0; + const oldLength = this.length || 0; let i = 0; - let l = cues.length; + const l = cues.length; this.cues_ = cues; this.length_ = cues.length; - let defineProp = function(index) { + const defineProp = function(index) { if (!('' + index in this)) { Object.defineProperty(this, '' + index, { get() { @@ -90,7 +90,7 @@ class TextTrackCueList { let result = null; for (let i = 0, l = this.length; i < l; i++) { - let cue = this[i]; + const cue = this[i]; if (cue.id === id) { result = cue; diff --git a/src/js/tracks/text-track-display.js b/src/js/tracks/text-track-display.js index 40ca875b37..995554e5ac 100644 --- a/src/js/tracks/text-track-display.js +++ b/src/js/tracks/text-track-display.js @@ -2,28 +2,60 @@ * @file text-track-display.js */ import Component from '../component'; -import Menu from '../menu/menu.js'; -import MenuItem from '../menu/menu-item.js'; -import MenuButton from '../menu/menu-button.js'; import * as Fn from '../utils/fn.js'; -import document from 'global/document'; import window from 'global/window'; const darkGray = '#222'; const lightGray = '#ccc'; const fontMap = { - monospace: 'monospace', - sansSerif: 'sans-serif', - serif: 'serif', - monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace', - monospaceSerif: '"Courier New", monospace', + monospace: 'monospace', + sansSerif: 'sans-serif', + serif: 'serif', + monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace', + monospaceSerif: '"Courier New", monospace', proportionalSansSerif: 'sans-serif', - proportionalSerif: 'serif', - casual: '"Comic Sans MS", Impact, fantasy', - script: '"Monotype Corsiva", cursive', - smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif' + proportionalSerif: 'serif', + casual: '"Comic Sans MS", Impact, fantasy', + script: '"Monotype Corsiva", cursive', + smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif' }; +/** + * Add cue HTML to display + * + * @param {Number} color Hex number for color, like #f0e + * @param {Number} opacity Value for opacity,0.0 - 1.0 + * @return {RGBAColor} In the form 'rgba(255, 0, 0, 0.3)' + * @method constructColor + */ +function constructColor(color, opacity) { + return 'rgba(' + + // color looks like "#f0e" + parseInt(color[1] + color[1], 16) + ',' + + parseInt(color[2] + color[2], 16) + ',' + + parseInt(color[3] + color[3], 16) + ',' + + opacity + ')'; +} + +/** + * Try to update style + * Some style changes will throw an error, particularly in IE8. Those should be noops. + * + * @param {Element} el The element to be styles + * @param {CSSProperty} style The CSS property to be styled + * @param {CSSStyle} rule The actual style to be applied to the property + * @method tryUpdateStyle + */ +function tryUpdateStyle(el, style, rule) { + try { + el.style[style] = rule; + } catch (e) { + + // Satisfies linter. + return; + } +} + /** * The component for displaying text track cues * @@ -35,7 +67,7 @@ const fontMap = { */ class TextTrackDisplay extends Component { - constructor(player, options, ready){ + constructor(player, options, ready) { super(player, options, ready); player.on('loadstart', Fn.bind(this, this.toggleDisplay)); @@ -46,27 +78,28 @@ class TextTrackDisplay extends Component { // Should probably be moved to an external track loader when we support // tracks that don't need a display. player.ready(Fn.bind(this, function() { - if (player.tech_ && player.tech_['featuresNativeTextTracks']) { + if (player.tech_ && player.tech_.featuresNativeTextTracks) { this.hide(); return; } player.on('fullscreenchange', Fn.bind(this, this.updateDisplay)); - let tracks = this.options_.playerOptions['tracks'] || []; + const tracks = this.options_.playerOptions.tracks || []; + for (let i = 0; i < tracks.length; i++) { - let track = tracks[i]; - this.player_.addRemoteTextTrack(track); + this.player_.addRemoteTextTrack(tracks[i]); } - let modes = {'captions': 1, 'subtitles': 1}; - let trackList = this.player_.textTracks(); + const modes = {captions: 1, subtitles: 1}; + const trackList = this.player_.textTracks(); let firstDesc; let firstCaptions; if (trackList) { for (let i = 0; i < trackList.length; i++) { - let track = trackList[i]; + const track = trackList[i]; + if (track.default) { if (track.kind === 'descriptions' && !firstDesc) { firstDesc = track; @@ -95,7 +128,7 @@ class TextTrackDisplay extends Component { * @method toggleDisplay */ toggleDisplay() { - if (this.player_.tech_ && this.player_.tech_['featuresNativeTextTracks']) { + if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) { this.hide(); } else { this.show(); @@ -123,8 +156,8 @@ class TextTrackDisplay extends Component { * @method clearDisplay */ clearDisplay() { - if (typeof window['WebVTT'] === 'function') { - window['WebVTT']['processCues'](window, [], this.el_); + if (typeof window.WebVTT === 'function') { + window.WebVTT.processCues(window, [], this.el_); } } @@ -134,7 +167,7 @@ class TextTrackDisplay extends Component { * @method updateDisplay */ updateDisplay() { - var tracks = this.player_.textTracks(); + const tracks = this.player_.textTracks(); this.clearDisplay(); @@ -150,10 +183,12 @@ class TextTrackDisplay extends Component { let captionsSubtitlesTrack = null; let i = tracks.length; + while (i--) { - let track = tracks[i]; - if (track['mode'] === 'showing') { - if (track['kind'] === 'descriptions') { + const track = tracks[i]; + + if (track.mode === 'showing') { + if (track.kind === 'descriptions') { descriptionsTrack = track; } else { captionsSubtitlesTrack = track; @@ -175,27 +210,30 @@ class TextTrackDisplay extends Component { * @method updateForTrack */ updateForTrack(track) { - if (typeof window['WebVTT'] !== 'function' || !track['activeCues']) { + if (typeof window.WebVTT !== 'function' || !track.activeCues) { return; } - let overrides = this.player_['textTrackSettings'].getValues(); + const overrides = this.player_.textTrackSettings.getValues(); + const cues = []; - let cues = []; - for (let i = 0; i < track['activeCues'].length; i++) { - cues.push(track['activeCues'][i]); + for (let i = 0; i < track.activeCues.length; i++) { + cues.push(track.activeCues[i]); } - window['WebVTT']['processCues'](window, cues, this.el_); + window.WebVTT.processCues(window, cues, this.el_); let i = cues.length; + while (i--) { - let cue = cues[i]; + const cue = cues[i]; + if (!cue) { continue; } - let cueDiv = cue.displayState; + const cueDiv = cue.displayState; + if (overrides.color) { cueDiv.firstChild.style.color = overrides.color; } @@ -236,6 +274,7 @@ class TextTrackDisplay extends Component { } if (overrides.fontPercent && overrides.fontPercent !== 1) { const fontSize = window.parseFloat(cueDiv.style.fontSize); + cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px'; cueDiv.style.height = 'auto'; cueDiv.style.top = 'auto'; @@ -253,38 +292,5 @@ class TextTrackDisplay extends Component { } -/** -* Add cue HTML to display -* -* @param {Number} color Hex number for color, like #f0e -* @param {Number} opacity Value for opacity,0.0 - 1.0 -* @return {RGBAColor} In the form 'rgba(255, 0, 0, 0.3)' -* @method constructColor -*/ -function constructColor(color, opacity) { - return 'rgba(' + - // color looks like "#f0e" - parseInt(color[1] + color[1], 16) + ',' + - parseInt(color[2] + color[2], 16) + ',' + - parseInt(color[3] + color[3], 16) + ',' + - opacity + ')'; -} - -/** - * Try to update style - * Some style changes will throw an error, particularly in IE8. Those should be noops. - * - * @param {Element} el The element to be styles - * @param {CSSProperty} style The CSS property to be styled - * @param {CSSStyle} rule The actual style to be applied to the property - * @method tryUpdateStyle - */ -function tryUpdateStyle(el, style, rule) { - // - try { - el.style[style] = rule; - } catch (e) {} -} - Component.registerComponent('TextTrackDisplay', TextTrackDisplay); export default TextTrackDisplay; diff --git a/src/js/tracks/text-track-list-converter.js b/src/js/tracks/text-track-list-converter.js index b7ab4dd1f5..375e584e32 100644 --- a/src/js/tracks/text-track-list-converter.js +++ b/src/js/tracks/text-track-list-converter.js @@ -12,14 +12,16 @@ * @return {Object} a serializable javascript representation of the * @private */ -let trackToJson_ = function(track) { - let ret = ['kind', 'label', 'language', 'id', - 'inBandMetadataTrackDispatchType', - 'mode', 'src'].reduce((acc, prop, i) => { +const trackToJson_ = function(track) { + const ret = [ + 'kind', 'label', 'language', 'id', + 'inBandMetadataTrackDispatchType', 'mode', 'src' + ].reduce((acc, prop, i) => { + if (track[prop]) { acc[prop] = track[prop]; } - + return acc; }, { cues: track.cues && Array.prototype.map.call(track.cues, function(cue) { @@ -43,13 +45,14 @@ let trackToJson_ = function(track) { * @return {Array} a serializable javascript representation of the * @function textTracksToJson */ -let textTracksToJson = function(tech) { +const textTracksToJson = function(tech) { - let trackEls = tech.$$('track'); + const trackEls = tech.$$('track'); + + const trackObjs = Array.prototype.map.call(trackEls, (t) => t.track); + const tracks = Array.prototype.map.call(trackEls, function(trackEl) { + const json = trackToJson_(trackEl.track); - let trackObjs = Array.prototype.map.call(trackEls, (t) => t.track); - let tracks = Array.prototype.map.call(trackEls, function(trackEl) { - let json = trackToJson_(trackEl.track); if (trackEl.src) { json.src = trackEl.src; } @@ -69,9 +72,10 @@ let textTracksToJson = function(tech) { * @param tech {tech} the tech to create text tracks on * @function jsonToTextTracks */ -let jsonToTextTracks = function(json, tech) { +const jsonToTextTracks = function(json, tech) { json.forEach(function(track) { - let addedTrack = tech.addRemoteTextTrack(track).track; + const addedTrack = tech.addRemoteTextTrack(track).track; + if (!track.src && track.cues) { track.cues.forEach((cue) => addedTrack.addCue(cue)); } diff --git a/src/js/tracks/text-track-list.js b/src/js/tracks/text-track-list.js index f316ef8987..58610d2c6c 100644 --- a/src/js/tracks/text-track-list.js +++ b/src/js/tracks/text-track-list.js @@ -33,12 +33,12 @@ class TextTrackList extends TrackList { // as it does not support Object.defineProperty properly if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in TrackList.prototype) { + for (const prop in TrackList.prototype) { if (prop !== 'constructor') { list[prop] = TrackList.prototype[prop]; } } - for (let prop in TextTrackList.prototype) { + for (const prop in TextTrackList.prototype) { if (prop !== 'constructor') { list[prop] = TextTrackList.prototype[prop]; } @@ -102,7 +102,7 @@ class TextTrackList extends TrackList { let result = null; for (let i = 0, l = this.length; i < l; i++) { - let track = this[i]; + const track = this[i]; if (track.id === id) { result = track; diff --git a/src/js/tracks/text-track-settings.js b/src/js/tracks/text-track-settings.js index 50d845f04b..4e8e21ab87 100644 --- a/src/js/tracks/text-track-settings.js +++ b/src/js/tracks/text-track-settings.js @@ -8,6 +8,159 @@ import log from '../utils/log.js'; import safeParseTuple from 'safe-json-parse/tuple'; import window from 'global/window'; +function captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId) { + const template = ` +
+
Captions Settings Dialog
+
Beginning of dialog window. Escape will cancel and close the window.
+
+
+
+ Text + + + + + + +
+
+ Background + + + + + + +
+
+ Window + + + + + + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ `; + + return template; +} + +function getSelectedOptionValue(target) { + let selectedOption; + + // not all browsers support selectedOptions, so, fallback to options + if (target.selectedOptions) { + selectedOption = target.selectedOptions[0]; + } else if (target.options) { + selectedOption = target.options[target.options.selectedIndex]; + } + + return selectedOption.value; +} + +function setSelectedOption(target, value) { + if (!value) { + return; + } + + let i; + + for (i = 0; i < target.options.length; i++) { + const option = target.options[i]; + + if (option.value === value) { + break; + } + } + + target.selectedIndex = i; +} + /** * Manipulate settings of texttracks * @@ -67,16 +220,16 @@ class TextTrackSettings extends Component { * @method createEl */ createEl() { - let uniqueId = this.id_; - let dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId; - let dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId; + const uniqueId = this.id_; + const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId; + const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId; return super.createEl('div', { className: 'vjs-caption-settings vjs-modal-overlay', innerHTML: captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId), tabIndex: -1 }, { - role: 'dialog', + 'role': 'dialog', 'aria-labelledby': dialogLabelId, 'aria-describedby': dialogDescriptionId }); @@ -106,20 +259,21 @@ class TextTrackSettings extends Component { const bgOpacity = getSelectedOptionValue(this.$('.vjs-bg-opacity > select')); const windowColor = getSelectedOptionValue(this.$('.window-color > select')); const windowOpacity = getSelectedOptionValue(this.$('.vjs-window-opacity > select')); - const fontPercent = window['parseFloat'](getSelectedOptionValue(this.$('.vjs-font-percent > select'))); - - let result = { - 'backgroundOpacity': bgOpacity, - 'textOpacity': textOpacity, - 'windowOpacity': windowOpacity, - 'edgeStyle': textEdge, - 'fontFamily': fontFamily, - 'color': fgColor, - 'backgroundColor': bgColor, - 'windowColor': windowColor, - 'fontPercent': fontPercent + const fontPercent = window.parseFloat(getSelectedOptionValue(this.$('.vjs-font-percent > select'))); + + const result = { + fontPercent, + fontFamily, + textOpacity, + windowColor, + windowOpacity, + backgroundOpacity: bgOpacity, + edgeStyle: textEdge, + color: fgColor, + backgroundColor: bgColor }; - for (let name in result) { + + for (const name in result) { if (result[name] === '' || result[name] === 'none' || (name === 'fontPercent' && result[name] === 1.00)) { delete result[name]; } @@ -167,7 +321,8 @@ class TextTrackSettings extends Component { * @method restoreSettings */ restoreSettings() { - let err, values; + let err; + let values; try { [err, values] = safeParseTuple(window.localStorage.getItem('vjs-text-track-settings')); @@ -194,7 +349,8 @@ class TextTrackSettings extends Component { return; } - let values = this.getValues(); + const values = this.getValues(); + try { if (Object.getOwnPropertyNames(values).length > 0) { window.localStorage.setItem('vjs-text-track-settings', JSON.stringify(values)); @@ -212,7 +368,8 @@ class TextTrackSettings extends Component { * @method updateDisplay */ updateDisplay() { - let ttDisplay = this.player_.getChild('textTrackDisplay'); + const ttDisplay = this.player_.getChild('textTrackDisplay'); + if (ttDisplay) { ttDisplay.updateDisplay(); } @@ -222,154 +379,4 @@ class TextTrackSettings extends Component { Component.registerComponent('TextTrackSettings', TextTrackSettings); -function getSelectedOptionValue(target) { - let selectedOption; - // not all browsers support selectedOptions, so, fallback to options - if (target.selectedOptions) { - selectedOption = target.selectedOptions[0]; - } else if (target.options) { - selectedOption = target.options[target.options.selectedIndex]; - } - - return selectedOption.value; -} - -function setSelectedOption(target, value) { - if (!value) { - return; - } - - let i; - for (i = 0; i < target.options.length; i++) { - const option = target.options[i]; - if (option.value === value) { - break; - } - } - - target.selectedIndex = i; -} - -function captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId) { - - let template = ` -
-
Captions Settings Dialog
-
Beginning of dialog window. Escape will cancel and close the window.
-
-
-
- Text - - - - - - -
-
- Background - - - - - - -
-
- Window - - - - - - -
-
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
-
`; - - return template; -} - export default TextTrackSettings; diff --git a/src/js/tracks/text-track.js b/src/js/tracks/text-track.js index 582734f3d7..39ec67bc86 100644 --- a/src/js/tracks/text-track.js +++ b/src/js/tracks/text-track.js @@ -5,7 +5,6 @@ import TextTrackCueList from './text-track-cue-list'; import * as Fn from '../utils/fn.js'; import {TextTrackKind, TextTrackMode} from './track-enums'; import log from '../utils/log.js'; -import document from 'global/document'; import window from 'global/window'; import Track from './track.js'; import { isCrossOrigin } from '../utils/url.js'; @@ -20,10 +19,10 @@ import * as browser from '../utils/browser.js'; * @param {Track} track track to addcues to */ const parseCues = function(srcContent, track) { - let parser = new window.WebVTT.Parser(window, + const parser = new window.WebVTT.Parser(window, window.vttjs, window.WebVTT.StringDecoder()); - let errors = []; + const errors = []; parser.oncue = function(cue) { track.addCue(cue); @@ -42,12 +41,12 @@ const parseCues = function(srcContent, track) { parser.parse(srcContent); if (errors.length > 0) { - if (console.groupCollapsed) { - console.groupCollapsed(`Text Track parsing errors for ${track.src}`); + if (window.console && window.console.groupCollapsed) { + window.console.groupCollapsed(`Text Track parsing errors for ${track.src}`); } errors.forEach((error) => log.error(error)); - if (console.groupEnd) { - console.groupEnd(); + if (window.console && window.console.groupEnd) { + window.console.groupEnd(); } } @@ -61,10 +60,10 @@ const parseCues = function(srcContent, track) { * @param {Track} track track to addcues to */ const loadTrack = function(src, track) { - let opts = { + const opts = { uri: src }; - let crossOrigin = isCrossOrigin(src); + const crossOrigin = isCrossOrigin(src); if (crossOrigin) { opts.cors = crossOrigin; @@ -81,7 +80,8 @@ const loadTrack = function(src, track) { // NOTE: this is only used for the alt/video.novtt.js build if (typeof window.WebVTT !== 'function') { if (track.tech_) { - let loadHandler = () => parseCues(responseBody, track); + const loadHandler = () => parseCues(responseBody, track); + track.tech_.on('vttjsloaded', loadHandler); track.tech_.on('vttjserror', () => { log.error(`vttjs failed to load, stopping trying to process ${track.src}`); @@ -129,23 +129,24 @@ class TextTrack extends Track { throw new Error('A tech was not provided.'); } - let settings = merge(options, { + const settings = merge(options, { kind: TextTrackKind[options.kind] || 'subtitles', language: options.language || options.srclang || '' }); let mode = TextTrackMode[settings.mode] || 'disabled'; - let default_ = settings.default; + const default_ = settings.default; if (settings.kind === 'metadata' || settings.kind === 'chapters') { mode = 'hidden'; } // on IE8 this will be a document element // for every other browser this will be a normal object - let tt = super(settings); + const tt = super(settings); + tt.tech_ = settings.tech; if (browser.IS_IE8) { - for (let prop in TextTrack.prototype) { + for (const prop in TextTrack.prototype) { if (prop !== 'constructor') { tt[prop] = TextTrack.prototype[prop]; } @@ -155,11 +156,17 @@ class TextTrack extends Track { tt.cues_ = []; tt.activeCues_ = []; - let cues = new TextTrackCueList(tt.cues_); - let activeCues = new TextTrackCueList(tt.activeCues_); + const cues = new TextTrackCueList(tt.cues_); + const activeCues = new TextTrackCueList(tt.activeCues_); let changed = false; - let timeupdateHandler = Fn.bind(tt, function() { + const timeupdateHandler = Fn.bind(tt, function() { + + // Accessing this.activeCues for the side-effects of updating itself + // due to it's nature as a getter function. Do not remove or cues will + // stop updating! + /* eslint-disable no-unused-expressions */ this.activeCues; + /* eslint-enable no-unused-expressions */ if (changed) { this.trigger('cuechange'); changed = false; @@ -215,11 +222,11 @@ class TextTrack extends Track { return activeCues; } - let ct = this.tech_.currentTime(); - let active = []; + const ct = this.tech_.currentTime(); + const active = []; for (let i = 0, l = this.cues.length; i < l; i++) { - let cue = this.cues[i]; + const cue = this.cues[i]; if (cue.startTime <= ct && cue.endTime >= ct) { active.push(cue); @@ -267,7 +274,7 @@ class TextTrack extends Track { * @method addCue */ addCue(cue) { - let tracks = this.tech_.textTracks(); + const tracks = this.tech_.textTracks(); if (tracks) { for (let i = 0; i < tracks.length; i++) { @@ -291,7 +298,7 @@ class TextTrack extends Track { let removed = false; for (let i = 0, l = this.cues_.length; i < l; i++) { - let cue = this.cues_[i]; + const cue = this.cues_[i]; if (cue === removeCue) { this.cues_.splice(i, 1); diff --git a/src/js/tracks/track-enums.js b/src/js/tracks/track-enums.js index ecab4dd4b3..e11d0d1c81 100644 --- a/src/js/tracks/track-enums.js +++ b/src/js/tracks/track-enums.js @@ -21,7 +21,7 @@ const VideoTrackKind = { main: 'main', sign: 'sign', subtitles: 'subtitles', - commentary: 'commentary', + commentary: 'commentary' }; /** @@ -38,12 +38,12 @@ const VideoTrackKind = { * }; */ const AudioTrackKind = { - alternative: 'alternative', - descriptions: 'descriptions', - main: 'main', + 'alternative': 'alternative', + 'descriptions': 'descriptions', + 'main': 'main', 'main-desc': 'main-desc', - translation: 'translation', - commentary: 'commentary', + 'translation': 'translation', + 'commentary': 'commentary' }; /** @@ -65,8 +65,6 @@ const TextTrackKind = { metadata: 'metadata' }; - - /** * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode * @@ -78,9 +76,4 @@ const TextTrackMode = { showing: 'showing' }; -/* jshint ignore:start */ -// we ignore jshint here because it does not see -// AudioTrackKind as defined here export default { VideoTrackKind, AudioTrackKind, TextTrackKind, TextTrackMode }; -/* jshint ignore:end */ - diff --git a/src/js/tracks/track-list.js b/src/js/tracks/track-list.js index 3d15e164b3..f71df82f8b 100644 --- a/src/js/tracks/track-list.js +++ b/src/js/tracks/track-list.js @@ -2,7 +2,6 @@ * @file track-list.js */ import EventTarget from '../event-target'; -import * as Fn from '../utils/fn.js'; import * as browser from '../utils/browser.js'; import document from 'global/document'; @@ -20,10 +19,10 @@ class TrackList extends EventTarget { constructor(tracks = [], list = null) { super(); if (!list) { - list = this; + list = this; // eslint-disable-line if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in TrackList.prototype) { + for (const prop in TrackList.prototype) { if (prop !== 'constructor') { list[prop] = TrackList.prototype[prop]; } @@ -53,7 +52,7 @@ class TrackList extends EventTarget { * @private */ addTrack_(track) { - let index = this.tracks_.length; + const index = this.tracks_.length; if (!('' + index in this)) { Object.defineProperty(this, index, { @@ -118,7 +117,8 @@ class TrackList extends EventTarget { let result = null; for (let i = 0, l = this.length; i < l; i++) { - let track = this[i]; + const track = this[i]; + if (track.id === id) { result = track; break; @@ -141,7 +141,7 @@ TrackList.prototype.allowedEvents_ = { }; // emulate attribute EventHandler support to allow for feature detection -for (let event in TrackList.prototype.allowedEvents_) { +for (const event in TrackList.prototype.allowedEvents_) { TrackList.prototype['on' + event] = null; } diff --git a/src/js/tracks/track.js b/src/js/tracks/track.js index 8c8a115350..401ad7193b 100644 --- a/src/js/tracks/track.js +++ b/src/js/tracks/track.js @@ -19,26 +19,29 @@ class Track extends EventTarget { constructor(options = {}) { super(); - let track = this; + let track = this; // eslint-disable-line + if (browser.IS_IE8) { track = document.createElement('custom'); - for (let prop in Track.prototype) { + for (const prop in Track.prototype) { if (prop !== 'constructor') { track[prop] = Track.prototype[prop]; } } } - let trackProps = { + const trackProps = { id: options.id || 'vjs_track_' + Guid.newGUID(), kind: options.kind || '', label: options.label || '', language: options.language || '' }; - for (let key in trackProps) { + for (const key in trackProps) { Object.defineProperty(track, key, { - get() { return trackProps[key]; }, + get() { + return trackProps[key]; + }, set() {} }); } diff --git a/src/js/tracks/video-track-list.js b/src/js/tracks/video-track-list.js index 89e7cdbfa3..9e68587beb 100644 --- a/src/js/tracks/video-track-list.js +++ b/src/js/tracks/video-track-list.js @@ -59,12 +59,12 @@ class VideoTrackList extends TrackList { // as it does not support Object.defineProperty properly if (browser.IS_IE8) { list = document.createElement('custom'); - for (let prop in TrackList.prototype) { + for (const prop in TrackList.prototype) { if (prop !== 'constructor') { list[prop] = TrackList.prototype[prop]; } } - for (let prop in VideoTrackList.prototype) { + for (const prop in VideoTrackList.prototype) { if (prop !== 'constructor') { list[prop] = VideoTrackList.prototype[prop]; } diff --git a/src/js/tracks/video-track.js b/src/js/tracks/video-track.js index fe8f172f53..eafa84f930 100644 --- a/src/js/tracks/video-track.js +++ b/src/js/tracks/video-track.js @@ -20,17 +20,17 @@ import * as browser from '../utils/browser.js'; */ class VideoTrack extends Track { constructor(options = {}) { - let settings = merge(options, { + const settings = merge(options, { kind: VideoTrackKind[options.kind] || '' }); // on IE8 this will be a document element // for every other browser this will be a normal object - let track = super(settings); + const track = super(settings); let selected = false; if (browser.IS_IE8) { - for (let prop in VideoTrack.prototype) { + for (const prop in VideoTrack.prototype) { if (prop !== 'constructor') { track[prop] = VideoTrack.prototype[prop]; } @@ -38,7 +38,9 @@ class VideoTrack extends Track { } Object.defineProperty(track, 'selected', { - get() { return selected; }, + get() { + return selected; + }, set(newSelected) { // an invalid or unchanged value if (typeof newSelected !== 'boolean' || newSelected === selected) { diff --git a/src/js/utils/browser.js b/src/js/utils/browser.js index 9bad972b40..72e52daff0 100644 --- a/src/js/utils/browser.js +++ b/src/js/utils/browser.js @@ -24,34 +24,35 @@ export const IS_IPHONE = (/iPhone/i).test(USER_AGENT) && !IS_IPAD; export const IS_IPOD = (/iPod/i).test(USER_AGENT); export const IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD; -export const IOS_VERSION = (function(){ - var match = USER_AGENT.match(/OS (\d+)_/i); - if (match && match[1]) { return match[1]; } -})(); +export const IOS_VERSION = (function() { + const match = USER_AGENT.match(/OS (\d+)_/i); + + if (match && match[1]) { + return match[1]; + } +}()); export const IS_ANDROID = (/Android/i).test(USER_AGENT); export const ANDROID_VERSION = (function() { // This matches Android Major.Minor.Patch versions // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned - var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i), - major, - minor; + const match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i); if (!match) { return null; } - major = match[1] && parseFloat(match[1]); - minor = match[2] && parseFloat(match[2]); + const major = match[1] && parseFloat(match[1]); + const minor = match[2] && parseFloat(match[2]); if (major && minor) { return parseFloat(match[1] + '.' + match[2]); } else if (major) { return major; - } else { - return null; } -})(); + return null; +}()); + // Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser export const IS_OLD_ANDROID = IS_ANDROID && (/webkit/i).test(USER_AGENT) && ANDROID_VERSION < 2.3; export const IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537; @@ -60,9 +61,9 @@ export const IS_FIREFOX = (/Firefox/i).test(USER_AGENT); export const IS_EDGE = (/Edge/i).test(USER_AGENT); export const IS_CHROME = !IS_EDGE && (/Chrome/i).test(USER_AGENT); export const IS_IE8 = (/MSIE\s8\.0/).test(USER_AGENT); -export const IE_VERSION = (function(result){ +export const IE_VERSION = (function(result) { return result && parseFloat(result[1]); -})((/MSIE\s(\d+)\.\d/).exec(USER_AGENT)); +}((/MSIE\s(\d+)\.\d/).exec(USER_AGENT))); export const TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch); export const BACKGROUND_SIZE_SUPPORTED = 'backgroundSize' in document.createElement('video').style; diff --git a/src/js/utils/buffer.js b/src/js/utils/buffer.js index 063a9ec95c..4a303aaa25 100644 --- a/src/js/utils/buffer.js +++ b/src/js/utils/buffer.js @@ -13,8 +13,9 @@ import { createTimeRange } from './time-ranges.js'; * @function bufferedPercent */ export function bufferedPercent(buffered, duration) { - var bufferedDuration = 0, - start, end; + let bufferedDuration = 0; + let start; + let end; if (!duration) { return 0; @@ -24,9 +25,9 @@ export function bufferedPercent(buffered, duration) { buffered = createTimeRange(0, 0); } - for (let i = 0; i < buffered.length; i++){ + for (let i = 0; i < buffered.length; i++) { start = buffered.start(i); - end = buffered.end(i); + end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction if (end > duration) { diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index 3d8fb51679..66fe9f597a 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -3,7 +3,7 @@ */ import document from 'global/document'; import window from 'global/window'; -import * as Guid from './guid.js'; +import * as Guid from './guid.js'; import log from './log.js'; import tsml from 'tsml'; @@ -14,7 +14,7 @@ import tsml from 'tsml'; * @return {Boolean} */ function isNonBlankString(str) { - return typeof str === 'string' && /\S/.test(str); + return typeof str === 'string' && (/\S/).test(str); } /** @@ -25,7 +25,7 @@ function isNonBlankString(str) { * @return {Boolean} */ function throwIfWhitespace(str) { - if (/\s/.test(str)) { + if ((/\s/).test(str)) { throw new Error('class has illegal whitespace characters'); } } @@ -40,6 +40,17 @@ function classRegExp(className) { return new RegExp('(^|\\s)' + className + '($|\\s)'); } +/** + * Determines, via duck typing, whether or not a value is a DOM element. + * + * @function isEl + * @param {Mixed} value + * @return {Boolean} + */ +export function isEl(value) { + return !!value && typeof value === 'object' && value.nodeType === 1; +} + /** * Creates functions to query the DOM using a given method. * @@ -49,7 +60,7 @@ function classRegExp(className) { * @return {Function} */ function createQuerier(method) { - return function (selector, context) { + return function(selector, context) { if (!isNonBlankString(selector)) { return document[method](null); } @@ -68,7 +79,7 @@ function createQuerier(method) { * @return {Element} Element with supplied ID * @function getEl */ -export function getEl(id){ +export function getEl(id) { if (id.indexOf('#') === 0) { id = id.slice(1); } @@ -85,11 +96,11 @@ export function getEl(id){ * @return {Element} * @function createEl */ -export function createEl(tagName='div', properties={}, attributes={}){ - let el = document.createElement(tagName); +export function createEl(tagName = 'div', properties = {}, attributes = {}) { + const el = document.createElement(tagName); - Object.getOwnPropertyNames(properties).forEach(function(propName){ - let val = properties[propName]; + Object.getOwnPropertyNames(properties).forEach(function(propName) { + const val = properties[propName]; // See #2176 // We originally were accepting both properties and attributes in the @@ -104,8 +115,7 @@ export function createEl(tagName='div', properties={}, attributes={}){ } }); - Object.getOwnPropertyNames(attributes).forEach(function(attrName){ - let val = attributes[attrName]; + Object.getOwnPropertyNames(attributes).forEach(function(attrName) { el.setAttribute(attrName, attributes[attrName]); }); @@ -136,7 +146,7 @@ export function textContent(el, text) { * @private * @function insertElFirst */ -export function insertElFirst(child, parent){ +export function insertElFirst(child, parent) { if (parent.firstChild) { parent.insertBefore(child, parent.firstChild); } else { @@ -210,7 +220,7 @@ export function hasElData(el) { * @function removeElData */ export function removeElData(el) { - let id = el[elIdAttr]; + const id = el[elIdAttr]; if (!id) { return; @@ -222,7 +232,7 @@ export function removeElData(el) { // Remove the elIdAttr property from the DOM node try { delete el[elIdAttr]; - } catch(e) { + } catch (e) { if (el.removeAttribute) { el.removeAttribute(elIdAttr); } else { @@ -242,10 +252,9 @@ export function removeElData(el) { export function hasElClass(element, classToCheck) { if (element.classList) { return element.classList.contains(classToCheck); - } else { - throwIfWhitespace(classToCheck); - return classRegExp(classToCheck).test(element.className); } + throwIfWhitespace(classToCheck); + return classRegExp(classToCheck).test(element.className); } /** @@ -305,7 +314,7 @@ export function toggleElClass(element, classToToggle, predicate) { // This CANNOT use `classList` internally because IE does not support the // second parameter to the `classList.toggle()` method! Which is fine because // `classList` will be used by the add/remove functions. - let has = hasElClass(element, classToToggle); + const has = hasElClass(element, classToToggle); if (typeof predicate === 'function') { predicate = predicate(element, classToToggle); @@ -339,8 +348,8 @@ export function toggleElClass(element, classToToggle, predicate) { * @function setElAttributes */ export function setElAttributes(el, attributes) { - Object.getOwnPropertyNames(attributes).forEach(function(attrName){ - let attrValue = attributes[attrName]; + Object.getOwnPropertyNames(attributes).forEach(function(attrName) { + const attrValue = attributes[attrName]; if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) { el.removeAttribute(attrName); @@ -362,25 +371,23 @@ export function setElAttributes(el, attributes) { * @function getElAttributes */ export function getElAttributes(tag) { - var obj, knownBooleans, attrs, attrName, attrVal; - - obj = {}; + const obj = {}; // known boolean attributes // we can check for matching boolean properties, but older browsers // won't know about HTML5 boolean attributes that we still read from - knownBooleans = ','+'autoplay,controls,loop,muted,default'+','; + const knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ','; if (tag && tag.attributes && tag.attributes.length > 0) { - attrs = tag.attributes; + const attrs = tag.attributes; - for (var i = attrs.length - 1; i >= 0; i--) { - attrName = attrs[i].name; - attrVal = attrs[i].value; + for (let i = attrs.length - 1; i >= 0; i--) { + const attrName = attrs[i].name; + let attrVal = attrs[i].value; // check for known booleans // the matching element property will return a value for typeof - if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(','+attrName+',') !== -1) { + if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) { // the value of an included boolean attribute is typically an empty // string ('') which would equal false if we just check for a false value. // we also don't want support bad code like autoplay='false' @@ -471,13 +478,13 @@ export function findElPosition(el) { * @return {Object} This object will have x and y coordinates corresponding to the mouse position */ export function getPointerPosition(el, event) { - let position = {}; - let box = findElPosition(el); - let boxW = el.offsetWidth; - let boxH = el.offsetHeight; + const position = {}; + const box = findElPosition(el); + const boxW = el.offsetWidth; + const boxH = el.offsetHeight; - let boxY = box.top; - let boxX = box.left; + const boxY = box.top; + const boxX = box.left; let pageY = event.pageY; let pageX = event.pageX; @@ -492,17 +499,6 @@ export function getPointerPosition(el, event) { return position; } -/** - * Determines, via duck typing, whether or not a value is a DOM element. - * - * @function isEl - * @param {Mixed} value - * @return {Boolean} - */ -export function isEl(value) { - return !!value && typeof value === 'object' && value.nodeType === 1; -} - /** * Determines, via duck typing, whether or not a value is a text node. * @@ -577,7 +573,7 @@ export function normalizeContent(content) { return value; } - if (typeof value === 'string' && /\S/.test(value)) { + if (typeof value === 'string' && (/\S/).test(value)) { return document.createTextNode(value); } }).filter(value => value); diff --git a/src/js/utils/events.js b/src/js/utils/events.js index 4c9ca211d1..febd27cc27 100644 --- a/src/js/utils/events.js +++ b/src/js/utils/events.js @@ -7,11 +7,193 @@ * robust as jquery's, so there's probably some differences. */ -import * as Dom from './dom.js'; -import * as Guid from './guid.js'; +import * as Dom from './dom.js'; +import * as Guid from './guid.js'; import window from 'global/window'; import document from 'global/document'; +/** + * Clean up the listener cache and dispatchers +* + * @param {Element|Object} elem Element to clean up + * @param {String} type Type of event to clean up + * @private + * @method _cleanUpEvents + */ +function _cleanUpEvents(elem, type) { + const data = Dom.getElData(elem); + + // Remove the events of a particular type if there are none left + if (data.handlers[type].length === 0) { + delete data.handlers[type]; + // data.handlers[type] = null; + // Setting to null was causing an error with data.handlers + + // Remove the meta-handler from the element + if (elem.removeEventListener) { + elem.removeEventListener(type, data.dispatcher, false); + } else if (elem.detachEvent) { + elem.detachEvent('on' + type, data.dispatcher); + } + } + + // Remove the events object if there are no types left + if (Object.getOwnPropertyNames(data.handlers).length <= 0) { + delete data.handlers; + delete data.dispatcher; + delete data.disabled; + } + + // Finally remove the element data if there is no data left + if (Object.getOwnPropertyNames(data).length === 0) { + Dom.removeElData(elem); + } +} + +/** + * Loops through an array of event types and calls the requested method for each type. + * + * @param {Function} fn The event method we want to use. + * @param {Element|Object} elem Element or object to bind listeners to + * @param {String} type Type of event to bind to. + * @param {Function} callback Event listener. + * @private + * @function _handleMultipleEvents + */ +function _handleMultipleEvents(fn, elem, types, callback) { + types.forEach(function(type) { + // Call the event method for each one of the types + fn(elem, type, callback); + }); +} + +/** + * Fix a native event to have standard property values + * + * @param {Object} event Event object to fix + * @return {Object} + * @private + * @method fixEvent + */ +export function fixEvent(event) { + + function returnTrue() { + return true; + } + + function returnFalse() { + return false; + } + + // Test if fixing up is needed + // Used to check if !event.stopPropagation instead of isPropagationStopped + // But native events return true for stopPropagation, but don't have + // other expected methods like isPropagationStopped. Seems to be a problem + // with the Javascript Ninja code. So we're just overriding all events now. + if (!event || !event.isPropagationStopped) { + const old = event || window.event; + + event = {}; + // Clone the old object so that we can modify the values event = {}; + // IE8 Doesn't like when you mess with native event properties + // Firefox returns false for event.hasOwnProperty('type') and other props + // which makes copying more difficult. + // TODO: Probably best to create a whitelist of event props + for (const key in old) { + // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y + // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation + // and webkitMovementX/Y + if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && + key !== 'webkitMovementX' && key !== 'webkitMovementY') { + // Chrome 32+ warns if you try to copy deprecated returnValue, but + // we still want to if preventDefault isn't supported (IE8). + if (!(key === 'returnValue' && old.preventDefault)) { + event[key] = old[key]; + } + } + } + + // The event occurred on this element + if (!event.target) { + event.target = event.srcElement || document; + } + + // Handle which other element the event is related to + if (!event.relatedTarget) { + event.relatedTarget = event.fromElement === event.target ? + event.toElement : + event.fromElement; + } + + // Stop the default browser action + event.preventDefault = function() { + if (old.preventDefault) { + old.preventDefault(); + } + event.returnValue = false; + old.returnValue = false; + event.defaultPrevented = true; + }; + + event.defaultPrevented = false; + + // Stop the event from bubbling + event.stopPropagation = function() { + if (old.stopPropagation) { + old.stopPropagation(); + } + event.cancelBubble = true; + old.cancelBubble = true; + event.isPropagationStopped = returnTrue; + }; + + event.isPropagationStopped = returnFalse; + + // Stop the event from bubbling and executing other handlers + event.stopImmediatePropagation = function() { + if (old.stopImmediatePropagation) { + old.stopImmediatePropagation(); + } + event.isImmediatePropagationStopped = returnTrue; + event.stopPropagation(); + }; + + event.isImmediatePropagationStopped = returnFalse; + + // Handle mouse position + if (event.clientX !== null && event.clientX !== undefined) { + const doc = document.documentElement; + const body = document.body; + + event.pageX = event.clientX + + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - + (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + + (doc && doc.scrollTop || body && body.scrollTop || 0) - + (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Handle key presses + event.which = event.charCode || event.keyCode; + + // Fix button for mouse clicks: + // 0 == left; 1 == middle; 2 == right + if (event.button !== null && event.button !== undefined) { + + // The following is disabled because it does not pass videojs-standard + // and... yikes. + /* eslint-disable */ + event.button = (event.button & 1 ? 0 : + (event.button & 4 ? 1 : + (event.button & 2 ? 2 : 0))); + /* eslint-enable */ + } + } + + // Returns fixed-up instance + return event; +} + /** * Add an event listener to element * It stores the handler function in a separate cache object @@ -23,37 +205,46 @@ import document from 'global/document'; * @param {Function} fn Event listener. * @method on */ -export function on(elem, type, fn){ +export function on(elem, type, fn) { if (Array.isArray(type)) { return _handleMultipleEvents(on, elem, type, fn); } - let data = Dom.getElData(elem); + const data = Dom.getElData(elem); // We need a place to store all our handler data - if (!data.handlers) data.handlers = {}; + if (!data.handlers) { + data.handlers = {}; + } - if (!data.handlers[type]) data.handlers[type] = []; + if (!data.handlers[type]) { + data.handlers[type] = []; + } - if (!fn.guid) fn.guid = Guid.newGUID(); + if (!fn.guid) { + fn.guid = Guid.newGUID(); + } data.handlers[type].push(fn); if (!data.dispatcher) { data.disabled = false; - data.dispatcher = function (event, hash){ + data.dispatcher = function(event, hash) { + + if (data.disabled) { + return; + } - if (data.disabled) return; event = fixEvent(event); - var handlers = data.handlers[event.type]; + const handlers = data.handlers[event.type]; if (handlers) { // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off. - var handlersCopy = handlers.slice(0); + const handlersCopy = handlers.slice(0); - for (var m = 0, n = handlersCopy.length; m < n; m++) { + for (let m = 0, n = handlersCopy.length; m < n; m++) { if (event.isImmediatePropagationStopped()) { break; } else { @@ -83,33 +274,41 @@ export function on(elem, type, fn){ */ export function off(elem, type, fn) { // Don't want to add a cache object through getElData if not needed - if (!Dom.hasElData(elem)) return; + if (!Dom.hasElData(elem)) { + return; + } - let data = Dom.getElData(elem); + const data = Dom.getElData(elem); // If no events exist, nothing to unbind - if (!data.handlers) { return; } + if (!data.handlers) { + return; + } if (Array.isArray(type)) { return _handleMultipleEvents(off, elem, type, fn); } // Utility function - var removeType = function(t){ - data.handlers[t] = []; - _cleanUpEvents(elem,t); + const removeType = function(t) { + data.handlers[t] = []; + _cleanUpEvents(elem, t); }; // Are we removing all bound events? if (!type) { - for (let t in data.handlers) removeType(t); + for (const t in data.handlers) { + removeType(t); + } return; } - var handlers = data.handlers[type]; + const handlers = data.handlers[type]; // If no handlers exist, nothing to unbind - if (!handlers) return; + if (!handlers) { + return; + } // If no listener was provided, remove all listeners for type if (!fn) { @@ -142,14 +341,14 @@ export function trigger(elem, event, hash) { // Fetches element data and a reference to the parent (for bubbling). // Don't want to add a data object to cache for every parent, // so checking hasElData first. - var elemData = (Dom.hasElData(elem)) ? Dom.getElData(elem) : {}; - var parent = elem.parentNode || elem.ownerDocument; + const elemData = (Dom.hasElData(elem)) ? Dom.getElData(elem) : {}; + const parent = elem.parentNode || elem.ownerDocument; // type = event.type || event, // handler; // If an event name was passed as a string, creates an event out of it if (typeof event === 'string') { - event = { type:event, target:elem }; + event = {type: event, target: elem}; } // Normalizes the event properties. event = fixEvent(event); @@ -160,13 +359,13 @@ export function trigger(elem, event, hash) { } // Unless explicitly stopped or the event does not bubble (e.g. media events) - // recursively calls this function to bubble the event up the DOM. - if (parent && !event.isPropagationStopped() && event.bubbles === true) { - trigger.call(null, parent, event, hash); + // recursively calls this function to bubble the event up the DOM. + if (parent && !event.isPropagationStopped() && event.bubbles === true) { + trigger.call(null, parent, event, hash); // If at the top of the DOM, triggers the default action unless disabled. } else if (!parent && !event.defaultPrevented) { - var targetData = Dom.getElData(event.target); + const targetData = Dom.getElData(event.target); // Checks if the target has a default action for this event. if (event.target[event.type]) { @@ -197,182 +396,12 @@ export function one(elem, type, fn) { if (Array.isArray(type)) { return _handleMultipleEvents(one, elem, type, fn); } - var func = function(){ + const func = function() { off(elem, type, func); fn.apply(this, arguments); }; + // copy the guid to the new function so it can removed using the original function's ID func.guid = fn.guid = fn.guid || Guid.newGUID(); on(elem, type, func); } - -/** - * Fix a native event to have standard property values - * - * @param {Object} event Event object to fix - * @return {Object} - * @private - * @method fixEvent - */ -export function fixEvent(event) { - - function returnTrue() { return true; } - function returnFalse() { return false; } - - // Test if fixing up is needed - // Used to check if !event.stopPropagation instead of isPropagationStopped - // But native events return true for stopPropagation, but don't have - // other expected methods like isPropagationStopped. Seems to be a problem - // with the Javascript Ninja code. So we're just overriding all events now. - if (!event || !event.isPropagationStopped) { - var old = event || window.event; - - event = {}; - // Clone the old object so that we can modify the values event = {}; - // IE8 Doesn't like when you mess with native event properties - // Firefox returns false for event.hasOwnProperty('type') and other props - // which makes copying more difficult. - // TODO: Probably best to create a whitelist of event props - for (var key in old) { - // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y - // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation - // and webkitMovementX/Y - if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && - key !== 'webkitMovementX' && key !== 'webkitMovementY') { - // Chrome 32+ warns if you try to copy deprecated returnValue, but - // we still want to if preventDefault isn't supported (IE8). - if (!(key === 'returnValue' && old.preventDefault)) { - event[key] = old[key]; - } - } - } - - // The event occurred on this element - if (!event.target) { - event.target = event.srcElement || document; - } - - // Handle which other element the event is related to - if (!event.relatedTarget) { - event.relatedTarget = event.fromElement === event.target ? - event.toElement : - event.fromElement; - } - - // Stop the default browser action - event.preventDefault = function () { - if (old.preventDefault) { - old.preventDefault(); - } - event.returnValue = false; - old.returnValue = false; - event.defaultPrevented = true; - }; - - event.defaultPrevented = false; - - // Stop the event from bubbling - event.stopPropagation = function () { - if (old.stopPropagation) { - old.stopPropagation(); - } - event.cancelBubble = true; - old.cancelBubble = true; - event.isPropagationStopped = returnTrue; - }; - - event.isPropagationStopped = returnFalse; - - // Stop the event from bubbling and executing other handlers - event.stopImmediatePropagation = function () { - if (old.stopImmediatePropagation) { - old.stopImmediatePropagation(); - } - event.isImmediatePropagationStopped = returnTrue; - event.stopPropagation(); - }; - - event.isImmediatePropagationStopped = returnFalse; - - // Handle mouse position - if (event.clientX != null) { - var doc = document.documentElement, body = document.body; - - event.pageX = event.clientX + - (doc && doc.scrollLeft || body && body.scrollLeft || 0) - - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = event.clientY + - (doc && doc.scrollTop || body && body.scrollTop || 0) - - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Handle key presses - event.which = event.charCode || event.keyCode; - - // Fix button for mouse clicks: - // 0 == left; 1 == middle; 2 == right - if (event.button != null) { - event.button = (event.button & 1 ? 0 : - (event.button & 4 ? 1 : - (event.button & 2 ? 2 : 0))); - } - } - - // Returns fixed-up instance - return event; -} - -/** - * Clean up the listener cache and dispatchers -* - * @param {Element|Object} elem Element to clean up - * @param {String} type Type of event to clean up - * @private - * @method _cleanUpEvents - */ -function _cleanUpEvents(elem, type) { - var data = Dom.getElData(elem); - - // Remove the events of a particular type if there are none left - if (data.handlers[type].length === 0) { - delete data.handlers[type]; - // data.handlers[type] = null; - // Setting to null was causing an error with data.handlers - - // Remove the meta-handler from the element - if (elem.removeEventListener) { - elem.removeEventListener(type, data.dispatcher, false); - } else if (elem.detachEvent) { - elem.detachEvent('on' + type, data.dispatcher); - } - } - - // Remove the events object if there are no types left - if (Object.getOwnPropertyNames(data.handlers).length <= 0) { - delete data.handlers; - delete data.dispatcher; - delete data.disabled; - } - - // Finally remove the element data if there is no data left - if (Object.getOwnPropertyNames(data).length === 0) { - Dom.removeElData(elem); - } -} - -/** - * Loops through an array of event types and calls the requested method for each type. - * - * @param {Function} fn The event method we want to use. - * @param {Element|Object} elem Element or object to bind listeners to - * @param {String} type Type of event to bind to. - * @param {Function} callback Event listener. - * @private - * @function _handleMultipleEvents - */ -function _handleMultipleEvents(fn, elem, types, callback) { - types.forEach(function(type) { - //Call the event method for each one of the types - fn(elem, type, callback); - }); -} diff --git a/src/js/utils/fn.js b/src/js/utils/fn.js index 69bbe83ad8..a492fe1d50 100644 --- a/src/js/utils/fn.js +++ b/src/js/utils/fn.js @@ -16,10 +16,12 @@ import { newGUID } from './guid.js'; */ export const bind = function(context, fn, uid) { // Make sure the function has a unique ID - if (!fn.guid) { fn.guid = newGUID(); } + if (!fn.guid) { + fn.guid = newGUID(); + } // Create the new function that changes the context - let ret = function() { + const ret = function() { return fn.apply(context, arguments); }; diff --git a/src/js/utils/format-time.js b/src/js/utils/format-time.js index dc6182c9c4..bd47c68079 100644 --- a/src/js/utils/format-time.js +++ b/src/js/utils/format-time.js @@ -11,7 +11,7 @@ * @private * @function formatTime */ -function formatTime(seconds, guide=seconds) { +function formatTime(seconds, guide = seconds) { seconds = seconds < 0 ? 0 : seconds; let s = Math.floor(seconds % 60); let m = Math.floor(seconds / 60 % 60); diff --git a/src/js/utils/guid.js b/src/js/utils/guid.js index e832e7088b..3a124d8fc7 100644 --- a/src/js/utils/guid.js +++ b/src/js/utils/guid.js @@ -10,7 +10,7 @@ let _guid = 1; /** * Get the next unique ID * - * @return {String} + * @return {String} * @function newGUID */ export function newGUID() { diff --git a/src/js/utils/log.js b/src/js/utils/log.js index 332cda6e73..fca15985b2 100644 --- a/src/js/utils/log.js +++ b/src/js/utils/log.js @@ -4,6 +4,8 @@ import window from 'global/window'; import {IE_VERSION} from './browser'; +let log; + /** * Log messages to the console and history based on the type of message * @@ -16,7 +18,6 @@ import {IE_VERSION} from './browser'; * but this is exposed as a parameter to facilitate testing. */ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 11) => { - const console = window.console; // If there's no console then don't try to output messages, but they will // still be stored in `log.history`. @@ -24,7 +25,7 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1 // Was setting these once outside of this function, but containing them // in the function makes it easier to test cases where console doesn't exist // when the module is executed. - const fn = console && console[type] || function(){}; + const fn = window.console && window.console[type] || function() {}; if (type !== 'log') { @@ -45,7 +46,9 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1 if (a && typeof a === 'object' || Array.isArray(a)) { try { return JSON.stringify(a); - } catch (x) {} + } catch (x) { + return String(a); + } } // Cast to string before joining, so we get null and undefined explicitly @@ -68,9 +71,9 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1 * * @function log */ -function log(...args) { +log = function(...args) { logByType('log', args); -} +}; /** * Keep a history of log messages @@ -93,5 +96,4 @@ log.error = (...args) => logByType('error', args); */ log.warn = (...args) => logByType('warn', args); - export default log; diff --git a/src/js/utils/merge-options.js b/src/js/utils/merge-options.js index ac3558a9ed..91dea8fb9a 100644 --- a/src/js/utils/merge-options.js +++ b/src/js/utils/merge-options.js @@ -4,10 +4,10 @@ import merge from 'lodash-compat/object/merge'; function isPlain(obj) { - return !!obj - && typeof obj === 'object' - && obj.toString() === '[object Object]' - && obj.constructor === Object; + return !!obj && + typeof obj === 'object' && + obj.toString() === '[object Object]' && + obj.constructor === Object; } /** @@ -15,7 +15,7 @@ function isPlain(obj) { * (like arrays) instead of attempting to overlay them. * @see https://lodash.com/docs#merge */ -const customizer = function(destination, source) { +function customizer(destination, source) { // If we're not working with a plain object, copy the value as is // If source is an array, for instance, it will replace destination if (!isPlain(source)) { @@ -30,7 +30,7 @@ const customizer = function(destination, source) { if (!isPlain(destination)) { return mergeOptions(source); } -}; +} /** * Merge one or more options objects, recursively merging **only** @@ -44,7 +44,7 @@ const customizer = function(destination, source) { export default function mergeOptions() { // contruct the call dynamically to handle the variable number of // objects to merge - let args = Array.prototype.slice.call(arguments); + const args = Array.prototype.slice.call(arguments); // unshift an empty object into the front of the call as the target // of the merge diff --git a/src/js/utils/stylesheet.js b/src/js/utils/stylesheet.js index b06f2bc3d8..bef2da4081 100644 --- a/src/js/utils/stylesheet.js +++ b/src/js/utils/stylesheet.js @@ -1,13 +1,14 @@ import document from 'global/document'; -export let createStyleElement = function(className) { - let style = document.createElement('style'); +export const createStyleElement = function(className) { + const style = document.createElement('style'); + style.className = className; return style; }; -export let setTextContent = function(el, content) { +export const setTextContent = function(el, content) { if (el.styleSheet) { el.styleSheet.cssText = content; } else { diff --git a/src/js/utils/time-ranges.js b/src/js/utils/time-ranges.js index 1f17619665..f81c71db1c 100644 --- a/src/js/utils/time-ranges.js +++ b/src/js/utils/time-ranges.js @@ -1,37 +1,28 @@ import log from './log.js'; -/** - * @file time-ranges.js - * - * Should create a fake TimeRange object - * Mimics an HTML5 time range instance, which has functions that - * return the start and end times for a range - * TimeRanges are returned by the buffered() method - * - * @param {(Number|Array)} Start of a single range or an array of ranges - * @param {Number} End of a single range - * @private - * @method createTimeRanges - */ -export function createTimeRanges(start, end){ - if (Array.isArray(start)) { - return createTimeRangesObj(start); - } else if (start === undefined || end === undefined) { - return createTimeRangesObj(); +function rangeCheck(fnName, index, maxIndex) { + if (index < 0 || index > maxIndex) { + throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is greater than or equal to the maximum bound (${maxIndex}).`); } - return createTimeRangesObj([[start, end]]); } -export { createTimeRanges as createTimeRange }; +function getRange(fnName, valueIndex, ranges, rangeIndex) { + if (rangeIndex === undefined) { + log.warn(`DEPRECATED: Function '${fnName}' on 'TimeRanges' called without an index argument.`); + rangeIndex = 0; + } + rangeCheck(fnName, rangeIndex, ranges.length - 1); + return ranges[rangeIndex][valueIndex]; +} -function createTimeRangesObj(ranges){ +function createTimeRangesObj(ranges) { if (ranges === undefined || ranges.length === 0) { return { length: 0, - start: function() { + start() { throw new Error('This TimeRanges object is empty'); }, - end: function() { + end() { throw new Error('This TimeRanges object is empty'); } }; @@ -43,17 +34,26 @@ function createTimeRangesObj(ranges){ }; } -function getRange(fnName, valueIndex, ranges, rangeIndex){ - if (rangeIndex === undefined) { - log.warn(`DEPRECATED: Function '${fnName}' on 'TimeRanges' called without an index argument.`); - rangeIndex = 0; +/** + * @file time-ranges.js + * + * Should create a fake TimeRange object + * Mimics an HTML5 time range instance, which has functions that + * return the start and end times for a range + * TimeRanges are returned by the buffered() method + * + * @param {(Number|Array)} Start of a single range or an array of ranges + * @param {Number} End of a single range + * @private + * @method createTimeRanges + */ +export function createTimeRanges(start, end) { + if (Array.isArray(start)) { + return createTimeRangesObj(start); + } else if (start === undefined || end === undefined) { + return createTimeRangesObj(); } - rangeCheck(fnName, rangeIndex, ranges.length - 1); - return ranges[rangeIndex][valueIndex]; + return createTimeRangesObj([[start, end]]); } -function rangeCheck(fnName, index, maxIndex){ - if (index < 0 || index > maxIndex) { - throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is greater than or equal to the maximum bound (${maxIndex}).`); - } -} +export { createTimeRanges as createTimeRange }; diff --git a/src/js/utils/to-title-case.js b/src/js/utils/to-title-case.js index f3984f8190..c9f97b88e1 100644 --- a/src/js/utils/to-title-case.js +++ b/src/js/utils/to-title-case.js @@ -8,7 +8,7 @@ * @private * @method toTitleCase */ -function toTitleCase(string){ +function toTitleCase(string) { return string.charAt(0).toUpperCase() + string.slice(1); } diff --git a/src/js/utils/url.js b/src/js/utils/url.js index 6532c28fe4..d294273583 100644 --- a/src/js/utils/url.js +++ b/src/js/utils/url.js @@ -16,13 +16,15 @@ export const parseUrl = function(url) { // add the url to an anchor and let the browser parse the URL let a = document.createElement('a'); + a.href = url; // IE8 (and 9?) Fix // ie8 doesn't parse the URL correctly until the anchor is actually // added to the body, and an innerHTML is needed to trigger the parsing - let addToBody = (a.host === '' && a.protocol !== 'file:'); + const addToBody = (a.host === '' && a.protocol !== 'file:'); let div; + if (addToBody) { div = document.createElement('div'); div.innerHTML = ``; @@ -35,8 +37,9 @@ export const parseUrl = function(url) { // Copy the specific URL properties to a new object // This is also needed for IE8 because the anchor loses its // properties when it's removed from the dom - let details = {}; - for (var i = 0; i < props.length; i++) { + const details = {}; + + for (let i = 0; i < props.length; i++) { details[props[i]] = a[props[i]]; } @@ -45,6 +48,7 @@ export const parseUrl = function(url) { if (details.protocol === 'http:') { details.host = details.host.replace(/:80$/, ''); } + if (details.protocol === 'https:') { details.host = details.host.replace(/:443$/, ''); } @@ -65,11 +69,12 @@ export const parseUrl = function(url) { * @private * @method getAbsoluteURL */ -export const getAbsoluteURL = function(url){ +export const getAbsoluteURL = function(url) { // Check if absolute URL if (!url.match(/^https?:\/\//)) { // Convert to absolute URL. Flash hosted off-site needs an absolute URL. - let div = document.createElement('div'); + const div = document.createElement('div'); + div.innerHTML = `x`; url = div.firstChild.href; } @@ -85,9 +90,9 @@ export const getAbsoluteURL = function(url){ * @method getFileExtension */ export const getFileExtension = function(path) { - if(typeof path === 'string'){ - let splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i; - let pathParts = splitPathRe.exec(path); + if (typeof path === 'string') { + const splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i; + const pathParts = splitPathRe.exec(path); if (pathParts) { return pathParts.pop().toLowerCase(); @@ -105,15 +110,15 @@ export const getFileExtension = function(path) { * @method isCrossOrigin */ export const isCrossOrigin = function(url) { - let winLoc = window.location; - let urlInfo = parseUrl(url); + const winLoc = window.location; + const urlInfo = parseUrl(url); // IE8 protocol relative urls will return ':' for protocol - let srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; + const srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; // Check if url is for another domain/origin // IE8 doesn't know location.origin, so we won't rely on it here - let crossOrigin = (srcProtocol + urlInfo.host) !== (winLoc.protocol + winLoc.host); + const crossOrigin = (srcProtocol + urlInfo.host) !== (winLoc.protocol + winLoc.host); return crossOrigin; }; diff --git a/src/js/video.js b/src/js/video.js index e9ac832f47..5c8ed3eee5 100644 --- a/src/js/video.js +++ b/src/js/video.js @@ -1,6 +1,9 @@ /** * @file video.js */ + +/* global define */ + import window from 'global/window'; import document from 'global/document'; import * as setup from './setup'; @@ -28,8 +31,6 @@ import xhr from 'xhr'; // Include the built-in techs import Tech from './tech/tech.js'; -import Html5 from './tech/html5.js'; -import Flash from './tech/flash.js'; // HTML5 Element Shim for IE8 if (typeof HTMLVideoElement === 'undefined') { @@ -53,8 +54,8 @@ if (typeof HTMLVideoElement === 'undefined') { * @mixes videojs * @method videojs */ -function videojs(id, options, ready){ - let tag; // Element of ID +function videojs(id, options, ready) { + let tag; // Allow for element or ID to be passed in // String ID @@ -78,11 +79,10 @@ function videojs(id, options, ready){ } return videojs.getPlayers()[id]; + } // Otherwise get element for ID - } else { - tag = Dom.getEl(id); - } + tag = Dom.getEl(id); // ID is a media element } else { @@ -90,13 +90,14 @@ function videojs(id, options, ready){ } // Check for a useable element - if (!tag || !tag.nodeName) { // re: nodeName, could be a box div also - throw new TypeError('The element or ID supplied is not valid. (videojs)'); // Returns + // re: nodeName, could be a box div also + if (!tag || !tag.nodeName) { + throw new TypeError('The element or ID supplied is not valid. (videojs)'); } // Element may have a player attr referring to an already created player instance. // If not, set up a new player and return the instance. - return tag['player'] || Player.players[tag.playerId] || new Player(tag, options, ready); + return tag.player || Player.players[tag.playerId] || new Player(tag, options, ready); } // Add default styles @@ -105,7 +106,8 @@ if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true) { if (!style) { style = stylesheet.createStyleElement('vjs-styles-defaults'); - let head = Dom.$('head'); + const head = Dom.$('head'); + head.insertBefore(style, head.firstChild); stylesheet.setTextContent(style, ` .video-js { @@ -121,7 +123,8 @@ if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true) { } // Run Auto-load players -// You have to wait at least once in case this script is loaded after your video in the DOM (weird behavior only with minified version) +// You have to wait at least once in case this script is loaded after your +// video in the DOM (weird behavior only with minified version) setup.autoSetupTimeout(1, videojs); /* @@ -269,12 +272,12 @@ videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED; * Mimics ES6 subclassing with the `extend` keyword * ```js * // Create a basic javascript 'class' - * function MyClass(name){ + * function MyClass(name) { * // Set a property at initialization * this.myName = name; * } * // Create an instance method - * MyClass.prototype.sayMyName = function(){ + * MyClass.prototype.sayMyName = function() { * alert(this.myName); * }; * // Subclass the exisitng class and change the name @@ -337,12 +340,12 @@ videojs.mergeOptions = mergeOptions; /** * Change the context (this) of a function * - * videojs.bind(newContext, function(){ + * videojs.bind(newContext, function() { * this === newContext * }); * * NOTE: as of v5.0 we require an ES5 shim, so you should use the native - * `function(){}.bind(newContext);` instead of this. + * `function() {}.bind(newContext);` instead of this. * * @param {*} context The object to bind as scope * @param {Function} fn The function to be bound to a scope @@ -365,7 +368,7 @@ videojs.bind = Fn.bind; * var player = this; * var alertText = myPluginOptions.text || 'Player is playing!' * - * player.on('play', function(){ + * player.on('play', function() { * alert(alertText); * }); * }); @@ -410,7 +413,7 @@ videojs.plugin = plugin; * @mixes videojs * @method addLanguage */ -videojs.addLanguage = function(code, data){ +videojs.addLanguage = function(code, data) { code = ('' + code).toLowerCase(); return merge(videojs.options.languages, { [code]: data })[code]; }; @@ -721,12 +724,12 @@ videojs.insertContent = Dom.insertContent; * still support requirejs and browserify. This also needs to be closure * compiler compatible, so string keys are used. */ -if (typeof define === 'function' && define['amd']) { - define('videojs', [], function(){ return videojs; }); +if (typeof define === 'function' && define.amd) { + define('videojs', [], () => videojs); // checking that module is an object too because of umdjs/umd#35 } else if (typeof exports === 'object' && typeof module === 'object') { - module['exports'] = videojs; + module.exports = videojs; } export default videojs; diff --git a/test/api/api.js b/test/api/api.js index f75dfb4794..37a59a20e7 100644 --- a/test/api/api.js +++ b/test/api/api.js @@ -1,212 +1,231 @@ +/* eslint-env qunit */ /** * These tests run on the minified, window.videojs and ensure the needed * APIs still exist */ +import document from 'global/document'; +import window from 'global/window'; +const videojs = window.videojs; -(function(){ - -q.module('Player API'); - -test('videojs should exist on the window', function() { - ok(window.videojs, 'videojs exists on the window'); +QUnit.module('Player API'); +QUnit.test('videojs should exist on the window', function() { + QUnit.ok(window.videojs, 'videojs exists on the window'); }); -test('should be able to access expected player API methods', function() { - var player = videojs.getComponent('Player').prototype; +QUnit.test('should be able to access expected player API methods', function() { + const player = videojs.getComponent('Player').prototype; // Native HTML5 Methods - ok(player.error, 'error exists'); - ok(player.src, 'src exists'); - ok(player.currentSrc, 'currentSrc exists'); - ok(player.buffered, 'buffered exists'); - ok(player.load, 'load exists'); - ok(player.seeking, 'seeking exists'); - ok(player.currentTime, 'currentTime exists'); - ok(player.duration, 'duration exists'); - ok(player.paused, 'paused exists'); - ok(player.ended, 'ended exists'); - ok(player.autoplay, 'autoplay exists'); - ok(player.loop, 'loop exists'); - ok(player.play , 'play exists'); - ok(player.pause , 'pause exists'); - ok(player.controls, 'controls exists'); - ok(player.volume, 'volume exists'); - ok(player.muted, 'muted exists'); - ok(player.width, 'width exists'); - ok(player.height, 'height exists'); - ok(player.poster, 'poster exists'); - ok(player.textTracks, 'textTracks exists'); - ok(player.requestFullscreen, 'requestFullscreen exists'); - ok(player.exitFullscreen, 'exitFullscreen exists'); - ok(player.playbackRate, 'playbackRate exists'); - ok(player.networkState, 'networkState exists'); - ok(player.readyState, 'readyState exists'); + QUnit.ok(player.error, 'error exists'); + QUnit.ok(player.src, 'src exists'); + QUnit.ok(player.currentSrc, 'currentSrc exists'); + QUnit.ok(player.buffered, 'buffered exists'); + QUnit.ok(player.load, 'load exists'); + QUnit.ok(player.seeking, 'seeking exists'); + QUnit.ok(player.currentTime, 'currentTime exists'); + QUnit.ok(player.duration, 'duration exists'); + QUnit.ok(player.paused, 'paused exists'); + QUnit.ok(player.ended, 'ended exists'); + QUnit.ok(player.autoplay, 'autoplay exists'); + QUnit.ok(player.loop, 'loop exists'); + QUnit.ok(player.play, 'play exists'); + QUnit.ok(player.pause, 'pause exists'); + QUnit.ok(player.controls, 'controls exists'); + QUnit.ok(player.volume, 'volume exists'); + QUnit.ok(player.muted, 'muted exists'); + QUnit.ok(player.width, 'width exists'); + QUnit.ok(player.height, 'height exists'); + QUnit.ok(player.poster, 'poster exists'); + QUnit.ok(player.textTracks, 'textTracks exists'); + QUnit.ok(player.requestFullscreen, 'requestFullscreen exists'); + QUnit.ok(player.exitFullscreen, 'exitFullscreen exists'); + QUnit.ok(player.playbackRate, 'playbackRate exists'); + QUnit.ok(player.networkState, 'networkState exists'); + QUnit.ok(player.readyState, 'readyState exists'); // Unsupported Native HTML5 Methods - // ok(player.canPlayType, 'canPlayType exists'); - // ok(player.startTime, 'startTime exists'); - // ok(player.defaultPlaybackRate, 'defaultPlaybackRate exists'); - // ok(player.playbackRate, 'playbackRate exists'); - // ok(player.played, 'played exists'); - // ok(player.seekable, 'seekable exists'); - // ok(player.videoWidth, 'videoWidth exists'); - // ok(player.videoHeight, 'videoHeight exists'); + // QUnit.ok(player.canPlayType, 'canPlayType exists'); + // QUnit.ok(player.startTime, 'startTime exists'); + // QUnit.ok(player.defaultPlaybackRate, 'defaultPlaybackRate exists'); + // QUnit.ok(player.playbackRate, 'playbackRate exists'); + // QUnit.ok(player.played, 'played exists'); + // QUnit.ok(player.seekable, 'seekable exists'); + // QUnit.ok(player.videoWidth, 'videoWidth exists'); + // QUnit.ok(player.videoHeight, 'videoHeight exists'); // Additional player methods - ok(player.bufferedPercent, 'bufferedPercent exists'); - ok(player.reportUserActivity, 'reportUserActivity exists'); - ok(player.userActive, 'userActive exists'); - ok(player.usingNativeControls, 'usingNativeControls exists'); - ok(player.isFullscreen, 'isFullscreen exists'); + QUnit.ok(player.bufferedPercent, 'bufferedPercent exists'); + QUnit.ok(player.reportUserActivity, 'reportUserActivity exists'); + QUnit.ok(player.userActive, 'userActive exists'); + QUnit.ok(player.usingNativeControls, 'usingNativeControls exists'); + QUnit.ok(player.isFullscreen, 'isFullscreen exists'); // Track methods - ok(player.audioTracks, 'audioTracks exists'); - ok(player.videoTracks, 'videoTracks exists'); - ok(player.textTracks, 'textTracks exists'); - ok(player.remoteTextTrackEls, 'remoteTextTrackEls exists'); - ok(player.remoteTextTracks, 'remoteTextTracks exists'); - ok(player.addTextTrack, 'addTextTrack exists'); - ok(player.addRemoteTextTrack, 'addRemoteTextTrack exists'); - ok(player.removeRemoteTextTrack, 'removeRemoteTextTrack exists'); + QUnit.ok(player.audioTracks, 'audioTracks exists'); + QUnit.ok(player.videoTracks, 'videoTracks exists'); + QUnit.ok(player.textTracks, 'textTracks exists'); + QUnit.ok(player.remoteTextTrackEls, 'remoteTextTrackEls exists'); + QUnit.ok(player.remoteTextTracks, 'remoteTextTracks exists'); + QUnit.ok(player.addTextTrack, 'addTextTrack exists'); + QUnit.ok(player.addRemoteTextTrack, 'addRemoteTextTrack exists'); + QUnit.ok(player.removeRemoteTextTrack, 'removeRemoteTextTrack exists'); // Deprecated methods that should still exist - ok(player.requestFullScreen, 'requestFullScreen exists'); - ok(player.isFullScreen, 'isFullScreen exists'); - ok(player.cancelFullScreen, 'cancelFullScreen exists'); + QUnit.ok(player.requestFullScreen, 'requestFullScreen exists'); + QUnit.ok(player.isFullScreen, 'isFullScreen exists'); + QUnit.ok(player.cancelFullScreen, 'cancelFullScreen exists'); }); -test('should be able to access expected component API methods', function() { - var Component = videojs.getComponent('Component'); - var comp = new Component({ id: function(){ return 1; }, reportUserActivity: function(){} }); +QUnit.test('should be able to access expected component API methods', function() { + const Component = videojs.getComponent('Component'); + const comp = new Component({ + id() { + return 1; + }, + reportUserActivity() {} + }); // Component methods - ok(comp.player, 'player exists'); - ok(comp.options, 'options exists'); - ok(comp.init, 'init exists'); - ok(comp.dispose, 'dispose exists'); - ok(comp.createEl, 'createEl exists'); - ok(comp.contentEl, 'contentEl exists'); - ok(comp.el, 'el exists'); - ok(comp.addChild, 'addChild exists'); - ok(comp.getChild, 'getChild exists'); - ok(comp.getChildById, 'getChildById exists'); - ok(comp.children, 'children exists'); - ok(comp.initChildren, 'initChildren exists'); - ok(comp.removeChild, 'removeChild exists'); - ok(comp.on, 'on exists'); - ok(comp.off, 'off exists'); - ok(comp.one, 'one exists'); - ok(comp.trigger, 'trigger exists'); - ok(comp.triggerReady, 'triggerReady exists'); - ok(comp.show, 'show exists'); - ok(comp.hide, 'hide exists'); - ok(comp.width, 'width exists'); - ok(comp.height, 'height exists'); - ok(comp.dimensions, 'dimensions exists'); - ok(comp.ready, 'ready exists'); - ok(comp.addClass, 'addClass exists'); - ok(comp.removeClass, 'removeClass exists'); - ok(comp.buildCSSClass, 'buildCSSClass exists'); - ok(comp.setInterval, 'setInterval exists'); - ok(comp.clearInterval, 'clearInterval exists'); - ok(comp.setTimeout, 'setTimeout exists'); - ok(comp.clearTimeout, 'clearTimeout exists'); + QUnit.ok(comp.player, 'player exists'); + QUnit.ok(comp.options, 'options exists'); + QUnit.ok(comp.init, 'init exists'); + QUnit.ok(comp.dispose, 'dispose exists'); + QUnit.ok(comp.createEl, 'createEl exists'); + QUnit.ok(comp.contentEl, 'contentEl exists'); + QUnit.ok(comp.el, 'el exists'); + QUnit.ok(comp.addChild, 'addChild exists'); + QUnit.ok(comp.getChild, 'getChild exists'); + QUnit.ok(comp.getChildById, 'getChildById exists'); + QUnit.ok(comp.children, 'children exists'); + QUnit.ok(comp.initChildren, 'initChildren exists'); + QUnit.ok(comp.removeChild, 'removeChild exists'); + QUnit.ok(comp.on, 'on exists'); + QUnit.ok(comp.off, 'off exists'); + QUnit.ok(comp.one, 'one exists'); + QUnit.ok(comp.trigger, 'trigger exists'); + QUnit.ok(comp.triggerReady, 'triggerReady exists'); + QUnit.ok(comp.show, 'show exists'); + QUnit.ok(comp.hide, 'hide exists'); + QUnit.ok(comp.width, 'width exists'); + QUnit.ok(comp.height, 'height exists'); + QUnit.ok(comp.dimensions, 'dimensions exists'); + QUnit.ok(comp.ready, 'ready exists'); + QUnit.ok(comp.addClass, 'addClass exists'); + QUnit.ok(comp.removeClass, 'removeClass exists'); + QUnit.ok(comp.buildCSSClass, 'buildCSSClass exists'); + QUnit.ok(comp.setInterval, 'setInterval exists'); + QUnit.ok(comp.clearInterval, 'clearInterval exists'); + QUnit.ok(comp.setTimeout, 'setTimeout exists'); + QUnit.ok(comp.clearTimeout, 'clearTimeout exists'); }); -test('should be able to access expected MediaTech API methods', function() { - var media = videojs.getComponent('Tech'); - var mediaProto = media.prototype; - var html5 = videojs.getComponent('Html5'); - var html5Proto = html5.prototype; - var flash = videojs.getComponent('Flash'); - var flashProto = flash.prototype; +QUnit.test('should be able to access expected MediaTech API methods', function() { + const media = videojs.getComponent('Tech'); + const mediaProto = media.prototype; + const html5 = videojs.getComponent('Html5'); + const html5Proto = html5.prototype; + const flash = videojs.getComponent('Flash'); + const flashProto = flash.prototype; - ok(mediaProto.setPoster, 'setPoster should exist on the Media tech'); - ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech'); - ok(flashProto.setPoster, 'setPoster should exist on the Flash tech'); + QUnit.ok(mediaProto.setPoster, 'setPoster should exist on the Media tech'); + QUnit.ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech'); + QUnit.ok(flashProto.setPoster, 'setPoster should exist on the Flash tech'); - ok(html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5'); - ok(html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5'); + QUnit.ok(html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5'); + QUnit.ok(html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5'); // Source Handler Functions - ok(media.withSourceHandlers, 'withSourceHandlers should exist for Media Tech'); - - ok(html5.canPlaySource, 'canPlaySource should exist for HTML5'); - ok(html5.registerSourceHandler, 'registerSourceHandler should exist for Html5'); - ok(html5.selectSourceHandler, 'selectSourceHandler should exist for Html5'); - ok(html5.prototype.setSource, 'setSource should exist for Html5'); - ok(html5.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Html5'); - - ok(flash.canPlaySource, 'canPlaySource should exist for Flash'); - ok(flash.registerSourceHandler, 'registerSourceHandler should exist for Flash'); - ok(flash.selectSourceHandler, 'selectSourceHandler should exist for Flash'); - ok(flash.prototype.setSource, 'setSource should exist for Flash'); - ok(flash.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Flash'); + QUnit.ok(media.withSourceHandlers, 'withSourceHandlers should exist for Media Tech'); + + QUnit.ok(html5.canPlaySource, 'canPlaySource should exist for HTML5'); + QUnit.ok(html5.registerSourceHandler, 'registerSourceHandler should exist for Html5'); + QUnit.ok(html5.selectSourceHandler, 'selectSourceHandler should exist for Html5'); + QUnit.ok(html5.prototype.setSource, 'setSource should exist for Html5'); + QUnit.ok(html5.prototype.disposeSourceHandler, + 'disposeSourceHandler should exist for Html5'); + + QUnit.ok(flash.canPlaySource, 'canPlaySource should exist for Flash'); + QUnit.ok(flash.registerSourceHandler, 'registerSourceHandler should exist for Flash'); + QUnit.ok(flash.selectSourceHandler, 'selectSourceHandler should exist for Flash'); + QUnit.ok(flash.prototype.setSource, 'setSource should exist for Flash'); + QUnit.ok(flash.prototype.disposeSourceHandler, + 'disposeSourceHandler should exist for Flash'); }); -test('should export ready api call to public', function() { - var videoTag = testHelperMakeTag(); +QUnit.test('should export ready api call to public', function() { + const videoTag = testHelperMakeTag(); + const fixture = document.getElementById('qunit-fixture'); - var fixture = document.getElementById('qunit-fixture'); fixture.appendChild(videoTag); - var player = videojs('example_1'); - ok(player.ready !== undefined, 'ready callback is defined'); + const player = videojs('example_1'); + + QUnit.ok(player.ready !== undefined, 'ready callback is defined'); player.dispose(); }); -test('should export useful components to the public', function () { - ok(videojs.browser.TOUCH_ENABLED !== undefined, 'Touch detection should be public'); - ok(videojs.getComponent('ControlBar'), 'ControlBar should be public'); - ok(videojs.getComponent('Button'), 'Button should be public'); - ok(videojs.getComponent('PlayToggle'), 'PlayToggle should be public'); - ok(videojs.getComponent('FullscreenToggle'), 'FullscreenToggle should be public'); - ok(videojs.getComponent('BigPlayButton'), 'BigPlayButton should be public'); - ok(videojs.getComponent('LoadingSpinner'), 'LoadingSpinner should be public'); - ok(videojs.getComponent('CurrentTimeDisplay'), 'CurrentTimeDisplay should be public'); - ok(videojs.getComponent('DurationDisplay'), 'DurationDisplay should be public'); - ok(videojs.getComponent('TimeDivider'), 'TimeDivider should be public'); - ok(videojs.getComponent('RemainingTimeDisplay'), 'RemainingTimeDisplay should be public'); - ok(videojs.getComponent('Slider'), 'Slider should be public'); - ok(videojs.getComponent('ProgressControl'), 'ProgressControl should be public'); - ok(videojs.getComponent('SeekBar'), 'SeekBar should be public'); - ok(videojs.getComponent('LoadProgressBar'), 'LoadProgressBar should be public'); - ok(videojs.getComponent('PlayProgressBar'), 'PlayProgressBar should be public'); - ok(videojs.getComponent('VolumeControl'), 'VolumeControl should be public'); - ok(videojs.getComponent('VolumeBar'), 'VolumeBar should be public'); - ok(videojs.getComponent('VolumeLevel'), 'VolumeLevel should be public'); - ok(videojs.getComponent('VolumeMenuButton'), 'VolumeMenuButton should be public'); - ok(videojs.getComponent('MuteToggle'), 'MuteToggle should be public'); - ok(videojs.getComponent('PosterImage'), 'PosterImage should be public'); - ok(videojs.getComponent('Menu'), 'Menu should be public'); - ok(videojs.getComponent('MenuItem'), 'MenuItem should be public'); - ok(videojs.getComponent('MenuButton'), 'MenuButton should be public'); - ok(videojs.getComponent('PlaybackRateMenuButton'), 'PlaybackRateMenuButton should be public'); - - ok(videojs.getComponent('CaptionSettingsMenuItem'), 'CaptionSettingsMenuItem should be public'); - ok(videojs.getComponent('OffTextTrackMenuItem'), 'OffTextTrackMenuItem should be public'); - ok(videojs.getComponent('TextTrackMenuItem'), 'TextTrackMenuItem should be public'); - ok(videojs.getComponent('TextTrackDisplay'), 'TextTrackDisplay should be public'); - ok(videojs.getComponent('TextTrackButton'), 'TextTrackButton should be public'); - ok(videojs.getComponent('CaptionsButton'), 'CaptionsButton should be public'); - ok(videojs.getComponent('SubtitlesButton'), 'SubtitlesButton should be public'); - ok(videojs.getComponent('DescriptionsButton'), 'DescriptionsButton should be public'); - ok(videojs.getComponent('ChaptersButton'), 'ChaptersButton should be public'); - ok(videojs.getComponent('ChaptersTrackMenuItem'), 'ChaptersTrackMenuItem should be public'); - - ok(videojs.mergeOptions, 'mergeOptions should be public'); +QUnit.test('should export useful components to the public', function() { + QUnit.ok(videojs.browser.TOUCH_ENABLED !== undefined, + 'Touch detection should be public'); + QUnit.ok(videojs.getComponent('ControlBar'), 'ControlBar should be public'); + QUnit.ok(videojs.getComponent('Button'), 'Button should be public'); + QUnit.ok(videojs.getComponent('PlayToggle'), 'PlayToggle should be public'); + QUnit.ok(videojs.getComponent('FullscreenToggle'), 'FullscreenToggle should be public'); + QUnit.ok(videojs.getComponent('BigPlayButton'), 'BigPlayButton should be public'); + QUnit.ok(videojs.getComponent('LoadingSpinner'), 'LoadingSpinner should be public'); + QUnit.ok(videojs.getComponent('CurrentTimeDisplay'), + 'CurrentTimeDisplay should be public'); + QUnit.ok(videojs.getComponent('DurationDisplay'), 'DurationDisplay should be public'); + QUnit.ok(videojs.getComponent('TimeDivider'), 'TimeDivider should be public'); + QUnit.ok(videojs.getComponent('RemainingTimeDisplay'), + 'RemainingTimeDisplay should be public'); + QUnit.ok(videojs.getComponent('Slider'), 'Slider should be public'); + QUnit.ok(videojs.getComponent('ProgressControl'), 'ProgressControl should be public'); + QUnit.ok(videojs.getComponent('SeekBar'), 'SeekBar should be public'); + QUnit.ok(videojs.getComponent('LoadProgressBar'), 'LoadProgressBar should be public'); + QUnit.ok(videojs.getComponent('PlayProgressBar'), 'PlayProgressBar should be public'); + QUnit.ok(videojs.getComponent('VolumeControl'), 'VolumeControl should be public'); + QUnit.ok(videojs.getComponent('VolumeBar'), 'VolumeBar should be public'); + QUnit.ok(videojs.getComponent('VolumeLevel'), 'VolumeLevel should be public'); + QUnit.ok(videojs.getComponent('VolumeMenuButton'), 'VolumeMenuButton should be public'); + QUnit.ok(videojs.getComponent('MuteToggle'), 'MuteToggle should be public'); + QUnit.ok(videojs.getComponent('PosterImage'), 'PosterImage should be public'); + QUnit.ok(videojs.getComponent('Menu'), 'Menu should be public'); + QUnit.ok(videojs.getComponent('MenuItem'), 'MenuItem should be public'); + QUnit.ok(videojs.getComponent('MenuButton'), 'MenuButton should be public'); + QUnit.ok(videojs.getComponent('PlaybackRateMenuButton'), + 'PlaybackRateMenuButton should be public'); + + QUnit.ok(videojs.getComponent('CaptionSettingsMenuItem'), + 'CaptionSettingsMenuItem should be public'); + QUnit.ok(videojs.getComponent('OffTextTrackMenuItem'), + 'OffTextTrackMenuItem should be public'); + QUnit.ok(videojs.getComponent('TextTrackMenuItem'), + 'TextTrackMenuItem should be public'); + QUnit.ok(videojs.getComponent('TextTrackDisplay'), 'TextTrackDisplay should be public'); + QUnit.ok(videojs.getComponent('TextTrackButton'), 'TextTrackButton should be public'); + QUnit.ok(videojs.getComponent('CaptionsButton'), 'CaptionsButton should be public'); + QUnit.ok(videojs.getComponent('SubtitlesButton'), 'SubtitlesButton should be public'); + QUnit.ok(videojs.getComponent('DescriptionsButton'), + 'DescriptionsButton should be public'); + QUnit.ok(videojs.getComponent('ChaptersButton'), 'ChaptersButton should be public'); + QUnit.ok(videojs.getComponent('ChaptersTrackMenuItem'), + 'ChaptersTrackMenuItem should be public'); + + QUnit.ok(videojs.mergeOptions, 'mergeOptions should be public'); }); -test('should be able to initialize player twice on the same tag using string reference', function() { - var videoTag = testHelperMakeTag(); - var id = videoTag.id; +QUnit.test('should be able to initialize player twice on the same tag using string reference', function() { + const videoTag = testHelperMakeTag(); + const id = videoTag.id; + const fixture = document.getElementById('qunit-fixture'); - var fixture = document.getElementById('qunit-fixture'); fixture.appendChild(videoTag); - var player = videojs('example_1'); + const player = videojs('example_1'); + player.dispose(); - ok(!document.getElementById(id), 'element is removed'); + QUnit.ok(!document.getElementById(id), 'element is removed'); videoTag = testHelperMakeTag(); fixture.appendChild(videoTag); @@ -215,66 +234,68 @@ test('should be able to initialize player twice on the same tag using string ref player.dispose(); }); -test('videojs.getPlayers() should be available after minification', function() { - var videoTag = testHelperMakeTag(); - var id = videoTag.id; +QUnit.test('videojs.getPlayers() should be available after minification', function() { + const videoTag = testHelperMakeTag(); + const id = videoTag.id; + const fixture = document.getElementById('qunit-fixture'); - var fixture = document.getElementById('qunit-fixture'); fixture.appendChild(videoTag); - var player = videojs(id); - ok(videojs.getPlayers()[id] === player, 'videojs.getPlayers() is available'); + const player = videojs(id); + + QUnit.ok(videojs.getPlayers()[id] === player, 'videojs.getPlayers() is available'); player.dispose(); }); -test('component can be subclassed externally', function(){ - var Component = videojs.getComponent('Component'); - var ControlBar = videojs.getComponent('ControlBar'); +QUnit.test('component can be subclassed externally', function() { + const Component = videojs.getComponent('Component'); + const ControlBar = videojs.getComponent('ControlBar'); - var player = new (videojs.extend(Component, { - reportUserActivity: function(){}, - textTracks: function(){ return { + const player = new (videojs.extend(Component, { + reportUserActivity() {}, + textTracks() { + return { addEventListener: Function.prototype, removeEventListener: Function.prototype }; } }))({ - id: function(){}, - reportUserActivity: function(){} + id() {}, + reportUserActivity() {} }); - ok(new ControlBar(player), 'created a control bar without throwing'); + QUnit.ok(new ControlBar(player), 'created a control bar without throwing'); }); -function testHelperMakeTag(){ - var videoTag = document.createElement('video'); +function testHelperMakeTag() { + const videoTag = document.createElement('video'); + videoTag.id = 'example_1'; videoTag.className = 'video-js vjs-default-skin'; return videoTag; } -test('should extend Component', function(){ - var Component = videojs.getComponent('Component'); - var MyComponent = videojs.extend(Component, { - constructor: function() { +QUnit.test('should extend Component', function() { + const Component = videojs.getComponent('Component'); + const MyComponent = videojs.extend(Component, { + constructor() { this.bar = true; }, - foo: function() { + foo() { return true; } }); - var myComponent = new MyComponent(); - ok(myComponent instanceof Component, 'creates an instance of Component'); - ok(myComponent instanceof MyComponent, 'creates an instance of MyComponent'); - ok(myComponent.bar, 'the constructor function is used'); - ok(myComponent.foo(), 'instance methods are applied'); + const myComponent = new MyComponent(); - var NoMethods = videojs.extend(Component); - var noMethods = new NoMethods({}); - ok(noMethods.on, 'should extend component with no methods or constructor'); -}); + QUnit.ok(myComponent instanceof Component, 'creates an instance of Component'); + QUnit.ok(myComponent instanceof MyComponent, 'creates an instance of MyComponent'); + QUnit.ok(myComponent.bar, 'the constructor function is used'); + QUnit.ok(myComponent.foo(), 'instance methods are applied'); + const NoMethods = videojs.extend(Component); + const noMethods = new NoMethods({}); -})(); + QUnit.ok(noMethods.on, 'should extend component with no methods or constructor'); +}); diff --git a/test/globals-shim.js b/test/globals-shim.js index b0ad8514bc..705f16bef9 100644 --- a/test/globals-shim.js +++ b/test/globals-shim.js @@ -1,3 +1,4 @@ +/* eslint-env qunit */ import 'es6-shim'; import document from 'global/document'; import window from 'global/window'; diff --git a/test/unit/button.test.js b/test/unit/button.test.js index 10bb1c60ae..d5e82a9a6e 100644 --- a/test/unit/button.test.js +++ b/test/unit/button.test.js @@ -1,27 +1,27 @@ +/* eslint-env qunit */ import Button from '../../src/js/button.js'; import TestHelpers from './test-helpers.js'; -q.module('Button'); +QUnit.module('Button'); -test('should localize its text', function(){ - expect(3); +QUnit.test('should localize its text', function() { + QUnit.expect(3); - var player, testButton, el; - - player = TestHelpers.makePlayer({ - 'language': 'es', - 'languages': { - 'es': { - 'Play': 'Juego' + const player = TestHelpers.makePlayer({ + language: 'es', + languages: { + es: { + Play: 'Juego' } } }); - testButton = new Button(player); + const testButton = new Button(player); + testButton.controlText_ = 'Play'; - el = testButton.createEl(); + const el = testButton.createEl(); - ok(el.nodeName.toLowerCase().match('button')); - ok(el.innerHTML.match(/vjs-control-text"?>Juego/)); - equal(el.getAttribute('title'), 'Juego'); + QUnit.ok(el.nodeName.toLowerCase().match('button')); + QUnit.ok(el.innerHTML.match(/vjs-control-text"?>Juego/)); + QUnit.equal(el.getAttribute('title'), 'Juego'); }); diff --git a/test/unit/clickable-component.test.js b/test/unit/clickable-component.test.js index b83b6c62e3..322acb6883 100644 --- a/test/unit/clickable-component.test.js +++ b/test/unit/clickable-component.test.js @@ -1,39 +1,40 @@ +/* eslint-env qunit */ import ClickableComponent from '../../src/js/clickable-component.js'; import TestHelpers from './test-helpers.js'; -q.module('ClickableComponent'); +QUnit.module('ClickableComponent'); -q.test('should create a div with role="button"', function(){ - expect(2); +QUnit.test('should create a div with role="button"', function() { + QUnit.expect(2); - let player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); - let testClickableComponent = new ClickableComponent(player); - let el = testClickableComponent.createEl(); + const testClickableComponent = new ClickableComponent(player); + const el = testClickableComponent.createEl(); - equal(el.nodeName.toLowerCase(), 'div', 'the name of the element is "div"'); - equal(el.getAttribute('role').toLowerCase(), 'button', 'the role of the element is "button"'); + QUnit.equal(el.nodeName.toLowerCase(), 'div', 'the name of the element is "div"'); + QUnit.equal(el.getAttribute('role').toLowerCase(), 'button', 'the role of the element is "button"'); testClickableComponent.dispose(); player.dispose(); }); -q.test('should be enabled/disabled', function(){ - expect(3); +QUnit.test('should be enabled/disabled', function() { + QUnit.expect(3); - let player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); - let testClickableComponent = new ClickableComponent(player); + const testClickableComponent = new ClickableComponent(player); - equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent defaults to enabled'); + QUnit.equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent defaults to enabled'); testClickableComponent.disable(); - equal(testClickableComponent.hasClass('vjs-disabled'), true, 'ClickableComponent is disabled'); + QUnit.equal(testClickableComponent.hasClass('vjs-disabled'), true, 'ClickableComponent is disabled'); testClickableComponent.enable(); - equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent is enabled'); + QUnit.equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent is enabled'); testClickableComponent.dispose(); player.dispose(); diff --git a/test/unit/close-button.test.js b/test/unit/close-button.test.js index bd02bbb7f4..646d3bf545 100644 --- a/test/unit/close-button.test.js +++ b/test/unit/close-button.test.js @@ -1,21 +1,23 @@ +/* eslint-env qunit */ import CloseButton from '../../src/js/close-button'; +import sinon from 'sinon'; import TestHelpers from './test-helpers'; -q.module('CloseButton', { +QUnit.module('CloseButton', { - beforeEach: function() { + beforeEach() { this.player = TestHelpers.makePlayer(); this.btn = new CloseButton(this.player); }, - afterEach: function() { + afterEach() { this.player.dispose(); this.btn.dispose(); } }); -q.test('should create the expected element', function(assert) { - let elAssertions = TestHelpers.assertEl(assert, this.btn.el(), { +QUnit.test('should create the expected element', function(assert) { + const elAssertions = TestHelpers.assertEl(assert, this.btn.el(), { tagName: 'button', classes: [ 'vjs-button', @@ -29,16 +31,16 @@ q.test('should create the expected element', function(assert) { assert.strictEqual(this.btn.el().querySelector('.vjs-control-text').innerHTML, 'Close'); }); -q.test('should allow setting the controlText_ property as an option', function(assert) { - var text = 'OK!'; - var btn = new CloseButton(this.player, {controlText: text}); +QUnit.test('should allow setting the controlText_ property as an option', function(assert) { + const text = 'OK!'; + const btn = new CloseButton(this.player, {controlText: text}); assert.expect(1); assert.strictEqual(btn.controlText_, text, 'set the controlText_ property'); }); -q.test('should trigger an event on activation', function(assert) { - var spy = sinon.spy(); +QUnit.test('should trigger an event on activation', function(assert) { + const spy = sinon.spy(); this.btn.on('close', spy); this.btn.trigger('click'); diff --git a/test/unit/component.test.js b/test/unit/component.test.js index 38d7ff0842..2dc5ac8b41 100644 --- a/test/unit/component.test.js +++ b/test/unit/component.test.js @@ -1,8 +1,10 @@ +/* eslint-env qunit */ import Component from '../../src/js/component.js'; import * as Dom from '../../src/js/utils/dom.js'; import * as Events from '../../src/js/utils/events.js'; import * as browser from '../../src/js/utils/browser.js'; import document from 'global/document'; +import sinon from 'sinon'; import TestHelpers from './test-helpers.js'; class TestComponent1 extends Component {} @@ -20,91 +22,96 @@ Component.registerComponent('TestComponent2', TestComponent2); Component.registerComponent('TestComponent3', TestComponent3); Component.registerComponent('TestComponent4', TestComponent4); -q.module('Component', { - 'setup': function() { +QUnit.module('Component', { + setup() { this.clock = sinon.useFakeTimers(); }, - 'teardown': function() { + teardown() { this.clock.restore(); } }); -var getFakePlayer = function(){ +const getFakePlayer = function() { return { // Fake player requries an ID - id: function(){ return 'player_1'; }, - reportUserActivity: function(){} + id() { + return 'player_1'; + }, + reportUserActivity() {} }; }; -test('should create an element', function(){ - var comp = new Component(getFakePlayer(), {}); +QUnit.test('should create an element', function() { + const comp = new Component(getFakePlayer(), {}); - ok(comp.el().nodeName); + QUnit.ok(comp.el().nodeName); }); -test('should add a child component', function(){ - var comp = new Component(getFakePlayer()); +QUnit.test('should add a child component', function() { + const comp = new Component(getFakePlayer()); - var child = comp.addChild('component'); + const child = comp.addChild('component'); - ok(comp.children().length === 1); - ok(comp.children()[0] === child); - ok(comp.el().childNodes[0] === child.el()); - ok(comp.getChild('component') === child); - ok(comp.getChildById(child.id()) === child); + QUnit.ok(comp.children().length === 1); + QUnit.ok(comp.children()[0] === child); + QUnit.ok(comp.el().childNodes[0] === child.el()); + QUnit.ok(comp.getChild('component') === child); + QUnit.ok(comp.getChildById(child.id()) === child); }); -test('should add a child component to an index', function(){ - var comp = new Component(getFakePlayer()); +QUnit.test('should add a child component to an index', function() { + const comp = new Component(getFakePlayer()); + + const child = comp.addChild('component'); + + QUnit.ok(comp.children().length === 1); + QUnit.ok(comp.children()[0] === child); + + const child0 = comp.addChild('component', {}, 0); + + QUnit.ok(comp.children().length === 2); + QUnit.ok(comp.children()[0] === child0); + QUnit.ok(comp.children()[1] === child); - var child = comp.addChild('component'); + const child1 = comp.addChild('component', {}, '2'); - ok(comp.children().length === 1); - ok(comp.children()[0] === child); + QUnit.ok(comp.children().length === 3); + QUnit.ok(comp.children()[2] === child1); - var child0 = comp.addChild('component', {}, 0); - ok(comp.children().length === 2); - ok(comp.children()[0] === child0); - ok(comp.children()[1] === child); + const child2 = comp.addChild('component', {}, undefined); - var child1 = comp.addChild('component', {}, '2'); - ok(comp.children().length === 3); - ok(comp.children()[2] === child1); + QUnit.ok(comp.children().length === 4); + QUnit.ok(comp.children()[3] === child2); - var child2 = comp.addChild('component', {}, undefined); - ok(comp.children().length === 4); - ok(comp.children()[3] === child2); + const child3 = comp.addChild('component', {}, -1); - var child3 = comp.addChild('component', {}, -1); - ok(comp.children().length === 5); - ok(comp.children()[3] === child3); - ok(comp.children()[4] === child2); + QUnit.ok(comp.children().length === 5); + QUnit.ok(comp.children()[3] === child3); + QUnit.ok(comp.children()[4] === child2); }); -test('addChild should throw if the child does not exist', function() { - var comp = new Component(getFakePlayer()); +QUnit.test('addChild should throw if the child does not exist', function() { + const comp = new Component(getFakePlayer()); throws(function() { - comp.addChild('non-existent-child'); + comp.addChild('non-existent-child'); }, new Error('Component Non-existent-child does not exist'), 'addChild threw'); }); - -test('should init child components from options', function(){ - var comp = new Component(getFakePlayer(), { +QUnit.test('should init child components from options', function() { + const comp = new Component(getFakePlayer(), { children: { - 'component': {} + component: {} } }); - ok(comp.children().length === 1); - ok(comp.el().childNodes.length === 1); + QUnit.ok(comp.children().length === 1); + QUnit.ok(comp.el().childNodes.length === 1); }); -test('should init child components from simple children array', function(){ - var comp = new Component(getFakePlayer(), { +QUnit.test('should init child components from simple children array', function() { + const comp = new Component(getFakePlayer(), { children: [ 'component', 'component', @@ -112,157 +119,163 @@ test('should init child components from simple children array', function(){ ] }); - ok(comp.children().length === 3); - ok(comp.el().childNodes.length === 3); + QUnit.ok(comp.children().length === 3); + QUnit.ok(comp.el().childNodes.length === 3); }); -test('should init child components from children array of objects', function(){ - var comp = new Component(getFakePlayer(), { +QUnit.test('should init child components from children array of objects', function() { + const comp = new Component(getFakePlayer(), { children: [ - { 'name': 'component' }, - { 'name': 'component' }, - { 'name': 'component' } + { name: 'component' }, + { name: 'component' }, + { name: 'component' } ] }); - ok(comp.children().length === 3); - ok(comp.el().childNodes.length === 3); + QUnit.ok(comp.children().length === 3); + QUnit.ok(comp.el().childNodes.length === 3); }); -test('should do a deep merge of child options', function(){ +QUnit.test('should do a deep merge of child options', function() { // Create a default option for component Component.prototype.options_ = { - 'example': { - 'childOne': { 'foo': 'bar', 'asdf': 'fdsa' }, - 'childTwo': {}, - 'childThree': {} + example: { + childOne: { foo: 'bar', asdf: 'fdsa' }, + childTwo: {}, + childThree: {} } }; - var comp = new Component(getFakePlayer(), { - 'example': { - 'childOne': { 'foo': 'baz', 'abc': '123' }, - 'childThree': false, - 'childFour': {} + const comp = new Component(getFakePlayer(), { + example: { + childOne: { foo: 'baz', abc: '123' }, + childThree: false, + childFour: {} } }); - var mergedOptions = comp.options_; - var children = mergedOptions['example']; + const mergedOptions = comp.options_; + const children = mergedOptions.example; - strictEqual(children['childOne']['foo'], 'baz', 'value three levels deep overridden'); - strictEqual(children['childOne']['asdf'], 'fdsa', 'value three levels deep maintained'); - strictEqual(children['childOne']['abc'], '123', 'value three levels deep added'); - ok(children['childTwo'], 'object two levels deep maintained'); - strictEqual(children['childThree'], false, 'object two levels deep removed'); - ok(children['childFour'], 'object two levels deep added'); + QUnit.strictEqual(children.childOne.foo, 'baz', 'value three levels deep overridden'); + QUnit.strictEqual(children.childOne.asdf, 'fdsa', 'value three levels deep maintained'); + QUnit.strictEqual(children.childOne.abc, '123', 'value three levels deep added'); + QUnit.ok(children.childTwo, 'object two levels deep maintained'); + QUnit.strictEqual(children.childThree, false, 'object two levels deep removed'); + QUnit.ok(children.childFour, 'object two levels deep added'); - strictEqual(Component.prototype.options_['example']['childOne']['foo'], 'bar', 'prototype options were not overridden'); + QUnit.strictEqual(Component.prototype.options_.example.childOne.foo, 'bar', + 'prototype options were not overridden'); // Reset default component options to none Component.prototype.options_ = null; }); -test('should init child components from component options', function(){ - let testComp = new TestComponent1(TestHelpers.makePlayer(), { +QUnit.test('should init child components from component options', function() { + const testComp = new TestComponent1(TestHelpers.makePlayer(), { testComponent2: false, testComponent4: {} }); - ok(!testComp.childNameIndex_.testComponent2, 'we do not have testComponent2'); - ok(testComp.childNameIndex_.testComponent4, 'we have a testComponent4'); + QUnit.ok(!testComp.childNameIndex_.testComponent2, 'we do not have testComponent2'); + QUnit.ok(testComp.childNameIndex_.testComponent4, 'we have a testComponent4'); }); -test('should allows setting child options at the parent options level', function(){ - var parent, options; +QUnit.test('should allows setting child options at the parent options level', function() { + let parent; // using children array - options = { - 'children': [ + let options = { + children: [ 'component', 'nullComponent' ], // parent-level option for child - 'component': { - 'foo': true + component: { + foo: true }, - 'nullComponent': false + nullComponent: false }; try { parent = new Component(getFakePlayer(), options); - } catch(err) { - ok(false, 'Child with `false` option was initialized'); + } catch (err) { + QUnit.ok(false, 'Child with `false` option was initialized'); } - equal(parent.children()[0].options_['foo'], true, 'child options set when children array is used'); - equal(parent.children().length, 1, 'we should only have one child'); + QUnit.equal(parent.children()[0].options_.foo, true, 'child options set when children array is used'); + QUnit.equal(parent.children().length, 1, 'we should only have one child'); // using children object options = { - 'children': { - 'component': { - 'foo': false + children: { + component: { + foo: false }, - 'nullComponent': {} + nullComponent: {} }, // parent-level option for child - 'component': { - 'foo': true + component: { + foo: true }, - 'nullComponent': false + nullComponent: false }; try { parent = new Component(getFakePlayer(), options); - } catch(err) { - ok(false, 'Child with `false` option was initialized'); + } catch (err) { + QUnit.ok(false, 'Child with `false` option was initialized'); } - equal(parent.children()[0].options_['foo'], true, 'child options set when children object is used'); - equal(parent.children().length, 1, 'we should only have one child'); + QUnit.equal(parent.children()[0].options_.foo, true, 'child options set when children object is used'); + QUnit.equal(parent.children().length, 1, 'we should only have one child'); }); -test('should dispose of component and children', function(){ - var comp = new Component(getFakePlayer()); +QUnit.test('should dispose of component and children', function() { + const comp = new Component(getFakePlayer()); // Add a child - var child = comp.addChild('Component'); - ok(comp.children().length === 1); + const child = comp.addChild('Component'); + + QUnit.ok(comp.children().length === 1); // Add a listener - comp.on('click', function(){ return true; }); - var el = comp.el(); - var data = Dom.getElData(el); + comp.on('click', function() { + return true; + }); + const el = comp.el(); + const data = Dom.getElData(el); + + let hasDisposed = false; + let bubbles = null; - var hasDisposed = false; - var bubbles = null; - comp.on('dispose', function(event){ + comp.on('dispose', function(event) { hasDisposed = true; bubbles = event.bubbles; }); comp.dispose(); - ok(hasDisposed, 'component fired dispose event'); - ok(bubbles === false, 'dispose event does not bubble'); - ok(!comp.children(), 'component children were deleted'); - ok(!comp.el(), 'component element was deleted'); - ok(!child.children(), 'child children were deleted'); - ok(!child.el(), 'child element was deleted'); - ok(!Dom.hasElData(el), 'listener data nulled'); - ok(!Object.getOwnPropertyNames(data).length, 'original listener data object was emptied'); + QUnit.ok(hasDisposed, 'component fired dispose event'); + QUnit.ok(bubbles === false, 'dispose event does not bubble'); + QUnit.ok(!comp.children(), 'component children were deleted'); + QUnit.ok(!comp.el(), 'component element was deleted'); + QUnit.ok(!child.children(), 'child children were deleted'); + QUnit.ok(!child.el(), 'child element was deleted'); + QUnit.ok(!Dom.hasElData(el), 'listener data nulled'); + QUnit.ok(!Object.getOwnPropertyNames(data).length, + 'original listener data object was emptied'); }); -test('should add and remove event listeners to element', function(){ - var comp = new Component(getFakePlayer(), {}); +QUnit.test('should add and remove event listeners to element', function() { + const comp = new Component(getFakePlayer(), {}); // No need to make this async because we're triggering events inline. // We're going to trigger the event after removing the listener, // So if we get extra asserts that's a problem. - expect(2); + QUnit.expect(2); - var testListener = function(){ - ok(true, 'fired event once'); - ok(this === comp, 'listener has the component as context'); + const testListener = function() { + QUnit.ok(true, 'fired event once'); + QUnit.ok(this === comp, 'listener has the component as context'); }; comp.on('test-event', testListener); @@ -271,13 +284,13 @@ test('should add and remove event listeners to element', function(){ comp.trigger('test-event'); }); -test('should trigger a listener once using one()', function(){ - var comp = new Component(getFakePlayer(), {}); +QUnit.test('should trigger a listener once using one()', function() { + const comp = new Component(getFakePlayer(), {}); - expect(1); + QUnit.expect(1); - var testListener = function(){ - ok(true, 'fired event once'); + const testListener = function() { + QUnit.ok(true, 'fired event once'); }; comp.one('test-event', testListener); @@ -285,14 +298,15 @@ test('should trigger a listener once using one()', function(){ comp.trigger('test-event'); }); -test('should be possible to pass data when you trigger an event', function () { - var comp = new Component(getFakePlayer(), {}); - var data1 = 'Data1'; - var data2 = {txt: 'Data2'}; - expect(3); +QUnit.test('should be possible to pass data when you trigger an event', function() { + const comp = new Component(getFakePlayer(), {}); + const data1 = 'Data1'; + const data2 = {txt: 'Data2'}; + + QUnit.expect(3); - var testListener = function(evt, hash){ - ok(true, 'fired event once'); + const testListener = function(evt, hash) { + QUnit.ok(true, 'fired event once'); deepEqual(hash.d1, data1); deepEqual(hash.d2, data2); }; @@ -302,158 +316,160 @@ test('should be possible to pass data when you trigger an event', function () { comp.trigger('test-event'); }); -test('should add listeners to other components and remove them', function(){ - var player = getFakePlayer(), - comp1 = new Component(player), - comp2 = new Component(player), - listenerFired = 0, - testListener; +QUnit.test('should add listeners to other components and remove them', function() { + const player = getFakePlayer(); + const comp1 = new Component(player); + const comp2 = new Component(player); + let listenerFired = 0; - testListener = function(){ - equal(this, comp1, 'listener has the first component as context'); + const testListener = function() { + QUnit.equal(this, comp1, 'listener has the first component as context'); listenerFired++; }; comp1.on(comp2, 'test-event', testListener); comp2.trigger('test-event'); - equal(listenerFired, 1, 'listener was fired once'); + QUnit.equal(listenerFired, 1, 'listener was fired once'); listenerFired = 0; comp1.off(comp2, 'test-event', testListener); comp2.trigger('test-event'); - equal(listenerFired, 0, 'listener was not fired after being removed'); + QUnit.equal(listenerFired, 0, 'listener was not fired after being removed'); // this component is disposed first listenerFired = 0; comp1.on(comp2, 'test-event', testListener); comp1.dispose(); comp2.trigger('test-event'); - equal(listenerFired, 0, 'listener was removed when this component was disposed first'); - comp1.off = function(){ throw 'Comp1 off called'; }; + QUnit.equal(listenerFired, 0, 'listener was removed when this component was disposed first'); + comp1.off = function() { + throw new Error('Comp1 off called'); + }; comp2.dispose(); - ok(true, 'this component removed dispose listeners from other component'); + QUnit.ok(true, 'this component removed dispose listeners from other component'); }); -test('should add listeners to other components and remove when them other component is disposed', function(){ - var player = getFakePlayer(), - comp1 = new Component(player), - comp2 = new Component(player), - listenerFired = 0, - testListener; +QUnit.test('should add listeners to other components and remove when them other component is disposed', function() { + const player = getFakePlayer(); + const comp1 = new Component(player); + const comp2 = new Component(player); + let listenerFired = 0; - testListener = function(){ - equal(this, comp1, 'listener has the first component as context'); + const testListener = function() { + QUnit.equal(this, comp1, 'listener has the first component as context'); listenerFired++; }; comp1.on(comp2, 'test-event', testListener); comp2.dispose(); - comp2.off = function(){ throw 'Comp2 off called'; }; + comp2.off = function() { + throw new Error('Comp2 off called'); + }; comp1.dispose(); - ok(true, 'this component removed dispose listener from this component that referenced other component'); + QUnit.ok(true, 'this component removed dispose listener from this component that referenced other component'); }); -test('should add listeners to other components that are fired once', function(){ - var player = getFakePlayer(), - comp1 = new Component(player), - comp2 = new Component(player), - listenerFired = 0, - testListener; +QUnit.test('should add listeners to other components that are fired once', function() { + const player = getFakePlayer(); + const comp1 = new Component(player); + const comp2 = new Component(player); + let listenerFired = 0; - testListener = function(){ - equal(this, comp1, 'listener has the first component as context'); + const testListener = function() { + QUnit.equal(this, comp1, 'listener has the first component as context'); listenerFired++; }; comp1.one(comp2, 'test-event', testListener); comp2.trigger('test-event'); - equal(listenerFired, 1, 'listener was executed once'); + QUnit.equal(listenerFired, 1, 'listener was executed once'); comp2.trigger('test-event'); - equal(listenerFired, 1, 'listener was executed only once'); + QUnit.equal(listenerFired, 1, 'listener was executed only once'); }); -test('should add listeners to other element and remove them', function(){ - var player = getFakePlayer(), - comp1 = new Component(player), - el = document.createElement('div'), - listenerFired = 0, - testListener; +QUnit.test('should add listeners to other element and remove them', function() { + const player = getFakePlayer(); + const comp1 = new Component(player); + const el = document.createElement('div'); + let listenerFired = 0; - testListener = function(){ - equal(this, comp1, 'listener has the first component as context'); + const testListener = function() { + QUnit.equal(this, comp1, 'listener has the first component as context'); listenerFired++; }; comp1.on(el, 'test-event', testListener); Events.trigger(el, 'test-event'); - equal(listenerFired, 1, 'listener was fired once'); + QUnit.equal(listenerFired, 1, 'listener was fired once'); listenerFired = 0; comp1.off(el, 'test-event', testListener); Events.trigger(el, 'test-event'); - equal(listenerFired, 0, 'listener was not fired after being removed from other element'); + QUnit.equal(listenerFired, 0, 'listener was not fired after being removed from other element'); // this component is disposed first listenerFired = 0; comp1.on(el, 'test-event', testListener); comp1.dispose(); Events.trigger(el, 'test-event'); - equal(listenerFired, 0, 'listener was removed when this component was disposed first'); - comp1.off = function(){ throw 'Comp1 off called'; }; + QUnit.equal(listenerFired, 0, 'listener was removed when this component was disposed first'); + comp1.off = function() { + throw new Error('Comp1 off called'); + }; + try { Events.trigger(el, 'dispose'); - } catch(e) { - ok(false, 'listener was not removed from other element'); + } catch (e) { + QUnit.ok(false, 'listener was not removed from other element'); } Events.trigger(el, 'dispose'); - ok(true, 'this component removed dispose listeners from other element'); + QUnit.ok(true, 'this component removed dispose listeners from other element'); }); -test('should add listeners to other components that are fired once', function(){ - var player = getFakePlayer(), - comp1 = new Component(player), - el = document.createElement('div'), - listenerFired = 0, - testListener; +QUnit.test('should add listeners to other components that are fired once', function() { + const player = getFakePlayer(); + const comp1 = new Component(player); + const el = document.createElement('div'); + let listenerFired = 0; - testListener = function(){ - equal(this, comp1, 'listener has the first component as context'); + const testListener = function() { + QUnit.equal(this, comp1, 'listener has the first component as context'); listenerFired++; }; comp1.one(el, 'test-event', testListener); Events.trigger(el, 'test-event'); - equal(listenerFired, 1, 'listener was executed once'); + QUnit.equal(listenerFired, 1, 'listener was executed once'); Events.trigger(el, 'test-event'); - equal(listenerFired, 1, 'listener was executed only once'); + QUnit.equal(listenerFired, 1, 'listener was executed only once'); }); -test('should trigger a listener when ready', function(){ +QUnit.test('should trigger a listener when ready', function() { let initListenerFired; let methodListenerFired; let syncListenerFired; - let comp = new Component(getFakePlayer(), {}, function(){ + const comp = new Component(getFakePlayer(), {}, function() { initListenerFired = true; }); - comp.ready(function(){ + comp.ready(function() { methodListenerFired = true; }); comp.triggerReady(); - comp.ready(function(){ + comp.ready(function() { syncListenerFired = true; }, true); - ok(!initListenerFired, 'init listener should NOT fire synchronously'); - ok(!methodListenerFired, 'method listener should NOT fire synchronously'); - ok(syncListenerFired, 'sync listener SHOULD fire synchronously if after ready'); + QUnit.ok(!initListenerFired, 'init listener should NOT fire synchronously'); + QUnit.ok(!methodListenerFired, 'method listener should NOT fire synchronously'); + QUnit.ok(syncListenerFired, 'sync listener SHOULD fire synchronously if after ready'); this.clock.tick(1); - ok(initListenerFired, 'init listener should fire asynchronously'); - ok(methodListenerFired, 'method listener should fire asynchronously'); + QUnit.ok(initListenerFired, 'init listener should fire asynchronously'); + QUnit.ok(methodListenerFired, 'method listener should fire asynchronously'); // Listeners should only be fired once and then removed initListenerFired = false; @@ -463,16 +479,17 @@ test('should trigger a listener when ready', function(){ comp.triggerReady(); this.clock.tick(1); - ok(!initListenerFired, 'init listener should be removed'); - ok(!methodListenerFired, 'method listener should be removed'); - ok(!syncListenerFired, 'sync listener should be removed'); + QUnit.ok(!initListenerFired, 'init listener should be removed'); + QUnit.ok(!methodListenerFired, 'method listener should be removed'); + QUnit.ok(!syncListenerFired, 'sync listener should be removed'); }); -test('should not retrigger a listener when the listener calls triggerReady', function(){ - var timesCalled = 0; - var selfTriggered = false; +QUnit.test('should not retrigger a listener when the listener calls triggerReady', function() { + let timesCalled = 0; + let selfTriggered = false; + const comp = new Component(getFakePlayer(), {}); - var readyListener = function(){ + const readyListener = function() { timesCalled++; // Don't bother calling again if we have @@ -483,71 +500,70 @@ test('should not retrigger a listener when the listener calls triggerReady', fun } }; - var comp = new Component(getFakePlayer(), {}); - comp.ready(readyListener); comp.triggerReady(); this.clock.tick(100); - equal(timesCalled, 1, 'triggerReady from inside a ready handler does not result in an infinite loop'); + QUnit.equal(timesCalled, 1, 'triggerReady from inside a ready handler does not result in an infinite loop'); }); -test('should add and remove a CSS class', function(){ - var comp = new Component(getFakePlayer(), {}); +QUnit.test('should add and remove a CSS class', function() { + const comp = new Component(getFakePlayer(), {}); comp.addClass('test-class'); - ok(comp.el().className.indexOf('test-class') !== -1); + QUnit.ok(comp.el().className.indexOf('test-class') !== -1); comp.removeClass('test-class'); - ok(comp.el().className.indexOf('test-class') === -1); + QUnit.ok(comp.el().className.indexOf('test-class') === -1); comp.toggleClass('test-class'); - ok(comp.el().className.indexOf('test-class') !== -1); + QUnit.ok(comp.el().className.indexOf('test-class') !== -1); comp.toggleClass('test-class'); - ok(comp.el().className.indexOf('test-class') === -1); + QUnit.ok(comp.el().className.indexOf('test-class') === -1); }); -test('should show and hide an element', function(){ - var comp = new Component(getFakePlayer(), {}); +QUnit.test('should show and hide an element', function() { + const comp = new Component(getFakePlayer(), {}); comp.hide(); - ok(comp.hasClass('vjs-hidden') === true); + QUnit.ok(comp.hasClass('vjs-hidden') === true); comp.show(); - ok(comp.hasClass('vjs-hidden') === false); + QUnit.ok(comp.hasClass('vjs-hidden') === false); }); -test('dimension() should treat NaN and null as zero', function() { - var comp, width, height, newWidth, newHeight; - width = 300; - height = 150; +QUnit.test('dimension() should treat NaN and null as zero', function() { + let newWidth; - comp = new Component(getFakePlayer(), {}), + const width = 300; + const height = 150; + + const comp = new Component(getFakePlayer(), {}); // set component dimension comp.dimensions(width, height); newWidth = comp.dimension('width', null); - notEqual(newWidth, width, 'new width and old width are not the same'); - equal(newWidth, comp, 'we set a value, so, return value is component'); - equal(comp.width(), 0, 'the new width is zero'); + QUnit.notEqual(newWidth, width, 'new width and old width are not the same'); + QUnit.equal(newWidth, comp, 'we set a value, so, return value is component'); + QUnit.equal(comp.width(), 0, 'the new width is zero'); - newHeight = comp.dimension('height', NaN); + const newHeight = comp.dimension('height', NaN); - notEqual(newHeight, height, 'new height and old height are not the same'); - equal(newHeight, comp, 'we set a value, so, return value is component'); - equal(comp.height(), 0, 'the new height is zero'); + QUnit.notEqual(newHeight, height, 'new height and old height are not the same'); + QUnit.equal(newHeight, comp, 'we set a value, so, return value is component'); + QUnit.equal(comp.height(), 0, 'the new height is zero'); comp.width(width); newWidth = comp.dimension('width', undefined); - equal(newWidth, width, 'we did not set the width with undefined'); + QUnit.equal(newWidth, width, 'we did not set the width with undefined'); }); -test('should change the width and height of a component', function(){ - var container = document.createElement('div'); - var comp = new Component(getFakePlayer(), {}); - var el = comp.el(); - var fixture = document.getElementById('qunit-fixture'); +QUnit.test('should change the width and height of a component', function() { + const container = document.createElement('div'); + const comp = new Component(getFakePlayer(), {}); + const el = comp.el(); + const fixture = document.getElementById('qunit-fixture'); fixture.appendChild(container); container.appendChild(el); @@ -558,21 +574,22 @@ test('should change the width and height of a component', function(){ comp.width('50%'); comp.height('123px'); - ok(comp.width() === 500, 'percent values working'); - var compStyle = TestHelpers.getComputedStyle(el, 'width'); - ok(compStyle === comp.width() + 'px', 'matches computed style'); - ok(comp.height() === 123, 'px values working'); + QUnit.ok(comp.width() === 500, 'percent values working'); + const compStyle = TestHelpers.getComputedStyle(el, 'width'); + + QUnit.ok(compStyle === comp.width() + 'px', 'matches computed style'); + QUnit.ok(comp.height() === 123, 'px values working'); comp.width(321); - ok(comp.width() === 321, 'integer values working'); + QUnit.ok(comp.width() === 321, 'integer values working'); comp.width('auto'); comp.height('auto'); - ok(comp.width() === 1000, 'forced width was removed'); - ok(comp.height() === 0, 'forced height was removed'); + QUnit.ok(comp.width() === 1000, 'forced width was removed'); + QUnit.ok(comp.height() === 0, 'forced height was removed'); }); -test('should get the computed dimensions', function(){ +QUnit.test('should get the computed dimensions', function() { const container = document.createElement('div'); const comp = new Component(getFakePlayer(), {}); const el = comp.el(); @@ -590,56 +607,57 @@ test('should get the computed dimensions', function(){ comp.width('50%'); comp.height('50%'); - equal(comp.currentWidth() + 'px', computedWidth, 'matches computed width'); - equal(comp.currentHeight() + 'px', computedHeight, 'matches computed height'); + QUnit.equal(comp.currentWidth() + 'px', computedWidth, 'matches computed width'); + QUnit.equal(comp.currentHeight() + 'px', computedHeight, 'matches computed height'); - equal(comp.currentDimension('width') + 'px', computedWidth, 'matches computed width'); - equal(comp.currentDimension('height') + 'px', computedHeight, 'matches computed height'); + QUnit.equal(comp.currentDimension('width') + 'px', computedWidth, 'matches computed width'); + QUnit.equal(comp.currentDimension('height') + 'px', computedHeight, 'matches computed height'); - equal(comp.currentDimensions()['width'] + 'px', computedWidth, 'matches computed width'); - equal(comp.currentDimensions()['height'] + 'px', computedHeight, 'matches computed width'); + QUnit.equal(comp.currentDimensions().width + 'px', computedWidth, 'matches computed width'); + QUnit.equal(comp.currentDimensions().height + 'px', computedHeight, 'matches computed width'); }); -test('should use a defined content el for appending children', function(){ +QUnit.test('should use a defined content el for appending children', function() { class CompWithContent extends Component {} - CompWithContent.prototype.createEl = function(){ + CompWithContent.prototype.createEl = function() { // Create the main componenent element - var el = Dom.createEl('div'); + const el = Dom.createEl('div'); + // Create the element where children will be appended - this.contentEl_ = Dom.createEl('div', { 'id': 'contentEl' }); + this.contentEl_ = Dom.createEl('div', { id: 'contentEl' }); el.appendChild(this.contentEl_); return el; }; - var comp = new CompWithContent(getFakePlayer()); - var child = comp.addChild('component'); + const comp = new CompWithContent(getFakePlayer()); + const child = comp.addChild('component'); - ok(comp.children().length === 1); - ok(comp.el().childNodes[0]['id'] === 'contentEl'); - ok(comp.el().childNodes[0].childNodes[0] === child.el()); + QUnit.ok(comp.children().length === 1); + QUnit.ok(comp.el().childNodes[0].id === 'contentEl'); + QUnit.ok(comp.el().childNodes[0].childNodes[0] === child.el()); comp.removeChild(child); - ok(comp.children().length === 0, 'Length should now be zero'); - ok(comp.el().childNodes[0]['id'] === 'contentEl', 'Content El should still exist'); - ok(comp.el().childNodes[0].childNodes[0] !== child.el(), 'Child el should be removed.'); + QUnit.ok(comp.children().length === 0, 'Length should now be zero'); + QUnit.ok(comp.el().childNodes[0].id === 'contentEl', 'Content El should still exist'); + QUnit.ok(comp.el().childNodes[0].childNodes[0] !== child.el(), + 'Child el should be removed.'); }); -test('should emit a tap event', function(){ - expect(3); +QUnit.test('should emit a tap event', function() { + const comp = new Component(getFakePlayer()); + let singleTouch = {}; + const origTouch = browser.TOUCH_ENABLED; + QUnit.expect(3); // Fake touch support. Real touch support isn't needed for this test. - var origTouch = browser.TOUCH_ENABLED; browser.TOUCH_ENABLED = true; - var comp = new Component(getFakePlayer()); - var singleTouch = {}; - comp.emitTapEvents(); - comp.on('tap', function(){ - ok(true, 'Tap event emitted'); + comp.on('tap', function() { + QUnit.ok(true, 'Tap event emitted'); }); // A touchstart followed by touchend should trigger a tap @@ -686,31 +704,30 @@ test('should emit a tap event', function(){ browser.TOUCH_ENABLED = origTouch; }); -test('should provide timeout methods that automatically get cleared on component disposal', function() { - expect(4); +QUnit.test('should provide timeout methods that automatically get cleared on component disposal', function() { + const comp = new Component(getFakePlayer()); + let timeoutsFired = 0; + const timeoutToClear = comp.setTimeout(function() { + timeoutsFired++; + QUnit.ok(false, 'Timeout should have been manually cleared'); + }, 500); - var comp = new Component(getFakePlayer()); - var timeoutsFired = 0; + QUnit.expect(4); comp.setTimeout(function() { timeoutsFired++; - equal(this, comp, 'Timeout fn has the component as its context'); - ok(true, 'Timeout created and fired.'); + QUnit.equal(this, comp, 'Timeout fn has the component as its context'); + QUnit.ok(true, 'Timeout created and fired.'); }, 100); - var timeoutToClear = comp.setTimeout(function() { - timeoutsFired++; - ok(false, 'Timeout should have been manually cleared'); - }, 500); - comp.setTimeout(function() { timeoutsFired++; - ok(false, 'Timeout should have been disposed'); + QUnit.ok(false, 'Timeout should have been disposed'); }, 1000); this.clock.tick(100); - ok(timeoutsFired === 1, 'One timeout should have fired by this point'); + QUnit.ok(timeoutsFired === 1, 'One timeout should have fired by this point'); comp.clearTimeout(timeoutToClear); @@ -720,47 +737,48 @@ test('should provide timeout methods that automatically get cleared on component this.clock.tick(1000); - ok(timeoutsFired === 1, 'One timeout should have fired overall'); + QUnit.ok(timeoutsFired === 1, 'One timeout should have fired overall'); }); -test('should provide interval methods that automatically get cleared on component disposal', function() { - expect(13); +QUnit.test('should provide interval methods that automatically get cleared on component disposal', function() { + const comp = new Component(getFakePlayer()); - var comp = new Component(getFakePlayer()); - var intervalsFired = 0; + let intervalsFired = 0; - var interval = comp.setInterval(function() { + const interval = comp.setInterval(function() { intervalsFired++; - equal(this, comp, 'Interval fn has the component as its context'); - ok(true, 'Interval created and fired.'); + QUnit.equal(this, comp, 'Interval fn has the component as its context'); + QUnit.ok(true, 'Interval created and fired.'); }, 100); + QUnit.expect(13); + comp.setInterval(function() { intervalsFired++; - ok(false, 'Interval should have been disposed'); + QUnit.ok(false, 'Interval should have been disposed'); }, 1200); this.clock.tick(500); - ok(intervalsFired === 5, 'Component interval fired 5 times'); + QUnit.ok(intervalsFired === 5, 'Component interval fired 5 times'); comp.clearInterval(interval); this.clock.tick(600); - ok(intervalsFired === 5, 'Interval was manually cleared'); + QUnit.ok(intervalsFired === 5, 'Interval was manually cleared'); comp.dispose(); this.clock.tick(1200); - ok(intervalsFired === 5, 'Interval was cleared when component was disposed'); + QUnit.ok(intervalsFired === 5, 'Interval was cleared when component was disposed'); }); -test('$ and $$ functions', function() { - var comp = new Component(getFakePlayer()); - var contentEl = document.createElement('div'); - var children = [ +QUnit.test('$ and $$ functions', function() { + const comp = new Component(getFakePlayer()); + const contentEl = document.createElement('div'); + const children = [ document.createElement('div'), document.createElement('div') ]; @@ -768,6 +786,6 @@ test('$ and $$ functions', function() { comp.contentEl_ = contentEl; children.forEach(child => contentEl.appendChild(child)); - strictEqual(comp.$('div'), children[0], '$ defaults to contentEl as scope'); - strictEqual(comp.$$('div').length, children.length, '$$ defaults to contentEl as scope'); + QUnit.strictEqual(comp.$('div'), children[0], '$ defaults to contentEl as scope'); + QUnit.strictEqual(comp.$$('div').length, children.length, '$$ defaults to contentEl as scope'); }); diff --git a/test/unit/controls.test.js b/test/unit/controls.test.js index d57e45e437..e2a8efc2fc 100644 --- a/test/unit/controls.test.js +++ b/test/unit/controls.test.js @@ -1,3 +1,4 @@ +/* eslint-env qunit */ import VolumeControl from '../../src/js/control-bar/volume-control/volume-control.js'; import MuteToggle from '../../src/js/control-bar/mute-toggle.js'; import PlaybackRateMenuButton from '../../src/js/control-bar/playback-rate-menu/playback-rate-menu-button.js'; @@ -5,110 +6,107 @@ import Slider from '../../src/js/slider/slider.js'; import TestHelpers from './test-helpers.js'; import document from 'global/document'; -q.module('Controls'); +QUnit.module('Controls'); -test('should hide volume control if it\'s not supported', function(){ - expect(2); - - var noop, player, volumeControl, muteToggle; - noop = function(){}; - player = { +QUnit.test('should hide volume control if it\'s not supported', function() { + QUnit.expect(2); + const noop = function() {}; + const player = { id: noop, on: noop, ready: noop, tech_: { - 'featuresVolumeControl': false + featuresVolumeControl: false }, - volume: function(){}, - muted: function(){}, - reportUserActivity: function(){} + volume() {}, + muted() {}, + reportUserActivity() {} }; - volumeControl = new VolumeControl(player); - muteToggle = new MuteToggle(player); + const volumeControl = new VolumeControl(player); + const muteToggle = new MuteToggle(player); - ok(volumeControl.el().className.indexOf('vjs-hidden') >= 0, 'volumeControl is not hidden'); - ok(muteToggle.el().className.indexOf('vjs-hidden') >= 0, 'muteToggle is not hidden'); + QUnit.ok(volumeControl.el().className.indexOf('vjs-hidden') >= 0, 'volumeControl is not hidden'); + QUnit.ok(muteToggle.el().className.indexOf('vjs-hidden') >= 0, 'muteToggle is not hidden'); }); -test('should test and toggle volume control on `loadstart`', function(){ - var noop, listeners, player, volumeControl, muteToggle, i; - noop = function(){}; - listeners = []; - player = { +QUnit.test('should test and toggle volume control on `loadstart`', function() { + const noop = function() {}; + const listeners = []; + const player = { id: noop, - on: function(event, callback){ + on(event, callback) { // don't fire dispose listeners if (event !== 'dispose') { listeners.push(callback); } }, ready: noop, - volume: function(){ + volume() { return 1; }, - muted: function(){ + muted() { return false; }, tech_: { - 'featuresVolumeControl': true + featuresVolumeControl: true }, - reportUserActivity: function(){} + reportUserActivity() {} }; - volumeControl = new VolumeControl(player); - muteToggle = new MuteToggle(player); + const volumeControl = new VolumeControl(player); + const muteToggle = new MuteToggle(player); - equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl is hidden initially'); - equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle is hidden initially'); + QUnit.equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl is hidden initially'); + QUnit.equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle is hidden initially'); - player.tech_['featuresVolumeControl'] = false; - for (i = 0; i < listeners.length; i++) { + player.tech_.featuresVolumeControl = false; + for (let i = 0; i < listeners.length; i++) { listeners[i](); } - equal(volumeControl.hasClass('vjs-hidden'), true, 'volumeControl does not hide itself'); - equal(muteToggle.hasClass('vjs-hidden'), true, 'muteToggle does not hide itself'); + QUnit.equal(volumeControl.hasClass('vjs-hidden'), true, 'volumeControl does not hide itself'); + QUnit.equal(muteToggle.hasClass('vjs-hidden'), true, 'muteToggle does not hide itself'); - player.tech_['featuresVolumeControl'] = true; - for (i = 0; i < listeners.length; i++) { + player.tech_.featuresVolumeControl = true; + for (let i = 0; i < listeners.length; i++) { listeners[i](); } - equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl does not show itself'); - equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle does not show itself'); + QUnit.equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl does not show itself'); + QUnit.equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle does not show itself'); }); -test('calculateDistance should use changedTouches, if available', function() { - var noop, player, slider, event; - noop = function(){}; - player = { +QUnit.test('calculateDistance should use changedTouches, if available', function() { + const noop = function() {}; + const player = { id: noop, on: noop, ready: noop, reportUserActivity: noop }; - slider = new Slider(player); + const slider = new Slider(player); + document.body.appendChild(slider.el_); slider.el_.style.position = 'absolute'; slider.el_.style.width = '200px'; slider.el_.style.left = '0px'; - event = { + const event = { pageX: 10, changedTouches: [{ pageX: 100 }] }; - equal(slider.calculateDistance(event), 0.5, 'we should have touched exactly in the center, so, the ratio should be half'); + QUnit.equal(slider.calculateDistance(event), 0.5, 'we should have touched exactly in the center, so, the ratio should be half'); }); -test('should hide playback rate control if it\'s not supported', function(){ - expect(1); +QUnit.test('should hide playback rate control if it\'s not supported', function() { + QUnit.expect(1); - var player = TestHelpers.makePlayer(); - var playbackRate = new PlaybackRateMenuButton(player); + const player = TestHelpers.makePlayer(); + const playbackRate = new PlaybackRateMenuButton(player); - ok(playbackRate.el().className.indexOf('vjs-hidden') >= 0, 'playbackRate is not hidden'); + QUnit.ok(playbackRate.el().className.indexOf('vjs-hidden') >= 0, 'playbackRate is not hidden'); }); diff --git a/test/unit/events.test.js b/test/unit/events.test.js index 1083c095d4..1f1add911e 100644 --- a/test/unit/events.test.js +++ b/test/unit/events.test.js @@ -1,28 +1,31 @@ +/* eslint-env qunit */ import * as Events from '../../src/js/utils/events.js'; import document from 'global/document'; -q.module('Events'); +QUnit.module('Events'); -test('should add and remove an event listener to an element', function(){ - expect(1); +QUnit.test('should add and remove an event listener to an element', function() { + QUnit.expect(1); - var el = document.createElement('div'); - var listener = function(){ - ok(true, 'Click Triggered'); + const el = document.createElement('div'); + const listener = function() { + QUnit.ok(true, 'Click Triggered'); }; Events.on(el, 'click', listener); - Events.trigger(el, 'click'); // 1 click + // 1 click + Events.trigger(el, 'click'); Events.off(el, 'click', listener); - Events.trigger(el, 'click'); // No click should happen. + // No click should happen. + Events.trigger(el, 'click'); }); -test('should add and remove multiple event listeners to an element with a single call', function(){ - expect(6); +QUnit.test('should add and remove multiple event listeners to an element with a single call', function() { + QUnit.expect(6); - var el = document.createElement('div'); - var listener = function(){ - ok(true, 'Callback triggered'); + const el = document.createElement('div'); + const listener = function() { + QUnit.ok(true, 'Callback triggered'); }; Events.on(el, ['click', 'event1', 'event2'], listener); @@ -30,92 +33,101 @@ test('should add and remove multiple event listeners to an element with a single Events.trigger(el, 'click'); Events.trigger(el, 'click'); Events.off(el, 'click', listener); - Events.trigger(el, 'click'); // No click should happen. + // No click should happen. + Events.trigger(el, 'click'); Events.trigger(el, 'event1'); Events.trigger(el, 'event1'); Events.off(el, 'event1', listener); - Events.trigger(el, 'event1'); // No event1 should happen. + // No event1 should happen. + Events.trigger(el, 'event1'); Events.trigger(el, 'event2'); Events.trigger(el, 'event2'); Events.off(el, 'event2', listener); - Events.trigger(el, 'event2'); // No event2 should happen. + // No event2 should happen. + Events.trigger(el, 'event2'); }); -test('should be possible to pass data when you trigger an event', function () { - expect(6); - var el = document.createElement('div'); - var fakeData1 = 'Fake Data 1'; - var fakeData2 = {txt: 'Fake Data 2'}; +QUnit.test('should be possible to pass data when you trigger an event', function() { + QUnit.expect(6); + const el = document.createElement('div'); + const fakeData1 = 'Fake Data 1'; + const fakeData2 = {txt: 'Fake Data 2'}; - var listener = function(evt, hash){ - ok(true, 'Callback triggered'); + const listener = function(evt, hash) { + QUnit.ok(true, 'Callback triggered'); deepEqual(fakeData1, hash.d1, 'Shoulbe be passed to the handler'); deepEqual(fakeData2, hash.d2, 'Shoulbe be passed to the handler'); }; Events.on(el, ['event1', 'event2'], listener); - Events.trigger(el, 'event1', { d1: fakeData1, d2:fakeData2}); - Events.trigger(el, 'event2', { d1: fakeData1, d2:fakeData2}); + Events.trigger(el, 'event1', { d1: fakeData1, d2: fakeData2}); + Events.trigger(el, 'event2', { d1: fakeData1, d2: fakeData2}); }); -test('should remove all listeners of a type', function(){ - var el = document.createElement('div'); - var clicks = 0; - var listener = function(){ +QUnit.test('should remove all listeners of a type', function() { + const el = document.createElement('div'); + let clicks = 0; + const listener = function() { clicks++; }; - var listener2 = function(){ + const listener2 = function() { clicks++; }; Events.on(el, 'click', listener); Events.on(el, 'click', listener2); - Events.trigger(el, 'click'); // 2 clicks + // 2 clicks + Events.trigger(el, 'click'); - ok(clicks === 2, 'both click listeners fired'); + QUnit.ok(clicks === 2, 'both click listeners fired'); Events.off(el, 'click'); - Events.trigger(el, 'click'); // No click should happen. + // No click should happen. + Events.trigger(el, 'click'); - ok(clicks === 2, 'no click listeners fired'); + QUnit.ok(clicks === 2, 'no click listeners fired'); }); -test('should remove all listeners of an array of types', function(){ - var el = document.createElement('div'); - var calls = 0; - var listener = function(){ +QUnit.test('should remove all listeners of an array of types', function() { + const el = document.createElement('div'); + let calls = 0; + const listener = function() { calls++; }; - var listener2 = function(){ + const listener2 = function() { calls++; }; Events.on(el, ['click', 'event1'], listener); Events.on(el, ['click', 'event1'], listener2); - Events.trigger(el, 'click'); // 2 calls - Events.trigger(el, 'event1'); // 2 calls + // 2 calls + Events.trigger(el, 'click'); + // 2 calls + Events.trigger(el, 'event1'); - ok(calls === 4, 'both click listeners fired'); + QUnit.ok(calls === 4, 'both click listeners fired'); Events.off(el, ['click', 'event1']); - Events.trigger(el, 'click'); // No click should happen. - Events.trigger(el, 'event1'); // No event1 should happen. + // No click should happen. + Events.trigger(el, 'click'); + // No event1 should happen. + Events.trigger(el, 'event1'); - ok(calls === 4, 'no event listeners fired'); + QUnit.ok(calls === 4, 'no event listeners fired'); }); -test('should remove all listeners from an element', function(){ - expect(2); +QUnit.test('should remove all listeners from an element', function() { + QUnit.expect(2); - var el = document.createElement('div'); - var listener = function(){ - ok(true, 'Fake1 Triggered'); + const el = document.createElement('div'); + const listener = function() { + QUnit.ok(true, 'Fake1 Triggered'); }; - var listener2 = function(){ - ok(true, 'Fake2 Triggered'); + const listener2 = function() { + QUnit.ok(true, 'Fake2 Triggered'); }; Events.on(el, 'fake1', listener); @@ -131,111 +143,119 @@ test('should remove all listeners from an element', function(){ Events.trigger(el, 'fake2'); }); -test('should listen only once', function(){ - expect(1); +QUnit.test('should listen only once', function() { + QUnit.expect(1); - var el = document.createElement('div'); - var listener = function(){ - ok(true, 'Click Triggered'); + const el = document.createElement('div'); + const listener = function() { + QUnit.ok(true, 'Click Triggered'); }; Events.one(el, 'click', listener); - Events.trigger(el, 'click'); // 1 click - Events.trigger(el, 'click'); // No click should happen. + // 1 click + Events.trigger(el, 'click'); + // No click should happen. + Events.trigger(el, 'click'); }); -test( 'should listen only once in multiple events from a single call', function(){ - expect(3); +QUnit.test('should listen only once in multiple events from a single call', function() { + QUnit.expect(3); - var el = document.createElement('div'); - var listener = function(){ - ok(true, 'Callback Triggered'); + const el = document.createElement('div'); + const listener = function() { + QUnit.ok(true, 'Callback Triggered'); }; Events.one(el, ['click', 'event1', 'event2'], listener); - Events.trigger(el, 'click'); // 1 click - Events.trigger(el, 'click'); // No click should happen. - Events.trigger(el, 'event1'); // event1 must be handled. - Events.trigger(el, 'event1'); // No event1 should be handled. - Events.trigger(el, 'event2'); // event2 must be handled. - Events.trigger(el, 'event2'); // No event2 should be handled. + // 1 click + Events.trigger(el, 'click'); + // No click should happen. + Events.trigger(el, 'click'); + // event1 must be handled. + Events.trigger(el, 'event1'); + // No event1 should be handled. + Events.trigger(el, 'event1'); + // event2 must be handled. + Events.trigger(el, 'event2'); + // No event2 should be handled. + Events.trigger(el, 'event2'); }); -test('should stop immediate propagtion', function(){ - expect(1); +QUnit.test('should stop immediate propagtion', function() { + QUnit.expect(1); - var el = document.createElement('div'); + const el = document.createElement('div'); - Events.on(el, 'test', function(e){ - ok(true, 'First listener fired'); + Events.on(el, 'test', function(e) { + QUnit.ok(true, 'First listener fired'); e.stopImmediatePropagation(); }); - Events.on(el, 'test', function(e){ - ok(false, 'Second listener fired'); + Events.on(el, 'test', function(e) { + QUnit.ok(false, 'Second listener fired'); }); Events.trigger(el, 'test'); }); -test('should bubble up DOM unless bubbles == false', function(){ - expect(3); +QUnit.test('should bubble up DOM unless bubbles == false', function() { + QUnit.expect(3); - var outer = document.createElement('div'); - var inner = outer.appendChild(document.createElement('div')); + const outer = document.createElement('div'); + const inner = outer.appendChild(document.createElement('div')); // Verify that if bubbles === true, event bubbles up dom. - Events.on(inner, 'bubbles', function(e){ - ok(true, 'Inner listener fired'); + Events.on(inner, 'bubbles', function(e) { + QUnit.ok(true, 'Inner listener fired'); }); - Events.on(outer, 'bubbles', function(e){ - ok(true, 'Outer listener fired'); + Events.on(outer, 'bubbles', function(e) { + QUnit.ok(true, 'Outer listener fired'); }); - Events.trigger(inner, { type:'bubbles', target:inner, bubbles:true }); + Events.trigger(inner, { type: 'bubbles', target: inner, bubbles: true }); // Only change 'bubbles' to false, and verify only inner handler is called. - Events.on(inner, 'nobub', function(e){ - ok(true, 'Inner listener fired'); + Events.on(inner, 'nobub', function(e) { + QUnit.ok(true, 'Inner listener fired'); }); - Events.on(outer, 'nobub', function(e){ - ok(false, 'Outer listener fired'); + Events.on(outer, 'nobub', function(e) { + QUnit.ok(false, 'Outer listener fired'); }); - Events.trigger(inner, { type:'nobub', target:inner, bubbles:false }); + Events.trigger(inner, { type: 'nobub', target: inner, bubbles: false }); }); -test('should have a defaultPrevented property on an event that was prevent from doing default action', function() { - expect(2); +QUnit.test('should have a defaultPrevented property on an event that was prevent from doing default action', function() { + QUnit.expect(2); - var el = document.createElement('div'); + const el = document.createElement('div'); - Events.on(el, 'test', function(e){ - ok(true, 'First listener fired'); + Events.on(el, 'test', function(e) { + QUnit.ok(true, 'First listener fired'); e.preventDefault(); }); - Events.on(el, 'test', function(e){ - ok(e.defaultPrevented, 'Should have `defaultPrevented` to signify preventDefault being called'); + Events.on(el, 'test', function(e) { + QUnit.ok(e.defaultPrevented, 'Should have `defaultPrevented` to signify preventDefault being called'); }); Events.trigger(el, 'test'); }); -test('should have relatedTarget correctly set on the event', function() { - expect(2); +QUnit.test('should have relatedTarget correctly set on the event', function() { + QUnit.expect(2); - var el1 = document.createElement('div'), - el2 = document.createElement('div'), - relatedEl = document.createElement('div'); + const el1 = document.createElement('div'); + const el2 = document.createElement('div'); + const relatedEl = document.createElement('div'); - Events.on(el1, 'click', function(e){ - equal(e.relatedTarget, relatedEl, 'relatedTarget is set for all browsers when related element is set on the event'); + Events.on(el1, 'click', function(e) { + QUnit.equal(e.relatedTarget, relatedEl, 'relatedTarget is set for all browsers when related element is set on the event'); }); - Events.trigger(el1, { type:'click', relatedTarget:relatedEl }); + Events.trigger(el1, { type: 'click', relatedTarget: relatedEl }); Events.on(el2, 'click', function(e) { - equal(e.relatedTarget, null, 'relatedTarget is null when none is provided'); + QUnit.equal(e.relatedTarget, null, 'relatedTarget is null when none is provided'); }); - Events.trigger(el2, { type:'click', relatedTarget:undefined }); + Events.trigger(el2, { type: 'click', relatedTarget: undefined }); }); diff --git a/test/unit/extend.test.js b/test/unit/extend.test.js index 12d8ae2894..3b5c7da469 100644 --- a/test/unit/extend.test.js +++ b/test/unit/extend.test.js @@ -1,16 +1,18 @@ +/* eslint-env qunit */ import extendFn from '../../src/js/extend.js'; -q.module('extend.js'); +QUnit.module('extend.js'); -test('should add implicit parent constructor call', function(){ - var superCalled = false; - var Parent = function() { +QUnit.test('should add implicit parent constructor call', function() { + let superCalled = false; + const Parent = function() { superCalled = true; }; - var Child = extendFn(Parent, { - foo: 'bar' + const Child = extendFn(Parent, { + foo: 'bar' }); - var child = new Child(); - ok(superCalled, 'super constructor called'); - ok(child.foo, 'child properties set'); + const child = new Child(); + + QUnit.ok(superCalled, 'super constructor called'); + QUnit.ok(child.foo, 'child properties set'); }); diff --git a/test/unit/menu.test.js b/test/unit/menu.test.js index a7345e7ff9..12ec097f07 100644 --- a/test/unit/menu.test.js +++ b/test/unit/menu.test.js @@ -1,75 +1,75 @@ +/* eslint-env qunit */ import MenuButton from '../../src/js/menu/menu-button.js'; import TestHelpers from './test-helpers.js'; import * as Events from '../../src/js/utils/events.js'; -q.module('MenuButton'); +QUnit.module('MenuButton'); -q.test('should not throw an error when there is no children', function() { - expect(0); - let player = TestHelpers.makePlayer(); +QUnit.test('should not throw an error when there is no children', function() { + QUnit.expect(0); + const player = TestHelpers.makePlayer(); - let menuButton = new MenuButton(player); - let el = menuButton.el(); + const menuButton = new MenuButton(player); + const el = menuButton.el(); try { Events.trigger(el, 'click'); } catch (error) { - ok(!error, 'click should not throw anything'); + QUnit.ok(!error, 'click should not throw anything'); } player.dispose(); }); -q.test('should place title list item into ul', function() { - var player, menuButton; - player = TestHelpers.makePlayer(); +QUnit.test('should place title list item into ul', function() { + const player = TestHelpers.makePlayer(); - menuButton = new MenuButton(player, { - 'title': 'testTitle' + const menuButton = new MenuButton(player, { + title: 'testTitle' }); - let menuContentElement = menuButton.el().getElementsByTagName('UL')[0]; - let titleElement = menuContentElement.children[0]; + const menuContentElement = menuButton.el().getElementsByTagName('UL')[0]; + const titleElement = menuContentElement.children[0]; - ok(titleElement.innerHTML === 'TestTitle', 'title element placed in ul'); + QUnit.ok(titleElement.innerHTML === 'TestTitle', 'title element placed in ul'); player.dispose(); }); -q.test('clicking should display the menu', function() { - expect(6); +QUnit.test('clicking should display the menu', function() { + QUnit.expect(6); - let player = TestHelpers.makePlayer(); + const player = TestHelpers.makePlayer(); // Make sure there's some content in the menu, even if it's just a title! - let menuButton = new MenuButton(player, { - 'title': 'testTitle' + const menuButton = new MenuButton(player, { + title: 'testTitle' }); - let el = menuButton.el(); + const el = menuButton.el(); - ok(menuButton.menu !== undefined, 'menu is created'); + QUnit.ok(menuButton.menu !== undefined, 'menu is created'); - equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'menu defaults to hidden'); + QUnit.equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'menu defaults to hidden'); Events.trigger(el, 'click'); - equal(menuButton.menu.hasClass('vjs-lock-showing'), true, 'clicking on the menu button shows the menu'); + QUnit.equal(menuButton.menu.hasClass('vjs-lock-showing'), true, 'clicking on the menu button shows the menu'); Events.trigger(el, 'click'); - equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'clicking again on the menu button hides the menu'); + QUnit.equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'clicking again on the menu button hides the menu'); menuButton.disable(); Events.trigger(el, 'click'); - equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'disable() prevents clicking from showing the menu'); + QUnit.equal(menuButton.menu.hasClass('vjs-lock-showing'), false, 'disable() prevents clicking from showing the menu'); menuButton.enable(); Events.trigger(el, 'click'); - equal(menuButton.menu.hasClass('vjs-lock-showing'), true, 'enable() allows clicking to show the menu'); + QUnit.equal(menuButton.menu.hasClass('vjs-lock-showing'), true, 'enable() allows clicking to show the menu'); player.dispose(); }); diff --git a/test/unit/modal-dialog.test.js b/test/unit/modal-dialog.test.js index 1e04c994f5..8d094515f2 100644 --- a/test/unit/modal-dialog.test.js +++ b/test/unit/modal-dialog.test.js @@ -1,28 +1,29 @@ +/* eslint-env qunit */ import CloseButton from '../../src/js/close-button'; +import sinon from 'sinon'; import ModalDialog from '../../src/js/modal-dialog'; import * as Dom from '../../src/js/utils/dom'; -import * as Fn from '../../src/js/utils/fn'; import TestHelpers from './test-helpers'; -var ESC = 27; +const ESC = 27; -q.module('ModalDialog', { +QUnit.module('ModalDialog', { - beforeEach: function() { + beforeEach() { this.player = TestHelpers.makePlayer(); this.modal = new ModalDialog(this.player, {temporary: false}); this.el = this.modal.el(); }, - afterEach: function() { + afterEach() { this.player.dispose(); this.modal.dispose(); this.el = null; } }); -q.test('should create the expected element', function(assert) { - let elAssertions = TestHelpers.assertEl(assert, this.el, { +QUnit.test('should create the expected element', function(assert) { + const elAssertions = TestHelpers.assertEl(assert, this.el, { tagName: 'div', classes: [ 'vjs-modal-dialog', @@ -43,8 +44,8 @@ q.test('should create the expected element', function(assert) { elAssertions(); }); -q.test('should create the expected description element', function(assert) { - let elAssertions = TestHelpers.assertEl(assert, this.modal.descEl_, { +QUnit.test('should create the expected description element', function(assert) { + const elAssertions = TestHelpers.assertEl(assert, this.modal.descEl_, { tagName: 'p', innerHTML: this.modal.description(), classes: [ @@ -60,8 +61,8 @@ q.test('should create the expected description element', function(assert) { elAssertions(); }); -q.test('should create the expected contentEl', function(assert) { - let elAssertions = TestHelpers.assertEl(assert, this.modal.contentEl(), { +QUnit.test('should create the expected contentEl', function(assert) { + const elAssertions = TestHelpers.assertEl(assert, this.modal.contentEl(), { tagName: 'div', classes: [ 'vjs-modal-dialog-content' @@ -75,8 +76,8 @@ q.test('should create the expected contentEl', function(assert) { elAssertions(); }); -q.test('should create a close button by default', function(assert) { - var btn = this.modal.getChild('closeButton'); +QUnit.test('should create a close button by default', function(assert) { + const btn = this.modal.getChild('closeButton'); // We only check the aspects of the button that relate to the modal. Other // aspects of the button (classes, etc) are tested in their appropriate test @@ -86,8 +87,8 @@ q.test('should create a close button by default', function(assert) { assert.strictEqual(btn.el().parentNode, this.el, 'close button is a child of el'); }); -q.test('returns `this` for expected methods', function(assert) { - var methods = ['close', 'empty', 'fill', 'fillWith', 'open']; +QUnit.test('returns `this` for expected methods', function(assert) { + const methods = ['close', 'empty', 'fill', 'fillWith', 'open']; assert.expect(methods.length); methods.forEach(function(method) { @@ -95,13 +96,13 @@ q.test('returns `this` for expected methods', function(assert) { }, this.modal); }); -q.test('open() triggers events', function(assert) { - var modal = this.modal; - var beforeModalOpenSpy = sinon.spy(function() { +QUnit.test('open() triggers events', function(assert) { + const modal = this.modal; + const beforeModalOpenSpy = sinon.spy(function() { assert.notOk(modal.opened(), 'modal is not opened before opening event'); }); - var modalOpenSpy = sinon.spy(function() { + const modalOpenSpy = sinon.spy(function() { assert.ok(modal.opened(), 'modal is opened on opening event'); }); @@ -116,15 +117,15 @@ q.test('open() triggers events', function(assert) { assert.strictEqual(modalOpenSpy.callCount, 1, 'modalopen spy was called'); }); -q.test('open() removes "vjs-hidden" class', function(assert) { +QUnit.test('open() removes "vjs-hidden" class', function(assert) { assert.expect(2); assert.ok(this.modal.hasClass('vjs-hidden'), 'modal starts hidden'); this.modal.open(); assert.notOk(this.modal.hasClass('vjs-hidden'), 'modal is not hidden after opening'); }); -q.test('open() cannot be called on an opened modal', function(assert) { - var spy = sinon.spy(); +QUnit.test('open() cannot be called on an opened modal', function(assert) { + const spy = sinon.spy(); this.modal.on('modalopen', spy).open().open(); @@ -132,13 +133,13 @@ q.test('open() cannot be called on an opened modal', function(assert) { assert.strictEqual(spy.callCount, 1, 'modal was only opened once'); }); -q.test('close() triggers events', function(assert) { - var modal = this.modal; - var beforeModalCloseSpy = sinon.spy(function() { +QUnit.test('close() triggers events', function(assert) { + const modal = this.modal; + const beforeModalCloseSpy = sinon.spy(function() { assert.ok(modal.opened(), 'modal is not closed before closing event'); }); - var modalCloseSpy = sinon.spy(function() { + const modalCloseSpy = sinon.spy(function() { assert.notOk(modal.opened(), 'modal is closed on closing event'); }); @@ -154,14 +155,14 @@ q.test('close() triggers events', function(assert) { assert.strictEqual(modalCloseSpy.callCount, 1, 'modalclose spy was called'); }); -q.test('close() adds the "vjs-hidden" class', function(assert) { +QUnit.test('close() adds the "vjs-hidden" class', function(assert) { assert.expect(1); this.modal.open().close(); assert.ok(this.modal.hasClass('vjs-hidden'), 'modal is hidden upon close'); }); -q.test('pressing ESC triggers close(), but only when the modal is opened', function(assert) { - var spy = sinon.spy(); +QUnit.test('pressing ESC triggers close(), but only when the modal is opened', function(assert) { + const spy = sinon.spy(); this.modal.on('modalclose', spy).handleKeyPress({which: ESC}); assert.expect(2); @@ -171,8 +172,8 @@ q.test('pressing ESC triggers close(), but only when the modal is opened', funct assert.strictEqual(spy.callCount, 1, 'ESC closed the now-opened modal'); }); -q.test('close() cannot be called on a closed modal', function(assert) { - var spy = sinon.spy(); +QUnit.test('close() cannot be called on a closed modal', function(assert) { + const spy = sinon.spy(); this.modal.on('modalclose', spy); this.modal.open().close().close(); @@ -181,9 +182,9 @@ q.test('close() cannot be called on a closed modal', function(assert) { assert.strictEqual(spy.callCount, 1, 'modal was only closed once'); }); -q.test('open() pauses playback, close() resumes', function(assert) { - var playSpy = sinon.spy(); - var pauseSpy = sinon.spy(); +QUnit.test('open() pauses playback, close() resumes', function(assert) { + const playSpy = sinon.spy(); + const pauseSpy = sinon.spy(); // Quick and dirty; make it looks like the player is playing. this.player.paused = function() { @@ -207,7 +208,7 @@ q.test('open() pauses playback, close() resumes', function(assert) { assert.strictEqual(playSpy.callCount, 1, 'player is resumed when the modal closes'); }); -q.test('open() hides controls, close() shows controls', function(assert) { +QUnit.test('open() hides controls, close() shows controls', function(assert) { this.modal.open(); assert.expect(2); @@ -217,9 +218,9 @@ q.test('open() hides controls, close() shows controls', function(assert) { assert.ok(this.player.controls_, 'controls are no longer hidden'); }); -q.test('opened()', function(assert) { - var openSpy = sinon.spy(); - var closeSpy = sinon.spy(); +QUnit.test('opened()', function(assert) { + const openSpy = sinon.spy(); + const closeSpy = sinon.spy(); assert.expect(4); assert.strictEqual(this.modal.opened(), false, 'the modal is closed'); @@ -238,23 +239,22 @@ q.test('opened()', function(assert) { assert.strictEqual(closeSpy.callCount, 1, 'modal was closed only once'); }); -q.test('content()', function(assert) { - var content; - +QUnit.test('content()', function(assert) { assert.expect(3); assert.strictEqual(typeof this.modal.content(), 'undefined', 'no content by default'); - content = this.modal.content(Dom.createEl()); + const content = this.modal.content(Dom.createEl()); + assert.ok(Dom.isEl(content), 'content was set from a single DOM element'); assert.strictEqual(this.modal.content(null), null, 'content was nullified'); }); -q.test('fillWith()', function(assert) { - var contentEl = this.modal.contentEl(); - var children = [Dom.createEl(), Dom.createEl(), Dom.createEl()]; - var beforeFillSpy = sinon.spy(); - var fillSpy = sinon.spy(); +QUnit.test('fillWith()', function(assert) { + const contentEl = this.modal.contentEl(); + const children = [Dom.createEl(), Dom.createEl(), Dom.createEl()]; + const beforeFillSpy = sinon.spy(); + const fillSpy = sinon.spy(); children.forEach(function(el) { contentEl.appendChild(el); @@ -278,9 +278,9 @@ q.test('fillWith()', function(assert) { assert.strictEqual(fillSpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal'); }); -q.test('empty()', function(assert) { - var beforeEmptySpy = sinon.spy(); - var emptySpy = sinon.spy(); +QUnit.test('empty()', function(assert) { + const beforeEmptySpy = sinon.spy(); + const emptySpy = sinon.spy(); this.modal. fillWith([Dom.createEl(), Dom.createEl()]). @@ -296,8 +296,8 @@ q.test('empty()', function(assert) { assert.strictEqual(emptySpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal'); }); -q.test('closeable()', function(assert) { - let initialCloseButton = this.modal.getChild('closeButton'); +QUnit.test('closeable()', function(assert) { + const initialCloseButton = this.modal.getChild('closeButton'); assert.expect(8); assert.strictEqual(this.modal.closeable(), true, 'the modal is closed'); @@ -322,13 +322,13 @@ q.test('closeable()', function(assert) { assert.notOk(this.modal.opened(), 'the modal was closed by the ESC key'); }); -q.test('"content" option (fills on first open() invocation)', function(assert) { - var modal = new ModalDialog(this.player, { +QUnit.test('"content" option (fills on first open() invocation)', function(assert) { + const modal = new ModalDialog(this.player, { content: Dom.createEl(), temporary: false }); - var spy = sinon.spy(); + const spy = sinon.spy(); modal.on('modalfill', spy); modal.open().close().open(); @@ -339,11 +339,11 @@ q.test('"content" option (fills on first open() invocation)', function(assert) { assert.strictEqual(modal.contentEl().firstChild, modal.options_.content, 'has the expected content in the DOM'); }); -q.test('"temporary" option', function(assert) { - var temp = new ModalDialog(this.player, {temporary: true}); - var tempSpy = sinon.spy(); - var perm = new ModalDialog(this.player, {temporary: false}); - var permSpy = sinon.spy(); +QUnit.test('"temporary" option', function(assert) { + const temp = new ModalDialog(this.player, {temporary: true}); + const tempSpy = sinon.spy(); + const perm = new ModalDialog(this.player, {temporary: false}); + const permSpy = sinon.spy(); temp.on('dispose', tempSpy); perm.on('dispose', permSpy); @@ -355,14 +355,14 @@ q.test('"temporary" option', function(assert) { assert.strictEqual(permSpy.callCount, 0, 'permanent modals are not disposed'); }); -q.test('"fillAlways" option', function(assert) { - var modal = new ModalDialog(this.player, { +QUnit.test('"fillAlways" option', function(assert) { + const modal = new ModalDialog(this.player, { content: 'foo', fillAlways: true, temporary: false }); - var spy = sinon.spy(); + const spy = sinon.spy(); modal.on('modalfill', spy); modal.open().close().open(); @@ -371,21 +371,21 @@ q.test('"fillAlways" option', function(assert) { assert.strictEqual(spy.callCount, 2, 'the modal was filled on each open call'); }); -q.test('"label" option', function(assert) { - var label = 'foo'; - var modal = new ModalDialog(this.player, {label: label}); +QUnit.test('"label" option', function(assert) { + const label = 'foo'; + const modal = new ModalDialog(this.player, {label}); assert.expect(1); assert.strictEqual(modal.el().getAttribute('aria-label'), label, 'uses the label as the aria-label'); }); -q.test('"uncloseable" option', function(assert) { - var modal = new ModalDialog(this.player, { +QUnit.test('"uncloseable" option', function(assert) { + const modal = new ModalDialog(this.player, { temporary: false, uncloseable: true }); - var spy = sinon.spy(); + const spy = sinon.spy(); modal.on('modalclose', spy); diff --git a/test/unit/player.test.js b/test/unit/player.test.js index fb906e6a76..6dc5af6933 100644 --- a/test/unit/player.test.js +++ b/test/unit/player.test.js @@ -1,3 +1,4 @@ +/* eslint-env qunit */ import Player from '../../src/js/player.js'; import videojs from '../../src/js/video.js'; import * as Dom from '../../src/js/utils/dom.js'; @@ -7,126 +8,134 @@ import MediaError from '../../src/js/media-error.js'; import Html5 from '../../src/js/tech/html5.js'; import TestHelpers from './test-helpers.js'; import document from 'global/document'; +import sinon from 'sinon'; import window from 'global/window'; import Tech from '../../src/js/tech/tech.js'; import TechFaker from './tech/tech-faker.js'; -q.module('Player', { - 'setup': function() { +QUnit.module('Player', { + setup() { this.clock = sinon.useFakeTimers(); }, - 'teardown': function() { + teardown() { this.clock.restore(); } }); -test('should create player instance that inherits from component and dispose it', function(){ - var player = TestHelpers.makePlayer(); +QUnit.test('should create player instance that inherits from component and dispose it', function() { + const player = TestHelpers.makePlayer(); - ok(player.el().nodeName === 'DIV'); - ok(player.on, 'component function exists'); + QUnit.ok(player.el().nodeName === 'DIV'); + QUnit.ok(player.on, 'component function exists'); player.dispose(); - ok(player.el() === null, 'element disposed'); + QUnit.ok(player.el() === null, 'element disposed'); }); -test('dispose should not throw if styleEl is missing', function(){ - var player = TestHelpers.makePlayer(); +QUnit.test('dispose should not throw if styleEl is missing', function() { + const player = TestHelpers.makePlayer(); player.styleEl_.parentNode.removeChild(player.styleEl_); player.dispose(); - ok(player.el() === null, 'element disposed'); + QUnit.ok(player.el() === null, 'element disposed'); }); // technically, all uses of videojs.options should be replaced with // Player.prototype.options_ in this file and a equivalent test using // videojs.options should be made in video.test.js. Keeping this here // until we make that move. -test('should accept options from multiple sources and override in correct order', function(){ +QUnit.test('should accept options from multiple sources and override in correct order', function() { // Set a global option videojs.options.attr = 1; - let tag0 = TestHelpers.makeTag(); - let player0 = new Player(tag0, { techOrder: ['techFaker'] }); + const tag0 = TestHelpers.makeTag(); + const player0 = new Player(tag0, { techOrder: ['techFaker'] }); - equal(player0.options_.attr, 1, 'global option was set'); + QUnit.equal(player0.options_.attr, 1, 'global option was set'); player0.dispose(); // Set a tag level option - let tag2 = TestHelpers.makeTag(); - tag2.setAttribute('attr', 'asdf'); // Attributes must be set as strings + const tag2 = TestHelpers.makeTag(); - let player2 = new Player(tag2, { techOrder: ['techFaker'] }); - equal(player2.options_.attr, 'asdf', 'Tag options overrode global options'); + // Attributes must be set as strings + tag2.setAttribute('attr', 'asdf'); + + const player2 = new Player(tag2, { techOrder: ['techFaker'] }); + + QUnit.equal(player2.options_.attr, 'asdf', 'Tag options overrode global options'); player2.dispose(); // Set a tag level option - let tag3 = TestHelpers.makeTag(); + const tag3 = TestHelpers.makeTag(); + tag3.setAttribute('attr', 'asdf'); - let player3 = new Player(tag3, { techOrder: ['techFaker'], 'attr': 'fdsa' }); - equal(player3.options_.attr, 'fdsa', 'Init options overrode tag and global options'); + const player3 = new Player(tag3, { techOrder: ['techFaker'], attr: 'fdsa' }); + + QUnit.equal(player3.options_.attr, 'fdsa', 'Init options overrode tag and global options'); player3.dispose(); }); -test('should get tag, source, and track settings', function(){ +QUnit.test('should get tag, source, and track settings', function() { // Partially tested in lib->getElAttributes - var fixture = document.getElementById('qunit-fixture'); + const fixture = document.getElementById('qunit-fixture'); - var html = ''; + let html = ''; fixture.innerHTML += html; - var tag = document.getElementById('example_1'); - var player = TestHelpers.makePlayer({}, tag); + const tag = document.getElementById('example_1'); + const player = TestHelpers.makePlayer({}, tag); - equal(player.options_.autoplay, true, 'autoplay is set to true'); - equal(player.options_.preload, 'none', 'preload is set to none'); - equal(player.options_.id, 'example_1', 'id is set to example_1'); - equal(player.options_.sources.length, 2, 'we have two sources'); - equal(player.options_.sources[0].src, 'http://google.com', 'first source is google.com'); - equal(player.options_.sources[0].type, 'video/mp4', 'first time is video/mp4'); - equal(player.options_.sources[1].type, 'video/webm', 'second type is video/webm'); - equal(player.options_.tracks.length, 1, 'we have one text track'); - equal(player.options_.tracks[0].kind, 'captions', 'the text track is a captions file'); - equal(player.options_.tracks[0].attrtest, '', 'we have an empty attribute called attrtest'); + QUnit.equal(player.options_.autoplay, true, 'autoplay is set to true'); + QUnit.equal(player.options_.preload, 'none', 'preload is set to none'); + QUnit.equal(player.options_.id, 'example_1', 'id is set to example_1'); + QUnit.equal(player.options_.sources.length, 2, 'we have two sources'); + QUnit.equal(player.options_.sources[0].src, 'http://google.com', 'first source is google.com'); + QUnit.equal(player.options_.sources[0].type, 'video/mp4', 'first time is video/mp4'); + QUnit.equal(player.options_.sources[1].type, 'video/webm', 'second type is video/webm'); + QUnit.equal(player.options_.tracks.length, 1, 'we have one text track'); + QUnit.equal(player.options_.tracks[0].kind, 'captions', 'the text track is a captions file'); + QUnit.equal(player.options_.tracks[0].attrtest, '', 'we have an empty attribute called attrtest'); - notEqual(player.el().className.indexOf('video-js'), -1, 'transferred class from tag to player div'); - equal(player.el().id,'example_1', 'transferred id from tag to player div'); + QUnit.notEqual(player.el().className.indexOf('video-js'), -1, 'transferred class from tag to player div'); + QUnit.equal(player.el().id, 'example_1', 'transferred id from tag to player div'); - equal(Player.players[player.id()], player, 'player referenceable from global list'); - notEqual(tag.id, player.id, 'tag ID no longer is the same as player ID'); - notEqual(tag.className, player.el().className, 'tag classname updated'); + QUnit.equal(Player.players[player.id()], player, 'player referenceable from global list'); + QUnit.notEqual(tag.id, player.id, 'tag ID no longer is the same as player ID'); + QUnit.notEqual(tag.className, player.el().className, 'tag classname updated'); player.dispose(); - notEqual(tag['player'], player, 'tag player ref killed'); - ok(!Player.players['example_1'], 'global player ref killed'); - equal(player.el(), null, 'player el killed'); + QUnit.notEqual(tag.player, player, 'tag player ref killed'); + QUnit.ok(!Player.players.example_1, 'global player ref killed'); + QUnit.equal(player.el(), null, 'player el killed'); }); -test('should asynchronously fire error events during source selection', function() { - expect(2); +QUnit.test('should asynchronously fire error events during source selection', function() { + QUnit.expect(2); sinon.stub(log, 'error'); - var player = TestHelpers.makePlayer({ - 'techOrder': ['foo'], - 'sources': [ - { 'src': 'http://vjs.zencdn.net/v/oceans.mp4', 'type': 'video/mp4' } + const player = TestHelpers.makePlayer({ + techOrder: ['foo'], + sources: [ + { src: 'http://vjs.zencdn.net/v/oceans.mp4', type: 'video/mp4' } ] }); - ok(player.options_['techOrder'][0] === 'foo', 'Foo listed as the only tech'); + + QUnit.ok(player.options_.techOrder[0] === 'foo', 'Foo listed as the only tech'); player.on('error', function(e) { - ok(player.error().code === 4, 'Source could not be played error thrown'); + QUnit.ok(player.error().code === 4, 'Source could not be played error thrown'); }); this.clock.tick(1); @@ -135,17 +144,17 @@ test('should asynchronously fire error events during source selection', function log.error.restore(); }); -test('should set the width, height, and aspect ratio via a css class', function(){ - let player = TestHelpers.makePlayer(); - let getStyleText = function(styleEl){ +QUnit.test('should set the width, height, and aspect ratio via a css class', function() { + const player = TestHelpers.makePlayer(); + const getStyleText = function(styleEl) { return (styleEl.styleSheet && styleEl.styleSheet.cssText) || styleEl.innerHTML; }; // NOTE: was using npm/css to parse the actual CSS ast // but the css module doesn't support ie8 - let confirmSetting = function(prop, val) { + const confirmSetting = function(prop, val) { let styleText = getStyleText(player.styleEl_); - let re = new RegExp(prop+':\\s?'+val); + const re = new RegExp(prop + ':\\s?' + val); // Lowercase string for IE8 styleText = styleText.toLowerCase(); @@ -154,86 +163,87 @@ test('should set the width, height, and aspect ratio via a css class', function( }; // Initial state - ok(!getStyleText(player.styleEl_), 'style element should be empty when the player is given no dimensions'); + QUnit.ok(!getStyleText(player.styleEl_), 'style element should be empty when the player is given no dimensions'); // Set only the width player.width(100); - ok(confirmSetting('width', '100px'), 'style width should equal the supplied width in pixels'); - ok(confirmSetting('height', '56.25px'), 'style height should match the default aspect ratio of the width'); + QUnit.ok(confirmSetting('width', '100px'), 'style width should equal the supplied width in pixels'); + QUnit.ok(confirmSetting('height', '56.25px'), 'style height should match the default aspect ratio of the width'); // Set the height player.height(200); - ok(confirmSetting('height', '200px'), 'style height should match the supplied height in pixels'); + QUnit.ok(confirmSetting('height', '200px'), 'style height should match the supplied height in pixels'); // Reset the width and height to defaults player.width(''); player.height(''); - ok(confirmSetting('width', '300px'), 'supplying an empty string should reset the width'); - ok(confirmSetting('height', '168.75px'), 'supplying an empty string should reset the height'); + QUnit.ok(confirmSetting('width', '300px'), 'supplying an empty string should reset the width'); + QUnit.ok(confirmSetting('height', '168.75px'), 'supplying an empty string should reset the height'); // Switch to fluid mode player.fluid(true); - ok(player.hasClass('vjs-fluid'), 'the vjs-fluid class should be added to the player'); - ok(confirmSetting('padding-top', '56.25%'), 'fluid aspect ratio should match the default aspect ratio'); + QUnit.ok(player.hasClass('vjs-fluid'), 'the vjs-fluid class should be added to the player'); + QUnit.ok(confirmSetting('padding-top', '56.25%'), 'fluid aspect ratio should match the default aspect ratio'); // Change the aspect ratio player.aspectRatio('4:1'); - ok(confirmSetting('padding-top', '25%'), 'aspect ratio percent should match the newly set aspect ratio'); + QUnit.ok(confirmSetting('padding-top', '25%'), 'aspect ratio percent should match the newly set aspect ratio'); }); -test('should use an class name that begins with an alpha character', function(){ - let alphaPlayer = TestHelpers.makePlayer({ id: 'alpha1' }); - let numericPlayer = TestHelpers.makePlayer({ id: '1numeric' }); +QUnit.test('should use an class name that begins with an alpha character', function() { + const alphaPlayer = TestHelpers.makePlayer({ id: 'alpha1' }); + const numericPlayer = TestHelpers.makePlayer({ id: '1numeric' }); - let getStyleText = function(styleEl){ + const getStyleText = function(styleEl) { return (styleEl.styleSheet && styleEl.styleSheet.cssText) || styleEl.innerHTML; }; alphaPlayer.width(100); numericPlayer.width(100); - ok(/\s*\.alpha1-dimensions\s*\{/.test(getStyleText(alphaPlayer.styleEl_)), 'appends -dimensions to an alpha player ID'); - ok(/\s*\.dimensions-1numeric\s*\{/.test(getStyleText(numericPlayer.styleEl_)), 'prepends dimensions- to a numeric player ID'); + QUnit.ok(/\s*\.alpha1-dimensions\s*\{/.test(getStyleText(alphaPlayer.styleEl_)), 'appends -dimensions to an alpha player ID'); + QUnit.ok(/\s*\.dimensions-1numeric\s*\{/.test(getStyleText(numericPlayer.styleEl_)), 'prepends dimensions- to a numeric player ID'); }); -test('should wrap the original tag in the player div', function(){ - var tag = TestHelpers.makeTag(); - var container = document.createElement('div'); - var fixture = document.getElementById('qunit-fixture'); +QUnit.test('should wrap the original tag in the player div', function() { + const tag = TestHelpers.makeTag(); + const container = document.createElement('div'); + const fixture = document.getElementById('qunit-fixture'); container.appendChild(tag); fixture.appendChild(container); - var player = new Player(tag, { techOrder: ['techFaker'] }); - var el = player.el(); + const player = new Player(tag, { techOrder: ['techFaker'] }); + const el = player.el(); - ok(el.parentNode === container, 'player placed at same level as tag'); + QUnit.ok(el.parentNode === container, 'player placed at same level as tag'); // Tag may be placed inside the player element or it may be removed from the DOM - ok(tag.parentNode !== container, 'tag removed from original place'); + QUnit.ok(tag.parentNode !== container, 'tag removed from original place'); player.dispose(); }); -test('should set and update the poster value', function(){ - var tag, poster, updatedPoster, player; +QUnit.test('should set and update the poster value', function() { + const poster = '#'; + const updatedPoster = 'http://example.com/updated-poster.jpg'; - poster = '#'; - updatedPoster = 'http://example.com/updated-poster.jpg'; + const tag = TestHelpers.makeTag(); - tag = TestHelpers.makeTag(); tag.setAttribute('poster', poster); - player = TestHelpers.makePlayer({}, tag); - equal(player.poster(), poster, 'the poster property should equal the tag attribute'); + const player = TestHelpers.makePlayer({}, tag); + + QUnit.equal(player.poster(), poster, 'the poster property should equal the tag attribute'); - var pcEmitted = false; - player.on('posterchange', function(){ + let pcEmitted = false; + + player.on('posterchange', function() { pcEmitted = true; }); player.poster(updatedPoster); - ok(pcEmitted, 'posterchange event was emitted'); - equal(player.poster(), updatedPoster, 'the updated poster is returned'); + QUnit.ok(pcEmitted, 'posterchange event was emitted'); + QUnit.equal(player.poster(), updatedPoster, 'the updated poster is returned'); player.dispose(); }); @@ -241,26 +251,24 @@ test('should set and update the poster value', function(){ // hasStarted() is equivalent to the "show poster flag" in the // standard, for the purpose of displaying the poster image // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play -test('should hide the poster when play is called', function() { - var player = TestHelpers.makePlayer({ +QUnit.test('should hide the poster when play is called', function() { + const player = TestHelpers.makePlayer({ poster: 'https://example.com/poster.jpg' }); - equal(player.hasStarted(), false, 'the show poster flag is true before play'); + QUnit.equal(player.hasStarted(), false, 'the show poster flag is true before play'); player.tech_.trigger('play'); - equal(player.hasStarted(), true, 'the show poster flag is false after play'); + QUnit.equal(player.hasStarted(), true, 'the show poster flag is false after play'); player.tech_.trigger('loadstart'); - equal(player.hasStarted(), - false, - 'the resource selection algorithm sets the show poster flag to true'); + QUnit.equal(player.hasStarted(), false, 'the resource selection algorithm sets the show poster flag to true'); player.tech_.trigger('play'); - equal(player.hasStarted(), true, 'the show poster flag is false after play'); + QUnit.equal(player.hasStarted(), true, 'the show poster flag is false after play'); }); -test('should load a media controller', function(){ - var player = TestHelpers.makePlayer({ +QUnit.test('should load a media controller', function() { + const player = TestHelpers.makePlayer({ preload: 'none', sources: [ { src: 'http://google.com', type: 'video/mp4' }, @@ -268,135 +276,133 @@ test('should load a media controller', function(){ ] }); - ok(player.el().children[0].className.indexOf('vjs-tech') !== -1, 'media controller loaded'); + QUnit.ok(player.el().children[0].className.indexOf('vjs-tech') !== -1, 'media controller loaded'); player.dispose(); }); -test('should be able to initialize player twice on the same tag using string reference', function() { - var videoTag = TestHelpers.makeTag(); - var id = videoTag.id; +QUnit.test('should be able to initialize player twice on the same tag using string reference', function() { + let videoTag = TestHelpers.makeTag(); + const id = videoTag.id; + + const fixture = document.getElementById('qunit-fixture'); - var fixture = document.getElementById('qunit-fixture'); fixture.appendChild(videoTag); - var player = videojs(videoTag.id, { techOrder: ['techFaker'] }); - ok(player, 'player is created'); + let player = videojs(videoTag.id, { techOrder: ['techFaker'] }); + + QUnit.ok(player, 'player is created'); player.dispose(); - ok(!document.getElementById(id), 'element is removed'); + QUnit.ok(!document.getElementById(id), 'element is removed'); videoTag = TestHelpers.makeTag(); fixture.appendChild(videoTag); - //here we receive cached version instead of real + // here we receive cached version instead of real player = videojs(videoTag.id, { techOrder: ['techFaker'] }); - //here it triggers error, because player was destroyed already after first dispose + // here it triggers error, because player was destroyed already after first dispose player.dispose(); }); -test('should set controls and trigger events', function() { - //expect(6); +QUnit.test('should set controls and trigger events', function() { + const player = TestHelpers.makePlayer({ controls: false }); + + QUnit.ok(player.controls() === false, 'controls set through options'); + const hasDisabledClass = player.el().className.indexOf('vjs-controls-disabled'); - var player = TestHelpers.makePlayer({ 'controls': false }); - ok(player.controls() === false, 'controls set through options'); - var hasDisabledClass = player.el().className.indexOf('vjs-controls-disabled'); - ok(hasDisabledClass !== -1, 'Disabled class added to player'); + QUnit.ok(hasDisabledClass !== -1, 'Disabled class added to player'); player.controls(true); - ok(player.controls() === true, 'controls updated'); - var hasEnabledClass = player.el().className.indexOf('vjs-controls-enabled'); - ok(hasEnabledClass !== -1, 'Disabled class added to player'); + QUnit.ok(player.controls() === true, 'controls updated'); + const hasEnabledClass = player.el().className.indexOf('vjs-controls-enabled'); + + QUnit.ok(hasEnabledClass !== -1, 'Disabled class added to player'); - player.on('controlsenabled', function(){ - ok(true, 'enabled fired once'); + player.on('controlsenabled', function() { + QUnit.ok(true, 'enabled fired once'); }); - player.on('controlsdisabled', function(){ - ok(true, 'disabled fired once'); + player.on('controlsdisabled', function() { + QUnit.ok(true, 'disabled fired once'); }); player.controls(false); - //player.controls(true); - // Check for unnecessary events - //player.controls(true); player.dispose(); }); -test('should toggle user the user state between active and inactive', function(){ - var player = TestHelpers.makePlayer({}); +QUnit.test('should toggle user the user state between active and inactive', function() { + const player = TestHelpers.makePlayer({}); - expect(9); + QUnit.expect(9); - ok(player.userActive(), 'User should be active at player init'); + QUnit.ok(player.userActive(), 'User should be active at player init'); - player.on('userinactive', function(){ - ok(true, 'userinactive event triggered'); + player.on('userinactive', function() { + QUnit.ok(true, 'userinactive event triggered'); }); - player.on('useractive', function(){ - ok(true, 'useractive event triggered'); + player.on('useractive', function() { + QUnit.ok(true, 'useractive event triggered'); }); player.userActive(false); - ok(player.userActive() === false, 'Player state changed to inactive'); - ok(player.el().className.indexOf('vjs-user-active') === -1, 'Active class removed'); - ok(player.el().className.indexOf('vjs-user-inactive') !== -1, 'Inactive class added'); + QUnit.ok(player.userActive() === false, 'Player state changed to inactive'); + QUnit.ok(player.el().className.indexOf('vjs-user-active') === -1, 'Active class removed'); + QUnit.ok(player.el().className.indexOf('vjs-user-inactive') !== -1, 'Inactive class added'); player.userActive(true); - ok(player.userActive() === true, 'Player state changed to active'); - ok(player.el().className.indexOf('vjs-user-inactive') === -1, 'Inactive class removed'); - ok(player.el().className.indexOf('vjs-user-active') !== -1, 'Active class added'); + QUnit.ok(player.userActive() === true, 'Player state changed to active'); + QUnit.ok(player.el().className.indexOf('vjs-user-inactive') === -1, 'Inactive class removed'); + QUnit.ok(player.el().className.indexOf('vjs-user-active') !== -1, 'Active class added'); player.dispose(); }); -test('should add a touch-enabled classname when touch is supported', function(){ - var player; - - expect(1); +QUnit.test('should add a touch-enabled classname when touch is supported', function() { + QUnit.expect(1); // Fake touch support. Real touch support isn't needed for this test. - var origTouch = browser.TOUCH_ENABLED; - browser.TOUCH_ENABLED = true; + const origTouch = browser.TOUCH_ENABLED; - player = TestHelpers.makePlayer({}); + browser.TOUCH_ENABLED = true; - ok(player.el().className.indexOf('vjs-touch-enabled'), 'touch-enabled classname added'); + const player = TestHelpers.makePlayer({}); + QUnit.ok(player.el().className.indexOf('vjs-touch-enabled'), 'touch-enabled classname added'); browser.TOUCH_ENABLED = origTouch; player.dispose(); }); -test('should allow for tracking when native controls are used', function(){ - var player = TestHelpers.makePlayer({}); +QUnit.test('should allow for tracking when native controls are used', function() { + const player = TestHelpers.makePlayer({}); - expect(6); + QUnit.expect(6); // Make sure native controls is false before starting test player.usingNativeControls(false); - player.on('usingnativecontrols', function(){ - ok(true, 'usingnativecontrols event triggered'); + player.on('usingnativecontrols', function() { + QUnit.ok(true, 'usingnativecontrols event triggered'); }); - player.on('usingcustomcontrols', function(){ - ok(true, 'usingcustomcontrols event triggered'); + player.on('usingcustomcontrols', function() { + QUnit.ok(true, 'usingcustomcontrols event triggered'); }); player.usingNativeControls(true); - ok(player.usingNativeControls() === true, 'Using native controls is true'); - ok(player.el().className.indexOf('vjs-using-native-controls') !== -1, 'Native controls class added'); + QUnit.ok(player.usingNativeControls() === true, 'Using native controls is true'); + QUnit.ok(player.el().className.indexOf('vjs-using-native-controls') !== -1, 'Native controls class added'); player.usingNativeControls(false); - ok(player.usingNativeControls() === false, 'Using native controls is false'); - ok(player.el().className.indexOf('vjs-using-native-controls') === -1, 'Native controls class removed'); + QUnit.ok(player.usingNativeControls() === false, 'Using native controls is false'); + QUnit.ok(player.el().className.indexOf('vjs-using-native-controls') === -1, 'Native controls class removed'); player.dispose(); }); -test('make sure that controls listeners do not get added too many times', function(){ - var player = TestHelpers.makePlayer({}); - var listeners = 0; +QUnit.test('make sure that controls listeners do not get added too many times', function() { + const player = TestHelpers.makePlayer({}); + let listeners = 0; player.addTechControlsListeners_ = function() { listeners++; @@ -409,76 +415,82 @@ test('make sure that controls listeners do not get added too many times', functi player.controls(true); - equal(listeners, 0, 'addTechControlsListeners_ should not have gotten called yet'); + QUnit.equal(listeners, 0, 'addTechControlsListeners_ should not have gotten called yet'); player.usingNativeControls(false); player.controls(false); player.controls(true); - equal(listeners, 1, 'addTechControlsListeners_ should have gotten called once'); + QUnit.equal(listeners, 1, 'addTechControlsListeners_ should have gotten called once'); player.dispose(); }); -test('should select the proper tech based on the the sourceOrder option', - function() { - let fixture = document.getElementById('qunit-fixture'); - let html = +QUnit.test('should select the proper tech based on the the sourceOrder option', function() { + const fixture = document.getElementById('qunit-fixture'); + const html = ''; - // Extend TechFaker to create a tech that plays the only mime-type that TechFaker - // will not play - class PlaysUnsupported extends TechFaker { - constructor(options, handleReady){ - super(options, handleReady); - } - // Support ONLY "video/unsupported-format" - static isSupported() { return true; } - static canPlayType(type) { return (type === 'video/unsupported-format' ? 'maybe' : ''); } - static canPlaySource(srcObj) { return srcObj.type === 'video/unsupported-format'; } + // Extend TechFaker to create a tech that plays the only mime-type that TechFaker + // will not play + class PlaysUnsupported extends TechFaker { + constructor(options, handleReady) { + super(options, handleReady); + } + // Support ONLY "video/unsupported-format" + static isSupported() { + return true; + } + static canPlayType(type) { + return (type === 'video/unsupported-format' ? 'maybe' : ''); + } + static canPlaySource(srcObj) { + return srcObj.type === 'video/unsupported-format'; } - Tech.registerTech('PlaysUnsupported', PlaysUnsupported); + } + Tech.registerTech('PlaysUnsupported', PlaysUnsupported); - fixture.innerHTML += html; - let tag = document.getElementById('example_1'); + fixture.innerHTML += html; + let tag = document.getElementById('example_1'); - let player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported'], sourceOrder: true }); - equal(player.techName_, 'PlaysUnsupported', 'selected the PlaysUnsupported tech when sourceOrder is truthy'); - player.dispose(); + let player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported'], sourceOrder: true }); - fixture.innerHTML += html; - tag = document.getElementById('example_1'); + QUnit.equal(player.techName_, 'PlaysUnsupported', 'selected the PlaysUnsupported tech when sourceOrder is truthy'); + player.dispose(); - player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported']}); - equal(player.techName_, 'TechFaker', 'selected the TechFaker tech when sourceOrder is falsey'); - player.dispose(); + fixture.innerHTML += html; + tag = document.getElementById('example_1'); + + player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported']}); + QUnit.equal(player.techName_, 'TechFaker', 'selected the TechFaker tech when sourceOrder is falsey'); + player.dispose(); }); -test('should register players with generated ids', function(){ - var fixture, video, player, id; - fixture = document.getElementById('qunit-fixture'); +QUnit.test('should register players with generated ids', function() { + const fixture = document.getElementById('qunit-fixture'); + + const video = document.createElement('video'); - video = document.createElement('video'); video.className = 'vjs-default-skin video-js'; fixture.appendChild(video); - player = new Player(video, { techOrder: ['techFaker'] }); - id = player.el().id; + const player = new Player(video, { techOrder: ['techFaker'] }); + const id = player.el().id; - equal(player.el().id, player.id(), 'the player and element ids are equal'); - ok(Player.players[id], 'the generated id is registered'); + QUnit.equal(player.el().id, player.id(), 'the player and element ids are equal'); + QUnit.ok(Player.players[id], 'the generated id is registered'); }); -test('should not add multiple first play events despite subsequent loads', function() { - expect(1); +QUnit.test('should not add multiple first play events despite subsequent loads', function() { + QUnit.expect(1); - var player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); - player.on('firstplay', function(){ - ok(true, 'First play should fire once.'); + player.on('firstplay', function() { + QUnit.ok(true, 'First play should fire once.'); }); // Checking to make sure onLoadStart removes first play listener before adding a new one. @@ -487,211 +499,210 @@ test('should not add multiple first play events despite subsequent loads', funct player.tech_.trigger('play'); }); -test('should fire firstplay after resetting the player', function() { - var player = TestHelpers.makePlayer({}); +QUnit.test('should fire firstplay after resetting the player', function() { + const player = TestHelpers.makePlayer({}); - var fpFired = false; - player.on('firstplay', function(){ + let fpFired = false; + + player.on('firstplay', function() { fpFired = true; }); // init firstplay listeners player.tech_.trigger('loadstart'); player.tech_.trigger('play'); - ok(fpFired, 'First firstplay fired'); + QUnit.ok(fpFired, 'First firstplay fired'); // reset the player player.tech_.trigger('loadstart'); fpFired = false; player.tech_.trigger('play'); - ok(fpFired, 'Second firstplay fired'); + QUnit.ok(fpFired, 'Second firstplay fired'); // the play event can fire before the loadstart event. // in that case we still want the firstplay even to fire. - player.tech_.paused = function(){ return false; }; + player.tech_.paused = function() { + return false; + }; fpFired = false; // reset the player player.tech_.trigger('loadstart'); // player.tech_.trigger('play'); - ok(fpFired, 'Third firstplay fired'); + QUnit.ok(fpFired, 'Third firstplay fired'); }); -test('should remove vjs-has-started class', function(){ - expect(3); +QUnit.test('should remove vjs-has-started class', function() { + QUnit.expect(3); - var player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); player.tech_.trigger('loadstart'); player.tech_.trigger('play'); - ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added'); + QUnit.ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added'); player.tech_.trigger('loadstart'); - ok(player.el().className.indexOf('vjs-has-started') === -1, 'vjs-has-started class removed'); + QUnit.ok(player.el().className.indexOf('vjs-has-started') === -1, 'vjs-has-started class removed'); player.tech_.trigger('play'); - ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added again'); + QUnit.ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added again'); }); -test('should add and remove vjs-ended class', function() { - expect(4); +QUnit.test('should add and remove vjs-ended class', function() { + QUnit.expect(4); - var player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); player.tech_.trigger('loadstart'); player.tech_.trigger('play'); player.tech_.trigger('ended'); - ok(player.el().className.indexOf('vjs-ended') !== -1, 'vjs-ended class added'); + QUnit.ok(player.el().className.indexOf('vjs-ended') !== -1, 'vjs-ended class added'); player.tech_.trigger('play'); - ok(player.el().className.indexOf('vjs-ended') === -1, 'vjs-ended class removed'); + QUnit.ok(player.el().className.indexOf('vjs-ended') === -1, 'vjs-ended class removed'); player.tech_.trigger('ended'); - ok(player.el().className.indexOf('vjs-ended') !== -1, 'vjs-ended class re-added'); + QUnit.ok(player.el().className.indexOf('vjs-ended') !== -1, 'vjs-ended class re-added'); player.tech_.trigger('loadstart'); - ok(player.el().className.indexOf('vjs-ended') === -1, 'vjs-ended class removed'); + QUnit.ok(player.el().className.indexOf('vjs-ended') === -1, 'vjs-ended class removed'); }); -test('player should handle different error types', function(){ - expect(8); - var player = TestHelpers.makePlayer({}); - var testMsg = 'test message'; +QUnit.test('player should handle different error types', function() { + QUnit.expect(8); + const player = TestHelpers.makePlayer({}); + const testMsg = 'test message'; // prevent error log messages in the console sinon.stub(log, 'error'); // error code supplied - function errCode(){ - equal(player.error().code, 1, 'error code is correct'); + function errCode() { + QUnit.equal(player.error().code, 1, 'error code is correct'); } player.on('error', errCode); player.error(1); player.off('error', errCode); // error instance supplied - function errInst(){ - equal(player.error().code, 2, 'MediaError code is correct'); - equal(player.error().message, testMsg, 'MediaError message is correct'); + function errInst() { + QUnit.equal(player.error().code, 2, 'MediaError code is correct'); + QUnit.equal(player.error().message, testMsg, 'MediaError message is correct'); } player.on('error', errInst); player.error(new MediaError({ code: 2, message: testMsg })); player.off('error', errInst); // error message supplied - function errMsg(){ - equal(player.error().code, 0, 'error message code is correct'); - equal(player.error().message, testMsg, 'error message is correct'); + function errMsg() { + QUnit.equal(player.error().code, 0, 'error message code is correct'); + QUnit.equal(player.error().message, testMsg, 'error message is correct'); } player.on('error', errMsg); player.error(testMsg); player.off('error', errMsg); // error config supplied - function errConfig(){ - equal(player.error().code, 3, 'error config code is correct'); - equal(player.error().message, testMsg, 'error config message is correct'); + function errConfig() { + QUnit.equal(player.error().code, 3, 'error config code is correct'); + QUnit.equal(player.error().message, testMsg, 'error config message is correct'); } player.on('error', errConfig); player.error({ code: 3, message: testMsg }); player.off('error', errConfig); // check for vjs-error classname - ok(player.el().className.indexOf('vjs-error') >= 0, 'player does not have vjs-error classname'); + QUnit.ok(player.el().className.indexOf('vjs-error') >= 0, 'player does not have vjs-error classname'); // restore error logging log.error.restore(); }); -test('Data attributes on the video element should persist in the new wrapper element', function() { - var dataId, tag, player; +QUnit.test('Data attributes on the video element should persist in the new wrapper element', function() { + const dataId = 123; - dataId = 123; + const tag = TestHelpers.makeTag(); - tag = TestHelpers.makeTag(); tag.setAttribute('data-id', dataId); - player = TestHelpers.makePlayer({}, tag); + const player = TestHelpers.makePlayer({}, tag); - equal(player.el().getAttribute('data-id'), dataId, 'data-id should be available on the new player element after creation'); + QUnit.equal(player.el().getAttribute('data-id'), dataId, 'data-id should be available on the new player element after creation'); }); -test('should restore attributes from the original video tag when creating a new element', function(){ - var tag, html5Mock, el; - +QUnit.test('should restore attributes from the original video tag when creating a new element', function() { // simulate attributes stored from the original tag - tag = Dom.createEl('video'); + const tag = Dom.createEl('video'); + tag.setAttribute('preload', 'auto'); tag.setAttribute('autoplay', ''); tag.setAttribute('webkit-playsinline', ''); - html5Mock = { options_: { tag: tag } }; + const html5Mock = { options_: {tag} }; // set options that should override tag attributes html5Mock.options_.preload = 'none'; // create the element - el = Html5.prototype.createEl.call(html5Mock); + const el = Html5.prototype.createEl.call(html5Mock); - equal(el.getAttribute('preload'), 'none', 'attribute was successful overridden by an option'); - equal(el.getAttribute('autoplay'), '', 'autoplay attribute was set properly'); - equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly'); + QUnit.equal(el.getAttribute('preload'), 'none', 'attribute was successful overridden by an option'); + QUnit.equal(el.getAttribute('autoplay'), '', 'autoplay attribute was set properly'); + QUnit.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly'); }); -test('should honor default inactivity timeout', function() { - var player; - var clock = sinon.useFakeTimers(); +QUnit.test('should honor default inactivity timeout', function() { + const clock = sinon.useFakeTimers(); - // default timeout is 2000ms - player = TestHelpers.makePlayer({}); + // default timeout is 2000ms + const player = TestHelpers.makePlayer({}); - equal(player.userActive(), true, 'User is active on creation'); - clock.tick(1800); - equal(player.userActive(), true, 'User is still active'); - clock.tick(500); - equal(player.userActive(), false, 'User is inactive after timeout expired'); + QUnit.equal(player.userActive(), true, 'User is active on creation'); + clock.tick(1800); + QUnit.equal(player.userActive(), true, 'User is still active'); + clock.tick(500); + QUnit.equal(player.userActive(), false, 'User is inactive after timeout expired'); - clock.restore(); + clock.restore(); }); -test('should honor configured inactivity timeout', function() { - var player; - var clock = sinon.useFakeTimers(); +QUnit.test('should honor configured inactivity timeout', function() { + const clock = sinon.useFakeTimers(); - // default timeout is 2000ms, set to shorter 200ms - player = TestHelpers.makePlayer({ - 'inactivityTimeout': 200 - }); + // default timeout is 2000ms, set to shorter 200ms + const player = TestHelpers.makePlayer({ + inactivityTimeout: 200 + }); - equal(player.userActive(), true, 'User is active on creation'); - clock.tick(150); - equal(player.userActive(), true, 'User is still active'); - clock.tick(350); - // make sure user is now inactive after 500ms - equal(player.userActive(), false, 'User is inactive after timeout expired'); + QUnit.equal(player.userActive(), true, 'User is active on creation'); + clock.tick(150); + QUnit.equal(player.userActive(), true, 'User is still active'); + clock.tick(350); + // make sure user is now inactive after 500ms + QUnit.equal(player.userActive(), false, 'User is inactive after timeout expired'); - clock.restore(); + clock.restore(); }); -test('should honor disabled inactivity timeout', function() { - var player; - var clock = sinon.useFakeTimers(); +QUnit.test('should honor disabled inactivity timeout', function() { + const clock = sinon.useFakeTimers(); - // default timeout is 2000ms, disable by setting to zero - player = TestHelpers.makePlayer({ - 'inactivityTimeout': 0 - }); + // default timeout is 2000ms, disable by setting to zero + const player = TestHelpers.makePlayer({ + inactivityTimeout: 0 + }); - equal(player.userActive(), true, 'User is active on creation'); - clock.tick(5000); - equal(player.userActive(), true, 'User is still active'); + QUnit.equal(player.userActive(), true, 'User is active on creation'); + clock.tick(5000); + QUnit.equal(player.userActive(), true, 'User is still active'); - clock.restore(); + clock.restore(); }); -test('should clear pending errors on disposal', function() { - var clock = sinon.useFakeTimers(), player; +QUnit.test('should clear pending errors on disposal', function() { + const clock = sinon.useFakeTimers(); + + const player = TestHelpers.makePlayer(); - player = TestHelpers.makePlayer(); player.src({ src: 'http://example.com/movie.unsupported-format', type: 'video/unsupported-format' @@ -700,15 +711,16 @@ test('should clear pending errors on disposal', function() { try { clock.tick(5000); } catch (e) { - return ok(!e, 'threw an error: ' + e.message); + return QUnit.ok(!e, 'threw an error: ' + e.message); } - ok(true, 'did not throw an error after disposal'); + QUnit.ok(true, 'did not throw an error after disposal'); }); -test('pause is called when player ended event is fired and player is not paused', function() { - var video = document.createElement('video'), - player = TestHelpers.makePlayer({}, video), - pauses = 0; +QUnit.test('pause is called when player ended event is fired and player is not paused', function() { + const video = document.createElement('video'); + const player = TestHelpers.makePlayer({}, video); + let pauses = 0; + player.paused = function() { return false; }; @@ -716,13 +728,14 @@ test('pause is called when player ended event is fired and player is not paused' pauses++; }; player.tech_.trigger('ended'); - equal(pauses, 1, 'pause was called'); + QUnit.equal(pauses, 1, 'pause was called'); }); -test('pause is not called if the player is paused and ended is fired', function() { - var video = document.createElement('video'), - player = TestHelpers.makePlayer({}, video), - pauses = 0; +QUnit.test('pause is not called if the player is paused and ended is fired', function() { + const video = document.createElement('video'); + const player = TestHelpers.makePlayer({}, video); + let pauses = 0; + player.paused = function() { return true; }; @@ -730,64 +743,62 @@ test('pause is not called if the player is paused and ended is fired', function( pauses++; }; player.tech_.trigger('ended'); - equal(pauses, 0, 'pause was not called when ended fired'); + QUnit.equal(pauses, 0, 'pause was not called when ended fired'); }); -test('should add an audio class if an audio el is used', function() { - var audio = document.createElement('audio'), - player = TestHelpers.makePlayer({}, audio), - audioClass = 'vjs-audio'; +QUnit.test('should add an audio class if an audio el is used', function() { + const audio = document.createElement('audio'); + const player = TestHelpers.makePlayer({}, audio); + const audioClass = 'vjs-audio'; - ok(player.el().className.indexOf(audioClass) !== -1, 'added '+ audioClass +' css class'); + QUnit.ok(player.el().className.indexOf(audioClass) !== -1, 'added ' + audioClass + ' css class'); }); -test('should add a video player region if a video el is used', function() { - var video = document.createElement('video'), - player = TestHelpers.makePlayer({}, video), - role = 'region', - label = 'video player'; +QUnit.test('should add a video player region if a video el is used', function() { + const video = document.createElement('video'); + const player = TestHelpers.makePlayer({}, video); - ok(player.el().getAttribute('role') === 'region', 'region role is present'); - ok(player.el().getAttribute('aria-label') === 'video player', 'video player label present'); + QUnit.ok(player.el().getAttribute('role') === 'region', 'region role is present'); + QUnit.ok(player.el().getAttribute('aria-label') === 'video player', 'video player label present'); }); -test('should add an audio player region if an audio el is used', function() { - var audio = document.createElement('audio'), - player = TestHelpers.makePlayer({}, audio), - role = 'region', - label = 'audio player'; +QUnit.test('should add an audio player region if an audio el is used', function() { + const audio = document.createElement('audio'); + const player = TestHelpers.makePlayer({}, audio); - ok(player.el().getAttribute('role') === 'region', 'region role is present'); - ok(player.el().getAttribute('aria-label') === 'audio player', 'audio player label present'); + QUnit.ok(player.el().getAttribute('role') === 'region', 'region role is present'); + QUnit.ok(player.el().getAttribute('aria-label') === 'audio player', 'audio player label present'); }); -test('should not be scrubbing while not seeking', function(){ - var player = TestHelpers.makePlayer(); - equal(player.scrubbing(), false, 'player is not scrubbing'); - ok(player.el().className.indexOf('scrubbing') === -1, 'scrubbing class is not present'); +QUnit.test('should not be scrubbing while not seeking', function() { + const player = TestHelpers.makePlayer(); + + QUnit.equal(player.scrubbing(), false, 'player is not scrubbing'); + QUnit.ok(player.el().className.indexOf('scrubbing') === -1, 'scrubbing class is not present'); player.scrubbing(false); - equal(player.scrubbing(), false, 'player is not scrubbing'); + QUnit.equal(player.scrubbing(), false, 'player is not scrubbing'); }); -test('should be scrubbing while seeking', function(){ - var player = TestHelpers.makePlayer(); +QUnit.test('should be scrubbing while seeking', function() { + const player = TestHelpers.makePlayer(); + player.scrubbing(true); - equal(player.scrubbing(), true, 'player is scrubbing'); - ok(player.el().className.indexOf('scrubbing') !== -1, 'scrubbing class is present'); + QUnit.equal(player.scrubbing(), true, 'player is scrubbing'); + QUnit.ok(player.el().className.indexOf('scrubbing') !== -1, 'scrubbing class is present'); }); -test('should throw on startup no techs are specified', function() { +QUnit.test('should throw on startup no techs are specified', function() { const techOrder = videojs.options.techOrder; videojs.options.techOrder = null; - q.throws(function() { + QUnit.throws(function() { videojs(TestHelpers.makeTag()); }, 'a falsey techOrder should throw'); videojs.options.techOrder = techOrder; }); -test('should have a sensible toJSON that is equivalent to player.options', function() { +QUnit.test('should have a sensible toJSON that is equivalent to player.options', function() { const playerOptions = { html5: { nativeTextTracks: false @@ -796,7 +807,7 @@ test('should have a sensible toJSON that is equivalent to player.options', funct const player = TestHelpers.makePlayer(playerOptions); - deepEqual(player.toJSON(), player.options_, 'simple player options toJSON produces output equivalent to player.options_'); + QUnit.deepEqual(player.toJSON(), player.options_, 'simple player options toJSON produces output equivalent to player.options_'); const playerOptions2 = { tracks: [{ @@ -812,42 +823,42 @@ test('should have a sensible toJSON that is equivalent to player.options', funct playerOptions2.tracks[0].player = player2; const popts = player2.options_; + popts.tracks[0].player = undefined; - deepEqual(player2.toJSON(), popts, 'no circular references'); + QUnit.deepEqual(player2.toJSON(), popts, 'no circular references'); }); -test('should ignore case in language codes and try primary code', function() { -expect(3); +QUnit.test('should ignore case in language codes and try primary code', function() { + QUnit.expect(3); - var player = TestHelpers.makePlayer({ - 'languages': { + const player = TestHelpers.makePlayer({ + languages: { 'en-gb': { - 'Good': 'Brilliant' + Good: 'Brilliant' }, 'EN': { - 'Good': 'Awesome', - 'Error': 'Problem' + Good: 'Awesome', + Error: 'Problem' } } }); player.language('en-gb'); - strictEqual(player.localize('Good'), 'Brilliant', 'Used subcode specific localisation'); - strictEqual(player.localize('Error'), 'Problem', 'Used primary code localisation'); + QUnit.strictEqual(player.localize('Good'), 'Brilliant', 'Used subcode specific localisation'); + QUnit.strictEqual(player.localize('Error'), 'Problem', 'Used primary code localisation'); player.language('en-GB'); - strictEqual(player.localize('Good'), 'Brilliant', 'Ignored case'); + QUnit.strictEqual(player.localize('Good'), 'Brilliant', 'Ignored case'); }); -test('inherits language from parent element', function() { - var fixture = document.getElementById('qunit-fixture'); - var oldLang = fixture.getAttribute('lang'); - var player; +QUnit.test('inherits language from parent element', function() { + const fixture = document.getElementById('qunit-fixture'); + const oldLang = fixture.getAttribute('lang'); fixture.setAttribute('lang', 'x-test'); - player = TestHelpers.makePlayer(); + const player = TestHelpers.makePlayer(); - equal(player.language(), 'x-test', 'player inherits parent element language'); + QUnit.equal(player.language(), 'x-test', 'player inherits parent element language'); player.dispose(); if (oldLang) { @@ -857,89 +868,93 @@ test('inherits language from parent element', function() { } }); -test('should return correct values for canPlayType', function(){ - var player = TestHelpers.makePlayer(); +QUnit.test('should return correct values for canPlayType', function() { + const player = TestHelpers.makePlayer(); - equal(player.canPlayType('video/mp4'), 'maybe', 'player can play mp4 files'); - equal(player.canPlayType('video/unsupported-format'), '', 'player can not play unsupported files'); + QUnit.equal(player.canPlayType('video/mp4'), 'maybe', 'player can play mp4 files'); + QUnit.equal(player.canPlayType('video/unsupported-format'), '', 'player can not play unsupported files'); player.dispose(); }); -test('createModal()', function() { - var player = TestHelpers.makePlayer(); - var modal = player.createModal('foo'); - var spy = sinon.spy(); +QUnit.test('createModal()', function() { + const player = TestHelpers.makePlayer(); + const modal = player.createModal('foo'); + const spy = sinon.spy(); modal.on('dispose', spy); - expect(5); - strictEqual(modal.el().parentNode, player.el(), 'the modal is injected into the player'); - strictEqual(modal.content(), 'foo', 'content is set properly'); - ok(modal.opened(), 'modal is opened by default'); + QUnit.expect(5); + QUnit.strictEqual(modal.el().parentNode, player.el(), 'the modal is injected into the player'); + QUnit.strictEqual(modal.content(), 'foo', 'content is set properly'); + QUnit.ok(modal.opened(), 'modal is opened by default'); modal.close(); - ok(spy.called, 'modal was disposed when closed'); - strictEqual(player.children().indexOf(modal), -1, 'modal was removed from player\'s children'); + QUnit.ok(spy.called, 'modal was disposed when closed'); + QUnit.strictEqual(player.children().indexOf(modal), -1, 'modal was removed from player\'s children'); }); -test('createModal() options object', function() { - var player = TestHelpers.makePlayer(); - var modal = player.createModal('foo', {content: 'bar', label: 'boo'}); +QUnit.test('createModal() options object', function() { + const player = TestHelpers.makePlayer(); + const modal = player.createModal('foo', {content: 'bar', label: 'boo'}); - expect(2); - strictEqual(modal.content(), 'foo', 'content argument takes precedence'); - strictEqual(modal.options_.label, 'boo', 'modal options are set properly'); + QUnit.expect(2); + QUnit.strictEqual(modal.content(), 'foo', 'content argument takes precedence'); + QUnit.strictEqual(modal.options_.label, 'boo', 'modal options are set properly'); modal.close(); }); -test('you can clear error in the error event', function() { - let player = TestHelpers.makePlayer(); +QUnit.test('you can clear error in the error event', function() { + const player = TestHelpers.makePlayer(); sinon.stub(log, 'error'); player.error({code: 4}); - ok(player.error(), 'we have an error'); + QUnit.ok(player.error(), 'we have an error'); player.error(null); player.one('error', function() { player.error(null); }); player.error({code: 4}); - ok(!player.error(), 'we no longer have an error'); + QUnit.ok(!player.error(), 'we no longer have an error'); log.error.restore(); }); -test('Player#tech will return tech given the appropriate input', function() { - let tech_ = {}; - let returnedTech = Player.prototype.tech.call({tech_}, {IWillNotUseThisInPlugins: true}); +QUnit.test('Player#tech will return tech given the appropriate input', function() { + const tech_ = {}; + const returnedTech = Player.prototype.tech.call({tech_}, {IWillNotUseThisInPlugins: true}); - equal(returnedTech, tech_, 'We got back the tech we wanted'); + QUnit.equal(returnedTech, tech_, 'We got back the tech we wanted'); }); -test('Player#tech alerts and throws without the appropriate input', function() { +QUnit.test('Player#tech alerts and throws without the appropriate input', function() { let alertCalled; - let oldAlert = window.alert; - window.alert = () => alertCalled = true; + const oldAlert = window.alert; + + window.alert = () => { + alertCalled = true; + }; + + const tech_ = {}; - let tech_ = {}; throws(function() { Player.prototype.tech.call({tech_}); }, new RegExp('https://github.com/videojs/video.js/issues/2617'), 'we threw an error'); - ok(alertCalled, 'we called an alert'); + QUnit.ok(alertCalled, 'we called an alert'); window.alert = oldAlert; }); -test('player#reset loads the Html5 tech and then techCalls reset', function() { +QUnit.test('player#reset loads the Html5 tech and then techCalls reset', function() { let loadedTech; let loadedSource; let techCallMethod; - let testPlayer = { + const testPlayer = { options_: { - techOrder: ['html5', 'flash'], + techOrder: ['html5', 'flash'] }, loadTech_(tech, source) { loadedTech = tech; @@ -952,19 +967,19 @@ test('player#reset loads the Html5 tech and then techCalls reset', function() { Player.prototype.reset.call(testPlayer); - equal(loadedTech, 'Html5', 'we loaded the html5 tech'); - equal(loadedSource, null, 'with a null source'); - equal(techCallMethod, 'reset', 'we then reset the tech'); + QUnit.equal(loadedTech, 'Html5', 'we loaded the html5 tech'); + QUnit.equal(loadedSource, null, 'with a null source'); + QUnit.equal(techCallMethod, 'reset', 'we then reset the tech'); }); -test('player#reset loads the first item in the techOrder and then techCalls reset', function() { +QUnit.test('player#reset loads the first item in the techOrder and then techCalls reset', function() { let loadedTech; let loadedSource; let techCallMethod; - let testPlayer = { + const testPlayer = { options_: { - techOrder: ['flash', 'html5'], + techOrder: ['flash', 'html5'] }, loadTech_(tech, source) { loadedTech = tech; @@ -977,70 +992,78 @@ test('player#reset loads the first item in the techOrder and then techCalls rese Player.prototype.reset.call(testPlayer); - equal(loadedTech, 'Flash', 'we loaded the Flash tech'); - equal(loadedSource, null, 'with a null source'); - equal(techCallMethod, 'reset', 'we then reset the tech'); + QUnit.equal(loadedTech, 'Flash', 'we loaded the Flash tech'); + QUnit.equal(loadedSource, null, 'with a null source'); + QUnit.equal(techCallMethod, 'reset', 'we then reset the tech'); }); -test('Remove waiting class on timeupdate after tech waiting', function() { - let player = TestHelpers.makePlayer(); +QUnit.test('Remove waiting class on timeupdate after tech waiting', function() { + const player = TestHelpers.makePlayer(); + player.tech_.trigger('waiting'); - ok(/vjs-waiting/.test(player.el().className), 'vjs-waiting is added to the player el on tech waiting'); + QUnit.ok(/vjs-waiting/.test(player.el().className), 'vjs-waiting is added to the player el on tech waiting'); player.trigger('timeupdate'); - ok(!/vjs-waiting/.test(player.el().className), 'vjs-waiting is removed from the player el on timeupdate'); + QUnit.ok(!(/vjs-waiting/).test(player.el().className), 'vjs-waiting is removed from the player el on timeupdate'); }); -test('Make sure that player\'s style el respects VIDEOJS_NO_DYNAMIC_STYLE option', function() { +QUnit.test('Make sure that player\'s style el respects VIDEOJS_NO_DYNAMIC_STYLE option', function() { // clear the HEAD before running this test let styles = document.querySelectorAll('style'); let i = styles.length; + while (i--) { - let style = styles[i]; + const style = styles[i]; + style.parentNode.removeChild(style); } let tag = TestHelpers.makeTag(); + tag.id = 'vjs-no-base-theme-tag'; tag.width = 600; tag.height = 300; window.VIDEOJS_NO_DYNAMIC_STYLE = true; - let player = TestHelpers.makePlayer({}, tag); + TestHelpers.makePlayer({}, tag); + styles = document.querySelectorAll('style'); - equal(styles.length, 0, 'we should not get any style elements included in the DOM'); + QUnit.equal(styles.length, 0, 'we should not get any style elements included in the DOM'); window.VIDEOJS_NO_DYNAMIC_STYLE = false; tag = TestHelpers.makeTag(); - player = TestHelpers.makePlayer({}, tag); + TestHelpers.makePlayer({}, tag); styles = document.querySelectorAll('style'); - equal(styles.length, 1, 'we should have one style element in the DOM'); - equal(styles[0].className, 'vjs-styles-dimensions', 'the class name is the one we expected'); + QUnit.equal(styles.length, 1, 'we should have one style element in the DOM'); + QUnit.equal(styles[0].className, 'vjs-styles-dimensions', 'the class name is the one we expected'); }); -test('When VIDEOJS_NO_DYNAMIC_STYLE is set, apply sizing directly to the tech el', function() { +QUnit.test('When VIDEOJS_NO_DYNAMIC_STYLE is set, apply sizing directly to the tech el', function() { // clear the HEAD before running this test - let styles = document.querySelectorAll('style'); + const styles = document.querySelectorAll('style'); let i = styles.length; + while (i--) { - let style = styles[i]; + const style = styles[i]; + style.parentNode.removeChild(style); } - let tag = TestHelpers.makeTag(); + const tag = TestHelpers.makeTag(); + tag.id = 'vjs-no-base-theme-tag'; tag.width = 600; tag.height = 300; window.VIDEOJS_NO_DYNAMIC_STYLE = true; - let player = TestHelpers.makePlayer({}, tag); + const player = TestHelpers.makePlayer({}, tag); player.width(300); player.height(600); - equal(player.tech_.el().width, 300, 'the width is equal to 300'); - equal(player.tech_.el().height, 600, 'the height is equal 600'); + QUnit.equal(player.tech_.el().width, 300, 'the width is equal to 300'); + QUnit.equal(player.tech_.el().height, 600, 'the height is equal 600'); player.width(600); player.height(300); - equal(player.tech_.el().width, 600, 'the width is equal to 600'); - equal(player.tech_.el().height, 300, 'the height is equal 300'); + QUnit.equal(player.tech_.el().width, 600, 'the width is equal to 600'); + QUnit.equal(player.tech_.el().height, 300, 'the height is equal 300'); }); diff --git a/test/unit/plugins.test.js b/test/unit/plugins.test.js index 2e34713ea8..365fabe325 100644 --- a/test/unit/plugins.test.js +++ b/test/unit/plugins.test.js @@ -1,27 +1,29 @@ +/* eslint-env qunit */ import {IE_VERSION} from '../../src/js/utils/browser'; import registerPlugin from '../../src/js/plugins.js'; import Player from '../../src/js/player.js'; import TestHelpers from './test-helpers.js'; import window from 'global/window'; +import sinon from 'sinon'; -q.module('Plugins'); +QUnit.module('Plugins'); -test('Plugin should get initialized and receive options', function(){ - expect(2); +QUnit.test('Plugin should get initialized and receive options', function() { + QUnit.expect(2); - registerPlugin('myPlugin1', function(options){ - ok(true, 'Plugin initialized'); - ok(options['test'], 'Option passed through'); + registerPlugin('myPlugin1', function(options) { + QUnit.ok(true, 'Plugin initialized'); + QUnit.ok(options.test, 'Option passed through'); }); - registerPlugin('myPlugin2', function(options){ - ok(false, 'Plugin initialized and should not have been'); + registerPlugin('myPlugin2', function(options) { + QUnit.ok(false, 'Plugin initialized and should not have been'); }); - var player = TestHelpers.makePlayer({ - 'plugins': { - 'myPlugin1': { - 'test': true + const player = TestHelpers.makePlayer({ + plugins: { + myPlugin1: { + test: true } } }); @@ -29,121 +31,122 @@ test('Plugin should get initialized and receive options', function(){ player.dispose(); }); -test('Plugin should have the option of being initilized outside of player init', function(){ - expect(3); +QUnit.test('Plugin should have the option of being initilized outside of player init', function() { + QUnit.expect(3); - registerPlugin('myPlugin3', function(options){ - ok(true, 'Plugin initialized after player init'); - ok(options['test'], 'Option passed through'); + registerPlugin('myPlugin3', function(options) { + QUnit.ok(true, 'Plugin initialized after player init'); + QUnit.ok(options.test, 'Option passed through'); }); - var player = TestHelpers.makePlayer({}); + const player = TestHelpers.makePlayer({}); - ok(player['myPlugin3'], 'Plugin has direct access on player instance'); + QUnit.ok(player.myPlugin3, 'Plugin has direct access on player instance'); - player['myPlugin3']({ - 'test': true + player.myPlugin3({ + test: true }); player.dispose(); }); -test('Plugin should be able to add a UI component', function(){ - expect(2); +QUnit.test('Plugin should be able to add a UI component', function() { + QUnit.expect(2); - registerPlugin('myPlugin4', function(options){ - ok((this instanceof Player), 'Plugin executed in player scope by default'); + registerPlugin('myPlugin4', function(options) { + QUnit.ok((this instanceof Player), 'Plugin executed in player scope by default'); this.addChild('component'); }); - var player = TestHelpers.makePlayer({}); - player['myPlugin4']({ - 'test': true + const player = TestHelpers.makePlayer({}); + + player.myPlugin4({ + test: true }); - var comp = player.getChild('component'); - ok(comp, 'Plugin added a component to the player'); + const comp = player.getChild('component'); + + QUnit.ok(comp, 'Plugin added a component to the player'); player.dispose(); }); -test('Plugin should overwrite plugin of same name', function(){ - var v1Called = 0, - v2Called = 0, - v3Called = 0; +QUnit.test('Plugin should overwrite plugin of same name', function() { + let v1Called = 0; + let v2Called = 0; + let v3Called = 0; // Create initial plugin - registerPlugin('myPlugin5', function(options){ + registerPlugin('myPlugin5', function(options) { v1Called++; }); - var player = TestHelpers.makePlayer({}); - player['myPlugin5']({}); + const player = TestHelpers.makePlayer({}); + + player.myPlugin5({}); // Overwrite and create new player - registerPlugin('myPlugin5', function(options){ + registerPlugin('myPlugin5', function(options) { v2Called++; }); - var player2 = TestHelpers.makePlayer({}); - player2['myPlugin5']({}); + const player2 = TestHelpers.makePlayer({}); + + player2.myPlugin5({}); // Overwrite and init new version on existing player - registerPlugin('myPlugin5', function(options){ + registerPlugin('myPlugin5', function(options) { v3Called++; }); - player2['myPlugin5']({}); + player2.myPlugin5({}); - var comp = player.getChild('component'); - ok(v1Called === 1, 'First version of plugin called once'); - ok(v2Called === 1, 'Plugin overwritten for new player'); - ok(v3Called === 1, 'Plugin overwritten for existing player'); + QUnit.ok(v1Called === 1, 'First version of plugin called once'); + QUnit.ok(v2Called === 1, 'Plugin overwritten for new player'); + QUnit.ok(v3Called === 1, 'Plugin overwritten for existing player'); player.dispose(); player2.dispose(); }); - -test('Plugins should get events in registration order', function() { - var order = []; - var expectedOrder = []; - var pluginName = 'orderPlugin'; - var i = 0; - var name; - var player = TestHelpers.makePlayer({}); - var plugin = function (name) { - registerPlugin(name, function (opts) { - this.on('test', function (event) { +QUnit.test('Plugins should get events in registration order', function() { + const order = []; + const expectedOrder = []; + const pluginName = 'orderPlugin'; + const player = TestHelpers.makePlayer({}); + const plugin = function(name) { + registerPlugin(name, function(opts) { + this.on('test', function(event) { order.push(name); }); }); player[name]({}); }; - for (; i < 3; i++ ) { - name = pluginName + i; + for (let i = 0; i < 3; i++) { + const name = pluginName + i; + expectedOrder.push(name); plugin(name); } - registerPlugin('testerPlugin', function (opts) { + registerPlugin('testerPlugin', function(opts) { this.trigger('test'); }); - player['testerPlugin']({}); + player.testerPlugin({}); - deepEqual(order, expectedOrder, 'plugins should receive events in order of initialization'); + QUnit.deepEqual(order, + expectedOrder, + 'plugins should receive events in order of initialization'); player.dispose(); }); -test('Plugins should not get events after stopImmediatePropagation is called', function () { - var order = []; - var expectedOrder = []; - var pluginName = 'orderPlugin'; - var i = 0; - var name; - var player = TestHelpers.makePlayer({}); - var plugin = function (name) { - registerPlugin(name, function (opts) { - this.on('test', function (event) { +QUnit.test('Plugins should not get events after stopImmediatePropagation is called', function() { + const order = []; + const expectedOrder = []; + const pluginName = 'orderPlugin'; + const player = TestHelpers.makePlayer({}); + const plugin = function(name) { + registerPlugin(name, function(opts) { + this.on('test', function(event) { order.push(name); event.stopImmediatePropagation(); }); @@ -151,56 +154,57 @@ test('Plugins should not get events after stopImmediatePropagation is called', f player[name]({}); }; - for (; i < 3; i++ ) { - name = pluginName + i; + for (let i = 0; i < 3; i++) { + const name = pluginName + i; + expectedOrder.push(name); plugin(name); } - registerPlugin('testerPlugin', function (opts) { + registerPlugin('testerPlugin', function(opts) { this.trigger('test'); }); - player['testerPlugin']({}); + player.testerPlugin({}); - deepEqual(order, expectedOrder.slice(0, order.length), 'plugins should receive events in order of initialization, until stopImmediatePropagation'); + QUnit.deepEqual(order, + expectedOrder.slice(0, order.length), + 'plugins should receive events in order of ' + + 'initialization, until stopImmediatePropagation'); - equal(order.length, 1, 'only one event listener should have triggered'); + QUnit.equal(order.length, 1, 'only one event listener should have triggered'); player.dispose(); }); -test('Plugin that does not exist logs an error', function() { +QUnit.test('Plugin that does not exist logs an error', function() { // stub the global log functions - var console, log, error, origConsole; - - origConsole = window.console; - - console = window.console = { - log: function(){}, - warn: function(){}, - error: function(){} + const console = window.console = { + log() {}, + warn() {}, + error() {} }; - - log = sinon.stub(console, 'log'); - error = sinon.stub(console, 'error'); + const log = sinon.stub(console, 'log'); + const error = sinon.stub(console, 'error'); + const origConsole = window.console; // enable a non-existing plugin TestHelpers.makePlayer({ plugins: { - 'nonExistingPlugin': { - 'foo': 'bar' + nonExistingPlugin: { + foo: 'bar' } } }); - ok(error.called, 'error was called'); + QUnit.ok(error.called, 'error was called'); if (IE_VERSION && IE_VERSION < 11) { - equal(error.firstCall.args[0], 'VIDEOJS: ERROR: Unable to find plugin: nonExistingPlugin'); + QUnit.equal(error.firstCall.args[0], + 'VIDEOJS: ERROR: Unable to find plugin: nonExistingPlugin'); } else { - equal(error.firstCall.args[2], 'Unable to find plugin:'); - equal(error.firstCall.args[3], 'nonExistingPlugin'); + QUnit.equal(error.firstCall.args[2], 'Unable to find plugin:'); + QUnit.equal(error.firstCall.args[3], 'nonExistingPlugin'); } // tear down logging stubs diff --git a/test/unit/poster.test.js b/test/unit/poster.test.js index 7e4217cf49..7470572aee 100644 --- a/test/unit/poster.test.js +++ b/test/unit/poster.test.js @@ -1,10 +1,11 @@ +/* eslint-env qunit */ import PosterImage from '../../src/js/poster-image.js'; import * as browser from '../../src/js/utils/browser.js'; import TestHelpers from './test-helpers.js'; import document from 'global/document'; -q.module('PosterImage', { - 'setup': function(){ +QUnit.module('PosterImage', { + setup() { // Store the original background support so we can test different vals this.origVal = browser.BACKGROUND_SIZE_SUPPORTED; this.poster1 = '#poster1'; @@ -13,69 +14,80 @@ q.module('PosterImage', { // Create a mock player object that responds as a player would this.mockPlayer = { poster_: this.poster1, - poster: function(){ + poster() { return this.poster_; }, handler_: null, - on: function(type, handler){ + on(type, handler) { this.handler_ = handler; }, - trigger: function(type){ + trigger(type) { this.handler_.call(); } }; }, - 'teardown': function(){ + teardown() { browser.BACKGROUND_SIZE_SUPPORTED = this.origVal; } }); -test('should create and update a poster image', function(){ +QUnit.test('should create and update a poster image', function() { browser.BACKGROUND_SIZE_SUPPORTED = true; - let posterImage = new PosterImage(this.mockPlayer); + const posterImage = new PosterImage(this.mockPlayer); let backgroundImage = posterImage.el().style.backgroundImage; - notEqual(backgroundImage.indexOf(this.poster1), -1, 'Background image used'); + + QUnit.notEqual(backgroundImage.indexOf(this.poster1), -1, 'Background image used'); // Update with a new poster source and check the new value this.mockPlayer.poster_ = this.poster2; this.mockPlayer.trigger('posterchange'); backgroundImage = posterImage.el().style.backgroundImage; - notEqual(backgroundImage.indexOf(this.poster2), -1, 'Background image updated'); + QUnit.notEqual(backgroundImage.indexOf(this.poster2), -1, 'Background image updated'); }); -test('should create and update a fallback image in older browsers', function(){ +QUnit.test('should create and update a fallback image in older browsers', function() { browser.BACKGROUND_SIZE_SUPPORTED = false; - let posterImage = new PosterImage(this.mockPlayer); - notEqual(posterImage.fallbackImg_.src.indexOf(this.poster1), -1, 'Fallback image created'); + const posterImage = new PosterImage(this.mockPlayer); + + QUnit.notEqual(posterImage.fallbackImg_.src.indexOf(this.poster1), + -1, + 'Fallback image created'); // Update with a new poster source and check the new value this.mockPlayer.poster_ = this.poster2; this.mockPlayer.trigger('posterchange'); - notEqual(posterImage.fallbackImg_.src.indexOf(this.poster2), -1, 'Fallback image updated'); + QUnit.notEqual(posterImage.fallbackImg_.src.indexOf(this.poster2), + -1, + 'Fallback image updated'); }); -test('should remove itself from the document flow when there is no poster', function(){ - let posterImage = new PosterImage(this.mockPlayer); - equal(posterImage.el().style.display, '', 'Poster image shows by default'); +QUnit.test('should remove itself from the document flow when there is no poster', function() { + const posterImage = new PosterImage(this.mockPlayer); + + QUnit.equal(posterImage.el().style.display, '', 'Poster image shows by default'); // Update with an empty string this.mockPlayer.poster_ = ''; this.mockPlayer.trigger('posterchange'); - equal(posterImage.hasClass('vjs-hidden'), true, 'Poster image hides with an empty source'); + QUnit.equal(posterImage.hasClass('vjs-hidden'), + true, + 'Poster image hides with an empty source'); // Updated with a valid source this.mockPlayer.poster_ = this.poster2; this.mockPlayer.trigger('posterchange'); - equal(posterImage.hasClass('vjs-hidden'), false, 'Poster image shows again when there is a source'); + QUnit.equal(posterImage.hasClass('vjs-hidden'), + false, + 'Poster image shows again when there is a source'); }); -test('should hide the poster in the appropriate player states', function(){ - var posterImage = new PosterImage(this.mockPlayer); - var playerDiv = document.createElement('div'); - var fixture = document.getElementById('qunit-fixture'); - var el = posterImage.el(); +QUnit.test('should hide the poster in the appropriate player states', function() { + const posterImage = new PosterImage(this.mockPlayer); + const playerDiv = document.createElement('div'); + const fixture = document.getElementById('qunit-fixture'); + const el = posterImage.el(); // Remove the source so when we add to the DOM it doesn't throw an error // We want to poster to still think it has a real source so it doesn't hide itself @@ -86,8 +98,12 @@ test('should hide the poster in the appropriate player states', function(){ fixture.appendChild(playerDiv); playerDiv.className = 'video-js vjs-has-started'; - equal(TestHelpers.getComputedStyle(el, 'display'), 'none', 'The poster hides when the video has started (CSS may not be loaded)'); + QUnit.equal(TestHelpers.getComputedStyle(el, 'display'), + 'none', + 'The poster hides when the video has started (CSS may not be loaded)'); playerDiv.className = 'video-js vjs-has-started vjs-audio'; - equal(TestHelpers.getComputedStyle(el, 'display'), 'block', 'The poster continues to show when playing audio'); + QUnit.equal(TestHelpers.getComputedStyle(el, 'display'), + 'block', + 'The poster continues to show when playing audio'); }); diff --git a/test/unit/setup.test.js b/test/unit/setup.test.js index db17db3085..7ea9e359b0 100644 --- a/test/unit/setup.test.js +++ b/test/unit/setup.test.js @@ -1,14 +1,17 @@ +/* eslint-env qunit */ import TestHelpers from './test-helpers.js'; -q.module('Setup'); +QUnit.module('Setup'); -test('should set options from data-setup even if autoSetup is not called before initialisation', function(){ - var el = TestHelpers.makeTag(); - el.setAttribute('data-setup', '{"controls": true, "autoplay": false, "preload": "auto"}'); +QUnit.test('should set options from data-setup even if autoSetup is not called before initialisation', function() { + const el = TestHelpers.makeTag(); - var player = TestHelpers.makePlayer({}, el); + el.setAttribute('data-setup', + '{"controls": true, "autoplay": false, "preload": "auto"}'); - ok(player.options_['controls'] === true); - ok(player.options_['autoplay'] === false); - ok(player.options_['preload'] === 'auto'); + const player = TestHelpers.makePlayer({}, el); + + QUnit.ok(player.options_.controls === true); + QUnit.ok(player.options_.autoplay === false); + QUnit.ok(player.options_.preload === 'auto'); }); diff --git a/test/unit/tech/flash-rtmp.test.js b/test/unit/tech/flash-rtmp.test.js index a9a7bccd82..0d45ad1d39 100644 --- a/test/unit/tech/flash-rtmp.test.js +++ b/test/unit/tech/flash-rtmp.test.js @@ -1,54 +1,58 @@ +/* eslint-env qunit */ import Flash from '../../../src/js/tech/flash.js'; -q.module('Flash RTMP'); +QUnit.module('Flash RTMP'); + +const streamToPartsAndBack = function(url) { + const parts = Flash.streamToParts(url); -var streamToPartsAndBack = function(url) { - var parts = Flash.streamToParts(url); return Flash.streamFromParts(parts.connection, parts.stream); }; -test('test using both streamToParts and streamFromParts', function() { - ok('rtmp://myurl.com/&isthis' === streamToPartsAndBack('rtmp://myurl.com/isthis')); - ok('rtmp://myurl.com/&isthis' === streamToPartsAndBack('rtmp://myurl.com/&isthis')); - ok('rtmp://myurl.com/isthis/&andthis' === streamToPartsAndBack('rtmp://myurl.com/isthis/andthis')); +QUnit.test('test using both streamToParts and streamFromParts', function() { + QUnit.ok(streamToPartsAndBack('rtmp://myurl.com/isthis') === 'rtmp://myurl.com/&isthis'); + QUnit.ok(streamToPartsAndBack('rtmp://myurl.com/&isthis') === 'rtmp://myurl.com/&isthis'); + QUnit.ok(streamToPartsAndBack('rtmp://myurl.com/isthis/andthis') === 'rtmp://myurl.com/isthis/&andthis'); }); -test('test streamToParts', function() { - var parts = Flash.streamToParts('http://myurl.com/streaming&/is/fun'); - ok(parts.connection === 'http://myurl.com/streaming'); - ok(parts.stream === '/is/fun'); +QUnit.test('test streamToParts', function() { + let parts = Flash.streamToParts('http://myurl.com/streaming&/is/fun'); + + QUnit.ok(parts.connection === 'http://myurl.com/streaming'); + QUnit.ok(parts.stream === '/is/fun'); parts = Flash.streamToParts('http://myurl.com/&streaming&/is/fun'); - ok(parts.connection === 'http://myurl.com/'); - ok(parts.stream === 'streaming&/is/fun'); + QUnit.ok(parts.connection === 'http://myurl.com/'); + QUnit.ok(parts.stream === 'streaming&/is/fun'); parts = Flash.streamToParts('http://myurl.com/really?streaming=fun&really=fun'); - ok(parts.connection === 'http://myurl.com/'); - ok(parts.stream === 'really?streaming=fun&really=fun'); + QUnit.ok(parts.connection === 'http://myurl.com/'); + QUnit.ok(parts.stream === 'really?streaming=fun&really=fun'); parts = Flash.streamToParts('http://myurl.com/streaming/is/fun'); - ok(parts.connection === 'http://myurl.com/streaming/is/'); - ok(parts.stream === 'fun'); + QUnit.ok(parts.connection === 'http://myurl.com/streaming/is/'); + QUnit.ok(parts.stream === 'fun'); parts = Flash.streamToParts('whatisgoingonhere'); - ok(parts.connection === 'whatisgoingonhere'); - ok(parts.stream === ''); + QUnit.ok(parts.connection === 'whatisgoingonhere'); + QUnit.ok(parts.stream === ''); parts = Flash.streamToParts(); - ok(parts.connection === ''); - ok(parts.stream === ''); + QUnit.ok(parts.connection === ''); + QUnit.ok(parts.stream === ''); }); -test('test isStreamingSrc', function() { - var isStreamingSrc = Flash.isStreamingSrc; - ok(isStreamingSrc('rtmp://streaming.is/fun')); - ok(isStreamingSrc('rtmps://streaming.is/fun')); - ok(isStreamingSrc('rtmpe://streaming.is/fun')); - ok(isStreamingSrc('rtmpt://streaming.is/fun')); +QUnit.test('test isStreamingSrc', function() { + const isStreamingSrc = Flash.isStreamingSrc; + + QUnit.ok(isStreamingSrc('rtmp://streaming.is/fun')); + QUnit.ok(isStreamingSrc('rtmps://streaming.is/fun')); + QUnit.ok(isStreamingSrc('rtmpe://streaming.is/fun')); + QUnit.ok(isStreamingSrc('rtmpt://streaming.is/fun')); // test invalid protocols - ok(!isStreamingSrc('rtmp:streaming.is/fun')); - ok(!isStreamingSrc('rtmpz://streaming.is/fun')); - ok(!isStreamingSrc('http://streaming.is/fun')); - ok(!isStreamingSrc('https://streaming.is/fun')); - ok(!isStreamingSrc('file://streaming.is/fun')); + QUnit.ok(!isStreamingSrc('rtmp:streaming.is/fun')); + QUnit.ok(!isStreamingSrc('rtmpz://streaming.is/fun')); + QUnit.ok(!isStreamingSrc('http://streaming.is/fun')); + QUnit.ok(!isStreamingSrc('https://streaming.is/fun')); + QUnit.ok(!isStreamingSrc('file://streaming.is/fun')); }); diff --git a/test/unit/tech/flash.test.js b/test/unit/tech/flash.test.js index 229d78911b..1980b5253f 100644 --- a/test/unit/tech/flash.test.js +++ b/test/unit/tech/flash.test.js @@ -1,28 +1,38 @@ +/* eslint-env qunit */ import Flash from '../../../src/js/tech/flash.js'; import { createTimeRange } from '../../../src/js/utils/time-ranges.js'; import document from 'global/document'; +import sinon from 'sinon'; -q.module('Flash'); +// fake out the interaction but leave all the other logic intact +class MockFlash extends Flash { + constructor() { + super({}); + } +} + +QUnit.module('Flash'); -test('Flash.canPlaySource', function() { - var canPlaySource = Flash.canPlaySource; +QUnit.test('Flash.canPlaySource', function() { + const canPlaySource = Flash.canPlaySource; // Supported - ok(canPlaySource({ type: 'video/mp4; codecs=avc1.42E01E,mp4a.40.2' }, {}), 'codecs supported'); - ok(canPlaySource({ type: 'video/mp4' }, {}), 'video/mp4 supported'); - ok(canPlaySource({ type: 'video/x-flv' }, {}), 'video/x-flv supported'); - ok(canPlaySource({ type: 'video/flv' }, {}), 'video/flv supported'); - ok(canPlaySource({ type: 'video/m4v' }, {}), 'video/m4v supported'); - ok(canPlaySource({ type: 'VIDEO/FLV' }, {}), 'capitalized mime type'); + QUnit.ok(canPlaySource({type: 'video/mp4; codecs=avc1.42E01E,mp4a.40.2' }, {}), + 'codecs supported'); + QUnit.ok(canPlaySource({type: 'video/mp4' }, {}), 'video/mp4 supported'); + QUnit.ok(canPlaySource({type: 'video/x-flv' }, {}), 'video/x-flv supported'); + QUnit.ok(canPlaySource({type: 'video/flv' }, {}), 'video/flv supported'); + QUnit.ok(canPlaySource({type: 'video/m4v' }, {}), 'video/m4v supported'); + QUnit.ok(canPlaySource({type: 'VIDEO/FLV' }, {}), 'capitalized mime type'); // Not supported - ok(!canPlaySource({ type: 'video/webm; codecs="vp8, vorbis"' }, {})); - ok(!canPlaySource({ type: 'video/webm' }, {})); + QUnit.ok(!canPlaySource({ type: 'video/webm; codecs="vp8, vorbis"' }, {})); + QUnit.ok(!canPlaySource({ type: 'video/webm' }, {})); }); -test('currentTime', function() { - let getCurrentTime = Flash.prototype.currentTime; - let setCurrentTime = Flash.prototype.setCurrentTime; +QUnit.test('currentTime', function() { + const getCurrentTime = Flash.prototype.currentTime; + const setCurrentTime = Flash.prototype.setCurrentTime; let seekingCount = 0; let seeking = false; let setPropVal; @@ -30,24 +40,26 @@ test('currentTime', function() { let result; // Mock out a Flash instance to avoid creating the swf object - let mockFlash = { + const mockFlash = { el_: { - vjs_setProperty(prop, val){ + /* eslint-disable camelcase */ + vjs_setProperty(prop, val) { setPropVal = val; }, - vjs_getProperty(){ + vjs_getProperty() { return getPropVal; } + /* eslint-enable camelcase */ }, - seekable(){ + seekable() { return createTimeRange(5, 1000); }, - trigger(event){ + trigger(event) { if (event === 'seeking') { seekingCount++; } }, - seeking(){ + seeking() { return seeking; } }; @@ -55,39 +67,43 @@ test('currentTime', function() { // Test the currentTime getter getPropVal = 3; result = getCurrentTime.call(mockFlash); - equal(result, 3, 'currentTime is retreived from the swf element'); + QUnit.equal(result, 3, 'currentTime is retreived from the swf element'); // Test the currentTime setter setCurrentTime.call(mockFlash, 10); - equal(setPropVal, 10, 'currentTime is set on the swf element'); - equal(seekingCount, 1, 'triggered seeking'); + QUnit.equal(setPropVal, 10, 'currentTime is set on the swf element'); + QUnit.equal(seekingCount, 1, 'triggered seeking'); // Test current time while seeking setCurrentTime.call(mockFlash, 20); seeking = true; result = getCurrentTime.call(mockFlash); - equal(result, 20, 'currentTime is retrieved from the lastSeekTarget while seeking'); - notEqual(result, getPropVal, 'currentTime is not retrieved from the element while seeking'); - equal(seekingCount, 2, 'triggered seeking'); + QUnit.equal(result, + 20, + 'currentTime is retrieved from the lastSeekTarget while seeking'); + QUnit.notEqual(result, + getPropVal, + 'currentTime is not retrieved from the element while seeking'); + QUnit.equal(seekingCount, 2, 'triggered seeking'); // clamp seeks to seekable setCurrentTime.call(mockFlash, 1001); result = getCurrentTime.call(mockFlash); - equal(result, mockFlash.seekable().end(0), 'clamped to the seekable end'); - equal(seekingCount, 3, 'triggered seeking'); + QUnit.equal(result, mockFlash.seekable().end(0), 'clamped to the seekable end'); + QUnit.equal(seekingCount, 3, 'triggered seeking'); setCurrentTime.call(mockFlash, 1); result = getCurrentTime.call(mockFlash); - equal(result, mockFlash.seekable().start(0), 'clamped to the seekable start'); - equal(seekingCount, 4, 'triggered seeking'); + QUnit.equal(result, mockFlash.seekable().start(0), 'clamped to the seekable start'); + QUnit.equal(seekingCount, 4, 'triggered seeking'); }); -test('dispose removes the object element even before ready fires', function() { +QUnit.test('dispose removes the object element even before ready fires', function() { // This test appears to test bad functionaly that was fixed // so it's debateable whether or not it's useful - let dispose = Flash.prototype.dispose; - let mockFlash = new MockFlash(); - let noop = function(){}; + const dispose = Flash.prototype.dispose; + const mockFlash = new MockFlash(); + const noop = function() {}; // Mock required functions for dispose mockFlash.off = noop; @@ -95,14 +111,14 @@ test('dispose removes the object element even before ready fires', function() { mockFlash.el_ = {}; dispose.call(mockFlash); - strictEqual(mockFlash.el_, null, 'swf el is nulled'); + QUnit.strictEqual(mockFlash.el_, null, 'swf el is nulled'); }); -test('ready triggering before and after disposing the tech', function() { - let checkReady = sinon.stub(Flash, 'checkReady'); - let fixtureDiv = document.getElementById('qunit-fixture'); - let playerDiv = document.createElement('div'); - let techEl = document.createElement('div'); +QUnit.test('ready triggering before and after disposing the tech', function() { + const checkReady = sinon.stub(Flash, 'checkReady'); + const fixtureDiv = document.getElementById('qunit-fixture'); + const playerDiv = document.createElement('div'); + const techEl = document.createElement('div'); techEl.id = 'foo1234'; playerDiv.appendChild(techEl); @@ -110,7 +126,7 @@ test('ready triggering before and after disposing the tech', function() { // Mock the swf element techEl.tech = { - el: function() { + el() { return techEl; } }; @@ -120,44 +136,55 @@ test('ready triggering before and after disposing the tech', function() { }; Flash.onReady(techEl.id); - ok(checkReady.called, 'checkReady should be called before the tech is disposed'); + QUnit.ok(checkReady.called, 'checkReady should be called before the tech is disposed'); // remove the tech el from the player div to simulate being disposed playerDiv.removeChild(techEl); Flash.onReady(techEl.id); - ok(!checkReady.calledTwice, 'checkReady should not be called after the tech is disposed'); + QUnit.ok(!checkReady.calledTwice, + 'checkReady should not be called after the tech is disposed'); Flash.checkReady.restore(); }); -test('should have the source handler interface', function() { - ok(Flash.registerSourceHandler, 'has the registerSourceHandler function'); +QUnit.test('should have the source handler interface', function() { + QUnit.ok(Flash.registerSourceHandler, 'has the registerSourceHandler function'); }); -test('canPlayType should select the correct types to play', function () { - let canPlayType = Flash.nativeSourceHandler.canPlayType; +QUnit.test('canPlayType should select the correct types to play', function() { + const canPlayType = Flash.nativeSourceHandler.canPlayType; - equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files'); - equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files'); - equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files'); - equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files'); - equal(canPlayType('video/ogg'), '', 'should return empty string if it can not play the video'); + QUnit.equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files'); + QUnit.equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files'); + QUnit.equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files'); + QUnit.equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files'); + QUnit.equal(canPlayType('video/ogg'), + '', + 'should return empty string if it can not play the video'); }); -test('canHandleSource should be able to work with src objects without a type', function () { - let canHandleSource = Flash.nativeSourceHandler.canHandleSource; - - equal('maybe', canHandleSource({ src: 'test.video.mp4' }, {}), 'should guess that it is a mp4 video'); - equal('maybe', canHandleSource({ src: 'test.video.m4v' }, {}), 'should guess that it is a m4v video'); - equal('maybe', canHandleSource({ src: 'test.video.flv' }, {}), 'should guess that it is a flash video'); - equal('', canHandleSource({ src: 'test.video.wgg' }, {}), 'should return empty string if it can not play the video'); +QUnit.test('canHandleSource should be able to work with src objects without a type', function() { + const canHandleSource = Flash.nativeSourceHandler.canHandleSource; + + QUnit.equal('maybe', + canHandleSource({ src: 'test.video.mp4' }, {}), + 'should guess that it is a mp4 video'); + QUnit.equal('maybe', + canHandleSource({ src: 'test.video.m4v' }, {}), + 'should guess that it is a m4v video'); + QUnit.equal('maybe', + canHandleSource({ src: 'test.video.flv' }, {}), + 'should guess that it is a flash video'); + QUnit.equal('', + canHandleSource({ src: 'test.video.wgg' }, {}), + 'should return empty string if it can not play the video'); }); -test('seekable', function() { - let seekable = Flash.prototype.seekable; +QUnit.test('seekable', function() { + const seekable = Flash.prototype.seekable; let result; - let mockFlash = { - duration: function() { + const mockFlash = { + duration() { return this.duration_; } }; @@ -165,24 +192,28 @@ test('seekable', function() { // Test a normal duration mockFlash.duration_ = 23; result = seekable.call(mockFlash); - equal(result.length, 1, 'seekable is non-empty'); - equal(result.start(0), 0, 'starts at zero'); - equal(result.end(0), mockFlash.duration_, 'ends at the duration'); + QUnit.equal(result.length, 1, 'seekable is non-empty'); + QUnit.equal(result.start(0), 0, 'starts at zero'); + QUnit.equal(result.end(0), mockFlash.duration_, 'ends at the duration'); // Test a zero duration mockFlash.duration_ = 0; result = seekable.call(mockFlash); - equal(result.length, mockFlash.duration_, 'seekable is empty with a zero duration'); + QUnit.equal(result.length, mockFlash.duration_, + 'seekable is empty with a zero duration'); }); -test('play after ended seeks to the beginning', function() { - let plays = 0, seeks = []; +QUnit.test('play after ended seeks to the beginning', function() { + let plays = 0; + const seeks = []; Flash.prototype.play.call({ el_: { + /* eslint-disable camelcase */ vjs_play() { plays++; } + /* eslint-enable camelcase */ }, ended() { return true; @@ -192,41 +223,41 @@ test('play after ended seeks to the beginning', function() { } }); - equal(plays, 1, 'called play on the SWF'); - equal(seeks.length, 1, 'seeked on play'); - equal(seeks[0], 0, 'seeked to the beginning'); + QUnit.equal(plays, 1, 'called play on the SWF'); + QUnit.equal(seeks.length, 1, 'seeked on play'); + QUnit.equal(seeks[0], 0, 'seeked to the beginning'); }); -test('duration returns NaN, Infinity or duration according to the HTML standard', function() { - let duration = Flash.prototype.duration; +QUnit.test('duration returns NaN, Infinity or duration according to the HTML standard', function() { + const duration = Flash.prototype.duration; let mockedDuration = -1; let mockedReadyState = 0; let result; - let mockFlash = { + const mockFlash = { el_: { + /* eslint-disable camelcase */ vjs_getProperty() { return mockedDuration; } + /* eslint-enable camelcase */ }, - readyState: function() { + readyState() { return mockedReadyState; } }; + result = duration.call(mockFlash); - ok(Number.isNaN(result), 'duration returns NaN when readyState equals 0'); + QUnit.ok(Number.isNaN(result), 'duration returns NaN when readyState equals 0'); mockedReadyState = 1; result = duration.call(mockFlash); - ok(!Number.isFinite(result), 'duration returns Infinity when duration property is less then 0'); + QUnit.ok(!Number.isFinite(result), + 'duration returns Infinity when duration property is less then 0'); mockedDuration = 1; result = duration.call(mockFlash); - equal(result, 1, 'duration returns duration property when readeyState and duration property are both higher than 0'); + QUnit.equal(result, + 1, + 'duration returns duration property when readyState' + + ' and duration property are both higher than 0'); }); - -// fake out the interaction but leave all the other logic intact -class MockFlash extends Flash { - constructor() { - super({}); - } -} diff --git a/test/unit/tech/html5.test.js b/test/unit/tech/html5.test.js index 92ac0fbc8a..b83d843fbb 100644 --- a/test/unit/tech/html5.test.js +++ b/test/unit/tech/html5.test.js @@ -1,381 +1,449 @@ -var player, tech, el; +/* eslint-env qunit */ +let player; +let tech; import Html5 from '../../../src/js/tech/html5.js'; import * as browser from '../../../src/js/utils/browser.js'; import document from 'global/document'; -q.module('HTML5', { - 'setup': function() { +QUnit.module('HTML5', { + setup() { + const el = document.createElement('div'); - el = document.createElement('div'); el.innerHTML = '
'; - player = { - id: function(){ return 'id'; }, - el: function(){ return el; }, + id() { + return 'id'; + }, + el() { + return el; + }, options_: {}, - options: function(){ return this.options_; }, - bufferedPercent: function() { return 0; }, - controls: function(){ return false; }, - usingNativeControls: function(){ return false; }, - on: function(){ return this; }, - off: function() { return this; }, - ready: function(){}, - addChild: function(){}, - trigger: function(){} + options() { + return this.options_; + }, + bufferedPercent() { + return 0; + }, + controls() { + return false; + }, + usingNativeControls() { + return false; + }, + on() { + return this; + }, + off() { + return this; + }, + ready() {}, + addChild() {}, + trigger() {} }; tech = new Html5({}); }, - 'teardown': function() { + teardown() { tech.dispose(); - el = null; player = null; tech = null; } }); -test('should detect whether the volume can be changed', function(){ - var testVid, ConstVolumeVideo; - if (!{}['__defineSetter__']) { - ok(true, 'your browser does not support this test, skipping it'); +QUnit.test('should detect whether the volume can be changed', function() { + + if (!{}.__defineSetter__) { + QUnit.ok(true, 'your browser does not support this test, skipping it'); return; } - testVid = Html5.TEST_VID; - ConstVolumeVideo = function(){ + const testVid = Html5.TEST_VID; + const ConstVolumeVideo = function() { this.volume = 1; - this.__defineSetter__('volume', function(){}); + this.__defineSetter__('volume', function() {}); }; + Html5.TEST_VID = new ConstVolumeVideo(); - ok(!Html5.canControlVolume()); + QUnit.ok(!Html5.canControlVolume()); Html5.TEST_VID = testVid; }); -test('test playbackRate', function() { - var playbackRate; - +QUnit.test('test playbackRate', function() { // Android 2.3 always returns 0 for playback rate if (!Html5.canControlPlaybackRate()) { - ok('Playback rate is not supported'); + QUnit.ok('Playback rate is not supported'); return; } tech.createEl(); tech.el().playbackRate = 1.25; - strictEqual(tech.playbackRate(), 1.25); + QUnit.strictEqual(tech.playbackRate(), 1.25); - tech['setPlaybackRate'](0.75); - strictEqual(tech.playbackRate(), 0.75); + tech.setPlaybackRate(0.75); + QUnit.strictEqual(tech.playbackRate(), 0.75); }); -test('should export played', function() { +QUnit.test('should export played', function() { tech.createEl(); - deepEqual(tech.played(), tech.el().played, 'returns the played attribute'); + QUnit.deepEqual(tech.played(), tech.el().played, 'returns the played attribute'); }); -test('should remove the controls attribute when recreating the element', function() { - var el; +QUnit.test('should remove the controls attribute when recreating the element', function() { player.tagAttributes = { controls: true }; // force custom controls so the test environment is equivalent on iOS - player.options_['nativeControlsForTouch'] = false; - el = tech.createEl(); + player.options_.nativeControlsForTouch = false; + const el = tech.createEl(); // On the iPhone controls are always true if (!browser.IS_IPHONE) { - ok(!el.controls, 'controls attribute is absent'); + QUnit.ok(!el.controls, 'controls attribute is absent'); } - ok(player.tagAttributes.controls, 'tag attribute is still present'); + QUnit.ok(player.tagAttributes.controls, 'tag attribute is still present'); }); -test('patchCanPlayType patches canplaytype with our function, conditionally', function() { +QUnit.test('patchCanPlayType patches canplaytype with our function, conditionally', function() { // the patch runs automatically so we need to first unpatch Html5.unpatchCanPlayType(); - var oldAV = browser.ANDROID_VERSION, - video = document.createElement('video'), - canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType, - patchedCanPlayType, - unpatchedCanPlayType; + const oldAV = browser.ANDROID_VERSION; + const video = document.createElement('video'); + const canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType; browser.ANDROID_VERSION = 4.0; Html5.patchCanPlayType(); - notStrictEqual(video.canPlayType, canPlayType, 'original canPlayType and patched canPlayType should not be equal'); + QUnit.notStrictEqual(video.canPlayType, + canPlayType, + 'original canPlayType and patched canPlayType should not be equal'); - patchedCanPlayType = video.canPlayType; - unpatchedCanPlayType = Html5.unpatchCanPlayType(); + const patchedCanPlayType = video.canPlayType; + const unpatchedCanPlayType = Html5.unpatchCanPlayType(); - strictEqual(canPlayType, Html5.TEST_VID.constructor.prototype.canPlayType, 'original canPlayType and unpatched canPlayType should be equal'); - strictEqual(patchedCanPlayType, unpatchedCanPlayType, 'patched canPlayType and function returned from unpatch are equal'); + QUnit.strictEqual(canPlayType, + Html5.TEST_VID.constructor.prototype.canPlayType, + 'original canPlayType and unpatched canPlayType should be equal'); + QUnit.strictEqual(patchedCanPlayType, + unpatchedCanPlayType, + 'patched canPlayType and function returned from unpatch are equal'); browser.ANDROID_VERSION = oldAV; Html5.unpatchCanPlayType(); }); -test('should return maybe for HLS urls on Android 4.0 or above', function() { - var oldAV = browser.ANDROID_VERSION, - video = document.createElement('video'); +QUnit.test('should return maybe for HLS urls on Android 4.0 or above', function() { + const oldAV = browser.ANDROID_VERSION; + const video = document.createElement('video'); browser.ANDROID_VERSION = 4.0; Html5.patchCanPlayType(); - strictEqual(video.canPlayType('application/x-mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegurl'); - strictEqual(video.canPlayType('application/x-mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegURL'); - strictEqual(video.canPlayType('application/vnd.apple.mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl'); - strictEqual(video.canPlayType('application/vnd.apple.mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl'); + QUnit.strictEqual(video.canPlayType('application/x-mpegurl'), + 'maybe', + 'android version 4.0 or above should be a maybe for x-mpegurl'); + QUnit.strictEqual(video.canPlayType('application/x-mpegURL'), + 'maybe', + 'android version 4.0 or above should be a maybe for x-mpegURL'); + QUnit.strictEqual(video.canPlayType('application/vnd.apple.mpegurl'), + 'maybe', + 'android version 4.0 or above should be a ' + + 'maybe for vnd.apple.mpegurl'); + QUnit.strictEqual(video.canPlayType('application/vnd.apple.mpegURL'), + 'maybe', + 'android version 4.0 or above should be a ' + + 'maybe for vnd.apple.mpegurl'); browser.ANDROID_VERSION = oldAV; Html5.unpatchCanPlayType(); }); -test('should return a maybe for mp4 on OLD ANDROID', function() { - var isOldAndroid = browser.IS_OLD_ANDROID, - video = document.createElement('video'); +QUnit.test('should return a maybe for mp4 on OLD ANDROID', function() { + const isOldAndroid = browser.IS_OLD_ANDROID; + const video = document.createElement('video'); browser.IS_OLD_ANDROID = true; Html5.patchCanPlayType(); - strictEqual(video.canPlayType('video/mp4'), 'maybe', 'old android should return a maybe for video/mp4'); + QUnit.strictEqual(video.canPlayType('video/mp4'), + 'maybe', + 'old android should return a maybe for video/mp4'); browser.IS_OLD_ANDROID = isOldAndroid; Html5.unpatchCanPlayType(); }); -test('error events may not set the errors property', function() { - equal(tech.error(), undefined, 'no tech-level error'); +QUnit.test('error events may not set the errors property', function() { + QUnit.equal(tech.error(), undefined, 'no tech-level error'); tech.trigger('error'); - ok(true, 'no error was thrown'); + QUnit.ok(true, 'no error was thrown'); }); -test('should have the source handler interface', function() { - ok(Html5.registerSourceHandler, 'has the registerSourceHandler function'); +QUnit.test('should have the source handler interface', function() { + QUnit.ok(Html5.registerSourceHandler, 'has the registerSourceHandler function'); }); -test('native source handler canPlayType', function(){ - var result; - +QUnit.test('native source handler canPlayType', function() { // Stub the test video canPlayType (used in canPlayType) to control results - var origCPT = Html5.TEST_VID.canPlayType; - Html5.TEST_VID.canPlayType = function(type){ + const origCPT = Html5.TEST_VID.canPlayType; + + Html5.TEST_VID.canPlayType = function(type) { if (type === 'video/mp4') { return 'maybe'; } return ''; }; - var canPlayType = Html5.nativeSourceHandler.canPlayType; + const canPlayType = Html5.nativeSourceHandler.canPlayType; - equal(canPlayType('video/mp4'), 'maybe', 'Native source handler reported type support'); - equal(canPlayType('foo'), '', 'Native source handler handled bad type'); + QUnit.equal(canPlayType('video/mp4'), + 'maybe', + 'Native source handler reported type support'); + QUnit.equal(canPlayType('foo'), '', 'Native source handler handled bad type'); // Reset test video canPlayType Html5.TEST_VID.canPlayType = origCPT; }); -test('native source handler canHandleSource', function(){ - var result; - +QUnit.test('native source handler canHandleSource', function() { // Stub the test video canPlayType (used in canHandleSource) to control results - var origCPT = Html5.TEST_VID.canPlayType; - Html5.TEST_VID.canPlayType = function(type){ + const origCPT = Html5.TEST_VID.canPlayType; + + Html5.TEST_VID.canPlayType = function(type) { if (type === 'video/mp4') { return 'maybe'; } return ''; }; - var canHandleSource = Html5.nativeSourceHandler.canHandleSource; - - equal(canHandleSource({ type: 'video/mp4', src: 'video.flv' }, {}), 'maybe', 'Native source handler reported type support'); - equal(canHandleSource({ src: 'http://www.example.com/video.mp4' }, {}), 'maybe', 'Native source handler reported extension support'); - equal(canHandleSource({ src: 'https://example.com/video.sd.mp4?s=foo&token=bar' }, {}), 'maybe', 'Native source handler reported extension support'); - equal(canHandleSource({ src: 'https://example.com/video.sd.mp4?s=foo' }, {}), 'maybe', 'Native source handler reported extension support'); + const canHandleSource = Html5.nativeSourceHandler.canHandleSource; + + QUnit.equal(canHandleSource({ type: 'video/mp4', src: 'video.flv' }, {}), + 'maybe', + 'Native source handler reported type support'); + QUnit.equal(canHandleSource({ src: 'http://www.example.com/video.mp4' }, {}), + 'maybe', + 'Native source handler reported extension support'); + QUnit.equal(canHandleSource({ src: 'https://example.com/video.sd.mp4?s=foo&token=bar' }, {}), + 'maybe', + 'Native source handler reported extension support'); + QUnit.equal(canHandleSource({ src: 'https://example.com/video.sd.mp4?s=foo' }, {}), + 'maybe', + 'Native source handler reported extension support'); // Test for issue videojs/video.js#1785 and other potential failures - equal(canHandleSource({ src: '' }, {}), '', 'Native source handler handled empty src'); - equal(canHandleSource({}, {}), '', 'Native source handler handled empty object'); - equal(canHandleSource({ src: 'foo' }, {}), '', 'Native source handler handled bad src'); - equal(canHandleSource({ type: 'foo' }, {}), '', 'Native source handler handled bad type'); + QUnit.equal(canHandleSource({ src: '' }, {}), + '', + 'Native source handler handled empty src'); + QUnit.equal(canHandleSource({}, {}), + '', + 'Native source handler handled empty object'); + QUnit.equal(canHandleSource({ src: 'foo' }, {}), + '', + 'Native source handler handled bad src'); + QUnit.equal(canHandleSource({ type: 'foo' }, {}), + '', + 'Native source handler handled bad type'); // Reset test video canPlayType Html5.TEST_VID.canPlayType = origCPT; }); if (Html5.supportsNativeTextTracks()) { - test('add native textTrack listeners on startup', function() { - let adds = []; - let rems = []; - let tt = { + QUnit.test('add native textTrack listeners on startup', function() { + const adds = []; + const rems = []; + const tt = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.textTracks = tt; - let htmlTech = new Html5({el}); + /* eslint-disable no-unused-vars */ + const htmlTech = new Html5({el}); + /* eslint-enable no-unused-vars */ - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); }); - test('remove all tracks from emulated list on dispose', function() { - let adds = []; - let rems = []; - let tt = { + QUnit.test('remove all tracks from emulated list on dispose', function() { + const adds = []; + const rems = []; + const tt = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.textTracks = tt; - let htmlTech = new Html5({el}); + const htmlTech = new Html5({el}); + htmlTech.dispose(); - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); - equal(rems[0][0], 'change', 'change event handler removed'); - equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); - equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); - equal(adds[0][0], rems[0][0], 'change event handler removed'); - equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); - equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(rems[0][0], 'change', 'change event handler removed'); + QUnit.equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); + QUnit.equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); + QUnit.equal(adds[0][0], rems[0][0], 'change event handler removed'); + QUnit.equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); + QUnit.equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); }); } if (Html5.supportsNativeAudioTracks()) { - test('add native audioTrack listeners on startup', function() { - let adds = []; - let rems = []; - let at = { + QUnit.test('add native audioTrack listeners on startup', function() { + const adds = []; + const rems = []; + const at = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.audioTracks = at; - let htmlTech = new Html5({el}); + /* eslint-disable no-unused-vars */ + const htmlTech = new Html5({el}); + /* eslint-enable no-unused-vars */ - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); }); - test('remove all tracks from emulated list on dispose', function() { - let adds = []; - let rems = []; - let at = { + QUnit.test('remove all tracks from emulated list on dispose', function() { + const adds = []; + const rems = []; + const at = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.audioTracks = at; - let htmlTech = new Html5({el}); + const htmlTech = new Html5({el}); + htmlTech.dispose(); - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); - equal(rems[0][0], 'change', 'change event handler removed'); - equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); - equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); - equal(adds[0][0], rems[0][0], 'change event handler removed'); - equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); - equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(rems[0][0], 'change', 'change event handler removed'); + QUnit.equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); + QUnit.equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); + QUnit.equal(adds[0][0], rems[0][0], 'change event handler removed'); + QUnit.equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); + QUnit.equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); }); } if (Html5.supportsNativeVideoTracks()) { - test('add native videoTrack listeners on startup', function() { - let adds = []; - let rems = []; - let vt = { + QUnit.test('add native videoTrack listeners on startup', function() { + const adds = []; + const rems = []; + const vt = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.videoTracks = vt; - let htmlTech = new Html5({el}); + /* eslint-disable no-unused-vars */ + const htmlTech = new Html5({el}); + /* eslint-enable no-unused-vars */ - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); }); - test('remove all tracks from emulated list on dispose', function() { - let adds = []; - let rems = []; - let vt = { + QUnit.test('remove all tracks from emulated list on dispose', function() { + const adds = []; + const rems = []; + const vt = { length: 0, addEventListener: (type, fn) => adds.push([type, fn]), - removeEventListener: (type, fn) => rems.push([type, fn]), + removeEventListener: (type, fn) => rems.push([type, fn]) }; - let el = document.createElement('div'); + const el = document.createElement('div'); + el.videoTracks = vt; - let htmlTech = new Html5({el}); + const htmlTech = new Html5({el}); + htmlTech.dispose(); - equal(adds[0][0], 'change', 'change event handler added'); - equal(adds[1][0], 'addtrack', 'addtrack event handler added'); - equal(adds[2][0], 'removetrack', 'removetrack event handler added'); - equal(rems[0][0], 'change', 'change event handler removed'); - equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); - equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); - equal(adds[0][0], rems[0][0], 'change event handler removed'); - equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); - equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); + QUnit.equal(adds[0][0], 'change', 'change event handler added'); + QUnit.equal(adds[1][0], 'addtrack', 'addtrack event handler added'); + QUnit.equal(adds[2][0], 'removetrack', 'removetrack event handler added'); + QUnit.equal(rems[0][0], 'change', 'change event handler removed'); + QUnit.equal(rems[1][0], 'addtrack', 'addtrack event handler removed'); + QUnit.equal(rems[2][0], 'removetrack', 'removetrack event handler removed'); + QUnit.equal(adds[0][0], rems[0][0], 'change event handler removed'); + QUnit.equal(adds[1][0], rems[1][0], 'addtrack event handler removed'); + QUnit.equal(adds[2][0], rems[2][0], 'removetrack event handler removed'); }); } -test('should always return currentSource_ if set', function(){ - let currentSrc = Html5.prototype.currentSrc; - equal(currentSrc.call({el_: {currentSrc:'test1'}}), 'test1', 'sould return source from element if nothing else set'); - equal(currentSrc.call({currentSource_:{src: 'test2'}}), 'test2', 'sould return source from currentSource_, if nothing else set'); - equal(currentSrc.call({currentSource_:{src: 'test2'}, el_:{currentSrc:'test1'}}), 'test2', 'sould return source from source set, not from element'); +QUnit.test('should always return currentSource_ if set', function() { + const currentSrc = Html5.prototype.currentSrc; + + QUnit.equal(currentSrc.call({el_: {currentSrc: 'test1'}}), + 'test1', + 'sould return source from element if nothing else set'); + QUnit.equal(currentSrc.call({currentSource_: {src: 'test2'}}), + 'test2', + 'sould return source from currentSource_, if nothing else set'); + QUnit.equal(currentSrc.call({currentSource_: {src: 'test2'}, + el_: {currentSrc: 'test1'}}), + 'test2', + 'sould return source from source set, not from element'); }); -test('should fire makeup events when a video tag is initialized late', function(){ - let lateInit = Html5.prototype.handleLateInit_; +QUnit.test('should fire makeup events when a video tag is initialized late', function() { + const lateInit = Html5.prototype.handleLateInit_; let triggeredEvents = []; - let mockHtml5 = { + const mockHtml5 = { readyListeners: [], - ready(listener){ + ready(listener) { this.readyListeners.push(listener); }, - triggerReady(){ - this.readyListeners.forEach(function(listener){ + triggerReady() { + this.readyListeners.forEach(function(listener) { listener.call(this); }, this); }, - trigger(type){ + trigger(type) { triggeredEvents.push(type); }, - on: function(){}, - off: function(){} + on() {}, + off() {} }; - function resetMock() { - triggeredEvents = {}; - mockHtml5.readyListeners = []; - } - function testStates(statesObject, expectedEvents) { lateInit.call(mockHtml5, statesObject); mockHtml5.triggerReady(); - deepEqual(triggeredEvents, expectedEvents, `wrong events triggered for networkState:${statesObject.networkState} and readyState:${statesObject.readyState || 'no readyState'}`); + QUnit.deepEqual(triggeredEvents, + expectedEvents, + 'wrong events triggered for ' + + `networkState:${statesObject.networkState} ` + + `and readyState:${statesObject.readyState || 'no readyState'}`); // reset mock triggeredEvents = []; @@ -391,19 +459,21 @@ test('should fire makeup events when a video tag is initialized late', function( // Ready States testStates({ networkState: 1, readyState: 0 }, ['loadstart']); testStates({ networkState: 1, readyState: 1 }, ['loadstart', 'loadedmetadata']); - testStates({ networkState: 1, readyState: 2 }, ['loadstart', 'loadedmetadata', 'loadeddata']); - testStates({ networkState: 1, readyState: 3 }, ['loadstart', 'loadedmetadata', 'loadeddata', 'canplay']); - testStates({ networkState: 1, readyState: 4 }, ['loadstart', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough']); + testStates({ networkState: 1, readyState: 2 }, + ['loadstart', 'loadedmetadata', 'loadeddata']); + testStates({ networkState: 1, readyState: 3 }, + ['loadstart', 'loadedmetadata', 'loadeddata', 'canplay']); + testStates({ networkState: 1, readyState: 4 }, + ['loadstart', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough']); }); -test('Html5.resetMediaElement should remove sources and call load', function() { +QUnit.test('Html5.resetMediaElement should remove sources and call load', function() { let selector; - let removedChildren = []; + const removedChildren = []; let removedAttribute; let loaded; - - let children = ['source1', 'source2', 'source3']; - let testEl = { + const children = ['source1', 'source2', 'source3']; + const testEl = { querySelectorAll(input) { selector = input; return children; @@ -423,22 +493,27 @@ test('Html5.resetMediaElement should remove sources and call load', function() { }; Html5.resetMediaElement(testEl); - equal(selector, 'source', 'we got the source elements from the test el'); - deepEqual(removedChildren, children.reverse(), 'we removed the children that were present'); - equal(removedAttribute, 'src', 'we removed the src attribute'); - ok(loaded, 'we called load on the element'); + QUnit.equal(selector, 'source', 'we got the source elements from the test el'); + QUnit.deepEqual(removedChildren, + children.reverse(), + 'we removed the children that were present'); + QUnit.equal(removedAttribute, 'src', 'we removed the src attribute'); + QUnit.ok(loaded, 'we called load on the element'); }); -test('Html5#reset calls Html5.resetMediaElement when called', function() { - let oldResetMedia = Html5.resetMediaElement; +QUnit.test('Html5#reset calls Html5.resetMediaElement when called', function() { + const oldResetMedia = Html5.resetMediaElement; let resetEl; - Html5.resetMediaElement = (el) => resetEl = el; + Html5.resetMediaElement = (el) => { + resetEl = el; + }; + + const el = {}; - let el = {}; Html5.prototype.reset.call({el_: el}); - equal(resetEl, el, 'we called resetMediaElement with the tech\'s el'); + QUnit.equal(resetEl, el, 'we called resetMediaElement with the tech\'s el'); Html5.resetMediaElement = oldResetMedia; }); diff --git a/test/unit/tech/tech-faker.js b/test/unit/tech/tech-faker.js index 3211b5f275..9b7190121c 100644 --- a/test/unit/tech/tech-faker.js +++ b/test/unit/tech/tech-faker.js @@ -1,6 +1,5 @@ // Fake a media playback tech controller so that player tests // can run without HTML5 or Flash, of which PhantomJS supports neither. - import Tech from '../../../src/js/tech/tech.js'; /** @@ -8,49 +7,82 @@ import Tech from '../../../src/js/tech/tech.js'; */ class TechFaker extends Tech { - constructor(options, handleReady){ + constructor(options, handleReady) { super(options, handleReady); this.triggerReady(); } createEl() { - var el = super.createEl('div', { + const el = super.createEl('div', { className: 'vjs-tech' }); - /*if (this.player().poster()) { - // transfer the poster image to mimic HTML - el.poster = this.player().poster(); - }*/ - return el; } // fake a poster attribute to mimic the video element - poster() { return this.el().poster; } - setPoster(val) { this.el().poster = val; } + poster() { + return this.el().poster; + } + setPoster(val) { + this.el().poster = val; + } setControls(val) {} - currentTime() { return 0; } - seeking() { return false; } - src() { return 'movie.mp4'; } - volume() { return 0; } - muted() { return false; } - pause() { return false; } - paused() { return true; } - play() { this.trigger('play'); } - supportsFullScreen() { return false; } - buffered() { return {}; } - duration() { return {}; } - networkState() { return 0; } - readyState() { return 0; } - controls() { return false; } + currentTime() { + return 0; + } + seeking() { + return false; + } + src() { + return 'movie.mp4'; + } + volume() { + return 0; + } + muted() { + return false; + } + pause() { + return false; + } + paused() { + return true; + } + play() { + this.trigger('play'); + } + supportsFullScreen() { + return false; + } + buffered() { + return {}; + } + duration() { + return {}; + } + networkState() { + return 0; + } + readyState() { + return 0; + } + controls() { + return false; + } // Support everything except for "video/unsupported-format" - static isSupported() { return true; } - static canPlayType(type) { return (type !== 'video/unsupported-format' ? 'maybe' : ''); } - static canPlaySource(srcObj) { return srcObj.type !== 'video/unsupported-format'; } + static isSupported() { + return true; + } + static canPlayType(type) { + return (type !== 'video/unsupported-format' ? 'maybe' : ''); + } + static canPlaySource(srcObj) { + return srcObj.type !== 'video/unsupported-format'; + } } Tech.registerTech('TechFaker', TechFaker); diff --git a/test/unit/tech/tech.test.js b/test/unit/tech/tech.test.js index e50e14806a..41374d3274 100644 --- a/test/unit/tech/tech.test.js +++ b/test/unit/tech/tech.test.js @@ -1,5 +1,4 @@ -var noop = function() {}, clock, oldTextTracks; - +/* eslint-env qunit */ import Tech from '../../../src/js/tech/tech.js'; import Html5 from '../../../src/js/tech/html5.js'; import Flash from '../../../src/js/tech/flash.js'; @@ -13,24 +12,25 @@ import TextTrack from '../../../src/js/tracks/text-track'; import AudioTrackList from '../../../src/js/tracks/audio-track-list'; import VideoTrackList from '../../../src/js/tracks/video-track-list'; import TextTrackList from '../../../src/js/tracks/text-track-list'; +import sinon from 'sinon'; -q.module('Media Tech', { - 'setup': function() { +QUnit.module('Media Tech', { + setup() { this.noop = function() {}; this.clock = sinon.useFakeTimers(); - this.featuresProgessEvents = Tech.prototype['featuresProgessEvents']; - Tech.prototype['featuresProgressEvents'] = false; + this.featuresProgessEvents = Tech.prototype.featuresProgessEvents; + Tech.prototype.featuresProgressEvents = false; }, - 'teardown': function() { + teardown() { this.clock.restore(); - Tech.prototype['featuresProgessEvents'] = this.featuresProgessEvents; + Tech.prototype.featuresProgessEvents = this.featuresProgessEvents; } }); -test('should synthesize timeupdate events by default', function() { - var timeupdates = 0, tech; +QUnit.test('should synthesize timeupdate events by default', function() { + let timeupdates = 0; + const tech = new Tech(); - tech = new Tech(); tech.on('timeupdate', function() { timeupdates++; }); @@ -38,33 +38,36 @@ test('should synthesize timeupdate events by default', function() { tech.trigger('play'); this.clock.tick(250); - equal(timeupdates, 1, 'triggered at least one timeupdate'); + QUnit.equal(timeupdates, 1, 'triggered at least one timeupdate'); }); -test('stops manual timeupdates while paused', function() { - var timeupdates = 0, tech, expected; - tech = new Tech(); +QUnit.test('stops manual timeupdates while paused', function() { + let timeupdates = 0; + const tech = new Tech(); + tech.on('timeupdate', function() { timeupdates++; }); tech.trigger('play'); this.clock.tick(10 * 250); - ok(timeupdates > 0, 'timeupdates fire during playback'); + QUnit.ok(timeupdates > 0, 'timeupdates fire during playback'); tech.trigger('pause'); timeupdates = 0; this.clock.tick(10 * 250); - equal(timeupdates, 0, 'timeupdates do not fire when paused'); + QUnit.equal(timeupdates, 0, 'timeupdates do not fire when paused'); tech.trigger('play'); this.clock.tick(10 * 250); - ok(timeupdates > 0, 'timeupdates fire when playback resumes'); + QUnit.ok(timeupdates > 0, 'timeupdates fire when playback resumes'); }); -test('should synthesize progress events by default', function() { - var progresses = 0, bufferedPercent = 0.5, tech; - tech = new Tech(); +QUnit.test('should synthesize progress events by default', function() { + let progresses = 0; + let bufferedPercent = 0.5; + const tech = new Tech(); + tech.on('progress', function() { progresses++; }); @@ -73,20 +76,21 @@ test('should synthesize progress events by default', function() { }; this.clock.tick(500); - equal(progresses, 0, 'waits until ready'); + QUnit.equal(progresses, 0, 'waits until ready'); tech.trigger('ready'); this.clock.tick(500); - equal(progresses, 1, 'triggered one event'); + QUnit.equal(progresses, 1, 'triggered one event'); tech.trigger('ready'); bufferedPercent = 0.75; this.clock.tick(500); - equal(progresses, 2, 'repeated readies are ok'); + QUnit.equal(progresses, 2, 'repeated readies are ok'); }); -test('dispose() should stop time tracking', function() { - var tech = new Tech(); +QUnit.test('dispose() should stop time tracking', function() { + const tech = new Tech(); + tech.dispose(); // progress and timeupdate events will throw exceptions after the @@ -94,34 +98,42 @@ test('dispose() should stop time tracking', function() { try { this.clock.tick(10 * 1000); } catch (e) { - return equal(e, undefined, 'threw an exception'); + return QUnit.equal(e, undefined, 'threw an exception'); } - ok(true, 'no exception was thrown'); + QUnit.ok(true, 'no exception was thrown'); }); -test('dispose() should clear all tracks that are passed when its created', function() { - var audioTracks = new AudioTrackList([new AudioTrack(), new AudioTrack()]); - var videoTracks = new VideoTrackList([new VideoTrack(), new VideoTrack()]); - var textTracks = new TextTrackList([new TextTrack({tech: {}}), new TextTrack({tech: {}})]); +QUnit.test('dispose() should clear all tracks that are passed when its created', function() { + const audioTracks = new AudioTrackList([new AudioTrack(), new AudioTrack()]); + const videoTracks = new VideoTrackList([new VideoTrack(), new VideoTrack()]); + const textTracks = new TextTrackList([new TextTrack({tech: {}}), + new TextTrack({tech: {}})]); - equal(audioTracks.length, 2, 'should have two audio tracks at the start'); - equal(videoTracks.length, 2, 'should have two video tracks at the start'); - equal(textTracks.length, 2, 'should have two text tracks at the start'); + QUnit.equal(audioTracks.length, 2, 'should have two audio tracks at the start'); + QUnit.equal(videoTracks.length, 2, 'should have two video tracks at the start'); + QUnit.equal(textTracks.length, 2, 'should have two text tracks at the start'); - var tech = new Tech({audioTracks, videoTracks, textTracks}); - equal(tech.videoTracks().length, videoTracks.length, 'should hold video tracks that we passed'); - equal(tech.audioTracks().length, audioTracks.length, 'should hold audio tracks that we passed'); - equal(tech.textTracks().length, textTracks.length, 'should hold text tracks that we passed'); + const tech = new Tech({audioTracks, videoTracks, textTracks}); + + QUnit.equal(tech.videoTracks().length, + videoTracks.length, + 'should hold video tracks that we passed'); + QUnit.equal(tech.audioTracks().length, + audioTracks.length, + 'should hold audio tracks that we passed'); + QUnit.equal(tech.textTracks().length, + textTracks.length, + 'should hold text tracks that we passed'); tech.dispose(); - equal(audioTracks.length, 0, 'should have zero audio tracks after dispose'); - equal(videoTracks.length, 0, 'should have zero video tracks after dispose'); - equal(textTracks.length, 0, 'should have zero text tracks after dispose'); + QUnit.equal(audioTracks.length, 0, 'should have zero audio tracks after dispose'); + QUnit.equal(videoTracks.length, 0, 'should have zero video tracks after dispose'); + QUnit.equal(textTracks.length, 0, 'should have zero text tracks after dispose'); }); -test('dispose() should clear all tracks that are added after creation', function() { - var tech = new Tech(); +QUnit.test('dispose() should clear all tracks that are added after creation', function() { + const tech = new Tech(); tech.addRemoteTextTrack({}); tech.addRemoteTextTrack({}); @@ -132,103 +144,140 @@ test('dispose() should clear all tracks that are added after creation', function tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack_(new VideoTrack()); - equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); - equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.remoteTextTrackEls().length, 2, 'should have two remote text tracks els'); - equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); + QUnit.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); + QUnit.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.remoteTextTrackEls().length, + 2, + 'should have two remote text tracks els'); + QUnit.equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); tech.dispose(); - equal(tech.audioTracks().length, 0, 'should have zero audio tracks after dispose'); - equal(tech.videoTracks().length, 0, 'should have zero video tracks after dispose'); - equal(tech.remoteTextTrackEls().length, 0, 'should have zero remote text tracks els'); - equal(tech.remoteTextTracks().length, 0, 'should have zero remote text tracks'); - equal(tech.textTracks().length, 0, 'should have zero video tracks after dispose'); + QUnit.equal(tech.audioTracks().length, + 0, + 'should have zero audio tracks after dispose'); + QUnit.equal(tech.videoTracks().length, + 0, + 'should have zero video tracks after dispose'); + QUnit.equal(tech.remoteTextTrackEls().length, + 0, + 'should have zero remote text tracks els'); + QUnit.equal(tech.remoteTextTracks().length, 0, 'should have zero remote text tracks'); + QUnit.equal(tech.textTracks().length, 0, 'should have zero video tracks after dispose'); }); -test('should add the source handler interface to a tech', function(){ - var sourceA = { src: 'foo.mp4', type: 'video/mp4' }; - var sourceB = { src: 'no-support', type: 'no-support' }; +QUnit.test('should add the source handler interface to a tech', function() { + const sourceA = { src: 'foo.mp4', type: 'video/mp4' }; + const sourceB = { src: 'no-support', type: 'no-support' }; // Define a new tech class - var MyTech = extendFn(Tech); + const MyTech = extendFn(Tech); // Extend Tech with source handlers Tech.withSourceHandlers(MyTech); // Check for the expected class methods - ok(MyTech.registerSourceHandler, 'added a registerSourceHandler function to the Tech'); - ok(MyTech.selectSourceHandler, 'added a selectSourceHandler function to the Tech'); + QUnit.ok(MyTech.registerSourceHandler, + 'added a registerSourceHandler function to the Tech'); + QUnit.ok(MyTech.selectSourceHandler, + 'added a selectSourceHandler function to the Tech'); // Create an instance of Tech - var tech = new MyTech(); + const tech = new MyTech(); // Check for the expected instance methods - ok(tech.setSource, 'added a setSource function to the tech instance'); + QUnit.ok(tech.setSource, 'added a setSource function to the tech instance'); // Create an internal state class for the source handler // The internal class would be used by a source handler to maintain state // and provde a dispose method for the handler. // This is optional for source handlers - var disposeCalled = false; - var handlerInternalState = function(){}; - handlerInternalState.prototype.dispose = function(){ + let disposeCalled = false; + const HandlerInternalState = function() {}; + + HandlerInternalState.prototype.dispose = function() { disposeCalled = true; }; // Create source handlers - var handlerOne = { - canPlayType: function(type){ - if (type !=='no-support') { + const handlerOne = { + canPlayType(type) { + if (type !== 'no-support') { return 'probably'; } return ''; }, - canHandleSource: function(source, options){ - strictEqual(tech.options_, options, 'the tech options were passed to the source handler canHandleSource'); - if (source.type !=='no-support') { + canHandleSource(source, options) { + QUnit.strictEqual(tech.options_, + options, + 'tech options passed to canHandleSource'); + if (source.type !== 'no-support') { return 'probably'; } return ''; }, - handleSource: function(s, t, o){ - strictEqual(tech, t, 'the tech instance was passed to the source handler'); - strictEqual(sourceA, s, 'the tech instance was passed to the source handler'); - strictEqual(tech.options_, o, 'the tech options were passed to the source handler handleSource'); - return new handlerInternalState(); + handleSource(s, t, o) { + QUnit.strictEqual(tech, + t, + 'tech instance passed to source handler'); + QUnit.strictEqual(sourceA, + s, + 'tech instance passed to the source handler'); + QUnit.strictEqual(tech.options_, + o, + 'tech options passed to the source handler handleSource'); + return new HandlerInternalState(); } }; - var handlerTwo = { - canPlayType: function(type){ - return ''; // no support + const handlerTwo = { + canPlayType(type) { + // no support + return ''; }, - canHandleSource: function(source, options){ - return ''; // no support + canHandleSource(source, options) { + // no support + return ''; }, - handleSource: function(source, tech, options){ - ok(false, 'handlerTwo supports nothing and should never be called'); + handleSource(source, tech_, options) { + QUnit.ok(false, 'handlerTwo supports nothing and should never be called'); } }; // Test registering source handlers MyTech.registerSourceHandler(handlerOne); - strictEqual(MyTech.sourceHandlers[0], handlerOne, 'handlerOne was added to the source handler array'); + QUnit.strictEqual(MyTech.sourceHandlers[0], + handlerOne, + 'handlerOne was added to the source handler array'); MyTech.registerSourceHandler(handlerTwo, 0); - strictEqual(MyTech.sourceHandlers[0], handlerTwo, 'handlerTwo was registered at the correct index (0)'); + QUnit.strictEqual(MyTech.sourceHandlers[0], + handlerTwo, + 'handlerTwo was registered at the correct index (0)'); // Test handler selection - strictEqual(MyTech.selectSourceHandler(sourceA, tech.options_), handlerOne, 'handlerOne was selected to handle the valid source'); - strictEqual(MyTech.selectSourceHandler(sourceB, tech.options_), null, 'no handler was selected to handle the invalid source'); + QUnit.strictEqual(MyTech.selectSourceHandler(sourceA, tech.options_), + handlerOne, + 'handlerOne was selected to handle the valid source'); + QUnit.strictEqual(MyTech.selectSourceHandler(sourceB, tech.options_), + null, + 'no handler was selected to handle the invalid source'); // Test canPlayType return values - strictEqual(MyTech.canPlayType(sourceA.type), 'probably', 'the Tech returned probably for the valid source'); - strictEqual(MyTech.canPlayType(sourceB.type), '', 'the Tech returned an empty string for the invalid source'); + QUnit.strictEqual(MyTech.canPlayType(sourceA.type), + 'probably', + 'the Tech returned probably for the valid source'); + QUnit.strictEqual(MyTech.canPlayType(sourceB.type), + '', + 'the Tech returned an empty string for the invalid source'); // Test canPlaySource return values - strictEqual(MyTech.canPlaySource(sourceA, tech.options_), 'probably', 'the Tech returned probably for the valid source'); - strictEqual(MyTech.canPlaySource(sourceB, tech.options_), '', 'the Tech returned an empty string for the invalid source'); + QUnit.strictEqual(MyTech.canPlaySource(sourceA, tech.options_), + 'probably', + 'the Tech returned probably for the valid source'); + QUnit.strictEqual(MyTech.canPlaySource(sourceB, tech.options_), + '', + 'the Tech returned an empty string for the invalid source'); tech.addRemoteTextTrack({}); tech.addRemoteTextTrack({}); @@ -239,155 +288,169 @@ test('should add the source handler interface to a tech', function(){ tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack_(new VideoTrack()); - equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); - equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.remoteTextTrackEls().length, 2, 'should have two remote text tracks els'); - equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); + QUnit.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); + QUnit.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.remoteTextTrackEls().length, + 2, + 'should have two remote text tracks els'); + QUnit.equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); // Pass a source through the source handler process of a tech instance tech.setSource(sourceA); // verify that the Tracks are still there - equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); - equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); - equal(tech.remoteTextTrackEls().length, 2, 'should have two remote text tracks els'); - equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); + QUnit.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); + QUnit.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.textTracks().length, 2, 'should have two video tracks at the start'); + QUnit.equal(tech.remoteTextTrackEls().length, + 2, + 'should have two remote text tracks els'); + QUnit.equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); - strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored'); - ok(tech.sourceHandler_.dispose, 'the handlerOne state instance was stored'); + QUnit.strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored'); + QUnit.ok(tech.sourceHandler_.dispose, 'the handlerOne state instance was stored'); // Pass a second source tech.setSource(sourceA); - strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored'); - ok(tech.sourceHandler_.dispose, 'the handlerOne state instance was stored'); + QUnit.strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored'); + QUnit.ok(tech.sourceHandler_.dispose, 'the handlerOne state instance was stored'); // verify that all the tracks were removed as we got a new source - equal(tech.audioTracks().length, 0, 'should have zero audio tracks'); - equal(tech.videoTracks().length, 0, 'should have zero video tracks'); - equal(tech.textTracks().length, 2, 'should have two text tracks'); - equal(tech.remoteTextTrackEls().length, 2, 'should have two remote text tracks els'); - equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); + QUnit.equal(tech.audioTracks().length, 0, 'should have zero audio tracks'); + QUnit.equal(tech.videoTracks().length, 0, 'should have zero video tracks'); + QUnit.equal(tech.textTracks().length, 2, 'should have two text tracks'); + QUnit.equal(tech.remoteTextTrackEls().length, + 2, + 'should have two remote text tracks els'); + QUnit.equal(tech.remoteTextTracks().length, 2, 'should have two remote text tracks'); // Check that the handler dipose method works - ok(disposeCalled, 'dispose has been called for the handler yet'); + QUnit.ok(disposeCalled, 'dispose has been called for the handler yet'); disposeCalled = false; tech.dispose(); - ok(disposeCalled, 'the handler dispose method was called when the tech was disposed'); + QUnit.ok(disposeCalled, + 'the handler dispose method was called when the tech was disposed'); }); -test('should handle unsupported sources with the source handler API', function(){ +QUnit.test('should handle unsupported sources with the source handler API', function() { // Define a new tech class - var MyTech = extendFn(Tech); + const MyTech = extendFn(Tech); + // Extend Tech with source handlers Tech.withSourceHandlers(MyTech); // Create an instance of Tech - var tech = new MyTech(); + const tech = new MyTech(); + let usedNative; - var usedNative; MyTech.nativeSourceHandler = { - handleSource: function(){ usedNative = true; } + handleSource() { + usedNative = true; + } }; tech.setSource(''); - ok(usedNative, 'native source handler was used when an unsupported source was set'); + QUnit.ok(usedNative, + 'native source handler was used when an unsupported source was set'); }); -test('should allow custom error events to be set', function() { - let tech = new Tech(); - let errors = []; +QUnit.test('should allow custom error events to be set', function() { + const tech = new Tech(); + const errors = []; + tech.on('error', function() { errors.push(tech.error()); }); - equal(tech.error(), null, 'error is null by default'); + QUnit.equal(tech.error(), null, 'error is null by default'); tech.error(new MediaError(1)); - equal(errors.length, 1, 'triggered an error event'); - equal(errors[0].code, 1, 'set the proper code'); + QUnit.equal(errors.length, 1, 'triggered an error event'); + QUnit.equal(errors[0].code, 1, 'set the proper code'); tech.error(2); - equal(errors.length, 2, 'triggered an error event'); - equal(errors[1].code, 2, 'wrapped the error code'); + QUnit.equal(errors.length, 2, 'triggered an error event'); + QUnit.equal(errors[1].code, 2, 'wrapped the error code'); }); -test('should track whether a video has played', function() { - let tech = new Tech(); +QUnit.test('should track whether a video has played', function() { + const tech = new Tech(); - equal(tech.played().length, 0, 'starts with zero length'); + QUnit.equal(tech.played().length, 0, 'starts with zero length'); tech.trigger('playing'); - equal(tech.played().length, 1, 'has length after playing'); + QUnit.equal(tech.played().length, 1, 'has length after playing'); }); -test('delegates seekable to the source handler', function(){ - let MyTech = extendFn(Tech, { - seekable: function() { +QUnit.test('delegates seekable to the source handler', function() { + const MyTech = extendFn(Tech, { + seekable() { throw new Error('You should not be calling me!'); } }); + Tech.withSourceHandlers(MyTech); let seekableCount = 0; - let handler = { - seekable: function() { + const handler = { + seekable() { seekableCount++; return createTimeRange(0, 0); } }; MyTech.registerSourceHandler({ - canPlayType: function() { + canPlayType() { return true; }, - canHandleSource: function() { + canHandleSource() { return true; }, - handleSource: function(source, tech, options) { + handleSource(source, tech, options) { return handler; } }); - let tech = new MyTech(); + const tech = new MyTech(); + tech.setSource({ src: 'example.mp4', type: 'video/mp4' }); tech.seekable(); - equal(seekableCount, 1, 'called the source handler'); + QUnit.equal(seekableCount, 1, 'called the source handler'); }); -test('Tech.isTech returns correct answers for techs and components', function() { - let isTech = Tech.isTech; - - ok(isTech(Tech), 'Tech is a Tech'); - ok(isTech(Html5), 'Html5 is a Tech'); - ok(isTech(new Html5({}, {})), 'An html5 instance is a Tech'); - ok(isTech(Flash), 'Flash is a Tech'); - ok(!isTech(5), 'A number is not a Tech'); - ok(!isTech('this is a tech'), 'A string is not a Tech'); - ok(!isTech(Button), 'A Button is not a Tech'); - ok(!isTech(new Button({}, {})), 'A Button instance is not a Tech'); - ok(!isTech(isTech), 'A function is not a Tech'); +QUnit.test('Tech.isTech returns correct answers for techs and components', function() { + const isTech = Tech.isTech; + + QUnit.ok(isTech(Tech), 'Tech is a Tech'); + QUnit.ok(isTech(Html5), 'Html5 is a Tech'); + QUnit.ok(isTech(new Html5({}, {})), 'An html5 instance is a Tech'); + QUnit.ok(isTech(Flash), 'Flash is a Tech'); + QUnit.ok(!isTech(5), 'A number is not a Tech'); + QUnit.ok(!isTech('this is a tech'), 'A string is not a Tech'); + QUnit.ok(!isTech(Button), 'A Button is not a Tech'); + QUnit.ok(!isTech(new Button({}, {})), 'A Button instance is not a Tech'); + QUnit.ok(!isTech(isTech), 'A function is not a Tech'); }); -test('Tech#setSource clears currentSource_ after repeated loadstart', function() { +QUnit.test('Tech#setSource clears currentSource_ after repeated loadstart', function() { let disposed = false; - let MyTech = extendFn(Tech); + const MyTech = extendFn(Tech); Tech.withSourceHandlers(MyTech); - let tech = new MyTech(); + const tech = new MyTech(); - var sourceHandler = { - canPlayType: function(type) { + const sourceHandler = { + canPlayType(type) { return true; }, - canHandleSource: function(source, options) { + canHandleSource(source, options) { return true; }, - handleSource: function(source, tech, options) { + handleSource(source, tech_, options) { return { - dispose: function() { + dispose() { disposed = true; } }; @@ -401,95 +464,98 @@ test('Tech#setSource clears currentSource_ after repeated loadstart', function() tech.setSource('test'); tech.currentSource_ = 'test'; tech.trigger('loadstart'); - equal(tech.currentSource_, 'test', 'Current source is test'); + QUnit.equal(tech.currentSource_, 'test', 'Current source is test'); // Second loadstart tech.trigger('loadstart'); - equal(tech.currentSource_, null, 'Current source is null'); - equal(disposed, true, 'disposed is true'); + QUnit.equal(tech.currentSource_, null, 'Current source is null'); + QUnit.equal(disposed, true, 'disposed is true'); // Third loadstart tech.currentSource_ = 'test'; tech.trigger('loadstart'); - equal(tech.currentSource_, null, 'Current source is still null'); + QUnit.equal(tech.currentSource_, null, 'Current source is still null'); }); -test('setSource after tech dispose should dispose source handler once', function(){ - let MyTech = extendFn(Tech); +QUnit.test('setSource after tech dispose should dispose source handler once', function() { + const MyTech = extendFn(Tech); + Tech.withSourceHandlers(MyTech); let disposeCount = 0; - let handler = { + const handler = { dispose() { disposeCount++; } }; MyTech.registerSourceHandler({ - canPlayType: function() { + canPlayType() { return true; }, - canHandleSource: function() { + canHandleSource() { return true; }, - handleSource: function(source, tech, options) { + handleSource(source, tech, options) { return handler; } }); - let tech = new MyTech(); + const tech = new MyTech(); + tech.setSource('test'); - equal(disposeCount, 0, 'did not call sourceHandler_ dispose for initial dispose'); + QUnit.equal(disposeCount, 0, 'did not call sourceHandler_ dispose for initial dispose'); tech.dispose(); - ok(!tech.sourceHandler_, 'sourceHandler should be unset'); - equal(disposeCount, 1, 'called the source handler dispose'); + QUnit.ok(!tech.sourceHandler_, 'sourceHandler should be unset'); + QUnit.equal(disposeCount, 1, 'called the source handler dispose'); // this would normally be done above tech on src after dispose tech.el_ = tech.createEl(); tech.setSource('test'); - equal(disposeCount, 1, 'did not dispose after initial setSource'); + QUnit.equal(disposeCount, 1, 'did not dispose after initial setSource'); tech.setSource('test'); - equal(disposeCount, 2, 'did dispose on second setSource'); + QUnit.equal(disposeCount, 2, 'did dispose on second setSource'); }); -test('setSource after previous setSource should dispose source handler once', function(){ - let MyTech = extendFn(Tech); +QUnit.test('setSource after previous setSource should dispose source handler once', function() { + const MyTech = extendFn(Tech); + Tech.withSourceHandlers(MyTech); let disposeCount = 0; - let handler = { + const handler = { dispose() { disposeCount++; } }; MyTech.registerSourceHandler({ - canPlayType: function() { + canPlayType() { return true; }, - canHandleSource: function() { + canHandleSource() { return true; }, - handleSource: function(source, tech, options) { + handleSource(source, tech, options) { return handler; } }); - let tech = new MyTech(); + const tech = new MyTech(); tech.setSource('test'); - equal(disposeCount, 0, 'did not call dispose for initial setSource'); + QUnit.equal(disposeCount, 0, 'did not call dispose for initial setSource'); tech.setSource('test'); - equal(disposeCount, 1, 'did dispose for second setSource'); + QUnit.equal(disposeCount, 1, 'did dispose for second setSource'); tech.setSource('test'); - equal(disposeCount, 2, 'did dispose for third setSource'); + QUnit.equal(disposeCount, 2, 'did dispose for third setSource'); }); diff --git a/test/unit/test-helpers.js b/test/unit/test-helpers.js index 5d3c2d5696..442de71017 100644 --- a/test/unit/test-helpers.js +++ b/test/unit/test-helpers.js @@ -1,32 +1,30 @@ import * as Dom from '../../src/js/utils/dom'; import Player from '../../src/js/player.js'; -import TechFaker from './tech/tech-faker.js'; -import window from 'global/window'; import document from 'global/document'; -var TestHelpers = { - makeTag: function(){ - var videoTag = document.createElement('video'); +const TestHelpers = { + makeTag() { + const videoTag = document.createElement('video'); + videoTag.id = 'example_1'; videoTag.className = 'video-js vjs-default-skin'; return videoTag; }, - makePlayer: function(playerOptions, videoTag){ - var player; - + makePlayer(playerOptions, videoTag) { videoTag = videoTag || TestHelpers.makeTag(); - var fixture = document.getElementById('qunit-fixture'); + const fixture = document.getElementById('qunit-fixture'); + fixture.appendChild(videoTag); playerOptions = playerOptions || {}; - playerOptions['techOrder'] = playerOptions['techOrder'] || ['techFaker']; + playerOptions.techOrder = playerOptions.techOrder || ['techFaker']; - return player = new Player(videoTag, playerOptions); + return new Player(videoTag, playerOptions); }, - getComputedStyle: function(el, rule){ + getComputedStyle(el, rule) { if (document.defaultView && document.defaultView.getComputedStyle) { return document.defaultView.getComputedStyle(el, null).getPropertyValue(rule); } @@ -37,9 +35,8 @@ var TestHelpers = { // return clientWidth or clientHeight instead for better accuracy rule = 'client' + rule.substr(0, 1).toUpperCase() + rule.substr(1); return el[rule] + 'px'; - } else { - return el.currentStyle[rule]; } + return el.currentStyle[rule]; } }, @@ -73,52 +70,60 @@ var TestHelpers = { * reference how many assertions will be run (e.g. for use * with `assert.expect()`). */ - assertEl: function(assert, el, spec) { - let attrs = spec.attrs ? Object.keys(spec.attrs) : []; - let classes = spec.classes || []; - let innerHTML = spec.innerHTML ? spec.innerHTML.trim() : ''; - let props = spec.props ? Object.keys(spec.props) : []; - let tagName = spec.tagName ? spec.tagName.toLowerCase() : ''; + assertEl(assert, el, spec) { + const attrs = spec.attrs ? Object.keys(spec.attrs) : []; + const classes = spec.classes || []; + const innerHTML = spec.innerHTML ? spec.innerHTML.trim() : ''; + const props = spec.props ? Object.keys(spec.props) : []; + const tagName = spec.tagName ? spec.tagName.toLowerCase() : ''; // Return value is a function, which runs through all the combined // assertions. This is done so that the count can be attached dynamically // and run whenever desired. - let run = () => { + const run = () => { if (tagName) { - let elTagName = el.tagName.toLowerCase(); - let msg = `el should have been a <${tagName}> and was a <${elTagName}>`; + const elTagName = el.tagName.toLowerCase(); + const msg = `el should have been a <${tagName}> and was a <${elTagName}>`; + assert.strictEqual(elTagName, tagName, msg); } if (innerHTML) { - let elInnerHTML = el.innerHTML.trim(); - let msg = `el should have expected HTML content`; + const elInnerHTML = el.innerHTML.trim(); + const msg = 'el should have expected HTML content'; + assert.strictEqual(elInnerHTML, innerHTML, msg); } attrs.forEach(a => { - let actual = el.getAttribute(a); - let expected = spec.attrs[a]; - let msg = `el should have the "${a}" attribute with the value "${expected}" and it was "${actual}"`; + const actual = el.getAttribute(a); + const expected = spec.attrs[a]; + const msg = `el should have the "${a}" attribute with ` + + `the value "${expected}" and it was "${actual}"`; + assert.strictEqual(actual, expected, msg); }); classes.forEach(c => { - let msg = `el should have the "${c}" class in its className, which is "${el.className}"`; + const msg = `el should have the "${c}" class in its ` + + `className, which is "${el.className}"`; + assert.ok(Dom.hasElClass(el, c), msg); }); props.forEach(p => { - let actual = el[p]; - let expected = spec.props[p]; - let msg = `el should have the "${p}" property with the value "${expected}" and it was "${actual}"`; + const actual = el[p]; + const expected = spec.props[p]; + const msg = `el should have the "${p}" property with the ` + + `value "${expected}" and it was "${actual}"`; + assert.strictEqual(actual, expected, msg); }); }; // Include the number of assertions to run, so it can be used to set // expectations (via `assert.expect()`). - run.count = Number(!!tagName) + + run.count = Number(!!tagName) + Number(!!innerHTML) + classes.length + attrs.length + diff --git a/test/unit/tracks/text-track-list-converter.test.js b/test/unit/tracks/text-track-list-converter.test.js index e8251c05b0..c21abe211a 100644 --- a/test/unit/tracks/text-track-list-converter.test.js +++ b/test/unit/tracks/text-track-list-converter.test.js @@ -7,13 +7,13 @@ import document from 'global/document'; QUnit.module('Text Track List Converter', {}); -let clean = (item) => { +const clean = (item) => { delete item.id; delete item.inBandMetadataTrackDispatchType; delete item.cues; }; -let cleanup = (item) => { +const cleanup = (item) => { if (Array.isArray(item)) { item.forEach(clean); } else { @@ -25,7 +25,7 @@ let cleanup = (item) => { if (Html5.supportsNativeTextTracks()) { QUnit.test('trackToJson_ produces correct representation for native track object', function(a) { - let track = document.createElement('track'); + const track = document.createElement('track'); track.src = 'example.com/english.vtt'; track.kind = 'captions'; @@ -41,25 +41,25 @@ if (Html5.supportsNativeTextTracks()) { }); QUnit.test('textTracksToJson produces good json output', function(a) { - let emulatedTrack = new TextTrack({ + const emulatedTrack = new TextTrack({ kind: 'captions', label: 'English', language: 'en', tech: {} }); - let nativeTrack = document.createElement('track'); + const nativeTrack = document.createElement('track'); nativeTrack.kind = 'captions'; nativeTrack.srclang = 'es'; nativeTrack.label = 'Spanish'; - let tt = new TextTrackList(); + const tt = new TextTrackList(); tt.addTrack_(nativeTrack.track); tt.addTrack_(emulatedTrack); - let tech = { + const tech = { $$() { return [nativeTrack]; }, @@ -90,7 +90,7 @@ if (Html5.supportsNativeTextTracks()) { }); QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with mixed tracks', function(a) { - let emulatedTrack = new TextTrack({ + const emulatedTrack = new TextTrack({ kind: 'captions', label: 'English', language: 'en', @@ -98,20 +98,20 @@ if (Html5.supportsNativeTextTracks()) { tech: {} }); - let nativeTrack = document.createElement('track'); + const nativeTrack = document.createElement('track'); nativeTrack.src = 'example.com/spanish.vtt'; nativeTrack.kind = 'captions'; nativeTrack.srclang = 'es'; nativeTrack.label = 'Spanish'; - let tt = new TextTrackList(); + const tt = new TextTrackList(); tt.addTrack_(nativeTrack.track); tt.addTrack_(emulatedTrack); let addRemotes = 0; - let tech = { + const tech = { $$() { return [nativeTrack]; }, @@ -141,7 +141,7 @@ if (Html5.supportsNativeTextTracks()) { } QUnit.test('trackToJson_ produces correct representation for emulated track object', function(a) { - let track = new TextTrack({ + const track = new TextTrack({ kind: 'captions', label: 'English', language: 'en', @@ -159,7 +159,7 @@ QUnit.test('trackToJson_ produces correct representation for emulated track obje }); QUnit.test('textTracksToJson produces good json output for emulated only', function(a) { - let emulatedTrack = new TextTrack({ + const emulatedTrack = new TextTrack({ kind: 'captions', label: 'English', language: 'en', @@ -167,7 +167,7 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct tech: {} }); - let anotherTrack = new TextTrack({ + const anotherTrack = new TextTrack({ src: 'example.com/spanish.vtt', kind: 'captions', srclang: 'es', @@ -175,12 +175,12 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct tech: {} }); - let tt = new TextTrackList(); + const tt = new TextTrackList(); tt.addTrack_(anotherTrack); tt.addTrack_(emulatedTrack); - let tech = { + const tech = { $$() { return []; }, @@ -213,7 +213,7 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct }); QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated tracks only', function(a) { - let emulatedTrack = new TextTrack({ + const emulatedTrack = new TextTrack({ kind: 'captions', label: 'English', language: 'en', @@ -221,7 +221,7 @@ QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated tech: {} }); - let anotherTrack = new TextTrack({ + const anotherTrack = new TextTrack({ src: 'example.com/spanish.vtt', kind: 'captions', srclang: 'es', @@ -229,13 +229,13 @@ QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated tech: {} }); - let tt = new TextTrackList(); + const tt = new TextTrackList(); tt.addTrack_(anotherTrack); tt.addTrack_(emulatedTrack); let addRemotes = 0; - let tech = { + const tech = { $$() { return []; }, diff --git a/test/unit/tracks/text-track.test.js b/test/unit/tracks/text-track.test.js index 5005bd60e1..4eea14d6c5 100644 --- a/test/unit/tracks/text-track.test.js +++ b/test/unit/tracks/text-track.test.js @@ -1,21 +1,22 @@ +/* eslint-env qunit */ import window from 'global/window'; import EventTarget from '../../../src/js/event-target.js'; import TrackBaseline from './track-baseline'; import TechFaker from '../tech/tech-faker'; import TextTrack from '../../../src/js/tracks/text-track.js'; import TestHelpers from '../test-helpers.js'; -import log from '../../../src/js/utils/log.js'; import proxyquireify from 'proxyquireify'; +import sinon from 'sinon'; const proxyquire = proxyquireify(require); - const defaultTech = { textTracks() {}, on() {}, off() {}, currentTime() {} }; -q.module('Text Track'); + +QUnit.module('Text Track'); // do baseline track testing TrackBaseline(TextTrack, { @@ -27,193 +28,189 @@ TrackBaseline(TextTrack, { tech: defaultTech }); -test('requires a tech', function() { - let error = new Error('A tech was not provided.'); +QUnit.test('requires a tech', function() { + const error = new Error('A tech was not provided.'); - q.throws(() => new TextTrack({}), error, 'a tech is required'); - q.throws(() => new TextTrack({tech: null}), error, 'a tech is required'); + QUnit.throws(() => new TextTrack({}), error, 'a tech is required'); + QUnit.throws(() => new TextTrack({tech: null}), error, 'a tech is required'); }); -test('can create a TextTrack with a mode property', function() { - let mode = 'disabled'; - let tt = new TextTrack({ +QUnit.test('can create a TextTrack with a mode property', function() { + const mode = 'disabled'; + const tt = new TextTrack({ mode, tech: defaultTech }); - equal(tt.mode, mode, 'we have a mode'); + QUnit.equal(tt.mode, mode, 'we have a mode'); }); -test('defaults when items not provided', function() { - let tt = new TextTrack({ +QUnit.test('defaults when items not provided', function() { + const tt = new TextTrack({ tech: TechFaker }); - equal(tt.kind, 'subtitles', 'kind defaulted to subtitles'); - equal(tt.mode, 'disabled', 'mode defaulted to disabled'); - equal(tt.label, '', 'label defaults to empty string'); - equal(tt.language, '', 'language defaults to empty string'); + QUnit.equal(tt.kind, 'subtitles', 'kind defaulted to subtitles'); + QUnit.equal(tt.mode, 'disabled', 'mode defaulted to disabled'); + QUnit.equal(tt.label, '', 'label defaults to empty string'); + QUnit.equal(tt.language, '', 'language defaults to empty string'); }); -test('kind can only be one of several options, defaults to subtitles', function() { +QUnit.test('kind can only be one of several options, defaults to subtitles', function() { let tt = new TextTrack({ tech: defaultTech, kind: 'foo' }); - equal(tt.kind, 'subtitles', 'the kind is set to subtitles, not foo'); - notEqual(tt.kind, 'foo', 'the kind is set to subtitles, not foo'); + QUnit.equal(tt.kind, 'subtitles', 'the kind is set to subtitles, not foo'); + QUnit.notEqual(tt.kind, 'foo', 'the kind is set to subtitles, not foo'); tt = new TextTrack({ tech: defaultTech, kind: 'subtitles' }); - equal(tt.kind, 'subtitles', 'the kind is set to subtitles'); + QUnit.equal(tt.kind, 'subtitles', 'the kind is set to subtitles'); tt = new TextTrack({ tech: defaultTech, kind: 'captions' }); - equal(tt.kind, 'captions', 'the kind is set to captions'); + QUnit.equal(tt.kind, 'captions', 'the kind is set to captions'); tt = new TextTrack({ tech: defaultTech, kind: 'descriptions' }); - equal(tt.kind, 'descriptions', 'the kind is set to descriptions'); + QUnit.equal(tt.kind, 'descriptions', 'the kind is set to descriptions'); tt = new TextTrack({ tech: defaultTech, kind: 'chapters' }); - equal(tt.kind, 'chapters', 'the kind is set to chapters'); + QUnit.equal(tt.kind, 'chapters', 'the kind is set to chapters'); tt = new TextTrack({ tech: defaultTech, kind: 'metadata' }); - equal(tt.kind, 'metadata', 'the kind is set to metadata'); + QUnit.equal(tt.kind, 'metadata', 'the kind is set to metadata'); }); -test('mode can only be one of several options, defaults to disabled', function() { +QUnit.test('mode can only be one of several options, defaults to disabled', function() { let tt = new TextTrack({ tech: defaultTech, mode: 'foo' }); - equal(tt.mode, 'disabled', 'the mode is set to disabled, not foo'); - notEqual(tt.mode, 'foo', 'the mode is set to disabld, not foo'); + QUnit.equal(tt.mode, 'disabled', 'the mode is set to disabled, not foo'); + QUnit.notEqual(tt.mode, 'foo', 'the mode is set to disabld, not foo'); tt = new TextTrack({ tech: defaultTech, mode: 'disabled' }); - equal(tt.mode, 'disabled', 'the mode is set to disabled'); + QUnit.equal(tt.mode, 'disabled', 'the mode is set to disabled'); tt = new TextTrack({ tech: defaultTech, mode: 'hidden' }); - equal(tt.mode, 'hidden', 'the mode is set to hidden'); + QUnit.equal(tt.mode, 'hidden', 'the mode is set to hidden'); tt = new TextTrack({ tech: defaultTech, mode: 'showing' }); - equal(tt.mode, 'showing', 'the mode is set to showing'); + QUnit.equal(tt.mode, 'showing', 'the mode is set to showing'); }); -test('cue and activeCues are read only', function() { - let mode = 'disabled'; - let tt = new TextTrack({ +QUnit.test('cue and activeCues are read only', function() { + const mode = 'disabled'; + const tt = new TextTrack({ mode, - tech: defaultTech, + tech: defaultTech }); tt.cues = 'foo'; tt.activeCues = 'bar'; - notEqual(tt.cues, 'foo', 'cues is still original value'); - notEqual(tt.activeCues, 'bar', 'activeCues is still original value'); + QUnit.notEqual(tt.cues, 'foo', 'cues is still original value'); + QUnit.notEqual(tt.activeCues, 'bar', 'activeCues is still original value'); }); -test('mode can only be set to a few options', function() { - let tt = new TextTrack({ +QUnit.test('mode can only be set to a few options', function() { + const tt = new TextTrack({ tech: defaultTech }); tt.mode = 'foo'; - notEqual(tt.mode, 'foo', 'the mode is still the old value, disabled'); - equal(tt.mode, 'disabled', 'still on the default mode, disabled'); + QUnit.notEqual(tt.mode, 'foo', 'the mode is still the old value, disabled'); + QUnit.equal(tt.mode, 'disabled', 'still on the default mode, disabled'); tt.mode = 'hidden'; - equal(tt.mode, 'hidden', 'mode set to hidden'); + QUnit.equal(tt.mode, 'hidden', 'mode set to hidden'); tt.mode = 'bar'; - notEqual(tt.mode, 'bar', 'the mode is still the old value, hidden'); - equal(tt.mode, 'hidden', 'still on the previous mode, hidden'); + QUnit.notEqual(tt.mode, 'bar', 'the mode is still the old value, hidden'); + QUnit.equal(tt.mode, 'hidden', 'still on the previous mode, hidden'); tt.mode = 'showing'; - equal(tt.mode, 'showing', 'mode set to showing'); + QUnit.equal(tt.mode, 'showing', 'mode set to showing'); tt.mode = 'baz'; - notEqual(tt.mode, 'baz', 'the mode is still the old value, showing'); - equal(tt.mode, 'showing', 'still on the previous mode, showing'); + QUnit.notEqual(tt.mode, 'baz', 'the mode is still the old value, showing'); + QUnit.equal(tt.mode, 'showing', 'still on the previous mode, showing'); }); -test('cues and activeCues return a TextTrackCueList', function() { - let tt = new TextTrack({ +QUnit.test('cues and activeCues return a TextTrackCueList', function() { + const tt = new TextTrack({ tech: defaultTech }); - ok(tt.cues.getCueById, 'cues are a TextTrackCueList'); - ok(tt.activeCues.getCueById, 'activeCues are a TextTrackCueList'); + QUnit.ok(tt.cues.getCueById, 'cues are a TextTrackCueList'); + QUnit.ok(tt.activeCues.getCueById, 'activeCues are a TextTrackCueList'); }); -test('cues can be added and removed from a TextTrack', function() { - let tt = new TextTrack({ +QUnit.test('cues can be added and removed from a TextTrack', function() { + const tt = new TextTrack({ tech: defaultTech }); - let cues; + const cues = tt.cues; - cues = tt.cues; - - equal(cues.length, 0, 'start with zero cues'); + QUnit.equal(cues.length, 0, 'start with zero cues'); tt.addCue({id: '1'}); - equal(cues.length, 1, 'we have one cue'); + QUnit.equal(cues.length, 1, 'we have one cue'); tt.removeCue(cues.getCueById('1')); - equal(cues.length, 0, 'we have removed our one cue'); + QUnit.equal(cues.length, 0, 'we have removed our one cue'); tt.addCue({id: '1'}); tt.addCue({id: '2'}); tt.addCue({id: '3'}); - equal(cues.length, 3, 'we now have 3 cues'); + QUnit.equal(cues.length, 3, 'we now have 3 cues'); }); -test('fires cuechange when cues become active and inactive', function() { - let player = TestHelpers.makePlayer(); +QUnit.test('fires cuechange when cues become active and inactive', function() { + const player = TestHelpers.makePlayer(); let changes = 0; - let cuechangeHandler; - let tt = new TextTrack({ + const tt = new TextTrack({ tech: player.tech_, mode: 'showing' }); - - cuechangeHandler = function() { + const cuechangeHandler = function() { changes++; }; @@ -232,7 +229,7 @@ test('fires cuechange when cues become active and inactive', function() { player.tech_.trigger('timeupdate'); - equal(changes, 2, 'a cuechange event trigger addEventListener and oncuechange'); + QUnit.equal(changes, 2, 'a cuechange event trigger addEventListener and oncuechange'); player.tech_.currentTime = function() { return 7; @@ -240,12 +237,12 @@ test('fires cuechange when cues become active and inactive', function() { player.tech_.trigger('timeupdate'); - equal(changes, 4, 'a cuechange event trigger addEventListener and oncuechange'); + QUnit.equal(changes, 4, 'a cuechange event trigger addEventListener and oncuechange'); player.dispose(); }); -test('tracks are parsed if vttjs is loaded', function() { +QUnit.test('tracks are parsed if vttjs is loaded', function() { const clock = sinon.useFakeTimers(); const oldVTT = window.WebVTT; let parserCreated = false; @@ -265,56 +262,60 @@ test('tracks are parsed if vttjs is loaded', function() { // use proxyquire to stub xhr module because IE8s XDomainRequest usage let xhrHandler; - let TextTrack = proxyquire('../../../src/js/tracks/text-track.js', { + const TextTrack_ = proxyquire('../../../src/js/tracks/text-track.js', { xhr(options, fn) { - xhrHandler = fn; + xhrHandler = fn; } }); - let tt = new TextTrack({ + /* eslint-disable no-unused-vars */ + const tt = new TextTrack_({ tech: defaultTech, src: 'http://example.com' }); + /* eslint-enable no-unused-vars */ xhrHandler(null, {}, 'WEBVTT\n'); - ok(parserCreated, 'WebVTT is loaded, so we can just parse'); + QUnit.ok(parserCreated, 'WebVTT is loaded, so we can just parse'); clock.restore(); window.WebVTT = oldVTT; - TextTrack = proxyquire('../../../src/js/tracks/text-track.js', {}); }); -test('tracks are parsed once vttjs is loaded', function() { +QUnit.test('tracks are parsed once vttjs is loaded', function() { const clock = sinon.useFakeTimers(); const oldVTT = window.WebVTT; let parserCreated = false; // use proxyquire to stub xhr module because IE8s XDomainRequest usage let xhrHandler; - let TextTrack = proxyquire('../../../src/js/tracks/text-track.js', { + const TextTrack_ = proxyquire('../../../src/js/tracks/text-track.js', { xhr(options, fn) { - xhrHandler = fn; + xhrHandler = fn; } }); window.WebVTT = true; - let testTech = new EventTarget(); + const testTech = new EventTarget(); + testTech.textTracks = () => {}; testTech.currentTime = () => {}; - let tt = new TextTrack({ + /* eslint-disable no-unused-vars */ + const tt = new TextTrack_({ tech: testTech, src: 'http://example.com' }); + /* eslint-enable no-unused-vars */ xhrHandler(null, {}, 'WEBVTT\n'); - ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); + QUnit.ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); clock.tick(100); - ok(!parserCreated, 'WebVTT still not loaded, do not try to parse yet'); + QUnit.ok(!parserCreated, 'WebVTT still not loaded, do not try to parse yet'); window.WebVTT = () => {}; window.WebVTT.StringDecoder = () => {}; @@ -330,61 +331,63 @@ test('tracks are parsed once vttjs is loaded', function() { }; testTech.trigger('vttjsloaded'); - ok(parserCreated, 'WebVTT is loaded, so we can parse now'); + QUnit.ok(parserCreated, 'WebVTT is loaded, so we can parse now'); clock.restore(); window.WebVTT = oldVTT; - TextTrack = proxyquire('../../../src/js/tracks/text-track.js', {}); }); -test('stops processing if vttjs loading errored out', function() { +QUnit.test('stops processing if vttjs loading errored out', function() { const clock = sinon.useFakeTimers(); const errorSpy = sinon.spy(); const oldVTT = window.WebVTT; - let parserCreated = false; + const parserCreated = false; + window.WebVTT = true; // use proxyquire to stub xhr module because IE8s XDomainRequest usage let xhrHandler; - let TextTrack = proxyquire('../../../src/js/tracks/text-track.js', { + const TextTrack_ = proxyquire('../../../src/js/tracks/text-track.js', { xhr(options, fn) { - xhrHandler = fn; + xhrHandler = fn; }, '../utils/log.js': { - 'default': { + default: { error: errorSpy } } }); - let testTech = new EventTarget(); + const testTech = new EventTarget(); + testTech.textTracks = () => {}; testTech.currentTime = () => {}; sinon.stub(testTech, 'off'); testTech.off.withArgs('vttjsloaded'); - let tt = new TextTrack({ + /* eslint-disable no-unused-vars */ + const tt = new TextTrack_({ tech: testTech, src: 'http://example.com' }); + /* eslint-enable no-unused-vars */ xhrHandler(null, {}, 'WEBVTT\n'); - ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); + QUnit.ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); testTech.trigger('vttjserror'); - let offSpyCall = testTech.off.getCall(0); + const offSpyCall = testTech.off.getCall(0); - ok(errorSpy.called, 'vttjs failed to load, so log.error was called'); + QUnit.ok(errorSpy.called, 'vttjs failed to load, so log.error was called'); if (errorSpy.called) { - ok(/^vttjs failed to load, stopping trying to process/.test(errorSpy.getCall(0).args[0]), + QUnit.ok(/^vttjs failed to load, stopping trying to process/.test(errorSpy.getCall(0).args[0]), 'log.error was called with the expected message'); } - ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); - ok(offSpyCall, 'tech.off was called'); + QUnit.ok(!parserCreated, 'WebVTT is not loaded, do not try to parse yet'); + QUnit.ok(offSpyCall, 'tech.off was called'); clock.restore(); window.WebVTT = oldVTT; - TextTrack = proxyquire('../../../src/js/tracks/text-track.js', {}); }); diff --git a/test/unit/tracks/text-tracks.test.js b/test/unit/tracks/text-tracks.test.js index baa85808bc..52b749fdc1 100644 --- a/test/unit/tracks/text-tracks.test.js +++ b/test/unit/tracks/text-tracks.test.js @@ -1,3 +1,4 @@ +/* eslint-env qunit */ import ChaptersButton from '../../../src/js/control-bar/text-track-controls/chapters-button.js'; import DescriptionsButton from '../../../src/js/control-bar/text-track-controls/descriptions-button.js'; import SubtitlesButton from '../../../src/js/control-bar/text-track-controls/subtitles-button.js'; @@ -12,8 +13,9 @@ import Component from '../../../src/js/component.js'; import * as browser from '../../../src/js/utils/browser.js'; import TestHelpers from '../test-helpers.js'; import document from 'global/document'; +import sinon from 'sinon'; -q.module('Tracks', { +QUnit.module('Tracks', { setup() { this.clock = sinon.useFakeTimers(); }, @@ -22,28 +24,22 @@ q.module('Tracks', { } }); -test('should place title list item into ul', function() { - let player; - let chaptersButton; +QUnit.test('should place title list item into ul', function() { + const player = TestHelpers.makePlayer(); + const chaptersButton = new ChaptersButton(player); - player = TestHelpers.makePlayer(); + const menuContentElement = chaptersButton.el().getElementsByTagName('UL')[0]; + const titleElement = menuContentElement.children[0]; - chaptersButton = new ChaptersButton(player); - - let menuContentElement = chaptersButton.el().getElementsByTagName('UL')[0]; - let titleElement = menuContentElement.children[0]; - - ok(titleElement.innerHTML === 'Chapters', 'title element placed in ul'); + QUnit.ok(titleElement.innerHTML === 'Chapters', 'title element placed in ul'); player.dispose(); }); -test('Player track methods call the tech', function() { - let player; +QUnit.test('Player track methods call the tech', function() { + const player = TestHelpers.makePlayer(); let calls = 0; - player = TestHelpers.makePlayer(); - player.tech_.textTracks = function() { calls++; }; @@ -54,14 +50,15 @@ test('Player track methods call the tech', function() { player.addTextTrack(); player.textTracks(); - equal(calls, 2, 'both textTrack and addTextTrack defer to the tech'); + QUnit.equal(calls, 2, 'both textTrack and addTextTrack defer to the tech'); player.dispose(); }); -test('TextTrackDisplay initializes tracks on player ready', function() { +QUnit.test('TextTrackDisplay initializes tracks on player ready', function() { let calls = 0; - let ttd = new TextTrackDisplay({ + /* eslint-disable no-unused-vars */ + const ttd = new TextTrackDisplay({ on() {}, addTextTracks() { calls--; @@ -73,17 +70,15 @@ test('TextTrackDisplay initializes tracks on player ready', function() { calls++; } }, {}); + /* eslint-enable no-unused-vars */ - equal(calls, 1, 'only a player.ready call was made'); + QUnit.equal(calls, 1, 'only a player.ready call was made'); }); -test('listen to remove and add track events in native text tracks', function() { - let oldTestVid = Html5.TEST_VID; - let player; - let options; - let oldTextTracks = Html5.prototype.textTracks; - let events = {}; - let html; +QUnit.test('listen to remove and add track events in native text tracks', function() { + const oldTestVid = Html5.TEST_VID; + const oldTextTracks = Html5.prototype.textTracks; + const events = {}; Html5.prototype.textTracks = function() { return { @@ -97,7 +92,7 @@ test('listen to remove and add track events in native text tracks', function() { textTracks: [] }; - player = { + const player = { // Function.prototype is a built-in no-op function. controls() {}, ready() {}, @@ -113,35 +108,29 @@ test('listen to remove and add track events in native text tracks', function() { }; } }; + player.player_ = player; - player.options_ = options = {}; + player.options_ = {}; - html = new Html5(options); + /* eslint-disable no-unused-vars */ + const html = new Html5({}); + /* eslint-enable no-unused-vars */ - ok(events.removetrack, 'removetrack listener was added'); - ok(events.addtrack, 'addtrack listener was added'); + QUnit.ok(events.removetrack, 'removetrack listener was added'); + QUnit.ok(events.addtrack, 'addtrack listener was added'); Html5.TEST_VID = oldTestVid; Html5.prototype.textTracks = oldTextTracks; }); -test('update texttrack buttons on removetrack or addtrack', function() { +QUnit.test('update texttrack buttons on removetrack or addtrack', function() { let update = 0; - let i; - let player; - let tag; - let track; - let oldTextTracks; - let events = {}; - let oldCaptionsUpdate; - let oldSubsUpdate; - let oldChaptersUpdate; - let oldDescriptionsUpdate; - - oldCaptionsUpdate = CaptionsButton.prototype.update; - oldSubsUpdate = SubtitlesButton.prototype.update; - oldDescriptionsUpdate = DescriptionsButton.prototype.update; - oldChaptersUpdate = ChaptersButton.prototype.update; + const events = {}; + const oldCaptionsUpdate = CaptionsButton.prototype.update; + const oldSubsUpdate = SubtitlesButton.prototype.update; + const oldDescriptionsUpdate = DescriptionsButton.prototype.update; + const oldChaptersUpdate = ChaptersButton.prototype.update; + CaptionsButton.prototype.update = function() { update++; oldCaptionsUpdate.call(this); @@ -160,7 +149,9 @@ test('update texttrack buttons on removetrack or addtrack', function() { }; Tech.prototype.featuresNativeTextTracks = true; - oldTextTracks = Tech.prototype.textTracks; + + const oldTextTracks = Tech.prototype.textTracks; + Tech.prototype.textTracks = function() { return { length: 0, @@ -175,37 +166,39 @@ test('update texttrack buttons on removetrack or addtrack', function() { }; }; - tag = document.createElement('video'); - track = document.createElement('track'); - track.kind = 'captions'; - track.label = 'en'; - track.language = 'English'; - track.src = '#en.vtt'; - tag.appendChild(track); - track = document.createElement('track'); - track.kind = 'captions'; - track.label = 'es'; - track.language = 'Spanish'; - track.src = '#es.vtt'; - tag.appendChild(track); - - player = TestHelpers.makePlayer({}, tag); + const tag = document.createElement('video'); + const track1 = document.createElement('track'); + const track2 = document.createElement('track'); + + track1.kind = 'captions'; + track1.label = 'en'; + track1.language = 'English'; + track1.src = '#en.vtt'; + tag.appendChild(track1); + + track2.kind = 'captions'; + track2.label = 'es'; + track2.language = 'Spanish'; + track2.src = '#es.vtt'; + tag.appendChild(track2); + + const player = TestHelpers.makePlayer({}, tag); player.player_ = player; - equal(update, 4, 'update was called on the four buttons during init'); + QUnit.equal(update, 4, 'update was called on the four buttons during init'); - for (i = 0; i < events.removetrack.length; i++) { + for (let i = 0; i < events.removetrack.length; i++) { events.removetrack[i](); } - equal(update, 8, 'update was called on the four buttons for remove track'); + QUnit.equal(update, 8, 'update was called on the four buttons for remove track'); - for (i = 0; i < events.addtrack.length; i++) { + for (let i = 0; i < events.addtrack.length; i++) { events.addtrack[i](); } - equal(update, 12, 'update was called on the four buttons for remove track'); + QUnit.equal(update, 12, 'update was called on the four buttons for remove track'); Tech.prototype.textTracks = oldTextTracks; Tech.prototype.featuresNativeTextTracks = false; @@ -216,28 +209,26 @@ test('update texttrack buttons on removetrack or addtrack', function() { player.dispose(); }); -test('if native text tracks are not supported, create a texttrackdisplay', function() { - let oldTestVid = Html5.TEST_VID; - let oldIsFirefox = browser.IS_FIREFOX; - let oldTextTrackDisplay = Component.getComponent('TextTrackDisplay'); +QUnit.test('if native text tracks are not supported, create a texttrackdisplay', function() { + const oldTestVid = Html5.TEST_VID; + const oldIsFirefox = browser.IS_FIREFOX; + const oldTextTrackDisplay = Component.getComponent('TextTrackDisplay'); let called = false; - let player; - let tag; - let track; - - tag = document.createElement('video'); - track = document.createElement('track'); - track.kind = 'captions'; - track.label = 'en'; - track.language = 'English'; - track.src = 'en.vtt'; - tag.appendChild(track); - track = document.createElement('track'); - track.kind = 'captions'; - track.label = 'es'; - track.language = 'Spanish'; - track.src = 'es.vtt'; - tag.appendChild(track); + const tag = document.createElement('video'); + const track1 = document.createElement('track'); + const track2 = document.createElement('track'); + + track1.kind = 'captions'; + track1.label = 'en'; + track1.language = 'English'; + track1.src = 'en.vtt'; + tag.appendChild(track1); + + track2.kind = 'captions'; + track2.label = 'es'; + track2.language = 'Spanish'; + track2.src = 'es.vtt'; + tag.appendChild(track2); Html5.TEST_VID = { textTracks: [] @@ -248,9 +239,9 @@ test('if native text tracks are not supported, create a texttrackdisplay', funct called = true; }); - player = TestHelpers.makePlayer({}, tag); + const player = TestHelpers.makePlayer({}, tag); - ok(called, 'text track display was created'); + QUnit.ok(called, 'text track display was created'); Html5.TEST_VID = oldTestVid; browser.IS_FIREFOX = oldIsFirefox; @@ -259,8 +250,8 @@ test('if native text tracks are not supported, create a texttrackdisplay', funct player.dispose(); }); -test('html5 tech supports native text tracks if the video supports it, unless mode is a number', function() { - let oldTestVid = Html5.TEST_VID; +QUnit.test('html5 tech supports native text tracks if the video supports it, unless mode is a number', function() { + const oldTestVid = Html5.TEST_VID; Html5.TEST_VID = { textTracks: [{ @@ -268,14 +259,15 @@ test('html5 tech supports native text tracks if the video supports it, unless mo }] }; - ok(!Html5.supportsNativeTextTracks(), 'native text tracks are not supported if mode is a number'); + QUnit.ok(!Html5.supportsNativeTextTracks(), + 'native text tracks are not supported if mode is a number'); Html5.TEST_VID = oldTestVid; }); -test('html5 tech supports native text tracks if the video supports it, unless it is firefox', function() { - let oldTestVid = Html5.TEST_VID; - let oldIsFirefox = browser.IS_FIREFOX; +QUnit.test('html5 tech supports native text tracks if the video supports it, unless it is firefox', function() { + const oldTestVid = Html5.TEST_VID; + const oldIsFirefox = browser.IS_FIREFOX; Html5.TEST_VID = { textTracks: [] @@ -283,39 +275,41 @@ test('html5 tech supports native text tracks if the video supports it, unless it browser.IS_FIREFOX = true; - ok(!Html5.supportsNativeTextTracks(), 'if textTracks are available on video element, native text tracks are supported'); + QUnit.ok(!Html5.supportsNativeTextTracks(), + 'if textTracks are available on video element,' + + ' native text tracks are supported'); Html5.TEST_VID = oldTestVid; browser.IS_FIREFOX = oldIsFirefox; }); -test('when switching techs, we should not get a new text track', function() { - let player = TestHelpers.makePlayer(); +QUnit.test('when switching techs, we should not get a new text track', function() { + const player = TestHelpers.makePlayer(); player.loadTech_('TechFaker'); - let firstTracks = player.textTracks(); + const firstTracks = player.textTracks(); player.loadTech_('TechFaker'); - let secondTracks = player.textTracks(); + const secondTracks = player.textTracks(); - ok(firstTracks === secondTracks, 'the tracks are equal'); + QUnit.ok(firstTracks === secondTracks, 'the tracks are equal'); }); if (Html5.supportsNativeTextTracks()) { - test('listen to native remove and add track events in native text tracks', function(assert) { - let done = assert.async(); + QUnit.test('listen to native remove and add track events in native text tracks', function(assert) { + const done = assert.async(); - let el = document.createElement('video'); - let html = new Html5({el}); - let tt = el.textTracks; - let emulatedTt = html.textTracks(); - let track = document.createElement('track'); + const el = document.createElement('video'); + const html = new Html5({el}); + const tt = el.textTracks; + const emulatedTt = html.textTracks(); + const track = document.createElement('track'); el.appendChild(track); - let addtrack = function() { - equal(emulatedTt.length, tt.length, 'we have matching tracks length'); - equal(emulatedTt.length, 1, 'we have one text track'); + const addtrack = function() { + QUnit.equal(emulatedTt.length, tt.length, 'we have matching tracks length'); + QUnit.equal(emulatedTt.length, 1, 'we have one text track'); emulatedTt.off('addtrack', addtrack); el.removeChild(track); @@ -323,32 +317,32 @@ if (Html5.supportsNativeTextTracks()) { emulatedTt.on('addtrack', addtrack); emulatedTt.on('removetrack', function() { - equal(emulatedTt.length, tt.length, 'we have matching tracks length'); - equal(emulatedTt.length, 0, 'we have no more text tracks'); + QUnit.equal(emulatedTt.length, tt.length, 'we have matching tracks length'); + QUnit.equal(emulatedTt.length, 0, 'we have no more text tracks'); done(); }); }); - test('should have removed tracks on dispose', function(assert) { - let done = assert.async(); + QUnit.test('should have removed tracks on dispose', function(assert) { + const done = assert.async(); - let el = document.createElement('video'); - let html = new Html5({el}); - let tt = el.textTracks; - let emulatedTt = html.textTracks(); - let track = document.createElement('track'); + const el = document.createElement('video'); + const html = new Html5({el}); + const tt = el.textTracks; + const emulatedTt = html.textTracks(); + const track = document.createElement('track'); el.appendChild(track); - let addtrack = function() { - equal(emulatedTt.length, tt.length, 'we have matching tracks length'); - equal(emulatedTt.length, 1, 'we have one text track'); + const addtrack = function() { + QUnit.equal(emulatedTt.length, tt.length, 'we have matching tracks length'); + QUnit.equal(emulatedTt.length, 1, 'we have one text track'); emulatedTt.off('addtrack', addtrack); html.dispose(); - equal(emulatedTt.length, tt.length, 'we have matching tracks length'); - equal(emulatedTt.length, 0, 'we have no more text tracks'); + QUnit.equal(emulatedTt.length, tt.length, 'we have matching tracks length'); + QUnit.equal(emulatedTt.length, 0, 'we have no more text tracks'); done(); }; @@ -357,20 +351,20 @@ if (Html5.supportsNativeTextTracks()) { }); } -test('should check for text track changes when emulating text tracks', function() { - let tech = new Tech(); +QUnit.test('should check for text track changes when emulating text tracks', function() { + const tech = new Tech(); let numTextTrackChanges = 0; tech.on('texttrackchange', function() { numTextTrackChanges++; }); tech.emulateTextTracks(); - equal(numTextTrackChanges, 1, 'we got a texttrackchange event'); + QUnit.equal(numTextTrackChanges, 1, 'we got a texttrackchange event'); }); -test('removes cuechange event when text track is hidden for emulated tracks', function() { - let player = TestHelpers.makePlayer(); - let tt = new TextTrack({ +QUnit.test('removes cuechange event when text track is hidden for emulated tracks', function() { + const player = TestHelpers.makePlayer(); + const tt = new TextTrack({ tech: player.tech_, mode: 'showing' }); @@ -390,34 +384,34 @@ test('removes cuechange event when text track is hidden for emulated tracks', fu }); tt.mode = 'showing'; - equal(numTextTrackChanges, 1, + QUnit.equal(numTextTrackChanges, 1, 'texttrackchange should be called once for mode change'); tt.mode = 'showing'; - equal(numTextTrackChanges, 2, + QUnit.equal(numTextTrackChanges, 2, 'texttrackchange should be called once for mode change'); player.tech_.currentTime = function() { return 3; }; player.tech_.trigger('timeupdate'); - equal(numTextTrackChanges, 3, + QUnit.equal(numTextTrackChanges, 3, 'texttrackchange should be triggered once for the cuechange'); tt.mode = 'hidden'; - equal(numTextTrackChanges, 4, + QUnit.equal(numTextTrackChanges, 4, 'texttrackchange should be called once for the mode change'); player.tech_.currentTime = function() { return 7; }; player.tech_.trigger('timeupdate'); - equal(numTextTrackChanges, 4, + QUnit.equal(numTextTrackChanges, 4, 'texttrackchange should be not be called since mode is hidden'); }); -test('should return correct remote text track values', function() { - let fixture = document.getElementById('qunit-fixture'); - let html = ` +QUnit.test('should return correct remote text track values', function() { + const fixture = document.getElementById('qunit-fixture'); + const html = `