code:
/***********************************************************************************************************************

	lib/debugview.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/

/*
	TODO: Make this use jQuery throughout.
*/
var DebugView = (() => { // eslint-disable-line no-unused-vars, no-var
	'use strict';

	/*******************************************************************************************************************
		DebugView Class.
	*******************************************************************************************************************/
	class DebugView {
		constructor(parent, type, name, title) {
			Object.defineProperties(this, {
				parent : {
					value : parent
				},

				view : {
					value : document.createElement('span')
				},

				break : {
					value : document.createElement('wbr')
				}
			});

			// Set up the wrapper (`<span>`) element.
			jQuery(this.view)
				.attr({
					title,
					'aria-label' : title,
					'data-type'  : type != null ? type : '', // lazy equality for null
					'data-name'  : name != null ? name : ''  // lazy equality for null
				})
				.addClass('debug');

			// Set up the word break (`<wbr>`) element.
			jQuery(this.break).addClass('debug hidden');

			// Add the wrapper (`<span>`) and word break (`<wbr>`) elements to the `parent` element.
			this.parent.appendChild(this.view);
			this.parent.appendChild(this.break);
		}

		get output() {
			return this.view;
		}

		get type() {
			return this.view.getAttribute('data-type');
		}
		set type(type) {
			this.view.setAttribute('data-type', type != null ? type : ''); // lazy equality for null
		}

		get name() {
			return this.view.getAttribute('data-name');
		}
		set name(name) {
			this.view.setAttribute('data-name', name != null ? name : ''); // lazy equality for null
		}

		get title() {
			return this.view.title;
		}
		set title(title) {
			this.view.title = title;
		}

		append(el) {
			jQuery(this.view).append(el);
			return this;
		}

		modes(options) {
			if (options == null) { // lazy equality for null
				const current = {};

				this.view.className.splitOrEmpty(/\s+/).forEach(name => {
					if (name !== 'debug') {
						current[name] = true;
					}
				});

				return current;
			}
			else if (typeof options === 'object') {
				Object.keys(options).forEach(function (name) {
					this[options[name] ? 'addClass' : 'removeClass'](name);
				}, jQuery(this.view));

				return this;
			}

			throw new Error('DebugView.prototype.modes options parameter must be an object or null/undefined');
		}

		remove() {
			const $view = jQuery(this.view);

			if (this.view.hasChildNodes()) {
				$view.contents().appendTo(this.parent);
			}

			$view.remove();
			jQuery(this.break).remove();
		}

		static isEnabled() {
			return jQuery(document.documentElement).attr('data-debug-view') === 'enabled';
		}

		static enable() {
			jQuery(document.documentElement).attr('data-debug-view', 'enabled');
			jQuery.event.trigger(':debugviewupdate');
		}

		static disable() {
			jQuery(document.documentElement).removeAttr('data-debug-view');
			jQuery.event.trigger(':debugviewupdate');
		}

		static toggle() {
			if (jQuery(document.documentElement).attr('data-debug-view') === 'enabled') {
				DebugView.disable();
			}
			else {
				DebugView.enable();
			}
		}
	}

	/*******************************************************************************************************************
		Module Exports.
	*******************************************************************************************************************/
	return DebugView;
})();


code:
/***********************************************************************************************************************

	lib/diff.js

	Copyright © 2013–2023 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global clone, enumFrom */

var Diff = (() => { // eslint-disable-line no-unused-vars, no-var
	// Diff operations object.
	const Op = enumFrom({
		Delete      : 0,
		SpliceArray : 1,
		Copy        : 2,

		/* legacy */
		CopyDate : 3
		/* /legacy */
	});

	/*******************************************************************************
		Diff Functions.
	*******************************************************************************/

	// Returns whether the given value is a finite number or a numeric string
	// that yields a finite number when parsed.
	function isNumeric(O) {
		let num;

		switch (typeof O) {
			case 'number': num = O; break;
			case 'string': num = Number(O); break;
			default:       return false;
		}

		return !Number.isNaN(num) && Number.isFinite(num);
	}

	// Returns a delta object generated from comparing the `a` and `b` objects.
	function diff(a, b) /* delta object */ {
		const toString = Object.prototype.toString;
		const aIsArray = a instanceof Array;
		const delta    = Object.create(null);
		const keys     = [...Object.keys(a), ...Object.keys(b)]
			.sort()
			.filter((val, i, arr) => i === 0 || arr[i - 1] !== val);
		let aOpKey;

		// Array operation predicate.
		const isAOpKey = key => key === aOpKey;

		/* eslint-disable max-depth */
		for (let i = 0, klen = keys.length; i < klen; ++i) {
			const key  = keys[i];
			const aVal = a[key];
			const bVal = b[key];

			// Key exists in `a`.
			if (Object.hasOwn(a, key)) {
				// Key exists in both.
				if (Object.hasOwn(b, key)) {
					// Values are exactly the same, so do nothing.
					if (aVal === bVal) {
						continue;
					}

					// Values are of the same basic type.
					if (typeof aVal === typeof bVal) {
						// Values are functions.
						if (typeof aVal === 'function') {
							/* delta[key] = [Op.Copy, bVal]; */
							if (aVal.toString() !== bVal.toString()) {
								delta[key] = [Op.Copy, bVal];
							}
						}

						// Values are primitives.
						else if (typeof aVal !== 'object' || aVal === null) {
							delta[key] = [Op.Copy, bVal];
						}

						// Values are objects.
						else {
							const aValType = toString.call(aVal);
							const bValType = toString.call(bVal);

							// Values are objects of the same reported type.
							if (aValType === bValType) {
								// Supported natives and generic objects.
								if (aVal instanceof Date) {
									if (aVal.getTime() !== bVal.getTime()) {
										delta[key] = [Op.Copy, clone(bVal)];
									}
								}
								else if (aVal instanceof Map) {
									delta[key] = [Op.Copy, clone(bVal)];
								}
								else if (aVal instanceof RegExp) {
									if (aVal.toString() !== bVal.toString()) {
										delta[key] = [Op.Copy, clone(bVal)];
									}
								}
								else if (aVal instanceof Set) {
									delta[key] = [Op.Copy, clone(bVal)];
								}
								else if (aVal instanceof Array || aValType === '[object Object]') {
									const subDelta = diff(aVal, bVal);

									if (subDelta !== null) {
										delta[key] = subDelta;
									}
								}

								// Unknown non-generic objects (custom or unsupported natives).
								else {
									// We cannot know how to process these objects,
									// so we simply accept them as-is.
									delta[key] = [Op.Copy, clone(bVal)];
								}
							}

							// Values are objects of different reported types.
							else {
								delta[key] = [Op.Copy, clone(bVal)];
							}
						}
					}

					// Values are of different types.
					else {
						delta[key] = [
							Op.Copy,
							typeof bVal !== 'object' || bVal === null ? bVal : clone(bVal)
						];
					}
				}

				// Key exists only in `a`.
				else {
					if (aIsArray && isNumeric(key)) {
						const index = Number(key);

						if (!aOpKey) {
							aOpKey = '';

							do {
								aOpKey += '~';
							} while (keys.some(isAOpKey));

							delta[aOpKey] = [Op.SpliceArray, index, index];
						}

						if (index < delta[aOpKey][1]) {
							delta[aOpKey][1] = index;
						}

						if (index > delta[aOpKey][2]) {
							delta[aOpKey][2] = index;
						}
					}
					else {
						delta[key] = Op.Delete;
					}
				}
			}

			// Key exists only in `b`.
			else {
				delta[key] = [
					Op.Copy,
					typeof bVal !== 'object' || bVal === null ? bVal : clone(bVal)
				];
			}
		}
		/* eslint-enable max-depth */

		return Object.keys(delta).length > 0 ? delta : null;
	}

	// Returns the object resulting from updating the `orig` object with the
	// `delta` object.
	function patch(orig, delta) /* patched object */ {
		const keys    = delta ? Object.keys(delta) : [];
		const patched = clone(orig);

		for (let i = 0, klen = keys.length; i < klen; ++i) {
			const key   = keys[i];
			const value = delta[key];

			if (value === Op.Delete) {
				delete patched[key];
			}
			else if (value instanceof Array) {
				switch (value[0]) {
					case Op.SpliceArray:
						patched.splice(value[1], value[2] - value[1] + 1);
						break;

					case Op.Copy:
						patched[key] = clone(value[1]);
						break;

					/* legacy */
					case Op.CopyDate:
						patched[key] = new Date(value[1]);
						break;
					/* /legacy */
				}
			}
			else {
				patched[key] = patch(patched[key], value);
			}
		}

		return patched;
	}

	/*******************************************************************************
		Object Exports.
	*******************************************************************************/

	return Object.preventExtensions(Object.create(null, {
		Op    : { value : Op },
		diff  : { value : diff },
		patch : { value : patch }
	}));
})();


code:
/***********************************************************************************************************************

	lib/extensions.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Patterns */

