1
0
mirror of https://github.com/videojs/video.js.git synced 2025-02-02 11:34:50 +02:00

refactor(texttracksettings): DRYer code and remove massive HTML blob (#3679)

* DRYer code while keeping tests passing
* Replace massive HTML blob with DOM methods
* Create obj util and implement it
This commit is contained in:
Pat O'Neill 2016-11-04 13:45:51 -04:00 committed by Gary Katsevman
parent 74cddcad73
commit fb74c71ba6
6 changed files with 641 additions and 321 deletions

View File

@ -1,168 +1,220 @@
/**
* @file text-track-settings.js
*/
import Component from '../component';
import * as Events from '../utils/events.js';
import * as Fn from '../utils/fn.js';
import log from '../utils/log.js';
import safeParseTuple from 'safe-json-parse/tuple';
import window from 'global/window';
import Component from '../component';
import {createEl} from '../utils/dom';
import * as Fn from '../utils/fn';
import * as Obj from '../utils/obj';
import log from '../utils/log';
function captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId) {
const template = `
<div role="document">
<div role="heading" aria-level="1" id="${dialogLabelId}" class="vjs-control-text">Captions Settings Dialog</div>
<div id="${dialogDescriptionId}" class="vjs-control-text">Beginning of dialog window. Escape will cancel and close the window.</div>
<div class="vjs-tracksettings">
<div class="vjs-tracksettings-colors">
<fieldset class="vjs-fg-color vjs-tracksetting">
<legend>Text</legend>
<label class="vjs-label" for="captions-foreground-color-${uniqueId}">Color</label>
<select id="captions-foreground-color-${uniqueId}">
<option value="#FFF" selected>White</option>
<option value="#000">Black</option>
<option value="#F00">Red</option>
<option value="#0F0">Green</option>
<option value="#00F">Blue</option>
<option value="#FF0">Yellow</option>
<option value="#F0F">Magenta</option>
<option value="#0FF">Cyan</option>
</select>
<span class="vjs-text-opacity vjs-opacity">
<label class="vjs-label" for="captions-foreground-opacity-${uniqueId}">Transparency</label>
<select id="captions-foreground-opacity-${uniqueId}">
<option value="1" selected>Opaque</option>
<option value="0.5">Semi-Opaque</option>
</select>
</span>
</fieldset>
<fieldset class="vjs-bg-color vjs-tracksetting">
<legend>Background</legend>
<label class="vjs-label" for="captions-background-color-${uniqueId}">Color</label>
<select id="captions-background-color-${uniqueId}">
<option value="#000" selected>Black</option>
<option value="#FFF">White</option>
<option value="#F00">Red</option>
<option value="#0F0">Green</option>
<option value="#00F">Blue</option>
<option value="#FF0">Yellow</option>
<option value="#F0F">Magenta</option>
<option value="#0FF">Cyan</option>
</select>
<span class="vjs-bg-opacity vjs-opacity">
<label class="vjs-label" for="captions-background-opacity-${uniqueId}">Transparency</label>
<select id="captions-background-opacity-${uniqueId}">
<option value="1" selected>Opaque</option>
<option value="0.5">Semi-Transparent</option>
<option value="0">Transparent</option>
</select>
</span>
</fieldset>
<fieldset class="window-color vjs-tracksetting">
<legend>Window</legend>
<label class="vjs-label" for="captions-window-color-${uniqueId}">Color</label>
<select id="captions-window-color-${uniqueId}">
<option value="#000" selected>Black</option>
<option value="#FFF">White</option>
<option value="#F00">Red</option>
<option value="#0F0">Green</option>
<option value="#00F">Blue</option>
<option value="#FF0">Yellow</option>
<option value="#F0F">Magenta</option>
<option value="#0FF">Cyan</option>
</select>
<span class="vjs-window-opacity vjs-opacity">
<label class="vjs-label" for="captions-window-opacity-${uniqueId}">Transparency</label>
<select id="captions-window-opacity-${uniqueId}">
<option value="0" selected>Transparent</option>
<option value="0.5">Semi-Transparent</option>
<option value="1">Opaque</option>
</select>
</span>
</fieldset>
</div> <!-- vjs-tracksettings-colors -->
<div class="vjs-tracksettings-font">
<div class="vjs-font-percent vjs-tracksetting">
<label class="vjs-label" for="captions-font-size-${uniqueId}">Font Size</label>
<select id="captions-font-size-${uniqueId}">
<option value="0.50">50%</option>
<option value="0.75">75%</option>
<option value="1.00" selected>100%</option>
<option value="1.25">125%</option>
<option value="1.50">150%</option>
<option value="1.75">175%</option>
<option value="2.00">200%</option>
<option value="3.00">300%</option>
<option value="4.00">400%</option>
</select>
</div>
<div class="vjs-edge-style vjs-tracksetting">
<label class="vjs-label" for="captions-edge-style-${uniqueId}">Text Edge Style</label>
<select id="captions-edge-style-${uniqueId}">
<option value="none" selected>None</option>
<option value="raised">Raised</option>
<option value="depressed">Depressed</option>
<option value="uniform">Uniform</option>
<option value="dropshadow">Dropshadow</option>
</select>
</div>
<div class="vjs-font-family vjs-tracksetting">
<label class="vjs-label" for="captions-font-family-${uniqueId}">Font Family</label>
<select id="captions-font-family-${uniqueId}">
<option value="proportionalSansSerif" selected>Proportional Sans-Serif</option>
<option value="monospaceSansSerif">Monospace Sans-Serif</option>
<option value="proportionalSerif">Proportional Serif</option>
<option value="monospaceSerif">Monospace Serif</option>
<option value="casual">Casual</option>
<option value="script">Script</option>
<option value="small-caps">Small Caps</option>
</select>
</div>
</div> <!-- vjs-tracksettings-font -->
<div class="vjs-tracksettings-controls">
<button class="vjs-default-button">Defaults</button>
<button class="vjs-done-button">Done</button>
</div>
</div> <!-- vjs-tracksettings -->
</div> <!-- role="document" -->
`;
const LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
return template;
}
const COLOR_BLACK = ['#000', 'Black'];
const COLOR_BLUE = ['#00F', 'Blue'];
const COLOR_CYAN = ['#0FF', 'Cyan'];
const COLOR_GREEN = ['#0F0', 'Green'];
const COLOR_MAGENTA = ['#F0F', 'Magenta'];
const COLOR_RED = ['#F00', 'Red'];
const COLOR_WHITE = ['#FFF', 'White'];
const COLOR_YELLOW = ['#FF0', 'Yellow'];
function getSelectedOptionValue(target) {
let selectedOption;
const OPACITY_OPAQUE = ['1', 'Opaque'];
const OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
const OPACITY_TRANS = ['0', 'Transparent'];
// not all browsers support selectedOptions, so, fallback to options
if (target.selectedOptions) {
selectedOption = target.selectedOptions[0];
} else if (target.options) {
selectedOption = target.options[target.options.selectedIndex];
// Configuration for the various <select> elements in the DOM of this component.
//
// Possible keys include:
//
// `default`:
// The default option index. Only needs to be provided if not zero.
// `parser`:
// A function which is used to parse the value from the selected option in
// a customized way.
// `selector`:
// The selector used to find the associated <select> element.
const selectConfigs = {
backgroundColor: {
selector: '.vjs-bg-color > select',
id: 'captions-background-color-%s',
label: 'Color',
options: [
COLOR_BLACK,
COLOR_WHITE,
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_YELLOW,
COLOR_MAGENTA,
COLOR_CYAN
]
},
backgroundOpacity: {
selector: '.vjs-bg-opacity > select',
id: 'captions-background-opacity-%s',
label: 'Transparency',
options: [
OPACITY_OPAQUE,
OPACITY_SEMI,
OPACITY_TRANS
]
},
color: {
selector: '.vjs-fg-color > select',
id: 'captions-foreground-color-%s',
label: 'Color',
options: [
COLOR_WHITE,
COLOR_BLACK,
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_YELLOW,
COLOR_MAGENTA,
COLOR_CYAN
]
},
edgeStyle: {
selector: '.vjs-edge-style > select',
id: '%s',
label: 'Text Edge Style',
options: [
['none', 'None'],
['raised', 'Raised'],
['depressed', 'Depressed'],
['uniform', 'Uniform'],
['dropshadow', 'Dropshadow']
]
},
fontFamily: {
selector: '.vjs-font-family > select',
id: 'captions-font-family-%s',
label: 'Font Family',
options: [
['proportionalSansSerif', 'Proportional Sans-Serif'],
['monospaceSansSerif', 'Monospace Sans-Serif'],
['proportionalSerif', 'Proportional Serif'],
['monospaceSerif', 'Monospace Serif'],
['casual', 'Casual'],
['script', 'Script'],
['small-caps', 'Small Caps']
]
},
fontPercent: {
selector: '.vjs-font-percent > select',
id: 'captions-font-size-%s',
label: 'Font Size',
options: [
['0.50', '50%'],
['0.75', '75%'],
['1.00', '100%'],
['1.25', '125%'],
['1.50', '150%'],
['1.75', '175%'],
['2.00', '200%'],
['3.00', '300%'],
['4.00', '400%']
],
default: 2,
parser: (v) => v === '1.00' ? null : Number(v)
},
textOpacity: {
selector: '.vjs-text-opacity > select',
id: 'captions-foreground-opacity-%s',
label: 'Transparency',
options: [
OPACITY_OPAQUE,
OPACITY_SEMI
]
},
// Options for this object are defined below.
windowColor: {
selector: '.vjs-window-color > select',
id: 'captions-window-color-%s',
label: 'Color'
},
// Options for this object are defined below.
windowOpacity: {
selector: '.vjs-window-opacity > select',
id: 'captions-window-opacity-%s',
label: 'Transparency',
options: [
OPACITY_TRANS,
OPACITY_SEMI,
OPACITY_OPAQUE
]
}
};
selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
/**
* Parses out option values.
*
* @private
* @param {String} value
* @param {Function} [parser]
* Optional function to adjust the value.
* @return {Mixed}
* Will be `undefined` if no value exists (or if given value is "none").
*/
function parseOptionValue(value, parser) {
if (parser) {
value = parser(value);
}
return selectedOption.value;
if (value && value !== 'none') {
return value;
}
}
function setSelectedOption(target, value) {
/**
* Gets the value of the selected <option> element within a <select> element.
*
* @param {Object} config
* @param {Function} [parser]
* Optional function to adjust the value.
* @return {Mixed}
*/
function getSelectedOptionValue(el, parser) {
const value = el.options[el.options.selectedIndex].value;
return parseOptionValue(value, parser);
}
/**
* Sets the selected <option> element within a <select> element based on a
* given value.
*
* @param {Object} el
* @param {String} value
* @param {Function} [parser]
* Optional function to adjust the value before comparing.
*/
function setSelectedOption(el, value, parser) {
if (!value) {
return;
}
let i;
for (i = 0; i < target.options.length; i++) {
const option = target.options[i];
if (option.value === value) {
for (let i = 0; i < el.options.length; i++) {
if (parseOptionValue(el.options[i].value, parser) === value) {
el.selectedIndex = i;
break;
}
}
target.selectedIndex = i;
}
/**
* Manipulate settings of texttracks
* Manipulate settings of text tracks
*
* @param {Object} player Main Player
* @param {Object=} options Object of option names and values
@ -173,46 +225,196 @@ class TextTrackSettings extends Component {
constructor(player, options) {
super(player, options);
this.setDefaults();
this.hide();
this.updateDisplay = Fn.bind(this, this.updateDisplay);
// Grab `persistTextTrackSettings` from the player options if not passed in child options
if (options.persistTextTrackSettings === undefined) {
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
}
Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function() {
this.on(this.$('.vjs-done-button'), 'click', () => {
this.saveSettings();
this.hide();
}));
});
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.on(this.$('.vjs-default-button'), 'click', () => {
this.setDefaults();
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));
Obj.each(selectConfigs, config => {
this.on(this.$(config.selector), 'change', this.updateDisplay);
});
if (this.options_.persistTextTrackSettings) {
this.restoreSettings();
}
}
/**
* Create a <select> element with configured options.
*
* @private
* @return {Element}
* @method createElSelect_
*/
createElSelect_(key) {
const config = selectConfigs[key];
const id = config.id.replace('%s', this.id_);
return [
createEl('label', {
className: 'vjs-label',
textContent: config.label
}, {
for: id
}),
createEl('select', {id}, undefined, config.options.map(o => {
return createEl('option', {
textContent: this.localize(o[1]),
value: o[0]
});
}))
];
}
/**
* Create foreground color element for the component
*
* @private
* @return {Element}
* @method createElFgColor_
*/
createElFgColor_() {
const legend = createEl('legend', {
textContent: this.localize('Text')
});
const select = this.createElSelect_('color');
const opacity = createEl('span', {
className: 'vjs-text-opacity vjs-opacity'
}, undefined, this.createElSelect_('textOpacity'));
return createEl('fieldset', {
className: 'vjs-fg-color vjs-tracksetting'
}, undefined, [legend].concat(select, opacity));
}
/**
* Create background color element for the component
*
* @private
* @return {Element}
* @method createElBgColor_
*/
createElBgColor_() {
const legend = createEl('legend', {
textContent: this.localize('Background')
});
const select = this.createElSelect_('backgroundColor');
const opacity = createEl('span', {
className: 'vjs-bg-opacity vjs-opacity'
}, undefined, this.createElSelect_('backgroundOpacity'));
return createEl('fieldset', {
className: 'vjs-bg-color vjs-tracksetting'
}, undefined, [legend].concat(select, opacity));
}
/**
* Create window color element for the component
*
* @private
* @return {Element}
* @method createElWinColor_
*/
createElWinColor_() {
const legend = createEl('legend', {
textContent: this.localize('Window')
});
const select = this.createElSelect_('windowColor');
const opacity = createEl('span', {
className: 'vjs-window-opacity vjs-opacity'
}, undefined, this.createElSelect_('windowOpacity'));
return createEl('fieldset', {
className: 'vjs-window-color vjs-tracksetting'
}, undefined, [legend].concat(select, opacity));
}
/**
* Create color elements for the component
*
* @private
* @return {Element}
* @method createElColors_
*/
createElColors_() {
return createEl('div', {
className: 'vjs-tracksettings-colors'
}, undefined, [
this.createElFgColor_(),
this.createElBgColor_(),
this.createElWinColor_()
]);
}
/**
* Create font elements for the component
*
* @private
* @return {Element}
* @method createElFont_
*/
createElFont_() {
const fontPercent = createEl('div', {
className: 'vjs-font-percent vjs-tracksetting'
}, undefined, this.createElSelect_('fontPercent'));
const edgeStyle = createEl('div', {
className: 'vjs-edge-style vjs-tracksetting'
}, undefined, this.createElSelect_('edgeStyle'));
const fontFamily = createEl('div', {
className: 'vjs-font-family vjs-tracksetting'
}, undefined, this.createElSelect_('fontFamily'));
return createEl('div', {
className: 'vjs-tracksettings-font'
}, undefined, [fontPercent, edgeStyle, fontFamily]);
}
/**
* Create controls for the component
*
* @private
* @return {Element}
* @method createElControls_
*/
createElControls_() {
const defaultsButton = createEl('button', {
className: 'vjs-default-button',
textContent: this.localize('Defaults')
});
const doneButton = createEl('button', {
className: 'vjs-done-button',
textContent: 'Done'
});
return createEl('div', {
className: 'vjs-tracksettings-controls'
}, undefined, [defaultsButton, doneButton]);
}
/**
* Create the component's DOM element
*
@ -220,99 +422,86 @@ class TextTrackSettings extends Component {
* @method createEl
*/
createEl() {
const uniqueId = this.id_;
const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId;
const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId;
const settings = createEl('div', {
className: 'vjs-tracksettings'
}, undefined, [
this.createElColors_(),
this.createElFont_(),
this.createElControls_()
]);
return super.createEl('div', {
const heading = createEl('div', {
className: 'vjs-control-text',
id: `TTsettingsDialogLabel-${this.id_}`,
textContent: 'Caption Settings Dialog'
}, {
'aria-level': '1',
'role': 'heading'
});
const description = createEl('div', {
className: 'vjs-control-text',
id: `TTsettingsDialogDescription-${this.id_}`,
textContent: 'Beginning of dialog window. Escape will cancel and close the window.'
});
const doc = createEl('div', undefined, {
role: 'document'
}, [heading, description, settings]);
return createEl('div', {
className: 'vjs-caption-settings vjs-modal-overlay',
innerHTML: captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId),
tabIndex: -1
}, {
'role': 'dialog',
'aria-labelledby': dialogLabelId,
'aria-describedby': dialogDescriptionId
'aria-labelledby': heading.id,
'aria-describedby': description.id
}, doc);
}
/**
* Gets an object of text track settings (or null).
*
* @return {Object}
* An object with config values parsed from the DOM or localStorage.
* @method getValues
*/
getValues() {
return Obj.reduce(selectConfigs, (accum, config, key) => {
const value = getSelectedOptionValue(this.$(config.selector), config.parser);
if (value !== undefined) {
accum[key] = value;
}
return accum;
}, {});
}
/**
* Sets text track settings from an object of values.
*
* @param {Object} values
* An object with config values parsed from the DOM or localStorage.
* @method setValues
*/
setValues(values) {
Obj.each(selectConfigs, (config, key) => {
setSelectedOption(this.$(config.selector), values[key], config.parser);
});
}
/**
* Get texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
* Sets all <select> elements to their default values.
*
* @return {Object}
* @method getValues
* @method setDefaults
*/
getValues() {
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')));
setDefaults() {
Obj.each(selectConfigs, (config) => {
const index = config.hasOwnProperty('default') ? config.default : 0;
const result = {
fontPercent,
fontFamily,
textOpacity,
windowColor,
windowOpacity,
backgroundOpacity: bgOpacity,
edgeStyle: textEdge,
color: fgColor,
backgroundColor: bgColor
};
for (const name in result) {
if (result[name] === '' || result[name] === 'none' || (name === 'fontPercent' && result[name] === 1.00)) {
delete result[name];
}
}
return result;
}
/**
* Set texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
*
* @param {Object} values Object with texttrack setting values
* @method setValues
*/
setValues(values) {
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;
if (fontPercent) {
fontPercent = fontPercent.toFixed(2);
}
setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
this.$(config.selector).selectedIndex = index;
});
}
/**
@ -321,17 +510,12 @@ class TextTrackSettings extends Component {
* @method restoreSettings
*/
restoreSettings() {
let err;
let values;
try {
[err, values] = safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'));
if (err) {
log.error(err);
}
} catch (e) {
log.warn(e);
values = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY));
} catch (err) {
log.warn(err);
}
if (values) {
@ -340,7 +524,7 @@ class TextTrackSettings extends Component {
}
/**
* Save texttrack settings to local storage
* Save text track settings to local storage
*
* @method saveSettings
*/
@ -352,18 +536,18 @@ class TextTrackSettings extends Component {
const values = this.getValues();
try {
if (Object.getOwnPropertyNames(values).length > 0) {
window.localStorage.setItem('vjs-text-track-settings', JSON.stringify(values));
if (Object.keys(values).length) {
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
} else {
window.localStorage.removeItem('vjs-text-track-settings');
window.localStorage.removeItem(LOCAL_STORAGE_KEY);
}
} catch (e) {
log.warn(e);
} catch (err) {
log.warn(err);
}
}
/**
* Update display of texttrack settings
* Update display of text track settings
*
* @method updateDisplay
*/

View File

@ -96,10 +96,11 @@ export function getEl(id) {
* @param {String} [tagName='div'] Name of tag to be created.
* @param {Object} [properties={}] Element properties to be applied.
* @param {Object} [attributes={}] Element attributes to be applied.
* @param {String|Element|TextNode|Array|Function} [content] Contents for the element (see: `normalizeContent`)
* @return {Element}
* @function createEl
*/
export function createEl(tagName = 'div', properties = {}, attributes = {}) {
export function createEl(tagName = 'div', properties = {}, attributes = {}, content) {
const el = document.createElement(tagName);
Object.getOwnPropertyNames(properties).forEach(function(propName) {
@ -113,6 +114,11 @@ export function createEl(tagName = 'div', properties = {}, attributes = {}) {
has been deprecated. Use the third argument instead.
createEl(type, properties, attributes). Attempting to set ${propName} to ${val}.`);
el.setAttribute(propName, val);
// Handle textContent since it's not supported everywhere and we have a
// method for it.
} else if (propName === 'textContent') {
textContent(el, val);
} else {
el[propName] = val;
}
@ -122,6 +128,10 @@ export function createEl(tagName = 'div', properties = {}, attributes = {}) {
el.setAttribute(attrName, attributes[attrName]);
});
if (content) {
appendContent(el, content);
}
return el;
}
@ -139,6 +149,7 @@ export function textContent(el, text) {
} else {
el.textContent = text;
}
return el;
}
/**

32
src/js/utils/obj.js Normal file
View File

@ -0,0 +1,32 @@
/**
* @file obj.js
*/
/**
* Array-like iteration for objects.
*
* @param {Object} object
* @param {Function} fn
* A callback function which is called for each key in the object. It
* receives the value and key as arguments.
*/
export function each(object, fn) {
Object.keys(object).forEach(key => fn(object[key], key));
}
/**
* Array-like reduce for objects.
*
* @param {Object} object
* @param {Function} fn
* A callback function which is called for each key in the object. It
* receives the accumulated value and the per-iteration value and key
* as arguments.
* @param {Mixed} [initial = 0]
* @return {Mixed}
*/
export function reduce(object, fn, initial = 0) {
return Object.keys(object).reduce(
(accum, key) => fn(accum, object[key], key),
initial);
}

View File

@ -31,6 +31,7 @@ QUnit.test('should update settings', function(assert) {
tracks,
persistTextTrackSettings: true
});
const newSettings = {
backgroundOpacity: '0.5',
textOpacity: '0.5',
@ -44,43 +45,52 @@ QUnit.test('should update settings', function(assert) {
};
player.textTrackSettings.setValues(newSettings);
assert.deepEqual(player.textTrackSettings.getValues(),
newSettings,
'values are updated');
newSettings,
'values are updated');
assert.equal(player.$('.vjs-fg-color > select').selectedIndex,
2,
'fg-color is set to new value');
2,
'fg-color is set to new value');
assert.equal(player.$('.vjs-bg-color > select').selectedIndex,
1,
'bg-color is set to new value');
assert.equal(player.$('.window-color > select').selectedIndex,
1,
'window-color is set to new value');
1,
'bg-color is set to new value');
assert.equal(player.$('.vjs-window-color > select').selectedIndex,
1,
'window-color is set to new value');
assert.equal(player.$('.vjs-text-opacity > select').selectedIndex,
1,
'text-opacity is set to new value');
1,
'text-opacity is set to new value');
assert.equal(player.$('.vjs-bg-opacity > select').selectedIndex,
1,
'bg-opacity is set to new value');
1,
'bg-opacity is set to new value');
assert.equal(player.$('.vjs-window-opacity > select').selectedIndex,
1,
'window-opacity is set to new value');
1,
'window-opacity is set to new value');
assert.equal(player.$('.vjs-edge-style select').selectedIndex,
1,
'edge-style is set to new value');
1,
'edge-style is set to new value');
assert.equal(player.$('.vjs-font-family select').selectedIndex,
3,
'font-family is set to new value');
3,
'font-family is set to new value');
assert.equal(player.$('.vjs-font-percent select').selectedIndex,
3,
'font-percent is set to new value');
3,
'font-percent is set to new value');
Events.trigger(player.$('.vjs-done-button'), 'click');
assert.deepEqual(safeParseTuple(
window.localStorage.getItem('vjs-text-track-settings'))[1],
newSettings,
'values are saved');
assert.deepEqual(safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'))[1],
newSettings,
'values are saved');
player.dispose();
});
@ -93,7 +103,7 @@ QUnit.test('should restore default settings', function(assert) {
player.$('.vjs-fg-color > select').selectedIndex = 1;
player.$('.vjs-bg-color > select').selectedIndex = 1;
player.$('.window-color > select').selectedIndex = 1;
player.$('.vjs-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;
@ -106,8 +116,8 @@ QUnit.test('should restore default settings', function(assert) {
Events.trigger(player.$('.vjs-done-button'), 'click');
assert.deepEqual(player.textTrackSettings.getValues(),
defaultSettings,
'values are defaulted');
defaultSettings,
'values are defaulted');
// TODO:
// MikeA: need to figure out how to modify saveSettings
// to factor in defaults are no longer null
@ -116,32 +126,40 @@ QUnit.test('should restore default settings', function(assert) {
// 'values are saved');
assert.equal(player.$('.vjs-fg-color > select').selectedIndex,
0,
'fg-color is set to default value');
0,
'fg-color is set to default value');
assert.equal(player.$('.vjs-bg-color > select').selectedIndex,
0,
'bg-color is set to default value');
assert.equal(player.$('.window-color > select').selectedIndex,
0,
'window-color is set to default value');
0,
'bg-color is set to default value');
assert.equal(player.$('.vjs-window-color > select').selectedIndex,
0,
'window-color is set to default value');
assert.equal(player.$('.vjs-text-opacity > select').selectedIndex,
0,
'text-opacity is set to default value');
0,
'text-opacity is set to default value');
assert.equal(player.$('.vjs-bg-opacity > select').selectedIndex,
0,
'bg-opacity is set to default value');
0,
'bg-opacity is set to default value');
assert.equal(player.$('.vjs-window-opacity > select').selectedIndex,
0,
'window-opacity is set to default value');
0,
'window-opacity is set to default value');
assert.equal(player.$('.vjs-edge-style select').selectedIndex,
0,
'edge-style is set to default value');
0,
'edge-style is set to default value');
assert.equal(player.$('.vjs-font-family select').selectedIndex,
0,
'font-family is set to default value');
0,
'font-family is set to default value');
assert.equal(player.$('.vjs-font-percent select').selectedIndex,
2,
'font-percent is set to default value');
2,
'font-percent is set to default value');
player.dispose();
});

View File

@ -15,8 +15,8 @@ QUnit.test('should return the element with the ID', function(assert) {
el1.id = 'test_id1';
el2.id = 'test_id2';
assert.ok(Dom.getEl('test_id1') === el1, 'found element for ID');
assert.ok(Dom.getEl('#test_id2') === el2, 'found element for CSS ID');
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) {
@ -27,10 +27,27 @@ QUnit.test('should create an element', function(assert) {
'data-test': 'asdf'
});
assert.ok(div.nodeName === 'DIV');
assert.ok(span.nodeName === 'SPAN');
assert.ok(span.getAttribute('data-test') === 'asdf');
assert.ok(span.innerHTML === 'fdsa');
assert.strictEqual(div.nodeName, 'DIV');
assert.strictEqual(span.nodeName, 'SPAN');
assert.strictEqual(span.getAttribute('data-test'), 'asdf');
assert.strictEqual(span.innerHTML, 'fdsa');
});
QUnit.test('should create an element, supporting textContent', function(assert) {
const span = Dom.createEl('span', {textContent: 'howdy'});
if (span.textContent) {
assert.strictEqual(span.textContent, 'howdy', 'works in browsers that support textContent');
} else {
assert.strictEqual(span.innerText, 'howdy', 'works in browsers that DO NOT support textContent');
}
});
QUnit.test('should create an element with content', function(assert) {
const span = Dom.createEl('span');
const div = Dom.createEl('div', undefined, undefined, span);
assert.strictEqual(div.firstChild, span);
});
QUnit.test('should insert an element first in another', function(assert) {
@ -39,28 +56,28 @@ QUnit.test('should insert an element first in another', function(assert) {
const parent = document.createElement('div');
Dom.insertElFirst(el1, parent);
assert.ok(parent.firstChild === el1, 'inserts first into empty parent');
assert.strictEqual(parent.firstChild, el1, 'inserts first into empty parent');
Dom.insertElFirst(el2, parent);
assert.ok(parent.firstChild === el2, 'inserts first into parent with child');
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.ok(typeof data === 'object', 'data object created');
assert.strictEqual(typeof data, 'object', 'data object created');
// Add data
const testData = { asdf: 'fdsa' };
const testData = {asdf: 'fdsa'};
data.test = testData;
assert.ok(Dom.getElData(el).test === testData, 'data added');
assert.strictEqual(Dom.getElData(el).test, testData, 'data added');
// Remove all data
Dom.removeElData(el);
assert.ok(!Dom.hasElData(el), 'cached item emptied');
assert.notOk(Dom.hasElData(el), 'cached item emptied');
});
QUnit.test('addElClass()', function(assert) {

View File

@ -0,0 +1,58 @@
/* eslint-env qunit */
import sinon from 'sinon';
import * as Obj from '../../../src/js/utils/obj';
QUnit.module('utils/obj');
QUnit.test('each', function(assert) {
const spy = sinon.spy();
Obj.each({
a: 1,
b: 'foo',
c: null
}, spy);
assert.strictEqual(spy.callCount, 3);
assert.ok(spy.calledWith(1, 'a'));
assert.ok(spy.calledWith('foo', 'b'));
assert.ok(spy.calledWith(null, 'c'));
Obj.each({}, spy);
assert.strictEqual(spy.callCount, 3, 'an empty object was not iterated over');
});
QUnit.test('reduce', function(assert) {
const first = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value) => accum + value);
assert.strictEqual(first, 10);
const second = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value) => accum + value, 10);
assert.strictEqual(second, 20);
const third = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value, key) => {
accum[key] = 0 - value;
return accum;
}, {});
assert.strictEqual(third.a, -1);
assert.strictEqual(third.b, -2);
assert.strictEqual(third.c, -3);
assert.strictEqual(third.d, -4);
});