videojs-playlist
Advanced tools
Comparing version 5.0.1 to 5.1.0
@@ -1,2 +0,2 @@ | ||
/*! @name videojs-playlist @version 5.0.1 @license Apache-2.0 */ | ||
/*! @name videojs-playlist @version 5.1.0 @license Apache-2.0 */ | ||
'use strict'; | ||
@@ -147,21 +147,35 @@ | ||
let guid = 1; | ||
/** | ||
* Returns whether a playlist item is an object of any kind, excluding null. | ||
* Transform any primitive playlist item value into an object. | ||
* | ||
* @private | ||
* For non-object values, adds a property to the transformed item containing | ||
* original value passed. | ||
* | ||
* @param {Object} | ||
* value to be checked | ||
* For all items, add a unique ID to each playlist item object. This id is | ||
* used to determine the index of an item in the playlist array in cases where | ||
* there are multiple otherwise identical items. | ||
* | ||
* @return {boolean} | ||
* The result | ||
* @param {Object} newItem | ||
* An playlist item object, but accepts any value. | ||
* | ||
* @return {Object} | ||
*/ | ||
const isItemObject = value => { | ||
return !!value && typeof value === 'object'; | ||
const preparePlaylistItem = newItem => { | ||
let item = newItem; | ||
if (!newItem || typeof newItem !== 'object') { | ||
// Casting to an Object in this way allows primitives to retain their | ||
// primitiveness (i.e. they will be cast back to primitives as needed). | ||
item = Object(newItem); | ||
item.originalValue = newItem; | ||
} | ||
item.playlistItemId_ = guid++; | ||
return item; | ||
}; | ||
/** | ||
* Look through an array of playlist items and transform any primitive | ||
* as well as null values to objects. This method also adds a property | ||
* to the transformed item containing original value passed in an input list. | ||
* Look through an array of playlist items and passes them to | ||
* preparePlaylistItem. | ||
* | ||
@@ -178,36 +192,4 @@ * @private | ||
const transformPrimitiveItems = arr => { | ||
const list = []; | ||
let tempItem; | ||
arr.forEach(item => { | ||
if (!isItemObject(item)) { | ||
tempItem = Object(item); | ||
tempItem.originalValue = item; | ||
} else { | ||
tempItem = item; | ||
} | ||
list.push(tempItem); | ||
}); | ||
return list; | ||
}; | ||
const preparePlaylistItems = arr => arr.map(preparePlaylistItem); | ||
/** | ||
* Generate a unique id for each playlist item object. This id will be used to determine | ||
* index of an item in the playlist array for cases where there are multiple items with | ||
* the same source set. | ||
* | ||
* @private | ||
* | ||
* @param {Array} arr | ||
* An array of playlist items | ||
*/ | ||
const generatePlaylistItemId = arr => { | ||
let guid = 1; | ||
arr.forEach(item => { | ||
item.playlistItemId_ = guid++; | ||
}); | ||
}; | ||
/** | ||
* Look through an array of playlist items for a specific playlist item id. | ||
@@ -389,3 +371,3 @@ * | ||
const playlist = player.playlist = (newList, newIndex = 0) => { | ||
const playlist = player.playlist = (nextPlaylist, newIndex = 0) => { | ||
if (changing) { | ||
@@ -395,17 +377,7 @@ throw new Error('do not call playlist() during a playlist change'); | ||
if (Array.isArray(newList)) { | ||
if (Array.isArray(nextPlaylist)) { | ||
// @todo - Simplify this to `list.slice()` for v5. | ||
const previousPlaylist = Array.isArray(list) ? list.slice() : null; | ||
const nextPlaylist = newList.slice(); | ||
list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects | ||
list = preparePlaylistItems(nextPlaylist); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
if (list.filter(item => isItemObject(item)).length !== list.length) { | ||
list = transformPrimitiveItems(list); | ||
} // Add unique id to each playlist item. This id will be used | ||
// to determine index in cases where there are more than one | ||
// identical sources in the playlist. | ||
generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
changing = true; | ||
@@ -435,10 +407,13 @@ player.trigger({ | ||
player.setTimeout(() => { | ||
player.trigger('playlistchange'); | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'change' | ||
}); | ||
}, 0); | ||
} | ||
} // Always return a shallow clone of the playlist list. | ||
// We also want to return originalValue if any item in the list has it. | ||
// We also want to return originalValue if any item in the list has it. | ||
return list.map(item => item.originalValue || item).slice(); | ||
return list.map(item => item.originalValue || item); | ||
}; // On a new source, if there is no current item, disable auto-advance. | ||
@@ -518,2 +493,121 @@ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are added to the current | ||
* playlist (rather than replacing the entire playlist). | ||
* | ||
* Unlike playlistchange, this is fired synchronously as it does not | ||
* affect playback. | ||
* | ||
* @typedef {Object} PlaylistAddEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistadd" | ||
* | ||
* @property {number} count | ||
* The number of items that were added. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were added. | ||
*/ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are removed from the | ||
* current playlist (rather than replacing the entire playlist). | ||
* | ||
* This is fired synchronously as it does not affect playback. | ||
* | ||
* @typedef {Object} PlaylistRemoveEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistremove" | ||
* | ||
* @property {number} count | ||
* The number of items that were removed. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were removed. | ||
*/ | ||
/** | ||
* Add one or more items to the playlist. | ||
* | ||
* @fires {PlaylistAddEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {string|Object|Array} item | ||
* An item - or array of items - to be added to the playlist. | ||
* | ||
* @param {number} [index] | ||
* If given as a valid value, injects the new playlist item(s) | ||
* starting from that index. Otherwise, the item(s) are appended. | ||
*/ | ||
playlist.add = (items, index) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
index = list.length; | ||
} | ||
if (!Array.isArray(items)) { | ||
items = [items]; | ||
} | ||
list.splice(index, 0, ...preparePlaylistItems(items)); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'add' | ||
}); | ||
player.trigger({ | ||
type: 'playlistadd', | ||
count: items.length, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Remove one or more items from the playlist. | ||
* | ||
* @fires {PlaylistRemoveEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {number} index | ||
* If a valid index in the current playlist, removes the item at that | ||
* index from the playlist. | ||
* | ||
* If no valid index is given, nothing is removed from the playlist. | ||
* | ||
* @param {number} [count=1] | ||
* The number of items to remove from the playlist. | ||
*/ | ||
playlist.remove = (index, count = 1) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
return; | ||
} | ||
list.splice(index, count); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'remove' | ||
}); | ||
player.trigger({ | ||
type: 'playlistremove', | ||
count, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Checks if the playlist contains a value. | ||
@@ -871,3 +965,3 @@ * | ||
if (Array.isArray(initialList)) { | ||
playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array. | ||
playlist(initialList, initialIndex); // If there is no initial list given, silently set an empty array. | ||
} else { | ||
@@ -880,3 +974,3 @@ list = []; | ||
var version = "5.0.1"; | ||
var version = "5.1.0"; | ||
@@ -883,0 +977,0 @@ const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin; |
@@ -1,2 +0,2 @@ | ||
/*! @name videojs-playlist @version 5.0.1 @license Apache-2.0 */ | ||
/*! @name videojs-playlist @version 5.1.0 @license Apache-2.0 */ | ||
import videojs from 'video.js'; | ||
@@ -141,21 +141,35 @@ | ||
let guid = 1; | ||
/** | ||
* Returns whether a playlist item is an object of any kind, excluding null. | ||
* Transform any primitive playlist item value into an object. | ||
* | ||
* @private | ||
* For non-object values, adds a property to the transformed item containing | ||
* original value passed. | ||
* | ||
* @param {Object} | ||
* value to be checked | ||
* For all items, add a unique ID to each playlist item object. This id is | ||
* used to determine the index of an item in the playlist array in cases where | ||
* there are multiple otherwise identical items. | ||
* | ||
* @return {boolean} | ||
* The result | ||
* @param {Object} newItem | ||
* An playlist item object, but accepts any value. | ||
* | ||
* @return {Object} | ||
*/ | ||
const isItemObject = value => { | ||
return !!value && typeof value === 'object'; | ||
const preparePlaylistItem = newItem => { | ||
let item = newItem; | ||
if (!newItem || typeof newItem !== 'object') { | ||
// Casting to an Object in this way allows primitives to retain their | ||
// primitiveness (i.e. they will be cast back to primitives as needed). | ||
item = Object(newItem); | ||
item.originalValue = newItem; | ||
} | ||
item.playlistItemId_ = guid++; | ||
return item; | ||
}; | ||
/** | ||
* Look through an array of playlist items and transform any primitive | ||
* as well as null values to objects. This method also adds a property | ||
* to the transformed item containing original value passed in an input list. | ||
* Look through an array of playlist items and passes them to | ||
* preparePlaylistItem. | ||
* | ||
@@ -172,36 +186,4 @@ * @private | ||
const transformPrimitiveItems = arr => { | ||
const list = []; | ||
let tempItem; | ||
arr.forEach(item => { | ||
if (!isItemObject(item)) { | ||
tempItem = Object(item); | ||
tempItem.originalValue = item; | ||
} else { | ||
tempItem = item; | ||
} | ||
list.push(tempItem); | ||
}); | ||
return list; | ||
}; | ||
const preparePlaylistItems = arr => arr.map(preparePlaylistItem); | ||
/** | ||
* Generate a unique id for each playlist item object. This id will be used to determine | ||
* index of an item in the playlist array for cases where there are multiple items with | ||
* the same source set. | ||
* | ||
* @private | ||
* | ||
* @param {Array} arr | ||
* An array of playlist items | ||
*/ | ||
const generatePlaylistItemId = arr => { | ||
let guid = 1; | ||
arr.forEach(item => { | ||
item.playlistItemId_ = guid++; | ||
}); | ||
}; | ||
/** | ||
* Look through an array of playlist items for a specific playlist item id. | ||
@@ -383,3 +365,3 @@ * | ||
const playlist = player.playlist = (newList, newIndex = 0) => { | ||
const playlist = player.playlist = (nextPlaylist, newIndex = 0) => { | ||
if (changing) { | ||
@@ -389,17 +371,7 @@ throw new Error('do not call playlist() during a playlist change'); | ||
if (Array.isArray(newList)) { | ||
if (Array.isArray(nextPlaylist)) { | ||
// @todo - Simplify this to `list.slice()` for v5. | ||
const previousPlaylist = Array.isArray(list) ? list.slice() : null; | ||
const nextPlaylist = newList.slice(); | ||
list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects | ||
list = preparePlaylistItems(nextPlaylist); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
if (list.filter(item => isItemObject(item)).length !== list.length) { | ||
list = transformPrimitiveItems(list); | ||
} // Add unique id to each playlist item. This id will be used | ||
// to determine index in cases where there are more than one | ||
// identical sources in the playlist. | ||
generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
changing = true; | ||
@@ -429,10 +401,13 @@ player.trigger({ | ||
player.setTimeout(() => { | ||
player.trigger('playlistchange'); | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'change' | ||
}); | ||
}, 0); | ||
} | ||
} // Always return a shallow clone of the playlist list. | ||
// We also want to return originalValue if any item in the list has it. | ||
// We also want to return originalValue if any item in the list has it. | ||
return list.map(item => item.originalValue || item).slice(); | ||
return list.map(item => item.originalValue || item); | ||
}; // On a new source, if there is no current item, disable auto-advance. | ||
@@ -512,2 +487,121 @@ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are added to the current | ||
* playlist (rather than replacing the entire playlist). | ||
* | ||
* Unlike playlistchange, this is fired synchronously as it does not | ||
* affect playback. | ||
* | ||
* @typedef {Object} PlaylistAddEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistadd" | ||
* | ||
* @property {number} count | ||
* The number of items that were added. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were added. | ||
*/ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are removed from the | ||
* current playlist (rather than replacing the entire playlist). | ||
* | ||
* This is fired synchronously as it does not affect playback. | ||
* | ||
* @typedef {Object} PlaylistRemoveEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistremove" | ||
* | ||
* @property {number} count | ||
* The number of items that were removed. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were removed. | ||
*/ | ||
/** | ||
* Add one or more items to the playlist. | ||
* | ||
* @fires {PlaylistAddEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {string|Object|Array} item | ||
* An item - or array of items - to be added to the playlist. | ||
* | ||
* @param {number} [index] | ||
* If given as a valid value, injects the new playlist item(s) | ||
* starting from that index. Otherwise, the item(s) are appended. | ||
*/ | ||
playlist.add = (items, index) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
index = list.length; | ||
} | ||
if (!Array.isArray(items)) { | ||
items = [items]; | ||
} | ||
list.splice(index, 0, ...preparePlaylistItems(items)); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'add' | ||
}); | ||
player.trigger({ | ||
type: 'playlistadd', | ||
count: items.length, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Remove one or more items from the playlist. | ||
* | ||
* @fires {PlaylistRemoveEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {number} index | ||
* If a valid index in the current playlist, removes the item at that | ||
* index from the playlist. | ||
* | ||
* If no valid index is given, nothing is removed from the playlist. | ||
* | ||
* @param {number} [count=1] | ||
* The number of items to remove from the playlist. | ||
*/ | ||
playlist.remove = (index, count = 1) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
return; | ||
} | ||
list.splice(index, count); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'remove' | ||
}); | ||
player.trigger({ | ||
type: 'playlistremove', | ||
count, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Checks if the playlist contains a value. | ||
@@ -865,3 +959,3 @@ * | ||
if (Array.isArray(initialList)) { | ||
playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array. | ||
playlist(initialList, initialIndex); // If there is no initial list given, silently set an empty array. | ||
} else { | ||
@@ -874,3 +968,3 @@ list = []; | ||
var version = "5.0.1"; | ||
var version = "5.1.0"; | ||
@@ -877,0 +971,0 @@ const registerPlugin = videojs.registerPlugin || videojs.plugin; |
@@ -1,2 +0,2 @@ | ||
/*! @name videojs-playlist @version 5.0.1 @license Apache-2.0 */ | ||
/*! @name videojs-playlist @version 5.1.0 @license Apache-2.0 */ | ||
(function (global, factory) { | ||
@@ -149,21 +149,35 @@ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) : | ||
let guid = 1; | ||
/** | ||
* Returns whether a playlist item is an object of any kind, excluding null. | ||
* Transform any primitive playlist item value into an object. | ||
* | ||
* @private | ||
* For non-object values, adds a property to the transformed item containing | ||
* original value passed. | ||
* | ||
* @param {Object} | ||
* value to be checked | ||
* For all items, add a unique ID to each playlist item object. This id is | ||
* used to determine the index of an item in the playlist array in cases where | ||
* there are multiple otherwise identical items. | ||
* | ||
* @return {boolean} | ||
* The result | ||
* @param {Object} newItem | ||
* An playlist item object, but accepts any value. | ||
* | ||
* @return {Object} | ||
*/ | ||
const isItemObject = value => { | ||
return !!value && typeof value === 'object'; | ||
const preparePlaylistItem = newItem => { | ||
let item = newItem; | ||
if (!newItem || typeof newItem !== 'object') { | ||
// Casting to an Object in this way allows primitives to retain their | ||
// primitiveness (i.e. they will be cast back to primitives as needed). | ||
item = Object(newItem); | ||
item.originalValue = newItem; | ||
} | ||
item.playlistItemId_ = guid++; | ||
return item; | ||
}; | ||
/** | ||
* Look through an array of playlist items and transform any primitive | ||
* as well as null values to objects. This method also adds a property | ||
* to the transformed item containing original value passed in an input list. | ||
* Look through an array of playlist items and passes them to | ||
* preparePlaylistItem. | ||
* | ||
@@ -180,36 +194,4 @@ * @private | ||
const transformPrimitiveItems = arr => { | ||
const list = []; | ||
let tempItem; | ||
arr.forEach(item => { | ||
if (!isItemObject(item)) { | ||
tempItem = Object(item); | ||
tempItem.originalValue = item; | ||
} else { | ||
tempItem = item; | ||
} | ||
list.push(tempItem); | ||
}); | ||
return list; | ||
}; | ||
const preparePlaylistItems = arr => arr.map(preparePlaylistItem); | ||
/** | ||
* Generate a unique id for each playlist item object. This id will be used to determine | ||
* index of an item in the playlist array for cases where there are multiple items with | ||
* the same source set. | ||
* | ||
* @private | ||
* | ||
* @param {Array} arr | ||
* An array of playlist items | ||
*/ | ||
const generatePlaylistItemId = arr => { | ||
let guid = 1; | ||
arr.forEach(item => { | ||
item.playlistItemId_ = guid++; | ||
}); | ||
}; | ||
/** | ||
* Look through an array of playlist items for a specific playlist item id. | ||
@@ -391,3 +373,3 @@ * | ||
const playlist = player.playlist = (newList, newIndex = 0) => { | ||
const playlist = player.playlist = (nextPlaylist, newIndex = 0) => { | ||
if (changing) { | ||
@@ -397,17 +379,7 @@ throw new Error('do not call playlist() during a playlist change'); | ||
if (Array.isArray(newList)) { | ||
if (Array.isArray(nextPlaylist)) { | ||
// @todo - Simplify this to `list.slice()` for v5. | ||
const previousPlaylist = Array.isArray(list) ? list.slice() : null; | ||
const nextPlaylist = newList.slice(); | ||
list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects | ||
list = preparePlaylistItems(nextPlaylist); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
if (list.filter(item => isItemObject(item)).length !== list.length) { | ||
list = transformPrimitiveItems(list); | ||
} // Add unique id to each playlist item. This id will be used | ||
// to determine index in cases where there are more than one | ||
// identical sources in the playlist. | ||
generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
changing = true; | ||
@@ -437,10 +409,13 @@ player.trigger({ | ||
player.setTimeout(() => { | ||
player.trigger('playlistchange'); | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'change' | ||
}); | ||
}, 0); | ||
} | ||
} // Always return a shallow clone of the playlist list. | ||
// We also want to return originalValue if any item in the list has it. | ||
// We also want to return originalValue if any item in the list has it. | ||
return list.map(item => item.originalValue || item).slice(); | ||
return list.map(item => item.originalValue || item); | ||
}; // On a new source, if there is no current item, disable auto-advance. | ||
@@ -520,2 +495,121 @@ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are added to the current | ||
* playlist (rather than replacing the entire playlist). | ||
* | ||
* Unlike playlistchange, this is fired synchronously as it does not | ||
* affect playback. | ||
* | ||
* @typedef {Object} PlaylistAddEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistadd" | ||
* | ||
* @property {number} count | ||
* The number of items that were added. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were added. | ||
*/ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are removed from the | ||
* current playlist (rather than replacing the entire playlist). | ||
* | ||
* This is fired synchronously as it does not affect playback. | ||
* | ||
* @typedef {Object} PlaylistRemoveEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistremove" | ||
* | ||
* @property {number} count | ||
* The number of items that were removed. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were removed. | ||
*/ | ||
/** | ||
* Add one or more items to the playlist. | ||
* | ||
* @fires {PlaylistAddEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {string|Object|Array} item | ||
* An item - or array of items - to be added to the playlist. | ||
* | ||
* @param {number} [index] | ||
* If given as a valid value, injects the new playlist item(s) | ||
* starting from that index. Otherwise, the item(s) are appended. | ||
*/ | ||
playlist.add = (items, index) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
index = list.length; | ||
} | ||
if (!Array.isArray(items)) { | ||
items = [items]; | ||
} | ||
list.splice(index, 0, ...preparePlaylistItems(items)); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'add' | ||
}); | ||
player.trigger({ | ||
type: 'playlistadd', | ||
count: items.length, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Remove one or more items from the playlist. | ||
* | ||
* @fires {PlaylistRemoveEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {number} index | ||
* If a valid index in the current playlist, removes the item at that | ||
* index from the playlist. | ||
* | ||
* If no valid index is given, nothing is removed from the playlist. | ||
* | ||
* @param {number} [count=1] | ||
* The number of items to remove from the playlist. | ||
*/ | ||
playlist.remove = (index, count = 1) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
return; | ||
} | ||
list.splice(index, count); // playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({ | ||
type: 'playlistchange', | ||
action: 'remove' | ||
}); | ||
player.trigger({ | ||
type: 'playlistremove', | ||
count, | ||
index | ||
}); | ||
}; | ||
/** | ||
* Checks if the playlist contains a value. | ||
@@ -873,3 +967,3 @@ * | ||
if (Array.isArray(initialList)) { | ||
playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array. | ||
playlist(initialList, initialIndex); // If there is no initial list given, silently set an empty array. | ||
} else { | ||
@@ -882,3 +976,3 @@ list = []; | ||
var version = "5.0.1"; | ||
var version = "5.1.0"; | ||
@@ -885,0 +979,0 @@ const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin; |
@@ -1,2 +0,2 @@ | ||
/*! @name videojs-playlist @version 5.0.1 @license Apache-2.0 */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js")):"function"==typeof define&&define.amd?define(["video.js"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).videojsPlaylist=t(e.videojs)}(this,(function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=t(e);let n=e=>{const t=e.playlist.autoadvance_;t.timeout&&e.clearTimeout(t.timeout),t.trigger&&e.off("ended",t.trigger),t.timeout=null,t.trigger=null};const l=(e,t)=>{var r;(n(e),"number"==typeof(r=t)&&!isNaN(r)&&r>=0&&r<1/0)?(e.playlist.autoadvance_.delay=t,e.playlist.autoadvance_.trigger=function(){const r=()=>l(e,t);e.one("play",r),e.playlist.autoadvance_.timeout=e.setTimeout((()=>{n(e),e.off("play",r),e.playlist.next()}),1e3*t)},e.one("ended",e.playlist.autoadvance_.trigger)):e.playlist.autoadvance_.delay=null},i=e=>!!e&&"object"==typeof e,a=(e,t)=>{let r=e,n=t;return"object"==typeof e&&(r=e.src),"object"==typeof t&&(n=t.src),/^\/\//.test(r)&&(n=n.slice(n.indexOf("//"))),/^\/\//.test(n)&&(r=r.slice(r.indexOf("//"))),r===n},o=(e,t)=>{for(let r=0;r<e.length;r++){const n=e[r].sources;if(Array.isArray(n))for(let e=0;e<n.length;e++){const l=n[e];if(l&&a(l,t))return r}}return-1};function s(e,t,a=0){let s=null,u=!1;const c=e.playlist=(t,r=0)=>{if(u)throw new Error("do not call playlist() during a playlist change");if(Array.isArray(t)){const n=Array.isArray(s)?s.slice():null,l=t.slice();s=l.slice(),s.filter((e=>i(e))).length!==s.length&&(s=(e=>{const t=[];let r;return e.forEach((e=>{i(e)?r=e:(r=Object(e),r.originalValue=e),t.push(r)})),t})(s)),(e=>{let t=1;e.forEach((e=>{e.playlistItemId_=t++}))})(s),u=!0,e.trigger({type:"duringplaylistchange",nextIndex:r,nextPlaylist:l,previousIndex:c.currentIndex_,previousPlaylist:n||[]}),u=!1,-1!==r&&c.currentItem(r),n&&e.setTimeout((()=>{e.trigger("playlistchange")}),0)}return s.map((e=>e.originalValue||e)).slice()};return e.on("loadstart",(()=>{-1===c.currentItem()&&n(e)})),c.currentIndex_=-1,c.player_=e,c.autoadvance_={},c.repeat_=!1,c.currentPlaylistItemId_=null,c.currentItem=t=>{if(u)return c.currentIndex_;if("number"==typeof t&&c.currentIndex_!==t&&t>=0&&t<s.length)return c.currentIndex_=t,((e,t)=>{const r=!e.paused()||e.ended();e.trigger("beforeplaylistitem",t.originalValue||t),t.playlistItemId_&&(e.playlist.currentPlaylistItemId_=t.playlistItemId_),e.poster(t.poster||""),e.src(t.sources),(e=>{const t=e.remoteTextTracks();let r=t&&t.length||0;for(;r--;)e.removeRemoteTextTrack(t[r])})(e),e.ready((()=>{if((t.textTracks||[]).forEach(e.addRemoteTextTrack.bind(e)),e.trigger("playlistitem",t.originalValue||t),r){const t=e.play();void 0!==t&&"function"==typeof t.then&&t.then(null,(e=>{}))}l(e,e.playlist.autoadvance_.delay)}))})(c.player_,s[c.currentIndex_]),t>0&&e.poster(""),c.currentIndex_;const r=c.player_.currentSrc()||"";if(c.currentPlaylistItemId_){const e=((e,t)=>{for(let r=0;r<e.length;r++)if(e[r].playlistItemId_===t)return r;return-1})(s,c.currentPlaylistItemId_),t=s[e];if(t&&Array.isArray(t.sources)&&o([t],r)>-1)return c.currentIndex_=e,c.currentIndex_;c.currentPlaylistItemId_=null}return c.currentIndex_=c.indexOf(r),c.currentIndex_},c.contains=e=>-1!==c.indexOf(e),c.indexOf=e=>{if("string"==typeof e)return o(s,e);const t=Array.isArray(e)?e:e.sources;for(let e=0;e<t.length;e++){const r=t[e];if("string"==typeof r)return o(s,r);if(r.src)return o(s,r.src)}return-1},c.currentIndex=()=>c.currentItem(),c.lastIndex=()=>s.length-1,c.nextIndex=()=>{const e=c.currentItem();if(-1===e)return-1;const t=c.lastIndex();return c.repeat_&&e===t?0:Math.min(e+1,t)},c.previousIndex=()=>{const e=c.currentItem();return-1===e?-1:c.repeat_&&0===e?c.lastIndex():Math.max(e-1,0)},c.first=()=>{if(u)return;const e=c.currentItem(0);if(s.length)return s[e].originalValue||s[e];c.currentIndex_=-1},c.last=()=>{if(u)return;const e=c.currentItem(c.lastIndex());if(s.length)return s[e].originalValue||s[e];c.currentIndex_=-1},c.next=()=>{if(u)return;const e=c.nextIndex();if(e!==c.currentIndex_){const t=c.currentItem(e);return s[t].originalValue||s[t]}},c.previous=()=>{if(u)return;const e=c.previousIndex();if(e!==c.currentIndex_){const t=c.currentItem(e);return s[t].originalValue||s[t]}},c.autoadvance=e=>{l(c.player_,e)},c.repeat=e=>void 0===e?c.repeat_:"boolean"==typeof e?(c.repeat_=!!e,c.repeat_):void r.default.log.error("videojs-playlist: Invalid value for repeat",e),c.sort=t=>{s.length&&(s.sort(t),u||e.trigger("playlistsorted"))},c.reverse=()=>{s.length&&(s.reverse(),u||e.trigger("playlistsorted"))},c.shuffle=({rest:t}={})=>{let r=0,n=s;t&&(r=c.currentIndex_+1,n=s.slice(r)),n.length<=1||((e=>{let t=-1;const r=e.length-1;for(;++t<e.length;){const n=t+Math.floor(Math.random()*(r-t+1)),l=e[n];e[n]=e[t],e[t]=l}})(n),t&&s.splice(...[r,n.length].concat(n)),u||e.trigger("playlistsorted"))},Array.isArray(t)?c(t.slice(),a):s=[],c}const u=function(e,t){s(this,e,t)};return(r.default.registerPlugin||r.default.plugin)("playlist",u),u.VERSION="5.0.1",u})); | ||
/*! @name videojs-playlist @version 5.1.0 @license Apache-2.0 */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js")):"function"==typeof define&&define.amd?define(["video.js"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).videojsPlaylist=t(e.videojs)}(this,(function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=t(e);let n=e=>{const t=e.playlist.autoadvance_;t.timeout&&e.clearTimeout(t.timeout),t.trigger&&e.off("ended",t.trigger),t.timeout=null,t.trigger=null};const l=(e,t)=>{var r;(n(e),"number"==typeof(r=t)&&!isNaN(r)&&r>=0&&r<1/0)?(e.playlist.autoadvance_.delay=t,e.playlist.autoadvance_.trigger=function(){const r=()=>l(e,t);e.one("play",r),e.playlist.autoadvance_.timeout=e.setTimeout((()=>{n(e),e.off("play",r),e.playlist.next()}),1e3*t)},e.one("ended",e.playlist.autoadvance_.trigger)):e.playlist.autoadvance_.delay=null};let i=1;const a=e=>{let t=e;return e&&"object"==typeof e||(t=Object(e),t.originalValue=e),t.playlistItemId_=i++,t},o=e=>e.map(a),s=(e,t)=>{let r=e,n=t;return"object"==typeof e&&(r=e.src),"object"==typeof t&&(n=t.src),/^\/\//.test(r)&&(n=n.slice(n.indexOf("//"))),/^\/\//.test(n)&&(r=r.slice(r.indexOf("//"))),r===n},u=(e,t)=>{for(let r=0;r<e.length;r++){const n=e[r].sources;if(Array.isArray(n))for(let e=0;e<n.length;e++){const l=n[e];if(l&&s(l,t))return r}}return-1};function c(e,t,i=0){let a=null,s=!1;const c=e.playlist=(t,r=0)=>{if(s)throw new Error("do not call playlist() during a playlist change");if(Array.isArray(t)){const n=Array.isArray(a)?a.slice():null;a=o(t),s=!0,e.trigger({type:"duringplaylistchange",nextIndex:r,nextPlaylist:t,previousIndex:c.currentIndex_,previousPlaylist:n||[]}),s=!1,-1!==r&&c.currentItem(r),n&&e.setTimeout((()=>{e.trigger({type:"playlistchange",action:"change"})}),0)}return a.map((e=>e.originalValue||e))};return e.on("loadstart",(()=>{-1===c.currentItem()&&n(e)})),c.currentIndex_=-1,c.player_=e,c.autoadvance_={},c.repeat_=!1,c.currentPlaylistItemId_=null,c.currentItem=t=>{if(s)return c.currentIndex_;if("number"==typeof t&&c.currentIndex_!==t&&t>=0&&t<a.length)return c.currentIndex_=t,((e,t)=>{const r=!e.paused()||e.ended();e.trigger("beforeplaylistitem",t.originalValue||t),t.playlistItemId_&&(e.playlist.currentPlaylistItemId_=t.playlistItemId_),e.poster(t.poster||""),e.src(t.sources),(e=>{const t=e.remoteTextTracks();let r=t&&t.length||0;for(;r--;)e.removeRemoteTextTrack(t[r])})(e),e.ready((()=>{if((t.textTracks||[]).forEach(e.addRemoteTextTrack.bind(e)),e.trigger("playlistitem",t.originalValue||t),r){const t=e.play();void 0!==t&&"function"==typeof t.then&&t.then(null,(e=>{}))}l(e,e.playlist.autoadvance_.delay)}))})(c.player_,a[c.currentIndex_]),t>0&&e.poster(""),c.currentIndex_;const r=c.player_.currentSrc()||"";if(c.currentPlaylistItemId_){const e=((e,t)=>{for(let r=0;r<e.length;r++)if(e[r].playlistItemId_===t)return r;return-1})(a,c.currentPlaylistItemId_),t=a[e];if(t&&Array.isArray(t.sources)&&u([t],r)>-1)return c.currentIndex_=e,c.currentIndex_;c.currentPlaylistItemId_=null}return c.currentIndex_=c.indexOf(r),c.currentIndex_},c.add=(t,r)=>{if(s)throw new Error("cannot modify a playlist that is currently changing");("number"!=typeof r||r<0||r>a.length)&&(r=a.length),Array.isArray(t)||(t=[t]),a.splice(r,0,...o(t)),e.trigger({type:"playlistchange",action:"add"}),e.trigger({type:"playlistadd",count:t.length,index:r})},c.remove=(t,r=1)=>{if(s)throw new Error("cannot modify a playlist that is currently changing");"number"!=typeof t||t<0||t>a.length||(a.splice(t,r),e.trigger({type:"playlistchange",action:"remove"}),e.trigger({type:"playlistremove",count:r,index:t}))},c.contains=e=>-1!==c.indexOf(e),c.indexOf=e=>{if("string"==typeof e)return u(a,e);const t=Array.isArray(e)?e:e.sources;for(let e=0;e<t.length;e++){const r=t[e];if("string"==typeof r)return u(a,r);if(r.src)return u(a,r.src)}return-1},c.currentIndex=()=>c.currentItem(),c.lastIndex=()=>a.length-1,c.nextIndex=()=>{const e=c.currentItem();if(-1===e)return-1;const t=c.lastIndex();return c.repeat_&&e===t?0:Math.min(e+1,t)},c.previousIndex=()=>{const e=c.currentItem();return-1===e?-1:c.repeat_&&0===e?c.lastIndex():Math.max(e-1,0)},c.first=()=>{if(s)return;const e=c.currentItem(0);if(a.length)return a[e].originalValue||a[e];c.currentIndex_=-1},c.last=()=>{if(s)return;const e=c.currentItem(c.lastIndex());if(a.length)return a[e].originalValue||a[e];c.currentIndex_=-1},c.next=()=>{if(s)return;const e=c.nextIndex();if(e!==c.currentIndex_){const t=c.currentItem(e);return a[t].originalValue||a[t]}},c.previous=()=>{if(s)return;const e=c.previousIndex();if(e!==c.currentIndex_){const t=c.currentItem(e);return a[t].originalValue||a[t]}},c.autoadvance=e=>{l(c.player_,e)},c.repeat=e=>void 0===e?c.repeat_:"boolean"==typeof e?(c.repeat_=!!e,c.repeat_):void r.default.log.error("videojs-playlist: Invalid value for repeat",e),c.sort=t=>{a.length&&(a.sort(t),s||e.trigger("playlistsorted"))},c.reverse=()=>{a.length&&(a.reverse(),s||e.trigger("playlistsorted"))},c.shuffle=({rest:t}={})=>{let r=0,n=a;t&&(r=c.currentIndex_+1,n=a.slice(r)),n.length<=1||((e=>{let t=-1;const r=e.length-1;for(;++t<e.length;){const n=t+Math.floor(Math.random()*(r-t+1)),l=e[n];e[n]=e[t],e[t]=l}})(n),t&&a.splice(...[r,n.length].concat(n)),s||e.trigger("playlistsorted"))},Array.isArray(t)?c(t,i):a=[],c}const d=function(e,t){c(this,e,t)};return(r.default.registerPlugin||r.default.plugin)("playlist",d),d.VERSION="5.1.0",d})); |
202
docs/api.md
@@ -5,10 +5,4 @@ # video.js Playlist API | ||
A playlist is an array of playlist items. A playlist item is an object with the following properties: | ||
A playlist is an array of playlist items. Usually, a playlist item is an object with an array of `sources`, but it could also be a primitive value like a string. | ||
| Property | Type | Optional | Description | | ||
| ------------ | ------ | -------- | -------------------------------------------------- | | ||
| `sources` | Array | | An array of sources that video.js understands. | | ||
| `poster` | String | ✓ | A poster image to display for these sources. | | ||
| `textTracks` | Array | ✓ | An array of text tracks that Video.js understands. | | ||
## Methods | ||
@@ -24,30 +18,30 @@ ### `player.playlist([Array newList], [Number newIndex]) -> Array` | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://vjs.zencdn.net/v/oceans.mp4', | ||
src: '//vjs.zencdn.net/v/oceans.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://www.videojs.com/img/poster.jpg' | ||
poster: '//www.videojs.com/img/poster.jpg' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/movie.mp4', | ||
src: '//media.w3.org/2010/05/bunny/movie.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/video/movie_300.mp4', | ||
src: '//media.w3.org/2010/05/video/movie_300.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/video/poster.png' | ||
poster: '//media.w3.org/2010/05/video/poster.png' | ||
}]); | ||
@@ -58,6 +52,6 @@ // [{ ... }, ... ] | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/video/movie_300.mp4', | ||
src: '//media.w3.org/2010/05/video/movie_300.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/video/poster.png' | ||
poster: '//media.w3.org/2010/05/video/poster.png' | ||
}]); | ||
@@ -73,12 +67,12 @@ // [{ ... }] | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}], 1); | ||
@@ -99,12 +93,12 @@ // [{ ... }] | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}]; | ||
@@ -120,3 +114,3 @@ | ||
player.src('http://example.com/video.mp4'); | ||
player.src('//example.com/video.mp4'); | ||
player.playlist.currentItem(); | ||
@@ -126,2 +120,82 @@ // -1 | ||
#### `player.playlist.add(String|Object|Array items, [Number index])` | ||
Adds one or more items to the current playlist without replacing the playlist. | ||
Fires the `playlistadd` event. | ||
Calling this method during the `duringplaylistchange` event throws an error. | ||
```js | ||
var samplePlaylist = [{ | ||
sources: [{ | ||
src: 'sintel.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}]; | ||
player.playlist(samplePlaylist); | ||
// Playlist will contain two items after this call: sintel.mp4, bbb.mp4 | ||
player.add({ | ||
sources: [{ | ||
src: 'bbb.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}); | ||
// Playlist will contain four items after this call: sintel.mp4, tears.mp4, test.mp4, and bbb.mp4 | ||
player.add([{ | ||
sources: [{ | ||
src: 'tears.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: 'test.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}], 1); | ||
``` | ||
#### `player.playlist.remove(Number index, [Number count=1])` | ||
Removes one or more items from the current playlist without replacing the playlist. By default, if `count` is not provided, one item will be removed. | ||
Fires the `playlistremove` event. | ||
Calling this method during the `duringplaylistchange` event throws an error. | ||
```js | ||
var samplePlaylist = [{ | ||
sources: [{ | ||
src: 'sintel.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: 'bbb.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: 'tears.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}, { | ||
sources: [{ | ||
src: 'test.mp4', | ||
type: 'video/mp4' | ||
}] | ||
}]; | ||
player.playlist(samplePlaylist); | ||
// Playlist will contain three items after this call: bbb.mp4, tears.mp4, and test.mp4 | ||
player.remove(0); | ||
// Playlist will contain one item after this call: bbb.mp4 | ||
player.remove(1, 2); | ||
``` | ||
#### `player.playlist.contains(String|Object|Array value) -> Boolean` | ||
@@ -134,7 +208,7 @@ | ||
```js | ||
player.playlist.contains('http://media.w3.org/2010/05/sintel/trailer.mp4'); | ||
player.playlist.contains('//media.w3.org/2010/05/sintel/trailer.mp4'); | ||
// true | ||
player.playlist.contains([{ | ||
src: 'http://media.w3.org/2010/05/sintel/poster.png', | ||
src: '//media.w3.org/2010/05/sintel/poster.png', | ||
type: 'image/png' | ||
@@ -146,3 +220,3 @@ }]); | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
@@ -161,7 +235,7 @@ }] | ||
```js | ||
player.playlist.indexOf('http://media.w3.org/2010/05/bunny/trailer.mp4'); | ||
player.playlist.indexOf('//media.w3.org/2010/05/bunny/trailer.mp4'); | ||
// 1 | ||
player.playlist.indexOf([{ | ||
src: 'http://media.w3.org/2010/05/bunny/movie.mp4', | ||
src: '//media.w3.org/2010/05/bunny/movie.mp4', | ||
type: 'video/mp4' | ||
@@ -173,3 +247,3 @@ }]); | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/video/movie_300.mp4', | ||
src: '//media.w3.org/2010/05/video/movie_300.mp4', | ||
type: 'video/mp4' | ||
@@ -188,12 +262,12 @@ }] | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}]; | ||
@@ -216,12 +290,12 @@ | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}]; | ||
@@ -242,3 +316,3 @@ | ||
player.src('http://example.com/video.mp4'); | ||
player.src('//example.com/video.mp4'); | ||
player.playlist.nextIndex(); | ||
@@ -259,12 +333,12 @@ // -1 | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}]; | ||
@@ -285,3 +359,3 @@ | ||
player.src('http://example.com/video.mp4'); | ||
player.src('//example.com/video.mp4'); | ||
player.playlist.previousIndex(); | ||
@@ -298,12 +372,12 @@ // -1 | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4', | ||
src: '//media.w3.org/2010/05/sintel/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/sintel/poster.png' | ||
poster: '//media.w3.org/2010/05/sintel/poster.png' | ||
}, { | ||
sources: [{ | ||
src: 'http://media.w3.org/2010/05/bunny/trailer.mp4', | ||
src: '//media.w3.org/2010/05/bunny/trailer.mp4', | ||
type: 'video/mp4' | ||
}], | ||
poster: 'http://media.w3.org/2010/05/bunny/poster.png' | ||
poster: '//media.w3.org/2010/05/bunny/poster.png' | ||
}]; | ||
@@ -484,5 +558,5 @@ | ||
This event is fired _asynchronously_ whenever the contents of the playlist are changed (i.e., when `player.playlist()` is called with an argument) - except the first time. | ||
This event is fired whenever the contents of the playlist are changed (i.e., when `player.playlist()` is called with an argument) - except the first time. Additionally, it is fired when item(s) are added or removed from the playlist. | ||
It is fired asynchronously to let the browser start loading the first video in the new playlist. | ||
In cases where a change to the playlist may result in a new video source being loaded, it is fired asynchronously to let the browser start loading the first video in the new playlist. | ||
@@ -501,2 +575,12 @@ ```js | ||
#### `action` | ||
Each `playlistchange` event object has an additional `action` property. This can help you identify the action that caused the `playlistchange` event to be triggered. It will be one of: | ||
* `add`: One or more items were added to the playlist | ||
* `change`: The entire playlist was replaced | ||
* `remove`: One or more items were removed from the playlist | ||
This is considered temporary/deprecated behavior because future implementations should only fire the `playlistadd` and `playlistremove` events. | ||
#### Backward Compatibility | ||
@@ -516,2 +600,10 @@ | ||
### `playlistadd` | ||
One or more items were added to the playlist via the `playlist.add()` method. | ||
### `playlistremove` | ||
One or more items were removed from the playlist via the `playlist.remove()` method. | ||
### `beforeplaylistitem` | ||
@@ -527,2 +619,2 @@ | ||
This event is fired when any method is called that changes the order of playlist items - `sort()`, `reverse()`, or `shuffle()`. | ||
This event is fired when any method is called that changes the order of playlist items - `sort()`, `reverse()`, or `shuffle()`. |
{ | ||
"name": "videojs-playlist", | ||
"version": "5.0.1", | ||
"version": "5.1.0", | ||
"description": "Playlist plugin for Video.js", | ||
@@ -48,3 +48,3 @@ "main": "dist/videojs-playlist.cjs.js", | ||
"global": "^4.3.2", | ||
"video.js": "^6 || ^7" | ||
"video.js": "^6 || ^7 || ^8" | ||
}, | ||
@@ -51,0 +51,0 @@ "devDependencies": { |
@@ -5,52 +5,39 @@ import videojs from 'video.js'; | ||
// Shared incrementing GUID for playlist items. | ||
let guid = 1; | ||
/** | ||
* Returns whether a playlist item is an object of any kind, excluding null. | ||
* Transform any primitive playlist item value into an object. | ||
* | ||
* @private | ||
* For non-object values, adds a property to the transformed item containing | ||
* original value passed. | ||
* | ||
* @param {Object} | ||
* value to be checked | ||
* For all items, add a unique ID to each playlist item object. This id is | ||
* used to determine the index of an item in the playlist array in cases where | ||
* there are multiple otherwise identical items. | ||
* | ||
* @return {boolean} | ||
* The result | ||
*/ | ||
const isItemObject = (value) => { | ||
return !!value && typeof value === 'object'; | ||
}; | ||
/** | ||
* Look through an array of playlist items and transform any primitive | ||
* as well as null values to objects. This method also adds a property | ||
* to the transformed item containing original value passed in an input list. | ||
* @param {Object} newItem | ||
* An playlist item object, but accepts any value. | ||
* | ||
* @private | ||
* | ||
* @param {Array} arr | ||
* An array of playlist items | ||
* | ||
* @return {Array} | ||
* A new array with transformed items | ||
* @return {Object} | ||
*/ | ||
const transformPrimitiveItems = (arr) => { | ||
const list = []; | ||
let tempItem; | ||
const preparePlaylistItem = (newItem) => { | ||
let item = newItem; | ||
arr.forEach(item => { | ||
if (!isItemObject(item)) { | ||
tempItem = Object(item); | ||
tempItem.originalValue = item; | ||
} else { | ||
tempItem = item; | ||
} | ||
if (!newItem || typeof newItem !== 'object') { | ||
list.push(tempItem); | ||
}); | ||
// Casting to an Object in this way allows primitives to retain their | ||
// primitiveness (i.e. they will be cast back to primitives as needed). | ||
item = Object(newItem); | ||
item.originalValue = newItem; | ||
} | ||
return list; | ||
item.playlistItemId_ = guid++; | ||
return item; | ||
}; | ||
/** | ||
* Generate a unique id for each playlist item object. This id will be used to determine | ||
* index of an item in the playlist array for cases where there are multiple items with | ||
* the same source set. | ||
* Look through an array of playlist items and passes them to | ||
* preparePlaylistItem. | ||
* | ||
@@ -61,11 +48,8 @@ * @private | ||
* An array of playlist items | ||
* | ||
* @return {Array} | ||
* A new array with transformed items | ||
*/ | ||
const generatePlaylistItemId = (arr) => { | ||
let guid = 1; | ||
const preparePlaylistItems = (arr) => arr.map(preparePlaylistItem); | ||
arr.forEach(item => { | ||
item.playlistItemId_ = guid++; | ||
}); | ||
}; | ||
/** | ||
@@ -241,3 +225,3 @@ * Look through an array of playlist items for a specific playlist item id. | ||
*/ | ||
const playlist = player.playlist = (newList, newIndex = 0) => { | ||
const playlist = player.playlist = (nextPlaylist, newIndex = 0) => { | ||
if (changing) { | ||
@@ -247,20 +231,9 @@ throw new Error('do not call playlist() during a playlist change'); | ||
if (Array.isArray(newList)) { | ||
if (Array.isArray(nextPlaylist)) { | ||
// @todo - Simplify this to `list.slice()` for v5. | ||
const previousPlaylist = Array.isArray(list) ? list.slice() : null; | ||
const nextPlaylist = newList.slice(); | ||
list = nextPlaylist.slice(); | ||
list = preparePlaylistItems(nextPlaylist); | ||
// Transform any primitive and null values in an input list to objects | ||
if (list.filter(item => isItemObject(item)).length !== list.length) { | ||
list = transformPrimitiveItems(list); | ||
} | ||
// Add unique id to each playlist item. This id will be used | ||
// to determine index in cases where there are more than one | ||
// identical sources in the playlist. | ||
generatePlaylistItemId(list); | ||
// Mark the playlist as changing during the duringplaylistchange lifecycle. | ||
@@ -294,3 +267,3 @@ changing = true; | ||
player.setTimeout(() => { | ||
player.trigger('playlistchange'); | ||
player.trigger({type: 'playlistchange', action: 'change'}); | ||
}, 0); | ||
@@ -301,4 +274,4 @@ } | ||
// Always return a shallow clone of the playlist list. | ||
// We also want to return originalValue if any item in the list has it. | ||
return list.map((item) => item.originalValue || item).slice(); | ||
// We also want to return originalValue if any item in the list has it. | ||
return list.map((item) => item.originalValue || item); | ||
}; | ||
@@ -392,2 +365,102 @@ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are added to the current | ||
* playlist (rather than replacing the entire playlist). | ||
* | ||
* Unlike playlistchange, this is fired synchronously as it does not | ||
* affect playback. | ||
* | ||
* @typedef {Object} PlaylistAddEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistadd" | ||
* | ||
* @property {number} count | ||
* The number of items that were added. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were added. | ||
*/ | ||
/** | ||
* A custom DOM event that is fired when new item(s) are removed from the | ||
* current playlist (rather than replacing the entire playlist). | ||
* | ||
* This is fired synchronously as it does not affect playback. | ||
* | ||
* @typedef {Object} PlaylistRemoveEvent | ||
* @see [CustomEvent Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} | ||
* @property {string} type | ||
* Always "playlistremove" | ||
* | ||
* @property {number} count | ||
* The number of items that were removed. | ||
* | ||
* @property {number} index | ||
* The starting index where item(s) were removed. | ||
*/ | ||
/** | ||
* Add one or more items to the playlist. | ||
* | ||
* @fires {PlaylistAddEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {string|Object|Array} item | ||
* An item - or array of items - to be added to the playlist. | ||
* | ||
* @param {number} [index] | ||
* If given as a valid value, injects the new playlist item(s) | ||
* starting from that index. Otherwise, the item(s) are appended. | ||
*/ | ||
playlist.add = (items, index) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
index = list.length; | ||
} | ||
if (!Array.isArray(items)) { | ||
items = [items]; | ||
} | ||
list.splice(index, 0, ...preparePlaylistItems(items)); | ||
// playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({type: 'playlistchange', action: 'add'}); | ||
player.trigger({type: 'playlistadd', count: items.length, index}); | ||
}; | ||
/** | ||
* Remove one or more items from the playlist. | ||
* | ||
* @fires {PlaylistRemoveEvent} | ||
* @throws {Error} | ||
* If called during the duringplaylistchange event, throws an error. | ||
* | ||
* @param {number} index | ||
* If a valid index in the current playlist, removes the item at that | ||
* index from the playlist. | ||
* | ||
* If no valid index is given, nothing is removed from the playlist. | ||
* | ||
* @param {number} [count=1] | ||
* The number of items to remove from the playlist. | ||
*/ | ||
playlist.remove = (index, count = 1) => { | ||
if (changing) { | ||
throw new Error('cannot modify a playlist that is currently changing'); | ||
} | ||
if (typeof index !== 'number' || index < 0 || index > list.length) { | ||
return; | ||
} | ||
list.splice(index, count); | ||
// playlistchange is triggered synchronously in this case because it does | ||
// not change the current media source | ||
player.trigger({type: 'playlistchange', action: 'remove'}); | ||
player.trigger({type: 'playlistremove', count, index}); | ||
}; | ||
/** | ||
* Checks if the playlist contains a value. | ||
@@ -731,3 +804,3 @@ * | ||
if (Array.isArray(initialList)) { | ||
playlist(initialList.slice(), initialIndex); | ||
playlist(initialList, initialIndex); | ||
@@ -734,0 +807,0 @@ // If there is no initial list given, silently set an empty array. |
@@ -733,2 +733,3 @@ import QUnit from 'qunit'; | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'change'); | ||
}); | ||
@@ -911,1 +912,158 @@ | ||
}); | ||
QUnit.test('playlist.add will append an item by default', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistadd'], spy); | ||
playlist.add(4); | ||
assert.deepEqual(playlist(), [1, 2, 3, 4]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'add'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistadd'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 3); | ||
assert.strictEqual(spy.secondCall.args[0].count, 1); | ||
}); | ||
QUnit.test('playlist.add can insert an item at a specific index', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistadd'], spy); | ||
playlist.add(4, 1); | ||
assert.deepEqual(playlist(), [1, 4, 2, 3]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'add'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistadd'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 1); | ||
assert.strictEqual(spy.secondCall.args[0].count, 1); | ||
}); | ||
QUnit.test('playlist.add appends when specified index is out of bounds', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistadd'], spy); | ||
playlist.add(4, 10); | ||
assert.deepEqual(playlist(), [1, 2, 3, 4]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'add'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistadd'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 3); | ||
assert.strictEqual(spy.secondCall.args[0].count, 1); | ||
}); | ||
QUnit.test('playlist.add can append multiple items', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistadd'], spy); | ||
playlist.add([4, 5, 6]); | ||
assert.deepEqual(playlist(), [1, 2, 3, 4, 5, 6]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'add'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistadd'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 3); | ||
assert.strictEqual(spy.secondCall.args[0].count, 3); | ||
}); | ||
QUnit.test('playlist.add can insert multiple items at a specific index', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistadd'], spy); | ||
playlist.add([4, 5, 6, 7], 1); | ||
assert.deepEqual(playlist(), [1, 4, 5, 6, 7, 2, 3]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'add'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistadd'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 1); | ||
assert.strictEqual(spy.secondCall.args[0].count, 4); | ||
}); | ||
QUnit.test('playlist.add throws an error duringplaylistchange', function(assert) { | ||
const done = assert.async(); | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
player.on('duringplaylistchange', (e) => { | ||
assert.throws(() => playlist.add(4)); | ||
done(); | ||
}); | ||
playlist([4, 5, 6]); | ||
}); | ||
QUnit.test('playlist.remove can remove an item at an index', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistremove'], spy); | ||
playlist.remove(1); | ||
assert.deepEqual(playlist(), [1, 3]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'remove'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistremove'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 1); | ||
assert.strictEqual(spy.secondCall.args[0].count, 1); | ||
}); | ||
QUnit.test('playlist.remove does nothing when index is out of range', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistremove'], spy); | ||
playlist.remove(4); | ||
assert.deepEqual(playlist(), [1, 2, 3]); | ||
assert.strictEqual(spy.callCount, 0); | ||
}); | ||
QUnit.test('playlist.remove can remove multiple items at an index', function(assert) { | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
const spy = sinon.spy(); | ||
this.clock.tick(1); | ||
player.on(['playlistchange', 'playlistremove'], spy); | ||
playlist.remove(1, 2); | ||
assert.deepEqual(playlist(), [1]); | ||
assert.strictEqual(spy.callCount, 2); | ||
assert.strictEqual(spy.firstCall.args[0].type, 'playlistchange'); | ||
assert.strictEqual(spy.firstCall.args[0].action, 'remove'); | ||
assert.strictEqual(spy.secondCall.args[0].type, 'playlistremove'); | ||
assert.strictEqual(spy.secondCall.args[0].index, 1); | ||
assert.strictEqual(spy.secondCall.args[0].count, 2); | ||
}); | ||
QUnit.test('playlist.remove throws an error duringplaylistchange', function(assert) { | ||
const done = assert.async(); | ||
const player = playerProxyMaker(); | ||
const playlist = playlistMaker(player, [1, 2, 3]); | ||
player.on('duringplaylistchange', (e) => { | ||
assert.throws(() => playlist.remove(0)); | ||
done(); | ||
}); | ||
playlist([4, 5, 6]); | ||
}); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
423251
26
14156
+ Added@videojs/http-streaming@3.16.0(transitive)
+ Added@videojs/vhs-utils@4.1.1(transitive)
+ Added@videojs/xhr@2.7.0(transitive)
+ Addedaes-decrypter@4.0.2(transitive)
+ Addedm3u8-parser@7.2.0(transitive)
+ Addedmpd-parser@1.3.1(transitive)
+ Addedmux.js@7.1.0(transitive)
+ Addedvideo.js@8.20.0(transitive)
+ Addedvideojs-contrib-quality-levels@4.1.0(transitive)
+ Addedvideojs-font@4.2.0(transitive)
- Removed@videojs/http-streaming@2.16.3(transitive)
- Removed@videojs/vhs-utils@3.0.5(transitive)
- Removed@videojs/xhr@2.6.0(transitive)
- Removedaes-decrypter@3.1.3(transitive)
- Removedindividual@2.0.0(transitive)
- Removedkeycode@2.2.1(transitive)
- Removedm3u8-parser@4.8.0(transitive)
- Removedmpd-parser@0.22.1(transitive)
- Removedmux.js@6.0.1(transitive)
- Removedrust-result@1.0.0(transitive)
- Removedsafe-json-parse@4.0.0(transitive)
- Removedurl-toolkit@2.2.5(transitive)
- Removedvideo.js@7.21.6(transitive)
- Removedvideojs-font@3.2.0(transitive)
Updatedvideo.js@^6 || ^7 || ^8