/*
	JavaScript Polyfills.

	NOTE: The ES5 and ES6 polyfills come from the vendored `es5-shim.js` and `es6-shim.js` libraries.
*/
(() => {
	'use strict';

	/*******************************************************************************************************************
		Utility Functions.
	*******************************************************************************************************************/
	/*
		Trims whitespace from either the start or end of the given string.
	*/
	const _trimString = (() => {
		// Whitespace regular expressions.
		const startWSRe = new RegExp(`^${Patterns.space}${Patterns.space}*`);
		const endWSRe   = new RegExp(`${Patterns.space}${Patterns.space}*$`);

		function trimString(str, where) {
			const val = String(str);

			if (!val) {
				return val;
			}

			switch (where) {
			case 'start':
				return startWSRe.test(val) ? val.replace(startWSRe, '') : val;

			case 'end':
				return endWSRe.test(val) ? val.replace(endWSRe, '') : val;

			default:
				throw new Error(`_trimString called with incorrect where parameter value: "${where}"`);
			}
		}

		return trimString;
	})();

	/*
		Generates a pad string based upon the given string and length.
	*/
	function _createPadString(length, padding) {
		const targetLength = Number.parseInt(length, 10) || 0;

		if (targetLength < 1) {
			return '';
		}

		let padString = typeof padding === 'undefined' ? '' : String(padding);

		if (padString === '') {
			padString = ' ';
		}

		while (padString.length < targetLength) {
			const curPadLength    = padString.length;
			const remainingLength = targetLength - curPadLength;

			padString += curPadLength > remainingLength
				? padString.slice(0, remainingLength)
				: padString;
		}

		if (padString.length > targetLength) {
			padString = padString.slice(0, targetLength);
		}

		return padString;
	}

	/*******************************************************************************************************************
		Polyfills.
	*******************************************************************************************************************/
	/*
		[ES2019] Returns a new array consisting of the source array with all sub-array elements
		concatenated into it recursively up to the given depth.
	*/
	if (!Array.prototype.flat) {
		Object.defineProperty(Array.prototype, 'flat', {
			configurable : true,
			writable     : true,
			value        : (() => {
				function flat(/* depth */) {
					if (this == null) { // lazy equality for null
						throw new TypeError('Array.prototype.flat called on null or undefined');
					}

					const depth = arguments.length === 0 ? 1 : Number(arguments[0]) || 0;

					if (depth < 1) {
						return Array.prototype.slice.call(this);
					}

					return Array.prototype.reduce.call(
						this,
						(acc, cur) => {
							if (cur instanceof Array) {
								// acc.push.apply(acc, flat.call(cur, depth - 1));
								acc.push(...flat.call(cur, depth - 1));
							}
							else {
								acc.push(cur);
							}

							return acc;
						},
						[]
					);
				}

				return flat;
			})()
		});
	}

	/*
		[ES2019] Returns a new array consisting of the result of calling the given mapping function
		on every element in the source array and then concatenating all sub-array elements into it
		recursively up to a depth of `1`.  Identical to calling `<Array>.map(fn).flat()`.
	*/
	if (!Array.prototype.flatMap) {
		Object.defineProperty(Array.prototype, 'flatMap', {
			configurable : true,
			writable     : true,

			value(/* callback [, thisArg] */) {
				if (this == null) { // lazy equality for null
					throw new TypeError('Array.prototype.flatMap called on null or undefined');
				}

				return Array.prototype.map.apply(this, arguments).flat();
			}
		});
	}

	/*
		[ES2016] Returns whether the given element was found within the array.
	*/
	if (!Array.prototype.includes) {
		Object.defineProperty(Array.prototype, 'includes', {
			configurable : true,
			writable     : true,

			value(/* needle [, fromIndex] */) {
				if (this == null) { // lazy equality for null
					throw new TypeError('Array.prototype.includes called on null or undefined');
				}

				if (arguments.length === 0) {
					return false;
				}

				const length = this.length >>> 0;

				if (length === 0) {
					return false;
				}

				const needle = arguments[0];
				let i = Number(arguments[1]) || 0;

				if (i < 0) {
					i = Math.max(0, length + i);
				}

				for (/* empty */; i < length; ++i) {
					const value = this[i];

					if (value === needle || value !== value && needle !== needle) {
						return true;
					}
				}

				return false;
			}
		});
	}

	/*
		[ES2017] Returns a new array consisting of the given object's own enumerable property/value
		pairs as `[key, value]` arrays.
	*/
	if (!Object.entries) {
		Object.defineProperty(Object, 'entries', {
			configurable : true,
			writable     : true,

			value(obj) {
				if (typeof obj !== 'object' || obj === null) {
					throw new TypeError('Object.entries object parameter must be an object');
				}

				return Object.keys(obj).map(key => [key, obj[key]]);
			}
		});
	}

	/*
		[ES2019] Returns a new generic object consisting of the given list's key/value pairs.
	*/
	if (!Object.fromEntries) {
		Object.defineProperty(Object, 'fromEntries', {
			configurable : true,
			writable     : true,

			value(iter) {
				return Array.from(iter).reduce(
					(acc, pair) => {
						if (Object(pair) !== pair) {
							throw new TypeError('Object.fromEntries iterable parameter must yield objects');
						}

						if (pair[0] in acc) {
							Object.defineProperty(acc, pair[0], {
								configurable : true,
								enumerable   : true,
								writable     : true,
								value        : pair[1]
							});
						}
						else {
							acc[pair[0]] = pair[1]; // eslint-disable-line no-param-reassign
						}

						return acc;
					},
					{}
				);
			}
		});
	}

	/*
		[ES2017] Returns all own property descriptors of the given object.
	*/
	if (!Object.getOwnPropertyDescriptors) {
		Object.defineProperty(Object, 'getOwnPropertyDescriptors', {
			configurable : true,
			writable     : true,

			value(obj) {
				if (obj == null) { // lazy equality for null
					throw new TypeError('Object.getOwnPropertyDescriptors object parameter is null or undefined');
				}

				const O = Object(obj);

				return Reflect.ownKeys(O).reduce(
					(acc, key) => {
						const desc = Object.getOwnPropertyDescriptor(O, key);

						if (typeof desc !== 'undefined') {
							if (key in acc) {
								Object.defineProperty(acc, key, {
									configurable : true,
									enumerable   : true,
									writable     : true,
									value        : desc
								});
							}
							else {
								acc[key] = desc; // eslint-disable-line no-param-reassign
							}
						}

						return acc;
					},
					{}
				);
			}
		});
	}

	/*
		[ES2017] Returns a new array consisting of the given object's own enumerable property values.
	*/
	if (!Object.values) {
		Object.defineProperty(Object, 'values', {
			configurable : true,
			writable     : true,

			value(obj) {
				if (typeof obj !== 'object' || obj === null) {
					throw new TypeError('Object.values object parameter must be an object');
				}

				return Object.keys(obj).map(key => obj[key]);
			}
		});
	}

	/*
		[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,
		to the start of the string so that the given length is reached.

		NOTE: This pads based upon Unicode code units, rather than code points.
	*/
	if (!String.prototype.padStart) {
		Object.defineProperty(String.prototype, 'padStart', {
			configurable : true,
			writable     : true,

			value(length, padding) {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.padStart called on null or undefined');
				}

				const baseString   = String(this);
				const baseLength   = baseString.length;
				const targetLength = Number.parseInt(length, 10);

				if (targetLength <= baseLength) {
					return baseString;
				}

				return _createPadString(targetLength - baseLength, padding) + baseString;
			}
		});
	}

	/*
		[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,
		to the end of the string so that the given length is reached.

		NOTE: This pads based upon Unicode code units, rather than code points.
	*/
	if (!String.prototype.padEnd) {
		Object.defineProperty(String.prototype, 'padEnd', {
			configurable : true,
			writable     : true,

			value(length, padding) {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.padEnd called on null or undefined');
				}

				const baseString   = String(this);
				const baseLength   = baseString.length;
				const targetLength = Number.parseInt(length, 10);

				if (targetLength <= baseLength) {
					return baseString;
				}

				return baseString + _createPadString(targetLength - baseLength, padding);
			}
		});
	}

	/*
		[ES2019] Returns a string with all whitespace removed from the start of the string.
	*/
	if (!String.prototype.trimStart) {
		Object.defineProperty(String.prototype, 'trimStart', {
			configurable : true,
			writable     : true,

			value() {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.trimStart called on null or undefined');
				}

				return _trimString(this, 'start');
			}
		});
	}

	if (!String.prototype.trimLeft) {
		Object.defineProperty(String.prototype, 'trimLeft', {
			configurable : true,
			writable     : true,

			value() {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.trimLeft called on null or undefined');
				}

				return _trimString(this, 'start');
			}
		});
	}

	/*
		[ES2019] Returns a string with all whitespace removed from the end of the string.
	*/
	if (!String.prototype.trimEnd) {
		Object.defineProperty(String.prototype, 'trimEnd', {
			configurable : true,
			writable     : true,

			value() {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.trimEnd called on null or undefined');
				}

				return _trimString(this, 'end');
			}
		});
	}

	if (!String.prototype.trimRight) {
		Object.defineProperty(String.prototype, 'trimRight', {
			configurable : true,
			writable     : true,

			value() {
				if (this == null) { // lazy equality for null
					throw new TypeError('String.prototype.trimRight called on null or undefined');
				}

				return _trimString(this, 'end');
			}
		});
	}
})();

