diff --git a/package.json b/package.json index 3b15b46117..5352d41dca 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "tsml": "1.0.1", "videojs-font": "2.0.0", "videojs-ie8": "1.1.2", - "videojs-swf": "5.1.0", "videojs-vtt.js": "0.12.1", "xhr": "2.2.2" }, diff --git a/src/js/component.js b/src/js/component.js index c43058a552..d55ff4d7ea 100644 --- a/src/js/component.js +++ b/src/js/component.js @@ -5,6 +5,7 @@ */ import window from 'global/window'; import * as Dom from './utils/dom.js'; +import * as DomData from './utils/dom-data'; import * as Fn from './utils/fn.js'; import * as Guid from './utils/guid.js'; import * as Events from './utils/events.js'; @@ -141,7 +142,7 @@ class Component { this.el_.parentNode.removeChild(this.el_); } - Dom.removeElData(this.el_); + DomData.removeData(this.el_); this.el_ = null; } @@ -816,7 +817,7 @@ class Component { * - False if the `Component` does not have the class` */ hasClass(classToCheck) { - return Dom.hasElClass(this.el_, classToCheck); + return Dom.hasClass(this.el_, classToCheck); } /** @@ -826,7 +827,7 @@ class Component { * CSS class name to add */ addClass(classToAdd) { - Dom.addElClass(this.el_, classToAdd); + Dom.addClass(this.el_, classToAdd); } /** @@ -836,7 +837,7 @@ class Component { * CSS class name to remove */ removeClass(classToRemove) { - Dom.removeElClass(this.el_, classToRemove); + Dom.removeClass(this.el_, classToRemove); } /** @@ -851,7 +852,7 @@ class Component { * An {@link Dom~predicate} function or a boolean */ toggleClass(classToToggle, predicate) { - Dom.toggleElClass(this.el_, classToToggle, predicate); + Dom.toggleClass(this.el_, classToToggle, predicate); } /** diff --git a/src/js/control-bar/mute-toggle.js b/src/js/control-bar/mute-toggle.js index 06794dbbb8..c7d211d777 100644 --- a/src/js/control-bar/mute-toggle.js +++ b/src/js/control-bar/mute-toggle.js @@ -88,9 +88,9 @@ class MuteToggle extends Button { // TODO improve muted icon classes for (let i = 0; i < 4; i++) { - Dom.removeElClass(this.el_, `vjs-vol-${i}`); + Dom.removeClass(this.el_, `vjs-vol-${i}`); } - Dom.addElClass(this.el_, `vjs-vol-${level}`); + Dom.addClass(this.el_, `vjs-vol-${level}`); } } diff --git a/src/js/menu/menu-button.js b/src/js/menu/menu-button.js index 2b061064c5..e80e77562b 100644 --- a/src/js/menu/menu-button.js +++ b/src/js/menu/menu-button.js @@ -83,7 +83,7 @@ class MenuButton extends ClickableComponent { }); menu.children_.unshift(title); - Dom.insertElFirst(title, menu.contentEl()); + Dom.prependTo(title, menu.contentEl()); } this.items = this.createItems(); diff --git a/src/js/player.js b/src/js/player.js index c46868b551..75bd217cf2 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -31,7 +31,6 @@ import {ALL as TRACK_TYPES} from './tracks/track-types'; // are always included in the video.js package. Importing the modules will // execute them and they will register themselves with video.js. import './tech/loader.js'; -import './tech/flash.js'; import './poster-image.js'; import './tracks/text-track-display.js'; import './loading-spinner.js'; @@ -281,7 +280,7 @@ class Player extends Component { let element = tag; while (element && element.nodeType === 1) { - if (Dom.getElAttributes(element).hasOwnProperty('lang')) { + if (Dom.getAttributes(element).hasOwnProperty('lang')) { options.language = element.getAttribute('lang'); break; } @@ -307,7 +306,7 @@ class Player extends Component { this.tag = tag; // Store the tag attributes used to restore html5 element - this.tagAttributes = tag && Dom.getElAttributes(tag); + this.tagAttributes = tag && Dom.getAttributes(tag); // Update current language this.language(this.options_.language); @@ -487,7 +486,7 @@ class Player extends Component { // Copy over all the attributes from the tag, including ID and class // ID will now reference player box, not the video tag - const attrs = Dom.getElAttributes(tag); + const attrs = Dom.getAttributes(tag); Object.getOwnPropertyNames(attrs).forEach(function(attr) { // workaround so we don't totally break IE7 @@ -534,7 +533,7 @@ class Player extends Component { for (let i = 0; i < links.length; i++) { const linkEl = links.item(i); - Dom.addElClass(linkEl, 'vjs-hidden'); + Dom.addClass(linkEl, 'vjs-hidden'); linkEl.setAttribute('hidden', 'hidden'); } @@ -552,7 +551,7 @@ class Player extends Component { // will work properly for other components // // Breaks iPhone, fixed in HTML5 setup. - Dom.insertElFirst(tag, el); + Dom.prependTo(tag, el); this.children_.unshift(tag); this.el_ = el; @@ -893,7 +892,7 @@ class Player extends Component { // Add the tech element in the DOM if it was not already there // Make sure to not insert the original video element if using Html5 if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) { - Dom.insertElFirst(this.tech_.el(), this.el()); + Dom.prependTo(this.tech_.el(), this.el()); } // Get rid of the original video tag reference after the first tech is loaded @@ -1999,7 +1998,7 @@ class Player extends Component { document.documentElement.style.overflow = 'hidden'; // Apply fullscreen styles - Dom.addElClass(document.body, 'vjs-full-window'); + Dom.addClass(document.body, 'vjs-full-window'); /** * @event Player#enterFullWindow @@ -2038,7 +2037,7 @@ class Player extends Component { document.documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles - Dom.removeElClass(document.body, 'vjs-full-window'); + Dom.removeClass(document.body, 'vjs-full-window'); // Resize the box, controller, and poster to original sizes // this.positionAll(); @@ -3042,10 +3041,10 @@ class Player extends Component { tracks: [] }; - const tagOptions = Dom.getElAttributes(tag); + const tagOptions = Dom.getAttributes(tag); const dataSetup = tagOptions['data-setup']; - if (Dom.hasElClass(tag, 'vjs-fluid')) { + if (Dom.hasClass(tag, 'vjs-fluid')) { tagOptions.fluid = true; } @@ -3073,9 +3072,9 @@ class Player extends Component { const childName = child.nodeName.toLowerCase(); if (childName === 'source') { - baseOptions.sources.push(Dom.getElAttributes(child)); + baseOptions.sources.push(Dom.getAttributes(child)); } else if (childName === 'track') { - baseOptions.tracks.push(Dom.getElAttributes(child)); + baseOptions.tracks.push(Dom.getAttributes(child)); } } } @@ -3187,8 +3186,7 @@ const navigator = window.navigator; */ Player.prototype.options_ = { // Default order of fallback technology - techOrder: ['html5', 'flash'], - // techOrder: ['flash','html5'], + techOrder: ['html5'], html5: {}, flash: {}, diff --git a/src/js/tech/flash-rtmp.js b/src/js/tech/flash-rtmp.js deleted file mode 100644 index eda11c1bb8..0000000000 --- a/src/js/tech/flash-rtmp.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @file flash-rtmp.js - * @module flash-rtmp - */ - -/** - * Add RTMP properties to the {@link Flash} Tech. - * - * @param {Flash} Flash - * The flash tech class. - * - * @mixin FlashRtmpDecorator - */ -function FlashRtmpDecorator(Flash) { - Flash.streamingFormats = { - 'rtmp/mp4': 'MP4', - 'rtmp/flv': 'FLV' - }; - - /** - * Join connection and stream with an ampersand. - * - * @param {string} connection - * The connection string. - * - * @param {string} stream - * The stream string. - */ - Flash.streamFromParts = function(connection, stream) { - return connection + '&' + stream; - }; - - /** - * The flash parts object that contains connection and stream info. - * - * @typedef {Object} Flash~PartsObject - * - * @property {string} connection - * The connection string of a source, defaults to an empty string. - * - * @property {string} stream - * The stream string of the source, defaults to an empty string. - */ - - /** - * Convert a source url into a stream and connection parts. - * - * @param {string} src - * the source url - * - * @return {Flash~PartsObject} - * The parts object that contains a connection and a stream - */ - Flash.streamToParts = function(src) { - const parts = { - connection: '', - stream: '' - }; - - if (!src) { - return parts; - } - - // Look for the normal URL separator we expect, '&'. - // If found, we split the URL into two pieces around the - // first '&'. - let connEnd = src.search(/&(?!\w+=)/); - let streamBegin; - - if (connEnd !== -1) { - streamBegin = connEnd + 1; - } else { - // If there's not a '&', we use the last '/' as the delimiter. - connEnd = streamBegin = src.lastIndexOf('/') + 1; - if (connEnd === 0) { - // really, there's not a '/'? - connEnd = streamBegin = src.length; - } - } - - parts.connection = src.substring(0, connEnd); - parts.stream = src.substring(streamBegin, src.length); - - return parts; - }; - - /** - * Check if the source type is a streaming type. - * - * @param {string} srcType - * The mime type to check. - * - * @return {boolean} - * - True if the source type is a streaming type. - * - False if the source type is not a streaming type. - */ - Flash.isStreamingType = function(srcType) { - return srcType in Flash.streamingFormats; - }; - - // RTMP has four variations, any string starting - // with one of these protocols should be valid - - /** - * Regular expression used to check if the source is an rtmp source. - * - * @property {RegExp} Flash.RTMP_RE - */ - Flash.RTMP_RE = /^rtmp[set]?:\/\//i; - - /** - * Check if the source itself is a streaming type. - * - * @param {string} src - * The url to the source. - * - * @return {boolean} - * - True if the source url indicates that the source is streaming. - * - False if the shource url indicates that the source url is not streaming. - */ - Flash.isStreamingSrc = function(src) { - return Flash.RTMP_RE.test(src); - }; - - /** - * A source handler for RTMP urls - * @type {Object} - */ - Flash.rtmpSourceHandler = {}; - - /** - * Check if Flash can play the given mime type. - * - * @param {string} type - * The mime type to check - * - * @return {string} - * 'maybe', or '' (empty string) - */ - Flash.rtmpSourceHandler.canPlayType = function(type) { - if (Flash.isStreamingType(type)) { - return 'maybe'; - } - - return ''; - }; - - /** - * Check if Flash can handle the source natively - * - * @param {Object} source - * The source object - * - * @param {Object} [options] - * The options passed to the tech - * - * @return {string} - * 'maybe', or '' (empty string) - */ - Flash.rtmpSourceHandler.canHandleSource = function(source, options) { - const can = Flash.rtmpSourceHandler.canPlayType(source.type); - - if (can) { - return can; - } - - if (Flash.isStreamingSrc(source.src)) { - return 'maybe'; - } - - return ''; - }; - - /** - * Pass the source to the flash object. - * - * @param {Object} source - * The source object - * - * @param {Flash} tech - * The instance of the Flash tech - * - * @param {Object} [options] - * The options to pass to the source - */ - Flash.rtmpSourceHandler.handleSource = function(source, tech, options) { - const srcParts = Flash.streamToParts(source.src); - - tech.setRtmpConnection(srcParts.connection); - tech.setRtmpStream(srcParts.stream); - }; - - // Register the native source handler - Flash.registerSourceHandler(Flash.rtmpSourceHandler); - - return Flash; -} - -export default FlashRtmpDecorator; diff --git a/src/js/tech/flash.js b/src/js/tech/flash.js deleted file mode 100644 index b5e71b2fe8..0000000000 --- a/src/js/tech/flash.js +++ /dev/null @@ -1,1078 +0,0 @@ -/** - * @file flash.js - * VideoJS-SWF - Custom Flash Player with HTML5-ish API - * https://github.com/zencoder/video-js-swf - * Not using setupTriggers. Using global onEvent func to distribute events - */ - -import Tech from './tech'; -import * as Dom from '../utils/dom.js'; -import * as Url from '../utils/url.js'; -import { createTimeRange } from '../utils/time-ranges.js'; -import FlashRtmpDecorator from './flash-rtmp'; -import window from 'global/window'; -import {assign} from '../utils/obj'; - -const navigator = window.navigator; - -/** - * Flash Media Controller - Wrapper for Flash Media API - * - * @mixes FlashRtmpDecorator - * @mixes Tech~SouceHandlerAdditions - * @extends Tech - */ -class Flash extends Tech { - - /** - * Create an instance of this Tech. - * - * @param {Object} [options] - * The key/value store of player options. - * - * @param {Component~ReadyCallback} ready - * Callback function to call when the `Flash` Tech is ready. - */ - constructor(options, ready) { - super(options, ready); - - // Set the source when ready - if (options.source) { - this.ready(function() { - this.setSource(options.source); - }, true); - } - - // Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers - // This allows resetting the playhead when we catch the reload - if (options.startTime) { - this.ready(function() { - this.load(); - this.play(); - this.currentTime(options.startTime); - }, true); - } - - // Add global window functions that the swf expects - // A 4.x workflow we weren't able to solve for in 5.0 - // because of the need to hard code these functions - // into the swf for security reasons - window.videojs = window.videojs || {}; - window.videojs.Flash = window.videojs.Flash || {}; - window.videojs.Flash.onReady = Flash.onReady; - window.videojs.Flash.onEvent = Flash.onEvent; - window.videojs.Flash.onError = Flash.onError; - - this.on('seeked', function() { - this.lastSeekTarget_ = undefined; - }); - - } - - /** - * Create the `Flash` Tech's DOM element. - * - * @return {Element} - * The element that gets created. - */ - createEl() { - const options = this.options_; - - // If video.js is hosted locally you should also set the location - // for the hosted swf, which should be relative to the page (not video.js) - // Otherwise this adds a CDN url. - // The CDN also auto-adds a swf URL for that specific version. - if (!options.swf) { - const ver = require('videojs-swf/package.json').version; - - options.swf = `//vjs.zencdn.net/swf/${ver}/video-js.swf`; - } - - // Generate ID for swf object - const objId = options.techId; - - // Merge default flashvars with ones passed in to init - const flashVars = assign({ - - // SWF Callback Functions - readyFunction: 'videojs.Flash.onReady', - eventProxyFunction: 'videojs.Flash.onEvent', - errorEventProxyFunction: 'videojs.Flash.onError', - - // Player Settings - autoplay: options.autoplay, - preload: options.preload, - loop: options.loop, - muted: options.muted - - }, options.flashVars); - - // Merge default parames with ones passed in - const params = assign({ - // Opaque is needed to overlay controls, but can affect playback performance - wmode: 'opaque', - // Using bgcolor prevents a white flash when the object is loading - bgcolor: '#000000' - }, options.params); - - // Merge default attributes with ones passed in - const attributes = assign({ - // Both ID and Name needed or swf to identify itself - id: objId, - name: objId, - class: 'vjs-tech' - }, options.attributes); - - this.el_ = Flash.embed(options.swf, flashVars, params, attributes); - this.el_.tech = this; - - return this.el_; - } - - /** - * Called by {@link Player#play} to play using the `Flash` `Tech`. - */ - play() { - if (this.ended()) { - this.setCurrentTime(0); - } - this.el_.vjs_play(); - } - - /** - * Called by {@link Player#pause} to pause using the `Flash` `Tech`. - */ - pause() { - this.el_.vjs_pause(); - } - - /** - * A getter/setter for the `Flash` Tech's source object. - * > Note: Please use {@link Flash#setSource} - * - * @param {Tech~SourceObject} [src] - * The source object you want to set on the `Flash` techs. - * - * @return {Tech~SourceObject|undefined} - * - The current source object when a source is not passed in. - * - undefined when setting - * - * @deprecated Since version 5. - */ - src(src) { - if (src === undefined) { - return this.currentSrc(); - } - - // Setting src through `src` not `setSrc` will be deprecated - return this.setSrc(src); - } - - /** - * A getter/setter for the `Flash` Tech's source object. - * - * @param {Tech~SourceObject} [src] - * The source object you want to set on the `Flash` techs. - * - * @return {Tech~SourceObject|undefined} - * - The current source object when a source is not passed in. - * - undefined when setting - */ - setSrc(src) { - // Make sure source URL is absolute. - src = Url.getAbsoluteURL(src); - this.el_.vjs_src(src); - - // Currently the SWF doesn't autoplay if you load a source later. - // e.g. Load player w/ no source, wait 2s, set src. - if (this.autoplay()) { - this.setTimeout(() => this.play(), 0); - } - } - - /** - * Indicates whether the media is currently seeking to a new position or not. - * - * @return {boolean} - * - True if seeking to a new position - * - False otherwise - */ - seeking() { - return this.lastSeekTarget_ !== undefined; - } - - /** - * Returns the current time in seconds that the media is at in playback. - * - * @param {number} time - * Current playtime of the media in seconds. - */ - setCurrentTime(time) { - const seekable = this.seekable(); - - if (seekable.length) { - // clamp to the current seekable range - time = time > seekable.start(0) ? time : seekable.start(0); - time = time < seekable.end(seekable.length - 1) ? time : seekable.end(seekable.length - 1); - - this.lastSeekTarget_ = time; - this.trigger('seeking'); - this.el_.vjs_setProperty('currentTime', time); - super.setCurrentTime(); - } - } - - /** - * Get the current playback time in seconds - * - * @return {number} - * The current time of playback in seconds. - */ - currentTime() { - // when seeking make the reported time keep up with the requested time - // by reading the time we're seeking to - if (this.seeking()) { - return this.lastSeekTarget_ || 0; - } - return this.el_.vjs_getProperty('currentTime'); - } - - /** - * Get the current source - * - * @method currentSrc - * @return {Tech~SourceObject} - * The current source - */ - currentSrc() { - if (this.currentSource_) { - return this.currentSource_.src; - } - return this.el_.vjs_getProperty('currentSrc'); - } - - /** - * Get the total duration of the current media. - * - * @return {number} - 8 The total duration of the current media. - */ - duration() { - if (this.readyState() === 0) { - return NaN; - } - const duration = this.el_.vjs_getProperty('duration'); - - return duration >= 0 ? duration : Infinity; - } - - /** - * Load media into Tech. - */ - load() { - this.el_.vjs_load(); - } - - /** - * Get the poster image that was set on the tech. - */ - poster() { - this.el_.vjs_getProperty('poster'); - } - - /** - * Poster images are not handled by the Flash tech so make this is a no-op. - */ - setPoster() {} - - /** - * Determine the time ranges that can be seeked to in the media. - * - * @return {TimeRange} - * Returns the time ranges that can be seeked to. - */ - seekable() { - const duration = this.duration(); - - if (duration === 0) { - return createTimeRange(); - } - return createTimeRange(0, duration); - } - - /** - * Get and create a `TimeRange` object for buffering. - * - * @return {TimeRange} - * The time range object that was created. - */ - buffered() { - const ranges = this.el_.vjs_getProperty('buffered'); - - if (ranges.length === 0) { - return createTimeRange(); - } - return createTimeRange(ranges[0][0], ranges[0][1]); - } - - /** - * Get fullscreen support - - * - * Flash does not allow fullscreen through javascript - * so this always returns false. - * - * @return {boolean} - * The Flash tech does not support fullscreen, so it will always return false. - */ - supportsFullScreen() { - // Flash does not allow fullscreen through javascript - return false; - } - - /** - * Flash does not allow fullscreen through javascript - * so this always returns false. - * - * @return {boolean} - * The Flash tech does not support fullscreen, so it will always return false. - */ - enterFullScreen() { - return false; - } - -} - -// Create setters and getters for attributes -const _api = Flash.prototype; -const _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,controls,volume,muted,defaultMuted'.split(','); -const _readOnly = 'networkState,readyState,initialTime,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(','); - -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); - }; -} - -// Create getter and setters for all read/write attributes -for (let i = 0; i < _readWrite.length; i++) { - _createGetter(_readWrite[i]); - _createSetter(_readWrite[i]); -} - -// Create getters for read-only attributes -for (let i = 0; i < _readOnly.length; i++) { - _createGetter(_readOnly[i]); -} - -/** ------------------------------ Getters ------------------------------ **/ -/** - * Get the value of `rtmpConnection` from the swf. - * - * @method Flash#rtmpConnection - * @return {string} - * The current value of `rtmpConnection` on the swf. - */ - -/** - * Get the value of `rtmpStream` from the swf. - * - * @method Flash#rtmpStream - * @return {string} - * The current value of `rtmpStream` on the swf. - */ - -/** - * Get the value of `preload` from the swf. `preload` indicates - * what should download before the media is interacted with. It can have the following - * values: - * - none: nothing should be downloaded - * - metadata: poster and the first few frames of the media may be downloaded to get - * media dimensions and other metadata - * - auto: allow the media and metadata for the media to be downloaded before - * interaction - * - * @method Flash#preload - * @return {string} - * The value of `preload` from the swf. Will be 'none', 'metadata', - * or 'auto'. - */ - -/** - * Get the value of `defaultPlaybackRate` from the swf. - * - * @method Flash#defaultPlaybackRate - * @return {number} - * The current value of `defaultPlaybackRate` on the swf. - */ - -/** - * Get the value of `playbackRate` from the swf. `playbackRate` indicates - * the rate at which the media is currently playing back. Examples: - * - if playbackRate is set to 2, media will play twice as fast. - * - if playbackRate is set to 0.5, media will play half as fast. - * - * @method Flash#playbackRate - * @return {number} - * The value of `playbackRate` from the swf. A number indicating - * the current playback speed of the media, where 1 is normal speed. - */ - -/** - * Get the value of `autoplay` from the swf. `autoplay` indicates - * that the media should start to play as soon as the page is ready. - * - * @method Flash#autoplay - * @return {boolean} - * - The value of `autoplay` from the swf. - * - True indicates that the media ashould start as soon as the page loads. - * - False indicates that the media should not start as soon as the page loads. - */ - -/** - * Get the value of `loop` from the swf. `loop` indicates - * that the media should return to the start of the media and continue playing once - * it reaches the end. - * - * @method Flash#loop - * @return {boolean} - * - The value of `loop` from the swf. - * - True indicates that playback should seek back to start once - * the end of a media is reached. - * - False indicates that playback should not loop back to the start when the - * end of the media is reached. - */ - -/** - * Get the value of `mediaGroup` from the swf. - * - * @method Flash#mediaGroup - * @return {string} - * The current value of `mediaGroup` on the swf. - */ - -/** - * Get the value of `controller` from the swf. - * - * @method Flash#controller - * @return {string} - * The current value of `controller` on the swf. - */ - -/** - * Get the value of `controls` from the swf. `controls` indicates - * whether the native flash controls should be shown or hidden. - * - * @method Flash#controls - * @return {boolean} - * - The value of `controls` from the swf. - * - True indicates that native controls should be showing. - * - False indicates that native controls should be hidden. - */ - -/** - * Get the value of the `volume` from the swf. `volume` indicates the current - * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and - * so on. - * - * @method Flash#volume - * @return {number} - * The volume percent as a decimal. Value will be between 0-1. - */ - -/** - * Get the value of the `muted` from the swf. `muted` indicates the current - * audio level should be silent. - * - * @method Flash#muted - * @return {boolean} - * - True if the audio should be set to silent - * - False otherwise - */ - -/** - * Get the value of `defaultMuted` from the swf. `defaultMuted` indicates - * whether the media should start muted or not. Only changes the default state of the - * media. `muted` and `defaultMuted` can have different values. `muted` indicates the - * current state. - * - * @method Flash#defaultMuted - * @return {boolean} - * - The value of `defaultMuted` from the swf. - * - True indicates that the media should start muted. - * - False indicates that the media should not start muted. - */ - -/** - * Get the value of `networkState` from the swf. `networkState` indicates - * the current network state. It returns an enumeration from the following list: - * - 0: NETWORK_EMPTY - * - 1: NEWORK_IDLE - * - 2: NETWORK_LOADING - * - 3: NETWORK_NO_SOURCE - * - * @method Flash#networkState - * @return {number} - * The value of `networkState` from the swf. This will be a number - * from the list in the description. - */ - -/** - * Get the value of `readyState` from the swf. `readyState` indicates - * the current state of the media element. It returns an enumeration from the - * following list: - * - 0: HAVE_NOTHING - * - 1: HAVE_METADATA - * - 2: HAVE_CURRENT_DATA - * - 3: HAVE_FUTURE_DATA - * - 4: HAVE_ENOUGH_DATA - * - * @method Flash#readyState - * @return {number} - * The value of `readyState` from the swf. This will be a number - * from the list in the description. - */ - -/** - * Get the value of `readyState` from the swf. `readyState` indicates - * the current state of the media element. It returns an enumeration from the - * following list: - * - 0: HAVE_NOTHING - * - 1: HAVE_METADATA - * - 2: HAVE_CURRENT_DATA - * - 3: HAVE_FUTURE_DATA - * - 4: HAVE_ENOUGH_DATA - * - * @method Flash#readyState - * @return {number} - * The value of `readyState` from the swf. This will be a number - * from the list in the description. - */ - -/** - * Get the value of `initialTime` from the swf. - * - * @method Flash#initialTime - * @return {number} - * The `initialTime` proprety on the swf. - */ - -/** - * Get the value of `startOffsetTime` from the swf. - * - * @method Flash#startOffsetTime - * @return {number} - * The `startOffsetTime` proprety on the swf. - */ - -/** - * Get the value of `paused` from the swf. `paused` indicates whether the swf - * is current paused or not. - * - * @method Flash#paused - * @return {boolean} - * The value of `paused` from the swf. - */ - -/** - * Get the value of `ended` from the swf. `ended` indicates whether - * the media has reached the end or not. - * - * @method Flash#ended - * @return {boolean} - * - True indicates that the media has ended. - * - False indicates that the media has not ended. - * - * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} - */ - -/** - * Get the value of `videoWidth` from the swf. `videoWidth` indicates - * the current width of the media in css pixels. - * - * @method Flash#videoWidth - * @return {number} - * The value of `videoWidth` from the swf. This will be a number - * in css pixels. - */ - -/** - * Get the value of `videoHeight` from the swf. `videoHeigth` indicates - * the current height of the media in css pixels. - * - * @method Flassh.prototype.videoHeight - * @return {number} - * The value of `videoHeight` from the swf. This will be a number - * in css pixels. - */ -/** ------------------------------ Setters ------------------------------ **/ - -/** - * Set the value of `rtmpConnection` on the swf. - * - * @method Flash#setRtmpConnection - * @param {string} rtmpConnection - * New value to set the `rtmpConnection` property to. - */ - -/** - * Set the value of `rtmpStream` on the swf. - * - * @method Flash#setRtmpStream - * @param {string} rtmpStream - * New value to set the `rtmpStream` property to. - */ - -/** - * Set the value of `preload` on the swf. `preload` indicates - * what should download before the media is interacted with. It can have the following - * values: - * - none: nothing should be downloaded - * - metadata: poster and the first few frames of the media may be downloaded to get - * media dimensions and other metadata - * - auto: allow the media and metadata for the media to be downloaded before - * interaction - * - * @method Flash#setPreload - * @param {string} preload - * The value of `preload` to set on the swf. Should be 'none', 'metadata', - * or 'auto'. - */ - -/** - * Set the value of `defaultPlaybackRate` on the swf. - * - * @method Flash#setDefaultPlaybackRate - * @param {number} defaultPlaybackRate - * New value to set the `defaultPlaybackRate` property to. - */ - -/** - * Set the value of `playbackRate` on the swf. `playbackRate` indicates - * the rate at which the media is currently playing back. Examples: - * - if playbackRate is set to 2, media will play twice as fast. - * - if playbackRate is set to 0.5, media will play half as fast. - * - * @method Flash#setPlaybackRate - * @param {number} playbackRate - * New value of `playbackRate` on the swf. A number indicating - * the current playback speed of the media, where 1 is normal speed. - */ - -/** - * Set the value of `autoplay` on the swf. `autoplay` indicates - * that the media should start to play as soon as the page is ready. - * - * @method Flash#setAutoplay - * @param {boolean} autoplay - * - The value of `autoplay` from the swf. - * - True indicates that the media ashould start as soon as the page loads. - * - False indicates that the media should not start as soon as the page loads. - */ - -/** - * Set the value of `loop` on the swf. `loop` indicates - * that the media should return to the start of the media and continue playing once - * it reaches the end. - * - * @method Flash#setLoop - * @param {boolean} loop - * - True indicates that playback should seek back to start once - * the end of a media is reached. - * - False indicates that playback should not loop back to the start when the - * end of the media is reached. - */ - -/** - * Set the value of `mediaGroup` on the swf. - * - * @method Flash#setMediaGroup - * @param {string} mediaGroup - * New value of `mediaGroup` to set on the swf. - */ - -/** - * Set the value of `controller` on the swf. - * - * @method Flash#setController - * @param {string} controller - * New value the current value of `controller` on the swf. - */ - -/** - * Get the value of `controls` from the swf. `controls` indicates - * whether the native flash controls should be shown or hidden. - * - * @method Flash#controls - * @return {boolean} - * - The value of `controls` from the swf. - * - True indicates that native controls should be showing. - * - False indicates that native controls should be hidden. - */ - -/** - * Set the value of the `volume` on the swf. `volume` indicates the current - * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and - * so on. - * - * @method Flash#setVolume - * @param {number} percentAsDecimal - * The volume percent as a decimal. Value will be between 0-1. - */ - -/** - * Set the value of the `muted` on the swf. `muted` indicates that the current - * audio level should be silent. - * - * @method Flash#setMuted - * @param {boolean} muted - * - True if the audio should be set to silent - * - False otherwise - */ - -/** - * Set the value of `defaultMuted` on the swf. `defaultMuted` indicates - * whether the media should start muted or not. Only changes the default state of the - * media. `muted` and `defaultMuted` can have different values. `muted` indicates the - * current state. - * - * @method Flash#setDefaultMuted - * @param {boolean} defaultMuted - * - True indicates that the media should start muted. - * - False indicates that the media should not start muted. - */ - -/* Flash Support Testing -------------------------------------------------------- */ - -/** - * Check if the Flash tech is currently supported. - * - * @return {boolean} - * - True if the flash tech is supported. - * - False otherwise. - */ -Flash.isSupported = function() { - return Flash.version()[0] >= 10; - // return swfobject.hasFlashPlayerVersion('10'); -}; - -// Add Source Handler pattern functions to this tech -Tech.withSourceHandlers(Flash); - -/* - * Native source handler for flash, simply passes the source to the swf element. - * - * @property {Tech~SourceObject} source - * The source object - * - * @property {Flash} tech - * The instance of the Flash tech - */ -Flash.nativeSourceHandler = {}; - -/** - * Check if the Flash can play the given mime type. - * - * @param {string} type - * The mimetype to check - * - * @return {string} - * 'maybe', or '' (empty string) - */ -Flash.nativeSourceHandler.canPlayType = function(type) { - if (type in Flash.formats) { - return 'maybe'; - } - - return ''; -}; - -/** - * Check if the media element can handle a source natively. - * - * @param {Tech~SourceObject} source - * The source object - * - * @param {Object} [options] - * Options to be passed to the tech. - * - * @return {string} - * 'maybe', or '' (empty string). - */ -Flash.nativeSourceHandler.canHandleSource = function(source, options) { - let type; - - function guessMimeType(src) { - const ext = Url.getFileExtension(src); - - if (ext) { - return `video/${ext}`; - } - return ''; - } - - if (!source.type) { - type = guessMimeType(source.src); - } else { - // Strip code information from the type because we don't get that specific - type = source.type.replace(/;.*/, '').toLowerCase(); - } - - return Flash.nativeSourceHandler.canPlayType(type); -}; - -/** - * Pass the source to the swf. - * - * @param {Tech~SourceObject} source - * The source object - * - * @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) { - tech.setSrc(source.src); -}; - -/** - * noop for native source handler dispose, as cleanup will happen automatically. - */ -Flash.nativeSourceHandler.dispose = function() {}; - -// Register the native source handler -Flash.registerSourceHandler(Flash.nativeSourceHandler); - -/** - * Flash supported mime types. - * - * @constant {Object} - */ -Flash.formats = { - 'video/flv': 'FLV', - 'video/x-flv': 'FLV', - 'video/mp4': 'MP4', - 'video/m4v': 'MP4' -}; - -/** - * Called when the the swf is "ready", and makes sure that the swf is really - * ready using {@link Flash#checkReady} - */ -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 - if (tech && tech.el()) { - // check that the flash object is really ready - Flash.checkReady(tech); - } -}; - -/** - * 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. - * - * @param {Flash} tech - * The instance of the flash tech to check. - */ -Flash.checkReady = function(tech) { - // stop worrying if the tech has been disposed - if (!tech.el()) { - return; - } - - // check if API property exists - if (tech.el().vjs_getProperty) { - // tell tech it's ready - tech.triggerReady(); - } else { - // wait longer - this.setTimeout(function() { - Flash.checkReady(tech); - }, 50); - } -}; - -/** - * Trigger events from the swf on the Flash Tech. - * - * @param {number} swfID - * The id of the swf that had the event - * - * @param {string} eventName - * The name of the event to trigger - */ -Flash.onEvent = function(swfID, eventName) { - const tech = Dom.getEl(swfID).tech; - const args = Array.prototype.slice.call(arguments, 2); - - // dispatch Flash events asynchronously for two reasons: - // - Flash swallows any exceptions generated by javascript it - // invokes - // - Flash is suspended until the javascript returns which may cause - // playback performance issues - tech.setTimeout(function() { - tech.trigger(eventName, args); - }, 1); -}; - -/** - * Log errors from the swf on the Flash tech. - * - * @param {number} swfID - * The id of the swf that had an error. - * - * @param {string} The error string - * The error to set on the Flash Tech. - * - * @return {MediaError|undefined} - * - Returns a MediaError when err is 'srcnotfound' - * - Returns undefined otherwise. - */ -Flash.onError = function(swfID, err) { - const tech = Dom.getEl(swfID).tech; - - // trigger MEDIA_ERR_SRC_NOT_SUPPORTED - if (err === 'srcnotfound') { - return tech.error(4); - } - - // trigger a custom error - tech.error('FLASH: ' + err); -}; - -/** - * Get the current version of Flash that is in use on the page. - * - * @return {Array} - * an array of versions that are available. - */ -Flash.version = function() { - let version = '0,0,0'; - - // IE - try { - version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; - - // other browsers - } catch (e) { - try { - 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) { - // satisfy linter - } - } - return version.split(','); -}; - -/** - * Only use for non-iframe embeds. - * - * @param {Object} swf - * The videojs-swf object. - * - * @param {Object} flashVars - * Names and values to use as flash option variables. - * - * @param {Object} params - * Style parameters to set on the object. - * - * @param {Object} attributes - * Attributes to set on the element. - * - * @return {Element} - * The embeded Flash DOM element. - */ -Flash.embed = function(swf, flashVars, params, attributes) { - const code = Flash.getEmbedCode(swf, flashVars, params, attributes); - - // Get element by embedding code and retrieving created element - const obj = Dom.createEl('div', { innerHTML: code }).childNodes[0]; - - return obj; -}; - -/** - * Only use for non-iframe embeds. - * - * @param {Object} swf - * The videojs-swf object. - * - * @param {Object} flashVars - * Names and values to use as flash option variables. - * - * @param {Object} params - * Style parameters to set on the object. - * - * @param {Object} attributes - * Attributes to set on the element. - * - * @return {Element} - * The embeded Flash DOM element. - */ -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, - - // Default to 100% width/height - width: '100%', - height: '100%' - - }, attributes); - - // Create Attributes string - Object.getOwnPropertyNames(attributes).forEach(function(key) { - attrsString += `${key}="${attributes[key]}" `; - }); - - return `${objTag}${attrsString}>${paramsString}`; -}; - -// Run Flash through the RTMP decorator -FlashRtmpDecorator(Flash); - -Tech.registerTech('Flash', Flash); -export default Flash; diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js index f106d057a6..a70600a5da 100644 --- a/src/js/tech/html5.js +++ b/src/js/tech/html5.js @@ -214,14 +214,14 @@ class Html5 extends Tech { el = document.createElement('video'); // determine if native controls should be used - const tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag); + const tagAttributes = this.options_.tag && Dom.getAttributes(this.options_.tag); const attributes = mergeOptions({}, tagAttributes); if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) { delete attributes.controls; } - Dom.setElAttributes(el, + Dom.setAttributes(el, assign(attributes, { id: this.options_.techId, class: 'vjs-tech' @@ -242,7 +242,7 @@ class Html5 extends Tech { if (typeof this.options_[attr] !== 'undefined') { overwriteAttrs[attr] = this.options_[attr]; } - Dom.setElAttributes(el, overwriteAttrs); + Dom.setAttributes(el, overwriteAttrs); } return el; diff --git a/src/js/utils/dom-data.js b/src/js/utils/dom-data.js new file mode 100644 index 0000000000..753044a1b4 --- /dev/null +++ b/src/js/utils/dom-data.js @@ -0,0 +1,98 @@ +/** + * @file dom-data.js + * @module dom-data + */ +import * as Guid from './guid.js'; + +/** + * Element Data Store. + * + * Allows for binding data to an element without putting it directly on the + * element. Ex. Event listeners are stored here. + * (also from jsninja.com, slightly modified and updated for closure compiler) + * + * @type {Object} + * @private + */ +const elData = {}; + +/* + * Unique attribute name to store an element's guid in + * + * @type {String} + * @constant + * @private + */ +const elIdAttr = 'vdata' + (new Date()).getTime(); + +/** + * Returns the cache object where data for an element is stored + * + * @param {Element} el + * Element to store data for. + * + * @return {Object} + * The cache object for that el that was passed in. + */ +export function getData(el) { + let id = el[elIdAttr]; + + if (!id) { + id = el[elIdAttr] = Guid.newGUID(); + } + + if (!elData[id]) { + elData[id] = {}; + } + + return elData[id]; +} + +/** + * Returns whether or not an element has cached data + * + * @param {Element} el + * Check if this element has cached data. + * + * @return {boolean} + * - True if the DOM element has cached data. + * - False otherwise. + */ +export function hasData(el) { + const id = el[elIdAttr]; + + if (!id) { + return false; + } + + return !!Object.getOwnPropertyNames(elData[id]).length; +} + +/** + * Delete data for the element from the cache and the guid attr from getElementById + * + * @param {Element} el + * Remove cached data for this element. + */ +export function removeData(el) { + const id = el[elIdAttr]; + + if (!id) { + return; + } + + // Remove all stored data + delete elData[id]; + + // Remove the elIdAttr property from the DOM node + try { + delete el[elIdAttr]; + } catch (e) { + if (el.removeAttribute) { + el.removeAttribute(elIdAttr); + } else { + // IE doesn't appear to support removeAttribute on the document element + el[elIdAttr] = null; + } + } +} diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index c6db4f9892..03c0c914d2 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -4,7 +4,6 @@ */ import document from 'global/document'; import window from 'global/window'; -import * as Guid from './guid.js'; import log from './log.js'; import tsml from 'tsml'; import {isObject} from './obj'; @@ -110,24 +109,6 @@ function createQuerier(method) { }; } -/** - * Shorthand for document.getElementById() - * Also allows for CSS (jQuery) ID syntax. But nothing other than IDs. - * - * @param {string} id - * The id of the element to get - * - * @return {Element|null} - * Element with supplied ID or null if there wasn't one. - */ -export function getEl(id) { - if (id.indexOf('#') === 0) { - id = id.slice(1); - } - - return document.getElementById(id); -} - /** * Creates an element and applies properties. * @@ -210,9 +191,8 @@ export function textContent(el, text) { * * @param {Element} parent * Element to insert child into - * */ -export function insertElFirst(child, parent) { +export function prependTo(child, parent) { if (parent.firstChild) { parent.insertBefore(child, parent.firstChild); } else { @@ -220,97 +200,6 @@ export function insertElFirst(child, parent) { } } -/** - * Element Data Store. Allows for binding data to an element without putting it directly on the element. - * Ex. Event listeners are stored here. - * (also from jsninja.com, slightly modified and updated for closure compiler) - * - * @type {Object} - * @private - */ -const elData = {}; - -/* - * Unique attribute name to store an element's guid in - * - * @type {string} - * @constant - * @private - */ -const elIdAttr = 'vdata' + (new Date()).getTime(); - -/** - * Returns the cache object where data for an element is stored - * - * @param {Element} el - * Element to store data for. - * - * @return {Object} - * The cache object for that el that was passed in. - */ -export function getElData(el) { - let id = el[elIdAttr]; - - if (!id) { - id = el[elIdAttr] = Guid.newGUID(); - } - - if (!elData[id]) { - elData[id] = {}; - } - - return elData[id]; -} - -/** - * Returns whether or not an element has cached data - * - * @param {Element} el - * Check if this element has cached data. - * - * @return {boolean} - * - True if the DOM element has cached data. - * - False otherwise. - */ -export function hasElData(el) { - const id = el[elIdAttr]; - - if (!id) { - return false; - } - - return !!Object.getOwnPropertyNames(elData[id]).length; -} - -/** - * Delete data for the element from the cache and the guid attr from getElementById - * - * @param {Element} el - * Remove cached data for this element. - */ -export function removeElData(el) { - const id = el[elIdAttr]; - - if (!id) { - return; - } - - // Remove all stored data - delete elData[id]; - - // Remove the elIdAttr property from the DOM node - try { - delete el[elIdAttr]; - } catch (e) { - if (el.removeAttribute) { - el.removeAttribute(elIdAttr); - } else { - // IE doesn't appear to support removeAttribute on the document element - el[elIdAttr] = null; - } - } -} - /** * Check if an element has a CSS class * @@ -327,7 +216,7 @@ export function removeElData(el) { * @throws {Error} * Throws an error if `classToCheck` has white space. */ -export function hasElClass(element, classToCheck) { +export function hasClass(element, classToCheck) { throwIfWhitespace(classToCheck); if (element.classList) { return element.classList.contains(classToCheck); @@ -347,13 +236,13 @@ export function hasElClass(element, classToCheck) { * @return {Element} * The dom element with the added class name. */ -export function addElClass(element, classToAdd) { +export function addClass(element, classToAdd) { if (element.classList) { element.classList.add(classToAdd); // Don't need to `throwIfWhitespace` here because `hasElClass` will do it // in the case of classList not being supported. - } else if (!hasElClass(element, classToAdd)) { + } else if (!hasClass(element, classToAdd)) { element.className = (element.className + ' ' + classToAdd).trim(); } @@ -372,7 +261,7 @@ export function addElClass(element, classToAdd) { * @return {Element} * The dom element with class name removed. */ -export function removeElClass(element, classToRemove) { +export function removeClass(element, classToRemove) { if (element.classList) { element.classList.remove(classToRemove); } else { @@ -417,12 +306,12 @@ export function removeElClass(element, classToRemove) { * @return {Element} * The element with a class that has been toggled. */ -export function toggleElClass(element, classToToggle, predicate) { +export function toggleClass(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. - const has = hasElClass(element, classToToggle); + const has = hasClass(element, classToToggle); if (typeof predicate === 'function') { predicate = predicate(element, classToToggle); @@ -439,9 +328,9 @@ export function toggleElClass(element, classToToggle, predicate) { } if (predicate) { - addElClass(element, classToToggle); + addClass(element, classToToggle); } else { - removeElClass(element, classToToggle); + removeClass(element, classToToggle); } return element; @@ -456,7 +345,7 @@ export function toggleElClass(element, classToToggle, predicate) { * @param {Object} [attributes] * Attributes to be applied. */ -export function setElAttributes(el, attributes) { +export function setAttributes(el, attributes) { Object.getOwnPropertyNames(attributes).forEach(function(attrName) { const attrValue = attributes[attrName]; @@ -480,7 +369,7 @@ export function setElAttributes(el, attributes) { * @return {Object} * All attributes of the element. */ -export function getElAttributes(tag) { +export function getAttributes(tag) { const obj = {}; // known boolean attributes @@ -642,7 +531,7 @@ export function getBoundingClientRect(el) { * @return {Dom~Position} * The position of the element that was passed in. */ -export function findElPosition(el) { +export function findPosition(el) { let box; if (el.getBoundingClientRect && el.parentNode) { @@ -703,7 +592,7 @@ export function findElPosition(el) { */ export function getPointerPosition(el, event) { const position = {}; - const box = findElPosition(el); + const box = findPosition(el); const boxW = el.offsetWidth; const boxH = el.offsetHeight; diff --git a/src/js/utils/events.js b/src/js/utils/events.js index 97e4594fcc..428ee32f78 100644 --- a/src/js/utils/events.js +++ b/src/js/utils/events.js @@ -7,7 +7,7 @@ * @module events */ -import * as Dom from './dom.js'; +import * as DomData from './dom-data'; import * as Guid from './guid.js'; import log from './log.js'; import window from 'global/window'; @@ -23,7 +23,7 @@ import document from 'global/document'; * Type of event to clean up */ function _cleanUpEvents(elem, type) { - const data = Dom.getElData(elem); + const data = DomData.getData(elem); // Remove the events of a particular type if there are none left if (data.handlers[type].length === 0) { @@ -48,7 +48,7 @@ function _cleanUpEvents(elem, type) { // Finally remove the element data if there is no data left if (Object.getOwnPropertyNames(data).length === 0) { - Dom.removeElData(elem); + DomData.removeData(elem); } } @@ -222,7 +222,7 @@ export function on(elem, type, fn) { return _handleMultipleEvents(on, elem, type, fn); } - const data = Dom.getElData(elem); + const data = DomData.getData(elem); // We need a place to store all our handler data if (!data.handlers) { @@ -295,11 +295,11 @@ 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)) { + if (!DomData.hasData(elem)) { return; } - const data = Dom.getElData(elem); + const data = DomData.getData(elem); // If no events exist, nothing to unbind if (!data.handlers) { @@ -369,7 +369,7 @@ 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. - const elemData = (Dom.hasElData(elem)) ? Dom.getElData(elem) : {}; + const elemData = (DomData.hasData(elem)) ? DomData.getData(elem) : {}; const parent = elem.parentNode || elem.ownerDocument; // type = event.type || event, // handler; @@ -393,7 +393,7 @@ export function trigger(elem, event, hash) { // If at the top of the DOM, triggers the default action unless disabled. } else if (!parent && !event.defaultPrevented) { - const targetData = Dom.getElData(event.target); + const targetData = DomData.getData(event.target); // Checks if the target has a default action for this event. if (event.target[event.type]) { diff --git a/src/js/video.js b/src/js/video.js index 4255c3bd35..9fb3d25d00 100644 --- a/src/js/video.js +++ b/src/js/video.js @@ -63,6 +63,7 @@ function videojs(id, options, ready) { // Allow for element or ID to be passed in // String ID if (typeof id === 'string') { + const players = videojs.getPlayers(); // Adjust for jQuery ID syntax if (id.indexOf('#') === 0) { @@ -70,22 +71,22 @@ function videojs(id, options, ready) { } // If a player instance has already been created for this ID return it. - if (videojs.getPlayers()[id]) { + if (players[id]) { - // If options or ready funtion are passed, warn + // If options or ready function are passed, warn if (options) { log.warn(`Player "${id}" is already initialised. Options will not be applied.`); } if (ready) { - videojs.getPlayers()[id].ready(ready); + players[id].ready(ready); } - return videojs.getPlayers()[id]; + return players[id]; } // Otherwise get element for ID - tag = Dom.getEl(id); + tag = Dom.$('#' + id); // ID is a media element } else { @@ -558,58 +559,58 @@ videojs.VideoTrack = VideoTrack; * Determines, via duck typing, whether or not a value is a DOM element. * * @borrows dom:isEl as videojs.isEl + * @deprecated Use videojs.dom.isEl() instead */ -videojs.isEl = Dom.isEl; /** * Determines, via duck typing, whether or not a value is a text node. * * @borrows dom:isTextNode as videojs.isTextNode + * @deprecated Use videojs.dom.isTextNode() instead */ -videojs.isTextNode = Dom.isTextNode; /** * Creates an element and applies properties. * * @borrows dom:createEl as videojs.createEl + * @deprecated Use videojs.dom.createEl() instead */ -videojs.createEl = Dom.createEl; /** * Check if an element has a CSS class * * @borrows dom:hasElClass as videojs.hasClass + * @deprecated Use videojs.dom.hasClass() instead */ -videojs.hasClass = Dom.hasElClass; /** * Add a CSS class name to an element * * @borrows dom:addElClass as videojs.addClass + * @deprecated Use videojs.dom.addClass() instead */ -videojs.addClass = Dom.addElClass; /** * Remove a CSS class name from an element * * @borrows dom:removeElClass as videojs.removeClass + * @deprecated Use videojs.dom.removeClass() instead */ -videojs.removeClass = Dom.removeElClass; /** * Adds or removes a CSS class name on an element depending on an optional * condition or the presence/absence of the class name. * * @borrows dom:toggleElClass as videojs.toggleClass + * @deprecated Use videojs.dom.toggleClass() instead */ -videojs.toggleClass = Dom.toggleElClass; /** * Apply attributes to an HTML element. * * @borrows dom:setElAttributes as videojs.setAttribute + * @deprecated Use videojs.dom.setAttributes() instead */ -videojs.setAttributes = Dom.setElAttributes; /** * Get an element's attribute values, as defined on the HTML tag @@ -618,15 +619,15 @@ videojs.setAttributes = Dom.setElAttributes; * This will return true or false for boolean attributes. * * @borrows dom:getElAttributes as videojs.getAttributes + * @deprecated Use videojs.dom.getAttributes() instead */ -videojs.getAttributes = Dom.getElAttributes; /** * Empties the contents of an element. * * @borrows dom:emptyEl as videojs.emptyEl + * @deprecated Use videojs.dom.emptyEl() instead */ -videojs.emptyEl = Dom.emptyEl; /** * Normalizes and appends content to an element. @@ -649,8 +650,8 @@ videojs.emptyEl = Dom.emptyEl; * node, or array. * * @borrows dom:appendContents as videojs.appendContet + * @deprecated Use videojs.dom.appendContent() instead */ -videojs.appendContent = Dom.appendContent; /** * Normalizes and inserts content into an element; this is identical to @@ -674,8 +675,27 @@ videojs.appendContent = Dom.appendContent; * node, or array. * * @borrows dom:insertContent as videojs.insertContent - */ -videojs.insertContent = Dom.insertContent; + * @deprecated Use videojs.dom.insertContent() instead + */ +[ + 'isEl', + 'isTextNode', + 'createEl', + 'hasClass', + 'addClass', + 'removeClass', + 'toggleClass', + 'setAttributes', + 'getAttributes', + 'emptyEl', + 'appendContent', + 'insertContent' +].forEach(k => { + videojs[k] = function() { + log.warn(`videojs.${k}() is deprecated; use videojs.dom.${k}() instead`); + return Dom[k].apply(null, arguments); + }; +}); /** * A safe getComputedStyle with an IE8 fallback. @@ -689,6 +709,19 @@ videojs.insertContent = Dom.insertContent; */ videojs.computedStyle = computedStyle; +/** + * Export the Dom utilities for use in external plugins + * and Tech's + */ +videojs.dom = Dom; + +/** + * Export the Url utilities for use in external plugins + * and Tech's + */ +videojs.url = Url; + // We use Node-style module.exports here instead of ES6 because it is more // compatible with different module systems. module.exports = videojs; + diff --git a/test/api/api.js b/test/api/api.js index 62eb735b35..558c9ab061 100644 --- a/test/api/api.js +++ b/test/api/api.js @@ -124,12 +124,9 @@ QUnit.test('should be able to access expected MediaTech API methods', function(a const mediaProto = media.prototype; const html5 = videojs.getComponent('Html5'); const html5Proto = html5.prototype; - const flash = videojs.getComponent('Flash'); - const flashProto = flash.prototype; assert.ok(mediaProto.setPoster, 'setPoster should exist on the Media tech'); assert.ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech'); - assert.ok(flashProto.setPoster, 'setPoster should exist on the Flash tech'); assert.ok(html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5'); assert.ok(html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5'); @@ -143,13 +140,6 @@ QUnit.test('should be able to access expected MediaTech API methods', function(a assert.ok(html5.prototype.setSource, 'setSource should exist for Html5'); assert.ok(html5.prototype.disposeSourceHandler, 'disposeSourceHandler should exist for Html5'); - - assert.ok(flash.canPlaySource, 'canPlaySource should exist for Flash'); - assert.ok(flash.registerSourceHandler, 'registerSourceHandler should exist for Flash'); - assert.ok(flash.selectSourceHandler, 'selectSourceHandler should exist for Flash'); - assert.ok(flash.prototype.setSource, 'setSource should exist for Flash'); - assert.ok(flash.prototype.disposeSourceHandler, - 'disposeSourceHandler should exist for Flash'); }); QUnit.test('should export ready api call to public', function(assert) { diff --git a/test/unit/component.test.js b/test/unit/component.test.js index ae63de1261..00e5e75a50 100644 --- a/test/unit/component.test.js +++ b/test/unit/component.test.js @@ -2,6 +2,7 @@ import window from 'global/window'; import Component from '../../src/js/component.js'; import * as Dom from '../../src/js/utils/dom.js'; +import * as DomData from '../../src/js/utils/dom-data'; import * as Events from '../../src/js/utils/events.js'; import * as browser from '../../src/js/utils/browser.js'; import document from 'global/document'; @@ -294,7 +295,7 @@ QUnit.test('should dispose of component and children', function(assert) { return true; }); const el = comp.el(); - const data = Dom.getElData(el); + const data = DomData.getData(el); let hasDisposed = false; let bubbles = null; @@ -312,7 +313,7 @@ QUnit.test('should dispose of component and children', function(assert) { assert.ok(!comp.el(), 'component element was deleted'); assert.ok(!child.children(), 'child children were deleted'); assert.ok(!child.el(), 'child element was deleted'); - assert.ok(!Dom.hasElData(el), 'listener data nulled'); + assert.ok(!DomData.hasData(el), 'listener data nulled'); assert.ok(!Object.getOwnPropertyNames(data).length, 'original listener data object was emptied'); }); diff --git a/test/unit/player.test.js b/test/unit/player.test.js index 456fab7774..6f900b2dbc 100644 --- a/test/unit/player.test.js +++ b/test/unit/player.test.js @@ -87,7 +87,7 @@ QUnit.test('should accept options from multiple sources and override in correct }); QUnit.test('should get tag, source, and track settings', function(assert) { - // Partially tested in lib->getElAttributes + // Partially tested in lib->getAttributes const fixture = document.getElementById('qunit-fixture'); diff --git a/test/unit/tech/flash-rtmp.test.js b/test/unit/tech/flash-rtmp.test.js deleted file mode 100644 index a908171882..0000000000 --- a/test/unit/tech/flash-rtmp.test.js +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-env qunit */ -import Flash from '../../../src/js/tech/flash.js'; - -QUnit.module('Flash RTMP'); - -const streamToPartsAndBack = function(url) { - const parts = Flash.streamToParts(url); - - return Flash.streamFromParts(parts.connection, parts.stream); -}; - -QUnit.test('test using both streamToParts and streamFromParts', function(assert) { - assert.ok(streamToPartsAndBack('rtmp://myurl.com/isthis') === 'rtmp://myurl.com/&isthis'); - assert.ok(streamToPartsAndBack('rtmp://myurl.com/&isthis') === 'rtmp://myurl.com/&isthis'); - assert.ok(streamToPartsAndBack('rtmp://myurl.com/isthis/andthis') === 'rtmp://myurl.com/isthis/&andthis'); -}); - -QUnit.test('test streamToParts', function(assert) { - let parts = Flash.streamToParts('http://myurl.com/streaming&/is/fun'); - - assert.ok(parts.connection === 'http://myurl.com/streaming'); - assert.ok(parts.stream === '/is/fun'); - - parts = Flash.streamToParts('http://myurl.com/&streaming&/is/fun'); - assert.ok(parts.connection === 'http://myurl.com/'); - assert.ok(parts.stream === 'streaming&/is/fun'); - - parts = Flash.streamToParts('http://myurl.com/really?streaming=fun&really=fun'); - assert.ok(parts.connection === 'http://myurl.com/'); - assert.ok(parts.stream === 'really?streaming=fun&really=fun'); - - parts = Flash.streamToParts('http://myurl.com/streaming/is/fun'); - assert.ok(parts.connection === 'http://myurl.com/streaming/is/'); - assert.ok(parts.stream === 'fun'); - - parts = Flash.streamToParts('whatisgoingonhere'); - assert.ok(parts.connection === 'whatisgoingonhere'); - assert.ok(parts.stream === ''); - - parts = Flash.streamToParts(); - assert.ok(parts.connection === ''); - assert.ok(parts.stream === ''); -}); - -QUnit.test('test isStreamingSrc', function(assert) { - const isStreamingSrc = Flash.isStreamingSrc; - - assert.ok(isStreamingSrc('rtmp://streaming.is/fun')); - assert.ok(isStreamingSrc('rtmps://streaming.is/fun')); - assert.ok(isStreamingSrc('rtmpe://streaming.is/fun')); - assert.ok(isStreamingSrc('rtmpt://streaming.is/fun')); - // test invalid protocols - assert.ok(!isStreamingSrc('rtmp:streaming.is/fun')); - assert.ok(!isStreamingSrc('rtmpz://streaming.is/fun')); - assert.ok(!isStreamingSrc('http://streaming.is/fun')); - assert.ok(!isStreamingSrc('https://streaming.is/fun')); - assert.ok(!isStreamingSrc('file://streaming.is/fun')); -}); diff --git a/test/unit/tech/flash.test.js b/test/unit/tech/flash.test.js deleted file mode 100644 index 96dcd7e2c2..0000000000 --- a/test/unit/tech/flash.test.js +++ /dev/null @@ -1,263 +0,0 @@ -/* 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'; - -// fake out the interaction but leave all the other logic intact -class MockFlash extends Flash { - constructor() { - super({}); - } -} - -QUnit.module('Flash'); - -QUnit.test('Flash.canPlaySource', function(assert) { - const canPlaySource = Flash.canPlaySource; - - // Supported - assert.ok(canPlaySource({type: 'video/mp4; codecs=avc1.42E01E,mp4a.40.2' }, {}), - 'codecs supported'); - assert.ok(canPlaySource({type: 'video/mp4' }, {}), 'video/mp4 supported'); - assert.ok(canPlaySource({type: 'video/x-flv' }, {}), 'video/x-flv supported'); - assert.ok(canPlaySource({type: 'video/flv' }, {}), 'video/flv supported'); - assert.ok(canPlaySource({type: 'video/m4v' }, {}), 'video/m4v supported'); - assert.ok(canPlaySource({type: 'VIDEO/FLV' }, {}), 'capitalized mime type'); - - // Not supported - assert.ok(!canPlaySource({ type: 'video/webm; codecs="vp8, vorbis"' }, {})); - assert.ok(!canPlaySource({ type: 'video/webm' }, {})); -}); - -QUnit.test('currentTime', function(assert) { - const getCurrentTime = Flash.prototype.currentTime; - const setCurrentTime = Flash.prototype.setCurrentTime; - let seekingCount = 0; - let seeking = false; - let setPropVal; - let getPropVal; - let result; - - // Mock out a Flash instance to avoid creating the swf object - const mockFlash = { - el_: { - /* eslint-disable camelcase */ - vjs_setProperty(prop, val) { - setPropVal = val; - }, - vjs_getProperty() { - return getPropVal; - } - /* eslint-enable camelcase */ - }, - seekable() { - return createTimeRange(5, 1000); - }, - trigger(event) { - if (event === 'seeking') { - seekingCount++; - } - }, - seeking() { - return seeking; - } - }; - - // Test the currentTime getter - getPropVal = 3; - result = getCurrentTime.call(mockFlash); - assert.equal(result, 3, 'currentTime is retreived from the swf element'); - - // Test the currentTime setter - setCurrentTime.call(mockFlash, 10); - assert.equal(setPropVal, 10, 'currentTime is set on the swf element'); - assert.equal(seekingCount, 1, 'triggered seeking'); - - // Test current time while seeking - setCurrentTime.call(mockFlash, 20); - seeking = true; - result = getCurrentTime.call(mockFlash); - assert.equal(result, - 20, - 'currentTime is retrieved from the lastSeekTarget while seeking'); - assert.notEqual(result, - getPropVal, - 'currentTime is not retrieved from the element while seeking'); - assert.equal(seekingCount, 2, 'triggered seeking'); - - // clamp seeks to seekable - setCurrentTime.call(mockFlash, 1001); - result = getCurrentTime.call(mockFlash); - assert.equal(result, mockFlash.seekable().end(0), 'clamped to the seekable end'); - assert.equal(seekingCount, 3, 'triggered seeking'); - - setCurrentTime.call(mockFlash, 1); - result = getCurrentTime.call(mockFlash); - assert.equal(result, mockFlash.seekable().start(0), 'clamped to the seekable start'); - assert.equal(seekingCount, 4, 'triggered seeking'); -}); - -QUnit.test('dispose removes the object element even before ready fires', function(assert) { - // This test appears to test bad functionaly that was fixed - // so it's debateable whether or not it's useful - const dispose = Flash.prototype.dispose; - const mockFlash = new MockFlash(); - const noop = function() {}; - - // Mock required functions for dispose - mockFlash.off = noop; - mockFlash.trigger = noop; - mockFlash.el_ = {}; - - dispose.call(mockFlash); - assert.strictEqual(mockFlash.el_, null, 'swf el is nulled'); -}); - -QUnit.test('ready triggering before and after disposing the tech', function(assert) { - 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); - fixtureDiv.appendChild(playerDiv); - - // Mock the swf element - techEl.tech = { - el() { - return techEl; - } - }; - - playerDiv.player = { - tech: techEl.tech - }; - - Flash.onReady(techEl.id); - assert.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); - assert.ok(!checkReady.calledTwice, - 'checkReady should not be called after the tech is disposed'); - - Flash.checkReady.restore(); -}); - -QUnit.test('should have the source handler interface', function(assert) { - assert.ok(Flash.registerSourceHandler, 'has the registerSourceHandler function'); -}); - -QUnit.test('canPlayType should select the correct types to play', function(assert) { - const canPlayType = Flash.nativeSourceHandler.canPlayType; - - assert.equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files'); - assert.equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files'); - assert.equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files'); - assert.equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files'); - assert.equal(canPlayType('video/ogg'), - '', - '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(assert) { - const canHandleSource = Flash.nativeSourceHandler.canHandleSource; - - assert.equal('maybe', - canHandleSource({ src: 'test.video.mp4' }, {}), - 'should guess that it is a mp4 video'); - assert.equal('maybe', - canHandleSource({ src: 'test.video.m4v' }, {}), - 'should guess that it is a m4v video'); - assert.equal('maybe', - canHandleSource({ src: 'test.video.flv' }, {}), - 'should guess that it is a flash video'); - assert.equal('', - canHandleSource({ src: 'test.video.wgg' }, {}), - 'should return empty string if it can not play the video'); -}); - -QUnit.test('seekable', function(assert) { - const seekable = Flash.prototype.seekable; - let result; - const mockFlash = { - duration() { - return this.duration_; - } - }; - - // Test a normal duration - mockFlash.duration_ = 23; - result = seekable.call(mockFlash); - assert.equal(result.length, 1, 'seekable is non-empty'); - assert.equal(result.start(0), 0, 'starts at zero'); - assert.equal(result.end(0), mockFlash.duration_, 'ends at the duration'); - - // Test a zero duration - mockFlash.duration_ = 0; - result = seekable.call(mockFlash); - assert.equal(result.length, mockFlash.duration_, - 'seekable is empty with a zero duration'); -}); - -QUnit.test('play after ended seeks to the beginning', function(assert) { - let plays = 0; - const seeks = []; - - Flash.prototype.play.call({ - el_: { - /* eslint-disable camelcase */ - vjs_play() { - plays++; - } - /* eslint-enable camelcase */ - }, - ended() { - return true; - }, - setCurrentTime(time) { - seeks.push(time); - } - }); - - assert.equal(plays, 1, 'called play on the SWF'); - assert.equal(seeks.length, 1, 'seeked on play'); - assert.equal(seeks[0], 0, 'seeked to the beginning'); -}); - -QUnit.test('duration returns NaN, Infinity or duration according to the HTML standard', function(assert) { - const duration = Flash.prototype.duration; - let mockedDuration = -1; - let mockedReadyState = 0; - let result; - const mockFlash = { - el_: { - /* eslint-disable camelcase */ - vjs_getProperty() { - return mockedDuration; - } - /* eslint-enable camelcase */ - }, - readyState() { - return mockedReadyState; - } - }; - - result = duration.call(mockFlash); - assert.ok(Number.isNaN(result), 'duration returns NaN when readyState equals 0'); - - mockedReadyState = 1; - result = duration.call(mockFlash); - assert.ok(!Number.isFinite(result), - 'duration returns Infinity when duration property is less then 0'); - - mockedDuration = 1; - result = duration.call(mockFlash); - assert.equal(result, - 1, - 'duration returns duration property when readyState' + - ' and duration property are both higher than 0'); -}); diff --git a/test/unit/tech/tech.test.js b/test/unit/tech/tech.test.js index 158163db2f..1ef1b1a11d 100644 --- a/test/unit/tech/tech.test.js +++ b/test/unit/tech/tech.test.js @@ -1,7 +1,6 @@ /* 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'; import Button from '../../../src/js/button.js'; import { createTimeRange } from '../../../src/js/utils/time-ranges.js'; import extendFn from '../../../src/js/extend.js'; @@ -498,7 +497,6 @@ QUnit.test('Tech.isTech returns correct answers for techs and components', funct assert.ok(isTech(Tech), 'Tech is a Tech'); assert.ok(isTech(Html5), 'Html5 is a Tech'); assert.ok(isTech(new Html5({}, {})), 'An html5 instance is a Tech'); - assert.ok(isTech(Flash), 'Flash is a Tech'); assert.ok(!isTech(5), 'A number is not a Tech'); assert.ok(!isTech('this is a tech'), 'A string is not a Tech'); assert.ok(!isTech(Button), 'A Button is not a Tech'); diff --git a/test/unit/test-helpers.js b/test/unit/test-helpers.js index 8a8f8b4311..1f9f73f693 100644 --- a/test/unit/test-helpers.js +++ b/test/unit/test-helpers.js @@ -108,7 +108,7 @@ const TestHelpers = { const msg = `el should have the "${c}" class in its ` + `className, which is "${el.className}"`; - assert.ok(Dom.hasElClass(el, c), msg); + assert.ok(Dom.hasClass(el, c), msg); }); props.forEach(p => { diff --git a/test/unit/utils/dom-data.test.js b/test/unit/utils/dom-data.test.js new file mode 100644 index 0000000000..4077006e98 --- /dev/null +++ b/test/unit/utils/dom-data.test.js @@ -0,0 +1,23 @@ +/* eslint-env qunit */ +import document from 'global/document'; +import * as DomData from '../../../src/js/utils/dom-data'; + +QUnit.module('dom-data'); + +QUnit.test('should get and remove data from an element', function(assert) { + const el = document.createElement('div'); + const data = DomData.getData(el); + + assert.strictEqual(typeof data, 'object', 'data object created'); + + // Add data + const testData = {asdf: 'fdsa'}; + + data.test = testData; + assert.strictEqual(DomData.getData(el).test, testData, 'data added'); + + // Remove all data + DomData.removeData(el); + + assert.notOk(DomData.hasData(el), 'cached item emptied'); +}); diff --git a/test/unit/utils/dom.test.js b/test/unit/utils/dom.test.js index 1e7ff582e3..e1610b1835 100644 --- a/test/unit/utils/dom.test.js +++ b/test/unit/utils/dom.test.js @@ -6,21 +6,6 @@ import * as Dom from '../../../src/js/utils/dom.js'; QUnit.module('dom'); -QUnit.test('should return the element with the ID', function(assert) { - const el1 = document.createElement('div'); - const el2 = document.createElement('div'); - const fixture = document.getElementById('qunit-fixture'); - - fixture.appendChild(el1); - fixture.appendChild(el2); - - el1.id = 'test_id1'; - el2.id = 'test_id2'; - - assert.strictEqual(Dom.getEl('test_id1'), el1, 'found element for ID'); - assert.strictEqual(Dom.getEl('#test_id2'), el2, 'found element for CSS ID'); -}); - QUnit.test('should create an element', function(assert) { const div = Dom.createEl(); const span = Dom.createEl('span', { @@ -57,97 +42,79 @@ QUnit.test('should insert an element first in another', function(assert) { const el2 = document.createElement('div'); const parent = document.createElement('div'); - Dom.insertElFirst(el1, parent); + Dom.prependTo(el1, parent); assert.strictEqual(parent.firstChild, el1, 'inserts first into empty parent'); - Dom.insertElFirst(el2, parent); + Dom.prependTo(el2, parent); assert.strictEqual(parent.firstChild, el2, 'inserts first into parent with child'); }); -QUnit.test('should get and remove data from an element', function(assert) { - const el = document.createElement('div'); - const data = Dom.getElData(el); - - assert.strictEqual(typeof data, 'object', 'data object created'); - - // Add data - const testData = {asdf: 'fdsa'}; - - data.test = testData; - assert.strictEqual(Dom.getElData(el).test, testData, 'data added'); - - // Remove all data - Dom.removeElData(el); - - assert.notOk(Dom.hasElData(el), 'cached item emptied'); -}); - -QUnit.test('addElClass()', function(assert) { +QUnit.test('addClass()', function(assert) { const el = document.createElement('div'); assert.expect(5); - Dom.addElClass(el, 'test-class'); + Dom.addClass(el, 'test-class'); assert.strictEqual(el.className, 'test-class', 'adds a single class'); - Dom.addElClass(el, 'test-class'); + Dom.addClass(el, 'test-class'); assert.strictEqual(el.className, 'test-class', 'does not duplicate classes'); assert.throws(function() { - Dom.addElClass(el, 'foo foo-bar'); + Dom.addClass(el, 'foo foo-bar'); }, 'throws when attempting to add a class with whitespace'); - Dom.addElClass(el, 'test2_className'); + Dom.addClass(el, 'test2_className'); assert.strictEqual(el.className, 'test-class test2_className', 'adds second class'); - Dom.addElClass(el, 'FOO'); + Dom.addClass(el, 'FOO'); assert.strictEqual(el.className, 'test-class test2_className FOO', 'adds third class'); }); -QUnit.test('removeElClass()', function(assert) { +QUnit.test('removeClass()', function(assert) { const el = document.createElement('div'); el.className = 'test-class test2_className FOO bar'; assert.expect(4); - Dom.removeElClass(el, 'test-class'); + Dom.removeClass(el, 'test-class'); assert.strictEqual(el.className, 'test2_className FOO bar', 'removes one class'); assert.throws(function() { - Dom.removeElClass(el, 'test2_className bar'); + Dom.removeClass(el, 'test2_className bar'); }, 'throws when attempting to remove a class with whitespace'); - Dom.removeElClass(el, 'test2_className'); + Dom.removeClass(el, 'test2_className'); assert.strictEqual(el.className, 'FOO bar', 'removes another class'); - Dom.removeElClass(el, 'FOO'); + Dom.removeClass(el, 'FOO'); assert.strictEqual(el.className, 'bar', 'removes another class'); }); -QUnit.test('hasElClass()', function(assert) { +QUnit.test('hasClass()', function(assert) { const el = document.createElement('div'); el.className = 'test-class foo foo test2_className FOO bar'; - assert.strictEqual(Dom.hasElClass(el, 'test-class'), true, 'class detected'); - assert.strictEqual(Dom.hasElClass(el, 'foo'), true, 'class detected'); - assert.strictEqual(Dom.hasElClass(el, 'test2_className'), true, 'class detected'); - assert.strictEqual(Dom.hasElClass(el, 'FOO'), true, 'class detected'); - assert.strictEqual(Dom.hasElClass(el, 'bar'), true, 'class detected'); - assert.strictEqual(Dom.hasElClass(el, 'test2'), + assert.strictEqual(Dom.hasClass(el, 'test-class'), true, 'class detected'); + assert.strictEqual(Dom.hasClass(el, 'foo'), true, 'class detected'); + assert.strictEqual(Dom.hasClass(el, 'test2_className'), true, 'class detected'); + assert.strictEqual(Dom.hasClass(el, 'FOO'), true, 'class detected'); + assert.strictEqual(Dom.hasClass(el, 'bar'), true, 'class detected'); + assert.strictEqual(Dom.hasClass(el, 'test2'), false, 'valid substring - but not a class - correctly not detected'); - assert.strictEqual(Dom.hasElClass(el, 'className'), + assert.strictEqual(Dom.hasClass(el, 'className'), false, 'valid substring - but not a class - correctly not detected'); assert.throws(function() { - Dom.hasElClass(el, 'FOO bar'); + Dom.hasClass(el, 'FOO bar'); }, 'throws when attempting to detect a class with whitespace'); }); -QUnit.test('toggleElClass()', function(assert) { +QUnit.test('toggleClass()', function(assert) { const el = Dom.createEl('div', {className: 'foo bar'}); const predicateToggles = [ @@ -235,18 +202,18 @@ QUnit.test('toggleElClass()', function(assert) { assert.expect(3 + predicateToggles.length); - Dom.toggleElClass(el, 'bar'); + Dom.toggleClass(el, 'bar'); assert.strictEqual(el.className, 'foo', 'toggles a class off, if present'); - Dom.toggleElClass(el, 'bar'); + Dom.toggleClass(el, 'bar'); assert.strictEqual(el.className, 'foo bar', 'toggles a class on, if absent'); assert.throws(function() { - Dom.toggleElClass(el, 'foo bar'); + Dom.toggleClass(el, 'foo bar'); }, 'throws when attempting to toggle a class with whitespace'); predicateToggles.forEach(x => { - Dom.toggleElClass(el, x.toggle, x.predicate); + Dom.toggleClass(el, x.toggle, x.predicate); assert.strictEqual(el.className, x.className, x.message); }); }); @@ -255,7 +222,7 @@ QUnit.test('should set element attributes from object', function(assert) { const el = document.createElement('div'); el.id = 'el1'; - Dom.setElAttributes(el, {'controls': true, 'data-test': 'asdf'}); + Dom.setAttributes(el, {'controls': true, 'data-test': 'asdf'}); assert.equal(el.getAttribute('id'), 'el1'); assert.equal(el.getAttribute('controls'), ''); @@ -277,9 +244,9 @@ QUnit.test('should read tag attributes from elements, including HTML5 in all bro // Also it must be added to the page body, not just in memory. fixture.innerHTML += tags; - const vid1Vals = Dom.getElAttributes(fixture.getElementsByTagName('video')[0]); - const sourceVals = Dom.getElAttributes(fixture.getElementsByTagName('source')[0]); - const trackVals = Dom.getElAttributes(fixture.getElementsByTagName('track')[0]); + const vid1Vals = Dom.getAttributes(fixture.getElementsByTagName('video')[0]); + const sourceVals = Dom.getAttributes(fixture.getElementsByTagName('source')[0]); + const trackVals = Dom.getAttributes(fixture.getElementsByTagName('track')[0]); // vid1 // was using deepEqual, but ie8 would send all properties as attributes @@ -311,9 +278,9 @@ QUnit.test('should read tag attributes from elements, including HTML5 in all bro assert.equal(trackVals.title, 'test'); }); -QUnit.test('Dom.findElPosition should find top and left position', function(assert) { +QUnit.test('Dom.findPosition should find top and left position', function(assert) { const d = document.createElement('div'); - let position = Dom.findElPosition(d); + let position = Dom.findPosition(d); d.style.top = '10px'; d.style.left = '20px'; @@ -324,11 +291,11 @@ QUnit.test('Dom.findElPosition should find top and left position', function(asse 'If element isn\'t in the DOM, we should get zeros'); document.body.appendChild(d); - position = Dom.findElPosition(d); + position = Dom.findPosition(d); assert.deepEqual(position, {left: 20, top: 10}, 'The position was not correct'); d.getBoundingClientRect = null; - position = Dom.findElPosition(d); + position = Dom.findPosition(d); assert.deepEqual(position, {left: 0, top: 0}, 'If there is no gBCR, we should get zeros'); diff --git a/test/unit/video.test.js b/test/unit/video.test.js index db8b74d039..3f04622f01 100644 --- a/test/unit/video.test.js +++ b/test/unit/video.test.js @@ -182,33 +182,49 @@ QUnit.test('should expose options and players properties for backward-compatibil }); QUnit.test('should expose DOM functions', function(assert) { + const origWarnLog = log.warn; + const warnLogs = []; - // Keys are videojs methods, values are Dom methods. - const methods = { - isEl: 'isEl', - isTextNode: 'isTextNode', - createEl: 'createEl', - hasClass: 'hasElClass', - addClass: 'addElClass', - removeClass: 'removeElClass', - toggleClass: 'toggleElClass', - setAttributes: 'setElAttributes', - getAttributes: 'getElAttributes', - emptyEl: 'emptyEl', - insertContent: 'insertContent', - appendContent: 'appendContent' + log.warn = (args) => { + warnLogs.push(args); }; - const keys = Object.keys(methods); - - assert.expect(keys.length); - keys.forEach(function(vjsName) { - const domName = methods[vjsName]; - - assert.strictEqual(videojs[vjsName], - Dom[domName], - `videojs.${vjsName} is a reference to Dom.${domName}`); + const methods = [ + 'isEl', + 'isTextNode', + 'createEl', + 'hasClass', + 'addClass', + 'removeClass', + 'toggleClass', + 'setAttributes', + 'getAttributes', + 'emptyEl', + 'insertContent', + 'appendContent' + ]; + + methods.forEach(name => { + assert.strictEqual(typeof videojs[name], 'function', `function videojs.${name}`); + assert.strictEqual(typeof Dom[name], 'function', `Dom.${name} function exists`); + + const oldMethod = Dom[name]; + let domCalls = 0; + + Dom[name] = () => domCalls++; + + videojs[name](); + + assert.equal(domCalls, 1, `Dom.${name} was called when videojs.${name} is run.`); + assert.equal(warnLogs.length, 1, `videojs.${name} logs a deprecation warning`); + + // reset + warnLogs.length = 0; + Dom[name] = oldMethod; }); + + // reset log + log.warn = origWarnLog; }); QUnit.test('ingest player div if data-vjs-player attribute is present on video parentNode', function(assert) {