videojs-playlist-ui
Version:
A user interface for the videojs-playlist API
2,168 lines (2,090 loc) • 171 kB
JavaScript
/*! @name videojs-playlist-ui @version 5.0.0 @license Apache-2.0 */
(function (QUnit, sinon, videojs) {
'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var QUnit__default = /*#__PURE__*/_interopDefaultLegacy(QUnit);
var sinon__default = /*#__PURE__*/_interopDefaultLegacy(sinon);
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getAugmentedNamespace(n) {
if (n.__esModule) return n;
var a = Object.defineProperty({}, '__esModule', {value: true});
Object.keys(n).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(n, k);
Object.defineProperty(a, k, d.get ? d : {
enumerable: true,
get: function () {
return n[k];
}
});
});
return a;
}
var _nodeResolve_empty = {};
var _nodeResolve_empty$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
'default': _nodeResolve_empty
});
var require$$0 = /*@__PURE__*/getAugmentedNamespace(_nodeResolve_empty$1);
var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof window !== 'undefined' ? window : {};
var minDoc = require$$0;
var doccy;
if (typeof document !== 'undefined') {
doccy = document;
} else {
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
if (!doccy) {
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
}
}
var document_1 = doccy;
var win;
if (typeof window !== "undefined") {
win = window;
} else if (typeof commonjsGlobal !== "undefined") {
win = commonjsGlobal;
} else if (typeof self !== "undefined") {
win = self;
} else {
win = {};
}
var window_1 = win;
/*! @name videojs-playlist @version 5.1.0 @license Apache-2.0 */
/**
* Validates a number of seconds to use as the auto-advance delay.
*
* @private
* @param {number} s
* The number to check
*
* @return {boolean}
* Whether this is a valid second or not
*/
const validSeconds = s => typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
/**
* Resets the auto-advance behavior of a player.
*
* @param {Player} player
* The player to reset the behavior on
*/
let reset = player => {
const aa = player.playlist.autoadvance_;
if (aa.timeout) {
player.clearTimeout(aa.timeout);
}
if (aa.trigger) {
player.off('ended', aa.trigger);
}
aa.timeout = null;
aa.trigger = null;
};
/**
* Sets up auto-advance behavior on a player.
*
* @param {Player} player
* the current player
*
* @param {number} delay
* The number of seconds to wait before each auto-advance.
*
* @return {undefined}
* Used to short circuit function logic
*/
const setup$1 = (player, delay) => {
reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
// called with a valid value.
if (!validSeconds(delay)) {
player.playlist.autoadvance_.delay = null;
return;
}
player.playlist.autoadvance_.delay = delay;
player.playlist.autoadvance_.trigger = function () {
// This calls setup again, which will reset the existing auto-advance and
// set up another auto-advance for the next "ended" event.
const cancelOnPlay = () => setup$1(player, delay); // If there is a "play" event while we're waiting for an auto-advance,
// we need to cancel the auto-advance. This could mean the user seeked
// back into the content or restarted the content. This is reproducible
// with an auto-advance > 0.
player.one('play', cancelOnPlay);
player.playlist.autoadvance_.timeout = player.setTimeout(() => {
reset(player);
player.off('play', cancelOnPlay);
player.playlist.next();
}, delay * 1000);
};
player.one('ended', player.playlist.autoadvance_.trigger);
};
/**
* Removes all remote text tracks from a player.
*
* @param {Player} player
* The player to clear tracks on
*/
const clearTracks = player => {
const tracks = player.remoteTextTracks();
let i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
// `TextTrackList` object is a live DOM list (not an array).
while (i--) {
player.removeRemoteTextTrack(tracks[i]);
}
};
/**
* Plays an item on a player's playlist.
*
* @param {Player} player
* The player to play the item on
*
* @param {Object} item
* A source from the playlist.
*
* @return {Player}
* The player that is now playing the item
*/
const playItem = (player, item) => {
const replay = !player.paused() || player.ended();
player.trigger('beforeplaylistitem', item.originalValue || item);
if (item.playlistItemId_) {
player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
}
player.poster(item.poster || '');
player.src(item.sources);
clearTracks(player);
player.ready(() => {
(item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
player.trigger('playlistitem', item.originalValue || item);
if (replay) {
const playPromise = player.play(); // silence error when a pause interrupts a play request
// on browsers which return a promise
if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
playPromise.then(null, e => {});
}
}
setup$1(player, player.playlist.autoadvance_.delay);
});
return player;
};
let guid = 1;
/**
* Transform any primitive playlist item value into an object.
*
* For non-object values, adds a property to the transformed item containing
* original value passed.
*
* 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.
*
* @param {Object} newItem
* An playlist item object, but accepts any value.
*
* @return {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 passes them to
* preparePlaylistItem.
*
* @private
*
* @param {Array} arr
* An array of playlist items
*
* @return {Array}
* A new array with transformed items
*/
const preparePlaylistItems = arr => arr.map(preparePlaylistItem);
/**
* Look through an array of playlist items for a specific playlist item id.
*
* @private
* @param {Array} list
* An array of playlist items to look through
*
* @param {number} currentItemId
* The current item ID.
*
* @return {number}
* The index of the playlist item or -1 if not found
*/
const indexInPlaylistItemIds = (list, currentItemId) => {
for (let i = 0; i < list.length; i++) {
if (list[i].playlistItemId_ === currentItemId) {
return i;
}
}
return -1;
};
/**
* Given two sources, check to see whether the two sources are equal.
* If both source urls have a protocol, the protocols must match, otherwise, protocols
* are ignored.
*
* @private
* @param {string|Object} source1
* The first source
*
* @param {string|Object} source2
* The second source
*
* @return {boolean}
* The result
*/
const sourceEquals = (source1, source2) => {
let src1 = source1;
let src2 = source2;
if (typeof source1 === 'object') {
src1 = source1.src;
}
if (typeof source2 === 'object') {
src2 = source2.src;
}
if (/^\/\//.test(src1)) {
src2 = src2.slice(src2.indexOf('//'));
}
if (/^\/\//.test(src2)) {
src1 = src1.slice(src1.indexOf('//'));
}
return src1 === src2;
};
/**
* Look through an array of playlist items for a specific `source`;
* checking both the value of elements and the value of their `src`
* property.
*
* @private
* @param {Array} arr
* An array of playlist items to look through
*
* @param {string} src
* The source to look for
*
* @return {number}
* The index of that source or -1
*/
const indexInSources = (arr, src) => {
for (let i = 0; i < arr.length; i++) {
const sources = arr[i].sources;
if (Array.isArray(sources)) {
for (let j = 0; j < sources.length; j++) {
const source = sources[j];
if (source && sourceEquals(source, src)) {
return i;
}
}
}
}
return -1;
};
/**
* Randomize the contents of an array.
*
* @private
* @param {Array} arr
* An array.
*
* @return {Array}
* The same array that was passed in.
*/
const randomize = arr => {
let index = -1;
const lastIndex = arr.length - 1;
while (++index < arr.length) {
const rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
const value = arr[rand];
arr[rand] = arr[index];
arr[index] = value;
}
return arr;
};
/**
* Factory function for creating new playlist implementation on the given player.
*
* API summary:
*
* playlist(['a', 'b', 'c']) // setter
* playlist() // getter
* playlist.currentItem() // getter, 0
* playlist.currentItem(1) // setter, 1
* playlist.next() // 'c'
* playlist.previous() // 'b'
* playlist.first() // 'a'
* playlist.last() // 'c'
* playlist.autoadvance(5) // 5 second delay
* playlist.autoadvance() // cancel autoadvance
*
* @param {Player} player
* The current player
*
* @param {Array=} initialList
* If given, an initial list of sources with which to populate
* the playlist.
*
* @param {number=} initialIndex
* If given, the index of the item in the list that should
* be loaded first. If -1, no video is loaded. If omitted, The
* the first video is loaded.
*
* @return {Function}
* Returns the playlist function specific to the given player.
*/
function factory(player, initialList, initialIndex = 0) {
let list = null;
let changing = false;
/**
* Get/set the playlist for a player.
*
* This function is added as an own property of the player and has its
* own methods which can be called to manipulate the internal state.
*
* @param {Array} [newList]
* If given, a new list of sources with which to populate the
* playlist. Without this, the function acts as a getter.
*
* @param {number} [newIndex]
* If given, the index of the item in the list that should
* be loaded first. If -1, no video is loaded. If omitted, The
* the first video is loaded.
*
* @return {Array}
* The playlist
*/
const playlist = player.playlist = (nextPlaylist, newIndex = 0) => {
if (changing) {
throw new Error('do not call playlist() during a playlist change');
}
if (Array.isArray(nextPlaylist)) {
// @todo - Simplify this to `list.slice()` for v5.
const previousPlaylist = Array.isArray(list) ? list.slice() : null;
list = preparePlaylistItems(nextPlaylist); // Mark the playlist as changing during the duringplaylistchange lifecycle.
changing = true;
player.trigger({
type: 'duringplaylistchange',
nextIndex: newIndex,
nextPlaylist,
previousIndex: playlist.currentIndex_,
// @todo - Simplify this to simply pass along `previousPlaylist` for v5.
previousPlaylist: previousPlaylist || []
});
changing = false;
if (newIndex !== -1) {
playlist.currentItem(newIndex);
} // The only time the previous playlist is null is the first call to this
// function. This allows us to fire the `duringplaylistchange` event
// every time the playlist is populated and to maintain backward
// compatibility by not firing the `playlistchange` event on the initial
// population of the list.
//
// @todo - Remove this condition in preparation for v5.
if (previousPlaylist) {
player.setTimeout(() => {
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.
return list.map(item => item.originalValue || item);
}; // On a new source, if there is no current item, disable auto-advance.
player.on('loadstart', () => {
if (playlist.currentItem() === -1) {
reset(player);
}
});
playlist.currentIndex_ = -1;
playlist.player_ = player;
playlist.autoadvance_ = {};
playlist.repeat_ = false;
playlist.currentPlaylistItemId_ = null;
/**
* Get or set the current item in the playlist.
*
* During the duringplaylistchange event, acts only as a getter.
*
* @param {number} [index]
* If given as a valid value, plays the playlist item at that index.
*
* @return {number}
* The current item index.
*/
playlist.currentItem = index => {
// If the playlist is changing, only act as a getter.
if (changing) {
return playlist.currentIndex_;
} // Act as a setter when the index is given and is a valid number.
if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
playlist.currentIndex_ = index;
playItem(playlist.player_, list[playlist.currentIndex_]); // When playing multiple videos in a playlist the videojs PosterImage
// will be hidden using CSS. However, in some browsers the native poster
// attribute will briefly appear while the new source loads. Prevent
// this by hiding every poster after the first play list item. This
// doesn't cover every use case for showing/hiding the poster, but
// it will significantly improve the user experience.
if (index > 0) {
player.poster('');
}
return playlist.currentIndex_;
}
const src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
// current source URL returned by the player. This is sufficient evidence
// to suggest that the source was set by the playlist plugin. This code
// exists primarily to deal with playlists where multiple items have the
// same source.
if (playlist.currentPlaylistItemId_) {
const indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
const item = list[indexInItemIds]; // Found a match, this is our current index!
if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
playlist.currentIndex_ = indexInItemIds;
return playlist.currentIndex_;
} // If this does not match the current source, null it out so subsequent
// calls can skip this step.
playlist.currentPlaylistItemId_ = null;
} // Finally, if we don't have a valid, current playlist item ID, we can
// auto-detect it based on the player's current source URL.
playlist.currentIndex_ = playlist.indexOf(src);
return playlist.currentIndex_;
};
/**
* 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.
*
* @param {string|Object|Array} value
* The value to check
*
* @return {boolean}
* The result
*/
playlist.contains = value => {
return playlist.indexOf(value) !== -1;
};
/**
* Gets the index of a value in the playlist or -1 if not found.
*
* @param {string|Object|Array} value
* The value to find the index of
*
* @return {number}
* The index or -1
*/
playlist.indexOf = value => {
if (typeof value === 'string') {
return indexInSources(list, value);
}
const sources = Array.isArray(value) ? value : value.sources;
for (let i = 0; i < sources.length; i++) {
const source = sources[i];
if (typeof source === 'string') {
return indexInSources(list, source);
} else if (source.src) {
return indexInSources(list, source.src);
}
}
return -1;
};
/**
* Get the index of the current item in the playlist. This is identical to
* calling `currentItem()` with no arguments.
*
* @return {number}
* The current item index.
*/
playlist.currentIndex = () => playlist.currentItem();
/**
* Get the index of the last item in the playlist.
*
* @return {number}
* The index of the last item in the playlist or -1 if there are no
* items.
*/
playlist.lastIndex = () => list.length - 1;
/**
* Get the index of the next item in the playlist.
*
* @return {number}
* The index of the next item in the playlist or -1 if there is no
* current item.
*/
playlist.nextIndex = () => {
const current = playlist.currentItem();
if (current === -1) {
return -1;
}
const lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
if (playlist.repeat_ && current === lastIndex) {
return 0;
} // Don't go past the end of the playlist.
return Math.min(current + 1, lastIndex);
};
/**
* Get the index of the previous item in the playlist.
*
* @return {number}
* The index of the previous item in the playlist or -1 if there is
* no current item.
*/
playlist.previousIndex = () => {
const current = playlist.currentItem();
if (current === -1) {
return -1;
} // When repeating, loop back to the end of the playlist.
if (playlist.repeat_ && current === 0) {
return playlist.lastIndex();
} // Don't go past the beginning of the playlist.
return Math.max(current - 1, 0);
};
/**
* Plays the first item in the playlist.
*
* @return {Object|undefined}
* Returns undefined and has no side effects if the list is empty.
*/
playlist.first = () => {
if (changing) {
return;
}
const newItem = playlist.currentItem(0);
if (list.length) {
return list[newItem].originalValue || list[newItem];
}
playlist.currentIndex_ = -1;
};
/**
* Plays the last item in the playlist.
*
* @return {Object|undefined}
* Returns undefined and has no side effects if the list is empty.
*/
playlist.last = () => {
if (changing) {
return;
}
const newItem = playlist.currentItem(playlist.lastIndex());
if (list.length) {
return list[newItem].originalValue || list[newItem];
}
playlist.currentIndex_ = -1;
};
/**
* Plays the next item in the playlist.
*
* @return {Object|undefined}
* Returns undefined and has no side effects if on last item.
*/
playlist.next = () => {
if (changing) {
return;
}
const index = playlist.nextIndex();
if (index !== playlist.currentIndex_) {
const newItem = playlist.currentItem(index);
return list[newItem].originalValue || list[newItem];
}
};
/**
* Plays the previous item in the playlist.
*
* @return {Object|undefined}
* Returns undefined and has no side effects if on first item.
*/
playlist.previous = () => {
if (changing) {
return;
}
const index = playlist.previousIndex();
if (index !== playlist.currentIndex_) {
const newItem = playlist.currentItem(index);
return list[newItem].originalValue || list[newItem];
}
};
/**
* Set up auto-advance on the playlist.
*
* @param {number} [delay]
* The number of seconds to wait before each auto-advance.
*/
playlist.autoadvance = delay => {
setup$1(playlist.player_, delay);
};
/**
* Sets `repeat` option, which makes the "next" video of the last video in
* the playlist be the first video in the playlist.
*
* @param {boolean} [val]
* The value to set repeat to
*
* @return {boolean}
* The current value of repeat
*/
playlist.repeat = val => {
if (val === undefined) {
return playlist.repeat_;
}
if (typeof val !== 'boolean') {
videojs__default["default"].log.error('videojs-playlist: Invalid value for repeat', val);
return;
}
playlist.repeat_ = !!val;
return playlist.repeat_;
};
/**
* Sorts the playlist array.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
* @fires playlistsorted
*
* @param {Function} compare
* A comparator function as per the native Array method.
*/
playlist.sort = compare => {
// Bail if the array is empty.
if (!list.length) {
return;
}
list.sort(compare); // If the playlist is changing, don't trigger events.
if (changing) {
return;
}
/**
* Triggered after the playlist is sorted internally.
*
* @event playlistsorted
* @type {Object}
*/
player.trigger('playlistsorted');
};
/**
* Reverses the playlist array.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
* @fires playlistsorted
*/
playlist.reverse = () => {
// Bail if the array is empty.
if (!list.length) {
return;
}
list.reverse(); // If the playlist is changing, don't trigger events.
if (changing) {
return;
}
/**
* Triggered after the playlist is sorted internally.
*
* @event playlistsorted
* @type {Object}
*/
player.trigger('playlistsorted');
};
/**
* Shuffle the contents of the list randomly.
*
* @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
* @fires playlistsorted
* @todo Make the `rest` option default to `true` in v5.0.0.
* @param {Object} [options]
* An object containing shuffle options.
*
* @param {boolean} [options.rest = false]
* By default, the entire playlist is randomized. However, this may
* not be desirable in all cases, such as when a user is already
* watching a video.
*
* When `true` is passed for this option, it will only shuffle
* playlist items after the current item. For example, when on the
* first item, will shuffle the second item and beyond.
*/
playlist.shuffle = ({
rest
} = {}) => {
let index = 0;
let arr = list; // When options.rest is true, start randomization at the item after the
// current item.
if (rest) {
index = playlist.currentIndex_ + 1;
arr = list.slice(index);
} // Bail if the array is empty or too short to shuffle.
if (arr.length <= 1) {
return;
}
randomize(arr); // When options.rest is true, splice the randomized sub-array back into
// the original array.
if (rest) {
list.splice(...[index, arr.length].concat(arr));
} // If the playlist is changing, don't trigger events.
if (changing) {
return;
}
/**
* Triggered after the playlist is sorted internally.
*
* @event playlistsorted
* @type {Object}
*/
player.trigger('playlistsorted');
}; // If an initial list was given, populate the playlist with it.
if (Array.isArray(initialList)) {
playlist(initialList, initialIndex); // If there is no initial list given, silently set an empty array.
} else {
list = [];
}
return playlist;
}
var version$1 = "5.1.0";
const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin;
/**
* The video.js playlist plugin. Invokes the playlist-maker to create a
* playlist function on the specific player.
*
* @param {Array} list
* a list of sources
*
* @param {number} item
* The index to start at
*/
const plugin = function (list, item) {
factory(this, list, item);
};
registerPlugin('playlist', plugin);
plugin.VERSION = version$1;
var version = "5.0.0";
function cov_1mxbo1hw9z() {
var path = "/Users/poneill/dev/videojs-playlist-ui/src/playlist-menu-item.js";
var hash = "5dc6058daf6156feb0b6ade2a9ee32d9474598a1";
var global = new Function("return this")();
var gcv = "__coverage__";
var coverageData = {
path: "/Users/poneill/dev/videojs-playlist-ui/src/playlist-menu-item.js",
statementMap: {
"0": {
start: {
line: 4,
column: 18
},
end: {
line: 4,
column: 51
}
},
"1": {
start: {
line: 6,
column: 24
},
end: {
line: 54,
column: 1
}
},
"2": {
start: {
line: 7,
column: 2
},
end: {
line: 12,
column: 3
}
},
"3": {
start: {
line: 8,
column: 24
},
end: {
line: 8,
column: 53
}
},
"4": {
start: {
line: 10,
column: 4
},
end: {
line: 10,
column: 88
}
},
"5": {
start: {
line: 11,
column: 4
},
end: {
line: 11,
column: 23
}
},
"6": {
start: {
line: 14,
column: 18
},
end: {
line: 14,
column: 51
}
},
"7": {
start: {
line: 16,
column: 2
},
end: {
line: 16,
column: 47
}
},
"8": {
start: {
line: 18,
column: 2
},
end: {
line: 52,
column: 3
}
},
"9": {
start: {
line: 20,
column: 16
},
end: {
line: 20,
column: 45
}
},
"10": {
start: {
line: 22,
column: 4
},
end: {
line: 22,
column: 25
}
},
"11": {
start: {
line: 23,
column: 4
},
end: {
line: 23,
column: 24
}
},
"12": {
start: {
line: 24,
column: 4
},
end: {
line: 24,
column: 17
}
},
"13": {
start: {
line: 25,
column: 4
},
end: {
line: 25,
column: 29
}
},
"14": {
start: {
line: 31,
column: 4
},
end: {
line: 40,
column: 5
}
},
"15": {
start: {
line: 31,
column: 17
},
end: {
line: 31,
column: 18
}
},
"16": {
start: {
line: 32,
column: 22
},
end: {
line: 32,
column: 34
}
},
"17": {
start: {
line: 33,
column: 21
},
end: {
line: 33,
column: 53
}
},
"18": {
start: {
line: 36,
column: 6
},
end: {
line: 38,
column: 7
}
},
"19": {
start: {
line: 37,
column: 8
},
end: {
line: 37,
column: 37
}
},
"20": {
start: {
line: 39,
column: 6
},
end: {
line: 39,
column: 34
}
},
"21": {
start: {
line: 43,
column: 20
},
end: {
line: 43,
column: 51
}
},
"22": {
start: {
line: 44,
column: 16
},
end: {
line: 44,
column: 45
}
},
"23": {
start: {
line: 46,
column: 4
},
end: {
line: 46,
column: 25
}
},
"24": {
start: {
line: 47,
column: 4
},
end: {
line: 47,
column: 17
}
},
"25": {
start: {
line: 48,
column: 4
},
end: {
line: 50,
column: 5
}
},
"26": {
start: {
line: 49,
column: 6
},
end: {
line: 49,
column: 32
}
},
"27": {
start: {
line: 51,
column: 4
},
end: {
line: 51,
column: 29
}
},
"28": {
start: {
line: 53,
column: 2
},
end: {
line: 53,
column: 17
}
},
"29": {
start: {
line: 59,
column: 4
},
end: {
line: 61,
column: 5
}
},
"30": {
start: {
line: 60,
column: 6
},
end: {
line: 60,
column: 84
}
},
"31": {
start: {
line: 63,
column: 4
},
end: {
line: 63,
column: 60
}
},
"32": {
start: {
line: 64,
column: 4
},
end: {
line: 64,
column: 32
}
},
"33": {
start: {
line: 65,
column: 4
},
end: {
line: 65,
column: 34
}
},
"34": {
start: {
line: 67,
column: 4
},
end: {
line: 67,
column: 46
}
},
"35": {
start: {
line: 69,
column: 4
},
end: {
line: 69,
column: 25
}
},
"36": {
start: {
line: 71,
column: 4
},
end: {
line: 71,
column: 56
}
},
"37": {
start: {
line: 72,
column: 4
},
end: {
line: 72,
column: 44
}
},
"38": {
start: {
line: 79,
column: 4
},
end: {
line: 81,
column: 5
}
},
"39": {
start: {
line: 80,
column: 6
},
end: {
line: 80,
column: 33
}
},
"40": {
start: {
line: 85,
column: 4
},
end: {
line: 85,
column: 82
}
},
"41": {
start: {
line: 86,
column: 4
},
end: {
line: 88,
column: 5
}
},
"42": {
start: {
line: 87,
column: 6
},
end: {
line: 87,
column: 26
}
},
"43": {
start: {
line: 92,
column: 15
},
end: {
line: 92,
column: 43
}
},
"44": {
start: {
line: 93,
column: 17
},
end: {
line: 93,
column: 35
}
},
"45": {
start: {
line: 94,
column: 28
},
end: {
line: 94,
column: 57
}
},
"46": {
start: {
line: 96,
column: 4
},
end: {
line: 104,
column: 5
}
},
"47": {
start: {
line: 97,
column: 23
},
end: {
line: 97,
column: 45
}
},
"48": {
start: {
line: 99,
column: 6
},
end: {
line: 103,
column: 9
}
},
"49": {
start: {
line: 100,
column: 22
},
end: {
line: 100,
column: 36
}
},
"50": {
start: {
line: 102,
column: 8
},
end: {
line: 102,
column: 32
}
},
"51": {
start: {
line: 106,
column: 4
},
end: {
line: 106,
column: 39
}
},
"52": {
start: {
line: 107,
column: 4
},
end: {
line: 107,
column: 35
}
},
"53": {
start: {
line: 110,
column: 4
},
end: {
line: 110,
column: 53
}
},
"54": {
start: {
line: 111,
column: 4
},
end: {
line: 111,
column: 35
}
},
"55": {
start: {
line: 114,
column: 4
},
end: {
line: 122,
column: 5
}
},
"56": {
start: {
line: 115,
column: 23
},
end: {
line: 115,
column: 53
}
},
"57": {
start: {
line: 116,
column: 19
},
end: {
line: 116,
column: 57
}
},
"58": {
start: {
line: 118,
column: 6
},
end: {
line: 118,
column: 51
}
},
"59": {
start: {
line: 119,
column: 6
},
end: {
line: 119,
column: 72
}
},
"60": {
start: {
line: 120,
column: 6
},
end: {
line: 120,
column: 58
}
},
"61": {
start: {
line: 121,
column: 6
},
end: {
line: 121,
column: 31
}
},
"62": {
start: {
line: 125,
column: 25
},
end: {
line: 125,
column: 55
}
},
"63": {
start: {
line: 126,
column: 27
},
end: {
line: 126,
column: 55
}
},
"64": {
start: {
line: 128,
column: 4
},
end: {
line: 128,
column: 61
}
},
"65": {
start: {
line: 129,
column: 4
},
end: {
line: 129,
column: 70
}
},
"66": {
start: {
line: 130,
column: 4
},
end: {
line: 130,
column: 55
}
},
"67": {
start: {
line: 131,
column: 4
},
end: {
line: 131,
column: 45
}
},
"68": {
start: {
line: 134,
column: 29
},
end: {
line: 134,
column: 58
}
},
"69": {
start: {
line: 136,
column: 4
},
end: {
line: 136,
column: 64
}
},
"70": {
start: {
line: 137,
column: 4
},
end: {
line: 137,
column: 49
}
},
"71": {
start: {
line: 140,
column: 21
},
end: {
line: 140,
column: 51
}
},
"72": {
start: {
line: 141,
column: 23
},
end: {
line: 141,
column: 47
}
},
"73": {
start: {
line: 143,
column: 4
},
end: {
line: 143,
column: 44
}
},
"74": {
start: {
line: 144,
column: 4
},
end: {
line: 144,
column: 62
}
},
"75": {
start: {
line: 145,
column: 4
},
end: {
line: 145,
column: 47
}
},
"76": {
start: {
line: 146,
column: 4
},
end: {
line: 146,
column: 43
}
},
"77": {
start: {
line: 149,
column: 20
},
end: {
line: 149,
column: 50
}
},
"78": {
start: {
line: 150,
column: 22
},
end: {
line: 150,
column: 66
}
},
"79": {
start: {
line: 152,
column: 4
},
end: {
line: 152,
column: 44
}
},
"80": {
start: {
line: 153,
column: 4
},
end: {
line: 153,
column: 60
}
},
"81": {
start: {
line: 154,
column: 4
},
end: {
line: 154,
column: 45
}
},
"82": {
start: {
line: 155,
column: 4
},
end: {
line: 155,
column: 42
}
},
"83": {
start: {
line: 158,
column: 4
},
end: {
line: 158,
column: 63
}
},
"84": {
start: {
line: 161,
column: 4
},
end: {
line: 169,
column: 5
}
},
"85": {
start: {
line: 162,
column: 28
},
end: {
line: 162,
column: 57
}
},
"86": {
start: {
line: 163,
column: 30
},
end: {
line: 163,
column: 52
}
},
"87": {
start: {
line: 165,
column: 6
},
end: {
line: 165,
column: 59
}
},
"88": {
start: {
line: 166,
column: 6
},
end: {
line: 166,
column: 74
}
},
"89": {
start: {
line: 167,
column: 6
},
end: {
line: 167,
column: 59
}
},
"90": {
start: {
line: 168,
column: 6
},
end: {
line: 168,
column: 50
}
},
"91": {
start: {
line: 171,
column: 4
},
end: {
line: 171,
column: 14
}
},
"92": {
start: {
line: 175,
column: 0
},
end: {
line: 175,
column: 64
}
}
},
fnMap: {
"0": {
name: "(anonymous_0)",
decl: {
start: {
line: 6,
column: 24
},
end: {
line: 6,
column: 25
}
},
loc: {
start: {
line: 6,
column: 44
},
end: {
line: 54,
column: 1
}
},
line: 6
},
"1": {
name: "(anonymous_1)",
decl: {
start: {
line: 58,
column: 2
},
end: {
line: 58,
column: 3
}
},
loc: {
start: {
line: 58,
column: 46
},
end: {
line: 74,
column: 3
}
},
line: 58
},
"2": {
name: "(anonymous_2)",
decl: {
start: {
line: 76,
column: 2
},
end: {
line: 76,
column: 3
}
},
loc: {
start: {
line: 76,
column: 24
},
end: {
line: 82,
column: 3
}
},
line: 76
},
"3": {
name: "(anonymous_3)",
decl: {
start: {
line: 84,
column: 2
},
end: {
line: 84,
column: 3
}
},
loc: {
start: {
line: 84,
column: 29
},
end: {
line: 89,
column: 3
}
},
line: 84
},
"4": {
name: "(anonymous_4)",
decl: {
start: {
line: 91,
column: 2
},
end: {
line: 91,
column: 3
}
},
loc: {
start: {
line: 91,
column: 13
},
end: {
line: 172,
column: 3
}
},
line: 91
},
"5": {
name: "(anonymous_5)",
decl: {
start: {
line: 99,
column: 23
},
end: {
line: 99,
column: 24
}
},
loc: {
start: {
line: 99,
column: 30
},
end: {
line: 103,
column: 7
}
},
line: 99
}
},
branchMap: {
"0": {
loc: {
start: {
line: 7,
column: 2
},
end: {
line: 12,
column: 3
}
},
type: "if",
locations: [{
start: {
line: 7,
column: 2
},
end: {
line: 12,
column: 3
}
}, {
start: {
line: 7,
column: 2
},
end: {
line: 12,
column: 3
}
}],
line: 7
},
"1": {
loc: {
start: {
line: 18,
column: 2
},
end: {
line: 52,
column: 3
}
},
type: "if",
locations: [{
start: {
line: 18,
column: 2
},
end: {
line: 52,
column: 3
}
}, {
start: {
line: 18,
column: 2
},
end: {
line: 52,
column: 3
}
}],
line: 18
},
"2": {
loc: {
start: {
line: 59,
column: 4
},
end: {
line: 61,
column: 5
}
},
type: "if",
locations: [{
start: {
line: 59,
column: 4
},
end: {
line: 61,
column: 5
}
}, {
start: {
line: 59,
column: 4
},
end: {
line: 61,
column: 5
}
}],
line: 59
},
"3": {
loc: {
start: {
line: 79,
column: 4
},
end: {
line: 81,
column: 5
}
},
type: "if",
locations: [{
start: {
line: 79,
column: 4
},
end: {
line: 81,
column: 5
}
}, {
start: {
line: 79,
column: 4
},
end: {
line: 81,
column: 5
}
}],
line: 79
},
"4": {
loc: {
start: {
line: 79,
column: 8
},
end: {