/*
	JavaScript Extensions.
*/
(() => {
	'use strict';

	function _nativeMathRandom(useMath) {
		if (useMath || !State.prng.isEnabled()) return Math.random();
		return State.random();
	}

	/*******************************************************************************************************************
		Utility Functions.
	*******************************************************************************************************************/
	/*
		Returns a pseudo-random whole number (integer) within the given bounds.
	*/
	function _random(/* [min ,] max, [useMath] */) {
		let min = 0;
		let max;
		let useMath = false;

		switch (arguments.length) {
		case 0:
			throw new Error('_random called with insufficient parameters');
		case 1:
			max = arguments[0];
			break;
		case 2:
			if (arguments[1] === true) {
				max = arguments[0];
				useMath = arguments[1];
			}
			else {
				min = arguments[0];
				max = arguments[1];
			}
			break;
		default:
			min = arguments[0];
			max = arguments[1];
			useMath = arguments[2];
			break;
		}

		if (min > max) {
			[min, max] = [max, min];
		}

		return Math.floor(_nativeMathRandom(useMath) * (max - min + 1)) + min;
	}

	/*
		[DEPRECATED]
		Returns a randomly selected index within the given length and bounds.
		Bounds may be negative.
	*/
	function _randomIndex(length, boundsArgs) {
		let min;
		let max;

		switch (boundsArgs.length) {
		case 1:
			min = 0;
			max = length - 1;
			break;
		case 2:
			min = 0;
			max = Math.trunc(boundsArgs[1]);
			break;
		default:
			min = Math.trunc(boundsArgs[1]);
			max = Math.trunc(boundsArgs[2]);
			break;
		}

		if (Number.isNaN(min)) {
			min = 0;
		}
		else if (!Number.isFinite(min) || min >= length) {
			min = length - 1;
		}
		else if (min < 0) {
			min = length + min;

			if (min < 0) {
				min = 0;
			}
		}

		if (Number.isNaN(max)) {
			max = 0;
		}
		else if (!Number.isFinite(max) || max >= length) {
			max = length - 1;
		}
		else if (max < 0) {
			max = length + max;

			if (max < 0) {
				max = length - 1;
			}
		}

		return _random(min, max);
	}

	/*
		Returns an object (`{ char, start, end }`) containing the Unicode character at
		position `pos`, its starting position, and its ending position—surrogate pairs
		are properly handled.  If `pos` is out-of-bounds, returns an object containing
		the empty string and start/end positions of `-1`.

		This function is necessary because JavaScript strings are sequences of UTF-16
		code units, so surrogate pairs are exposed and thus must be handled.  While the
		ES6/2015 standard does improve the situation somewhat, it does not alleviate
		the need for this function.

		NOTE: Will throw exceptions on invalid surrogate pairs.

		IDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
	*/
	function _getCodePointStartAndEnd(str, pos) {
		const code = str.charCodeAt(pos);

		// Given position was out-of-bounds.
		if (Number.isNaN(code)) {
			return { char : '', start : -1, end : -1 };
		}

		// Code unit is not a UTF-16 surrogate.
		if (code < 0xD800 || code > 0xDFFF) {
			return {
				char  : str.charAt(pos),
				start : pos,
				end   : pos
			};
		}

		// Code unit is a high surrogate (D800–DBFF).
		if (code >= 0xD800 && code <= 0xDBFF) {
			const nextPos = pos + 1;

			// End of string.
			if (nextPos >= str.length) {
				throw new Error('high surrogate without trailing low surrogate');
			}

			const nextCode = str.charCodeAt(nextPos);

			// Next code unit is not a low surrogate (DC00–DFFF).
			if (nextCode < 0xDC00 || nextCode > 0xDFFF) {
				throw new Error('high surrogate without trailing low surrogate');
			}

			return {
				char  : str.charAt(pos) + str.charAt(nextPos),
				start : pos,
				end   : nextPos
			};
		}

		// Code unit is a low surrogate (DC00–DFFF) in the first position.
		if (pos === 0) {
			throw new Error('low surrogate without leading high surrogate');
		}

		const prevPos  = pos - 1;
		const prevCode = str.charCodeAt(prevPos);

		// Previous code unit is not a high surrogate (D800–DBFF).
		if (prevCode < 0xD800 || prevCode > 0xDBFF) {
			throw new Error('low surrogate without leading high surrogate');
		}

		return {
			char  : str.charAt(prevPos) + str.charAt(pos),
			start : prevPos,
			end   : pos
		};
	}

	/*******************************************************************************************************************
		Extensions, General.
	*******************************************************************************************************************/
	/*
		Randomly selects an element from the given array, or array-like object, and returns it.
	*/
	Object.defineProperty(Array, 'random', {
		configurable : true,
		writable     : true,

		value(array, useMath) {
			if (
				   typeof array !== 'object'
				|| array === null
				|| !Object.prototype.hasOwnProperty.call(array, 'length')
			) {
				throw new TypeError('Array.random array parameter must be an array or array-lke object');
			}

			const length = array.length >>> 0;

			if (length === 0) {
				return;
			}

			const index = _random(0, length - 1, useMath);

			return array[index];
		}
	});

	/*
		Concatenates one or more unique elements to the end of the base array
		and returns the result as a new array.  Elements which are arrays will
		be merged—i.e. their elements will be concatenated, rather than the
		array itself.
	*/
	Object.defineProperty(Array.prototype, 'concatUnique', {
		configurable : true,
		writable     : true,

		value(/* variadic */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.concatUnique called on null or undefined');
			}

			const result = Array.from(this);

			if (arguments.length === 0) {
				return result;
			}

			const items   = Array.prototype.reduce.call(arguments, (prev, cur) => prev.concat(cur), []);
			const addSize = items.length;

			if (addSize === 0) {
				return result;
			}

			const indexOf = Array.prototype.indexOf;
			const push    = Array.prototype.push;

			for (let i = 0; i < addSize; ++i) {
				const value = items[i];

				if (indexOf.call(result, value) === -1) {
					push.call(result, value);
				}
			}

			return result;
		}
	});

	/*
		Returns the number of times the given element was found within the array.
	*/
	Object.defineProperty(Array.prototype, 'count', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex ] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.count called on null or undefined');
			}

			const indexOf = Array.prototype.indexOf;
			const needle  = arguments[0];
			let pos   = Number(arguments[1]) || 0;
			let count = 0;

			while ((pos = indexOf.call(this, needle, pos)) !== -1) {
				++count;
				++pos;
			}

			return count;
		}
	});

	/*
		Returns the number elements within the array that pass the test
		implemented by the given predicate function.
	*/
	Object.defineProperty(Array.prototype, 'countWith', {
		configurable : true,
		writable     : true,

		value(predicate, thisArg) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.countWith called on null or undefined');
			}
			if (typeof predicate !== 'function') {
				throw new Error('Array.prototype.countWith predicate parameter must be a function');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return 0;
			}

			let count = 0;

			for (let i = 0; i < length; ++i) {
				if (predicate.call(thisArg, this[i], i, this)) {
					++count;
				}
			}

			return count;
		}
	});

	/*
		Removes and returns all of the given elements from the array.
	*/
	Object.defineProperty(Array.prototype, 'delete', {
		configurable : true,
		writable     : true,

		value(/* needles */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.delete called on null or undefined');
			}

			if (arguments.length === 0) {
				return [];
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return [];
			}

			const needles       = Array.prototype.concat.apply([], arguments);
			const needlesLength = needles.length;
			const indices       = [];

			for (let i = 0; i < length; ++i) {
				const value = this[i];

				for (let j = 0; j < needlesLength; ++j) {
					const needle = needles[j];

					if (value === needle || value !== value && needle !== needle) {
						indices.push(i);
						break;
					}
				}
			}

			const result = [];

			// Copy the elements (in original order).
			for (let i = 0, iend = indices.length; i < iend; ++i) {
				result[i] = this[indices[i]];
			}

			const splice = Array.prototype.splice;

			// Delete the elements (in reverse order).
			for (let i = indices.length - 1; i >= 0; --i) {
				splice.call(this, indices[i], 1);
			}

			return result;
		}
	});

	/*
		Removes and returns all of the elements at the given indices from the array.
	*/
	Object.defineProperty(Array.prototype, 'deleteAt', {
		configurable : true,
		writable     : true,

		value(/* indices */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.deleteAt called on null or undefined');
			}

			if (arguments.length === 0) {
				return [];
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return [];
			}

			const splice     = Array.prototype.splice;
			const cpyIndices = [
				...new Set(
					Array.prototype.concat.apply([], arguments)
						// Map negative indices to their positive counterparts,
						// so the Set can properly filter out duplicates.
						.map(x => x < 0 ? Math.max(0, length + x) : x)
				).values()
			];
			const delIndices = [...cpyIndices].sort((a, b) => b - a);
			const result     = [];

			// Copy the elements (in originally specified order).
			for (let i = 0, iend = cpyIndices.length; i < iend; ++i) {
				result[i] = this[cpyIndices[i]];
			}

			// Delete the elements (in descending numeric order).
			for (let i = 0, iend = delIndices.length; i < iend; ++i) {
				splice.call(this, delIndices[i], 1);
			}

			return result;
		}
	});

	/*
		Removes and returns all of the elements that pass the test implemented
		by the given predicate function from the array.
	*/
	Object.defineProperty(Array.prototype, 'deleteWith', {
		configurable : true,
		writable     : true,

		value(predicate, thisArg) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.deleteWith called on null or undefined');
			}
			if (typeof predicate !== 'function') {
				throw new Error('Array.prototype.deleteWith predicate parameter must be a function');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return [];
			}

			const splice  = Array.prototype.splice;
			const indices = [];
			const result  = [];

			// Copy the elements (in original order).
			for (let i = 0; i < length; ++i) {
				if (predicate.call(thisArg, this[i], i, this)) {
					result.push(this[i]);
					indices.push(i);
				}
			}

			// Delete the elements (in reverse order).
			for (let i = indices.length - 1; i >= 0; --i) {
				splice.call(this, indices[i], 1);
			}

			return result;
		}
	});

	/*
		Returns the first element from the array.
	*/
	Object.defineProperty(Array.prototype, 'first', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.first called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return;
			}

			return this[0];
		}
	});

	/*
		Returns whether all of the given elements were found within the array.
	*/
	Object.defineProperty(Array.prototype, 'includesAll', {
		configurable : true,
		writable     : true,

		value(/* needles */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.includesAll called on null or undefined');
			}

			if (arguments.length === 1) {
				if (Array.isArray(arguments[0])) {
					return Array.prototype.includesAll.apply(this, arguments[0]);
				}

				return Array.prototype.includes.apply(this, arguments);
			}

			for (let i = 0, iend = arguments.length; i < iend; ++i) {
				if (
					!Array.prototype.some.call(this, function (val) {
						return val === this.val || val !== val && this.val !== this.val;
					}, { val : arguments[i] })
				) {
					return false;
				}
			}

			return true;
		}
	});

	/*
		Returns whether any of the given elements were found within the array.
	*/
	Object.defineProperty(Array.prototype, 'includesAny', {
		configurable : true,
		writable     : true,

		value(/* needles */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.includesAny called on null or undefined');
			}

			if (arguments.length === 1) {
				if (Array.isArray(arguments[0])) {
					return Array.prototype.includesAny.apply(this, arguments[0]);
				}

				return Array.prototype.includes.apply(this, arguments);
			}

			for (let i = 0, iend = arguments.length; i < iend; ++i) {
				if (
					Array.prototype.some.call(this, function (val) {
						return val === this.val || val !== val && this.val !== this.val;
					}, { val : arguments[i] })
				) {
					return true;
				}
			}

			return false;
		}
	});

	/*
		Returns the last element from the array.
	*/
	Object.defineProperty(Array.prototype, 'last', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.last called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return;
			}

			return this[length - 1];
		}
	});

	/*
		Randomly removes an element from the base array and returns it.
	*/
	Object.defineProperty(Array.prototype, 'pluck', {
		configurable : true,
		writable     : true,

		value(useMath) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.pluck called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return;
			}

			const index = _random(0, length - 1, useMath);

			return Array.prototype.splice.call(this, index, 1)[0];
		}
	});

	/*
		Randomly removes the given number of unique elements from the base array
		and returns the removed elements as a new array.
	*/
	Object.defineProperty(Array.prototype, 'pluckMany', {
		configurable : true,
		writable     : true,

		value(wantSize) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.pluckMany called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return [];
			}

			let want = Math.trunc(wantSize);

			if (!Number.isInteger(want)) {
				throw new Error('Array.prototype.pluckMany want parameter must be an integer');
			}

			if (want < 1) {
				return [];
			}

			if (want > length) {
				want = length;
			}

			const splice = Array.prototype.splice;
			const result = [];
			let max = length - 1;

			do {
				result.push(splice.call(this, _random(0, max--), 1)[0]);
			} while (result.length < want);

			return result;
		}
	});

	/*
		Appends one or more unique elements to the end of the base array and
		returns its new length.
	*/
	Object.defineProperty(Array.prototype, 'pushUnique', {
		configurable : true,
		writable     : true,

		value(/* variadic */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.pushUnique called on null or undefined');
			}

			const addSize = arguments.length;

			if (addSize === 0) {
				return this.length >>> 0;
			}

			const indexOf = Array.prototype.indexOf;
			const push    = Array.prototype.push;

			for (let i = 0; i < addSize; ++i) {
				const value = arguments[i];

				if (indexOf.call(this, value) === -1) {
					push.call(this, value);
				}
			}

			return this.length >>> 0;
		}
	});

	/*
		Randomly selects an element from the base array and returns it.
	*/
	Object.defineProperty(Array.prototype, 'random', {
		configurable : true,
		writable     : true,

		value(useMath) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.random called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return;
			}

			const index = _random(0, length - 1, useMath);

			return this[index];
		}
	});

	/*
		Randomly selects the given number of unique elements from the base array
		and returns the selected elements as a new array.
	*/
	Object.defineProperty(Array.prototype, 'randomMany', {
		configurable : true,
		writable     : true,

		value(wantSize) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.randomMany called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return [];
			}

			let want = Math.trunc(wantSize);

			if (!Number.isInteger(want)) {
				throw new Error('Array.prototype.randomMany want parameter must be an integer');
			}

			if (want < 1) {
				return [];
			}

			if (want > length) {
				want = length;
			}

			const picked = new Map();
			const result = [];
			const max    = length - 1;

			do {
				let i;
				do {
					i = _random(0, max);
				} while (picked.has(i));
				picked.set(i, true);
				result.push(this[i]);
			} while (result.length < want);

			return result;
		}
	});

	/*
		Randomly shuffles the array and returns it.
	*/
	Object.defineProperty(Array.prototype, 'shuffle', {
		configurable : true,
		writable     : true,

		value(useMath) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.shuffle called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return this;
			}

			for (let i = length - 1; i > 0; --i) {
				const j = Math.floor(_nativeMathRandom(useMath) * (i + 1));

				if (i === j) {
					continue;
				}

				// [this[i], this[j]] = [this[j], this[i]];
				const swap = this[i];
				this[i] = this[j];
				this[j] = swap;
			}

			return this;
		}
	});

	/*
		Prepends one or more unique elements to the beginning of the base array
		and returns its new length.
	*/
	Object.defineProperty(Array.prototype, 'unshiftUnique', {
		configurable : true,
		writable     : true,

		value(/* variadic */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.unshiftUnique called on null or undefined');
			}

			const addSize = arguments.length;

			if (addSize === 0) {
				return this.length >>> 0;
			}

			const indexOf = Array.prototype.indexOf;
			const unshift = Array.prototype.unshift;

			for (let i = 0; i < addSize; ++i) {
				const value = arguments[i];

				if (indexOf.call(this, value) === -1) {
					unshift.call(this, value);
				}
			}

			return this.length >>> 0;
		}
	});

	/*
		Returns a bound function that supplies the given arguments to the base
		function, followed by the arguments are supplied to the bound function,
		whenever it is called.
	*/
	Object.defineProperty(Function.prototype, 'partial', {
		configurable : true,
		writable     : true,

		value(/* variadic */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Function.prototype.partial called on null or undefined');
			}

			const slice = Array.prototype.slice;
			const fn    = this;
			const bound = slice.call(arguments, 0);

			return function () {
				const applied = [];
				let argc = 0;

				for (let i = 0; i < bound.length; ++i) {
					applied.push(bound[i] === undefined ? arguments[argc++] : bound[i]);
				}

				return fn.apply(this, applied.concat(slice.call(arguments, argc)));
			};
		}
	});

	/*
		Returns the given numerical clamped to the specified bounds.
	*/
	Object.defineProperty(Math, 'clamp', {
		configurable : true,
		writable     : true,

		value(num, min, max) {
			const value = Number(num);
			return Number.isNaN(value) ? NaN : value.clamp(min, max);
		}
	});

	/*
		Returns a decimal number eased from 0 to 1.

		NOTE: The magnitude of the returned value decreases if num < 0.5 or increases if num > 0.5.
	*/
	Object.defineProperty(Math, 'easeInOut', {
		configurable : true,
		writable     : true,

		value(num) {
			return 1 - (Math.cos(Number(num) * Math.PI) + 1) / 2;
		}
	});

	/*
		Returns the number clamped to the specified bounds.
	*/
	Object.defineProperty(Number.prototype, 'clamp', {
		configurable : true,
		writable     : true,

		value(/* min, max */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Number.prototype.clamp called on null or undefined');
			}

			if (arguments.length !== 2) {
				throw new Error('Number.prototype.clamp called with an incorrect number of parameters');
			}

			let min = Number(arguments[0]);
			let max = Number(arguments[1]);

			if (min > max) {
				[min, max] = [max, min];
			}

			return Math.min(Math.max(this, min), max);
		}
	});

	/*
		Returns a copy of the given string with all RegExp metacharacters escaped.
	*/
	if (!RegExp.escape) {
		(() => {
			const _regExpMetaCharsRe    = /[\\^$*+?.()|[\]{}]/g;
			const _hasRegExpMetaCharsRe = new RegExp(_regExpMetaCharsRe.source); // to drop the global flag

			Object.defineProperty(RegExp, 'escape', {
				configurable : true,
				writable     : true,

				value(str) {
					const val = String(str);
					return val && _hasRegExpMetaCharsRe.test(val)
						? val.replace(_regExpMetaCharsRe, '\\$&')
						: val;
				}
			});
		})();
	}

	/*
		Returns a formatted string, after replacing each format item in the given
		format string with the text equivalent of the corresponding argument's value.
	*/
	(() => {
		const _formatRegExp    = /{(\d+)(?:,([+-]?\d+))?}/g;
		const _hasFormatRegExp = new RegExp(_formatRegExp.source); // to drop the global flag

		Object.defineProperty(String, 'format', {
			configurable : true,
			writable     : true,

			value(format) {
				function padString(str, align, pad) {
					if (!align) {
						return str;
					}

					const plen = Math.abs(align) - str.length;

					if (plen < 1) {
						return str;
					}

					// const padding = Array(plen + 1).join(pad);
					const padding = String(pad).repeat(plen);
					return align < 0 ? str + padding : padding + str;
				}

				if (arguments.length < 2) {
					return arguments.length === 0 ? '' : format;
				}

				const args = arguments.length === 2 && Array.isArray(arguments[1])
					? [...arguments[1]]
					: Array.prototype.slice.call(arguments, 1);

				if (args.length === 0) {
					return format;
				}

				if (!_hasFormatRegExp.test(format)) {
					return format;
				}

				// Possibly required by some old buggy browsers.
				_formatRegExp.lastIndex = 0;

				return format.replace(_formatRegExp, (match, index, align) => {
					let retval = args[index];

					if (retval == null) { // lazy equality for null
						return '';
					}

					while (typeof retval === 'function') {
						retval = retval();
					}

					switch (typeof retval) {
					case 'string': /* no-op */ break;
					case 'object': retval = JSON.stringify(retval); break;
					default:       retval = String(retval); break;
					}

					return padString(retval, !align ? 0 : Number.parseInt(align, 10), ' ');
				});
			}
		});
	})();

	/*
		Returns whether the given string was found within the string.
	*/
	Object.defineProperty(String.prototype, 'contains', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.contains called on null or undefined');
			}

			return String.prototype.indexOf.apply(this, arguments) !== -1;
		}
	});

	/*
		Returns the number of times the given substring was found within the string.
	*/
	Object.defineProperty(String.prototype, 'count', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex ] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.count called on null or undefined');
			}

			const needle = String(arguments[0] || '');

			if (needle === '') {
				return 0;
			}

			const indexOf = String.prototype.indexOf;
			const step    = needle.length;
			let pos     = Number(arguments[1]) || 0;
			let count   = 0;

			while ((pos = indexOf.call(this, needle, pos)) !== -1) {
				++count;
				pos += step;
			}

			return count;
		}
	});

	/*
		Returns the first Unicode code point from the string.
	*/
	Object.defineProperty(String.prototype, 'first', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.first called on null or undefined');
			}

			// Required as `this` could be a `String` object or come from a `call()` or `apply()`.
			const str = String(this);

			// Get the first code point—may be one or two code units—and its end position.
			const { char } = _getCodePointStartAndEnd(str, 0);

			return char;
		}
	});

	/*
		Returns the last Unicode code point from the string.
	*/
	Object.defineProperty(String.prototype, 'last', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.last called on null or undefined');
			}

			// Required as `this` could be a `String` object or come from a `call()` or `apply()`.
			const str = String(this);

			// Get the last code point—may be one or two code units—and its end position.
			const { char } = _getCodePointStartAndEnd(str, str.length - 1);

			return char;
		}
	});

	/*
		Returns a copy of the base string with `delCount` characters replaced with
		`replacement`, starting at `startAt`.
	*/
	Object.defineProperty(String.prototype, 'splice', {
		configurable : true,
		writable     : true,

		value(startAt, delCount, replacement) {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.splice called on null or undefined');
			}

			const length = this.length >>> 0;

			if (length === 0) {
				return '';
			}

			let start = Number(startAt);

			if (!Number.isSafeInteger(start)) {
				start = 0;
			}
			else if (start < 0) {
				start += length;

				if (start < 0) {
					start = 0;
				}
			}

			if (start > length) {
				start = length;
			}

			let count = Number(delCount);

			if (!Number.isSafeInteger(count) || count < 0) {
				count = 0;
			}

			let res = this.slice(0, start);

			if (typeof replacement !== 'undefined') {
				res += replacement;
			}

			if (start + count < length) {
				res += this.slice(start + count);
			}

			return res;
		}
	});

	/*
		Returns an array of strings, split from the string, or an empty array if the
		string is empty.
	*/
	Object.defineProperty(String.prototype, 'splitOrEmpty', {
		configurable : true,
		writable     : true,

		value(/* [ separator [, limit ]] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.splitOrEmpty called on null or undefined');
			}

			// Required as `this` could be a `String` object or come from a `call()` or `apply()`.
			if (String(this) === '') {
				return [];
			}

			return String.prototype.split.apply(this, arguments);
		}
	});

	/*
		Returns a copy of the base string with the first Unicode code point uppercased,
		according to any locale-specific rules.
	*/
	Object.defineProperty(String.prototype, 'toLocaleUpperFirst', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.toLocaleUpperFirst called on null or undefined');
			}

			// Required as `this` could be a `String` object or come from a `call()` or `apply()`.
			const str = String(this);

			// Get the first code point—may be one or two code units—and its end position.
			const { char, end } = _getCodePointStartAndEnd(str, 0);

			return end === -1 ? '' : char.toLocaleUpperCase() + str.slice(end + 1);
		}
	});

	/*
		Returns a copy of the base string with the first Unicode code point uppercased.
	*/
	Object.defineProperty(String.prototype, 'toUpperFirst', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.toUpperFirst called on null or undefined');
			}

			// Required as `this` could be a `String` object or come from a `call()` or `apply()`.
			const str = String(this);

			// Get the first code point—may be one or two code units—and its end position.
			const { char, end } = _getCodePointStartAndEnd(str, 0);

			return end === -1 ? '' : char.toUpperCase() + str.slice(end + 1);
		}
	});

	/*******************************************************************************************************************
		Extensions, JSON.
	*******************************************************************************************************************/
	/*
		Define `toJSON()` methods on each prototype we wish to support.
	*/
	Object.defineProperty(Date.prototype, 'toJSON', {
		configurable : true,
		writable     : true,

		value() {
			return ['(revive:date)', this.toISOString()];
		}
	});
	Object.defineProperty(Function.prototype, 'toJSON', {
		configurable : true,
		writable     : true,

		value() {
			/*
				The enclosing parenthesis here are necessary to force the function expression code
				string, returned by `this.toString()`, to be evaluated as an expression during
				revival.  Without them, the function expression, which is likely nameless, will be
				evaluated as a function definition—which will throw a syntax error exception, since
				function definitions must have a name.
			*/
			return ['(revive:eval)', `(${this.toString()})`];
		}
	});
	Object.defineProperty(Map.prototype, 'toJSON', {
		configurable : true,
		writable     : true,

		value() {
			return ['(revive:map)', [...this]];
		}
	});
	Object.defineProperty(RegExp.prototype, 'toJSON', {
		configurable : true,
		writable     : true,

		value() {
			return ['(revive:eval)', this.toString()];
		}
	});
	Object.defineProperty(Set.prototype, 'toJSON', {
		configurable : true,
		writable     : true,

		value() {
			return ['(revive:set)', [...this]];
		}
	});

	/*
		Utility method to allow users to easily wrap their code in the revive wrapper.
	*/
	Object.defineProperty(JSON, 'reviveWrapper', {
		configurable : true,
		writable     : true,

		value(code, data) {
			if (typeof code !== 'string') {
				throw new TypeError('JSON.reviveWrapper code parameter must be a string');
			}

			return ['(revive:eval)', [code, data]];
		}
	});

	/*
		Backup the original `JSON.stringify()` and replace it with a revive wrapper aware version.
	*/
	Object.defineProperty(JSON, '_real_stringify', {
		value : JSON.stringify
	});
	Object.defineProperty(JSON, 'stringify', {
		configurable : true,
		writable     : true,

		value(value, replacer, space) {
			return JSON._real_stringify(value, (key, val) => {
				let value = val;

				/*
					Call the custom replacer, if specified.
				*/
				if (typeof replacer === 'function') {
					try {
						value = replacer(key, value);
					}
					catch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }
				}

				/*
					Attempt to replace values.
				*/
				if (typeof value === 'undefined') value = ['(revive:eval)', 'undefined'];
				else if (value === Infinity) value = ['(revive:eval)', 'Infinity'];
				else if (value === -Infinity) value = ['(revive:eval)', '-Infinity'];
				else if (Number.isNaN(value)) value = ['(revive:eval)', 'NaN'];

				return value;
			}, space);
		}
	});

	/*
		Backup the original `JSON.parse()` and replace it with a revive wrapper aware version.
	*/
	Object.defineProperty(JSON, '_real_parse', {
		value : JSON.parse
	});
	Object.defineProperty(JSON, 'parse', {
		configurable : true,
		writable     : true,

		value(text, reviver) {
			return JSON._real_parse(text, (key, val) => {
				let value = val;

				/*
					Attempt to revive wrapped values.
				*/
				if (Array.isArray(value) && value.length === 2) {
					switch (value[0]) {
					case '(revive:set)':
						value = new Set(value[1]);
						break;
					case '(revive:map)':
						value = new Map(value[1]);
						break;
					case '(revive:date)':
						value = new Date(value[1]);
						break;
					case '(revive:eval)':
						try {
							/* eslint-disable no-eval */
							// For post-v2.9.0 `JSON.reviveWrapper()`.
							if (Array.isArray(value[1])) {
								const $ReviveData$ = value[1][1]; // eslint-disable-line no-unused-vars
								value = eval(value[1][0]);
							}

							// For regular expressions, functions, and pre-v2.9.0 `JSON.reviveWrapper()`.
							else {
								value = eval(value[1]);
							}
							/* eslint-enable no-eval */
						}
						catch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }
						break;
					}
				}

				/* legacy */
				else if (typeof value === 'string' && value.slice(0, 10) === '@@revive@@') {
					try {
						value = eval(value.slice(10)); // eslint-disable-line no-eval
					}
					catch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }
				}
				/* /legacy */

				/*
					Call the custom reviver, if specified.
				*/
				if (typeof reviver === 'function') {
					try {
						value = reviver(key, value);
					}
					catch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }
				}

				return value;
			});
		}
	});

	/*******************************************************************************************************************
		Extensions, Deprecated.
	*******************************************************************************************************************/
	/*
		[DEPRECATED] Returns whether the given element was found within the array.
	*/
	Object.defineProperty(Array.prototype, 'contains', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.contains called on null or undefined');
			}

			return Array.prototype.includes.apply(this, arguments);
		}
	});

	/*
		[DEPRECATED] Returns whether all of the given elements were found within the array.
	*/
	Object.defineProperty(Array.prototype, 'containsAll', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.containsAll called on null or undefined');
			}

			return Array.prototype.includesAll.apply(this, arguments);
		}
	});

	/*
		[DEPRECATED] Returns whether any of the given elements were found within the array.
	*/
	Object.defineProperty(Array.prototype, 'containsAny', {
		configurable : true,
		writable     : true,

		value(/* needle [, fromIndex] */) {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.containsAny called on null or undefined');
			}

			return Array.prototype.includesAny.apply(this, arguments);
		}
	});

	/*
		[DEPRECATED] Returns a new array consisting of the flattened source array.
	*/
	Object.defineProperty(Array.prototype, 'flatten', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('Array.prototype.flatten called on null or undefined');
			}

			return Array.prototype.flat.call(this, Infinity);
		}
	});

	/*
		[DEPRECATED] Returns an array of link titles, parsed from the string.

		NOTE: Unused in SugarCube, only included for compatibility.
	*/
	Object.defineProperty(String.prototype, 'readBracketedList', {
		configurable : true,
		writable     : true,

		value() {
			if (this == null) { // lazy equality for null
				throw new TypeError('String.prototype.readBracketedList called on null or undefined');
			}

			// RegExp groups: Double-square-bracket quoted | Unquoted.
			const re    = new RegExp('(?:\\[\\[((?:\\s|\\S)*?)\\]\\])|([^"\'\\s]\\S*)', 'gm');
			const names = [];
			let match;

			while ((match = re.exec(this)) !== null) {
				if (match[1]) { // double-square-bracket quoted
					names.push(match[1]);
				}
				else if (match[2]) { // unquoted
					names.push(match[2]);
				}
			}

			return names;
		}
	});
})();


