mirror of
https://github.com/videojs/video.js.git
synced 2025-01-10 23:30:03 +02:00
@misteroneill exposed DOM helpers. closes #2754
This commit is contained in:
parent
cd800b02ea
commit
f2fa8f8687
@ -10,6 +10,7 @@ CHANGELOG
|
||||
* @paladox updated grunt-cli dependency ([view](https://github.com/videojs/video.js/pull/2555))
|
||||
* @paladox updated grunt-contrib-jshint ([view](https://github.com/videojs/video.js/pull/2554))
|
||||
* @siebrand updated dutch translations ([view](https://github.com/videojs/video.js/pull/2556))
|
||||
* @misteroneill exposed DOM helpers ([view](https://github.com/videojs/video.js/pull/2754))
|
||||
|
||||
--------------------
|
||||
|
||||
|
@ -830,6 +830,46 @@ class Component {
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single DOM element matching `selector` within the component's
|
||||
* `contentEl` or another custom context.
|
||||
*
|
||||
* @method $
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelector`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {Element|null}
|
||||
*/
|
||||
$(selector, context) {
|
||||
return Dom.$(selector, context || this.contentEl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a all DOM elements matching `selector` within the component's
|
||||
* `contentEl` or another custom context.
|
||||
*
|
||||
* @method $$
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelectorAll`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {NodeList}
|
||||
*/
|
||||
$$(selector, context) {
|
||||
return Dom.$$(selector, context || this.contentEl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a component's element has a CSS class name
|
||||
*
|
||||
@ -854,7 +894,7 @@ class Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return a CSS class name from the component's element
|
||||
* Remove a CSS class name from the component's element
|
||||
*
|
||||
* @param {String} classToRemove Classname to remove
|
||||
* @return {Component}
|
||||
@ -865,6 +905,23 @@ class Component {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove a CSS class name from the component's element
|
||||
*
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*
|
||||
* @return {Component}
|
||||
* @method toggleClass
|
||||
*/
|
||||
toggleClass(classToToggle, predicate) {
|
||||
Dom.toggleElClass(this.el_, classToToggle, predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the component element if hidden
|
||||
*
|
||||
|
@ -281,8 +281,8 @@ class Player extends Component {
|
||||
// of the player in a way that's still overrideable by CSS, just like the
|
||||
// video element
|
||||
this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
|
||||
let defaultsStyleEl = document.querySelector('.vjs-styles-defaults');
|
||||
let head = document.querySelector('head');
|
||||
let defaultsStyleEl = Dom.$('.vjs-styles-defaults');
|
||||
let head = Dom.$('head');
|
||||
head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
|
||||
|
||||
// Pass in the width/height/aspectRatio options which will update the style el
|
||||
|
@ -771,7 +771,7 @@ class Html5 extends Tech {
|
||||
|
||||
this.remoteTextTracks().removeTrack_(track);
|
||||
|
||||
tracks = this.el().querySelectorAll('track');
|
||||
tracks = this.$$('track');
|
||||
|
||||
i = tracks.length;
|
||||
while (i--) {
|
||||
|
@ -44,6 +44,8 @@ let trackToJson_ = function(track) {
|
||||
* @function textTracksToJson
|
||||
*/
|
||||
let textTracksToJson = function(tech) {
|
||||
|
||||
// Cannot use $$ here because it is not an instance of Tech
|
||||
let trackEls = tech.el().querySelectorAll('track');
|
||||
|
||||
let trackObjs = Array.prototype.map.call(trackEls, (t) => t.track);
|
||||
|
@ -27,33 +27,33 @@ class TextTrackSettings extends Component {
|
||||
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
|
||||
}
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-done-button'), 'click', Fn.bind(this, function() {
|
||||
Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function() {
|
||||
this.saveSettings();
|
||||
this.hide();
|
||||
}));
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-default-button'), 'click', Fn.bind(this, function() {
|
||||
this.el().querySelector('.vjs-fg-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-bg-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.window-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-text-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-bg-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-window-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-edge-style select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-font-family select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-font-percent select').selectedIndex = 2;
|
||||
Events.on(this.$('.vjs-default-button'), 'click', Fn.bind(this, function() {
|
||||
this.$('.vjs-fg-color > select').selectedIndex = 0;
|
||||
this.$('.vjs-bg-color > select').selectedIndex = 0;
|
||||
this.$('.window-color > select').selectedIndex = 0;
|
||||
this.$('.vjs-text-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-bg-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-window-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-edge-style select').selectedIndex = 0;
|
||||
this.$('.vjs-font-family select').selectedIndex = 0;
|
||||
this.$('.vjs-font-percent select').selectedIndex = 2;
|
||||
this.updateDisplay();
|
||||
}));
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
|
||||
if (this.options_.persistTextTrackSettings) {
|
||||
this.restoreSettings();
|
||||
@ -89,17 +89,15 @@ class TextTrackSettings extends Component {
|
||||
* @method getValues
|
||||
*/
|
||||
getValues() {
|
||||
const el = this.el();
|
||||
|
||||
const textEdge = getSelectedOptionValue(el.querySelector('.vjs-edge-style select'));
|
||||
const fontFamily = getSelectedOptionValue(el.querySelector('.vjs-font-family select'));
|
||||
const fgColor = getSelectedOptionValue(el.querySelector('.vjs-fg-color > select'));
|
||||
const textOpacity = getSelectedOptionValue(el.querySelector('.vjs-text-opacity > select'));
|
||||
const bgColor = getSelectedOptionValue(el.querySelector('.vjs-bg-color > select'));
|
||||
const bgOpacity = getSelectedOptionValue(el.querySelector('.vjs-bg-opacity > select'));
|
||||
const windowColor = getSelectedOptionValue(el.querySelector('.window-color > select'));
|
||||
const windowOpacity = getSelectedOptionValue(el.querySelector('.vjs-window-opacity > select'));
|
||||
const fontPercent = window['parseFloat'](getSelectedOptionValue(el.querySelector('.vjs-font-percent > select')));
|
||||
const textEdge = getSelectedOptionValue(this.$('.vjs-edge-style select'));
|
||||
const fontFamily = getSelectedOptionValue(this.$('.vjs-font-family select'));
|
||||
const fgColor = getSelectedOptionValue(this.$('.vjs-fg-color > select'));
|
||||
const textOpacity = getSelectedOptionValue(this.$('.vjs-text-opacity > select'));
|
||||
const bgColor = getSelectedOptionValue(this.$('.vjs-bg-color > select'));
|
||||
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,
|
||||
@ -136,16 +134,14 @@ class TextTrackSettings extends Component {
|
||||
* @method setValues
|
||||
*/
|
||||
setValues(values) {
|
||||
const el = this.el();
|
||||
|
||||
setSelectedOption(el.querySelector('.vjs-edge-style select'), values.edgeStyle);
|
||||
setSelectedOption(el.querySelector('.vjs-font-family select'), values.fontFamily);
|
||||
setSelectedOption(el.querySelector('.vjs-fg-color > select'), values.color);
|
||||
setSelectedOption(el.querySelector('.vjs-text-opacity > select'), values.textOpacity);
|
||||
setSelectedOption(el.querySelector('.vjs-bg-color > select'), values.backgroundColor);
|
||||
setSelectedOption(el.querySelector('.vjs-bg-opacity > select'), values.backgroundOpacity);
|
||||
setSelectedOption(el.querySelector('.window-color > select'), values.windowColor);
|
||||
setSelectedOption(el.querySelector('.vjs-window-opacity > select'), values.windowOpacity);
|
||||
setSelectedOption(this.$('.vjs-edge-style select'), values.edgeStyle);
|
||||
setSelectedOption(this.$('.vjs-font-family select'), values.fontFamily);
|
||||
setSelectedOption(this.$('.vjs-fg-color > select'), values.color);
|
||||
setSelectedOption(this.$('.vjs-text-opacity > select'), values.textOpacity);
|
||||
setSelectedOption(this.$('.vjs-bg-color > select'), values.backgroundColor);
|
||||
setSelectedOption(this.$('.vjs-bg-opacity > select'), values.backgroundOpacity);
|
||||
setSelectedOption(this.$('.window-color > select'), values.windowColor);
|
||||
setSelectedOption(this.$('.vjs-window-opacity > select'), values.windowOpacity);
|
||||
|
||||
let fontPercent = values.fontPercent;
|
||||
|
||||
@ -153,7 +149,7 @@ class TextTrackSettings extends Component {
|
||||
fontPercent = fontPercent.toFixed(2);
|
||||
}
|
||||
|
||||
setSelectedOption(el.querySelector('.vjs-font-percent > select'), fontPercent);
|
||||
setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,59 @@ import * as Guid from './guid.js';
|
||||
import log from './log.js';
|
||||
import tsml from 'tsml';
|
||||
|
||||
/**
|
||||
* Detect if a value is a string with any non-whitespace characters.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNonBlankString(str) {
|
||||
return typeof str === 'string' && /\S/.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error if the passed string has whitespace. This is used by
|
||||
* class methods to be relatively consistent with the classList API.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function throwIfWhitespace(str) {
|
||||
if (/\s/.test(str)) {
|
||||
throw new Error('class has illegal whitespace characters');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a regular expression for matching a class name.
|
||||
*
|
||||
* @param {String} className
|
||||
* @return {RegExp}
|
||||
*/
|
||||
function classRegExp(className) {
|
||||
return new RegExp('(^|\\s)' + className + '($|\\s)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates functions to query the DOM using a given method.
|
||||
*
|
||||
* @function createQuerier
|
||||
* @private
|
||||
* @param {String} method
|
||||
* @return {Function}
|
||||
*/
|
||||
function createQuerier(method) {
|
||||
return function (selector, context) {
|
||||
if (!isNonBlankString(selector)) {
|
||||
return document[method](null);
|
||||
}
|
||||
if (isNonBlankString(context)) {
|
||||
context = document.querySelector(context);
|
||||
}
|
||||
return (isEl(context) ? context : document)[method](selector);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for document.getElementById()
|
||||
* Also allows for CSS (jQuery) ID syntax. But nothing other than IDs.
|
||||
@ -181,47 +234,99 @@ export function removeElData(el) {
|
||||
/**
|
||||
* Check if an element has a CSS class
|
||||
*
|
||||
* @function hasElClass
|
||||
* @param {Element} element Element to check
|
||||
* @param {String} classToCheck Classname to check
|
||||
* @function hasElClass
|
||||
*/
|
||||
export function hasElClass(element, classToCheck) {
|
||||
return ((' ' + element.className + ' ').indexOf(' ' + classToCheck + ' ') !== -1);
|
||||
if (element.classList) {
|
||||
return element.classList.contains(classToCheck);
|
||||
} else {
|
||||
throwIfWhitespace(classToCheck);
|
||||
return classRegExp(classToCheck).test(element.className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS class name to an element
|
||||
*
|
||||
* @function addElClass
|
||||
* @param {Element} element Element to add class name to
|
||||
* @param {String} classToAdd Classname to add
|
||||
* @function addElClass
|
||||
*/
|
||||
export function addElClass(element, classToAdd) {
|
||||
if (!hasElClass(element, classToAdd)) {
|
||||
element.className = element.className === '' ? classToAdd : element.className + ' ' + 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)) {
|
||||
element.className = (element.className + ' ' + classToAdd).trim();
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a CSS class name from an element
|
||||
*
|
||||
* @function removeElClass
|
||||
* @param {Element} element Element to remove from class name
|
||||
* @param {String} classToRemove Classname to remove
|
||||
* @function removeElClass
|
||||
*/
|
||||
export function removeElClass(element, classToRemove) {
|
||||
if (!hasElClass(element, classToRemove)) {return;}
|
||||
|
||||
let classNames = element.className.split(' ');
|
||||
|
||||
// no arr.indexOf in ie8, and we don't want to add a big shim
|
||||
for (let i = classNames.length - 1; i >= 0; i--) {
|
||||
if (classNames[i] === classToRemove) {
|
||||
classNames.splice(i,1);
|
||||
}
|
||||
if (element.classList) {
|
||||
element.classList.remove(classToRemove);
|
||||
} else {
|
||||
throwIfWhitespace(classToRemove);
|
||||
element.className = element.className.split(/\s+/).filter(function(c) {
|
||||
return c !== classToRemove;
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
element.className = classNames.join(' ');
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a CSS class name on an element depending on an optional
|
||||
* condition or the presence/absence of the class name.
|
||||
*
|
||||
* @function toggleElClass
|
||||
* @param {Element} element
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*/
|
||||
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);
|
||||
|
||||
if (typeof predicate === 'function') {
|
||||
predicate = predicate(element, classToToggle);
|
||||
}
|
||||
|
||||
if (typeof predicate !== 'boolean') {
|
||||
predicate = !has;
|
||||
}
|
||||
|
||||
// If the necessary class operation matches the current state of the
|
||||
// element, no action is required.
|
||||
if (predicate === has) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
addElClass(element, classToToggle);
|
||||
} else {
|
||||
removeElClass(element, classToToggle);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -292,7 +397,7 @@ export function getElAttributes(tag) {
|
||||
* Attempt to block the ability to select text while dragging controls
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method blockTextSelection
|
||||
* @function blockTextSelection
|
||||
*/
|
||||
export function blockTextSelection() {
|
||||
document.body.focus();
|
||||
@ -305,7 +410,7 @@ export function blockTextSelection() {
|
||||
* Turn off text selection blocking
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method unblockTextSelection
|
||||
* @function unblockTextSelection
|
||||
*/
|
||||
export function unblockTextSelection() {
|
||||
document.onselectstart = function() {
|
||||
@ -318,9 +423,9 @@ export function unblockTextSelection() {
|
||||
* getBoundingClientRect technique from
|
||||
* John Resig http://ejohn.org/blog/getboundingclientrect-is-awesome/
|
||||
*
|
||||
* @function findElPosition
|
||||
* @param {Element} el Element from which to get offset
|
||||
* @return {Object=}
|
||||
* @method findElPosition
|
||||
* @return {Object}
|
||||
*/
|
||||
export function findElPosition(el) {
|
||||
let box;
|
||||
@ -359,10 +464,10 @@ export function findElPosition(el) {
|
||||
* Returns an object with x and y coordinates.
|
||||
* The base on the coordinates are the bottom left of the element.
|
||||
*
|
||||
* @function getPointerPosition
|
||||
* @param {Element} el Element on which to get the pointer position on
|
||||
* @param {Event} event Event object
|
||||
* @return {Object=} position This object will have x and y coordinates corresponding to the mouse position
|
||||
* @metho getPointerPosition
|
||||
* @return {Object} This object will have x and y coordinates corresponding to the mouse position
|
||||
*/
|
||||
export function getPointerPosition(el, event) {
|
||||
let position = {};
|
||||
@ -389,8 +494,9 @@ export function getPointerPosition(el, event) {
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a DOM element.
|
||||
*
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
* @function isEl
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export function isEl(value) {
|
||||
return !!value && typeof value === 'object' && value.nodeType === 1;
|
||||
@ -427,18 +533,25 @@ export function emptyEl(el) {
|
||||
* from falling into the trap of simply writing to `innerHTML`, which is
|
||||
* an XSS concern.
|
||||
*
|
||||
* The content for an element can be passed in multiple types, whose
|
||||
* behavior is as follows:
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String: Normalized into a text node.
|
||||
* - Node: An Element or TextNode is passed through.
|
||||
* - Array: A one-dimensional array of strings, nodes, or functions (which
|
||||
* return single strings or nodes).
|
||||
* - Function: If the sole argument, is expected to produce a string, node,
|
||||
* or array.
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @function normalizeContent
|
||||
* @param {String|Element|Array|Function} content
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Array}
|
||||
*/
|
||||
export function normalizeContent(content) {
|
||||
@ -474,7 +587,8 @@ export function normalizeContent(content) {
|
||||
*
|
||||
* @function appendContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|Array|Function} content
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* See: `normalizeContent`
|
||||
* @return {Element}
|
||||
*/
|
||||
export function appendContent(el, content) {
|
||||
@ -488,9 +602,46 @@ export function appendContent(el, content) {
|
||||
*
|
||||
* @function insertContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|Array|Function} content
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* See: `normalizeContent`
|
||||
* @return {Element}
|
||||
*/
|
||||
export function insertContent(el, content) {
|
||||
return appendContent(emptyEl(el), content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single DOM element matching `selector` within the optional
|
||||
* `context` of another DOM element (defaulting to `document`).
|
||||
*
|
||||
* @function $
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelector`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {Element|null}
|
||||
*/
|
||||
export const $ = createQuerier('querySelector');
|
||||
|
||||
/**
|
||||
* Finds a all DOM elements matching `selector` within the optional
|
||||
* `context` of another DOM element (defaulting to `document`).
|
||||
*
|
||||
* @function $$
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelectorAll`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {NodeList}
|
||||
*/
|
||||
export const $$ = createQuerier('querySelectorAll');
|
||||
|
163
src/js/video.js
163
src/js/video.js
@ -99,10 +99,10 @@ var videojs = function(id, options, ready){
|
||||
};
|
||||
|
||||
// Add default styles
|
||||
let style = document.querySelector('.vjs-styles-defaults');
|
||||
let style = Dom.$('.vjs-styles-defaults');
|
||||
if (!style) {
|
||||
style = stylesheet.createStyleElement('vjs-styles-defaults');
|
||||
let head = document.querySelector('head');
|
||||
let head = Dom.$('head');
|
||||
head.insertBefore(style, head.firstChild);
|
||||
stylesheet.setTextContent(style, `
|
||||
.video-js {
|
||||
@ -545,22 +545,149 @@ videojs.xhr = xhr;
|
||||
*/
|
||||
videojs.TextTrack = TextTrack;
|
||||
|
||||
// REMOVING: We probably should add this to the migration plugin
|
||||
// // Expose but deprecate the window[componentName] method for accessing components
|
||||
// Object.getOwnPropertyNames(Component.components).forEach(function(name){
|
||||
// let component = Component.components[name];
|
||||
//
|
||||
// // A deprecation warning as the constuctor
|
||||
// module.exports[name] = function(player, options, ready){
|
||||
// log.warn('Using videojs.'+name+' to access the '+name+' component has been deprecated. Please use videojs.getComponent("componentName")');
|
||||
//
|
||||
// return new Component(player, options, ready);
|
||||
// };
|
||||
//
|
||||
// // Allow the prototype and class methods to be accessible still this way
|
||||
// // Though anything that attempts to override class methods will no longer work
|
||||
// assign(module.exports[name], component);
|
||||
// });
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a DOM element.
|
||||
*
|
||||
* @method isEl
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
videojs.isEl = Dom.isEl;
|
||||
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a text node.
|
||||
*
|
||||
* @method isTextNode
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
videojs.isTextNode = Dom.isTextNode;
|
||||
|
||||
/**
|
||||
* Check if an element has a CSS class
|
||||
*
|
||||
* @method hasClass
|
||||
* @param {Element} element Element to check
|
||||
* @param {String} classToCheck Classname to check
|
||||
*/
|
||||
videojs.hasClass = Dom.hasElClass;
|
||||
|
||||
/**
|
||||
* Add a CSS class name to an element
|
||||
*
|
||||
* @method addClass
|
||||
* @param {Element} element Element to add class name to
|
||||
* @param {String} classToAdd Classname to add
|
||||
*/
|
||||
videojs.addClass = Dom.addElClass;
|
||||
|
||||
/**
|
||||
* Remove a CSS class name from an element
|
||||
*
|
||||
* @method removeClass
|
||||
* @param {Element} element Element to remove from class name
|
||||
* @param {String} classToRemove Classname to remove
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @method toggleElClass
|
||||
* @param {Element} element
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*/
|
||||
videojs.toggleClass = Dom.toggleElClass;
|
||||
|
||||
/**
|
||||
* Apply attributes to an HTML element.
|
||||
*
|
||||
* @method setAttributes
|
||||
* @param {Element} el Target element.
|
||||
* @param {Object=} attributes Element attributes to be applied.
|
||||
*/
|
||||
videojs.setAttributes = Dom.setElAttributes;
|
||||
|
||||
/**
|
||||
* Get an element's attribute values, as defined on the HTML tag
|
||||
* Attributes are not the same as properties. They're defined on the tag
|
||||
* or with setAttribute (which shouldn't be used with HTML)
|
||||
* This will return true or false for boolean attributes.
|
||||
*
|
||||
* @method getAttributes
|
||||
* @param {Element} tag Element from which to get tag attributes
|
||||
* @return {Object}
|
||||
*/
|
||||
videojs.getAttributes = Dom.getElAttributes;
|
||||
|
||||
/**
|
||||
* Empties the contents of an element.
|
||||
*
|
||||
* @method emptyEl
|
||||
* @param {Element} el
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.emptyEl = Dom.emptyEl;
|
||||
|
||||
/**
|
||||
* Normalizes and appends content to an element.
|
||||
*
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @method appendContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.appendContent = Dom.appendContent;
|
||||
|
||||
/**
|
||||
* Normalizes and inserts content into an element; this is identical to
|
||||
* `appendContent()`, except it empties the element first.
|
||||
*
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @method insertContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.insertContent = Dom.insertContent;
|
||||
|
||||
/*
|
||||
* Custom Universal Module Definition (UMD)
|
||||
|
@ -471,6 +471,10 @@ test('should add and remove a CSS class', function(){
|
||||
ok(comp.el().className.indexOf('test-class') !== -1);
|
||||
comp.removeClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') === -1);
|
||||
comp.toggleClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') !== -1);
|
||||
comp.toggleClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') === -1);
|
||||
});
|
||||
|
||||
test('should show and hide an element', function(){
|
||||
@ -695,3 +699,18 @@ test('should provide interval methods that automatically get cleared on componen
|
||||
|
||||
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 = [
|
||||
document.createElement('div'),
|
||||
document.createElement('div')
|
||||
];
|
||||
|
||||
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');
|
||||
});
|
||||
|
@ -35,17 +35,17 @@ test('should update settings', function() {
|
||||
player.textTrackSettings.setValues(newSettings);
|
||||
deepEqual(player.textTrackSettings.getValues(), newSettings, 'values are updated');
|
||||
|
||||
equal(player.el().querySelector('.vjs-fg-color > select').selectedIndex, 1, 'fg-color is set to new value');
|
||||
equal(player.el().querySelector('.vjs-bg-color > select').selectedIndex, 1, 'bg-color is set to new value');
|
||||
equal(player.el().querySelector('.window-color > select').selectedIndex, 1, 'window-color is set to new value');
|
||||
equal(player.el().querySelector('.vjs-text-opacity > select').selectedIndex, 1, 'text-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-bg-opacity > select').selectedIndex, 1, 'bg-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-window-opacity > select').selectedIndex, 1, 'window-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-edge-style select').selectedIndex, 1, 'edge-style is set to new value');
|
||||
equal(player.el().querySelector('.vjs-font-family select').selectedIndex, 1, 'font-family is set to new value');
|
||||
equal(player.el().querySelector('.vjs-font-percent select').selectedIndex, 3, 'font-percent is set to new value');
|
||||
equal(player.$('.vjs-fg-color > select').selectedIndex, 1, 'fg-color is set to new value');
|
||||
equal(player.$('.vjs-bg-color > select').selectedIndex, 1, 'bg-color is set to new value');
|
||||
equal(player.$('.window-color > select').selectedIndex, 1, 'window-color is set to new value');
|
||||
equal(player.$('.vjs-text-opacity > select').selectedIndex, 1, 'text-opacity is set to new value');
|
||||
equal(player.$('.vjs-bg-opacity > select').selectedIndex, 1, 'bg-opacity is set to new value');
|
||||
equal(player.$('.vjs-window-opacity > select').selectedIndex, 1, 'window-opacity is set to new value');
|
||||
equal(player.$('.vjs-edge-style select').selectedIndex, 1, 'edge-style is set to new value');
|
||||
equal(player.$('.vjs-font-family select').selectedIndex, 1, 'font-family is set to new value');
|
||||
equal(player.$('.vjs-font-percent select').selectedIndex, 3, 'font-percent is set to new value');
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
deepEqual(safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'))[1], newSettings, 'values are saved');
|
||||
|
||||
player.dispose();
|
||||
@ -57,32 +57,32 @@ test('should restore default settings', function() {
|
||||
persistTextTrackSettings: true
|
||||
});
|
||||
|
||||
player.el().querySelector('.vjs-fg-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-bg-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.window-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-text-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-bg-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-window-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-edge-style select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-font-family select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-font-percent select').selectedIndex = 3;
|
||||
player.$('.vjs-fg-color > select').selectedIndex = 1;
|
||||
player.$('.vjs-bg-color > select').selectedIndex = 1;
|
||||
player.$('.window-color > select').selectedIndex = 1;
|
||||
player.$('.vjs-text-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-bg-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-window-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-edge-style select').selectedIndex = 1;
|
||||
player.$('.vjs-font-family select').selectedIndex = 1;
|
||||
player.$('.vjs-font-percent select').selectedIndex = 3;
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-default-button'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-default-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
deepEqual(player.textTrackSettings.getValues(), {}, 'values are defaulted');
|
||||
deepEqual(window.localStorage.getItem('vjs-text-track-settings'), null, 'values are saved');
|
||||
|
||||
equal(player.el().querySelector('.vjs-fg-color > select').selectedIndex, 0, 'fg-color is set to default value');
|
||||
equal(player.el().querySelector('.vjs-bg-color > select').selectedIndex, 0, 'bg-color is set to default value');
|
||||
equal(player.el().querySelector('.window-color > select').selectedIndex, 0, 'window-color is set to default value');
|
||||
equal(player.el().querySelector('.vjs-text-opacity > select').selectedIndex, 0, 'text-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-bg-opacity > select').selectedIndex, 0, 'bg-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-window-opacity > select').selectedIndex, 0, 'window-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-edge-style select').selectedIndex, 0, 'edge-style is set to default value');
|
||||
equal(player.el().querySelector('.vjs-font-family select').selectedIndex, 0, 'font-family is set to default value');
|
||||
equal(player.el().querySelector('.vjs-font-percent select').selectedIndex, 2, 'font-percent is set to default value');
|
||||
equal(player.$('.vjs-fg-color > select').selectedIndex, 0, 'fg-color is set to default value');
|
||||
equal(player.$('.vjs-bg-color > select').selectedIndex, 0, 'bg-color is set to default value');
|
||||
equal(player.$('.window-color > select').selectedIndex, 0, 'window-color is set to default value');
|
||||
equal(player.$('.vjs-text-opacity > select').selectedIndex, 0, 'text-opacity is set to default value');
|
||||
equal(player.$('.vjs-bg-opacity > select').selectedIndex, 0, 'bg-opacity is set to default value');
|
||||
equal(player.$('.vjs-window-opacity > select').selectedIndex, 0, 'window-opacity is set to default value');
|
||||
equal(player.$('.vjs-edge-style select').selectedIndex, 0, 'edge-style is set to default value');
|
||||
equal(player.$('.vjs-font-family select').selectedIndex, 0, 'font-family is set to default value');
|
||||
equal(player.$('.vjs-font-percent select').selectedIndex, 2, 'font-percent is set to default value');
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
@ -91,7 +91,7 @@ test('should open on click', function() {
|
||||
var player = TestHelpers.makePlayer({
|
||||
tracks: tracks
|
||||
});
|
||||
Events.trigger(player.el().querySelector('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.$('.vjs-texttrack-settings'), 'click');
|
||||
ok(!player.textTrackSettings.hasClass('vjs-hidden'), 'settings open');
|
||||
|
||||
player.dispose();
|
||||
@ -101,8 +101,8 @@ test('should close on done click', function() {
|
||||
var player = TestHelpers.makePlayer({
|
||||
tracks: tracks
|
||||
});
|
||||
Events.trigger(player.el().querySelector('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
ok(player.textTrackSettings.hasClass('vjs-hidden'), 'settings closed');
|
||||
|
||||
player.dispose();
|
||||
@ -141,7 +141,7 @@ test('if persist option is set, save settings when "done"', function() {
|
||||
save++;
|
||||
};
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
equal(save, 1, 'save was called');
|
||||
|
||||
@ -171,7 +171,7 @@ test('do not try to restore or save settings if persist option is not set', func
|
||||
|
||||
equal(restore, 0, 'restore was not called');
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
// saveSettings is called but does nothing
|
||||
equal(save, 1, 'save was not called');
|
||||
|
@ -62,23 +62,164 @@ test('should get and remove data from an element', function(){
|
||||
ok(!Dom.hasElData(el), 'cached item emptied');
|
||||
});
|
||||
|
||||
test('should add and remove a class name on an element', function(){
|
||||
test('addElClass()', function(){
|
||||
var el = document.createElement('div');
|
||||
|
||||
expect(5);
|
||||
|
||||
Dom.addElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class', 'class added');
|
||||
strictEqual(el.className, 'test-class', 'adds a single class');
|
||||
|
||||
Dom.addElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class', 'same class not duplicated');
|
||||
Dom.addElClass(el, 'test-class2');
|
||||
ok(el.className === 'test-class test-class2', 'added second class');
|
||||
Dom.removeElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class2', 'removed first class');
|
||||
strictEqual(el.className, 'test-class', 'does not duplicate classes');
|
||||
|
||||
throws(function(){
|
||||
Dom.addElClass(el, 'foo foo-bar');
|
||||
}, 'throws when attempting to add a class with whitespace');
|
||||
|
||||
Dom.addElClass(el, 'test2_className');
|
||||
strictEqual(el.className, 'test-class test2_className', 'adds second class');
|
||||
|
||||
Dom.addElClass(el, 'FOO');
|
||||
strictEqual(el.className, 'test-class test2_className FOO', 'adds third class');
|
||||
});
|
||||
|
||||
test('should read class names on an element', function(){
|
||||
test('removeElClass()', function() {
|
||||
var el = document.createElement('div');
|
||||
Dom.addElClass(el, 'test-class1');
|
||||
ok(Dom.hasElClass(el, 'test-class1') === true, 'class detected');
|
||||
ok(Dom.hasElClass(el, 'test-class') === false, 'substring correctly not detected');
|
||||
|
||||
el.className = 'test-class foo foo test2_className FOO bar';
|
||||
|
||||
expect(5);
|
||||
|
||||
Dom.removeElClass(el, 'test-class');
|
||||
strictEqual(el.className, 'foo foo test2_className FOO bar', 'removes one class');
|
||||
|
||||
Dom.removeElClass(el, 'foo');
|
||||
strictEqual(el.className, 'test2_className FOO bar', 'removes all instances of a class');
|
||||
|
||||
throws(function(){
|
||||
Dom.removeElClass(el, 'test2_className bar');
|
||||
}, 'throws when attempting to remove a class with whitespace');
|
||||
|
||||
Dom.removeElClass(el, 'test2_className');
|
||||
strictEqual(el.className, 'FOO bar', 'removes another class');
|
||||
|
||||
Dom.removeElClass(el, 'FOO');
|
||||
strictEqual(el.className, 'bar', 'removes another class');
|
||||
});
|
||||
|
||||
test('hasElClass()', function(){
|
||||
var el = document.createElement('div');
|
||||
|
||||
el.className = 'test-class foo foo test2_className FOO bar';
|
||||
|
||||
strictEqual(Dom.hasElClass(el, 'test-class'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'foo'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'test2_className'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'FOO'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'bar'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'test2'), false, 'valid substring - but not a class - correctly not detected');
|
||||
strictEqual(Dom.hasElClass(el, 'className'), false, 'valid substring - but not a class - correctly not detected');
|
||||
|
||||
throws(function(){
|
||||
Dom.hasElClass(el, 'FOO bar');
|
||||
}, 'throws when attempting to detect a class with whitespace');
|
||||
});
|
||||
|
||||
test('toggleElClass()', function() {
|
||||
let el = Dom.createEl('div', {className: 'foo bar'});
|
||||
|
||||
let predicateToggles = [
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: true,
|
||||
className: 'foo bar',
|
||||
message: 'if predicate `true` matches state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: false,
|
||||
className: 'foo bar',
|
||||
message: 'if predicate `false` matches state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: true,
|
||||
className: 'foo bar baz',
|
||||
message: 'if predicate `true` differs from state of the element, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: false,
|
||||
className: 'bar baz',
|
||||
message: 'if predicate `false` differs from state of the element, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: () => true,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `true`, matching the state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => false,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `false`, matching the state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => true,
|
||||
className: 'bar baz foo',
|
||||
message: 'if a predicate function returns `true`, differing from state of the element, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => false,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `false`, differing from state of the element, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: Function.prototype,
|
||||
className: 'bar baz foo',
|
||||
message: 'if a predicate function returns `undefined` and the element does not have the class, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: Function.prototype,
|
||||
className: 'baz foo',
|
||||
message: 'if a predicate function returns `undefined` and the element has the class, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: () => [],
|
||||
className: 'baz foo bar',
|
||||
message: 'if a predicate function returns a defined non-boolean value and the element does not have the class, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: () => 'this is incorrect',
|
||||
className: 'foo bar',
|
||||
message: 'if a predicate function returns a defined non-boolean value and the element has the class, remove the class'
|
||||
},
|
||||
];
|
||||
|
||||
expect(3 + predicateToggles.length);
|
||||
|
||||
Dom.toggleElClass(el, 'bar');
|
||||
strictEqual(el.className, 'foo', 'toggles a class off, if present');
|
||||
|
||||
Dom.toggleElClass(el, 'bar');
|
||||
strictEqual(el.className, 'foo bar', 'toggles a class on, if absent');
|
||||
|
||||
throws(function(){
|
||||
Dom.toggleElClass(el, 'foo bar');
|
||||
}, 'throws when attempting to toggle a class with whitespace');
|
||||
|
||||
predicateToggles.forEach(x => {
|
||||
Dom.toggleElClass(el, x.toggle, x.predicate);
|
||||
strictEqual(el.className, x.className, x.message);
|
||||
});
|
||||
});
|
||||
|
||||
test('should set element attributes from object', function(){
|
||||
@ -293,4 +434,37 @@ test('Dom.appendContent', function(assert) {
|
||||
assert.strictEqual(el.firstChild.nextSibling, p2, 'the second paragraph was appended');
|
||||
});
|
||||
|
||||
test('$() and $$()', function() {
|
||||
let fixture = document.getElementById('qunit-fixture');
|
||||
let container = document.createElement('div');
|
||||
let children = [
|
||||
document.createElement('div'),
|
||||
document.createElement('div'),
|
||||
document.createElement('div'),
|
||||
];
|
||||
|
||||
children.forEach(child => container.appendChild(child));
|
||||
fixture.appendChild(container);
|
||||
|
||||
let totalDivCount = document.getElementsByTagName('div').length;
|
||||
|
||||
expect(12);
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture'), fixture, 'can find an element in the document context');
|
||||
strictEqual(Dom.$$('div').length, totalDivCount, 'finds elements in the document context');
|
||||
|
||||
strictEqual(Dom.$('div', container), children[0], 'can find an element in a DOM element context');
|
||||
strictEqual(Dom.$$('div', container).length, children.length, 'finds elements in a DOM element context');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', document.querySelector('unknown')), fixture, 'falls back to document given a bad context element');
|
||||
strictEqual(Dom.$$('div', document.querySelector('unknown')).length, totalDivCount, 'falls back to document given a bad context element');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', 'body'), fixture, 'can find an element in a selector context');
|
||||
strictEqual(Dom.$$('div', '#qunit-fixture').length, 1 + children.length, 'finds elements in a selector context');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', 'unknown'), fixture, 'falls back to document given a bad context selector');
|
||||
strictEqual(Dom.$$('div', 'unknown').length, totalDivCount, 'falls back to document given a bad context selector');
|
||||
|
||||
strictEqual(Dom.$('div', children[0]), null, 'returns null for missing elements');
|
||||
strictEqual(Dom.$$('div', children[0]).length, 0, 'returns 0 for missing elements');
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import videojs from '../../src/js/video.js';
|
||||
import TestHelpers from './test-helpers.js';
|
||||
import Player from '../../src/js/player.js';
|
||||
import * as Dom from '../../src/js/utils/dom.js';
|
||||
import log from '../../src/js/utils/log.js';
|
||||
import document from 'global/document';
|
||||
|
||||
@ -78,3 +79,29 @@ test('should expose options and players properties for backward-compatibility',
|
||||
ok(typeof videojs.options, 'object', 'options should be an object');
|
||||
ok(typeof videojs.players, 'object', 'players should be an object');
|
||||
});
|
||||
|
||||
test('should expose DOM functions', function() {
|
||||
|
||||
// Keys are videojs methods, values are Dom methods.
|
||||
let methods = {
|
||||
isEl: 'isEl',
|
||||
isTextNode: 'isTextNode',
|
||||
hasClass: 'hasElClass',
|
||||
addClass: 'addElClass',
|
||||
removeClass: 'removeElClass',
|
||||
toggleClass: 'toggleElClass',
|
||||
setAttributes: 'setElAttributes',
|
||||
getAttributes: 'getElAttributes',
|
||||
emptyEl: 'emptyEl',
|
||||
insertContent: 'insertContent',
|
||||
appendContent: 'appendContent'
|
||||
};
|
||||
|
||||
let keys = Object.keys(methods);
|
||||
|
||||
expect(keys.length);
|
||||
keys.forEach(function(vjsName) {
|
||||
let domName = methods[vjsName];
|
||||
strictEqual(videojs[vjsName], Dom[domName], `videojs.${vjsName} is a reference to Dom.${domName}`);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user