code:
/***********************************************************************************************************************

	lib/fullscreen.js

	Copyright © 2018–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Browser */

var Fullscreen = (() => { // eslint-disable-line no-unused-vars, no-var
	'use strict';

	/*
		SEE:
			https://fullscreen.spec.whatwg.org
			https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
	*/

	// Vendor properties object.
	const vendor = (() => {
		try {
			return Object.freeze([
				// Specification.
				{
					isEnabled   : 'fullscreenEnabled',
					element     : 'fullscreenElement',
					requestFn   : 'requestFullscreen',
					exitFn      : 'exitFullscreen',
					changeEvent : 'fullscreenchange', // prop: onfullscreenchange
					errorEvent  : 'fullscreenerror'   // prop: onfullscreenerror
				},

				// `webkit` prefixed: old Blink, WebKit, & Edge.
				{
					isEnabled   : 'webkitFullscreenEnabled',
					element     : 'webkitFullscreenElement',
					requestFn   : 'webkitRequestFullscreen',
					exitFn      : 'webkitExitFullscreen',
					changeEvent : 'webkitfullscreenchange',
					errorEvent  : 'webkitfullscreenerror'
				},

				// `moz` prefixed: old Gecko, maybe Seamonkey.
				{
					isEnabled   : 'mozFullScreenEnabled',
					element     : 'mozFullScreenElement',
					requestFn   : 'mozRequestFullScreen',
					exitFn      : 'mozCancelFullScreen',
					changeEvent : 'mozfullscreenchange',
					errorEvent  : 'mozfullscreenerror'
				},

				// `ms` prefixed: IE 11.
				{
					isEnabled   : 'msFullscreenEnabled',
					element     : 'msFullscreenElement',
					requestFn   : 'msRequestFullscreen',
					exitFn      : 'msExitFullscreen',
					changeEvent : 'MSFullscreenChange',
					errorEvent  : 'MSFullscreenError'
				}
			].find(vnd => vnd.isEnabled in document));
		}
		catch (ex) { /* no-op */ }

		return undefined;
	})();

	/*******************************************************************************
		Feature Detection Functions.
	*******************************************************************************/

	// Return whether the request and exit fullscreen methods return a `Promise`.
	//
	// NOTE: The initial result is cached for future calls.
	const _returnsPromise = (function () {
		// Cache of whether the request and exit methods return a `Promise`.
		let _hasPromise = null;

		function _returnsPromise() {
			if (_hasPromise !== null) {
				return _hasPromise;
			}

			_hasPromise = false;

			if (vendor) {
				try {
					const value = document.exitFullscreen();

					// Silence "Uncaught (in promise)" console errors from Blink.
					//
					// NOTE: Swallowing errors is generally bad, but in this case we know there's
					// going to be an error regardless, since we shouldn't be in fullscreen yet,
					// and we don't actually care about the error, since we just want the return
					// value, so we consign it to the bit bucket.
					//
					// NOTE: We don't ensure that the return value is not `undefined` here because
					// having the attempted call to `<Promise>.catch()` on an `undefined` value throw
					// is acceptable, since it will be caught and `false` eventually returned.
					value.catch(() => { /* no-op */ });

					_hasPromise = value instanceof Promise;
				}
				catch (ex) { /* no-op */ }
			}

			return _hasPromise;
		}

		return _returnsPromise;
	})();

	/*******************************************************************************
		Utility Functions.
	*******************************************************************************/

	function _selectElement(requestedEl) {
		let selectedEl = requestedEl || document.documentElement;

		// Document element scrolling workaround for older browsers.
		if (
			   selectedEl === document.documentElement
			&& (
				   vendor.requestFn === 'msRequestFullscreen'   // IE 11
				|| Browser.isOpera && Browser.operaVersion < 15 // Opera 12 (Presto)
			)
		) {
			selectedEl = document.body;
		}

		return selectedEl;
	}

	/*******************************************************************************
		API Functions.
	*******************************************************************************/

	function getVendor() {
		return vendor;
	}

	function getElement() {
		return (vendor || null) && document[vendor.element];
	}

	function isEnabled() {
		return Boolean(vendor && document[vendor.isEnabled]);
	}

	function isFullscreen() {
		return Boolean(vendor && document[vendor.element]);
	}

	function requestFullscreen(options, requestedEl) {
		if (!vendor) {
			return Promise.reject(new Error('fullscreen not supported'));
		}

		const element = _selectElement(requestedEl);

		if (typeof element[vendor.requestFn] !== 'function') {
			return Promise.reject(new Error('fullscreen not supported'));
		}
		if (isFullscreen()) {
			return Promise.resolve();
		}

		if (_returnsPromise()) {
			return element[vendor.requestFn](options);
		}
		else { // eslint-disable-line no-else-return
			const namespace = '.Fullscreen_requestFullscreen';

			return new Promise((resolve, reject) => {
				jQuery(element)
					.off(namespace)
					.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {
						jQuery(this).off(namespace);

						if (ev.type === vendor.errorEvent) {
							reject(new Error('unknown fullscreen request error'));
						}
						else {
							resolve();
						}
					});
				element[vendor.requestFn](options);
			});
		}
	}

	function exitFullscreen() {
		if (!vendor || typeof document[vendor.exitFn] !== 'function') {
			return Promise.reject(new TypeError('fullscreen not supported'));
		}
		if (!isFullscreen()) {
			return Promise.reject(new TypeError('fullscreen mode not active'));
		}

		if (_returnsPromise()) {
			return document[vendor.exitFn]();
		}
		else { // eslint-disable-line no-else-return
			const namespace = '.Fullscreen_exitFullscreen';

			return new Promise((resolve, reject) => {
				jQuery(document)
					.off(namespace)
					.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {
						jQuery(this).off(namespace);

						if (ev.type === vendor.errorEvent) {
							reject(new Error('unknown fullscreen exit error'));
						}
						else {
							resolve();
						}
					});
				document[vendor.exitFn]();
			});
		}
	}

	function toggleFullscreen(options, requestedEl) {
		return isFullscreen() ? exitFullscreen() : requestFullscreen(options, requestedEl);
	}

	function onChange(handlerFn, requestedEl) {
		if (!vendor) {
			return;
		}

		const element = _selectElement(requestedEl);

		$(element).on(vendor.changeEvent, handlerFn);
	}

	function offChange(handlerFn, requestedEl) {
		if (!vendor) {
			return;
		}

		const element = _selectElement(requestedEl);

		if (handlerFn) {
			$(element).off(vendor.changeEvent, handlerFn);
		}
		else {
			$(element).off(vendor.changeEvent);
		}
	}

	function onError(handlerFn, requestedEl) {
		if (!vendor) {
			return;
		}

		const element = _selectElement(requestedEl);

		$(element).on(vendor.errorEvent, handlerFn);
	}

	function offError(handlerFn, requestedEl) {
		if (!vendor) {
			return;
		}

		const element = _selectElement(requestedEl);

		if (handlerFn) {
			$(element).off(vendor.errorEvent, handlerFn);
		}
		else {
			$(element).off(vendor.errorEvent);
		}
	}

	/*******************************************************************************
		Module Exports.
	*******************************************************************************/

	return Object.freeze(Object.defineProperties({}, {
		vendor       : { get : getVendor },
		element      : { get : getElement },
		isEnabled    : { value : isEnabled },
		isFullscreen : { value : isFullscreen },
		request      : { value : requestFullscreen },
		exit         : { value : exitFullscreen },
		toggle       : { value : toggleFullscreen },
		onChange     : { value : onChange },
		offChange    : { value : offChange },
		onError      : { value : onError },
		offError     : { value : offError }
	}));
})();


code:
/***********************************************************************************************************************

	lib/has.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Browser */

var Has = (() => { // eslint-disable-line no-unused-vars, no-var
	'use strict';

	/*
		NOTE: The aggressive try/catch feature tests are necessitated by implementation
		bugs in various browsers.
	*/

	// Is the `HTMLAudioElement` API available?
	const hasAudioElement = (() => {
		try {
			return typeof document.createElement('audio').canPlayType === 'function';
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the `File` API available?
	const hasFile = (() => {
		try {
			return 'Blob' in window &&
				'File' in window &&
				'FileList' in window &&
				'FileReader' in window &&
				(!Browser.isOpera || Browser.operaVersion >= 15);
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the `geolocation` API available?
	const hasGeolocation = (() => {
		try {
			return 'geolocation' in navigator &&
				typeof navigator.geolocation.getCurrentPosition === 'function' &&
				typeof navigator.geolocation.watchPosition === 'function';
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the `MutationObserver` API available?
	const hasMutationObserver = (() => {
		try {
			return 'MutationObserver' in window &&
				typeof window.MutationObserver === 'function';
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the `performance` API available?
	const hasPerformance = (() => {
		try {
			return 'performance' in window &&
				typeof window.performance.now === 'function';
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the platform a touch device?
	const hasTouch = (() => {
		try {
			return 'ontouchstart' in window ||
				!!window.DocumentTouch &&
				document instanceof window.DocumentTouch ||
				!!navigator.maxTouchPoints ||
				!!navigator.msMaxTouchPoints;
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Is the transition end event available and by what name?
	const hasTransitionEndEvent = (() => {
		try {
			const teMap = new Map([
				['transition',       'transitionend'],
				['MSTransition',     'msTransitionEnd'],
				['WebkitTransition', 'webkitTransitionEnd'],
				['MozTransition',    'transitionend']
			]);
			const teKeys = [...teMap.keys()];
			const el     = document.createElement('div');

			for (let i = 0; i < teKeys.length; ++i) {
				if (el.style[teKeys[i]] !== undefined) {
					return teMap.get(teKeys[i]);
				}
			}
		}
		catch (ex) { /* no-op */ }

		return false;
	})();

	// Module Exports.
	return Object.freeze({
		audio              : hasAudioElement,
		fileAPI            : hasFile,
		geolocation        : hasGeolocation,
		mutationObserver   : hasMutationObserver,
		performance        : hasPerformance,
		touch              : hasTouch,
		transitionEndEvent : hasTransitionEndEvent
	});
})();


code:
/***********************************************************************************************************************

	lib/helpers.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Config, L10n, State, Story, Util, Wikifier */

var { // eslint-disable-line no-var
	/* eslint-disable no-unused-vars */
	clone,
	convertBreaks,
	safeActiveElement,
	setDisplayTitle,
	setPageElement,
	throwError,
	stringFrom
	/* eslint-enable no-unused-vars */
} = (() => {
	'use strict';

	/*******************************************************************************************************************
		Utility Functions.
	*******************************************************************************************************************/
	function _getTextContent(source) {
		const copy = source.cloneNode(true);
		const frag = document.createDocumentFragment();
		let node;

		while ((node = copy.firstChild) !== null) {
			// Insert spaces before various elements.
			if (node.nodeType === Node.ELEMENT_NODE) {
				switch (node.nodeName.toUpperCase()) {
				case 'BR':
				case 'DIV':
				case 'P':
					frag.appendChild(document.createTextNode(' '));
					break;
				}
			}

			frag.appendChild(node);
		}

		return frag.textContent;
	}

	/*******************************************************************************************************************
		Helper Functions.
	*******************************************************************************************************************/
	/*
		Returns a deep copy of the given object.

		NOTE:
			1. `clone()` does not clone functions, however, since function definitions
			   are immutable, the only issues are with expando properties and scope.
			   The former really should not be done.  The latter is problematic either
			   way—damned if you do, damned if you don't.
			2. `clone()` does not maintain referential relationships—e.g. multiple
			   references to the same object will, post-cloning, refer to different
			   equivalent objects; i.e. each reference will receive its own clone
			   of the original object.
	*/
	function clone(orig) {
		/*
			Immediately return the primitives and functions.
		*/
		if (typeof orig !== 'object' || orig === null) {
			return orig;
		}

		/*
			Unbox instances of the primitive exemplar objects.
		*/
		if (orig instanceof String) {
			return String(orig);
		}
		if (orig instanceof Number) {
			return Number(orig);
		}
		if (orig instanceof Boolean) {
			return Boolean(orig);
		}

		/*
			Honor native clone methods.
		*/
		if (typeof orig.clone === 'function') {
			return orig.clone(true);
		}
		if (orig.nodeType && typeof orig.cloneNode === 'function') {
			return orig.cloneNode(true);
		}

		/*
			Create a copy of the original object.

			NOTE: Each non-generic object that we wish to support must be
			explicitly handled below.
		*/
		let copy;

		// Handle instances of the core supported object types.
		if (orig instanceof Array) {
			copy = new Array(orig.length);
		}
		else if (orig instanceof Date) {
			copy = new Date(orig.getTime());
		}
		else if (orig instanceof Map) {
			copy = new Map();
			orig.forEach((val, key) => copy.set(key, clone(val)));
		}
		else if (orig instanceof RegExp) {
			copy = new RegExp(orig);
		}
		else if (orig instanceof Set) {
			copy = new Set();
			orig.forEach(val => copy.add(clone(val)));
		}

		// Handle instances of unknown or generic objects.
		else {
			// We try to ensure that the returned copy has the same prototype as
			// the original, but this will probably produce less than satisfactory
			// results on non-generics.
			copy = Object.create(Object.getPrototypeOf(orig));
		}

		/*
			Duplicate the original object's own enumerable properties, which will
			include expando properties on non-generic objects.

			NOTE: This preserves neither symbol properties nor ES5 property attributes.
			Neither does the delta coding or serialization code, however, so it's not
			really an issue at the moment.
		*/
		Object.keys(orig).forEach(name => copy[name] = clone(orig[name]));

		return copy;
	}

	/*
		Converts <br> elements to <p> elements within the given node tree.
	*/
	function convertBreaks(source) {
		const output = document.createDocumentFragment();
		let para = document.createElement('p');
		let node;

		while ((node = source.firstChild) !== null) {
			if (node.nodeType === Node.ELEMENT_NODE) {
				const tagName = node.nodeName.toUpperCase();

				switch (tagName) {
				case 'BR':
					if (
						   node.nextSibling !== null
						&& node.nextSibling.nodeType === Node.ELEMENT_NODE
						&& node.nextSibling.nodeName.toUpperCase() === 'BR'
					) {
						source.removeChild(node.nextSibling);
						source.removeChild(node);
						output.appendChild(para);
						para = document.createElement('p');
						continue;
					}
					else if (!para.hasChildNodes()) {
						source.removeChild(node);
						continue;
					}
					break;

				case 'ADDRESS':
				case 'ARTICLE':
				case 'ASIDE':
				case 'BLOCKQUOTE':
				case 'CENTER':
				case 'DIV':
				case 'DL':
				case 'FIGURE':
				case 'FOOTER':
				case 'FORM':
				case 'H1':
				case 'H2':
				case 'H3':
				case 'H4':
				case 'H5':
				case 'H6':
				case 'HEADER':
				case 'HR':
				case 'MAIN':
				case 'NAV':
				case 'OL':
				case 'P':
				case 'PRE':
				case 'SECTION':
				case 'TABLE':
				case 'UL':
					if (para.hasChildNodes()) {
						output.appendChild(para);
						para = document.createElement('p');
					}

					output.appendChild(node);
					continue;
				}
			}

			para.appendChild(node);
		}

		if (para.hasChildNodes()) {
			output.appendChild(para);
		}

		source.appendChild(output);
	}

	/*
		Returns `document.activeElement` or `null`.
	*/
	function safeActiveElement() {
		/*
			IE9 contains a bug where trying to access the active element of an iframe's
			parent document (i.e. `window.parent.document.activeElement`) will throw an
			exception, so we must allow for an exception to be thrown.

			We could simply return `undefined` here, but since the API's default behavior
			should be to return `document.body` or `null` when there is no selection, we
			choose to return `null` in all non-element cases (i.e. whether it returns
			`null` or throws an exception).  Just a bit of normalization.
		*/
		try {
			return document.activeElement || null;
		}
		catch (ex) {
			return null;
		}
	}

	/*
		Sets the display title.
	*/
	function setDisplayTitle(title) {
		if (typeof title !== 'string') {
			throw new TypeError(`story display title must be a string (received: ${Util.getType(title)})`);
		}

		const render = document.createDocumentFragment();
		new Wikifier(render, title);

		const text = _getTextContent(render).trim();

		// if (text === '') {
		// 	throw new Error('story display title must not render to an empty string or consist solely of whitespace');
		// }

		document.title = Config.passages.displayTitles && State.passage !== '' && State.passage !== Config.passages.start
			? `${State.passage} | ${text}`
			: text;

		const storyTitle = document.getElementById('story-title');

		if (storyTitle !== null) {
			jQuery(storyTitle).empty().append(render);
		}
	}

	/*
		Wikifies a passage into a DOM element corresponding to the passed ID and returns the element.
	*/
	function setPageElement(idOrElement, titles, defaultText) {
		const el = typeof idOrElement === 'object'
			? idOrElement
			: document.getElementById(idOrElement);

		if (el == null) { // lazy equality for null
			return null;
		}

		const ids = Array.isArray(titles) ? titles : [titles];

		jQuery(el).empty();

		for (let i = 0, iend = ids.length; i < iend; ++i) {
			if (Story.has(ids[i])) {
				el.append(Story.get(ids[i]).render());
				return el;
			}
		}

		if (defaultText != null) { // lazy equality for null
			const text = String(defaultText).trim();

			if (text !== '') {
				new Wikifier(el, text);
			}
		}

		return el;
	}

	/*
		Appends an error view to the passed DOM element.
	*/
	function throwError(place, message, source, stack) {
		const $wrapper = jQuery(document.createElement('div'));
		const $toggle  = jQuery(document.createElement('button'));
		const $source  = jQuery(document.createElement('pre'));
		const mesg     = `${L10n.get('errorTitle')}: ${message || 'unknown error'} ${Config.saves.version}`;

		$toggle
			.addClass('error-toggle')
			.ariaClick({
				label : L10n.get('errorToggle')
			}, () => {
				if ($toggle.hasClass('enabled')) {
					$toggle.removeClass('enabled');
					$source.attr({
						'aria-hidden' : true,
						hidden        : 'hidden'
					});
				}
				else {
					$toggle.addClass('enabled');
					$source.removeAttr('aria-hidden hidden');
				}
			})
			.appendTo($wrapper);
		jQuery(document.createElement('span'))
			.addClass('error')
			.text(mesg)
			.appendTo($wrapper);
		jQuery(document.createElement('code'))
			.text(source)
			.appendTo($source);
		$source
			.addClass('error-source')
			.attr({
				'aria-hidden' : true,
				hidden        : 'hidden'
			})
			.appendTo($wrapper);
		if (stack) {
			const lines = stack.split('\n');
			for (const ll of lines) {
				const div = document.createElement('div');
				div.append(ll.replace(/file:.*\//, '<path>/'));
				$source.append(div);
			}
		}
		$wrapper
			.addClass('error-view')
			.appendTo(place);

		console.warn(`${mesg}\n\t${source.replace(/\n/g, '\n\t')}`);

		return false;
	}

	/*
		Returns the simple string representation of the given value or, if there is
		none, a square bracketed representation.
	*/
	function stringFrom(value) {
		switch (typeof value) {
		case 'function':
			return '[function]';

		case 'number':
			if (Number.isNaN(value)) {
				return '[number NaN]';
			}

			break;

		case 'object':
			if (value === null) {
				return '[null]';
			}
			else if (value instanceof Array) {
				return value.map(val => stringFrom(val)).join(', ');
			}
			else if (value instanceof Set) {
				return Array.from(value).map(val => stringFrom(val)).join(', ');
			}
			else if (value instanceof Map) {
				const result = Array.from(value).map(([key, val]) => `${stringFrom(key)} \u2192 ${stringFrom(val)}`);
				return `{\u202F${result.join(', ')}\u202F}`;
			}
			else if (value instanceof Date) {
				return value.toLocaleString();
			}
			else if (value instanceof Element) {
				if (
					value === document.documentElement
					|| value === document.head
					|| value === document.body
				) {
					throw new Error('illegal operation; attempting to convert the <html>, <head>, or <body> tags to string is not allowed');
				}

				return value.outerHTML;
			}
			else if (value instanceof Node) {
				return value.textContent;
			}
			else if (typeof value.toString === 'function') {
				return value.toString();
			}

			return Object.prototype.toString.call(value);

		case 'symbol': {
			const desc = typeof value.description !== 'undefined' ? ` "${value.description}"` : '';
			return `[symbol${desc}]`;
		}

		case 'undefined':
			return '[undefined]';
		}

		return String(value);
	}

	/*******************************************************************************************************************
		Module Exports.
	*******************************************************************************************************************/
	return Object.freeze(Object.defineProperties({}, {
		clone             : { value : clone },
		convertBreaks     : { value : convertBreaks },
		safeActiveElement : { value : safeActiveElement },
		setDisplayTitle   : { value : setDisplayTitle },
		setPageElement    : { value : setPageElement },
		throwError        : { value : throwError },
		stringFrom        : { value : stringFrom }
	}));
})();


code:
/***********************************************************************************************************************

	lib/jquery-plugins.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Wikifier, errorPrologRegExp, safeActiveElement */

/*
	WAI-ARIA methods plugin.

	`<jQuery>.ariaClick([options,] handler)`
	    Makes the target element(s) WAI-ARIA compatible clickables.

	`<jQuery>.ariaDisabled(state)`
	    Changes the disabled state of the target WAI-ARIA-compatible clickable element(s).

	`<jQuery>.ariaIsDisabled()`
	    Checks the disabled status of the target WAI-ARIA-compatible clickable element(s).
*/
(() => {
	'use strict';

	/*
		Event handler & utility functions.

		NOTE: Do not replace the anonymous functions herein with arrow functions.
	*/
	function onKeypressFn(ev) {
		// 13 is Enter/Return, 32 is Space.
		if (ev.which === 13 || ev.which === 32) {
			ev.preventDefault();

			// To allow delegation, attempt to trigger the event on `document.activeElement`,
			// if possible, elsewise on `this`.
			jQuery(safeActiveElement() || this).trigger('click');
		}
	}

	function onClickFnWrapper(fn) {
		return function () {
			const $this = jQuery(this);

			const dataPassage = $this.attr('data-passage');
			const initialDataPassage = window && window.SugarCube && window.SugarCube.State && window.SugarCube.State.passage;
			const savedYOffset = window.pageYOffset;

			// Toggle "aria-pressed" status, if the attribute exists.
			if ($this.is('[aria-pressed]')) {
				$this.attr('aria-pressed', $this.attr('aria-pressed') === 'true' ? 'false' : 'true');
			}

			// Call the true handler.
			fn.apply(this, arguments);

			const doJump = () => window.scrollTo(0, savedYOffset);
			if (dataPassage && (window.lastDataPassageLink === dataPassage || initialDataPassage === dataPassage)) {
				if (Config.navigation.rememberYPos && (!V.options || V.options && V.options.scrollRemember !== false)) doJump();
			}
			window.lastDataPassageLink = dataPassage;
		};
	}

	function oneClickFnWrapper(fn) {
		return onClickFnWrapper(function () {
			// Remove both event handlers (keypress & click) and the other components.
			jQuery(this)
				.off('.aria-clickable')
				.removeAttr('role tabindex aria-controls aria-pressed')
				.filter('button')
				.prop('disabled', true);

			// Call the true handler.
			fn.apply(this, arguments);
		});
	}

	jQuery.fn.extend({
		/*
			Extend jQuery's chainable methods with an `ariaClick()` method.
		*/
		ariaClick(options, handler) {
			// Bail out if there are no target element(s) or parameters.
			if (this.length === 0 || arguments.length === 0) {
				return this;
			}

			let opts = options;
			let fn   = handler;

			if (fn == null) { // lazy equality for null
				fn   = opts;
				opts = undefined;
			}

			opts = jQuery.extend({
				namespace : undefined,
				one       : false,
				selector  : undefined,
				data      : undefined,
				role      : undefined,
				controls  : undefined,
				pressed   : undefined,
				label     : undefined
			}, opts);

			if (typeof opts.namespace !== 'string') {
				opts.namespace = '';
			}
			else if (opts.namespace[0] !== '.') {
				opts.namespace = `.${opts.namespace}`;
			}

			if (typeof opts.pressed === 'boolean') {
				opts.pressed = opts.pressed ? 'true' : 'false';
			}

			// Set `type` to `button` to suppress "submit" semantics, for <button> elements.
			this.filter('button').prop('type', 'button');

			// Set `role`.
			if (opts.role != null) { // lazy equality for null
				this.attr('role', opts.role);
			}

			// Elsewise, set `role` to default values based on elements.
			else {
				this
					// Elements without an existing `role`.
					.not('[role]')

					// Elements that are `<a>` OR with `data-passage`.
					.filter('a,[data-passage]')
					.attr('role', 'link')
					.end()

					// Elements that are not `<a>` AND without `data-passage`.
					// WARNING: Do not merge the separate `.not()` instances below.  It is correct as-is.
					.not('a')
					.not('[data-passage]')
					.attr('role', 'button')
					.end()
					.end()

					.end();
			}

			// Set `tabindex` to `0` to make them focusable (unnecessary on <button> elements, but it doesn't hurt).
			this.attr('tabindex', 0);

			// Set `aria-controls`.
			if (opts.controls != null) { // lazy equality for null
				this.attr('aria-controls', opts.controls);
			}

			// Set `aria-pressed`.
			if (opts.pressed != null) { // lazy equality for null
				this.attr('aria-pressed', opts.pressed);
			}

			// Set `aria-label` and `title`.
			if (opts.label != null) { // lazy equality for null
				this.attr({
					'aria-label' : opts.label,
					title        : opts.label
				});
			}

			// Set the keypress handlers, for non-<button> elements.
			// NOTE: For the single-use case, the click handler will also remove this handler.
			this.not('button').on(
				`keypress.aria-clickable${opts.namespace}`,
				opts.selector,
				onKeypressFn
			);

			// Set the click handlers.
			// NOTE: To ensure both handlers are properly removed, `one()` must not be used here.
			this.on(
				`click.aria-clickable${opts.namespace}`,
				opts.selector,
				opts.data,
				opts.one ? oneClickFnWrapper(fn) : onClickFnWrapper(fn)
			);

			// Return `this` for further chaining.
			return this;
		},

		/*
			Extend jQuery's chainable methods with an `ariaDisabled()` method.
		*/
		ariaDisabled(disable) {
			// Bail out if there are no target element(s) or parameters.
			if (this.length === 0 || arguments.length === 0) {
				return this;
			}

			/*
				NOTE: We use `<jQuery>.each()` callbacks to invoke the `<Element>.setAttribute()`
				methods in the following because the `<jQuery>.attr()` method does not allow you
				to set a content attribute without a value, which is recommended for boolean
				content attributes by the HTML specification.
			*/

			const $nonDisableable = this.not('button,fieldset,input,menuitem,optgroup,option,select,textarea');
			const $disableable    = this.filter('button,fieldset,input,menuitem,optgroup,option,select,textarea');

			if (disable) {
				// Add boolean content attribute `disabled` and set non-boolean content attribute
				// `aria-disabled` to `'true'`, for non-disableable elements.
				$nonDisableable.each(function () {
					this.setAttribute('disabled', '');
					this.setAttribute('aria-disabled', 'true');
				});

				// Set IDL attribute `disabled` to `true` and set non-boolean content attribute
				// `aria-disabled` to `'true'`, for disableable elements.
				$disableable.each(function () {
					this.disabled = true;
					this.setAttribute('aria-disabled', 'true');
				});
			}
			else {
				// Remove content attributes `disabled` and `aria-disabled`, for non-disableable elements.
				$nonDisableable.each(function () {
					this.removeAttribute('disabled');
					this.removeAttribute('aria-disabled');
				});

				// Set IDL attribute `disabled` to `false` and remove content attribute `aria-disabled`,
				// for disableable elements.
				$disableable.each(function () {
					this.disabled = false;
					this.removeAttribute('aria-disabled');
				});
			}

			// Return `this` for further chaining.
			return this;
		},

		/*
			Extend jQuery's chainable methods with an `ariaIsDisabled()` method.
		*/
		ariaIsDisabled() {
			// Check content attribute `disabled`.
			//
			// NOTE: We simply check the `disabled` content attribute for all elements
			// since we have to check it for non-disableable elements and it may also
			// be used for disableable elements since their `disabled` IDL attribute
			// is required to reflect the status of their `disabled` content attribute,
			// and vice versa, by the HTML specification.
			// return this.toArray().some(el => el.hasAttribute('disabled'));
			return this.is('[disabled]');
		}
	});
})();

/*
	Wikifier methods plugin.

	`jQuery.wikiWithOptions(options, sources…)`
	    Wikifies the given content source(s), as directed by the given options.

	`jQuery.wiki(sources…)`
	    Wikifies the given content source(s).

	`<jQuery>.wikiWithOptions(options, sources…)`
	    Wikifies the given content source(s) and appends the result to the target
	    element(s), as directed by the given options.

	`<jQuery>.wiki(sources…)`
	    Wikifies the given content source(s) and appends the result to the target
	    element(s).
*/
(() => {
	'use strict';

	jQuery.extend({
		/*
			Extend jQuery's static methods with a `wikiWithOptions()` method.
		*/
		wikiWithOptions(options, ...sources) {
			// Bail out, if there are no content sources.
			if (sources.length === 0) {
				return;
			}

			// Wikify the content sources into a fragment.
			const frag = document.createDocumentFragment();
			sources.forEach(content => new Wikifier(frag, content, options));

			// Gather the text of any error elements within the fragment…
			const errors = [...frag.querySelectorAll('.error')]
				.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));

			// …and throw an exception, if there were any errors.
			if (errors.length > 0) {
				throw new Error(errors.join('; '));
			}
		},

		/*
			Extend jQuery's static methods with a `wiki()` method.
		*/
		wiki(...sources) {
			this.wikiWithOptions(undefined, ...sources);
		}
	});

	jQuery.fn.extend({
		/*
			Extend jQuery's chainable methods with a `wikiWithOptions()` method.
		*/
		wikiWithOptions(options, ...sources) {
			// Bail out if there are no target element(s) or content sources.
			if (this.length === 0 || sources.length === 0) {
				return this;
			}

			// Wikify the content sources into a fragment.
			const frag = document.createDocumentFragment();
			sources.forEach(content => new Wikifier(frag, content, options));

			// Append the fragment to the target element(s).
			this.append(frag);

			// Return `this` for further chaining.
			return this;
		},

		/*
			Extend jQuery's chainable methods with a `wiki()` method.
		*/
		wiki(...sources) {
			return this.wikiWithOptions(undefined, ...sources);
		}
	});
})();


code:
/***********************************************************************************************************************

	lib/nodetyper.js

	Copyright © 2020–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/
/* global Util */

var NodeTyper = (() => { // eslint-disable-line no-unused-vars, no-var
	'use strict';

	/*******************************************************************************************************************
		NodeTyper Class.
	*******************************************************************************************************************/
	class NodeTyper {
		constructor(config) {
			if (typeof config !== 'object' || config === null) {
				throw new Error(`config parameter must be an object (received: ${Util.getType(config)})`);
			}
			if (!config.hasOwnProperty('targetNode') || !(config.targetNode instanceof Node)) {
				throw new Error('config parameter object "targetNode" property must be a node');
			}

			Object.defineProperties(this, {
				node : {
					value : config.targetNode
				},

				childNodes : {
					value : []
				},

				nodeValue : {
					writable : true,
					value    : ''
				},

				appendTo : {
					writable : true,
					value    : config.parentNode || null
				},

				classNames : {
					writable : true,
					value    : config.classNames || null
				},

				finished : {
					writable : true,
					value    : false
				}
			});

			const node = this.node;

			if (node.nodeValue) {
				this.nodeValue = node.nodeValue;
				node.nodeValue = '';
			}

			let childNode;

			while ((childNode = node.firstChild) !== null) {
				this.childNodes.push(new NodeTyper({
					targetNode : childNode,
					parentNode : node,
					classNames : this.classNames
				}));

				node.removeChild(childNode);
			}
		}

		finish() {
			while (this.type(true)) /* no-op */;
			return false;
		}

		type(flush) {
			if (this.finished) {
				return false;
			}

			if (this.appendTo) {
				this.appendTo.appendChild(this.node);
				this.appendTo = null;

				// Immediately finish typing this node if….
				if (
					// …it's neither a element or text node.
					this.node.nodeType !== Node.ELEMENT_NODE && this.node.nodeType !== Node.TEXT_NODE

					// …or the computed value of its parent node's `display` property is `'none'`.
					|| jQuery(this.node.parentNode).css('display') === 'none'
				) {
					return this.finish();
				}

				if (this.node.parentNode && this.classNames) {
					jQuery(this.node.parentNode).addClass(this.classNames);
				}
			}

			if (this.nodeValue) {
				if (flush) {
					// Concatenate here in case we've already done some processing.
					this.node.nodeValue += this.nodeValue;
					this.nodeValue = '';
				}
				else {
					// Use `Util.charAndPosAt()` here to properly handle Unicode code points
					// that are comprised of surrogate pairs.
					const { char, start, end } = Util.charAndPosAt(this.nodeValue, 0);
					this.node.nodeValue += char;
					this.nodeValue = this.nodeValue.slice(1 + end - start);
				}

				return true;
			}

			if (this.classNames) {
				jQuery(this.node.parentNode).removeClass(this.classNames);
				this.classNames = null;
			}

			const childNodes = this.childNodes;

			while (childNodes.length > 0) {
				if (childNodes[0].type()) {
					return true;
				}

				childNodes.shift();
			}

			this.finished = true;
			return false;
		}
	}

	/*******************************************************************************************************************
		Module Exports.
	*******************************************************************************************************************/
	return NodeTyper;
})();


code:
/***********************************************************************************************************************

	lib/prngwrapper.js

	Copyright © 2013–2021 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.
	Use of this source code is governed by a BSD 2-clause "Simplified" License, which may be found in the LICENSE file.

***********************************************************************************************************************/

var PRNGWrapper = (() => { // eslint-disable-line no-unused-vars, no-var
	'use strict';

	/*******************************************************************************************************************
		PRNGWrapper Class.
	*******************************************************************************************************************/
	class PRNGWrapper {
		constructor(seed, options) {
			/* Create the Math.seedrandom initialisation to use the state object. */
			/* Pass seed through the constructor to return either a copy of itself, or a new generated seed. */
			const seeder = new Math.seedrandom(seed, { state : false }, (_, seed) => ({ seed }));
			const prngObj = new Math.seedrandom(seeder.seed, options);
			/* eslint-disable new-cap */
			Object.defineProperties(this, {
				_prng : {
					value : prngObj
				},
				seed : {
					writable : true,
					value    : seeder.seed
				},
				pull : {
					writable : true,
					value    : 0
				},
				state : {
					value : prngObj.state
				},
				random : {
					value() {
						++this.pull;
						return this._prng();
					}
				}
			});
			/* eslint-enable new-cap */
		}

		static marshal(prng) {
			/* Modify warning message so that it instead checks for only the new state property. */
			if (!prng || !prng.hasOwnProperty('state')) {
				throw new Error('PRNG is missing required data');
			}

			return {
				prng : prng.state(),
				seed : prng.seed,
				pull : prng.pull
			};
		}

		static unmarshal(prngObj) {
			/* Modify warning message so that it instead checks for only the new state property. */
			if (!prngObj || !prngObj.hasOwnProperty('state')) {
				throw new Error('PRNG object is missing required state data');
			}

			/*
				Create a new PRNG using the original seed and pull values from it until it
				has reached the original pull count.
			*/
			const prng = new PRNGWrapper(prngObj.seed, { state : prngObj.state });
			prng.pull = prngObj.pull;
			return prng;
		}
	}

	/*******************************************************************************************************************
		Module Exports.
	*******************************************************************************************************************/
	return PRNGWrapper;
})();