diff --git a/Changelog.txt b/Changelog.txt
index ec93db6b02ead801e2756b00b36c546fd89f633b..5e86ae1f3f330818beae544242542c37c42f2155 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -2,6 +2,12 @@ Pregmod
 
 0.10.7.1-3.7.x
 
+	8/30/2020
+
+	1
+	-added hormone controls to the dairy
+	-various fixes and code cleaning
+
 	8/27/2020
 
 	0
diff --git a/compile_themes.bat b/compile_themes.bat
index 09d80826ae2fd8a199455467fc9176a4a1073882..460d1c11704b80a49674276031f87aa63a2f66c0 100644
--- a/compile_themes.bat
+++ b/compile_themes.bat
@@ -6,8 +6,6 @@ for /d %%i in (%~dp0\themes\*) do (
 )
 cd %back%
 
-
-PAUSE
 EXIT /B %ERRORLEVEL%
 
 :compileDirectory
diff --git a/devNotes/sugarcube stuff/sugarcube-fc-changes.patch b/devNotes/sugarcube stuff/sugarcube-fc-changes.patch
index 5956801c0326edd78ebbcf6b53fb6213da1ba61f..8fbf0a2439de9a5353e4bc9a3de53ac7ce31e675 100644
--- a/devNotes/sugarcube stuff/sugarcube-fc-changes.patch	
+++ b/devNotes/sugarcube stuff/sugarcube-fc-changes.patch	
@@ -340,7 +340,7 @@ index bd211cd..712e9e1 100644
  	}
  
 diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js
-index cbfc9d4..6e04094 100644
+index 7383bd0..af57d75 100644
 --- a/src/macros/macrolib.js
 +++ b/src/macros/macrolib.js
 @@ -90,7 +90,7 @@
@@ -361,7 +361,7 @@ index cbfc9d4..6e04094 100644
  			}
  		}
  	});
-@@ -784,7 +784,7 @@
+@@ -807,7 +807,7 @@
  				}
  			}
  			catch (ex) {
@@ -882,10 +882,10 @@ index 184c92b..d445630 100644
  		for (let i = 1, iend = historyArr.length; i < iend; ++i) {
  			delta.push(Diff.diff(historyArr[i - 1], historyArr[i]));
 diff --git a/src/ui.js b/src/ui.js
-index 22fd8ae..97fe8b3 100644
+index 8402bb8..415e74c 100644
 --- a/src/ui.js
 +++ b/src/ui.js
-@@ -252,7 +252,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -254,7 +254,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  				return $btn;
  			}
  
@@ -894,7 +894,7 @@ index 22fd8ae..97fe8b3 100644
  			const $tbody = jQuery(document.createElement('tbody'));
  
  			if (Save.autosave.ok()) {
-@@ -270,7 +270,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -272,7 +272,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  					.text('A') // '\u25C6' Black Diamond
  					.appendTo($tdSlot);
  
@@ -903,7 +903,7 @@ index 22fd8ae..97fe8b3 100644
  					// Add the load button.
  					$tdLoad.append(
  						createButton('load', 'ui-close', L10n.get('savesLabelLoad'), 'auto', () => {
-@@ -280,13 +280,13 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -282,13 +282,13 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  
  					// Add the description (title and datestamp).
  					jQuery(document.createElement('div'))
@@ -920,7 +920,7 @@ index 22fd8ae..97fe8b3 100644
  								: `<em>${L10n.get('savesUnknownDate')}</em>`
  						)
  						.appendTo($tdDesc);
-@@ -322,7 +322,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -324,7 +324,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  					.appendTo($tbody);
  			}
  
@@ -929,7 +929,7 @@ index 22fd8ae..97fe8b3 100644
  				const $tdSlot = jQuery(document.createElement('td'));
  				const $tdLoad = jQuery(document.createElement('td'));
  				const $tdDesc = jQuery(document.createElement('td'));
-@@ -331,9 +331,10 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -333,9 +333,10 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  				// Add the slot ID.
  				$tdSlot.append(document.createTextNode(i + 1));
  
@@ -942,7 +942,7 @@ index 22fd8ae..97fe8b3 100644
  						createButton('load', 'ui-close', L10n.get('savesLabelLoad'), i, slot => {
  							jQuery(document).one(':dialogclosed', () => Save.slots.load(slot));
  						})
-@@ -341,13 +342,13 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -343,13 +344,13 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
  
  					// Add the description (title and datestamp).
  					jQuery(document.createElement('div'))
diff --git a/devTools/tweeGo/storyFormats/sugarcube-2/format.js b/devTools/tweeGo/storyFormats/sugarcube-2/format.js
index e230a079e3bb3f73e1a5785aafd0948885eafc32..be4dc1a85cc79b715e119bef7cf41fa60a1307d7 100644
--- a/devTools/tweeGo/storyFormats/sugarcube-2/format.js
+++ b/devTools/tweeGo/storyFormats/sugarcube-2/format.js
@@ -1 +1 @@
-window.storyFormat({"name":"SugarCube","version":"2.33.1","description":"A full featured, highly customizable story format.  See its <a href=\"http://www.motoslave.net/sugarcube/2/#documentation\" target=\"_blank\">documentation</a>.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"<!DOCTYPE html>\n<html data-init=\"no-js\">\n<head>\n<meta charset=\"UTF-8\" />\n<title>{{STORY_NAME}}</title>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<!--\n\nSugarCube (v2.33.1): A free (gratis and libre) story format.\n\nCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-->\n<script id=\"script-libraries\" type=\"text/javascript\">\nif(document.head&&document.addEventListener&&document.querySelector&&Object.create&&Object.freeze&&JSON){document.documentElement.setAttribute(\"data-init\", \"loading\");\n/*! @source http://purl.eligrey.com/github/classList.js/blob/1.2.20171210/classList.js */\n\"document\"in self&&(\"classList\"in document.createElement(\"_\")&&(!document.createElementNS||\"classList\"in document.createElementNS(\"http://www.w3.org/2000/svg\",\"g\"))||!function(t){\"use strict\";if(\"Element\"in t){var e=\"classList\",n=\"prototype\",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\\s+|\\s+$/g,\"\")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(\"\"===e)throw new c(\"SYNTAX_ERR\",\"The token must not be empty.\");if(/\\s/.test(e))throw new c(\"INVALID_CHARACTER_ERR\",\"The token must not contain space characters.\");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute(\"class\")||\"\"),n=e?e.split(/\\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute(\"class\",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+\"\")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+\"\",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+\"\",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&\"remove\":e!==!1&&\"add\";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+\"\");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(\" \")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){\"use strict\";var t=document.createElement(\"_\");if(t.classList.add(\"c1\",\"c2\"),!t.classList.contains(\"c2\")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e(\"add\"),e(\"remove\")}if(t.classList.toggle(\"c3\",!1),t.classList.contains(\"c3\")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}\"replace\"in document.createElement(\"_\").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(\" \"),i=n.indexOf(t+\"\");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());\n/*!\n * https://github.com/es-shims/es5-shim\n * @license es5-shim Copyright 2009-2020 by contributors, MIT License\n * see https://github.com/es-shims/es5-shim/blob/v4.5.14/LICENSE\n */\n(function(t,r){\"use strict\";if(typeof define===\"function\"&&define.amd){define(r)}else if(typeof exports===\"object\"){module.exports=r()}else{t.returnExports=r()}})(this,function(){var t=Array;var r=t.prototype;var e=Object;var n=e.prototype;var i=Function;var a=i.prototype;var o=String;var f=o.prototype;var u=Number;var l=u.prototype;var s=r.slice;var c=r.splice;var v=r.push;var h=r.unshift;var p=r.concat;var y=r.join;var d=a.call;var g=a.apply;var w=Math.max;var b=Math.min;var T=n.toString;var m=typeof Symbol===\"function\"&&typeof Symbol.toStringTag===\"symbol\";var D;var S=Function.prototype.toString,x=/^\\s*class /,O=function isES6ClassFn(t){try{var r=S.call(t);var e=r.replace(/\\/\\/.*\\n/g,\"\");var n=e.replace(/\\/\\*[.\\s\\S]*\\*\\//g,\"\");var i=n.replace(/\\n/gm,\" \").replace(/ {2}/g,\" \");return x.test(i)}catch(a){return false}},E=function tryFunctionObject(t){try{if(O(t)){return false}S.call(t);return true}catch(r){return false}},j=\"[object Function]\",I=\"[object GeneratorFunction]\",D=function isCallable(t){if(!t){return false}if(typeof t!==\"function\"&&typeof t!==\"object\"){return false}if(m){return E(t)}if(O(t)){return false}var r=T.call(t);return r===j||r===I};var M;var U=RegExp.prototype.exec,$=function tryRegexExec(t){try{U.call(t);return true}catch(r){return false}},F=\"[object RegExp]\";M=function isRegex(t){if(typeof t!==\"object\"){return false}return m?$(t):T.call(t)===F};var N;var C=String.prototype.valueOf,k=function tryStringObject(t){try{C.call(t);return true}catch(r){return false}},A=\"[object String]\";N=function isString(t){if(typeof t===\"string\"){return true}if(typeof t!==\"object\"){return false}return m?k(t):T.call(t)===A};var R=e.defineProperty&&function(){try{var t={};e.defineProperty(t,\"x\",{enumerable:false,value:t});for(var r in t){return false}return t.x===t}catch(n){return false}}();var P=function(t){var r;if(R){r=function(t,r,n,i){if(!i&&r in t){return}e.defineProperty(t,r,{configurable:true,enumerable:false,writable:true,value:n})}}else{r=function(t,r,e,n){if(!n&&r in t){return}t[r]=e}}return function defineProperties(e,n,i){for(var a in n){if(t.call(n,a)){r(e,a,n[a],i)}}}}(n.hasOwnProperty);var J=function isPrimitive(t){var r=typeof t;return t===null||r!==\"object\"&&r!==\"function\"};var Y=u.isNaN||function isActualNaN(t){return t!==t};var z={ToInteger:function ToInteger(t){var r=+t;if(Y(r)){r=0}else if(r!==0&&r!==1/0&&r!==-(1/0)){r=(r>0||-1)*Math.floor(Math.abs(r))}return r},ToPrimitive:function ToPrimitive(t){var r,e,n;if(J(t)){return t}e=t.valueOf;if(D(e)){r=e.call(t);if(J(r)){return r}}n=t.toString;if(D(n)){r=n.call(t);if(J(r)){return r}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError(\"can't convert \"+t+\" to object\")}return e(t)},ToUint32:function ToUint32(t){return t>>>0}};var Z=function Empty(){};P(a,{bind:function bind(t){var r=this;if(!D(r)){throw new TypeError(\"Function.prototype.bind called on incompatible \"+r)}var n=s.call(arguments,1);var a;var o=function(){if(this instanceof a){var i=g.call(r,this,p.call(n,s.call(arguments)));if(e(i)===i){return i}return this}else{return g.call(r,t,p.call(n,s.call(arguments)))}};var f=w(0,r.length-n.length);var u=[];for(var l=0;l<f;l++){v.call(u,\"$\"+l)}a=i(\"binder\",\"return function (\"+y.call(u,\",\")+\"){ return binder.apply(this, arguments); }\")(o);if(r.prototype){Z.prototype=r.prototype;a.prototype=new Z;Z.prototype=null}return a}});var G=d.bind(n.hasOwnProperty);var H=d.bind(n.toString);var W=d.bind(s);var B=g.bind(s);if(typeof document===\"object\"&&document&&document.documentElement){try{W(document.documentElement.childNodes)}catch(X){var L=W;var q=B;W=function arraySliceIE(t){var r=[];var e=t.length;while(e-- >0){r[e]=t[e]}return q(r,L(arguments,1))};B=function arraySliceApplyIE(t,r){return q(W(t),r)}}}var K=d.bind(f.slice);var Q=d.bind(f.split);var V=d.bind(f.indexOf);var _=d.bind(v);var tt=d.bind(n.propertyIsEnumerable);var rt=d.bind(r.sort);var et=t.isArray||function isArray(t){return H(t)===\"[object Array]\"};var nt=[].unshift(0)!==1;P(r,{unshift:function(){h.apply(this,arguments);return this.length}},nt);P(t,{isArray:et});var it=e(\"a\");var at=it[0]!==\"a\"||!(0 in it);var ot=function properlyBoxed(t){var r=true;var e=true;var n=false;if(t){try{t.call(\"foo\",function(t,e,n){if(typeof n!==\"object\"){r=false}});t.call([1],function(){\"use strict\";e=typeof this===\"string\"},\"x\")}catch(i){n=true}}return!!t&&!n&&r&&e};P(r,{forEach:function forEach(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=-1;var i=z.ToUint32(e.length);var a;if(arguments.length>1){a=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.forEach callback must be a function\")}while(++n<i){if(n in e){if(typeof a===\"undefined\"){t(e[n],n,r)}else{t.call(a,e[n],n,r)}}}}},!ot(r.forEach));P(r,{map:function map(r){var e=z.ToObject(this);var n=at&&N(this)?Q(this,\"\"):e;var i=z.ToUint32(n.length);var a=t(i);var o;if(arguments.length>1){o=arguments[1]}if(!D(r)){throw new TypeError(\"Array.prototype.map callback must be a function\")}for(var f=0;f<i;f++){if(f in n){if(typeof o===\"undefined\"){a[f]=r(n[f],f,e)}else{a[f]=r.call(o,n[f],f,e)}}}return a}},!ot(r.map));P(r,{filter:function filter(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.filter callback must be a function\")}for(var f=0;f<n;f++){if(f in e){a=e[f];if(typeof o===\"undefined\"?t(a,f,r):t.call(o,a,f,r)){_(i,a)}}}return i}},!ot(r.filter));P(r,{every:function every(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.every callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&!(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return false}}return true}},!ot(r.every));P(r,{some:function some(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.some callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return true}}return false}},!ot(r.some));var ft=false;if(r.reduce){ft=typeof r.reduce.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduce:function reduce(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduce callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduce of empty array with no initial value\")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in e){a=e[i++];break}if(++i>=n){throw new TypeError(\"reduce of empty array with no initial value\")}}while(true)}for(;i<n;i++){if(i in e){a=t(a,e[i],i,r)}}return a}},!ft);var ut=false;if(r.reduceRight){ut=typeof r.reduceRight.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduceRight:function reduceRight(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduceRight callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduceRight of empty array with no initial value\")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in e){i=e[a--];break}if(--a<0){throw new TypeError(\"reduceRight of empty array with no initial value\")}}while(true)}if(a<0){return i}do{if(a in e){i=t(i,e[a],a,r)}}while(a--);return i}},!ut);var lt=r.indexOf&&[0,1].indexOf(1,2)!==-1;P(r,{indexOf:function indexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=0;if(arguments.length>1){n=z.ToInteger(arguments[1])}n=n>=0?n:w(0,e+n);for(;n<e;n++){if(n in r&&r[n]===t){return n}}return-1}},lt);var st=r.lastIndexOf&&[0,1].lastIndexOf(0,-3)!==-1;P(r,{lastIndexOf:function lastIndexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=e-1;if(arguments.length>1){n=b(n,z.ToInteger(arguments[1]))}n=n>=0?n:e-Math.abs(n);for(;n>=0;n--){if(n in r&&t===r[n]){return n}}return-1}},st);var ct=function(){var t=[1,2];var r=t.splice();return t.length===2&&et(r)&&r.length===0}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}else{return c.apply(this,arguments)}}},!ct);var vt=function(){var t={};r.splice.call(t,0,0,1);return t.length===1}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}var e=arguments;this.length=w(z.ToInteger(this.length),0);if(arguments.length>0&&typeof r!==\"number\"){e=W(arguments);if(e.length<2){_(e,this.length-t)}else{e[1]=z.ToInteger(r)}}return c.apply(this,e)}},!vt);var ht=function(){var r=new t(1e5);r[8]=\"x\";r.splice(1,1);return r.indexOf(\"x\")===7}();var pt=function(){var t=256;var r=[];r[t]=\"a\";r.splice(t+1,0,\"b\");return r[t]===\"a\"}();P(r,{splice:function splice(t,r){var e=z.ToObject(this);var n=[];var i=z.ToUint32(e.length);var a=z.ToInteger(t);var f=a<0?w(i+a,0):b(a,i);var u=arguments.length===0?0:arguments.length===1?i-f:b(w(z.ToInteger(r),0),i-f);var l=0;var s;while(l<u){s=o(f+l);if(G(e,s)){n[l]=e[s]}l+=1}var c=W(arguments,2);var v=c.length;var h;if(v<u){l=f;var p=i-u;while(l<p){s=o(l+u);h=o(l+v);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l+=1}l=i;var y=i-u+v;while(l>y){delete e[l-1];l-=1}}else if(v>u){l=i-u;while(l>f){s=o(l+u-1);h=o(l+v-1);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l-=1}}l=f;for(var d=0;d<c.length;++d){e[l]=c[d];l+=1}e.length=i-u+v;return n}},!ht||!pt);var yt=r.join;var dt;try{dt=Array.prototype.join.call(\"123\",\",\")!==\"1,2,3\"}catch(X){dt=true}if(dt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(N(this)?Q(this,\"\"):this,r)}},dt)}var gt=[1,2].join(undefined)!==\"1,2\";if(gt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(this,r)}},gt)}var wt=function push(t){var r=z.ToObject(this);var e=z.ToUint32(r.length);var n=0;while(n<arguments.length){r[e+n]=arguments[n];n+=1}r.length=e+n;return e+n};var bt=function(){var t={};var r=Array.prototype.push.call(t,undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:function push(t){if(et(this)){return v.apply(this,arguments)}return wt.apply(this,arguments)}},bt);var Tt=function(){var t=[];var r=t.push(undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:wt},Tt);P(r,{slice:function(t,r){var e=N(this)?Q(this,\"\"):this;return B(e,arguments)}},at);var mt=function(){try{[1,2].sort(null)}catch(t){try{[1,2].sort({})}catch(r){return false}}return true}();var Dt=function(){try{[1,2].sort(/a/);return false}catch(t){}return true}();var St=function(){try{[1,2].sort(undefined);return true}catch(t){}return false}();P(r,{sort:function sort(t){if(typeof t===\"undefined\"){return rt(this)}if(!D(t)){throw new TypeError(\"Array.prototype.sort callback must be a function\")}return rt(this,t)}},mt||!St||!Dt);var xt=!tt({toString:null},\"toString\");var Ot=tt(function(){},\"prototype\");var Et=!G(\"x\",\"0\");var jt=function(t){var r=t.constructor;return r&&r.prototype===t};var It={$applicationCache:true,$console:true,$external:true,$frame:true,$frameElement:true,$frames:true,$innerHeight:true,$innerWidth:true,$onmozfullscreenchange:true,$onmozfullscreenerror:true,$outerHeight:true,$outerWidth:true,$pageXOffset:true,$pageYOffset:true,$parent:true,$scrollLeft:true,$scrollTop:true,$scrollX:true,$scrollY:true,$self:true,$webkitIndexedDB:true,$webkitStorageInfo:true,$window:true,$width:true,$height:true,$top:true,$localStorage:true};var Mt=function(){if(typeof window===\"undefined\"){return false}for(var t in window){try{if(!It[\"$\"+t]&&G(window,t)&&window[t]!==null&&typeof window[t]===\"object\"){jt(window[t])}}catch(r){return true}}return false}();var Ut=function(t){if(typeof window===\"undefined\"||!Mt){return jt(t)}try{return jt(t)}catch(r){return false}};var $t=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"];var Ft=$t.length;var Nt=function isArguments(t){return H(t)===\"[object Arguments]\"};var Ct=function isArguments(t){return t!==null&&typeof t===\"object\"&&typeof t.length===\"number\"&&t.length>=0&&!et(t)&&D(t.callee)};var kt=Nt(arguments)?Nt:Ct;P(e,{keys:function keys(t){var r=D(t);var e=kt(t);var n=t!==null&&typeof t===\"object\";var i=n&&N(t);if(!n&&!r&&!e){throw new TypeError(\"Object.keys called on a non-object\")}var a=[];var f=Ot&&r;if(i&&Et||e){for(var u=0;u<t.length;++u){_(a,o(u))}}if(!e){for(var l in t){if(!(f&&l===\"prototype\")&&G(t,l)){_(a,o(l))}}}if(xt){var s=Ut(t);for(var c=0;c<Ft;c++){var v=$t[c];if(!(s&&v===\"constructor\")&&G(t,v)){_(a,v)}}}return a}});var At=e.keys&&function(){return e.keys(arguments).length===2}(1,2);var Rt=e.keys&&function(){var t=e.keys(arguments);return arguments.length!==1||t.length!==1||t[0]!==1}(1);var Pt=e.keys;P(e,{keys:function keys(t){if(kt(t)){return Pt(W(t))}else{return Pt(t)}}},!At||Rt);var Jt=new Date(-0xc782b5b342b24).getUTCMonth()!==0;var Yt=new Date(-0x55d318d56a724);var zt=new Date(14496624e5);var Zt=Yt.toUTCString()!==\"Mon, 01 Jan -45875 11:59:59 GMT\";var Gt;var Ht;var Wt=Yt.getTimezoneOffset();if(Wt<-720){Gt=Yt.toDateString()!==\"Tue Jan 02 -45875\";Ht=!/^Thu Dec 10 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}else{Gt=Yt.toDateString()!==\"Mon Jan 01 -45875\";Ht=!/^Wed Dec 09 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}var Bt=d.bind(Date.prototype.getFullYear);var Xt=d.bind(Date.prototype.getMonth);var Lt=d.bind(Date.prototype.getDate);var qt=d.bind(Date.prototype.getUTCFullYear);var Kt=d.bind(Date.prototype.getUTCMonth);var Qt=d.bind(Date.prototype.getUTCDate);var Vt=d.bind(Date.prototype.getUTCDay);var _t=d.bind(Date.prototype.getUTCHours);var tr=d.bind(Date.prototype.getUTCMinutes);var rr=d.bind(Date.prototype.getUTCSeconds);var er=d.bind(Date.prototype.getUTCMilliseconds);var nr=[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];var ir=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];var ar=function daysInMonth(t,r){return Lt(new Date(r,t,0))};P(Date.prototype,{getFullYear:function getFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);if(t<0&&Xt(this)>11){return t+1}return t},getMonth:function getMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);if(t<0&&r>11){return 0}return r},getDate:function getDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);var e=Lt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e},getUTCFullYear:function getUTCFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);if(t<0&&Kt(this)>11){return t+1}return t},getUTCMonth:function getUTCMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);if(t<0&&r>11){return 0}return r},getUTCDate:function getUTCDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);var e=Qt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e}},Jt);P(Date.prototype,{toUTCString:function toUTCString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Vt(this);var r=Qt(this);var e=Kt(this);var n=qt(this);var i=_t(this);var a=tr(this);var o=rr(this);return nr[t]+\", \"+(r<10?\"0\"+r:r)+\" \"+ir[e]+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"}},Jt||Zt);P(Date.prototype,{toDateString:function toDateString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n}},Jt||Gt);if(Jt||Ht){Date.prototype.toString=function toString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();var i=this.getHours();var a=this.getMinutes();var o=this.getSeconds();var f=this.getTimezoneOffset();var u=Math.floor(Math.abs(f)/60);var l=Math.floor(Math.abs(f)%60);return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"+(f>0?\"-\":\"+\")+(u<10?\"0\"+u:u)+(l<10?\"0\"+l:l)};if(R){e.defineProperty(Date.prototype,\"toString\",{configurable:true,enumerable:false,writable:true})}}var or=-621987552e5;var fr=\"-000001\";var ur=Date.prototype.toISOString&&new Date(or).toISOString().indexOf(fr)===-1;var lr=Date.prototype.toISOString&&new Date(-1).toISOString()!==\"1969-12-31T23:59:59.999Z\";var sr=d.bind(Date.prototype.getTime);P(Date.prototype,{toISOString:function toISOString(){if(!isFinite(this)||!isFinite(sr(this))){throw new RangeError(\"Date.prototype.toISOString called on non-finite value.\")}var t=qt(this);var r=Kt(this);t+=Math.floor(r/12);r=(r%12+12)%12;var e=[r+1,Qt(this),_t(this),tr(this),rr(this)];t=(t<0?\"-\":t>9999?\"+\":\"\")+K(\"00000\"+Math.abs(t),0<=t&&t<=9999?-4:-6);for(var n=0;n<e.length;++n){e[n]=K(\"00\"+e[n],-2)}return t+\"-\"+W(e,0,2).join(\"-\")+\"T\"+W(e,2).join(\":\")+\".\"+K(\"000\"+er(this),-3)+\"Z\"}},ur||lr);var cr=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(or).toJSON().indexOf(fr)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!cr){Date.prototype.toJSON=function toJSON(t){var r=e(this);var n=z.ToPrimitive(r);if(typeof n===\"number\"&&!isFinite(n)){return null}var i=r.toISOString;if(!D(i)){throw new TypeError(\"toISOString property is not callable\")}return i.call(r)}}var vr=Date.parse(\"+033658-09-27T01:46:40.000Z\")===1e15;var hr=!isNaN(Date.parse(\"2012-04-04T24:00:00.500Z\"))||!isNaN(Date.parse(\"2012-11-31T23:59:59.000Z\"))||!isNaN(Date.parse(\"2012-12-31T23:59:60.000Z\"));var pr=isNaN(Date.parse(\"2000-01-01T00:00:00.000Z\"));if(pr||hr||!vr){var yr=Math.pow(2,31)-1;var dr=Y(new Date(1970,0,1,0,0,0,yr+1).getTime());Date=function(t){var r=function Date(e,n,i,a,f,u,l){var s=arguments.length;var c;if(this instanceof t){var v=u;var h=l;if(dr&&s>=7&&l>yr){var p=Math.floor(l/yr)*yr;var y=Math.floor(p/1e3);v+=y;h-=y*1e3}c=s===1&&o(e)===e?new t(r.parse(e)):s>=7?new t(e,n,i,a,f,v,h):s>=6?new t(e,n,i,a,f,v):s>=5?new t(e,n,i,a,f):s>=4?new t(e,n,i,a):s>=3?new t(e,n,i):s>=2?new t(e,n):s>=1?new t(e instanceof t?+e:e):new t}else{c=t.apply(this,arguments)}if(!J(c)){P(c,{constructor:r},true)}return c};var e=new RegExp(\"^\"+\"(\\\\d{4}|[+-]\\\\d{6})\"+\"(?:-(\\\\d{2})\"+\"(?:-(\\\\d{2})\"+\"(?:\"+\"T(\\\\d{2})\"+\":(\\\\d{2})\"+\"(?:\"+\":(\\\\d{2})\"+\"(?:(\\\\.\\\\d{1,}))?\"+\")?\"+\"(\"+\"Z|\"+\"(?:\"+\"([-+])\"+\"(\\\\d{2})\"+\":(\\\\d{2})\"+\")\"+\")?)?)?)?\"+\"$\");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function dayFromMonth(t,r){var e=r>1?1:0;return n[r]+Math.floor((t-1969+e)/4)-Math.floor((t-1901+e)/100)+Math.floor((t-1601+e)/400)+365*(t-1970)};var a=function toUTC(r){var e=0;var n=r;if(dr&&n>yr){var i=Math.floor(n/yr)*yr;var a=Math.floor(i/1e3);e+=a;n-=a*1e3}return u(new t(1970,0,1,0,0,e,n))};for(var f in t){if(G(t,f)){r[f]=t[f]}}P(r,{now:t.now,UTC:t.UTC},true);r.prototype=t.prototype;P(r.prototype,{constructor:r},true);var l=function parse(r){var n=e.exec(r);if(n){var o=u(n[1]),f=u(n[2]||1)-1,l=u(n[3]||1)-1,s=u(n[4]||0),c=u(n[5]||0),v=u(n[6]||0),h=Math.floor(u(n[7]||0)*1e3),p=Boolean(n[4]&&!n[8]),y=n[9]===\"-\"?1:-1,d=u(n[10]||0),g=u(n[11]||0),w;var b=c>0||v>0||h>0;if(s<(b?24:25)&&c<60&&v<60&&h<1e3&&f>-1&&f<12&&d<24&&g<60&&l>-1&&l<i(o,f+1)-i(o,f)){w=((i(o,f)+l)*24+s+d*y)*60;w=((w+c+g*y)*60+v)*1e3+h;if(p){w=a(w)}if(-864e13<=w&&w<=864e13){return w}}return NaN}return t.parse.apply(this,arguments)};P(r,{parse:l});return r}(Date)}if(!Date.now){Date.now=function now(){return(new Date).getTime()}}var gr=l.toFixed&&(8e-5.toFixed(3)!==\"0.000\"||.9.toFixed(0)!==\"1\"||1.255.toFixed(2)!==\"1.25\"||(1000000000000000128).toFixed(0)!==\"1000000000000000128\");var wr={base:1e7,size:6,data:[0,0,0,0,0,0],multiply:function multiply(t,r){var e=-1;var n=r;while(++e<wr.size){n+=t*wr.data[e];wr.data[e]=n%wr.base;n=Math.floor(n/wr.base)}},divide:function divide(t){var r=wr.size;var e=0;while(--r>=0){e+=wr.data[r];wr.data[r]=Math.floor(e/t);e=e%t*wr.base}},numToString:function numToString(){var t=wr.size;var r=\"\";while(--t>=0){if(r!==\"\"||t===0||wr.data[t]!==0){var e=o(wr.data[t]);if(r===\"\"){r=e}else{r+=K(\"0000000\",0,7-e.length)+e}}}return r},pow:function pow(t,r,e){return r===0?e:r%2===1?pow(t,r-1,e*t):pow(t*t,r/2,e)},log:function log(t){var r=0;var e=t;while(e>=4096){r+=12;e/=4096}while(e>=2){r+=1;e/=2}return r}};var br=function toFixed(t){var r,e,n,i,a,f,l,s;r=u(t);r=Y(r)?0:Math.floor(r);if(r<0||r>20){throw new RangeError(\"Number.toFixed called with invalid number of decimals\")}e=u(this);if(Y(e)){return\"NaN\"}if(e<=-1e21||e>=1e21){return o(e)}n=\"\";if(e<0){n=\"-\";e=-e}i=\"0\";if(e>1e-21){a=wr.log(e*wr.pow(2,69,1))-69;f=a<0?e*wr.pow(2,-a,1):e/wr.pow(2,a,1);f*=4503599627370496;a=52-a;if(a>0){wr.multiply(0,f);l=r;while(l>=7){wr.multiply(1e7,0);l-=7}wr.multiply(wr.pow(10,l,1),0);l=a-1;while(l>=23){wr.divide(1<<23);l-=23}wr.divide(1<<l);wr.multiply(1,1);wr.divide(2);i=wr.numToString()}else{wr.multiply(0,f);wr.multiply(1<<-a,0);i=wr.numToString()+K(\"0.00000000000000000000\",2,2+r)}}if(r>0){s=i.length;if(s<=r){i=n+K(\"0.0000000000000000000\",0,r-s+2)+i}else{i=n+K(i,0,s-r)+\".\"+K(i,s-r)}}else{i=n+i}return i};P(l,{toFixed:br},gr);var Tr=function(){try{return 1..toPrecision(undefined)===\"1\"}catch(t){return true}}();var mr=l.toPrecision;P(l,{toPrecision:function toPrecision(t){return typeof t===\"undefined\"?mr.call(this):mr.call(this,t)}},Tr);if(\"ab\".split(/(?:ab)*/).length!==2||\".\".split(/(.?)(.?)/).length!==4||\"tesst\".split(/(s)*/)[1]===\"t\"||\"test\".split(/(?:)/,-1).length!==4||\"\".split(/.?/).length||\".\".split(/()()/).length>1){(function(){var t=typeof/()??/.exec(\"\")[1]===\"undefined\";var r=Math.pow(2,32)-1;f.split=function(e,n){var i=String(this);if(typeof e===\"undefined\"&&n===0){return[]}if(!M(e)){return Q(this,e,n)}var a=[];var o=(e.ignoreCase?\"i\":\"\")+(e.multiline?\"m\":\"\")+(e.unicode?\"u\":\"\")+(e.sticky?\"y\":\"\"),f=0,u,l,s,c;var h=new RegExp(e.source,o+\"g\");if(!t){u=new RegExp(\"^\"+h.source+\"$(?!\\\\s)\",o)}var p=typeof n===\"undefined\"?r:z.ToUint32(n);l=h.exec(i);while(l){s=l.index+l[0].length;if(s>f){_(a,K(i,f,l.index));if(!t&&l.length>1){l[0].replace(u,function(){for(var t=1;t<arguments.length-2;t++){if(typeof arguments[t]===\"undefined\"){l[t]=void 0}}})}if(l.length>1&&l.index<i.length){v.apply(a,W(l,1))}c=l[0].length;f=s;if(a.length>=p){break}}if(h.lastIndex===l.index){h.lastIndex++}l=h.exec(i)}if(f===i.length){if(c||!h.test(\"\")){_(a,\"\")}}else{_(a,K(i,f))}return a.length>p?W(a,0,p):a}})()}else if(\"0\".split(void 0,0).length){f.split=function split(t,r){if(typeof t===\"undefined\"&&r===0){return[]}return Q(this,t,r)}}var Dr=f.replace;var Sr=function(){var t=[];\"x\".replace(/x(.)?/g,function(r,e){_(t,e)});return t.length===1&&typeof t[0]===\"undefined\"}();if(!Sr){f.replace=function replace(t,r){var e=D(r);var n=M(t)&&/\\)[*?]/.test(t.source);if(!e||!n){return Dr.call(this,t,r)}else{var i=function(e){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(e)||[];t.lastIndex=i;_(a,arguments[n-2],arguments[n-1]);return r.apply(this,a)};return Dr.call(this,t,i)}}}var xr=f.substr;var Or=\"\".substr&&\"0b\".substr(-1)!==\"b\";P(f,{substr:function substr(t,r){var e=t;if(t<0){e=w(this.length+t,0)}return xr.call(this,e,r)}},Or);var Er=\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\"+\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\"+\"\\u2029\\ufeff\";var jr=\"\\u200b\";var Ir=\"[\"+Er+\"]\";var Mr=new RegExp(\"^\"+Ir+Ir+\"*\");var Ur=new RegExp(Ir+Ir+\"*$\");var $r=f.trim&&(Er.trim()||!jr.trim());P(f,{trim:function trim(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}return o(this).replace(Mr,\"\").replace(Ur,\"\")}},$r);var Fr=d.bind(String.prototype.trim);var Nr=f.lastIndexOf&&\"abc\\u3042\\u3044\".lastIndexOf(\"\\u3042\\u3044\",2)!==-1;P(f,{lastIndexOf:function lastIndexOf(t){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var r=o(this);var e=o(t);var n=arguments.length>1?u(arguments[1]):NaN;var i=Y(n)?Infinity:z.ToInteger(n);var a=b(w(i,0),r.length);var f=e.length;var l=a+f;while(l>0){l=w(0,l-f);var s=V(K(r,l,a+f),e);if(s!==-1){return l+s}}return-1}},Nr);var Cr=f.lastIndexOf;P(f,{lastIndexOf:function lastIndexOf(t){return Cr.apply(this,arguments)}},f.lastIndexOf.length!==1);if(parseInt(Er+\"08\")!==8||parseInt(Er+\"0x16\")!==22){parseInt=function(t){var r=/^[-+]?0[xX]/;return function parseInt(e,n){if(typeof e===\"symbol\"){\"\"+e}var i=Fr(String(e));var a=u(n)||(r.test(i)?16:10);return t(i,a)}}(parseInt)}if(1/parseFloat(\"-0\")!==-Infinity){parseFloat=function(t){return function parseFloat(r){var e=Fr(String(r));var n=t(e);return n===0&&K(e,0,1)===\"-\"?-0:n}}(parseFloat)}if(String(new RangeError(\"test\"))!==\"RangeError: test\"){var kr=function toString(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var t=this.name;if(typeof t===\"undefined\"){t=\"Error\"}else if(typeof t!==\"string\"){t=o(t)}var r=this.message;if(typeof r===\"undefined\"){r=\"\"}else if(typeof r!==\"string\"){r=o(r)}if(!t){return r}if(!r){return t}return t+\": \"+r};Error.prototype.toString=kr}if(R){var Ar=function(t,r){if(tt(t,r)){var e=Object.getOwnPropertyDescriptor(t,r);if(e.configurable){e.enumerable=false;Object.defineProperty(t,r,e)}}};Ar(Error.prototype,\"message\");if(Error.prototype.message!==\"\"){Error.prototype.message=\"\"}Ar(Error.prototype,\"name\")}if(String(/a/gim)!==\"/a/gim\"){var Rr=function toString(){var t=\"/\"+this.source+\"/\";if(this.global){t+=\"g\"}if(this.ignoreCase){t+=\"i\"}if(this.multiline){t+=\"m\"}return t};RegExp.prototype.toString=Rr}});\n/*!\n * https://github.com/paulmillr/es6-shim\n * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)\n *   and contributors,  MIT License\n * es6-shim: v0.35.4\n * see https://github.com/paulmillr/es6-shim/blob/0.35.4/LICENSE\n * Details and documentation:\n * https://github.com/paulmillr/es6-shim/\n */\n(function(e,t){if(typeof define===\"function\"&&define.amd){define(t)}else if(typeof exports===\"object\"){module.exports=t()}else{e.returnExports=t()}})(this,function(){\"use strict\";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=Object.keys;var o=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var i=function(e){try{e();return false}catch(t){return true}};var a=function valueOrFalseIfThrows(e){try{return e()}catch(t){return false}};var u=o(i);var f=function(){return!i(function(){return Object.defineProperty({},\"x\",{get:function(){}})})};var s=!!Object.defineProperty&&f();var c=function foo(){}.name===\"foo\";var l=Function.call.bind(Array.prototype.forEach);var p=Function.call.bind(Array.prototype.reduce);var v=Function.call.bind(Array.prototype.filter);var y=Function.call.bind(Array.prototype.some);var h=function(e,t,r,n){if(!n&&t in e){return}if(s){Object.defineProperty(e,t,{configurable:true,enumerable:false,writable:true,value:r})}else{e[t]=r}};var b=function(e,t,r){l(n(t),function(n){var o=t[n];h(e,n,o,!!r)})};var g=Function.call.bind(Object.prototype.toString);var d=typeof/abc/===\"function\"?function IsCallableSlow(e){return typeof e===\"function\"&&g(e)===\"[object Function]\"}:function IsCallableFast(e){return typeof e===\"function\"};var m={getter:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}Object.defineProperty(e,t,{configurable:true,enumerable:false,get:r})},proxy:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,{configurable:n.configurable,enumerable:n.enumerable,get:function getKey(){return e[t]},set:function setKey(r){e[t]=r}})},redefine:function(e,t,r){if(s){var n=Object.getOwnPropertyDescriptor(e,t);n.value=r;Object.defineProperty(e,t,n)}else{e[t]=r}},defineByDescriptor:function(e,t,r){if(s){Object.defineProperty(e,t,r)}else if(\"value\"in r){e[t]=r.value}},preserveToString:function(e,t){if(t&&d(t.toString)){h(e,\"toString\",t.toString.bind(t),true)}}};var O=Object.create||function(e,t){var r=function Prototype(){};r.prototype=e;var o=new r;if(typeof t!==\"undefined\"){n(t).forEach(function(e){m.defineByDescriptor(o,e,t[e])})}return o};var w=function(e,t){if(!Object.setPrototypeOf){return false}return a(function(){var r=function Subclass(t){var r=new e(t);Object.setPrototypeOf(r,Subclass.prototype);return r};Object.setPrototypeOf(r,e);r.prototype=O(e.prototype,{constructor:{value:r}});return t(r)})};var j=function(){if(typeof self!==\"undefined\"){return self}if(typeof window!==\"undefined\"){return window}if(typeof global!==\"undefined\"){return global}throw new Error(\"unable to locate global object\")};var S=j();var T=S.isFinite;var I=Function.call.bind(String.prototype.indexOf);var E=Function.apply.bind(Array.prototype.indexOf);var P=Function.call.bind(Array.prototype.concat);var C=Function.call.bind(String.prototype.slice);var M=Function.call.bind(Array.prototype.push);var x=Function.apply.bind(Array.prototype.push);var N=Function.call.bind(Array.prototype.shift);var A=Math.max;var R=Math.min;var _=Math.floor;var k=Math.abs;var L=Math.exp;var F=Math.log;var D=Math.sqrt;var z=Function.call.bind(Object.prototype.hasOwnProperty);var q;var W=function(){};var G=S.Map;var H=G&&G.prototype[\"delete\"];var V=G&&G.prototype.get;var B=G&&G.prototype.has;var U=G&&G.prototype.set;var $=S.Symbol||{};var J=$.species||\"@@species\";var X=Number.isNaN||function isNaN(e){return e!==e};var K=Number.isFinite||function isFinite(e){return typeof e===\"number\"&&T(e)};var Z=d(Math.sign)?Math.sign:function sign(e){var t=Number(e);if(t===0){return t}if(X(t)){return t}return t<0?-1:1};var Y=function log1p(e){var t=Number(e);if(t<-1||X(t)){return NaN}if(t===0||t===Infinity){return t}if(t===-1){return-Infinity}return 1+t-1===0?t:t*(F(1+t)/(1+t-1))};var Q=function isArguments(e){return g(e)===\"[object Arguments]\"};var ee=function isArguments(e){return e!==null&&typeof e===\"object\"&&typeof e.length===\"number\"&&e.length>=0&&g(e)!==\"[object Array]\"&&g(e.callee)===\"[object Function]\"};var te=Q(arguments)?Q:ee;var re={primitive:function(e){return e===null||typeof e!==\"function\"&&typeof e!==\"object\"},string:function(e){return g(e)===\"[object String]\"},regex:function(e){return g(e)===\"[object RegExp]\"},symbol:function(e){return typeof S.Symbol===\"function\"&&typeof e===\"symbol\"}};var ne=function overrideNative(e,t,r){var n=e[t];h(e,t,r,true);m.preserveToString(e[t],n)};var oe=typeof $===\"function\"&&typeof $[\"for\"]===\"function\"&&re.symbol($());var ie=re.symbol($.iterator)?$.iterator:\"_es6-shim iterator_\";if(S.Set&&typeof(new S.Set)[\"@@iterator\"]===\"function\"){ie=\"@@iterator\"}if(!S.Reflect){h(S,\"Reflect\",{},true)}var ae=S.Reflect;var ue=String;var fe=typeof document===\"undefined\"||!document?null:document.all;var se=fe==null?function isNullOrUndefined(e){return e==null}:function isNullOrUndefinedAndNotDocumentAll(e){return e==null&&e!==fe};var ce={Call:function Call(t,r){var n=arguments.length>2?arguments[2]:[];if(!ce.IsCallable(t)){throw new TypeError(t+\" is not a function\")}return e(t,r,n)},RequireObjectCoercible:function(e,t){if(se(e)){throw new TypeError(t||\"Cannot call method on \"+e)}return e},TypeIsObject:function(e){if(e===void 0||e===null||e===true||e===false){return false}return typeof e===\"function\"||typeof e===\"object\"||e===fe},ToObject:function(e,t){return Object(ce.RequireObjectCoercible(e,t))},IsCallable:d,IsConstructor:function(e){return ce.IsCallable(e)},ToInt32:function(e){return ce.ToNumber(e)>>0},ToUint32:function(e){return ce.ToNumber(e)>>>0},ToNumber:function(e){if(g(e)===\"[object Symbol]\"){throw new TypeError(\"Cannot convert a Symbol value to a number\")}return+e},ToInteger:function(e){var t=ce.ToNumber(e);if(X(t)){return 0}if(t===0||!K(t)){return t}return(t>0?1:-1)*_(k(t))},ToLength:function(e){var t=ce.ToInteger(e);if(t<=0){return 0}if(t>Number.MAX_SAFE_INTEGER){return Number.MAX_SAFE_INTEGER}return t},SameValue:function(e,t){if(e===t){if(e===0){return 1/e===1/t}return true}return X(e)&&X(t)},SameValueZero:function(e,t){return e===t||X(e)&&X(t)},IsIterable:function(e){return ce.TypeIsObject(e)&&(typeof e[ie]!==\"undefined\"||te(e))},GetIterator:function(e){if(te(e)){return new q(e,\"value\")}var t=ce.GetMethod(e,ie);if(!ce.IsCallable(t)){throw new TypeError(\"value is not an iterable\")}var r=ce.Call(t,e);if(!ce.TypeIsObject(r)){throw new TypeError(\"bad iterator\")}return r},GetMethod:function(e,t){var r=ce.ToObject(e)[t];if(se(r)){return void 0}if(!ce.IsCallable(r)){throw new TypeError(\"Method not callable: \"+t)}return r},IteratorComplete:function(e){return!!e.done},IteratorClose:function(e,t){var r=ce.GetMethod(e,\"return\");if(r===void 0){return}var n,o;try{n=ce.Call(r,e)}catch(i){o=i}if(t){return}if(o){throw o}if(!ce.TypeIsObject(n)){throw new TypeError(\"Iterator's return method returned a non-object.\")}},IteratorNext:function(e){var t=arguments.length>1?e.next(arguments[1]):e.next();if(!ce.TypeIsObject(t)){throw new TypeError(\"bad iterator\")}return t},IteratorStep:function(e){var t=ce.IteratorNext(e);var r=ce.IteratorComplete(t);return r?false:t},Construct:function(e,t,r,n){var o=typeof r===\"undefined\"?e:r;if(!n&&ae.construct){return ae.construct(e,t,o)}var i=o.prototype;if(!ce.TypeIsObject(i)){i=Object.prototype}var a=O(i);var u=ce.Call(e,a,t);return ce.TypeIsObject(u)?u:a},SpeciesConstructor:function(e,t){var r=e.constructor;if(r===void 0){return t}if(!ce.TypeIsObject(r)){throw new TypeError(\"Bad constructor\")}var n=r[J];if(se(n)){return t}if(!ce.IsConstructor(n)){throw new TypeError(\"Bad @@species\")}return n},CreateHTML:function(e,t,r,n){var o=ce.ToString(e);var i=\"<\"+t;if(r!==\"\"){var a=ce.ToString(n);var u=a.replace(/\"/g,\"&quot;\");i+=\" \"+r+'=\"'+u+'\"'}var f=i+\">\";var s=f+o;return s+\"</\"+t+\">\"},IsRegExp:function IsRegExp(e){if(!ce.TypeIsObject(e)){return false}var t=e[$.match];if(typeof t!==\"undefined\"){return!!t}return re.regex(e)},ToString:function ToString(e){return ue(e)}};if(s&&oe){var le=function defineWellKnownSymbol(e){if(re.symbol($[e])){return $[e]}var t=$[\"for\"](\"Symbol.\"+e);Object.defineProperty($,e,{configurable:false,enumerable:false,writable:false,value:t});return t};if(!re.symbol($.search)){var pe=le(\"search\");var ve=String.prototype.search;h(RegExp.prototype,pe,function search(e){return ce.Call(ve,e,[this])});var ye=function search(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,pe);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(ve,t,[ce.ToString(e)])};ne(String.prototype,\"search\",ye)}if(!re.symbol($.replace)){var he=le(\"replace\");var be=String.prototype.replace;h(RegExp.prototype,he,function replace(e,t){return ce.Call(be,e,[this,t])});var ge=function replace(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,he);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(be,r,[ce.ToString(e),t])};ne(String.prototype,\"replace\",ge)}if(!re.symbol($.split)){var de=le(\"split\");var me=String.prototype.split;h(RegExp.prototype,de,function split(e,t){return ce.Call(me,e,[this,t])});var Oe=function split(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,de);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(me,r,[ce.ToString(e),t])};ne(String.prototype,\"split\",Oe)}var we=re.symbol($.match);var je=we&&function(){var e={};e[$.match]=function(){return 42};return\"a\".match(e)!==42}();if(!we||je){var Se=le(\"match\");var Te=String.prototype.match;h(RegExp.prototype,Se,function match(e){return ce.Call(Te,e,[this])});var Ie=function match(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,Se);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(Te,t,[ce.ToString(e)])};ne(String.prototype,\"match\",Ie)}}var Ee=function wrapConstructor(e,t,r){m.preserveToString(t,e);if(Object.setPrototypeOf){Object.setPrototypeOf(e,t)}if(s){l(Object.getOwnPropertyNames(e),function(n){if(n in W||r[n]){return}m.proxy(e,n,t)})}else{l(Object.keys(e),function(n){if(n in W||r[n]){return}t[n]=e[n]})}t.prototype=e.prototype;m.redefine(e.prototype,\"constructor\",t)};var Pe=function(){return this};var Ce=function(e){if(s&&!z(e,J)){m.getter(e,J,Pe)}};var Me=function(e,t){var r=t||function iterator(){return this};h(e,ie,r);if(!e[ie]&&re.symbol(ie)){e[ie]=r}};var xe=function createDataProperty(e,t,r){if(s){Object.defineProperty(e,t,{configurable:true,enumerable:true,writable:true,value:r})}else{e[t]=r}};var Ne=function createDataPropertyOrThrow(e,t,r){xe(e,t,r);if(!ce.SameValue(e[t],r)){throw new TypeError(\"property is nonconfigurable\")}};var Ae=function(e,t,r,n){if(!ce.TypeIsObject(e)){throw new TypeError(\"Constructor requires `new`: \"+t.name)}var o=t.prototype;if(!ce.TypeIsObject(o)){o=r}var i=O(o);for(var a in n){if(z(n,a)){var u=n[a];h(i,a,u,true)}}return i};if(String.fromCodePoint&&String.fromCodePoint.length!==1){var Re=String.fromCodePoint;ne(String,\"fromCodePoint\",function fromCodePoint(e){return ce.Call(Re,this,arguments)})}var _e={fromCodePoint:function fromCodePoint(e){var t=[];var r;for(var n=0,o=arguments.length;n<o;n++){r=Number(arguments[n]);if(!ce.SameValue(r,ce.ToInteger(r))||r<0||r>1114111){throw new RangeError(\"Invalid code point \"+r)}if(r<65536){M(t,String.fromCharCode(r))}else{r-=65536;M(t,String.fromCharCode((r>>10)+55296));M(t,String.fromCharCode(r%1024+56320))}}return t.join(\"\")},raw:function raw(e){var t=ce.ToObject(e,\"bad callSite\");var r=ce.ToObject(t.raw,\"bad raw value\");var n=r.length;var o=ce.ToLength(n);if(o<=0){return\"\"}var i=[];var a=0;var u,f,s,c;while(a<o){u=ce.ToString(a);s=ce.ToString(r[u]);M(i,s);if(a+1>=o){break}f=a+1<arguments.length?arguments[a+1]:\"\";c=ce.ToString(f);M(i,c);a+=1}return i.join(\"\")}};if(String.raw&&String.raw({raw:{0:\"x\",1:\"y\",length:2}})!==\"xy\"){ne(String,\"raw\",_e.raw)}b(String,_e);var ke=function repeat(e,t){if(t<1){return\"\"}if(t%2){return repeat(e,t-1)+e}var r=repeat(e,t/2);return r+r};var Le=Infinity;var Fe={repeat:function repeat(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);if(r<0||r>=Le){throw new RangeError(\"repeat count must be less than infinity and not overflow maximum string size\")}return ke(t,r)},startsWith:function startsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"startsWith\" with a regex')}var r=ce.ToString(e);var n;if(arguments.length>1){n=arguments[1]}var o=A(ce.ToInteger(n),0);return C(t,o,o+r.length)===r},endsWith:function endsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"endsWith\" with a regex')}var r=ce.ToString(e);var n=t.length;var o;if(arguments.length>1){o=arguments[1]}var i=typeof o===\"undefined\"?n:ce.ToInteger(o);var a=R(A(i,0),n);return C(t,a-r.length,a)===r},includes:function includes(e){if(ce.IsRegExp(e)){throw new TypeError('\"includes\" does not accept a RegExp')}var t=ce.ToString(e);var r;if(arguments.length>1){r=arguments[1]}return I(this,t,r)!==-1},codePointAt:function codePointAt(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);var n=t.length;if(r>=0&&r<n){var o=t.charCodeAt(r);var i=r+1===n;if(o<55296||o>56319||i){return o}var a=t.charCodeAt(r+1);if(a<56320||a>57343){return o}return(o-55296)*1024+(a-56320)+65536}}};if(String.prototype.includes&&\"a\".includes(\"a\",Infinity)!==false){ne(String.prototype,\"includes\",Fe.includes)}if(String.prototype.startsWith&&String.prototype.endsWith){var De=i(function(){return\"/a/\".startsWith(/a/)});var ze=a(function(){return\"abc\".startsWith(\"a\",Infinity)===false});if(!De||!ze){ne(String.prototype,\"startsWith\",Fe.startsWith);ne(String.prototype,\"endsWith\",Fe.endsWith)}}if(oe){var qe=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".startsWith(e)});if(!qe){ne(String.prototype,\"startsWith\",Fe.startsWith)}var We=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".endsWith(e)});if(!We){ne(String.prototype,\"endsWith\",Fe.endsWith)}var Ge=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".includes(e)});if(!Ge){ne(String.prototype,\"includes\",Fe.includes)}}b(String.prototype,Fe);var He=[\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\",\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\",\"\\u2029\\ufeff\"].join(\"\");var Ve=new RegExp(\"(^[\"+He+\"]+)|([\"+He+\"]+$)\",\"g\");var Be=function trim(){return ce.ToString(ce.RequireObjectCoercible(this)).replace(Ve,\"\")};var Ue=[\"\\x85\",\"\\u200b\",\"\\ufffe\"].join(\"\");var $e=new RegExp(\"[\"+Ue+\"]\",\"g\");var Je=/^[-+]0x[0-9a-f]+$/i;var Xe=Ue.trim().length!==Ue.length;h(String.prototype,\"trim\",Be,Xe);var Ke=function(e){return{value:e,done:arguments.length===0}};var Ze=function(e){ce.RequireObjectCoercible(e);this._s=ce.ToString(e);this._i=0};Ze.prototype.next=function(){var e=this._s;var t=this._i;if(typeof e===\"undefined\"||t>=e.length){this._s=void 0;return Ke()}var r=e.charCodeAt(t);var n,o;if(r<55296||r>56319||t+1===e.length){o=1}else{n=e.charCodeAt(t+1);o=n<56320||n>57343?1:2}this._i=t+o;return Ke(e.substr(t,o))};Me(Ze.prototype);Me(String.prototype,function(){return new Ze(this)});var Ye={from:function from(e){var r=this;var n;if(arguments.length>1){n=arguments[1]}var o,i;if(typeof n===\"undefined\"){o=false}else{if(!ce.IsCallable(n)){throw new TypeError(\"Array.from: when provided, the second argument must be a function\")}if(arguments.length>2){i=arguments[2]}o=true}var a=typeof(te(e)||ce.GetMethod(e,ie))!==\"undefined\";var u,f,s;if(a){f=ce.IsConstructor(r)?Object(new r):[];var c=ce.GetIterator(e);var l,p;s=0;while(true){l=ce.IteratorStep(c);if(l===false){break}p=l.value;try{if(o){p=typeof i===\"undefined\"?n(p,s):t(n,i,p,s)}f[s]=p}catch(v){ce.IteratorClose(c,true);throw v}s+=1}u=s}else{var y=ce.ToObject(e);u=ce.ToLength(y.length);f=ce.IsConstructor(r)?Object(new r(u)):new Array(u);var h;for(s=0;s<u;++s){h=y[s];if(o){h=typeof i===\"undefined\"?n(h,s):t(n,i,h,s)}Ne(f,s,h)}}f.length=u;return f},of:function of(){var e=arguments.length;var t=this;var n=r(t)||!ce.IsCallable(t)?new Array(e):ce.Construct(t,[e]);for(var o=0;o<e;++o){Ne(n,o,arguments[o])}n.length=e;return n}};b(Array,Ye);Ce(Array);q=function(e,t){this.i=0;this.array=e;this.kind=t};b(q.prototype,{next:function(){var e=this.i;var t=this.array;if(!(this instanceof q)){throw new TypeError(\"Not an ArrayIterator\")}if(typeof t!==\"undefined\"){var r=ce.ToLength(t.length);for(;e<r;e++){var n=this.kind;var o;if(n===\"key\"){o=e}else if(n===\"value\"){o=t[e]}else if(n===\"entry\"){o=[e,t[e]]}this.i=e+1;return Ke(o)}}this.array=void 0;return Ke()}});Me(q.prototype);var Qe=Array.of===Ye.of||function(){var e=function Foo(e){this.length=e};e.prototype=[];var t=Array.of.apply(e,[1,2]);return t instanceof e&&t.length===2}();if(!Qe){ne(Array,\"of\",Ye.of)}var et={copyWithin:function copyWithin(e,t){var r=ce.ToObject(this);var n=ce.ToLength(r.length);var o=ce.ToInteger(e);var i=ce.ToInteger(t);var a=o<0?A(n+o,0):R(o,n);var u=i<0?A(n+i,0):R(i,n);var f;if(arguments.length>2){f=arguments[2]}var s=typeof f===\"undefined\"?n:ce.ToInteger(f);var c=s<0?A(n+s,0):R(s,n);var l=R(c-u,n-a);var p=1;if(u<a&&a<u+l){p=-1;u+=l-1;a+=l-1}while(l>0){if(u in r){r[a]=r[u]}else{delete r[a]}u+=p;a+=p;l-=1}return r},fill:function fill(e){var t;if(arguments.length>1){t=arguments[1]}var r;if(arguments.length>2){r=arguments[2]}var n=ce.ToObject(this);var o=ce.ToLength(n.length);t=ce.ToInteger(typeof t===\"undefined\"?0:t);r=ce.ToInteger(typeof r===\"undefined\"?o:r);var i=t<0?A(o+t,0):R(t,o);var a=r<0?o+r:r;for(var u=i;u<o&&u<a;++u){n[u]=e}return n},find:function find(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#find: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0,a;i<n;i++){a=r[i];if(o){if(t(e,o,a,i,r)){return a}}else if(e(a,i,r)){return a}}},findIndex:function findIndex(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#findIndex: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0;i<n;i++){if(o){if(t(e,o,r[i],i,r)){return i}}else if(e(r[i],i,r)){return i}}return-1},keys:function keys(){return new q(this,\"key\")},values:function values(){return new q(this,\"value\")},entries:function entries(){return new q(this,\"entry\")}};if(Array.prototype.keys&&!ce.IsCallable([1].keys().next)){delete Array.prototype.keys}if(Array.prototype.entries&&!ce.IsCallable([1].entries().next)){delete Array.prototype.entries}if(Array.prototype.keys&&Array.prototype.entries&&!Array.prototype.values&&Array.prototype[ie]){b(Array.prototype,{values:Array.prototype[ie]});if(re.symbol($.unscopables)){Array.prototype[$.unscopables].values=true}}if(c&&Array.prototype.values&&Array.prototype.values.name!==\"values\"){var tt=Array.prototype.values;ne(Array.prototype,\"values\",function values(){return ce.Call(tt,this,arguments)});h(Array.prototype,ie,Array.prototype.values,true)}b(Array.prototype,et);if(1/[true].indexOf(true,-0)<0){h(Array.prototype,\"indexOf\",function indexOf(e){var t=E(this,arguments);if(t===0&&1/t<0){return 0}return t},true)}Me(Array.prototype,function(){return this.values()});if(Object.getPrototypeOf){Me(Object.getPrototypeOf([].values()))}var rt=function(){return a(function(){return Array.from({length:-1}).length===0})}();var nt=function(){var e=Array.from([0].entries());return e.length===1&&r(e[0])&&e[0][0]===0&&e[0][1]===0}();if(!rt||!nt){ne(Array,\"from\",Ye.from)}var ot=function(){return a(function(){return Array.from([0],void 0)})}();if(!ot){var it=Array.from;ne(Array,\"from\",function from(e){if(arguments.length>1&&typeof arguments[1]!==\"undefined\"){return ce.Call(it,this,arguments)}else{return t(it,this,e)}})}var at=-(Math.pow(2,32)-1);var ut=function(e,r){var n={length:at};n[r?(n.length>>>0)-1:0]=true;return a(function(){t(e,n,function(){throw new RangeError(\"should not reach here\")},[]);return true})};if(!ut(Array.prototype.forEach)){var ft=Array.prototype.forEach;ne(Array.prototype,\"forEach\",function forEach(e){return ce.Call(ft,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.map)){var st=Array.prototype.map;ne(Array.prototype,\"map\",function map(e){return ce.Call(st,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.filter)){var ct=Array.prototype.filter;ne(Array.prototype,\"filter\",function filter(e){return ce.Call(ct,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.some)){var lt=Array.prototype.some;ne(Array.prototype,\"some\",function some(e){return ce.Call(lt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.every)){var pt=Array.prototype.every;ne(Array.prototype,\"every\",function every(e){return ce.Call(pt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduce)){var vt=Array.prototype.reduce;ne(Array.prototype,\"reduce\",function reduce(e){return ce.Call(vt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduceRight,true)){var yt=Array.prototype.reduceRight;ne(Array.prototype,\"reduceRight\",function reduceRight(e){return ce.Call(yt,this.length>=0?this:[],arguments)},true)}var ht=Number(\"0o10\")!==8;var bt=Number(\"0b10\")!==2;var gt=y(Ue,function(e){return Number(e+0+e)===0});if(ht||bt||gt){var dt=Number;var mt=/^0b[01]+$/i;var Ot=/^0o[0-7]+$/i;var wt=mt.test.bind(mt);var jt=Ot.test.bind(Ot);var St=function(e){var t;if(typeof e.valueOf===\"function\"){t=e.valueOf();if(re.primitive(t)){return t}}if(typeof e.toString===\"function\"){t=e.toString();if(re.primitive(t)){return t}}throw new TypeError(\"No default value\")};var Tt=$e.test.bind($e);var It=Je.test.bind(Je);var Et=function(){var e=function Number(t){var r;if(arguments.length>0){r=re.primitive(t)?t:St(t,\"number\")}else{r=0}if(typeof r===\"string\"){r=ce.Call(Be,r);if(wt(r)){r=parseInt(C(r,2),2)}else if(jt(r)){r=parseInt(C(r,2),8)}else if(Tt(r)||It(r)){r=NaN}}var n=this;var o=a(function(){dt.prototype.valueOf.call(n);return true});if(n instanceof e&&!o){return new dt(r)}return dt(r)};return e}();Ee(dt,Et,{});b(Et,{NaN:dt.NaN,MAX_VALUE:dt.MAX_VALUE,MIN_VALUE:dt.MIN_VALUE,NEGATIVE_INFINITY:dt.NEGATIVE_INFINITY,POSITIVE_INFINITY:dt.POSITIVE_INFINITY});Number=Et;m.redefine(S,\"Number\",Et)}var Pt=Math.pow(2,53)-1;b(Number,{MAX_SAFE_INTEGER:Pt,MIN_SAFE_INTEGER:-Pt,EPSILON:2.220446049250313e-16,parseInt:S.parseInt,parseFloat:S.parseFloat,isFinite:K,isInteger:function isInteger(e){return K(e)&&ce.ToInteger(e)===e},isSafeInteger:function isSafeInteger(e){return Number.isInteger(e)&&k(e)<=Number.MAX_SAFE_INTEGER},isNaN:X});h(Number,\"parseInt\",S.parseInt,Number.parseInt!==S.parseInt);if([,1].find(function(){return true})===1){ne(Array.prototype,\"find\",et.find)}if([,1].findIndex(function(){return true})!==0){ne(Array.prototype,\"findIndex\",et.findIndex)}var Ct=Function.bind.call(Function.bind,Object.prototype.propertyIsEnumerable);var Mt=function ensureEnumerable(e,t){if(s&&Ct(e,t)){Object.defineProperty(e,t,{enumerable:false})}};var xt=function sliceArgs(){var e=Number(this);var t=arguments.length;var r=t-e;var n=new Array(r<0?0:r);for(var o=e;o<t;++o){n[o-e]=arguments[o]}return n};var Nt=function assignTo(e){return function assignToSource(t,r){t[r]=e[r];return t}};var At=function(e,t){var r=n(Object(t));var o;if(ce.IsCallable(Object.getOwnPropertySymbols)){o=v(Object.getOwnPropertySymbols(Object(t)),Ct(t))}return p(P(r,o||[]),Nt(t),e)};var Rt={assign:function(e,t){var r=ce.ToObject(e,\"Cannot convert undefined or null to object\");return p(ce.Call(xt,1,arguments),At,r)},is:function is(e,t){return ce.SameValue(e,t)}};var _t=Object.assign&&Object.preventExtensions&&function(){var e=Object.preventExtensions({1:2});try{Object.assign(e,\"xy\")}catch(t){return e[1]===\"y\"}}();if(_t){ne(Object,\"assign\",Rt.assign)}b(Object,Rt);if(s){var kt={setPrototypeOf:function(e,r){var n;var o=function(e,t){if(!ce.TypeIsObject(e)){throw new TypeError(\"cannot set prototype on a non-object\")}if(!(t===null||ce.TypeIsObject(t))){throw new TypeError(\"can only set prototype to an object or null\"+t)}};var i=function(e,r){o(e,r);t(n,e,r);return e};try{n=e.getOwnPropertyDescriptor(e.prototype,r).set;t(n,{},null)}catch(a){if(e.prototype!=={}[r]){return}n=function(e){this[r]=e};i.polyfill=i(i({},null),e.prototype)instanceof e}return i}(Object,\"__proto__\")};b(Object,kt)}if(Object.setPrototypeOf&&Object.getPrototypeOf&&Object.getPrototypeOf(Object.setPrototypeOf({},null))!==null&&Object.getPrototypeOf(Object.create(null))===null){(function(){var e=Object.create(null);var t=Object.getPrototypeOf;var r=Object.setPrototypeOf;Object.getPrototypeOf=function(r){var n=t(r);return n===e?null:n};Object.setPrototypeOf=function(t,n){var o=n===null?e:n;return r(t,o)};Object.setPrototypeOf.polyfill=false})()}var Lt=!i(function(){return Object.keys(\"foo\")});if(!Lt){var Ft=Object.keys;ne(Object,\"keys\",function keys(e){return Ft(ce.ToObject(e))});n=Object.keys}var Dt=i(function(){return Object.keys(/a/g)});if(Dt){var zt=Object.keys;ne(Object,\"keys\",function keys(e){if(re.regex(e)){var t=[];for(var r in e){if(z(e,r)){M(t,r)}}return t}return zt(e)});n=Object.keys}if(Object.getOwnPropertyNames){var qt=!i(function(){return Object.getOwnPropertyNames(\"foo\")});if(!qt){var Wt=typeof window===\"object\"?Object.getOwnPropertyNames(window):[];var Gt=Object.getOwnPropertyNames;ne(Object,\"getOwnPropertyNames\",function getOwnPropertyNames(e){var t=ce.ToObject(e);if(g(t)===\"[object Window]\"){try{return Gt(t)}catch(r){return P([],Wt)}}return Gt(t)})}}if(Object.getOwnPropertyDescriptor){var Ht=!i(function(){return Object.getOwnPropertyDescriptor(\"foo\",\"bar\")});if(!Ht){var Vt=Object.getOwnPropertyDescriptor;ne(Object,\"getOwnPropertyDescriptor\",function getOwnPropertyDescriptor(e,t){return Vt(ce.ToObject(e),t)})}}if(Object.seal){var Bt=!i(function(){return Object.seal(\"foo\")});if(!Bt){var Ut=Object.seal;ne(Object,\"seal\",function seal(e){if(!ce.TypeIsObject(e)){return e}return Ut(e)})}}if(Object.isSealed){var $t=!i(function(){return Object.isSealed(\"foo\")});if(!$t){var Jt=Object.isSealed;ne(Object,\"isSealed\",function isSealed(e){if(!ce.TypeIsObject(e)){return true}return Jt(e)})}}if(Object.freeze){var Xt=!i(function(){return Object.freeze(\"foo\")});if(!Xt){var Kt=Object.freeze;ne(Object,\"freeze\",function freeze(e){if(!ce.TypeIsObject(e)){return e}return Kt(e)})}}if(Object.isFrozen){var Zt=!i(function(){return Object.isFrozen(\"foo\")});if(!Zt){var Yt=Object.isFrozen;ne(Object,\"isFrozen\",function isFrozen(e){if(!ce.TypeIsObject(e)){return true}return Yt(e)})}}if(Object.preventExtensions){var Qt=!i(function(){return Object.preventExtensions(\"foo\")});if(!Qt){var er=Object.preventExtensions;ne(Object,\"preventExtensions\",function preventExtensions(e){if(!ce.TypeIsObject(e)){return e}return er(e)})}}if(Object.isExtensible){var tr=!i(function(){return Object.isExtensible(\"foo\")});if(!tr){var rr=Object.isExtensible;ne(Object,\"isExtensible\",function isExtensible(e){if(!ce.TypeIsObject(e)){return false}return rr(e)})}}if(Object.getPrototypeOf){var nr=!i(function(){return Object.getPrototypeOf(\"foo\")});if(!nr){var or=Object.getPrototypeOf;ne(Object,\"getPrototypeOf\",function getPrototypeOf(e){return or(ce.ToObject(e))})}}var ir=s&&function(){var e=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\");return e&&ce.IsCallable(e.get)}();if(s&&!ir){var ar=function flags(){if(!ce.TypeIsObject(this)){throw new TypeError(\"Method called on incompatible type: must be an object.\")}var e=\"\";if(this.global){e+=\"g\"}if(this.ignoreCase){e+=\"i\"}if(this.multiline){e+=\"m\"}if(this.unicode){e+=\"u\"}if(this.sticky){e+=\"y\"}return e};m.getter(RegExp.prototype,\"flags\",ar)}var ur=s&&a(function(){return String(new RegExp(/a/g,\"i\"))===\"/a/i\"});var fr=oe&&s&&function(){var e=/./;e[$.match]=false;return RegExp(e)===e}();var sr=a(function(){return RegExp.prototype.toString.call({source:\"abc\"})===\"/abc/\"});var cr=sr&&a(function(){return RegExp.prototype.toString.call({source:\"a\",flags:\"b\"})===\"/a/b\"});if(!sr||!cr){var lr=RegExp.prototype.toString;h(RegExp.prototype,\"toString\",function toString(){var e=ce.RequireObjectCoercible(this);if(re.regex(e)){return t(lr,e)}var r=ue(e.source);var n=ue(e.flags);return\"/\"+r+\"/\"+n},true);m.preserveToString(RegExp.prototype.toString,lr)}if(s&&(!ur||fr)){var pr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\").get;var vr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"source\")||{};var yr=function(){return this.source};var hr=ce.IsCallable(vr.get)?vr.get:yr;var br=RegExp;var gr=function(){return function RegExp(e,t){var r=ce.IsRegExp(e);var n=this instanceof RegExp;if(!n&&r&&typeof t===\"undefined\"&&e.constructor===RegExp){return e}var o=e;var i=t;if(re.regex(e)){o=ce.Call(hr,e);i=typeof t===\"undefined\"?ce.Call(pr,e):t;return new RegExp(o,i)}else if(r){o=e.source;i=typeof t===\"undefined\"?e.flags:t}return new br(e,t)}}();Ee(br,gr,{$input:true});RegExp=gr;m.redefine(S,\"RegExp\",gr)}if(s){var dr={input:\"$_\",lastMatch:\"$&\",lastParen:\"$+\",leftContext:\"$`\",rightContext:\"$'\"};l(n(dr),function(e){if(e in RegExp&&!(dr[e]in RegExp)){m.getter(RegExp,dr[e],function get(){return RegExp[e]})}})}Ce(RegExp);var mr=1/Number.EPSILON;var Or=function roundTiesToEven(e){return e+mr-mr};var wr=Math.pow(2,-23);var jr=Math.pow(2,127)*(2-wr);var Sr=Math.pow(2,-126);var Tr=Math.E;var Ir=Math.LOG2E;var Er=Math.LOG10E;var Pr=Number.prototype.clz;delete Number.prototype.clz;var Cr={acosh:function acosh(e){var t=Number(e);if(X(t)||e<1){return NaN}if(t===1){return 0}if(t===Infinity){return t}var r=1/(t*t);if(t<2){return Y(t-1+D(1-r)*t)}var n=t/2;return Y(n+D(1-r)*n-1)+1/Ir},asinh:function asinh(e){var t=Number(e);if(t===0||!T(t)){return t}var r=k(t);var n=r*r;var o=Z(t);if(r<1){return o*Y(r+n/(D(n+1)+1))}return o*(Y(r/2+D(1+1/n)*r/2-1)+1/Ir)},atanh:function atanh(e){var t=Number(e);if(t===0){return t}if(t===-1){return-Infinity}if(t===1){return Infinity}if(X(t)||t<-1||t>1){return NaN}var r=k(t);return Z(t)*Y(2*r/(1-r))/2},cbrt:function cbrt(e){var t=Number(e);if(t===0){return t}var r=t<0;var n;if(r){t=-t}if(t===Infinity){n=Infinity}else{n=L(F(t)/3);n=(t/(n*n)+2*n)/3}return r?-n:n},clz32:function clz32(e){var t=Number(e);var r=ce.ToUint32(t);if(r===0){return 32}return Pr?ce.Call(Pr,r):31-_(F(r+.5)*Ir)},cosh:function cosh(e){var t=Number(e);if(t===0){return 1}if(X(t)){return NaN}if(!T(t)){return Infinity}var r=L(k(t)-1);return(r+1/(r*Tr*Tr))*(Tr/2)},expm1:function expm1(e){var t=Number(e);if(t===-Infinity){return-1}if(!T(t)||t===0){return t}if(k(t)>.5){return L(t)-1}var r=t;var n=0;var o=1;while(n+r!==n){n+=r;o+=1;r*=t/o}return n},hypot:function hypot(e,t){var r=0;var n=0;for(var o=0;o<arguments.length;++o){var i=k(Number(arguments[o]));if(n<i){r*=n/i*(n/i);r+=1;n=i}else{r+=i>0?i/n*(i/n):i}}return n===Infinity?Infinity:n*D(r)},log2:function log2(e){return F(e)*Ir},log10:function log10(e){return F(e)*Er},log1p:Y,sign:Z,sinh:function sinh(e){var t=Number(e);if(!T(t)||t===0){return t}var r=k(t);if(r<1){var n=Math.expm1(r);return Z(t)*n*(1+1/(n+1))/2}var o=L(r-1);return Z(t)*(o-1/(o*Tr*Tr))*(Tr/2)},tanh:function tanh(e){var t=Number(e);if(X(t)||t===0){return t}if(t>=20){return 1}if(t<=-20){return-1}return(Math.expm1(t)-Math.expm1(-t))/(L(t)+L(-t))},trunc:function trunc(e){var t=Number(e);return t<0?-_(-t):_(t)},imul:function imul(e,t){var r=ce.ToUint32(e);var n=ce.ToUint32(t);var o=r>>>16&65535;var i=r&65535;var a=n>>>16&65535;var u=n&65535;return i*u+(o*u+i*a<<16>>>0)|0},fround:function fround(e){var t=Number(e);if(t===0||t===Infinity||t===-Infinity||X(t)){return t}var r=Z(t);var n=k(t);if(n<Sr){return r*Or(n/Sr/wr)*Sr*wr}var o=(1+wr/Number.EPSILON)*n;var i=o-(o-n);if(i>jr||X(i)){return r*Infinity}return r*i}};var Mr=function withinULPDistance(e,t,r){return k(1-e/t)/Number.EPSILON<(r||8)};b(Math,Cr);h(Math,\"sinh\",Cr.sinh,Math.sinh(710)===Infinity);h(Math,\"cosh\",Cr.cosh,Math.cosh(710)===Infinity);h(Math,\"log1p\",Cr.log1p,Math.log1p(-1e-17)!==-1e-17);h(Math,\"asinh\",Cr.asinh,Math.asinh(-1e7)!==-Math.asinh(1e7));h(Math,\"asinh\",Cr.asinh,Math.asinh(1e300)===Infinity);h(Math,\"atanh\",Cr.atanh,Math.atanh(1e-300)===0);h(Math,\"tanh\",Cr.tanh,Math.tanh(-2e-17)!==-2e-17);\nh(Math,\"acosh\",Cr.acosh,Math.acosh(Number.MAX_VALUE)===Infinity);h(Math,\"acosh\",Cr.acosh,!Mr(Math.acosh(1+Number.EPSILON),Math.sqrt(2*Number.EPSILON)));h(Math,\"cbrt\",Cr.cbrt,!Mr(Math.cbrt(1e-300),1e-100));h(Math,\"sinh\",Cr.sinh,Math.sinh(-2e-17)!==-2e-17);var xr=Math.expm1(10);h(Math,\"expm1\",Cr.expm1,xr>22025.465794806718||xr<22025.465794806718);var Nr=Math.round;var Ar=Math.round(.5-Number.EPSILON/4)===0&&Math.round(-.5+Number.EPSILON/3.99)===1;var Rr=mr+1;var _r=2*mr-1;var kr=[Rr,_r].every(function(e){return Math.round(e)===e});h(Math,\"round\",function round(e){var t=_(e);var r=t===-1?-0:t+1;return e-t<.5?t:r},!Ar||!kr);m.preserveToString(Math.round,Nr);var Lr=Math.imul;if(Math.imul(4294967295,5)!==-5){Math.imul=Cr.imul;m.preserveToString(Math.imul,Lr)}if(Math.imul.length!==2){ne(Math,\"imul\",function imul(e,t){return ce.Call(Lr,Math,arguments)})}var Fr=function(){var e=S.setTimeout;if(typeof e!==\"function\"&&typeof e!==\"object\"){return}ce.IsPromise=function(e){if(!ce.TypeIsObject(e)){return false}if(typeof e._promise===\"undefined\"){return false}return true};var r=function(e){if(!ce.IsConstructor(e)){throw new TypeError(\"Bad promise constructor\")}var t=this;var r=function(e,r){if(t.resolve!==void 0||t.reject!==void 0){throw new TypeError(\"Bad Promise implementation!\")}t.resolve=e;t.reject=r};t.resolve=void 0;t.reject=void 0;t.promise=new e(r);if(!(ce.IsCallable(t.resolve)&&ce.IsCallable(t.reject))){throw new TypeError(\"Bad promise constructor\")}};var n;if(typeof window!==\"undefined\"&&ce.IsCallable(window.postMessage)){n=function(){var e=[];var t=\"zero-timeout-message\";var r=function(r){M(e,r);window.postMessage(t,\"*\")};var n=function(r){if(r.source===window&&r.data===t){r.stopPropagation();if(e.length===0){return}var n=N(e);n()}};window.addEventListener(\"message\",n,true);return r}}var o=function(){var e=S.Promise;var t=e&&e.resolve&&e.resolve();return t&&function(e){return t.then(e)}};var i=ce.IsCallable(S.setImmediate)?S.setImmediate:typeof process===\"object\"&&process.nextTick?process.nextTick:o()||(ce.IsCallable(n)?n():function(t){e(t,0)});var a=function(e){return e};var u=function(e){throw e};var f=0;var s=1;var c=2;var l=0;var p=1;var v=2;var y={};var h=function(e,t,r){i(function(){g(e,t,r)})};var g=function(e,t,r){var n,o;if(t===y){return e(r)}try{n=e(r);o=t.resolve}catch(i){n=i;o=t.reject}o(n)};var d=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.fulfillReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+l],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=s;r.reactionLength=0};var m=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.rejectReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+p],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=c;r.reactionLength=0};var O=function(e){var t=false;var r=function(r){var n;if(t){return}t=true;if(r===e){return m(e,new TypeError(\"Self resolution\"))}if(!ce.TypeIsObject(r)){return d(e,r)}try{n=r.then}catch(o){return m(e,o)}if(!ce.IsCallable(n)){return d(e,r)}i(function(){j(e,r,n)})};var n=function(r){if(t){return}t=true;return m(e,r)};return{resolve:r,reject:n}};var w=function(e,r,n,o){if(e===I){t(e,r,n,o,y)}else{t(e,r,n,o)}};var j=function(e,t,r){var n=O(e);var o=n.resolve;var i=n.reject;try{w(r,t,o,i)}catch(a){i(a)}};var T,I;var E=function(){var e=function Promise(t){if(!(this instanceof e)){throw new TypeError('Constructor Promise requires \"new\"')}if(this&&this._promise){throw new TypeError(\"Bad construction\")}if(!ce.IsCallable(t)){throw new TypeError(\"not a valid resolver\")}var r=Ae(this,e,T,{_promise:{result:void 0,state:f,reactionLength:0,fulfillReactionHandler0:void 0,rejectReactionHandler0:void 0,reactionCapability0:void 0}});var n=O(r);var o=n.reject;try{t(n.resolve,o)}catch(i){o(i)}return r};return e}();T=E.prototype;var P=function(e,t,r,n){var o=false;return function(i){if(o){return}o=true;t[e]=i;if(--n.count===0){var a=r.resolve;a(t)}}};var C=function(e,t,r){var n=e.iterator;var o=[];var i={count:1};var a,u;var f=0;while(true){try{a=ce.IteratorStep(n);if(a===false){e.done=true;break}u=a.value}catch(s){e.done=true;throw s}o[f]=void 0;var c=t.resolve(u);var l=P(f,o,r,i);i.count+=1;w(c.then,c,l,r.reject);f+=1}if(--i.count===0){var p=r.resolve;p(o)}return r.promise};var x=function(e,t,r){var n=e.iterator;var o,i,a;while(true){try{o=ce.IteratorStep(n);if(o===false){e.done=true;break}i=o.value}catch(u){e.done=true;throw u}a=t.resolve(i);w(a.then,a,r.resolve,r.reject)}return r.promise};b(E,{all:function all(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return C(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},race:function race(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return x(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},reject:function reject(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}var n=new r(t);var o=n.reject;o(e);return n.promise},resolve:function resolve(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}if(ce.IsPromise(e)){var n=e.constructor;if(n===t){return e}}var o=new r(t);var i=o.resolve;i(e);return o.promise}});b(T,{\"catch\":function(e){return this.then(null,e)},then:function then(e,t){var n=this;if(!ce.IsPromise(n)){throw new TypeError(\"not a promise\")}var o=ce.SpeciesConstructor(n,E);var i;var b=arguments.length>2&&arguments[2]===y;if(b&&o===E){i=y}else{i=new r(o)}var g=ce.IsCallable(e)?e:a;var d=ce.IsCallable(t)?t:u;var m=n._promise;var O;if(m.state===f){if(m.reactionLength===0){m.fulfillReactionHandler0=g;m.rejectReactionHandler0=d;m.reactionCapability0=i}else{var w=3*(m.reactionLength-1);m[w+l]=g;m[w+p]=d;m[w+v]=i}m.reactionLength+=1}else if(m.state===s){O=m.result;h(g,i,O)}else if(m.state===c){O=m.result;h(d,i,O)}else{throw new TypeError(\"unexpected Promise state\")}return i.promise}});y=new r(E);I=T.then;return E}();if(S.Promise){delete S.Promise.accept;delete S.Promise.defer;delete S.Promise.prototype.chain}if(typeof Fr===\"function\"){b(S,{Promise:Fr});var Dr=w(S.Promise,function(e){return e.resolve(42).then(function(){})instanceof e});var zr=!i(function(){return S.Promise.reject(42).then(null,5).then(null,W)});var qr=i(function(){return S.Promise.call(3,W)});var Wr=function(e){var t=e.resolve(5);t.constructor={};var r=e.resolve(t);try{r.then(null,W).then(null,W)}catch(n){return true}return t===r}(S.Promise);var Gr=s&&function(){var e=0;var t=Object.defineProperty({},\"then\",{get:function(){e+=1}});Promise.resolve(t);return e===1}();var Hr=function BadResolverPromise(e){var t=new Promise(e);e(3,function(){});this.then=t.then;this.constructor=BadResolverPromise};Hr.prototype=Promise.prototype;Hr.all=Promise.all;var Vr=a(function(){return!!Hr.all([1,2])});if(!Dr||!zr||!qr||Wr||!Gr||Vr){Promise=Fr;ne(S,\"Promise\",Fr)}if(Promise.all.length!==1){var Br=Promise.all;ne(Promise,\"all\",function all(e){return ce.Call(Br,this,arguments)})}if(Promise.race.length!==1){var Ur=Promise.race;ne(Promise,\"race\",function race(e){return ce.Call(Ur,this,arguments)})}if(Promise.resolve.length!==1){var $r=Promise.resolve;ne(Promise,\"resolve\",function resolve(e){return ce.Call($r,this,arguments)})}if(Promise.reject.length!==1){var Jr=Promise.reject;ne(Promise,\"reject\",function reject(e){return ce.Call(Jr,this,arguments)})}Mt(Promise,\"all\");Mt(Promise,\"race\");Mt(Promise,\"resolve\");Mt(Promise,\"reject\");Ce(Promise)}var Xr=function(e){var t=n(p(e,function(e,t){e[t]=true;return e},{}));return e.join(\":\")===t.join(\":\")};var Kr=Xr([\"z\",\"a\",\"bb\"]);var Zr=Xr([\"z\",1,\"a\",\"3\",2]);if(s){var Yr=function fastkey(e,t){if(!t&&!Kr){return null}if(se(e)){return\"^\"+ce.ToString(e)}else if(typeof e===\"string\"){return\"$\"+e}else if(typeof e===\"number\"){if(!Zr){return\"n\"+e}return e}else if(typeof e===\"boolean\"){return\"b\"+e}return null};var Qr=function emptyObject(){return Object.create?Object.create(null):{}};var en=function addIterableToMap(e,n,o){if(r(o)||re.string(o)){l(o,function(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"Iterator value \"+e+\" is not an entry object\")}n.set(e[0],e[1])})}else if(o instanceof e){t(e.prototype.forEach,o,function(e,t){n.set(t,e)})}else{var i,a;if(!se(o)){a=n.set;if(!ce.IsCallable(a)){throw new TypeError(\"bad map\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{if(!ce.TypeIsObject(f)){throw new TypeError(\"Iterator value \"+f+\" is not an entry object\")}t(a,n,f[0],f[1])}catch(s){ce.IteratorClose(i,true);throw s}}}}};var tn=function addIterableToSet(e,n,o){if(r(o)||re.string(o)){l(o,function(e){n.add(e)})}else if(o instanceof e){t(e.prototype.forEach,o,function(e){n.add(e)})}else{var i,a;if(!se(o)){a=n.add;if(!ce.IsCallable(a)){throw new TypeError(\"bad set\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{t(a,n,f)}catch(s){ce.IteratorClose(i,true);throw s}}}}};var rn={Map:function(){var e={};var r=function MapEntry(e,t){this.key=e;this.value=t;this.next=null;this.prev=null};r.prototype.isRemoved=function isRemoved(){return this.key===e};var n=function isMap(e){return!!e._es6map};var o=function requireMapSlot(e,t){if(!ce.TypeIsObject(e)||!n(e)){throw new TypeError(\"Method Map.prototype.\"+t+\" called on incompatible receiver \"+ce.ToString(e))}};var i=function MapIterator(e,t){o(e,\"[[MapIterator]]\");this.head=e._head;this.i=this.head;this.kind=t};i.prototype={isMapIterator:true,next:function next(){if(!this.isMapIterator){throw new TypeError(\"Not a MapIterator\")}var e=this.i;var t=this.kind;var r=this.head;if(typeof this.i===\"undefined\"){return Ke()}while(e.isRemoved()&&e!==r){e=e.prev}var n;while(e.next!==r){e=e.next;if(!e.isRemoved()){if(t===\"key\"){n=e.key}else if(t===\"value\"){n=e.value}else{n=[e.key,e.value]}this.i=e;return Ke(n)}}this.i=void 0;return Ke()}};Me(i.prototype);var a;var u=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}if(this&&this._es6map){throw new TypeError(\"Bad construction\")}var e=Ae(this,Map,a,{_es6map:true,_head:null,_map:G?new G:null,_size:0,_storage:Qr()});var t=new r(null,null);t.next=t.prev=t;e._head=t;if(arguments.length>0){en(Map,e,arguments[0])}return e};a=u.prototype;m.getter(a,\"size\",function(){if(typeof this._size===\"undefined\"){throw new TypeError(\"size method called on incompatible Map\")}return this._size});b(a,{get:function get(e){o(this,\"get\");var t;var r=Yr(e,true);if(r!==null){t=this._storage[r];if(t){return t.value}else{return}}if(this._map){t=V.call(this._map,e);if(t){return t.value}else{return}}var n=this._head;var i=n;while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){return i.value}}},has:function has(e){o(this,\"has\");var t=Yr(e,true);if(t!==null){return typeof this._storage[t]!==\"undefined\"}if(this._map){return B.call(this._map,e)}var r=this._head;var n=r;while((n=n.next)!==r){if(ce.SameValueZero(n.key,e)){return true}}return false},set:function set(e,t){o(this,\"set\");var n=this._head;var i=n;var a;var u=Yr(e,true);if(u!==null){if(typeof this._storage[u]!==\"undefined\"){this._storage[u].value=t;return this}else{a=this._storage[u]=new r(e,t);i=n.prev}}else if(this._map){if(B.call(this._map,e)){V.call(this._map,e).value=t}else{a=new r(e,t);U.call(this._map,e,a);i=n.prev}}while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){i.value=t;return this}}a=a||new r(e,t);if(ce.SameValue(-0,e)){a.key=+0}a.next=this._head;a.prev=this._head.prev;a.prev.next=a;a.next.prev=a;this._size+=1;return this},\"delete\":function(t){o(this,\"delete\");var r=this._head;var n=r;var i=Yr(t,true);if(i!==null){if(typeof this._storage[i]===\"undefined\"){return false}n=this._storage[i].prev;delete this._storage[i]}else if(this._map){if(!B.call(this._map,t)){return false}n=V.call(this._map,t).prev;H.call(this._map,t)}while((n=n.next)!==r){if(ce.SameValueZero(n.key,t)){n.key=e;n.value=e;n.prev.next=n.next;n.next.prev=n.prev;this._size-=1;return true}}return false},clear:function clear(){o(this,\"clear\");this._map=G?new G:null;this._size=0;this._storage=Qr();var t=this._head;var r=t;var n=r.next;while((r=n)!==t){r.key=e;r.value=e;n=r.next;r.next=r.prev=t}t.next=t.prev=t},keys:function keys(){o(this,\"keys\");return new i(this,\"key\")},values:function values(){o(this,\"values\");return new i(this,\"value\")},entries:function entries(){o(this,\"entries\");return new i(this,\"key+value\")},forEach:function forEach(e){o(this,\"forEach\");var r=arguments.length>1?arguments[1]:null;var n=this.entries();for(var i=n.next();!i.done;i=n.next()){if(r){t(e,r,i.value[1],i.value[0],this)}else{e(i.value[1],i.value[0],this)}}}});Me(a,a.entries);return u}(),Set:function(){var e=function isSet(e){return e._es6set&&typeof e._storage!==\"undefined\"};var r=function requireSetSlot(t,r){if(!ce.TypeIsObject(t)||!e(t)){throw new TypeError(\"Set.prototype.\"+r+\" called on incompatible receiver \"+ce.ToString(t))}};var o;var i=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}if(this&&this._es6set){throw new TypeError(\"Bad construction\")}var e=Ae(this,Set,o,{_es6set:true,\"[[SetData]]\":null,_storage:Qr()});if(!e._es6set){throw new TypeError(\"bad set\")}if(arguments.length>0){tn(Set,e,arguments[0])}return e};o=i.prototype;var a=function(e){var t=e;if(t===\"^null\"){return null}else if(t===\"^undefined\"){return void 0}else{var r=t.charAt(0);if(r===\"$\"){return C(t,1)}else if(r===\"n\"){return+C(t,1)}else if(r===\"b\"){return t===\"btrue\"}}return+t};var u=function ensureMap(e){if(!e[\"[[SetData]]\"]){var t=new rn.Map;e[\"[[SetData]]\"]=t;l(n(e._storage),function(e){var r=a(e);t.set(r,r)});e[\"[[SetData]]\"]=t}e._storage=null};m.getter(i.prototype,\"size\",function(){r(this,\"size\");if(this._storage){return n(this._storage).length}u(this);return this[\"[[SetData]]\"].size});b(i.prototype,{has:function has(e){r(this,\"has\");var t;if(this._storage&&(t=Yr(e))!==null){return!!this._storage[t]}u(this);return this[\"[[SetData]]\"].has(e)},add:function add(e){r(this,\"add\");var t;if(this._storage&&(t=Yr(e))!==null){this._storage[t]=true;return this}u(this);this[\"[[SetData]]\"].set(e,e);return this},\"delete\":function(e){r(this,\"delete\");var t;if(this._storage&&(t=Yr(e))!==null){var n=z(this._storage,t);return delete this._storage[t]&&n}u(this);return this[\"[[SetData]]\"][\"delete\"](e)},clear:function clear(){r(this,\"clear\");if(this._storage){this._storage=Qr()}if(this[\"[[SetData]]\"]){this[\"[[SetData]]\"].clear()}},values:function values(){r(this,\"values\");u(this);return new f(this[\"[[SetData]]\"].values())},entries:function entries(){r(this,\"entries\");u(this);return new f(this[\"[[SetData]]\"].entries())},forEach:function forEach(e){r(this,\"forEach\");var n=arguments.length>1?arguments[1]:null;var o=this;u(o);this[\"[[SetData]]\"].forEach(function(r,i){if(n){t(e,n,i,i,o)}else{e(i,i,o)}})}});h(i.prototype,\"keys\",i.prototype.values,true);Me(i.prototype,i.prototype.values);var f=function SetIterator(e){this.it=e};f.prototype={isSetIterator:true,next:function next(){if(!this.isSetIterator){throw new TypeError(\"Not a SetIterator\")}return this.it.next()}};Me(f.prototype);return i}()};var nn=S.Set&&!Set.prototype[\"delete\"]&&Set.prototype.remove&&Set.prototype.items&&Set.prototype.map&&Array.isArray((new Set).keys);if(nn){S.Set=rn.Set}if(S.Map||S.Set){var on=a(function(){return new Map([[1,2]]).get(1)===2});if(!on){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,S.Map.prototype);return e};S.Map.prototype=O(G.prototype);h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var an=new Map;var un=function(){var e=new Map([[1,0],[2,0],[3,0],[4,0]]);e.set(-0,e);return e.get(0)===e&&e.get(-0)===e&&e.has(0)&&e.has(-0)}();var fn=an.set(1,2)===an;if(!un||!fn){ne(Map.prototype,\"set\",function set(e,r){t(U,this,e===0?0:e,r);return this})}if(!un){b(Map.prototype,{get:function get(e){return t(V,this,e===0?0:e)},has:function has(e){return t(B,this,e===0?0:e)}},true);m.preserveToString(Map.prototype.get,V);m.preserveToString(Map.prototype.has,B)}var sn=new Set;var cn=Set.prototype[\"delete\"]&&Set.prototype.add&&Set.prototype.has&&function(e){e[\"delete\"](0);e.add(-0);return!e.has(0)}(sn);var ln=sn.add(1)===sn;if(!cn||!ln){var pn=Set.prototype.add;Set.prototype.add=function add(e){t(pn,this,e===0?0:e);return this};m.preserveToString(Set.prototype.add,pn)}if(!cn){var vn=Set.prototype.has;Set.prototype.has=function has(e){return t(vn,this,e===0?0:e)};m.preserveToString(Set.prototype.has,vn);var yn=Set.prototype[\"delete\"];Set.prototype[\"delete\"]=function SetDelete(e){return t(yn,this,e===0?0:e)};m.preserveToString(Set.prototype[\"delete\"],yn)}var hn=w(S.Map,function(e){var t=new e([]);t.set(42,42);return t instanceof e});var bn=Object.setPrototypeOf&&!hn;var gn=function(){try{return!(S.Map()instanceof S.Map)}catch(e){return e instanceof TypeError}}();if(S.Map.length!==0||bn||!gn){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Map.prototype);return e};S.Map.prototype=G.prototype;h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var dn=w(S.Set,function(e){var t=new e([]);t.add(42,42);return t instanceof e});var mn=Object.setPrototypeOf&&!dn;var On=function(){try{return!(S.Set()instanceof S.Set)}catch(e){return e instanceof TypeError}}();if(S.Set.length!==0||mn||!On){var wn=S.Set;S.Set=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}var e=new wn;if(arguments.length>0){tn(Set,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Set.prototype);return e};S.Set.prototype=wn.prototype;h(S.Set.prototype,\"constructor\",S.Set,true);m.preserveToString(S.Set,wn)}var jn=new S.Map;var Sn=!a(function(){return jn.keys().next().done});if(typeof S.Map.prototype.clear!==\"function\"||(new S.Set).size!==0||jn.size!==0||typeof S.Map.prototype.keys!==\"function\"||typeof S.Set.prototype.keys!==\"function\"||typeof S.Map.prototype.forEach!==\"function\"||typeof S.Set.prototype.forEach!==\"function\"||u(S.Map)||u(S.Set)||typeof jn.keys().next!==\"function\"||Sn||!hn){b(S,{Map:rn.Map,Set:rn.Set},true)}if(S.Set.prototype.keys!==S.Set.prototype.values){h(S.Set.prototype,\"keys\",S.Set.prototype.values,true)}Me(Object.getPrototypeOf((new S.Map).keys()));Me(Object.getPrototypeOf((new S.Set).keys()));if(c&&S.Set.prototype.has.name!==\"has\"){var Tn=S.Set.prototype.has;ne(S.Set.prototype,\"has\",function has(e){return t(Tn,this,e)})}}b(S,rn);Ce(S.Map);Ce(S.Set)}var In=function throwUnlessTargetIsObject(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"target must be an object\")}};var En={apply:function apply(){return ce.Call(ce.Call,null,arguments)},construct:function construct(e,t){if(!ce.IsConstructor(e)){throw new TypeError(\"First argument must be a constructor.\")}var r=arguments.length>2?arguments[2]:e;if(!ce.IsConstructor(r)){throw new TypeError(\"new.target must be a constructor.\")}return ce.Construct(e,t,r,\"internal\")},deleteProperty:function deleteProperty(e,t){In(e);if(s){var r=Object.getOwnPropertyDescriptor(e,t);if(r&&!r.configurable){return false}}return delete e[t]},has:function has(e,t){In(e);return t in e}};if(Object.getOwnPropertyNames){Object.assign(En,{ownKeys:function ownKeys(e){In(e);var t=Object.getOwnPropertyNames(e);if(ce.IsCallable(Object.getOwnPropertySymbols)){x(t,Object.getOwnPropertySymbols(e))}return t}})}var Pn=function ConvertExceptionToBoolean(e){return!i(e)};if(Object.preventExtensions){Object.assign(En,{isExtensible:function isExtensible(e){In(e);return Object.isExtensible(e)},preventExtensions:function preventExtensions(e){In(e);return Pn(function(){return Object.preventExtensions(e)})}})}if(s){var Cn=function get(e,t,r){var n=Object.getOwnPropertyDescriptor(e,t);if(!n){var o=Object.getPrototypeOf(e);if(o===null){return void 0}return Cn(o,t,r)}if(\"value\"in n){return n.value}if(n.get){return ce.Call(n.get,r)}return void 0};var Mn=function set(e,r,n,o){var i=Object.getOwnPropertyDescriptor(e,r);if(!i){var a=Object.getPrototypeOf(e);if(a!==null){return Mn(a,r,n,o)}i={value:void 0,writable:true,enumerable:true,configurable:true}}if(\"value\"in i){if(!i.writable){return false}if(!ce.TypeIsObject(o)){return false}var u=Object.getOwnPropertyDescriptor(o,r);if(u){return ae.defineProperty(o,r,{value:n})}else{return ae.defineProperty(o,r,{value:n,writable:true,enumerable:true,configurable:true})}}if(i.set){t(i.set,o,n);return true}return false};Object.assign(En,{defineProperty:function defineProperty(e,t,r){In(e);return Pn(function(){return Object.defineProperty(e,t,r)})},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(e,t){In(e);return Object.getOwnPropertyDescriptor(e,t)},get:function get(e,t){In(e);var r=arguments.length>2?arguments[2]:e;return Cn(e,t,r)},set:function set(e,t,r){In(e);var n=arguments.length>3?arguments[3]:e;return Mn(e,t,r,n)}})}if(Object.getPrototypeOf){var xn=Object.getPrototypeOf;En.getPrototypeOf=function getPrototypeOf(e){In(e);return xn(e)}}if(Object.setPrototypeOf&&En.getPrototypeOf){var Nn=function(e,t){var r=t;while(r){if(e===r){return true}r=En.getPrototypeOf(r)}return false};Object.assign(En,{setPrototypeOf:function setPrototypeOf(e,t){In(e);if(t!==null&&!ce.TypeIsObject(t)){throw new TypeError(\"proto must be an object or null\")}if(t===ae.getPrototypeOf(e)){return true}if(ae.isExtensible&&!ae.isExtensible(e)){return false}if(Nn(e,t)){return false}Object.setPrototypeOf(e,t);return true}})}var An=function(e,t){if(!ce.IsCallable(S.Reflect[e])){h(S.Reflect,e,t)}else{var r=a(function(){S.Reflect[e](1);S.Reflect[e](NaN);S.Reflect[e](true);return true});if(r){ne(S.Reflect,e,t)}}};Object.keys(En).forEach(function(e){An(e,En[e])});var Rn=S.Reflect.getPrototypeOf;if(c&&Rn&&Rn.name!==\"getPrototypeOf\"){ne(S.Reflect,\"getPrototypeOf\",function getPrototypeOf(e){return t(Rn,S.Reflect,e)})}if(S.Reflect.setPrototypeOf){if(a(function(){S.Reflect.setPrototypeOf(1,{});return true})){ne(S.Reflect,\"setPrototypeOf\",En.setPrototypeOf)}}if(S.Reflect.defineProperty){if(!a(function(){var e=!S.Reflect.defineProperty(1,\"test\",{value:1});var t=typeof Object.preventExtensions!==\"function\"||!S.Reflect.defineProperty(Object.preventExtensions({}),\"test\",{});return e&&t})){ne(S.Reflect,\"defineProperty\",En.defineProperty)}}if(S.Reflect.construct){if(!a(function(){var e=function F(){};return S.Reflect.construct(function(){},[],e)instanceof e})){ne(S.Reflect,\"construct\",En.construct)}}if(String(new Date(NaN))!==\"Invalid Date\"){var _n=Date.prototype.toString;var kn=function toString(){var e=+this;if(e!==e){return\"Invalid Date\"}return ce.Call(_n,this)};ne(Date.prototype,\"toString\",kn)}var Ln={anchor:function anchor(e){return ce.CreateHTML(this,\"a\",\"name\",e)},big:function big(){return ce.CreateHTML(this,\"big\",\"\",\"\")},blink:function blink(){return ce.CreateHTML(this,\"blink\",\"\",\"\")},bold:function bold(){return ce.CreateHTML(this,\"b\",\"\",\"\")},fixed:function fixed(){return ce.CreateHTML(this,\"tt\",\"\",\"\")},fontcolor:function fontcolor(e){return ce.CreateHTML(this,\"font\",\"color\",e)},fontsize:function fontsize(e){return ce.CreateHTML(this,\"font\",\"size\",e)},italics:function italics(){return ce.CreateHTML(this,\"i\",\"\",\"\")},link:function link(e){return ce.CreateHTML(this,\"a\",\"href\",e)},small:function small(){return ce.CreateHTML(this,\"small\",\"\",\"\")},strike:function strike(){return ce.CreateHTML(this,\"strike\",\"\",\"\")},sub:function sub(){return ce.CreateHTML(this,\"sub\",\"\",\"\")},sup:function sub(){return ce.CreateHTML(this,\"sup\",\"\",\"\")}};l(Object.keys(Ln),function(e){var r=String.prototype[e];var n=false;if(ce.IsCallable(r)){var o=t(r,\"\",' \" ');var i=P([],o.match(/\"/g)).length;n=o!==o.toLowerCase()||i>2}else{n=true}if(n){ne(String.prototype,e,Ln[e])}});var Fn=function(){if(!oe){return false}var e=typeof JSON===\"object\"&&typeof JSON.stringify===\"function\"?JSON.stringify:null;if(!e){return false}if(typeof e($())!==\"undefined\"){return true}if(e([$()])!==\"[null]\"){return true}var t={a:$()};t[$()]=true;if(e(t)!==\"{}\"){return true}return false}();var Dn=a(function(){if(!oe){return true}return JSON.stringify(Object($()))===\"{}\"&&JSON.stringify([Object($())])===\"[{}]\"});if(Fn||!Dn){var zn=JSON.stringify;ne(JSON,\"stringify\",function stringify(e){if(typeof e===\"symbol\"){return}var n;if(arguments.length>1){n=arguments[1]}var o=[e];if(!r(n)){var i=ce.IsCallable(n)?n:null;var a=function(e,r){var n=i?t(i,this,e,r):r;if(typeof n!==\"symbol\"){if(re.symbol(n)){return Nt({})(n)}else{return n}}};o.push(a)}else{o.push(n)}if(arguments.length>2){o.push(arguments[2])}return zn.apply(this,o)})}return S});\n/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(C,e){\"use strict\";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return\"function\"==typeof e&&\"number\"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement(\"script\");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?n[o.call(e)]||\"object\":typeof e}var f=\"3.5.1\",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&\"length\"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&(\"array\"===n||0===t||\"number\"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for(\"boolean\"==typeof a&&(l=a,a=arguments[s]||{},s++),\"object\"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],\"__proto__\"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:\"jQuery\"+(f+Math.random()).replace(/\\D/g,\"\"),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||\"[object Object]\"!==o.call(e))&&(!(t=r(e))||\"function\"==typeof(n=v.call(t,\"constructor\")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,\"string\"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),\"function\"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){n[\"[object \"+t+\"]\"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S=\"sizzle\"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R=\"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",M=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",I=\"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",W=\"\\\\[\"+M+\"*(\"+I+\")(?:\"+M+\"*([*^$|!~]?=)\"+M+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+I+\"))|)\"+M+\"*\\\\]\",F=\":(\"+I+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+W+\")*)|.*)\\\\)|)\",B=new RegExp(M+\"+\",\"g\"),$=new RegExp(\"^\"+M+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+M+\"+$\",\"g\"),_=new RegExp(\"^\"+M+\"*,\"+M+\"*\"),z=new RegExp(\"^\"+M+\"*([>+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp(F),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+F),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+M+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+M+\"*(?:([+-]|)\"+M+\"*(\\\\d+)|))\"+M+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+R+\")$\",\"i\"),needsContext:new RegExp(\"^\"+M+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+M+\"*((?:-\\\\d)?\\\\d*)\"+M+\"*\\\\)|)(?=[^-]|$)\",\"i\")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),ne=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?\"\\0\"===e?\"\\ufffd\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&\"fieldset\"===e.nodeName.toLowerCase()},{dir:\"parentNode\",next:\"legend\"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],\"string\"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?\"#\"+s:\":scope\")+\" \"+xe(l[o]);c=l.join(\",\")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute(\"id\")}}}return g(t.replace($,\"$1\"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+\" \")>b.cacheLength&&delete e[r.shift()],e[t+\" \"]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split(\"|\"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return\"input\"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return(\"input\"===t||\"button\"===t)&&e.type===n}}function ge(t){return function(e){return\"form\"in e?e.parentNode&&!1===e.disabled?\"label\"in e?\"label\"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:\"label\"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||\"HTML\")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement(\"div\")),\"undefined\"!=typeof e.querySelectorAll&&!e.querySelectorAll(\":scope fieldset div\").length}),d.attributes=ce(function(e){return e.className=\"i\",!e.getAttribute(\"className\")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute(\"id\")===t}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=\"undefined\"!=typeof e.getAttributeNode&&e.getAttributeNode(\"id\");return t&&t.value===n}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(\"undefined\"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=\"<a id='\"+S+\"'></a><select id='\"+S+\"-\\r\\\\' msallowcapture=''><option selected=''></option></select>\",e.querySelectorAll(\"[msallowcapture^='']\").length&&v.push(\"[*^$]=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\"[selected]\").length||v.push(\"\\\\[\"+M+\"*(?:value|\"+R+\")\"),e.querySelectorAll(\"[id~=\"+S+\"-]\").length||v.push(\"~=\"),(t=C.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||v.push(\"\\\\[\"+M+\"*name\"+M+\"*=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+S+\"+*\").length||v.push(\".#.+[+~]\"),e.querySelectorAll(\"\\\\\\f\"),v.push(\"[\\\\r\\\\n\\\\f]\")}),ce(function(e){e.innerHTML=\"<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>\";var t=C.createElement(\"input\");t.setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),e.querySelectorAll(\"[name=d]\").length&&v.push(\"name\"+M+\"*[*^$|!~]?=\"),2!==e.querySelectorAll(\":enabled\").length&&v.push(\":enabled\",\":disabled\"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&v.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),v.push(\",.*:\")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,\"*\"),c.call(e,\"[s!='']:x\"),s.push(\"!=\",F)}),v=v.length&&new RegExp(v.join(\"|\")),s=s.length&&new RegExp(s.join(\"|\")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+\" \"]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+\"\").replace(re,ie)},se.error=function(e){throw new Error(\"Syntax error, unrecognized expression: \"+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||\"\").replace(te,ne),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&m(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?\"!=\"===r:!r||(t+=\"\",\"=\"===r?t===i:\"!=\"===r?t!==i:\"^=\"===r?i&&0===t.indexOf(i):\"*=\"===r?i&&-1<t.indexOf(i):\"$=\"===r?i&&t.slice(-i.length)===i:\"~=\"===r?-1<(\" \"+t.replace(B,\" \")+\" \").indexOf(i):\"|=\"===r&&(t===i||t.slice(0,i.length+1)===i+\"-\"))}},CHILD:function(h,e,t,g,v){var y=\"nth\"!==h.slice(0,3),m=\"last\"!==h.slice(-4),x=\"of-type\"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?\"nextSibling\":\"previousSibling\",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l=\"only\"===h&&!u&&\"nextSibling\"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error(\"unsupported pseudo: \"+e);return a[S]?a(o):1<a.length?(t=[e,e,\"\",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,\"$1\"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||\"\")||se.error(\"unsupported lang: \"+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute(\"xml:lang\")||e.getAttribute(\"lang\"))return(t=t.toLowerCase())===n||0===t.indexOf(n+\"-\")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||\"text\"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r=\"\";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&\"parentNode\"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||\"*\",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[\" \"],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:\" \"===e[s-2].type?\"*\":\"\"})).replace($,\"$1\"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+\" \"];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($,\" \")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+\" \"];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l=\"0\",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG(\"*\",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l=\"function\"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&\"ID\"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split(\"\").sort(D).join(\"\")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement(\"fieldset\"))}),ce(function(e){return e.innerHTML=\"<a href='#'></a>\",\"#\"===e.firstChild.getAttribute(\"href\")})||fe(\"type|href|height|width\",function(e,t,n){if(!n)return e.getAttribute(t,\"type\"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML=\"<input/>\",e.firstChild.setAttribute(\"value\",\"\"),\"\"===e.firstChild.getAttribute(\"value\")})||fe(\"value\",function(e,t,n){if(!n&&\"input\"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute(\"disabled\")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[\":\"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,\"string\"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a=\"string\"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,\"parentNode\")},parentsUntil:function(e,t,n){return h(e,\"parentNode\",n)},next:function(e){return O(e,\"nextSibling\")},prev:function(e){return O(e,\"previousSibling\")},nextAll:function(e){return h(e,\"nextSibling\")},prevAll:function(e){return h(e,\"previousSibling\")},nextUntil:function(e,t,n){return h(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return h(e,\"previousSibling\",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,\"template\")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return\"Until\"!==r.slice(-5)&&(t=e),t&&\"string\"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\\x20\\t\\r\\n\\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r=\"string\"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:\"\")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&\"string\"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t=\"\",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=\"\"),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[[\"notify\",\"progress\",S.Callbacks(\"memory\"),S.Callbacks(\"memory\"),2],[\"resolve\",\"done\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),1,\"rejected\"]],i=\"pending\",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},\"catch\":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+\"With\"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError(\"Thenable self-resolution\");t=e&&(\"object\"==typeof e||\"function\"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+\"With\"](this===s?void 0:this,arguments),this},s[t[0]+\"With\"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),\"pending\"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn(\"jQuery.Deferred exception: \"+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener(\"DOMContentLoaded\",B),C.removeEventListener(\"load\",B),S.ready()}S.fn.ready=function(e){return F.then(e)[\"catch\"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,\"complete\"===E.readyState||\"loading\"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener(\"DOMContentLoaded\",B),C.addEventListener(\"load\",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if(\"object\"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,\"ms-\").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if(\"string\"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&\"string\"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(K,\"-$&\").toLowerCase(),\"string\"==typeof(n=e.getAttribute(r))){try{n=\"true\"===(i=n)||\"false\"!==i&&(\"null\"===i?null:i===+i+\"\"?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,\"hasDataAttrs\"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf(\"data-\")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,\"hasDataAttrs\",!0)}return i}return\"object\"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks(\"once memory\").add(function(){Y.remove(e,[t+\"queue\",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return\"string\"!=typeof t&&(n=t,t=\"fx\",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),\"fx\"===t&&\"inprogress\"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};\"string\"!=typeof e&&(t=e,e=void 0),e=e||\"fx\";while(a--)(n=Y.get(o[a],e+\"queueHooks\"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,te=new RegExp(\"^(?:([+-])=|)(\"+ee+\")([a-z%]*)$\",\"i\"),ne=[\"Top\",\"Right\",\"Bottom\",\"Left\"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return\"none\"===(e=t||e).style.display||\"\"===e.style.display&&ie(e)&&\"none\"===S.css(e,\"display\")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,\"\")},u=s(),l=n&&n[3]||(S.cssNumber[t]?\"\":\"px\"),c=e.nodeType&&(S.cssNumber[t]||\"px\"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?(\"none\"===n&&(l[c]=Y.get(r,\"display\")||null,l[c]||(r.style.display=\"\")),\"\"===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,\"display\"),o.parentNode.removeChild(o),\"none\"===u&&(u=\"block\"),ue[s]=u)))):\"none\"!==n&&(l[c]=\"none\",Y.set(r,\"display\",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(fe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),fe.setAttribute(\"checked\",\"checked\"),fe.setAttribute(\"name\",\"t\"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML=\"<textarea>x</textarea>\",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=\"<option></option>\",y.option=!!ce.lastChild;var ge={thead:[1,\"<table>\",\"</table>\"],col:[2,\"<table><colgroup>\",\"</colgroup></table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:[0,\"\",\"\"]};function ve(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],\"globalEval\",!t||Y.get(t[n],\"globalEval\"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,\"<select multiple='multiple'>\",\"</select>\"]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if(\"object\"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement(\"div\")),s=(de.exec(o)||[\"\",\"\"])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=\"\"}else p.push(t.createTextNode(o));f.textContent=\"\",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),\"script\"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||\"\")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==(\"focus\"===t)}function ke(e,t,n,r,i,o){var a,s;if(\"object\"==typeof t){for(s in\"string\"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&(\"string\"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return\"undefined\"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||\"\").match(P)||[\"\"]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(\".\")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||\"\").match(P)||[\"\"]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&(\"**\"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,\"events\")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!(\"click\"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(\"click\"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+\" \"]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Y.get(t,\"click\")||A(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,\"char\":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+\".\"+r.namespace:r.origType,r.selector,r.handler),this;if(\"object\"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&\"function\"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\\s*(?:[^=]|=\\s*.checked.)/i,je=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;function qe(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&S(e).children(\"tbody\")[0]||e}function Le(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function He(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&\"string\"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,\"script\"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,\"script\"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||\"\")&&!Y.access(u,\"globalEval\")&&S.contains(l,u)&&(u.src&&\"module\"!==(u.type||\"\").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute(\"nonce\")},l):b(u.textContent.replace(je,\"\"),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,\"script\")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,\"input\"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:\"input\"!==l&&\"textarea\"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,\"script\")).length&&ye(a,!f&&ve(e,\"script\")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp(\"^(\"+ee+\")(?!px)[a-z%]+$\",\"i\"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join(\"|\"),\"i\");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(\"\"!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+\"\":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText=\"position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0\",l.style.cssText=\"position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%\",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n=\"1%\"!==e.top,s=12===t(e.marginLeft),l.style.right=\"60%\",o=36===t(e.right),r=36===t(e.width),l.style.position=\"absolute\",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement(\"div\"),l=E.createElement(\"div\");l.style&&(l.style.backgroundClip=\"content-box\",l.cloneNode(!0).style.backgroundClip=\"\",y.clearCloneStyle=\"content-box\"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement(\"table\"),t=E.createElement(\"tr\"),n=E.createElement(\"div\"),e.style.cssText=\"position:absolute;left:-11111px\",t.style.height=\"1px\",n.style.height=\"9px\",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=[\"Webkit\",\"Moz\",\"ms\"],ze=E.createElement(\"div\").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Qe={letterSpacing:\"0\",fontWeight:\"400\"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ke(e,t,n,r,i,o){var a=\"width\"===t?1:0,s=0,u=0;if(n===(r?\"border\":\"content\"))return 0;for(;a<4;a+=2)\"margin\"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?(\"content\"===n&&(u-=S.css(e,\"padding\"+ne[a],!0,i)),\"margin\"!==n&&(u-=S.css(e,\"border\"+ne[a]+\"Width\",!0,i))):(u+=S.css(e,\"padding\"+ne[a],!0,i),\"padding\"!==n?u+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i):s+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,r),o=i,a=Be(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a=\"auto\"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===S.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===S.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?\"\":\"px\")),y.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),\"normal\"===i&&t in Qe&&(i=Qe[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each([\"height\",\"width\"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&\"absolute\"===i.position,a=(o||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e[\"offset\"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,\"border\",!1,i)-.5)),s&&(r=te.exec(t))&&\"px\"!==(r[3]||\"px\")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,\"marginLeft\"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+\"px\"}),S.each({margin:\"\",padding:\"\",border:\"Width\"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r=\"string\"==typeof e?e.split(\" \"):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},\"margin\"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?\"\":\"px\")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=ne[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners[\"*\"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&\"expand\"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=[\"*\"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f=\"width\"in t||\"height\"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,\"fxshow\");for(r in n.queue||(null==(a=S._queueHooks(e,\"fx\")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,\"fx\").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||\"toggle\"===i,i===(g?\"hide\":\"show\")){if(\"show\"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,\"display\")),\"none\"===(c=S.css(e,\"display\"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,\"display\"),le([e]))),(\"inline\"===c||\"inline-block\"===c&&null!=l)&&\"none\"===S.css(e,\"float\")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l=\"none\"===c?\"\":c)),h.display=\"inline-block\")),n.overflow&&(h.overflow=\"hidden\",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?\"hidden\"in v&&(g=v.hidden):v=Y.access(e,\"fxshow\",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,\"fxshow\"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&\"object\"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,\"finish\"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return\"string\"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||\"fx\",[]),this.each(function(){var e=!0,t=null!=i&&i+\"queueHooks\",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||\"fx\"),this.each(function(){var e,t=Y.get(this),n=t[a+\"queue\"],r=t[a+\"queueHooks\"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each([\"toggle\",\"show\",\"hide\"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||\"boolean\"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt(\"show\"),slideUp:lt(\"hide\"),slideToggle:lt(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||\"fx\",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement(\"input\"),it=E.createElement(\"select\").appendChild(E.createElement(\"option\")),rt.type=\"checkbox\",y.checkOn=\"\"!==rt.value,y.optSelected=it.selected,(rt=E.createElement(\"input\")).value=\"t\",rt.type=\"radio\",y.radioValue=\"t\"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return\"undefined\"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&\"radio\"===t&&A(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(\" \")}function yt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function mt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,\"tabindex\");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr(\"class\",\"\");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])while(-1<r.indexOf(\" \"+o+\" \"))r=r.replace(\" \"+o+\" \",\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},toggleClass:function(i,t){var o=typeof i,a=\"string\"===o||Array.isArray(i);return\"boolean\"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&\"boolean\"!==o||((e=yt(this))&&Y.set(this,\"__className__\",e),this.setAttribute&&this.setAttribute(\"class\",e||!1===i?\"\":Y.get(this,\"__className__\")||\"\"))})},hasClass:function(e){var t,n,r=0;t=\" \"+e+\" \";while(n=this[r++])if(1===n.nodeType&&-1<(\" \"+vt(yt(n))+\" \").indexOf(t))return!0;return!1}});var xt=/\\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t=\"\":\"number\"==typeof t?t+=\"\":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?\"\":e+\"\"})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&\"set\"in r&&void 0!==r.set(this,t,\"value\")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&\"get\"in r&&void 0!==(e=r.get(t,\"value\"))?e:\"string\"==typeof(e=t.value)?e.replace(xt,\"\"):null==e?\"\":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,\"value\");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,\"optgroup\"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each([\"radio\",\"checkbox\"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}),y.focusin=\"onfocusin\"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,\"type\")?e.type:e,h=v.call(e,\"namespace\")?e.namespace.split(\".\"):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(\".\")&&(d=(h=d.split(\".\")).shift(),h.sort()),u=d.indexOf(\":\")<0&&\"on\"+d,(e=e[S.expando]?e:new S.Event(d,\"object\"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join(\".\"),e.rnamespace=e.namespace?new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,\"events\")||Object.create(null))[e.type]&&Y.get(o,\"handle\"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:\"focusin\",blur:\"focusout\"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\\?/;S.parseXML=function(e){var t;if(!e||\"string\"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,\"text/xml\")}catch(e){t=void 0}return t&&!t.getElementsByTagName(\"parsererror\").length||S.error(\"Invalid XML: \"+e),t};var St=/\\[\\]$/,kt=/\\r?\\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+\"[\"+(\"object\"==typeof t&&null!=t?e:\"\")+\"]\",t,r,i)});else if(r||\"object\"!==w(e))i(n,e);else for(t in e)Dt(n+\"[\"+t+\"]\",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join(\"&\")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,\"elements\");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(\":disabled\")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,\"\\r\\n\")}}):{name:t.name,value:n.replace(kt,\"\\r\\n\")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\\/\\//,Rt={},Mt={},It=\"*/\".concat(\"*\"),Wt=E.createElement(\"a\");function Ft(o){return function(e,t){\"string\"!=typeof e&&(t=e,e=\"*\");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])\"+\"===n[0]?(n=n.slice(1)||\"*\",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return\"string\"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s[\"*\"]&&l(\"*\")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":It,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){\"object\"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks(\"once memory\"),w=v.statusCode||{},a={},s={},u=\"canceled\",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+\" \"]=(n[t[1].toLowerCase()+\" \"]||[]).concat(t[2])}t=n[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+\"\").replace(Pt,Tt.protocol+\"//\"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||\"*\").toLowerCase().match(P)||[\"\"],null==v.crossDomain){r=E.createElement(\"a\");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+\"//\"+Wt.host!=r.protocol+\"//\"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&\"string\"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger(\"ajaxStart\"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,\"\"),v.hasContent?v.data&&v.processData&&0===(v.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(v.data=v.data.replace(jt,\"+\")):(o=v.url.slice(f.length),v.data&&(v.processData||\"string\"==typeof v.data)&&(f+=(Et.test(f)?\"&\":\"?\")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,\"$1\"),o=(Et.test(f)?\"&\":\"?\")+\"_=\"+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader(\"If-Modified-Since\",S.lastModified[f]),S.etag[f]&&T.setRequestHeader(\"If-None-Match\",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader(\"Content-Type\",v.contentType),T.setRequestHeader(\"Accept\",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+(\"*\"!==v.dataTypes[0]?\", \"+It+\"; q=0.01\":\"\"):v.accepts[\"*\"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u=\"abort\",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger(\"ajaxSend\",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort(\"timeout\")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,\"No Transport\");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||\"\",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while(\"*\"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray(\"script\",v.dataTypes)&&(v.converters[\"text script\"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e[\"throws\"])t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader(\"Last-Modified\"))&&(S.lastModified[f]=u),(u=T.getResponseHeader(\"etag\"))&&(S.etag[f]=u)),204===e||\"HEAD\"===v.type?l=\"nocontent\":304===e?l=\"notmodified\":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l=\"error\",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+\"\",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?\"ajaxSuccess\":\"ajaxError\",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger(\"ajaxComplete\",[T,v]),--S.active||S.event.trigger(\"ajaxStop\")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,\"json\")},getScript:function(e,t){return S.get(e,void 0,t,\"script\")}}),S.each([\"get\",\"post\"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&\"withCredentials\"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e[\"X-Requested-With\"]||(e[\"X-Requested-With\"]=\"XMLHttpRequest\"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,\"abort\"===e?r.abort():\"error\"===e?\"number\"!=typeof r.status?t(0,\"error\"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,\"text\"!==(r.responseType||\"text\")||\"string\"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o(\"error\"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o(\"abort\");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")}),S.ajaxTransport(\"script\",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S(\"<script>\").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on(\"load error\",i=function(e){r.remove(),i=null,e&&t(\"error\"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=Xt.pop()||S.expando+\"_\"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Vt.test(e.data)&&\"data\");if(a||\"jsonp\"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,\"$1\"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||S.error(r+\" was not called\"),o[0]},e.dataTypes[0]=\"json\",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"<form></form><form></form>\",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return\"string\"!=typeof e?[]:(\"boolean\"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument(\"\")).createElement(\"base\")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&\"object\"==typeof t&&(i=\"POST\"),0<a.length&&S.ajax({url:e,type:i||\"GET\",dataType:\"html\",data:t}).done(function(e){o=arguments,a.html(r?S(\"<div>\").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,\"position\"),c=S(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=S.css(e,\"top\"),u=S.css(e,\"left\"),(\"absolute\"===l||\"fixed\"===l)&&-1<(o+u).indexOf(\"auto\")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),\"using\"in t?t.using.call(e,f):(\"number\"==typeof f.top&&(f.top+=\"px\"),\"number\"==typeof f.left&&(f.left+=\"px\"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if(\"fixed\"===S.css(r,\"position\"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&\"static\"===S.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,\"borderTopWidth\",!0),i.left+=S.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-S.css(r,\"marginTop\",!0),left:t.left-i.left-S.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===S.css(e,\"position\"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each([\"top\",\"left\"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+\"px\":t})}),S.each({Height:\"height\",Width:\"width\"},function(a,s){S.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||\"boolean\"!=typeof e),i=r||(!0===e||!0===t?\"margin\":\"border\");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf(\"outer\")?e[\"inner\"+a]:e.document.documentElement[\"client\"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body[\"scroll\"+a],r[\"scroll\"+a],e.body[\"offset\"+a],r[\"offset\"+a],r[\"client\"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?\"\":(e+\"\").replace(Gt,\"\")},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},\"undefined\"==typeof e&&(C.jQuery=C.$=S),S});\n/*\n * jQuery throttle / debounce - v1.1 - 3/7/2010\n * http://benalman.com/projects/jquery-throttle-debounce-plugin/\n * \n * Copyright (c) 2010 \"Cowboy\" Ben Alman\n * Dual licensed under the MIT and GPL licenses.\n * http://benalman.com/about/license/\n */\n(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!==\"boolean\"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);\n/*!\n * imagesLoaded PACKAGED v4.1.4\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n!function(e,t){\"function\"==typeof define&&define.amd?define(\"ev-emitter/ev-emitter\",t):\"object\"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}(\"undefined\"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o<i.length;o++){var r=i[o],s=n&&n[r];s&&(this.off(e,r),delete n[r]),r.apply(this,t)}return this}},t.allOff=function(){delete this._events,delete this._onceEvents},e}),function(e,t){\"use strict\";\"function\"==typeof define&&define.amd?define([\"ev-emitter/ev-emitter\"],function(i){return t(e,i)}):\"object\"==typeof module&&module.exports?module.exports=t(e,require(\"ev-emitter\")):e.imagesLoaded=t(e,e.EvEmitter)}(\"undefined\"!=typeof window?window:this,function(e,t){function i(e,t){for(var i in t)e[i]=t[i];return e}function n(e){if(Array.isArray(e))return e;var t=\"object\"==typeof e&&\"number\"==typeof e.length;return t?d.call(e):[e]}function o(e,t,r){if(!(this instanceof o))return new o(e,t,r);var s=e;return\"string\"==typeof e&&(s=document.querySelectorAll(e)),s?(this.elements=n(s),this.options=i({},this.options),\"function\"==typeof t?r=t:i(this.options,t),r&&this.on(\"always\",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(this.check.bind(this))):void a.error(\"Bad element for imagesLoaded \"+(s||e))}function r(e){this.img=e}function s(e,t){this.url=e,this.element=t,this.img=new Image}var h=e.jQuery,a=e.console,d=Array.prototype.slice;o.prototype=Object.create(t.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(e){\"IMG\"==e.nodeName&&this.addImage(e),this.options.background===!0&&this.addElementBackgroundImages(e);var t=e.nodeType;if(t&&u[t]){for(var i=e.querySelectorAll(\"img\"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if(\"string\"==typeof this.options.background){var r=e.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var u={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(e){var t=getComputedStyle(e);if(t)for(var i=/url\\((['\"])?(.*?)\\1\\)/gi,n=i.exec(t.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,e),n=i.exec(t.backgroundImage)}},o.prototype.addImage=function(e){var t=new r(e);this.images.push(t)},o.prototype.addBackground=function(e,t){var i=new s(e,t);this.images.push(i)},o.prototype.check=function(){function e(e,i,n){setTimeout(function(){t.progress(e,i,n)})}var t=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(t){t.once(\"progress\",e),t.check()}):void this.complete()},o.prototype.progress=function(e,t,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emitEvent(\"progress\",[this,e,t]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,e),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log(\"progress: \"+i,e,t)},o.prototype.complete=function(){var e=this.hasAnyBroken?\"fail\":\"done\";if(this.isComplete=!0,this.emitEvent(e,[this]),this.emitEvent(\"always\",[this]),this.jqDeferred){var t=this.hasAnyBroken?\"reject\":\"resolve\";this.jqDeferred[t](this)}},r.prototype=Object.create(t.prototype),r.prototype.check=function(){var e=this.getIsImageComplete();return e?void this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"):(this.proxyImage=new Image,this.proxyImage.addEventListener(\"load\",this),this.proxyImage.addEventListener(\"error\",this),this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},r.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.img,t])},r.prototype.handleEvent=function(e){var t=\"on\"+e.type;this[t]&&this[t](e)},r.prototype.onload=function(){this.confirm(!0,\"onload\"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,\"onerror\"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener(\"load\",this),this.proxyImage.removeEventListener(\"error\",this),this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),this.img.src=this.url;var e=this.getIsImageComplete();e&&(this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.element,t])},o.makeJQueryPlugin=function(t){t=t||e.jQuery,t&&(h=t,h.fn.imagesLoaded=function(e,t){var i=new o(this,e,t);return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o});\n/*! lz-string-1.3.3-min.js | (c) 2013 Pieroxy | Licensed under a WTFPL license */\nvar LZString={_keyStr:\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",_f:String.fromCharCode,compressToBase64:function(e){if(e==null)return\"\";var t=\"\";var n,r,i,s,o,u,a;var f=0;e=LZString.compress(e);while(f<e.length*2){if(f%2==0){n=e.charCodeAt(f/2)>>8;r=e.charCodeAt(f/2)&255;if(f/2+1<e.length)i=e.charCodeAt(f/2+1)>>8;else i=NaN}else{n=e.charCodeAt((f-1)/2)&255;if((f+1)/2<e.length){r=e.charCodeAt((f+1)/2)>>8;i=e.charCodeAt((f+1)/2)&255}else r=i=NaN}f+=3;s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+LZString._keyStr.charAt(s)+LZString._keyStr.charAt(o)+LZString._keyStr.charAt(u)+LZString._keyStr.charAt(a)}return t},decompressFromBase64:function(e){if(e==null)return\"\";var t=\"\",n=0,r,i,s,o,u,a,f,l,c=0,h=LZString._f;e=e.replace(/[^A-Za-z0-9\\+\\/\\=]/g,\"\");while(c<e.length){u=LZString._keyStr.indexOf(e.charAt(c++));a=LZString._keyStr.indexOf(e.charAt(c++));f=LZString._keyStr.indexOf(e.charAt(c++));l=LZString._keyStr.indexOf(e.charAt(c++));i=u<<2|a>>4;s=(a&15)<<4|f>>2;o=(f&3)<<6|l;if(n%2==0){r=i<<8;if(f!=64){t+=h(r|s)}if(l!=64){r=o<<8}}else{t=t+h(r|i);if(f!=64){r=s<<8}if(l!=64){t+=h(r|o)}}n+=3}return LZString.decompress(t)},compressToUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i,s=0,o=LZString._f;e=LZString.compress(e);for(n=0;n<e.length;n++){r=e.charCodeAt(n);switch(s++){case 0:t+=o((r>>1)+32);i=(r&1)<<14;break;case 1:t+=o(i+(r>>2)+32);i=(r&3)<<13;break;case 2:t+=o(i+(r>>3)+32);i=(r&7)<<12;break;case 3:t+=o(i+(r>>4)+32);i=(r&15)<<11;break;case 4:t+=o(i+(r>>5)+32);i=(r&31)<<10;break;case 5:t+=o(i+(r>>6)+32);i=(r&63)<<9;break;case 6:t+=o(i+(r>>7)+32);i=(r&127)<<8;break;case 7:t+=o(i+(r>>8)+32);i=(r&255)<<7;break;case 8:t+=o(i+(r>>9)+32);i=(r&511)<<6;break;case 9:t+=o(i+(r>>10)+32);i=(r&1023)<<5;break;case 10:t+=o(i+(r>>11)+32);i=(r&2047)<<4;break;case 11:t+=o(i+(r>>12)+32);i=(r&4095)<<3;break;case 12:t+=o(i+(r>>13)+32);i=(r&8191)<<2;break;case 13:t+=o(i+(r>>14)+32);i=(r&16383)<<1;break;case 14:t+=o(i+(r>>15)+32,(r&32767)+32);s=0;break}}return t+o(i+32)},decompressFromUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i=0,s=0,o=LZString._f;while(s<e.length){r=e.charCodeAt(s)-32;switch(i++){case 0:n=r<<1;break;case 1:t+=o(n|r>>14);n=(r&16383)<<2;break;case 2:t+=o(n|r>>13);n=(r&8191)<<3;break;case 3:t+=o(n|r>>12);n=(r&4095)<<4;break;case 4:t+=o(n|r>>11);n=(r&2047)<<5;break;case 5:t+=o(n|r>>10);n=(r&1023)<<6;break;case 6:t+=o(n|r>>9);n=(r&511)<<7;break;case 7:t+=o(n|r>>8);n=(r&255)<<8;break;case 8:t+=o(n|r>>7);n=(r&127)<<9;break;case 9:t+=o(n|r>>6);n=(r&63)<<10;break;case 10:t+=o(n|r>>5);n=(r&31)<<11;break;case 11:t+=o(n|r>>4);n=(r&15)<<12;break;case 12:t+=o(n|r>>3);n=(r&7)<<13;break;case 13:t+=o(n|r>>2);n=(r&3)<<14;break;case 14:t+=o(n|r>>1);n=(r&1)<<15;break;case 15:t+=o(n|r);i=0;break}s++}return LZString.decompress(t)},compress:function(e){if(e==null)return\"\";var t,n,r={},i={},s=\"\",o=\"\",u=\"\",a=2,f=3,l=2,c=\"\",h=0,p=0,d,v=LZString._f;for(d=0;d<e.length;d+=1){s=e.charAt(d);if(!Object.prototype.hasOwnProperty.call(r,s)){r[s]=f++;i[s]=true}o=u+s;if(Object.prototype.hasOwnProperty.call(r,o)){u=o}else{if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}r[o]=f++;u=String(s)}}if(u!==\"\"){if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}}n=2;for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}while(true){h=h<<1;if(p==15){c+=v(h);break}else p++}return c},decompress:function(e){if(e==null)return\"\";if(e==\"\")return null;var t=[],n,r=4,i=4,s=3,o=\"\",u=\"\",a,f,l,c,h,p,d,v=LZString._f,m={string:e,val:e.charCodeAt(0),position:32768,index:1};for(a=0;a<3;a+=1){t[a]=a}l=0;h=Math.pow(2,2);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(n=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 2:return\"\"}t[3]=d;f=u=d;while(true){if(m.index>m.string.length){return\"\"}l=0;h=Math.pow(2,s);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(d=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 2:return u}if(r==0){r=Math.pow(2,s);s++}if(t[d]){o=t[d]}else{if(d===i){o=f+f.charAt(0)}else{return null}}u+=o;t[i++]=f+o.charAt(0);r--;f=o;if(r==0){r=Math.pow(2,s);s++}}}};if(typeof module!==\"undefined\"&&module!=null){module.exports=LZString}\n/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */\nvar saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||function(e){\"use strict\";var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=e.URL||e.webkitURL||e,i=t.createElementNS(\"http://www.w3.org/1999/xhtml\",\"a\"),s=\"download\"in i,o=function(n){var r=t.createEvent(\"MouseEvents\");r.initMouseEvent(\"click\",true,false,e,0,0,0,0,0,false,false,false,false,0,null);n.dispatchEvent(r)},u=e.webkitRequestFileSystem,a=e.requestFileSystem||u||e.mozRequestFileSystem,f=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},l=\"application/octet-stream\",c=0,h=[],p=function(){var e=h.length;while(e--){var t=h[e];if(typeof t===\"string\"){r.revokeObjectURL(t)}else{t.remove()}}h.length=0},d=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var i=e[\"on\"+t[r]];if(typeof i===\"function\"){try{i.call(e,n||e)}catch(s){f(s)}}}},v=function(t,r){var f=this,p=t.type,v=false,m,g,y=function(){var e=n().createObjectURL(t);h.push(e);return e},b=function(){d(f,\"writestart progress write writeend\".split(\" \"))},w=function(){if(v||!m){m=y(t)}if(g){g.location.href=m}else{window.open(m,\"_blank\")}f.readyState=f.DONE;b()},E=function(e){return function(){if(f.readyState!==f.DONE){return e.apply(this,arguments)}}},S={create:true,exclusive:false},x;f.readyState=f.INIT;if(!r){r=\"download\"}if(s){m=y(t);i.href=m;i.download=r;o(i);f.readyState=f.DONE;b();return}if(e.chrome&&p&&p!==l){x=t.slice||t.webkitSlice;t=x.call(t,0,t.size,l);v=true}if(u&&r!==\"download\"){r+=\".download\"}if(p===l||u){g=e}if(!a){w();return}c+=t.size;a(e.TEMPORARY,c,E(function(e){e.root.getDirectory(\"saved\",S,E(function(e){var n=function(){e.getFile(r,S,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){g.location.href=e.toURL();h.push(e);f.readyState=f.DONE;d(f,\"writeend\",t)};n.onerror=function(){var e=n.error;if(e.code!==e.ABORT_ERR){w()}};\"writestart progress write abort\".split(\" \").forEach(function(e){n[\"on\"+e]=f[\"on\"+e]});n.write(t);f.abort=function(){n.abort();f.readyState=f.DONE};f.readyState=f.WRITING}),w)}),w)};e.getFile(r,{create:false},E(function(e){e.remove();n()}),E(function(e){if(e.code===e.NOT_FOUND_ERR){n()}else{w()}}))}),w)}),w)},m=v.prototype,g=function(e,t){return new v(e,t)};m.abort=function(){var e=this;e.readyState=e.DONE;d(e,\"abort\")};m.readyState=m.INIT=0;m.WRITING=1;m.DONE=2;m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null;e.addEventListener(\"unload\",p,false);return g}(self)\n/*! seedrandom.js v2.3.3 | (c) 2013 David Bau, all rights reserved. | Licensed under a BSD-style license */\n!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=r&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=r&f+1],c=c*d+h[r&(h[f]=h[g=r&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&\"object\"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:\"string\"==e?a:a+\"\\0\"}function l(a,b){for(var c,d=a+\"\",e=0;e<d.length;)b[r&e]=r&(c^=19*b[r&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return a.crypto.getRandomValues(c=new Uint8Array(d)),n(c)}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o=c.pow(d,e),p=c.pow(2,f),q=2*p,r=d-1,s=c[\"seed\"+i]=function(a,f,g){var h=[],r=l(k(f?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=o,c=0;p>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=q;)a/=2,b/=2,c>>>=1;return(a+c)/b},r,this==c)};l(c[i](),b),g&&g.exports?g.exports=s:h&&h.amd&&h(function(){return s})}(this,[],Math,256,6,52,\"object\"==typeof module&&module,\"function\"==typeof define&&define,\"random\");\n/*! console_hack.js | (c) 2015 Thomas Michael Edwards | Licensed under SugarCube's Simple BSD license */\n!function(){for(var methods=[\"assert\",\"clear\",\"count\",\"debug\",\"dir\",\"dirxml\",\"error\",\"exception\",\"group\",\"groupCollapsed\",\"groupEnd\",\"info\",\"log\",\"markTimeline\",\"profile\",\"profileEnd\",\"table\",\"time\",\"timeEnd\",\"timeline\",\"timelineEnd\",\"timeStamp\",\"trace\",\"warn\"],length=methods.length,noop=function(){},console=window.console=window.console||{};length--;){var method=methods[length];console[method]||(console[method]=noop)}}();\n}else{document.documentElement.setAttribute(\"data-init\", \"lacking\");}\n</script>\n<style id=\"style-normalize\" type=\"text/css\">/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS and IE text size adjust after device orientation change,\n *    without disabling user zoom.\n */\n\nhtml {\n  font-family: sans-serif; /* 1 */\n  -ms-text-size-adjust: 100%; /* 2 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n  margin: 0;\n}\n\n/* HTML5 display definitions\n   ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; /* 1 */\n  vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n/* Links\n   ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n  background-color: transparent;\n}\n\n/**\n * Improve readability of focused elements when they are also in an\n * active/hover state.\n */\n\na:active,\na:hover {\n  outline: 0;\n}\n\n/* Text-level semantics\n   ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n  font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n  font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n/* Embedded content\n   ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n  border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n/* Grouping content\n   ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n  margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n  overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n/* Forms\n   ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n *    Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; /* 1 */\n  font: inherit; /* 2 */\n  margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n  overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n *    and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n *    `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; /* 2 */\n  cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n  line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n */\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  box-sizing: content-box; /* 2 */\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n  border: 0; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n  overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n  font-weight: bold;\n}\n\n/* Tables\n   ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n</style>\n<style id=\"style-init-screen\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/init-screen.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@-webkit-keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@-o-keyframes init-loading-spin {\n\t0%   { -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n#init-screen {\n\tdisplay: none;\n\tz-index: 500000;\n\tposition: fixed;\n\ttop: 0px;\n\tleft: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tfont: 28px/1 Helmet, Freesans, sans-serif;\n\tfont-weight: bold;\n\tcolor: #eee;\n\tbackground-color: #111;\n\ttext-align: center;\n}\n#init-screen > div {\n\tdisplay: none;\n\tposition: relative;\n\tmargin: 0 auto;\n\tmax-width: 1136px;\n\ttop: 25%;\n}\nhtml[data-init=\"no-js\"] #init-screen, html[data-init=\"lacking\"] #init-screen, html[data-init=\"loading\"] #init-screen {\n\tdisplay: block;\n}\nhtml[data-init=\"no-js\"] #init-no-js, html[data-init=\"lacking\"] #init-lacking {\n\tdisplay: block;\n\tpadding: 0 1em;\n}\nhtml[data-init=\"no-js\"] #init-no-js {\n\tcolor: red;\n}\nhtml[data-init=\"loading\"] #init-loading {\n\tdisplay: block;\n\tborder: 24px solid transparent;\n\tborder-radius: 50%;\n\tborder-top-color: #7f7f7f;\n\tborder-bottom-color: #7f7f7f;\n\twidth: 100px;\n\theight: 100px;\n\t-webkit-animation: init-loading-spin 2s linear infinite;\n\t     -o-animation: init-loading-spin 2s linear infinite;\n\t        animation: init-loading-spin 2s linear infinite;\n}\nhtml[data-init=\"loading\"] #init-loading > div {\n\ttext-indent: 9999em;\n\toverflow: hidden;\n\twhite-space: nowrap;\n}\nhtml[data-init=\"loading\"] #ui-bar, html[data-init=\"loading\"] #passages {\n\tdisplay: none;\n}\n</style>\n<style id=\"style-font\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/font.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@font-face {\n\t/*\n\t\ttme-fa-icons (2020-03-27)\n\n\t\t`tme-fa-icons` is a subset of the Font Awesome font v4.7.0 by Dave Gandy (http://fontawesome.com)\n\t\tand is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL).\n\t*/\n\tfont-family: \"tme-fa-icons\";\n\tsrc: url('data:application/octet-stream;base64,d09GRgABAAAAADLAAA8AAAAAWHgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+IEkIY21hcAAAAdgAAAG8AAAF3rob9jFjdnQgAAADlAAAABMAAAAgBtX/BGZwZ20AAAOoAAAFkAAAC3CKkZBZZ2FzcAAACTgAAAAIAAAACAAAABBnbHlmAAAJQAAAI6gAADv+gJOpzGhlYWQAACzoAAAAMwAAADYY1IZaaGhlYQAALRwAAAAgAAAAJAfCBClobXR4AAAtPAAAAJEAAAFMBfb/0WxvY2EAAC3QAAAAqAAAAKhjiHI5bWF4cAAALngAAAAgAAAAIAFjDA9uYW1lAAAumAAAAY0AAAL94+zEpHBvc3QAADAoAAACHAAAA11cG/YjcHJlcAAAMkQAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZNZgnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF4EMgf9z2KIYg5imAYUZgTJAQDSIQumAHic7dTVbltRAETR7dpNCikzQ8rMzMxt/M39mHnsU1/TfZz5jFpaV7pXJunMDLAZmOqaZjD5y4Tx+uPTyeL5lG2L5zN+L94zG8+ztr7ulXH1fra4bvK9M79xiWW2sNXPbWeFHexkF7vZw172sZ8DHOQQhznCUY5xnBOc5BSnOcNZVjnHeS5wkUtc5gpX/f3r3OAmt7jNHe5yj/s84CGPeMwTnvKM57zgJa94zRve8o73fOAjn/jMF77yje/84Ce/WGPun1zi/2tlXKbp3Xyc44bFyZanSWokJDXOOjXSk/LUSXn+pEwCKTNBaqQqZU5IjX+XMjukTBEp80TKZJEyY6RMGylzR8oEkjKLpEwlKfNJyqSSMrOkTC8pc0zKRJMy26RMOSnzTsrkk7IDpGwDKXtByoaQsiukbA0p+0PKJpGyU6RsFyl7RmosQcrukbKFpOwjKZtJyo6Ssq2k7C0pG0zKLpOy1aTsNymbTsrOk7L9pNwBUi4CKbeBlCtByr0g5XKQckNIuSak3BVSLgwpt4aUq0PK/SHlEpFyk0i5TqTcKVIuFim3i5QrRso9I+WykXLjXOYNzP8BuAPUwHicY2BAAxIQyBz0PwuEARJsA90AeJytVml300YUHXlJnIQsJQstamHExGmwRiZswYAJQbJjIF2crZWgixQ76b7xid/gX/Nk2nPoN35a7xsvJJC053Cak6N3583VzNtlElqS2AvrkZSbL8XU1iaN7DwJ6YZNy1F8KDt7IWWKyd8FURCtltq3HYdERCJQta6wRBD7HlmaZHzoUUbLtqRXTcotPekuW+NBvVXffho6yrE7oaRmM3RoPbIlVRhVokimPVLSpmWo+itJK7y/wsxXzVDCiE4iabwZxtBI3htntMpoNbbjKIpsstwoUiSa4UEUeZTVEufkigkMygfNkPLKpxHlw/yIrNijnFawS7bT/L4vead3OT+xX29RtuRAH8iO7ODsdCVfhFtbYdy0k+0oVBF213dCbNnsVP9mj/KaRgO3KzK90IxgqXyFECs/ocz+IVktnE/5kkejWrKRE0HrZU7sSz6B1uOIKXHNGFnQ3dEJEdT9kjMM9pg+Hvzx3imWCxMCeBzLekclnAgTKWFzNEnaMHJgJWWLKqn1rpg45XVaxFvCfu3a0ZfOaONQd2I8Ww8dWzlRyfFoUqeZTJ3aSc2jKQ2ilHQmeMyvAyg/oklebWM1iZVH0zhmxoREIgIt3EtTQSw7saQpBM2jGb25G6a5di1apMkD9dyj9/TmVri501PaDvSzRn9Wp2I62AvT6WnkL/Fp2uUiRen66Rl+TOJB1gIykS02w5SDB2/9DtLL15YchdcG2O7t8yuofdZE8KQB+xvQHk/VKQlMhZhViFZAYq1rWZbJ1awWqcjUd0OaVr6s0wSKchwXx76Mcf1fMzOWmBK+34nTsyMuPXPtSwjTHHybdT2a16nFcgFxZnlOp1mW7+s0x/IDneZZntfpCEtbp6MsP9RpgeVHOh1jeUELmnTfwZCLMOQCDpAwhKUDQ1hegiEsFQxhuQhDWBZhCMslGMLyYxjCchmGsLysZdXUU0nj2plYBmxCYGKOHrnMReVqKrlUQrtoVGpDnhJulVQUz6p/ZaBePPKGObAWSJfIml8xzpWPRuX41hUtbxo7V8Cx6m8fjvY58VLWi4U/Bf/V1lQlvWLNw5Or8BuGnmwnqjapeHRNl89VPbr+X1RUWAv0G0iFWCjKsmxwZyKEjzqdhmqglUPMbMw8tOt1y5qfw/03MUIWUP34NxQaC9yDTllJWe3grNXX27LcO4NyOBMsSTE38/pW+CIjs9J+kVnKno98HnAFjEpl2GoDrRW82ScxD5neJM8EcVtRNkja2M4EiQ0c84B5850EJmHqqg3kTuGGDfgFYW7BeSdconqjLIfuRezzKKT8W6fiRPaoaIzAs9kbYa/vQspvcQwkNPmlfgxUFaGpGDUV0DRSbqgGX8bZum1Cxg70Iyp2w7Ks4sPHFveVkm0ZhHykiNWjo5/WXqJOqtx+ZhSX752+BcEgNTF/e990cZDKu1rJMkdtA1O3GpVT15pD41WH6uZR9b3j7BM5a5puuiceel/TqtvBxVwssPZtDtJSJhfU9WGFDaLLxaVQ6mU0Se+4BxgWGNDvUIqN/6v62HyeK1WF0XEk307Ut9HnYAz8D9h/R/UD0Pdj6HINLs/3mhOfbvThbJmuohfrp+g3MGutuVm6BtzQdAPiIUetjrjKDXynBnF6pLkc6SHgY90V4gHAJoDF4BPdtYzmUwCj+Yw5PsDnzGHQZA6DLeYw2GbOGsAOcxjsMofBHnMYfMGcdYAvmcMgZA6DiDkMnjAnAHjKHAZfMYfB18xh8A1z7gN8yxwGMXMYJMxhsK/p1jDMLV7QXaC2QVWgA1NPWNzD4lBTZcj+jheG/b1BzP7BIKb+qOn2kPoTLwz1Z4OY+otBTP1V050h9TdeGOrvBjH1D4OY+ky/GMtlBr+MfJcKB5RdbD7n74n3D9vFQLkAAQAB//8AD3icrXsLcFzlleZ//v+++nb37dfte/VotVrdrW5JltuKpO6WJVluPyXbsmOEbGRjO4KVjWPZlsM4QIU4IYGlgCE2yxqKIQkJg2GreKQCTCZb1CZkiswjzOwOmSTATNVWTZLdDUwSMrVDsomD23vOf69assFAqgbk+773P4//nPOdc/5mwNjFl8WjosbaWblmxiOmIlTGYXzb11M7Z2ohAMbZCcZ5iG9prZl4whfwGju05xtttitUdwXYEUgkbQtWga5lC+XBaqJI285qpb8dVEc8Gnm5L5QM/f58yAlB399a7dD0mWAmdAqaMvBmKPKX9TdDwSjod9yhx03FAPcvI6Gk2lV33XoXUtKgL8C6Wa0WSzXbVjhg6JoqIPShCK0FOvOuE48K1V4B1VUQAVevugmP7Fz2CmTzW5/8+ZGP/+Kp7h/8oI4MuOZ7M5B9IvujH2Wf+PnCAjzn8ZK6AieMKRcvXnxWWSWCzGARlPcqtqs21WZzEBFgYIVDAYWzVJIrXBlH+hXGlWNM00GAJmYZ8sKBzTJFVZVppijqDFMVdTIWLa0o5JrdaHusPZGIG1IjFhQGK2mAZEe56kJnR1bTY7ZT7eivFGODBTdma3pHtlCNDVbwmgMHx/aO4R8ffeet5/ZCG6TfuV03IaSJU3oIzKsGO9+5PV+BwU5xqnOQx1aO8fW71yvD9fPn55/fA22PmsaFvfSgwZ8wzPiFvZ2DUMnzJ2hHVDMmHucPsSRrq7UQo4DMAV/AW7CANw/Zru3NIx20bBEpXwukArlxxOPReibaG63/SyQyiftzcBy3k1HuOHgjEgFHnkYfh4VodDJC4138Ff8hv5PlWbrWmm2O6grNEgGNCWGnbFtRm1Z04gzQPPWvkqMWF4eWU8DB267Df+gNe84bBvfRpfNI5Ny5yAmHDh5/PPLuByMlekDS9DsRR73nWHetwBShSO5PqCBQONO4E2wGlSvYZG6gMzeQ09SWFZC0tWION3oOVVXGTbGM+hrAzQj0O25yoN8R8YzzRsaZdzLwhpsGPEm783hAJ9+kq286eDX5pn/VydDjvk7i8EuchU21JJ3DNOqFzeABmywPcNXxhu/wB+3whrqwVw7Cn1j8vPfdDH6O7PR3fDV/Cb85wua/waSit309grZJHHOhHEQ9CA7zTGVCUb0ZvXzc1lrRe5Af+4An0aSL9op0IU9y6vRFtAYGKy7KJGFbQrdg2dXqGAwWilld0zUy9xLqd6A/zeFuy7gzYFmBOwOhZ6NNhZakm8YTI7S1pyM1mM032V26qevXGlzZ8+TKvROlB/BBkO9ACDalB7OZuBnuC5tRcAItpalENNOfhajVH1A2a1HjbHZ4N4r54oWLz4qPoe4jrMo2sonapm5QRQBQ3XycKVxwRRxjQuVCnWc6mrzOZ5FvBiraOmia5FubYRpok8n0ioRTKOQMNbWiszxYWAFZrQ1sB+dDJTFYghzyiBN3oB857ndIAha4Se/+YGUtjAkXXUO2xPE2eoe3TWPeMOXmzNi+j9w1HghvVbSAmu4c6nFac6MgbzXFU2baDr1241+98TfHtU/9t7df+MzU4msmfPYj06Wbw8Gqohda0/FkSyiyvtPGG/FsMKq1pLqmPvndkye/+y+08eYIHEJZpFmJra2Nop/VWuKcCwPvgRhnGlOFps6iIdAEmFVAKh53jblZyBU6nAFdbfX03tGwioZ9oGnEyDSWTQCyFzhoR+s/jdjgWLmc3L2K21zEmXCsM5aDm4g9Ny5v0PZVb+dY9Rcd/sl5eTiPj0nTaeiU+BhhW2qbuzJcUy2Nc2gCrjBkxWCqZiArGgOuwSzjAlDb5L91XfpvfYbpij6ZRIUWYoVcLqC2+UqNLddsMrekY3GZztVl2uQjpl4xjedIM+Uybc/r5viShuHgcgXuwQtpuoMHz8utCe/s8S+Y/7xMaYu2DX1yDrez3lp3cyyKdop+HLcLi878Uh9SKOSlj73cOIUFRVKVKHhaSS9JP452RCcYoJ3w85bz8Ckp9VN4xeZj9atpH4FnPCV4OtiDNIVYCrWwsbauK81VxUlaQnA+riFBisoUsiU0LbQpASCmmRAwg1MNJoH19uQ6WpriUV1jIQjphA5QjosiTfiy1tF+kPJKMTFYpAu6lkx40odnTv7VjYtC/QtTJyGH9HmMnKamGrcaqmaagRsMUwR9WeLmwsQREvIRevSvYa8uVFXo9Sc0w5A8/Rp5+jcxhXLuYAOIx/pbY+ggFp0FV8lNoLNAEQvyDkwBZR9T1ZC6xR0qFgsybix3DJcR7iIGwCsCHHQM0nYKVSBXQZPnqG7KDZiqrn8K524gpN9ghAx42k4GsvF3nohnA0kbnglkC9mrl7h4Db2TommKcVE1QOPRd97K5WJxsKO5nIjHbHtx/og3kK8iWys1he/EkKlqCbFOB6iKQp5Q5Yp6DB0AsjmPvHEFSGsoFraPaVpI2zLc2lmudErjJ/eeQwbKPoRBZulcwhwXYc6i1SdiEpiCF+KrFOIrAzKaHdhQ79tw4MAGuJv4rt8sQQu80jloGnnDfNVJBa+vn1WjSg1d8NHrg44FbTgdJ5+T77yy/gDI5wY7633yTbI7+CdyMDyEL2paDd0BvZjyfIYXH2/H+aqjDfXUisgnk3rFYMePoyZBegWanQpM5hKdlUSU1JnoQH2iG1d90IYADi0JPV2/oxMEeB7apm+aBngFw/ObMjzHzv7dgzyOh48fHZnmO9c8Wv+2RAGwHiP20UNnzx46mvYxyaMixDpZX22lQqQgRDqG4kYcMo92cpypAOo0Ti+iSYXJRGc5l8wvQhPfqpE0SYa7nDwkeQyh26OOlbecqVumoCwJ8+mDm+9/5T4eOyOt+4wk8WjavYTIG+7nD5Li2cWv8zGkMYoz51q2u3Y1JQSwfcumSn9JUxW0CZIWQ5isqKCo8zoGFPybx/lyHD2w4AaCBw7Ap1HIxAaHyZlr1tXG1owMt7h5Ox5QmxF4Iv6vDhaQoTFAYADuYIlnLa7b7RwtiOBDtVK1dbyCMXXxn5azOIIKfLFKaQP9K3HKIMYEQosvhkIWH23TQ9wIpCq9M4WxycnJsQIUYrEJ/bPGuOZohfHVzdmMaAmHm418c7DU3xdoyYPebFktPJtpHu7fefjw4R0VHiNTa06ZUTPe09a1sdTUVNrYtbo3nth11VW7tBa1d/U1a1t71rdG2u1IJNkWDYdbUs0pnnFT+OloWzISsdsjqVpvy9prqrNjed41POfPx2+J7TIXaWP5Wgf5bvToBEvpppQYOXPOJoda4gTQE6TsNK8sAvUSSHBFygfnfM9wL+8aK/Bddv0tZ8SufyKZ7ml7s20iCWdsPpPu4YVaXuur/2M6WX8riReTE21vtPUAnn4iyTxdf0vJ+/QM0pwsUL4D5BmUBQEeXpaU4W6RtExzN1In7eT9qHPpIoUdqAzSrdyHIPo1JNRps1uioCWJyIm2U/JG8oO4aZMXozF8b4RupRd5JJv7Nn8ZeUyxjlrakhJHcwO2gHMUDgGLR0MmS0GrIoMRWos0K81LUnPZYgmkw6sM8HtCoQTaT9y0Wu3f/MZuCYfijhMPhUVQNdL2hY8kMroS/8UvEqqeSfC/xzPV07k3vsqyrL+2ykYnhJjEz45OLIdbu4ncrbrWhImzltU7oqqaXAEdMWiHxPsSdvuF70K6/xf6FejjL13YBvFVP+UXrkwnyelZjBcltoLypQIoEmsoiDUw4C2QFS/I/J6yx5ybWOOqaMad0kJlKo+ElAdLqvRTMqOsgucuM64j3hgHU1H1mIkQ084Oje3eXT1lZwL1nwaD0BZMNfFTcHpv+sf7v6LEo4oZMlRbFNqH9tb60nEN0UkQ0mYawZNpR878eFuD1lWsl/Jb9J5iMbPlDKnkh3raBmS66U1M9Jm5rKUmMel1dKLOcxrVSgf5dJqU4o0g0pJyTlV37x4bytpCATOG8VUT4+m9cPoURiakE34ZMes/wbB0Roun+2p7h9oLSlwzQqZq2eIr+xe2/Rhp5QF8hHn5Hh/FfM9ibs32UrNG+l22ZbrnJ8N+fERAcD6YCp6nuPc2Aq/vWXiMfzKaye/BDfzTLPwe33MT8ns4P/y0uiLBdyZw3qRvNvGZegjF6H+escXvreOnkD7zm7IkADTbJEkU4sdw1jmwzpTj42smHLB82ijenjfx9YsXkccR+B5+I1azGtQk+4maTpnLlsBL75E7O1IPea+mzfPmUwQt00FJoG+nPxN/zruZzZprTljyx2GpXiHdYWBZtcINeB+2xVP16/GT9euDwf00TbqgK5gK7QvCmfp/wPn15WDa3BcM1l/Hy8F9wZQ31nf4Q2IjjrXyG+BnzXnMmqXiTuB5CLa01gINjvb8mesSTwHuSAEtzm8anp+uvwbdprkfhUs0wCNIxH6TP1l/vf6aPDThK0TXI5I+tjj+SX/8wIcaPxWX4y+CqsCiEIiAIzhsKrgfh+6qv+4L4RETPl6/zqMKukki9AA96OseZb3Zk7UKVKxZKt65tpR1J9p1sTGgP5Z4ah+KEzl73R/xEfr+I8H5fchlN/Jr0n0c3fSGkrz+vbhd5m2ZWltThDeKUaKh3WTZXWauS+WomBdMkjGvOCRuT9YfcIZxk0x24/5cT3q8redxe8TpTsLn03b9LIaFo/I0eQ7uxtjQm6rffI4elnTcJfbwX/qRFyG+Vw+SaJdyFIpugk0WXCcnaZFo1323viNAWHZPvk1+ugfjTf2sbcPR5LDT442bhwPj6Z5z9qi9wr8B85Ji51yXb3tISxlpSUtaZEYnFX8ZbHI6C8vk4ll2h5/YdRQbM0CUbSmXHsd5rH5zWz7fBnc/5iA1NLANwyQZ2+5JjiYfQ4mle+BxJA1prT9g+770dv4zTz9RnTPp+JlY4DJKoroOucmyT4fnsGIeFC3H9EtmB+qnGyP6413tPvs4BhIwipskDKakeIg+KKTgbl92JBkkHn1J3c8t+9ia2vBKhMFkGBJtEjrGDIzKE1SeAgTJiDAVRYpKmSGwMlksJor5gUWgjKlXYS0gurS4neaYflXJnaWBUjDKNAVhkWoF1WhHz7uJ2PapE8NHJkulySPD62/qVmLapMq10a997JqvnphQarc8dO3UQ2smYr38pfOWszK6fTs+eBKfHy4j8t2uWNrWnbDx5CNfe+TkxrHVE/EEW4ynxM9H2FhtpAeE2tnGFUH1J4xXChxD5hACLNXbLgOA3WU353rI37Y44SipbwdxwED/mEC0rOmuIzmlGkw7SBzNxZ6NN3519+zXRhV1Uosp3TdtGD68s4eXJo8uzHVtjyXc85gC9MYmRh+euuaRk+vhAG43Tm3RLGW7Clp52Oesq3N7dKVjnW9KxCdWjyFvi/nUs+Ja5CnPxtn+2t4NnVwLrELw76JqDMz1MacMGJoR0I5RVsA1lR/DLEdonLJLQWnOMcSamKlrszLnWWZ3mzcVOjsrnYWynTfVNmQ6aQFxrWtL9SQtgjqsojbxf5lp+qquSF1rVGyk02qZZEEKRkPdlv9ff3LVQ6MTFMas8xSft3fNVbd+vqg1KaF5w7RwBsirUye24UVXDS3oIcj/nz+56mF6qQlUAQ++gGoNytcxFG7P98DWMXMoHIL/6l/Z7p1riv8ko2oiyiru129WsSrmUjO1XdtWc0Pr7miOoWOV9YUQ07WQPmsCisWYDge5puAUAI3NIk6EQACmaQ+BGRaAwOSemV1TH90+vnlDrZBNFOi/nEUlLL96lYx5VZLqB5zDQLFQzGm6KuUX89L6YqxRuaPsCwXYTlJE1EVJt9ycWTo8bereoW7Wv38ewfOzmgI/N42Kn53LatjTxUCv85zbEyg+Y5hTcDddq99M2ysc8/51lABfjZ++8KvSxvUlnpCj7U+mIG3vR8yhXSbXEbaB3cDmatdds4lrhi9Z8hskNo2hkI+RQHWN6fMIUwKGFZiNhDmCNq6BoR1gejCoTzNdD86woB6cPDh33YFr91w99dHJLePr1tp525NylKZkzJtthLplN+ADzhOxjpiN1trRPwb/vhKfqIcMg8Mr3DDqd38o4cM36y9KWa+Tsn7v4/ocj134Vcg2TZsf/ABFKH4tYwrtObZYYxoF3QiQC+PjATwUho5+W8MkQlOOqUDxnlMNjZpsfB8zjJCxZe2afKeTjXeuboqT2XcOlsACB6XROFhWch7AeNy/VsZfx4fO5Aj8EjTVRPjLdtrmTS1NX7Azce6kmjZnnHf+1iuBiG0duzsmQTiZPzfjEoTGAqZ7xqt/nmmai8gXOWb0/sG9z8vyyPNOZjKDf9DlRgmQR93keVlHOU/9RTkfH8W8ieTQzWpsc21DGRM0Xw4soAUWDEDPtMB0oS9I5qeXC0PhMySPyTWjuYFctn9JEgWLp6FSXdz71TeSgzuQBmnXVHPXGqWhIhXkF+H2lQXxVqiSO5OthN9CQQSazmBOhdycwSgoZRJvQ28Yz8SVltDiwd3PUxMLN9De1dWehinH5783RvA9xhpyoHgnWAfGvE3sqtqOFT25rGIoMB4GBa0NAaepg2KYyiwVijQqFKGtopEeUFFYgQCboj0jZ8cCk7U1Q2W3MBBLjMRi0SCKxO0od6gD6Muon6Q35JGLDZS9DEpfLNZTA4aqrmrD0rwHCAO8Cs/Ur4a3J0Lql9WU4RfBJiYySfg+cviqaczLvipt59LuhbjXflPdSuRLUUd/9VV422jRv6SF/NbehYrcQ1p2AZ6jd80L5+kSRwE3WV+OVGTs9GvnU6yLDbJsrZ2aE+iijskqPtuHgTAktgz0r+xtaUaslZRRH6FKlRJrBwO9bK+lef8YXipg7j8GSJjM+GRPmSApRv6n//T4VrH3qqbRaNxoqoxSNMfwD6MV18yPulftrX+xZ7gXeka7vMCPgf2aQ8+N4bPuaKz7lg2LIGjjTT3x4T4jvubPYKL+cFtPTxscwm0DA+yVNaMjbFNt/cE9k+sUpoxgas8Gu1qjhG7GqUu+oAFel8B2gTDcgt+54If2XXv1VVsmVvRkM4m4TnnrYCGLtt5foc4F2rWO/NrIbxENvBHUy0WJBYqEg8hbyklAxoATvupfHMCZ3wAHaASEAFz/Y7pUPh+ZumWK7z65G1KGftgMJro0NbIzrOvbm1sCuhL9tBGKtrof1aLaZkdRjS4zYhzSDTDVw4bldnrPGtubWgKGiH0adR1JuR9VI/qErSgB72HMk0emp2+anr6F7kfTydZ+zdKSO0EdDRuTqaip3xAIjapaLa1aWqg/kmqNQEiXzza3ZFbqId3euezR4Iiqbkj5j7ZEIeTX7X4n9vLvLmKL2mAXILVUp0WPoyLCVJVGQ+/yNlGhjP8PNLq4y9vNy9rPrn+eW36OaPmdX0nTF7EIpu9XPlvW1XMgOiHbSHIL1ji1nsbpBdbAyXtlTRwjSHuCqyJvkjfAVEiMe317oeLEURlNKa/CI7tfVLxLp5qb4tFwUFVYJ3R6/STHqy1Xi1R9RLvvd6lSPIba14rZoo6zwa3w/7L18OGzRwC+N7B52+HD2zYPfA8OP3iIH9kyjkd4Fdwj9x85skUPzfXhQd9cSN96mB+97yjgoYUXvZ7kxYu/UW7mL7Eo+rwKK9byTKWsSWWz6N8VBaZxB5ShgDJZHerscm0J6L1at2fNGPH4CqQNZ+gAGTeaPZVNBB52IJ7nt/uI3N/B0707Do+8vmEH37rpdTLX8eEDd47Xr564Y3aIj+67azM8Q4dwYHjpHbJoOu1/8OkH++lk4s59Y2Lo+tsevG1ukA/N3uHb9f9TbkFebNRET61I/GEuOEtdCkxKcdfISpPptmSn01kdVNF4Y4NjHF1rWnh0cxWZKImsJdKcKPPIkZR5RIoYkcJ7dh665dDOHqV/4jgc2ILXkYz77zgwypGsH1zKst/L+ir6nJVsPdtR2zaC4aQTZC9CR4p0jigDQ6gmexJcUeeXdbS0xY7WslzDdpPF8uryAPX0L+tqoVNFD5QrFi7rbKGXwYnkJGCpwra4XMZe1th6JZcOCL0VgV445HepqL+l5jVdKMHP1VeH89a/WtYaK2/9Z/g4noyFYduzjf6WpSS0FIKDRovrC4aaQ9ZArQ9b1r/K58P0Yhi/4Msl4WPhFbUuzCTBb1OitsjmCVsAn+Qs3eokTJ1FeEQlI1nGXNlfnbS8tcc/sYz4wU8vsbd+lr/UII5u7mncOYBJ+WKc+5jsETe/L03AXCfmN4IlTVnCOYiVSxyWr5h45up7d/Lpu568c7ey4zRcu6yjzk9P3Xvu3im5qb9yaf98ab2AwZIsy0Zrq6nXxkG22zi121ScGiC71IoiZwcZqlAmzUCmvaU5GgkkzaQfoNCjlKimWnxvGhtR5b4r0roYFX59BZLFsvVB5NMRIVAGQ65bJoBsRuaFbLJM/0n3nZCLlZaWYKj+efWy88WezKv+yiG5dKnN23mX2uUJbi5bY+Rc4XgxBrHXGnlYb637gwLPEuXVBmD1ViiIyzgRfl5f9NddvfoB9NDxw0clWD9KV0FbdhMMjzfc+H7uJXEG8fo6Ns4O1q5fU+KCCq16PhXG3JuJcczFw6GwEaLSBXkQwNwF4DgLs1AgHDqA2FToAXEgCDpj+jTudDaDgUmnGsaG9bWx1UOVctJGzBlDf2HJWgZyh1HHkeV53RK5WC6Gc8fr45AzKSByxwlWHShTsxydSgfVLhALUZsXOiS8w0TlVOdJ006bC4ZayA43j7cN9WCqeCgYDTvGJzKnKG0Jn7ou6KSC18Frs8FUk2Jch1frv65/kWLdMMbf2fWfDKac4HFdaYpb8HY9ZDXZhnEylEgHP7t2L6YMcO46M22b111HA113zoFBDJQUpy/WLx6B36K+MxQdmlHBKerm0BIJKqUu68GgoLY0VSpexdChJiDC1WpC9qwK1cQYpS7U0BW0JAzeMrX6P+hRI2Dy4z/hqqmb4gS3jG8GLT77P1Ue5E4wfOFTFoioAS8NYVYZhv9umJapaPV6hct6wHdkHiowcqVYnvUivt5Qa+3vK/X2dBXy2Uy6pclBE4pR4XkwxWHTtq93vH+tv0l2U6pFvbNRePWrv51uBFbBWmgHbw/upXv+0LnRc1AxL/RjLrVgmvx/yP0Fq1KJxarV2A+PHct2HDvWwbvxJIYX60/THfzHrcdGH7shQm/iC2l6E/fXRumtaPU/ybeyx+p34UkVL0LJv9PAUHOomzL1w8q9uZaYoV3SIOoujHBKKii60eKMxhJHXUMgTQW0InVqqErq22AbYN6BHOMkFCMtlpWLDDc/0NM23tYLZ1uGMX5ZrWfPtkQj+chQ61lZiH+gZSiai0Sbz4JhDbeswXd2PSVr8E/twqtr8KXdu690Q2KpI6jHMMuhBjdRdbG9iQslEae6YVhDV7gBdOiHgK4iKtSERkv4yOpAP8ZMpimmNqtSRuUFG50FDD2AeaYRNrasGS0P2LTwuGDncpRJNtZ5Fpev85SVhMY6T3ewBJrtjAFIs8XZi95eSXOXLJkW75wKaXmE7afkst1TsjBDJxP3v3I//kG6Z9R+ce7WnfcfrvHRo6fPnT46CpteTMJZ7yXKMb2XTlHieMpspgUYLz+k3UsJV/LFTWNH7vvT08eHlfWHHtx+69yLSbZMRhHWhDn2SG0oYCC3qE/B0qAKuZqPg+b5XkErlwWmYkKlMkNY2eLmUAqF3OJinmKjrPIhuK1/W/IJ6z8kh5K1D2aK+/UTWqOzkz1fC4xVOgKKKqgjZqKV9nhTGGOgUGnZjiK4Mq8jJFKOMwJ0UxSLdlO/Ymvrtq8H3/UG+nBNcG0e7Vh79xt/yOf37KklDMPYaeyc3LZ1y8hwT671KoP8BApooL9agOqYUm2FATeRptTcdeQGxexmC3pWyw2u5ZSr4l9xsLCKW+DaaeqxVskXZql+XdTggY9NDrcHk331MoTzqZSj3fGlCe3GxJQT6IsGjeBkQOGQO53v+VKSb9E1EVMQ5vKs2/R7axiimWAmiVCh4/MZ1eYrecvvMWx9oa71KppmNkVhBs6G6m+veHkw8amOFi0QFY4pTI7RrikRxSd1jgBaCeytDEHmYSsUN/HTEEyqQVQ6ppY4937ER/nPmMXaWK6W8TvQy1cj+s28wcIlS70LHqQsSoS5rB18abP70ubwv0VMqrRhJALnb2SJXZ76vg7pAPc9++oF2+s8XzLyZUMt/3ajhy35encPGy4n+lIy4Xvvou1JERMJFmTa8zqHldSwlaBa9k6VC63heDzM/3cYttfndDMiKlbIwCN7eV2F8FS61toUNZhCzHltUoKChwg/yah6WVnr8jIXJer+ultoizj14CWn/GfvvEU5uIjTdtnx8nw86K1TlzWF5QsXBhYXGlw+3rLk/5IB/G8+gzZuYUagPa8ByoUaWfiRIuA35I4CrJOMVariLtOCaKD+Ryq3gvWTwSDci1AAfVtQi7zzsmWE4F5Vq/+RPKDe8714vX5SVSVGuXjxa2K/iCyN42qyOkbLkXwt6FIjmt5sRsHC11VB2yANZQl1HL+GWjFC9Em4Vx7Q2oKTeB2+oKkeL3xEkBU015zWlri4VEBrpICIlwZ7Eso2mgSVKs8GLeJN5YsLnmn7Lh7/45KS6ncSg5I/Por8NcaWi7UW27Te2JeyTGPqPg3VS9j2CzJyeznjn1qaGXCrpsq8ro655n7+1zi3WwkBRkGuRl5e3eMyi6L0TvDJcnYwTnM14fWG5Xyh5nXVLya56BmNJRUi25oYoiGRJBGXdaO+Rb1Y9R861mlZYzodsf21K8/wZ1mC8I75Hr9viTsF+fsW5BzxTSO1IFQNz6DJvnC/lPv9L6Ab4KcvfJU6gC94y9NfMP31s7i5XY5RIn6LJqKRxjAKDuOVppiQA65ycUhaEvVeQya8dVuF6uCYKClVFADVGd5FyGt9RTWAAgg50ahiNFm2YkcCarHv3QTW/6Jna0TY0VA0FEpnMkbciKJ0RGSrrMN/XzzKfy7p3saOsq21cUl7FVQ2Dboqxg8D38S4hnxooL2bGRU1qurqAtP1Q1M7xzd3Sd4MKlx9aN7cxtWq/1OQAi25ohrK0s0ilVHwbgmKVNGWD1T/IMmcmd2u68h5MhPsLpW6Mf7ZEUPfceC+0zfhdXy9uTm5cQffujnZrMQFzmZdv+n0HyDOnl33Z4RjOcFoIL338N50IIpBxBEd9+2+7fV+vGGHQ5Y18NCTDw1EwkITYRu/JwZe9fPMI+IfxFUsyjbSqvKhAcwt168pdedsTBbTzZxTnRzRxQyV/if8uggP8y2VcpqWw7iyTmgJOy3GuEu5oK35P5xC0L6GZFag308hopD9/6r3KyoE7rKG5Tqi3D9z8s6TM/3+7mEeeCxiPDanxdWDjxmRxwKIeebmVFVePajGtTl5VaWLcGDdLdMVpbTvxD0n9pWUyvQtew1RfioQFOU/1vU/Lotg4KmyMEz9nnuM2OINTVu8ETPuuUeXPuNZUcFYEmd9rFRbYSxfVUjLiN9dFerqyvd2yKWFBEEB548Fup3m1PWnBQHUERnjyKYur0O1v1pBfvl30u2Hnj4EwydOw/CBOyd23vd4+YefpuUbvHb84elmO9HXD1P3Tq1f48YM5VZ17msH5/d1fPtmWQndeOwTd1EnZNeXbtwsoBRbcbJ29T3T0GbGDO+3J95aen6KxViWDVCHL8i5wsPAxPLfE1FDb55RmQnmCYgrEoijOyX+6KeDoE66Tt5ONjkSgheKBA9LqL6qpqchU0E7QaSYtJ3+irSVimrrmpLJU3ej0gsK6vTRXbdlEXtnb9u17Z9B+Un9m9Hg5rmoE93YF4zCPwZ31H9b/6f6b3cEgzvAgAIYO4IwfMe64Q03nOX3fXzD8Lo7brzrLtiCz85tCkajwb6N0b9LJD738MOfSxTs2x7mj3yGsEgjzzBYB9Xw2hGCYAZOzYWG2uR6nFlvxlJeEZfO732ziNsX84be4ffMGz7/gRmQb1dyTX8Q/dtArU8Hb8Wybmgod53NBlQOilxbg9mPCIstK3sTAzE7O5BMJuTKkvJgwfulBskXM0CBMymXLQrpkSoDMVmJ6aA2QjGG4SJkRk38g5sd61dtYGoQ4St+jHY+U23r4aVWOEjtseoMnDgvfwCEm2+hp6r/X91AK4zYkU3HMVEaxnR3qBfqPzn+/wHPnsOreJxjYGRgYABi1vIsoXh+m68M3MwvgCIMt5YoxkDp2P9f/2exVDAHAbkcDEwgUQAz1Qu/AHicY2BkYGAO+p/FwMBS9v/r/68sFQxAERQQDACh8QbyeJxdT0sVwzAMy49AkAxAkbT3UQiAITGAISmAYQiA9th4ttUs7Q568UeSlVidiwSkB3PUPg+ESd6ZD/+8v7HyrtzwghY89ZB6BcyrYqc6RZhw48YhaA1Wc/v1PQtdeXJ/HppUmFM597nvBVK7D+Z+E8/uQZLBgDyaz3Llvxx02S3c/Hv813JrznryZP4F+6lSfQAAAAAAAAAAUAC2ATABaAGyAfoCJAKwAzYDmgQSBFwExgUyBbQF/AZOBvwHRAe2B/YISgigCPIJGglCCWQJignACgAKQAp2CroLAAtGC4oL8gxcDPINng5iDuYPag/6EF4RIBGGEeQSShKYEyQTbhOyFAoUYhS+FVoVphYoFooXHBeGGFoYnhjGGOwZChlOGXgZrBneGhwaWhqgGtIbLhvyHHQc2B1QHZ4d/wABAAAAUwBtAAYAAAAAAAIAIAAwAHMAAAB2C3AAAAAAeJx1kN9q2zAUh39q0441YxcbjN3tXJWWEcc1lEGvWkLbXZeSu8JUV/6T2VKQlY48w95ifYa9zt6jd/vFESUUYiP5O5/O8ZEE4AP+QWH9nHKsWeEdozXv4A0uIu/Sf488IN9G3sMQPyLv0/+MfICv+BV5iI/4wz+owVtGM/yNrPBZfYm8g/fqW+Rd+svIA/Jd5D18UovI+/S/Ix9gqp4iD3GoniduvvR1WQU5mhxLlmap3C/FUdVWN6IXoXK+k3MpnA2maVySuza0ZlToUZ07292YctFov6k2eWp8VzsrJ0m6qa+NNV4H87Dq1j2WWQiFFN61chX7yNy7mclDUoUwPxuPN/tjAoc5lvCoUaJCgOCI9pjfDGk/BPfMEGaus2pYaDQ0GgtWVP1Kx/ico2BkaQ0zGnKCnHNL09KNuK451721rLqhLfmfht5vzdrmp7Sr3nUfC07YL92afU1r+wrd7/Dh5WwdHrmLjDawanUK3+9acPXqPML7Wq3NaHL6pL+1QHuGMd8t5/8PvPGO3QAAAHicbZLnlpswEIV9DRiwvZtseu89Ib333vvmBWRZYMVC0pHEEufpg8BO/kTncOfTaLgMOtPr97o17P1/baKPACEiDBAjQYohRhhjDevYhu3YwA7sxC7sxh7sxT7sxwEcxCEcxhEcxTEcxwmcxCmcxhmcxTmcxwVcxCVkuIwruIpruI4buIlbuI07uIt7uI8HeIhHeIwneIpneI4XeIlXeI03eIt3eI8P+IhP+Iwv+Ipv+I5N/OiF1hEz9JKxUrtFrDl1lWF9NR9QIikToRaVjUouKxvOmNBjLxnlhgo2DbnM1djLKrNGnGPScSUzItzGv93yPP2bSQSX84z9cqFQdJ56yZRmMhW8mLlJJSaBI0XYPDaZKDUviZmvr6DrNjJMi0WcK1MTM02mqpbZlJtEsNx5SI238jSodJtoS7qv+BpPw67IY9xU+dg5TXjROTWwdGrIOzWhT+uA0jolxqjaZrSOnCF2Nmq16651EYpMm1fakAul9SJQeR5QVYQlk1VkZ8SwoVNFIVjWnKQrlBGdMToftdoZjrs77DajqXKrS02YEFxbbtdWkG0x44JJVUS5aBqKSlJwmhDrmOF2Hv9Wqsy4TNqoKhfmSrrQKuNSL5nvPG6p0s0AkEWkSWVZMy1Kx3ljk03qLuZ14lTmB8gNGmByGrGfjLrhlhJV2f7SaIneNF1ypQNbybBUSgZswQaWEUNngeay1/sD4l/60HicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZWJ02MTAyaIEYm7mYGDkgLD4GMIvNaRfTAaA0J5DN7rSLwQHCZmZw2ajC2BEYscGhI2Ijc4rLRjUQbxdHAwMji0NHckgESEkkEGzmYWLk0drB+L91A0vvRiYGFwAMdiP0AAA=') format('woff');\n}\n</style>\n<style id=\"style-core\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\nhtml {\n\t/*\n\t\tWe define the base font size and line height here as they affect the layout\n\t\tof the base page elements (i.e. `#ui-bar`, `#ui-dialog`, and `#story`).\n\t*/\n\tfont: 16px/1 Helmet, Freesans, sans-serif;\n}\n\n/* Story data styling. */\n#store-area, tw-storydata {\n\tdisplay: none !important;\n\tz-index: 0;\n}\n\n/* Special no transition styling. */\n.no-transition {\n\t-webkit-transition: none !important;\n\t-o-transition: none !important;\n\ttransition: none !important;\n}\n\n\n/*\n\tFullscreen appearance styles.\n*/\n*:-webkit-full-screen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:-ms-fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\nbody::-ms-backdrop { /* Prevent IE 11 from hiding the `body` element's background. */\n\tbackground: none;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n*:focus {\n\toutline: thin dotted;\n}\n*:disabled {\n\tcursor: not-allowed !important;\n}\nbody {\n\tcolor: #eee;\n\tbackground-color: #111;\n\toverflow: auto;\n}\na {\n\tcursor: pointer;\n\tcolor: #68d;\n\ttext-decoration: none;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\na:hover {\n\tcolor: #8af;\n\ttext-decoration: underline;\n}\na.link-broken {\n\tcolor: #c22;\n}\na.link-broken:hover {\n\tcolor: #e44;\n}\na[disabled], span.link-disabled {\n\tcolor: #aaa;\n\tcursor: not-allowed !important;\n\t/*\n\t\tNOTE: Do not use `pointer-events` here as it disables\n\t\tthe display of a cursor in some browsers.\n\n\t\tpointer-events: none;\n\t*/\n\ttext-decoration: none;\n}\narea {\n\tcursor: pointer;\n}\nbutton {\n\tcursor: pointer;\n\tcolor: #eee;\n\tbackground-color: #35a;\n\tborder: 1px solid #57c;\n\tline-height: normal;\n\tpadding: 0.4em;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\nbutton:hover {\n\tbackground-color: #57c;\n\tborder-color: #79e;\n}\nbutton:disabled {\n\tbackground-color: #444;\n\tborder: 1px solid #666;\n}\ninput, select, textarea {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\nselect {\n\tpadding: 0.34em 0.4em;\n}\ninput[type=\"text\"] {\n\tmin-width: 18em;\n}\ntextarea {\n\tmin-width: 30em;\n\tresize: vertical;\n}\ninput[type=\"checkbox\"], input[type=\"file\"], input[type=\"radio\"], select {\n\tcursor: pointer;\n}\n/* BEGIN: input[type=\"range\"] */\ninput[type=\"range\"] {\n\t-webkit-appearance: none;\n\tmin-height: 1.2em;\n}\ninput[type=\"range\"]:focus {\n\toutline: none;\n}\ninput[type=\"range\"]::-webkit-slider-runnable-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n\t-webkit-appearance: none;\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\t/*\n\t\tNOTE: Ideally, `margin-top` should be `0` for Edge/Spartan (ca. v17), but\n\t\treal WebKit/Blink-based browsers need it.  Since there's more of them and\n\t\tEdge is co-opting the prefix anyway, we cater to them.  Edge will simply\n\t\thave to look ever so slightly off.\n\t*/\n\tmargin-top: -5px;\n\twidth: 33px;\n}\ninput[type=\"range\"]:focus::-webkit-slider-runnable-track {\n\tbackground: #222;\n}\ninput[type=\"range\"]::-moz-range-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\twidth: 33px;\n}\ninput[type=\"range\"]::-ms-track {\n\tbackground: transparent;\n\tborder-color: transparent;\n\tcolor: transparent;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: calc(100% - 1px);\n}\ninput[type=\"range\"]::-ms-fill-lower {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-fill-upper {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 16px;\n\twidth: 33px;\n}\n/* END: input[type=\"range\"] */\ninput:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,\ninput:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\nhr {\n\tdisplay: block;\n\theight: 1px;\n\tborder: none;\n\tborder-top: 1px solid #eee;\n\tmargin: 1em 0;\n\tpadding: 0;\n}\naudio, canvas, progress, video {\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.error-view {\n\tbackground-color: #511;\n\tborder-left: 0.5em solid #c22;\n\tdisplay: inline-block;\n\tmargin: 0.1em;\n\tmax-width: 100%;\n\tpadding: 0 0.25em;\n\tposition: relative;\n}\n.error-view > .error-toggle {\n\tbackground-color: transparent;\n\tborder: none;\n\tline-height: inherit;\n\tleft: 0;\n\tpadding: 0;\n\tposition: absolute;\n\ttop: 0;\n\twidth: 1.75em;\n}\n.error-view > .error {\n\tdisplay: inline-block;\n\tmargin-left: 0.25em;\n}\n.error-view > .error-toggle + .error {\n\tmargin-left: 1.5em;\n}\n.error-view > .error-source[hidden] {\n\tdisplay: none;\n}\n.error-view > .error-source:not([hidden]) {\n\tbackground-color: rgba(0, 0, 0, 0.2);\n\tdisplay: block;\n\tmargin: 0 0 0.25em;\n\toverflow-x: auto;\n\tpadding: 0.25em;\n}\n\n.highlight, .marked {\n\tcolor: yellow;\n\tfont-weight: bold;\n\tfont-style: italic;\n}\n.nobr {\n\twhite-space: nowrap;\n}\n\n[data-icon]:before,\n[data-icon-before]:before,\n[data-icon-after]:after,\n.error-view > .error-toggle:before,\n.error-view > .error:before,\na.link-external:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n[data-icon]:before {\n\tcontent: attr(data-icon);\n}\n[data-icon-before]:before {\n\tcontent: attr(data-icon-before) \"\\00a0\\00a0\";\n}\n[data-icon-after]:after {\n\tcontent: \"\\00a0\\00a0\" attr(data-icon-after);\n}\n.error-view > .error-toggle:before {\n\tcontent: \"\\e81a\";\n}\n.error-view > .error-toggle.enabled:before {\n\tcontent: \"\\e818\";\n}\n.error-view > .error:before {\n\tcontent: \"\\e80d\\00a0\\00a0\";\n}\na.link-external:after {\n\tcontent: \"\\00a0\\e80e\";\n}\n</style>\n<style id=\"style-core-display\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-display.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n#story {\n\tz-index: 10;\n\tmargin: 2.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-right: 1.5em;\n\t}\n}\n#passages {\n\tmax-width: 54em;\n\tmargin: 0 auto;\n}\n</style>\n<style id=\"style-core-passage\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-passage.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n.passage {\n\tline-height: 1.75;\n\ttext-align: left;\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.passage-in {\n\topacity: 0;\n}\n.passage ul, .passage ol {\n\tmargin-left: 0.5em;\n\tpadding-left: 1.5em;\n}\n.passage table {\n\tmargin: 1em 0;\n\tborder-collapse: collapse;\n\tfont-size: 100%;\n}\n.passage tr, .passage th, .passage td, .passage caption {\n\tpadding: 3px;\n}\n</style>\n<style id=\"style-core-macro\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-macro.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n@-webkit-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@-o-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n\n.macro-linkappend-insert,\n.macro-linkprepend-insert,\n.macro-linkreplace-insert,\n.macro-append-insert,\n.macro-prepend-insert,\n.macro-replace-insert,\n.macro-repeat-insert,\n.macro-timed-insert {\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.macro-linkappend-in,\n.macro-linkprepend-in,\n.macro-linkreplace-in,\n.macro-append-in,\n.macro-prepend-in,\n.macro-replace-in,\n.macro-repeat-in,\n.macro-timed-in {\n\topacity: 0;\n}\n\n.macro-type-cursor:after {\n\t-webkit-animation: cursor-blink 1s infinite;\n\t     -o-animation: cursor-blink 1s infinite;\n\t        animation: cursor-blink 1s infinite;\n\tcontent: \"\\2590\"; /* \"\\2588\" */\n\topacity: 1;\n}\n</style>\n<style id=\"style-ui-dialog\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-dialog.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\nhtml[data-dialog] body {\n\toverflow: hidden;\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-overlay.open {\n\tvisibility: visible;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n#ui-overlay:not(.open) {\n\t-webkit-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\t-o-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\ttransition: visibility 200ms step-end, opacity 200ms ease-in;\n}\n#ui-overlay {\n\tvisibility: hidden;\n\topacity: 0;\n\tz-index: 100000;\n\tposition: fixed;\n\t/*\n\ttop: -50vh;\n\tleft: -50vw;\n\theight: 200vh;\n\twidth: 200vw;\n\t*/\n\ttop: -50%;\n\tleft: -50%;\n\theight: 200%;\n\twidth: 200%;\n}\n#ui-dialog.open {\n\tdisplay: block;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n/*\n\tWe do not animate `#ui-dialog:not(.open)` for various reasons.  Chief among\n\tthem, however, is so that the dialog isn't in the middle of its animation\n\twhen other page updates happen.\n\n\te.g. The restoration of `overflow` on `body` would cause the still animating\n\t     dialog to jump around a little if a scrollbar were to pop in.\n\n\t     Any dialog action which performs a task which has its own animations\n\t     (e.g. passage display) or causes the page to reload in addition to\n\t     closing the dialog could cause display shenanigans.\n*/\n#ui-dialog {\n\tdisplay: none;\n\topacity: 0;\n\tz-index: 100100;\n\tposition: fixed;\n\ttop: 50px;\n\tmargin: 0;\n\tpadding: 0;\n}\n#ui-dialog > * {\n\t-webkit-box-sizing: border-box;\n\t        box-sizing: border-box;\n}\n#ui-dialog-titlebar {\n\tposition: relative;\n}\n#ui-dialog-close {\n\tdisplay: block;\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\twhite-space: nowrap;\n}\n#ui-dialog-body {\n\toverflow: auto;\n\tmin-width: 280px;\n\theight: 92%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.1em); /* parent - title(2.1em) */\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-overlay {\n\tbackground-color: #000;\n}\n#ui-overlay.open {\n\topacity: 0.8;\n}\n#ui-dialog {\n\tmax-width: 66em;\n}\n#ui-dialog.open {\n\topacity: 1;\n}\n#ui-dialog-titlebar {\n\tbackground-color: #444;\n\tmin-height: 24px;\n}\n#ui-dialog-title {\n\tmargin: 0;\n\tpadding: 0.2em 3.5em 0.2em 0.5em;\n\tfont-size: 1.5em;\n\ttext-align: center;\n\ttext-transform: uppercase;\n}\n#ui-dialog-close {\n\tcursor: pointer;\n\tfont-size: 120%;\n\tmargin: 0;\n\tpadding: 0;\n\twidth: 3.6em;\n\theight: 92%;\n\tbackground-color: transparent;\n\tborder: 1px solid transparent;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\n#ui-dialog-close:hover {\n\tbackground-color: #b44;\n\tborder-color: #d66;\n}\n#ui-dialog-body {\n\tbackground-color: #111;\n\tborder: 1px solid #444;\n\ttext-align: left;\n\tline-height: 1.5;\n\tpadding: 1em;\n}\n#ui-dialog-body > *:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body hr {\n\tbackground-color: #444;\n}\n\n/* Default dialog button bar styling. */\n#ui-dialog-body ul.buttons {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n#ui-dialog-body ul.buttons li {\n\tdisplay: inline-block;\n\tmargin: 0;\n\tpadding: 0.4em 0.4em 0 0;\n}\n#ui-dialog-body ul.buttons > li + li > button {\n\tmargin-left: 1em;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-close {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-close {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n</style>\n<style id=\"style-ui\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n/* Settings dialog styling. */\n#ui-dialog-body.settings [id|=\"setting-body\"] > div:first-child {\n\tdisplay: table;\n\twidth: 100%;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] {\n\tdisplay: table-cell;\n\tpadding: 0.4em 2em 0.4em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] + div {\n\tdisplay: table-cell;\n\tmin-width: 8em;\n\ttext-align: right;\n\tvertical-align: middle;\n\twhite-space: nowrap;\n}\n\n\n/*\n\tBuilt-in dialog appearance styles.\n*/\n/* List-based dialog styling (primarily for the Jumpto & Share dialogs). */\n#ui-dialog-body.list {\n\tpadding: 0;\n}\n#ui-dialog-body.list ul {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid transparent;\n}\n#ui-dialog-body.list li {\n\tmargin: 0;\n}\n#ui-dialog-body.list li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.list li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-decoration: none;\n}\n#ui-dialog-body.list li a:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n/* Saves dialog styling. */\n#ui-dialog-body.saves {\n\tpadding: 0 0 1px; /* Webkit/Blink need 1px bottom padding or they'll trigger the scroll bar */\n}\n#ui-dialog-body.saves > *:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves table {\n\tborder-spacing: 0;\n\twidth: 100%;\n}\n#ui-dialog-body.saves tr:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves td {\n\tpadding: 0.33em 0.33em;\n}\n#ui-dialog-body.saves td:first-child {\n\tmin-width: 1.5em;\n\ttext-align: center;\n}\n#ui-dialog-body.saves td:nth-child(3) {\n\tline-height: 1.2;\n}\n#ui-dialog-body.saves td:last-child {\n\ttext-align: right;\n}\n#ui-dialog-body.saves .empty {\n\tcolor: #999;\n\tspeak: none;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n#ui-dialog-body.saves .datestamp {\n\tfont-size: 75%;\n\tmargin-left: 1em;\n}\n#ui-dialog-body.saves ul.buttons li {\n\tpadding: 0.4em;\n}\n#ui-dialog-body.saves ul.buttons > li + li > button {\n\tmargin-left: 0.2em;\n}\n#ui-dialog-body.saves ul.buttons li:last-child {\n\t/*\n\t\tUsing `position:absolute;right:0;` here can produce poor results,\n\t\tso we use `float:right` instead.\n\t*/\n\tfloat: right;\n}\n\n/* Settings dialog styling. */\n#ui-dialog-body.settings div[id|=\"header-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:not(:first-child) {\n\tborder-top: 1px solid #444;\n\tpadding-top: 1em;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"] > * {\n\tmargin: 0;\n}\n#ui-dialog-body.settings h2[id|=\"header-heading\"] {\n\tfont-size: 1.375em;\n}\n#ui-dialog-body.settings p[id|=\"header-desc\"],\n#ui-dialog-body.settings p[id|=\"setting-desc\"] {\n\tfont-size: 87.5%;\n\tmargin: 0 0 0 0.5em;\n}\n#ui-dialog-body.settings div[id|=\"setting-body\"] + div[id|=\"setting-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-control\"] {\n\twhite-space: nowrap;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"] {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n#ui-dialog-body.settings input[type=\"range\"][id|=\"setting-control\"] {\n\tmax-width: 35vw;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-body.list a,\n#ui-dialog-body.settings span[id|=\"setting-input\"] {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-body.saves button[id=\"saves-export\"]:before,\n#ui-dialog-body.saves button[id=\"saves-import\"]:before,\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before,\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after,\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-dialog-body.saves button[id=\"saves-export\"]:before {\n\tcontent: \"\\e829\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-import\"]:before {\n\tcontent: \"\\e82a\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before {\n\tcontent: \"\\e827\\00a0\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n</style>\n<style id=\"style-ui-bar\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-bar.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\n#story {\n\tmargin-left: 20em;\n\t-webkit-transition: margin-left 200ms ease-in;\n\t-o-transition: margin-left 200ms ease-in;\n\ttransition: margin-left 200ms ease-in;\n}\n#ui-bar.stowed ~ #story {\n\tmargin-left: 4.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-left: 19em;\n\t}\n\t#ui-bar.stowed ~ #story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n/*\n\tAt very narrow viewport widths, set `#story{margin-left}` equal to the value\n\tof `#ui-bar.stowed~#story{margin-left}`, so that `#ui-bar` will side over top\n\tof `#story` when unstowed, rather than shoving it over.\n*/\n@media screen and (max-width: 768px) {\n\t#story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-bar {\n\tposition: fixed;\n\tz-index: 50;\n\ttop: 0;\n\tleft: 0;\n\twidth: 17.5em;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\t-webkit-transition: left 200ms ease-in;\n\t-o-transition: left 200ms ease-in;\n\ttransition: left 200ms ease-in;\n}\n#ui-bar.stowed {\n\tleft: -15.5em;\n}\n#ui-bar-tray {\n\tposition: absolute;\n\ttop: 0.2em;\n\tleft: 0;\n\tright: 0;\n}\n#ui-bar-body {\n\theight: 90%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.5em);\n\tmargin: 2.5em 0;\n\tpadding: 0 1.5em;\n}\n#ui-bar.stowed #ui-bar-history,\n#ui-bar.stowed #ui-bar-body {\n\tvisibility: hidden;\n\t-webkit-transition: visibility 200ms step-end;\n\t-o-transition: visibility 200ms step-end;\n\ttransition: visibility 200ms step-end;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-bar {\n\tbackground-color: #222;\n\tborder-right: 1px solid #444;\n\ttext-align: center;\n}\n#ui-bar a {\n\ttext-decoration: none;\n}\n#ui-bar hr {\n\tborder-color: #444;\n}\n#ui-bar-toggle,\n#ui-bar-history [id|=\"history\"] {\n\tfont-size: 1.2em;\n\tline-height: inherit;\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n}\n#ui-bar-toggle {\n\tdisplay: block;\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tborder-right: none;\n\tpadding: 0.3em 0.45em 0.25em;\n}\n#ui-bar.stowed #ui-bar-toggle {\n\tpadding: 0.3em 0.35em 0.25em 0.55em;\n}\n#ui-bar-toggle:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history {\n\tmargin: 0 auto;\n}\n#ui-bar-history [id|=\"history\"] {\n\tpadding: 0.2em 0.45em 0.35em;\n}\n#ui-bar-history #history-jumpto {\n\tpadding: 0.2em 0.665em 0.35em;\n}\n#ui-bar-history [id|=\"history\"]:not(:first-child) {\n\tmargin-left: 1.2em;\n}\n#ui-bar-history [id|=\"history\"]:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history [id|=\"history\"]:disabled {\n\tcolor: #444;\n\tbackground-color: transparent;\n\tborder-color: #444;\n}\n#ui-bar-body {\n\tline-height: 1.5;\n\toverflow: auto;\n}\n#ui-bar-body > :not(:first-child) {\n\tmargin-top: 2em;\n}\n#story-title {\n\tmargin: 0;\n\tfont-size: 162.5%;\n}\n#story-author {\n\tmargin-top: 2em;\n\tfont-weight: bold;\n}\n#menu ul {\n\tmargin: 1em 0 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid #444;\n}\n#menu ul:empty {\n\tdisplay: none;\n}\n#menu li {\n\tmargin: 0;\n}\n#menu li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#menu li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-transform: uppercase;\n}\n#menu li a:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-bar-history [id|=\"history\"],\n#ui-bar-toggle,\n#menu a {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-bar-toggle:before,\n#ui-bar-history [id|=\"history\"],\n#menu-core li[id|=\"menu-item\"] a:before {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-bar-toggle:before {\n\tcontent: \"\\e81d\";\n}\n#ui-bar.stowed #ui-bar-toggle:before {\n\tcontent: \"\\e81e\";\n}\n#menu-item-saves a:before {\n\tcontent: \"\\e82b\\00a0\";\n}\n#menu-item-settings a:before {\n\tcontent: \"\\e82d\\00a0\";\n}\n#menu-item-restart a:before {\n\tcontent: \"\\e82c\\00a0\";\n}\n#menu-item-share a:before {\n\tcontent: \"\\e82f\\00a0\";\n}\n</style>\n<style id=\"style-ui-debug\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-debug.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault debug bar styles.\n*/\n#debug-bar {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 0;\n\tmargin: 0;\n\tmax-height: 75%;\n\t/* max-width: 28em;\n\tmin-width: 22em; */\n\tpadding: 0.5em;\n\tposition: fixed;\n\tright: 0;\n\tz-index: 99900;\n}\n#debug-bar > div:not([id]) + div {\n\tmargin-top: 0.5em;\n}\n#debug-bar > div > label {\n\tmargin-right: 0.5em;\n}\n#debug-bar > div > input[type=\"text\"] {\n\tmin-width: 0;\n\twidth: 8em;\n}\n#debug-bar > div > select {\n\twidth: 15em;\n}\n\n#debug-bar-toggle {\n\tcolor: #eee;\n\tbackground-color: #222;\n\tborder: 1px solid #444;\n\theight: 101%; /* fallback for browsers without support for calc() */\n\theight: calc(100% + 1px);\n\tleft: -2em; /* fallback for browsers without support for calc() */\n\tleft: calc(-2em - 1px);\n\tposition: absolute;\n\ttop: -1px;\n\twidth: 2em;\n}\n#debug-bar-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n#debug-bar-hint {\n\tbottom: 0.175em;\n\tfont-size: 4.5em;\n\topacity: 0.33;\n\tpointer-events: none;\n\tposition: fixed;\n\tright: 0.6em;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n\twhite-space: nowrap;\n}\n\n#debug-bar-watch {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 102%; /* fallback for browsers without support for calc() */\n\tbottom: calc(100% + 1px);\n\tfont-size: 0.9em;\n\tleft: -1px;\n\tmax-height: 650%; /* fallback for browsers without support for vh units */\n\tmax-height: 65vh;\n\tposition: absolute;\n\toverflow-x: hidden;\n\toverflow-y: scroll;\n\tright: 0;\n\tz-index: 99800;\n}\n#debug-bar-watch[hidden] {\n\tdisplay: none;\n}\n#debug-bar-watch div {\n\tcolor: #999;\n\tfont-style: italic;\n\tmargin: 1em auto;\n\ttext-align: center;\n}\n#debug-bar-watch table {\n\twidth: 100%;\n}\n#debug-bar-watch tr:nth-child(2n) {\n\tbackground-color: rgba(127, 127, 127, 0.15);\n}\n#debug-bar-watch td {\n\tpadding: 0.2em 0;\n}\n#debug-bar-watch td:first-child + td {\n\tpadding: 0.2em 0.3em 0.2em 0.1em;\n}\n#debug-bar-watch .watch-delete {\n\tbackground-color: transparent;\n\tborder: none;\n\tcolor: #c00;\n}\n#debug-bar-watch-all,\n#debug-bar-watch-none {\n\tmargin-left: 0.5em;\n}\n#debug-bar-watch-toggle,\n#debug-bar-views-toggle {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tmargin-right: 1em;\n\tpadding: 0.4em;\n}\n#debug-bar-watch-toggle:hover,\n#debug-bar-views-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle,\nhtml[data-debug-view] #debug-bar-views-toggle {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:hover,\nhtml[data-debug-view] #debug-bar-views-toggle:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n\n#debug-bar-toggle:before,\n#debug-bar-hint:after,\n#debug-bar-watch .watch-delete:before,\n#debug-bar-watch-add:before,\n#debug-bar-watch-all:before,\n#debug-bar-watch-none:before,\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#debug-bar-toggle:before {\n\tcontent: \"\\e838\";\n}\n#debug-bar-hint:after {\n\tcontent: \"\\e838\\202f\\e822\";\n}\n#debug-bar-watch .watch-delete:before {\n\tcontent: \"\\e804\";\n}\n#debug-bar-watch-add:before {\n\tcontent: \"\\e805\";\n}\n#debug-bar-watch-all:before {\n\tcontent: \"\\e83a\";\n}\n#debug-bar-watch-none:before {\n\tcontent: \"\\e827\";\n}\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:after,\nhtml[data-debug-view] #debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n\n\n/*\n\tDefault debug view styles.\n*/\nhtml[data-debug-view] .debug {\n\tpadding: 0.25em;\n\tbackground-color: #234; /* #541, #151 */\n}\nhtml[data-debug-view] .debug[title] {\n\tcursor: help;\n}\nhtml[data-debug-view] .debug.block {\n\tdisplay: inline-block;\n\tvertical-align: middle;\n}\nhtml[data-debug-view] .debug.invalid {\n\ttext-decoration: line-through;\n}\nhtml[data-debug-view] .debug.hidden,\nhtml[data-debug-view] .debug.hidden .debug {\n\tbackground-color: #555;\n}\nhtml:not([data-debug-view]) .debug.hidden {\n\tdisplay: none;\n}\n\nhtml[data-debug-view] .debug[data-name][data-type]:before,\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:after {\n\tbackground-color: rgba(0,0,0,0.25);\n\tfont-family: monospace, monospace;\n\twhite-space: pre;\n}\nhtml[data-debug-view] .debug[data-name][data-type]:before {\n\tcontent: attr(data-name);\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"]:before {\n\tcontent: \"<<\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"].nonvoid:after {\n\tcontent: \"<</\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"]:before {\n\tcontent: \"<\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"].nonvoid:after {\n\tcontent: \"</\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type]:not(:empty):before {\n\tmargin-right: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:not(:empty):after {\n\tmargin-left: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"],\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"]:before {\n\tdisplay: block;\n}\n</style>\n</head>\n<body>\n\t<div id=\"init-screen\">\n\t\t<div id=\"init-no-js\"><noscript>JavaScript is required. Please enable it to continue.</noscript></div>\n\t\t<div id=\"init-lacking\">Your browser lacks required capabilities. Please upgrade it or switch to another to continue.</div>\n\t\t<div id=\"init-loading\"><div>Loading&hellip;</div></div>\n\t</div>\n\t{{STORY_DATA}}\n\t<script id=\"script-sugarcube\" type=\"text/javascript\">\n\t/*! SugarCube JS */\n\tif(document.documentElement.getAttribute(\"data-init\")===\"loading\"){window.TWINE1=false;\nwindow.DEBUG=false;\n(function (window, document, jQuery, undefined) {\n\"use strict\";\n\n/***********************************************************************************************************************\n\n\tlib/alert.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: This regular expression should be elsewhere.\n\n\tError prologs by engine/browser: (ca. 2018)\n\t\tChrome, Opera, & Vivaldi → `Uncaught \\w*Error: …`\n\t\tEdge & IE                → `…`\n\t\tFirefox                  → `Error: …`\n\t\tOpera (Presto)           → `Uncaught exception: \\w*(?:Error|Exception): …`\n\t\tSafari (ca. v5.1)        → `\\w*(?:Error|_ERR): …`\n*/\nvar errorPrologRegExp = /^(?:(?:uncaught\\s+(?:exception:\\s+)?)?\\w*(?:error|exception|_err):\\s+)+/i; // eslint-disable-line no-unused-vars, no-var\n\nvar Alert = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tError Functions.\n\t*******************************************************************************************************************/\n\tfunction _alertMesg(type, where, what, error) {\n\t\tconst isFatal = type === 'fatal';\n\t\tlet mesg = `Apologies! ${isFatal ? 'A fatal' : 'An'} error has occurred.`;\n\n\t\tif (isFatal) {\n\t\t\tmesg += ' Aborting.';\n\t\t}\n\t\telse {\n\t\t\tmesg += ' You may be able to continue, but some parts may not work properly.';\n\t\t}\n\n\t\tif (where != null || what != null) { // lazy equality for null\n\t\t\tmesg += '\\n\\nError';\n\n\t\t\tif (where != null) { // lazy equality for null\n\t\t\t\tmesg += ` [${where}]`;\n\t\t\t}\n\n\t\t\tif (what != null) { // lazy equality for null\n\t\t\t\tmesg += `: ${what.replace(errorPrologRegExp, '')}.`;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmesg += ': unknown error.';\n\t\t\t}\n\t\t}\n\n\t\tif (typeof error === 'object' && error !== null && error.stack) {\n\t\t\tmesg += `\\n\\nStack Trace:\\n${error.stack}`;\n\t\t}\n\n\t\twindow.alert(mesg); // eslint-disable-line no-alert\n\t}\n\n\tfunction alertError(where, what, error) {\n\t\t_alertMesg(null, where, what, error);\n\t}\n\n\tfunction alertFatal(where, what, error) {\n\t\t_alertMesg('fatal', where, what, error);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tError Event.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSet up a global error handler for uncaught exceptions.\n\t*/\n\t(origOnError => {\n\t\twindow.onerror = function (what, source, lineNum, colNum, error) {\n\t\t\t// Uncaught exceptions during play may be recoverable/ignorable.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\talertError(null, what, error);\n\t\t\t}\n\n\t\t\t// Uncaught exceptions during startup should be fatal.\n\t\t\telse {\n\t\t\t\talertFatal(null, what, error);\n\t\t\t\twindow.onerror = origOnError;\n\n\t\t\t\tif (typeof window.onerror === 'function') {\n\t\t\t\t\twindow.onerror.apply(this, arguments);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})(window.onerror);\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\terror : { value : alertError },\n\t\tfatal : { value : alertFatal }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/patterns.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tTODO: Move all markup patterns into here.\n*/\n\nvar Patterns = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPatterns.\n\t*******************************************************************************************************************/\n\t/*\n\t\tWhitespace patterns.\n\n\t\tSpace class (equivalent to `\\s`):\n\t\t\t[\\u0020\\f\\n\\r\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]\n\t\tSpace class, sans line terminators:\n\t\t\t[\\u0020\\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]\n\t\tLine Terminator class:\n\t\t\t[\\n\\r\\u2028\\u2029]\n\t*/\n\tconst space = (() => {\n\t\t/*\n\t\t\tSome browsers still supported by SugarCube have faulty space classes (`\\s`).\n\t\t\tWe check for that lossage here and, if necessary, build our own class from\n\t\t\tthe component pieces.\n\t\t*/\n\t\tconst wsMap = new Map([\n\t\t\t['\\u0020', '\\\\u0020'],\n\t\t\t['\\f', '\\\\f'],\n\t\t\t['\\n', '\\\\n'],\n\t\t\t['\\r', '\\\\r'],\n\t\t\t['\\t', '\\\\t'],\n\t\t\t['\\v', '\\\\v'],\n\t\t\t['\\u00a0', '\\\\u00a0'],\n\t\t\t['\\u1680', '\\\\u1680'],\n\t\t\t['\\u180e', '\\\\u180e'],\n\t\t\t['\\u2000', '\\\\u2000'],\n\t\t\t['\\u2001', '\\\\u2001'],\n\t\t\t['\\u2002', '\\\\u2002'],\n\t\t\t['\\u2003', '\\\\u2003'],\n\t\t\t['\\u2004', '\\\\u2004'],\n\t\t\t['\\u2005', '\\\\u2005'],\n\t\t\t['\\u2006', '\\\\u2006'],\n\t\t\t['\\u2007', '\\\\u2007'],\n\t\t\t['\\u2008', '\\\\u2008'],\n\t\t\t['\\u2009', '\\\\u2009'],\n\t\t\t['\\u200a', '\\\\u200a'],\n\t\t\t['\\u2028', '\\\\u2028'],\n\t\t\t['\\u2029', '\\\\u2029'],\n\t\t\t['\\u202f', '\\\\u202f'],\n\t\t\t['\\u205f', '\\\\u205f'],\n\t\t\t['\\u3000', '\\\\u3000'],\n\t\t\t['\\ufeff', '\\\\ufeff']\n\t\t]);\n\t\tconst wsRe = /^\\s$/;\n\t\tlet missing = '';\n\n\t\twsMap.forEach((pat, char) => {\n\t\t\tif (!wsRe.test(char)) {\n\t\t\t\tmissing += pat;\n\t\t\t}\n\t\t});\n\n\t\treturn missing ? `[\\\\s${missing}]` : '\\\\s';\n\t})();\n\tconst spaceNoTerminator = '[\\\\u0020\\\\f\\\\t\\\\v\\\\u00a0\\\\u1680\\\\u180e\\\\u2000-\\\\u200a\\\\u202f\\\\u205f\\\\u3000\\\\ufeff]';\n\tconst lineTerminator    = '[\\\\n\\\\r\\\\u2028\\\\u2029]';\n\tconst notSpace          = space === '\\\\s' ? '\\\\S' : space.replace(/^\\[/, '[^');\n\n\t/*\n\t\tCharacter patterns.\n\t*/\n\tconst anyChar = `(?:.|${lineTerminator})`;\n\n\t/*\n\t\tLetter patterns.\n\n\t\tFIXME:\n\t\t\t1. The existing set, which is a TiddlyWiki holdover, should probably\n\t\t\t   encompass a significantly greater range of BMP code points.\n\t\t\t2. Should we include the surrogate pair code units (\\uD800-\\uDBFF &\n\t\t\t   \\uDC00-\\uDFFF) to handle non-BMP code points?  Further, should we\n\t\t\t   simply be checking for the code units themselves or checking for\n\t\t\t   properly mated pairs?\n\t*/\n\tconst anyLetter       = '[0-9A-Z_a-z\\\\-\\\\u00c0-\\\\u00d6\\\\u00d8-\\\\u00f6\\\\u00f8-\\\\u00ff\\\\u0150\\\\u0170\\\\u0151\\\\u0171]';\n\tconst anyLetterStrict = anyLetter.replace('\\\\-', ''); // anyLetter sans hyphen\n\n\t/*\n\t\tIdentifier patterns.\n\n\t\tNOTE: Since JavaScript's RegExp syntax does not support Unicode character\n\t\tclasses, the correct regular expression to match a valid identifier name,\n\t\twithin the scope of our needs, would be on the order of approximately 5–6\n\t\tor 11–16 KiB, depending on how the pattern was built.  That being the case,\n\t\tfor the moment we restrict valid TwineScript identifiers to US-ASCII.\n\n\t\tFIXME: Fix this to, at least, approximate the correct range.\n\t*/\n\tconst identifierFirstChar = '[$A-Z_a-z]';\n\tconst identifierNextChar  = '[$0-9A-Z_a-z]';\n\tconst identifier          = `${identifierFirstChar}${identifierNextChar}*`;\n\n\t// Variable patterns.\n\tconst variableSigil = '[$_]';\n\tconst variable      = variableSigil + identifier;\n\n\t// Macro name pattern.\n\tconst macroName = '[A-Za-z][\\\\w-]*|[=-]';\n\n\t// Template name pattern.\n\tconst templateName = '[A-Za-z][\\\\w-]*';\n\n\t// CSS ID or class sigil pattern.\n\tconst cssIdOrClassSigil = '[#.]';\n\n\t// CSS image transclusion template pattern.\n\t//\n\t// NOTE: The alignment syntax isn't supported, but removing it might break uses\n\t// of the template in the wild, so we leave it alone for now.\n\tconst cssImage = '\\\\[[<>]?[Ii][Mm][Gg]\\\\[(?:\\\\s|\\\\S)*?\\\\]\\\\]+';\n\n\t// Inline CSS pattern.\n\tconst inlineCss = (() => {\n\t\t/* legacy */\n\t\tconst twStyle   = `(${anyLetter}+)\\\\(([^\\\\)\\\\|\\\\n]+)\\\\):`;\n\t\t/* /legacy */\n\t\tconst cssStyle  = `${spaceNoTerminator}*(${anyLetter}+)${spaceNoTerminator}*:([^;\\\\|\\\\n]+);`;\n\t\tconst idOrClass = `${spaceNoTerminator}*((?:${cssIdOrClassSigil}${anyLetter}+${spaceNoTerminator}*)+);`;\n\n\t\t// [1,2] = style(value):\n\t\t// [3,4] = style:value;\n\t\t// [5]   = #id.className;\n\t\treturn `${twStyle}|${cssStyle}|${idOrClass}`;\n\t})();\n\n\t// URL pattern.\n\tconst url = '(?:file|https?|mailto|ftp|javascript|irc|news|data):[^\\\\s\\'\"]+';\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze({\n\t\tspace,\n\t\tspaceNoTerminator,\n\t\tlineTerminator,\n\t\tnotSpace,\n\t\tanyChar,\n\t\tanyLetter,\n\t\tanyLetterStrict,\n\t\tidentifierFirstChar,\n\t\tidentifierNextChar,\n\t\tidentifier,\n\t\tvariableSigil,\n\t\tvariable,\n\t\tmacroName,\n\t\ttemplateName,\n\t\tcssIdOrClassSigil,\n\t\tcssImage,\n\t\tinlineCss,\n\t\turl\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/extensions.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\n/*\n\tJavaScript Polyfills.\n\n\tNOTE: The ES5 and ES6 polyfills come from the vendored `es5-shim.js` and `es6-shim.js` libraries.\n*/\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tTrims whitespace from either the start or end of the given string.\n\t*/\n\tconst _trimString = (() => {\n\t\t// Whitespace regular expressions.\n\t\tconst startWSRe = new RegExp(`^${Patterns.space}${Patterns.space}*`);\n\t\tconst endWSRe   = new RegExp(`${Patterns.space}${Patterns.space}*$`);\n\n\t\tfunction trimString(str, where) {\n\t\t\tconst val = String(str);\n\n\t\t\tif (!val) {\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tswitch (where) {\n\t\t\tcase 'start':\n\t\t\t\treturn startWSRe.test(val) ? val.replace(startWSRe, '') : val;\n\n\t\t\tcase 'end':\n\t\t\t\treturn endWSRe.test(val) ? val.replace(endWSRe, '') : val;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`_trimString called with incorrect where parameter value: \"${where}\"`);\n\t\t\t}\n\t\t}\n\n\t\treturn trimString;\n\t})();\n\n\t/*\n\t\tGenerates a pad string based upon the given string and length.\n\t*/\n\tfunction _createPadString(length, padding) {\n\t\tconst targetLength = Number.parseInt(length, 10) || 0;\n\n\t\tif (targetLength < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet padString = typeof padding === 'undefined' ? '' : String(padding);\n\n\t\tif (padString === '') {\n\t\t\tpadString = ' ';\n\t\t}\n\n\t\twhile (padString.length < targetLength) {\n\t\t\tconst curPadLength    = padString.length;\n\t\t\tconst remainingLength = targetLength - curPadLength;\n\n\t\t\tpadString += curPadLength > remainingLength\n\t\t\t\t? padString.slice(0, remainingLength)\n\t\t\t\t: padString;\n\t\t}\n\n\t\tif (padString.length > targetLength) {\n\t\t\tpadString = padString.slice(0, targetLength);\n\t\t}\n\n\t\treturn padString;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPolyfills.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[ES2019] Returns a new array consisting of the source array with all sub-array elements\n\t\tconcatenated into it recursively up to the given depth.\n\t*/\n\tif (!Array.prototype.flat) {\n\t\tObject.defineProperty(Array.prototype, 'flat', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\t\t\tvalue        : (() => {\n\t\t\t\tfunction flat(/* depth */) {\n\t\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\t\tthrow new TypeError('Array.prototype.flat called on null or undefined');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst depth = arguments.length === 0 ? 1 : Number(arguments[0]) || 0;\n\n\t\t\t\t\tif (depth < 1) {\n\t\t\t\t\t\treturn Array.prototype.slice.call(this);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Array.prototype.reduce.call(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\t(acc, cur) => {\n\t\t\t\t\t\t\tif (cur instanceof Array) {\n\t\t\t\t\t\t\t\t// acc.push.apply(acc, flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t\tacc.push(...flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc.push(cur);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn flat;\n\t\t\t})()\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new array consisting of the result of calling the given mapping function\n\t\ton every element in the source array and then concatenating all sub-array elements into it\n\t\trecursively up to a depth of `1`.  Identical to calling `<Array>.map(fn).flat()`.\n\t*/\n\tif (!Array.prototype.flatMap) {\n\t\tObject.defineProperty(Array.prototype, 'flatMap', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* callback [, thisArg] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.flatMap called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.map.apply(this, arguments).flat();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2016] Returns whether the given element was found within the array.\n\t*/\n\tif (!Array.prototype.includes) {\n\t\tObject.defineProperty(Array.prototype, 'includes', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.includes called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst length = this.length >>> 0;\n\n\t\t\t\tif (length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst needle = arguments[0];\n\t\t\t\tlet i = Number(arguments[1]) || 0;\n\n\t\t\t\tif (i < 0) {\n\t\t\t\t\ti = Math.max(0, length + i);\n\t\t\t\t}\n\n\t\t\t\tfor (/* empty */; i < length; ++i) {\n\t\t\t\t\tconst value = this[i];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property/value\n\t\tpairs as `[key, value]` arrays.\n\t*/\n\tif (!Object.entries) {\n\t\tObject.defineProperty(Object, 'entries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.entries object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => [key, obj[key]]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new generic object consisting of the given list's key/value pairs.\n\t*/\n\tif (!Object.fromEntries) {\n\t\tObject.defineProperty(Object, 'fromEntries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(iter) {\n\t\t\t\treturn Array.from(iter).reduce(\n\t\t\t\t\t(acc, pair) => {\n\t\t\t\t\t\tif (Object(pair) !== pair) {\n\t\t\t\t\t\t\tthrow new TypeError('Object.fromEntries iterable parameter must yield objects');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (pair[0] in acc) {\n\t\t\t\t\t\t\tObject.defineProperty(acc, pair[0], {\n\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\tvalue        : pair[1]\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tacc[pair[0]] = pair[1]; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns all own property descriptors of the given object.\n\t*/\n\tif (!Object.getOwnPropertyDescriptors) {\n\t\tObject.defineProperty(Object, 'getOwnPropertyDescriptors', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (obj == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Object.getOwnPropertyDescriptors object parameter is null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst O = Object(obj);\n\n\t\t\t\treturn Reflect.ownKeys(O).reduce(\n\t\t\t\t\t(acc, key) => {\n\t\t\t\t\t\tconst desc = Object.getOwnPropertyDescriptor(O, key);\n\n\t\t\t\t\t\tif (typeof desc !== 'undefined') {\n\t\t\t\t\t\t\tif (key in acc) {\n\t\t\t\t\t\t\t\tObject.defineProperty(acc, key, {\n\t\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\t\tvalue        : desc\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc[key] = desc; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property values.\n\t*/\n\tif (!Object.values) {\n\t\tObject.defineProperty(Object, 'values', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.values object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => obj[key]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the start of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padStart) {\n\t\tObject.defineProperty(String.prototype, 'padStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn _createPadString(targetLength - baseLength, padding) + baseString;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the end of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padEnd) {\n\t\tObject.defineProperty(String.prototype, 'padEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn baseString + _createPadString(targetLength - baseLength, padding);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the start of the string.\n\t*/\n\tif (!String.prototype.trimStart) {\n\t\tObject.defineProperty(String.prototype, 'trimStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimLeft) {\n\t\tObject.defineProperty(String.prototype, 'trimLeft', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimLeft called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the end of the string.\n\t*/\n\tif (!String.prototype.trimEnd) {\n\t\tObject.defineProperty(String.prototype, 'trimEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimRight) {\n\t\tObject.defineProperty(String.prototype, 'trimRight', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimRight called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n})();\n\n\n/*\n\tJavaScript Extensions.\n*/\n(() => {\n\t'use strict';\n\n\tconst _nativeMathRandom = Math.random;\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the given bounds.\n\t*/\n\tfunction _random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('_random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = arguments[0];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = arguments[0];\n\t\t\tmax = arguments[1];\n\t\t\tbreak;\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(_nativeMathRandom() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a randomly selected index within the given length and bounds.\n\t\tBounds may be negative.\n\t*/\n\tfunction _randomIndex(length, boundsArgs) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (boundsArgs.length) {\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = length - 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(boundsArgs[1]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(boundsArgs[1]);\n\t\t\tmax = Math.trunc(boundsArgs[2]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min)) {\n\t\t\tmin = 0;\n\t\t}\n\t\telse if (!Number.isFinite(min) || min >= length) {\n\t\t\tmin = length - 1;\n\t\t}\n\t\telse if (min < 0) {\n\t\t\tmin = length + min;\n\n\t\t\tif (min < 0) {\n\t\t\t\tmin = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (Number.isNaN(max)) {\n\t\t\tmax = 0;\n\t\t}\n\t\telse if (!Number.isFinite(max) || max >= length) {\n\t\t\tmax = length - 1;\n\t\t}\n\t\telse if (max < 0) {\n\t\t\tmax = length + max;\n\n\t\t\tif (max < 0) {\n\t\t\t\tmax = length - 1;\n\t\t\t}\n\t\t}\n\n\t\treturn _random(min, max);\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Will throw exceptions on invalid surrogate pairs.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction _getCodePointStartAndEnd(str, pos) {\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : pos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos) + str.charAt(nextPos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : nextPos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\treturn {\n\t\t\tchar  : str.charAt(prevPos) + str.charAt(pos),\n\t\t\tstart : prevPos,\n\t\t\tend   : pos\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, General.\n\t*******************************************************************************************************************/\n\t/*\n\t\tRandomly selects an element from the given array, or array-like object, and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(array /* DEPRECATED: [, [min ,] max] */) {\n\t\t\tif (\n\t\t\t\t   typeof array !== 'object'\n\t\t\t\t|| array === null\n\t\t\t\t|| !Object.prototype.hasOwnProperty.call(array, 'length')\n\t\t\t) {\n\t\t\t\tthrow new TypeError('Array.random array parameter must be an array or array-lke object');\n\t\t\t}\n\n\t\t\tconst length = array.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, Array.prototype.slice.call(arguments, 1));\n\n\t\t\treturn array[index];\n\t\t}\n\t});\n\n\t/*\n\t\tConcatenates one or more unique elements to the end of the base array\n\t\tand returns the result as a new array.  Elements which are arrays will\n\t\tbe merged—i.e. their elements will be concatenated, rather than the\n\t\tarray itself.\n\t*/\n\tObject.defineProperty(Array.prototype, 'concatUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.concatUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst result = Array.from(this);\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst items   = Array.prototype.reduce.call(arguments, (prev, cur) => prev.concat(cur), []);\n\t\t\tconst addSize = items.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = items[i];\n\n\t\t\t\tif (indexOf.call(result, value) === -1) {\n\t\t\t\t\tpush.call(result, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst needle  = arguments[0];\n\t\t\tlet pos   = Number(arguments[1]) || 0;\n\t\t\tlet count = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\t++pos;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the given elements from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'delete', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.delete called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst needles       = Array.prototype.concat.apply([], arguments);\n\t\t\tconst needlesLength = needles.length;\n\t\t\tconst indices       = [];\n\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tconst value = this[i];\n\n\t\t\t\tfor (let j = 0; j < needlesLength; ++j) {\n\t\t\t\t\tconst needle = needles[j];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\tindices.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0, iend = indices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[indices[i]];\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements at the given indices from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteAt', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* indices */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteAt called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice     = Array.prototype.splice;\n\t\t\tconst cpyIndices = [\n\t\t\t\t...new Set(\n\t\t\t\t\tArray.prototype.concat.apply([], arguments)\n\t\t\t\t\t\t// Map negative indices to their positive counterparts,\n\t\t\t\t\t\t// so the Set can properly filter out duplicates.\n\t\t\t\t\t\t.map(x => x < 0 ? Math.max(0, length + x) : x)\n\t\t\t\t).values()\n\t\t\t];\n\t\t\tconst delIndices = [...cpyIndices].sort((a, b) => b - a);\n\t\t\tconst result     = [];\n\n\t\t\t// Copy the elements (in originally specified order).\n\t\t\tfor (let i = 0, iend = cpyIndices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[cpyIndices[i]];\n\t\t\t}\n\n\t\t\t// Delete the elements (in descending numeric order).\n\t\t\tfor (let i = 0, iend = delIndices.length; i < iend; ++i) {\n\t\t\t\tsplice.call(this, delIndices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements that pass the test implemented\n\t\tby the given predicate function from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteWith', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(predicate, thisArg) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteWith called on null or undefined');\n\t\t\t}\n\t\t\tif (typeof predicate !== 'function') {\n\t\t\t\tthrow new Error('Array.prototype.deleteWith predicate parameter must be a function');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice  = Array.prototype.splice;\n\t\t\tconst indices = [];\n\t\t\tconst result  = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tif (predicate.call(thisArg, this[i], i, this)) {\n\t\t\t\t\tresult.push(this[i]);\n\t\t\t\t\tindices.push(i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[0];\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAll called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAll.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\t!Array.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAny called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAny.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\tArray.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[length - 1];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluck', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluck called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn Array.prototype.splice.call(this, index, 1)[0];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes the given number of unique elements from the base array\n\t\tand returns the removed elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluckMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluckMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.pluckMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\t\t\tconst result = [];\n\t\t\tlet max = length - 1;\n\n\t\t\tdo {\n\t\t\t\tresult.push(splice.call(this, _random(0, max--), 1)[0]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tAppends one or more unique elements to the end of the base array and\n\t\treturns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pushUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pushUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tpush.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.random called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn this[index];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects the given number of unique elements from the base array\n\t\tand returns the selected elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'randomMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.randomMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.randomMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst picked = new Map();\n\t\t\tconst result = [];\n\t\t\tconst max    = length - 1;\n\n\t\t\tdo {\n\t\t\t\tlet i;\n\t\t\t\tdo {\n\t\t\t\t\ti = _random(0, max);\n\t\t\t\t} while (picked.has(i));\n\t\t\t\tpicked.set(i, true);\n\t\t\t\tresult.push(this[i]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly shuffles the array and returns it.\n\t*/\n\tObject.defineProperty(Array.prototype, 'shuffle', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.shuffle called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tfor (let i = length - 1; i > 0; --i) {\n\t\t\t\tconst j = Math.floor(_nativeMathRandom() * (i + 1));\n\n\t\t\t\tif (i === j) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// [this[i], this[j]] = [this[j], this[i]];\n\t\t\t\tconst swap = this[i];\n\t\t\t\tthis[i] = this[j];\n\t\t\t\tthis[j] = swap;\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t});\n\n\t/*\n\t\tPrepends one or more unique elements to the beginning of the base array\n\t\tand returns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'unshiftUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.unshiftUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst unshift = Array.prototype.unshift;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tunshift.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a bound function that supplies the given arguments to the base\n\t\tfunction, followed by the arguments are supplied to the bound function,\n\t\twhenever it is called.\n\t*/\n\tObject.defineProperty(Function.prototype, 'partial', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Function.prototype.partial called on null or undefined');\n\t\t\t}\n\n\t\t\tconst slice = Array.prototype.slice;\n\t\t\tconst fn    = this;\n\t\t\tconst bound = slice.call(arguments, 0);\n\n\t\t\treturn function () {\n\t\t\t\tconst applied = [];\n\t\t\t\tlet argc = 0;\n\n\t\t\t\tfor (let i = 0; i < bound.length; ++i) {\n\t\t\t\t\tapplied.push(bound[i] === undefined ? arguments[argc++] : bound[i]);\n\t\t\t\t}\n\n\t\t\t\treturn fn.apply(this, applied.concat(slice.call(arguments, argc)));\n\t\t\t};\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the given numerical clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Math, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num, min, max) {\n\t\t\tconst value = Number(num);\n\t\t\treturn Number.isNaN(value) ? NaN : value.clamp(min, max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a decimal number eased from 0 to 1.\n\n\t\tNOTE: The magnitude of the returned value decreases if num < 0.5 or increases if num > 0.5.\n\t*/\n\tObject.defineProperty(Math, 'easeInOut', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num) {\n\t\t\treturn 1 - (Math.cos(Number(num) * Math.PI) + 1) / 2;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Number.prototype, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* min, max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Number.prototype.clamp called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length !== 2) {\n\t\t\t\tthrow new Error('Number.prototype.clamp called with an incorrect number of parameters');\n\t\t\t}\n\n\t\t\tlet min = Number(arguments[0]);\n\t\t\tlet max = Number(arguments[1]);\n\n\t\t\tif (min > max) {\n\t\t\t\t[min, max] = [max, min];\n\t\t\t}\n\n\t\t\treturn Math.min(Math.max(this, min), max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the given string with all RegExp metacharacters escaped.\n\t*/\n\tif (!RegExp.escape) {\n\t\t(() => {\n\t\t\tconst _regExpMetaCharsRe    = /[\\\\^$*+?.()|[\\]{}]/g;\n\t\t\tconst _hasRegExpMetaCharsRe = new RegExp(_regExpMetaCharsRe.source); // to drop the global flag\n\n\t\t\tObject.defineProperty(RegExp, 'escape', {\n\t\t\t\tconfigurable : true,\n\t\t\t\twritable     : true,\n\n\t\t\t\tvalue(str) {\n\t\t\t\t\tconst val = String(str);\n\t\t\t\t\treturn val && _hasRegExpMetaCharsRe.test(val)\n\t\t\t\t\t\t? val.replace(_regExpMetaCharsRe, '\\\\$&')\n\t\t\t\t\t\t: val;\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t}\n\n\t/*\n\t\tReturns a formatted string, after replacing each format item in the given\n\t\tformat string with the text equivalent of the corresponding argument's value.\n\t*/\n\t(() => {\n\t\tconst _formatRegExp    = /{(\\d+)(?:,([+-]?\\d+))?}/g;\n\t\tconst _hasFormatRegExp = new RegExp(_formatRegExp.source); // to drop the global flag\n\n\t\tObject.defineProperty(String, 'format', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(format) {\n\t\t\t\tfunction padString(str, align, pad) {\n\t\t\t\t\tif (!align) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst plen = Math.abs(align) - str.length;\n\n\t\t\t\t\tif (plen < 1) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\t// const padding = Array(plen + 1).join(pad);\n\t\t\t\t\tconst padding = String(pad).repeat(plen);\n\t\t\t\t\treturn align < 0 ? str + padding : padding + str;\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length < 2) {\n\t\t\t\t\treturn arguments.length === 0 ? '' : format;\n\t\t\t\t}\n\n\t\t\t\tconst args = arguments.length === 2 && Array.isArray(arguments[1])\n\t\t\t\t\t? [...arguments[1]]\n\t\t\t\t\t: Array.prototype.slice.call(arguments, 1);\n\n\t\t\t\tif (args.length === 0) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\tif (!_hasFormatRegExp.test(format)) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t\t_formatRegExp.lastIndex = 0;\n\n\t\t\t\treturn format.replace(_formatRegExp, (match, index, align) => {\n\t\t\t\t\tlet retval = args[index];\n\n\t\t\t\t\tif (retval == null) { // lazy equality for null\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\n\t\t\t\t\twhile (typeof retval === 'function') {\n\t\t\t\t\t\tretval = retval();\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (typeof retval) {\n\t\t\t\t\tcase 'string': /* no-op */ break;\n\t\t\t\t\tcase 'object': retval = JSON.stringify(retval); break;\n\t\t\t\t\tdefault:       retval = String(retval); break;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn padString(retval, !align ? 0 : Number.parseInt(align, 10), ' ');\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t})();\n\n\t/*\n\t\tReturns whether the given string was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn String.prototype.indexOf.apply(this, arguments) !== -1;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given substring was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst needle = String(arguments[0] || '');\n\n\t\t\tif (needle === '') {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tconst indexOf = String.prototype.indexOf;\n\t\t\tconst step    = needle.length;\n\t\t\tlet pos     = Number(arguments[1]) || 0;\n\t\t\tlet count   = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\tpos += step;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the last code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, str.length - 1);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with `delCount` characters replaced with\n\t\t`replacement`, starting at `startAt`.\n\t*/\n\tObject.defineProperty(String.prototype, 'splice', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(startAt, delCount, replacement) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splice called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tlet start = Number(startAt);\n\n\t\t\tif (!Number.isSafeInteger(start)) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\telse if (start < 0) {\n\t\t\t\tstart += length;\n\n\t\t\t\tif (start < 0) {\n\t\t\t\t\tstart = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (start > length) {\n\t\t\t\tstart = length;\n\t\t\t}\n\n\t\t\tlet count = Number(delCount);\n\n\t\t\tif (!Number.isSafeInteger(count) || count < 0) {\n\t\t\t\tcount = 0;\n\t\t\t}\n\n\t\t\tlet res = this.slice(0, start);\n\n\t\t\tif (typeof replacement !== 'undefined') {\n\t\t\t\tres += replacement;\n\t\t\t}\n\n\t\t\tif (start + count < length) {\n\t\t\t\tres += this.slice(start + count);\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns an array of strings, split from the string, or an empty array if the\n\t\tstring is empty.\n\t*/\n\tObject.defineProperty(String.prototype, 'splitOrEmpty', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* [ separator [, limit ]] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splitOrEmpty called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tif (String(this) === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\treturn String.prototype.split.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased,\n\t\taccording to any locale-specific rules.\n\t*/\n\tObject.defineProperty(String.prototype, 'toLocaleUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toLocaleUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toLocaleUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased.\n\t*/\n\tObject.defineProperty(String.prototype, 'toUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, JSON.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDefine `toJSON()` methods on each prototype we wish to support.\n\t*/\n\tObject.defineProperty(Date.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:date)', this.toISOString()];\n\t\t}\n\t});\n\tObject.defineProperty(Function.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\t/*\n\t\t\t\tThe enclosing parenthesis here are necessary to force the function expression code\n\t\t\t\tstring, returned by `this.toString()`, to be evaluated as an expression during\n\t\t\t\trevival.  Without them, the function expression, which is likely nameless, will be\n\t\t\t\tevaluated as a function definition—which will throw a syntax error exception, since\n\t\t\t\tfunction definitions must have a name.\n\t\t\t*/\n\t\t\treturn ['(revive:eval)', `(${this.toString()})`];\n\t\t}\n\t});\n\tObject.defineProperty(Map.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:map)', [...this]];\n\t\t}\n\t});\n\tObject.defineProperty(RegExp.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:eval)', this.toString()];\n\t\t}\n\t});\n\tObject.defineProperty(Set.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:set)', [...this]];\n\t\t}\n\t});\n\n\t/*\n\t\tUtility method to allow users to easily wrap their code in the revive wrapper.\n\t*/\n\tObject.defineProperty(JSON, 'reviveWrapper', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(code, data) {\n\t\t\tif (typeof code !== 'string') {\n\t\t\t\tthrow new TypeError('JSON.reviveWrapper code parameter must be a string');\n\t\t\t}\n\n\t\t\treturn ['(revive:eval)', [code, data]];\n\t\t}\n\t});\n\n\t/*\n\t\tBackup the original `JSON.parse()` and replace it with a revive wrapper aware version.\n\t*/\n\tObject.defineProperty(JSON, '_real_parse', {\n\t\tvalue : JSON.parse\n\t});\n\tObject.defineProperty(JSON, 'parse', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(text, reviver) {\n\t\t\treturn JSON._real_parse(text, (key, val) => {\n\t\t\t\tlet value = val;\n\n\t\t\t\t/*\n\t\t\t\t\tAttempt to revive wrapped values.\n\t\t\t\t*/\n\t\t\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\t\t\tswitch (value[0]) {\n\t\t\t\t\tcase '(revive:set)':\n\t\t\t\t\t\tvalue = new Set(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:map)':\n\t\t\t\t\t\tvalue = new Map(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:date)':\n\t\t\t\t\t\tvalue = new Date(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:eval)':\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/* eslint-disable no-eval */\n\t\t\t\t\t\t\t// For post-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\tif (Array.isArray(value[1])) {\n\t\t\t\t\t\t\t\tconst $ReviveData$ = value[1][1]; // eslint-disable-line no-unused-vars\n\t\t\t\t\t\t\t\tvalue = eval(value[1][0]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// For regular expressions, functions, and pre-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvalue = eval(value[1]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* eslint-enable no-eval */\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* legacy */\n\t\t\t\telse if (typeof value === 'string' && value.slice(0, 10) === '@@revive@@') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = eval(value.slice(10)); // eslint-disable-line no-eval\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\t\t\t\t/* /legacy */\n\n\t\t\t\t/*\n\t\t\t\t\tCall the custom reviver, if specified.\n\t\t\t\t*/\n\t\t\t\tif (typeof reviver === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = reviver(key, value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\n\t\t\t\treturn value;\n\t\t\t});\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, Deprecated.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns whether the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAll called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAll.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAny called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAny.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns a new array consisting of the flattened source array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'flatten', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.flatten called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.flat.call(this, Infinity);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns an array of link titles, parsed from the string.\n\n\t\tNOTE: Unused in SugarCube, only included for compatibility.\n\t*/\n\tObject.defineProperty(String.prototype, 'readBracketedList', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.readBracketedList called on null or undefined');\n\t\t\t}\n\n\t\t\t// RegExp groups: Double-square-bracket quoted | Unquoted.\n\t\t\tconst re    = new RegExp('(?:\\\\[\\\\[((?:\\\\s|\\\\S)*?)\\\\]\\\\])|([^\"\\'\\\\s]\\\\S*)', 'gm');\n\t\t\tconst names = [];\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.exec(this)) !== null) {\n\t\t\t\tif (match[1]) { // double-square-bracket quoted\n\t\t\t\t\tnames.push(match[1]);\n\t\t\t\t}\n\t\t\t\telse if (match[2]) { // unquoted\n\t\t\t\t\tnames.push(match[2]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn names;\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/browser.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Browser = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable max-len */\n\tconst userAgent = navigator.userAgent.toLowerCase();\n\n\tconst winPhone = userAgent.includes('windows phone');\n\tconst isMobile = Object.freeze({\n\t\tAndroid    : !winPhone && userAgent.includes('android'),\n\t\tBlackBerry : /blackberry|bb10/.test(userAgent),\n\t\tiOS        : !winPhone && /ip(?:hone|ad|od)/.test(userAgent),\n\t\tOpera      : !winPhone && (typeof window.operamini === 'object' || userAgent.includes('opera mini')),\n\t\tWindows    : winPhone || /iemobile|wpdesktop/.test(userAgent),\n\n\t\tany() {\n\t\t\treturn isMobile.Android || isMobile.BlackBerry || isMobile.iOS || isMobile.Opera || isMobile.Windows;\n\t\t}\n\t});\n\n\tconst isGecko = !isMobile.Windows && !/khtml|trident|edge/.test(userAgent) && userAgent.includes('gecko');\n\n\tconst isIE      = !userAgent.includes('opera') && /msie|trident/.test(userAgent);\n\tconst ieVersion = isIE\n\t\t? (() => {\n\t\t\tconst ver = /(?:msie\\s+|rv:)(\\d+\\.\\d)/.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\t// opera <= 12: \"opera/9.80 (windows nt 6.1; wow64) presto/2.12.388 version/12.16\"\n\t// opera >= 15: \"mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/28.0.1500.52 safari/537.36 opr/15.0.1147.130\"\n\tconst isOpera      = userAgent.includes('opera') || userAgent.includes(' opr/');\n\tconst operaVersion = isOpera\n\t\t? (() => {\n\t\t\tconst re  = new RegExp(`${/khtml|chrome/.test(userAgent) ? 'opr' : 'version'}\\\\/(\\\\d+\\\\.\\\\d+)`);\n\t\t\tconst ver = re.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\tconst isVivaldi = userAgent.includes('vivaldi');\n\t/* eslint-enable max-len */\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\tuserAgent,\n\t\tisMobile,\n\t\tisGecko,\n\t\tisIE,\n\t\tieVersion,\n\t\tisOpera,\n\t\toperaVersion,\n\t\tisVivaldi\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/has.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Has = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tNOTE: The aggressive try/catch feature tests are necessitated by implementation\n\t\tbugs in various browsers.\n\t*/\n\n\t// Is the `HTMLAudioElement` API available?\n\tconst hasAudioElement = (() => {\n\t\ttry {\n\t\t\treturn typeof document.createElement('audio').canPlayType === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `File` API available?\n\tconst hasFile = (() => {\n\t\ttry {\n\t\t\treturn 'Blob' in window &&\n\t\t\t\t'File' in window &&\n\t\t\t\t'FileList' in window &&\n\t\t\t\t'FileReader' in window &&\n\t\t\t\t!Browser.isMobile.any() &&\n\t\t\t\t(!Browser.isOpera || Browser.operaVersion >= 15);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `geolocation` API available?\n\tconst hasGeolocation = (() => {\n\t\ttry {\n\t\t\treturn 'geolocation' in navigator &&\n\t\t\t\ttypeof navigator.geolocation.getCurrentPosition === 'function' &&\n\t\t\t\ttypeof navigator.geolocation.watchPosition === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `MutationObserver` API available?\n\tconst hasMutationObserver = (() => {\n\t\ttry {\n\t\t\treturn 'MutationObserver' in window &&\n\t\t\t\ttypeof window.MutationObserver === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `performance` API available?\n\tconst hasPerformance = (() => {\n\t\ttry {\n\t\t\treturn 'performance' in window &&\n\t\t\t\ttypeof window.performance.now === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the platform a touch device?\n\tconst hasTouch = (() => {\n\t\ttry {\n\t\t\treturn 'ontouchstart' in window ||\n\t\t\t\t!!window.DocumentTouch &&\n\t\t\t\tdocument instanceof window.DocumentTouch ||\n\t\t\t\t!!navigator.maxTouchPoints ||\n\t\t\t\t!!navigator.msMaxTouchPoints;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the transition end event available and by what name?\n\tconst hasTransitionEndEvent = (() => {\n\t\ttry {\n\t\t\tconst teMap = new Map([\n\t\t\t\t['transition',       'transitionend'],\n\t\t\t\t['MSTransition',     'msTransitionEnd'],\n\t\t\t\t['WebkitTransition', 'webkitTransitionEnd'],\n\t\t\t\t['MozTransition',    'transitionend']\n\t\t\t]);\n\t\t\tconst teKeys = [...teMap.keys()];\n\t\t\tconst el     = document.createElement('div');\n\n\t\t\tfor (let i = 0; i < teKeys.length; ++i) {\n\t\t\t\tif (el.style[teKeys[i]] !== undefined) {\n\t\t\t\t\treturn teMap.get(teKeys[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\taudio              : hasAudioElement,\n\t\tfileAPI            : hasFile,\n\t\tgeolocation        : hasGeolocation,\n\t\tmutationObserver   : hasMutationObserver,\n\t\tperformance        : hasPerformance,\n\t\ttouch              : hasTouch,\n\t\ttransitionEndEvent : hasTransitionEndEvent\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/visibility.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Visibility = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tThere are two versions of the Page Visibility API: First Edition and, the current,\n\t\tSecond Edition (i.e. \"Level 2\").  First Edition is mentioned here only because some\n\t\tolder browsers implement it, rather than the current specification.\n\n\t\tSEE:\n\t\t\tSecond Edition : https://www.w3.org/TR/page-visibility/\n\t\t\tFirst Edition  : https://www.w3.org/TR/2013/REC-page-visibility-20130514/\n\n\t\tNOTE: Generally, all supported browsers change the visibility state when either switching tabs\n\t\twithin the browser or minimizing the browser window.  Exceptions are noted below:\n\t\t\t* IE 9 doesn't support either version of the Page Visibility API.\n\t\t\t* Opera 12 (Presto) doesn't change the visibility state when the browser is minimized.\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'hidden',          // boolean; historical in 2nd edition\n\t\t\t\t\tstateProperty  : 'visibilityState', // string, values: 'hidden', 'visible'; 1st edition had more values\n\t\t\t\t\tchangeEvent    : 'visibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink & WebKit.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'webkitHidden',\n\t\t\t\t\tstateProperty  : 'webkitVisibilityState',\n\t\t\t\t\tchangeEvent    : 'webkitvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'mozHidden',\n\t\t\t\t\tstateProperty  : 'mozVisibilityState',\n\t\t\t\t\tchangeEvent    : 'mozvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 10.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'msHidden',\n\t\t\t\t\tstateProperty  : 'msVisibilityState',\n\t\t\t\t\tchangeEvent    : 'msvisibilitychange'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.hiddenProperty in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getVisibility() {\n\t\treturn vendor && document[vendor.stateProperty] || 'visible';\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor);\n\t}\n\n\tfunction isHidden() {\n\t\t// return Boolean(vendor && document[vendor.stateProperty] === 'hidden');\n\t\treturn Boolean(vendor && document[vendor.hiddenProperty]); // NOTE: Historical, but probably better for 1st edition.\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Functions.\n\t\tvendor    : { get : getVendor },\n\t\tstate     : { get : getVisibility },\n\t\tisEnabled : { value : isEnabled },\n\t\tisHidden  : { value : isHidden },\n\n\t\t// Properties.\n\t\thiddenProperty : { value : vendor && vendor.hiddenProperty },\n\t\tstateProperty  : { value : vendor && vendor.stateProperty },\n\t\tchangeEvent    : { value : vendor && vendor.changeEvent }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/fullscreen.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Fullscreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tSEE:\n\t\t\thttps://fullscreen.spec.whatwg.org\n\t\t\thttps://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'fullscreenEnabled',\n\t\t\t\t\telement     : 'fullscreenElement',\n\t\t\t\t\trequestFn   : 'requestFullscreen',\n\t\t\t\t\texitFn      : 'exitFullscreen',\n\t\t\t\t\tchangeEvent : 'fullscreenchange', // prop: onfullscreenchange\n\t\t\t\t\terrorEvent  : 'fullscreenerror'   // prop: onfullscreenerror\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink, WebKit, & Edge.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'webkitFullscreenEnabled',\n\t\t\t\t\telement     : 'webkitFullscreenElement',\n\t\t\t\t\trequestFn   : 'webkitRequestFullscreen',\n\t\t\t\t\texitFn      : 'webkitExitFullscreen',\n\t\t\t\t\tchangeEvent : 'webkitfullscreenchange',\n\t\t\t\t\terrorEvent  : 'webkitfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'mozFullScreenEnabled',\n\t\t\t\t\telement     : 'mozFullScreenElement',\n\t\t\t\t\trequestFn   : 'mozRequestFullScreen',\n\t\t\t\t\texitFn      : 'mozCancelFullScreen',\n\t\t\t\t\tchangeEvent : 'mozfullscreenchange',\n\t\t\t\t\terrorEvent  : 'mozfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 11.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'msFullscreenEnabled',\n\t\t\t\t\telement     : 'msFullscreenElement',\n\t\t\t\t\trequestFn   : 'msRequestFullscreen',\n\t\t\t\t\texitFn      : 'msExitFullscreen',\n\t\t\t\t\tchangeEvent : 'MSFullscreenChange',\n\t\t\t\t\terrorEvent  : 'MSFullscreenError'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.isEnabled in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************/\n\n\t// Return whether the request and exit fullscreen methods return a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _returnsPromise = (function () {\n\t\t// Cache of whether the request and exit methods return a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _returnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (vendor) {\n\t\t\t\ttry {\n\t\t\t\t\tconst value = document.exitFullscreen();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since we shouldn't be in fullscreen yet,\n\t\t\t\t\t// and we don't actually care about the error, since we just want the return\n\t\t\t\t\t// value, so we consign it to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _returnsPromise;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _selectElement(requestedEl) {\n\t\tlet selectedEl = requestedEl || document.documentElement;\n\n\t\t// Document element scrolling workaround for older browsers.\n\t\tif (\n\t\t\t   selectedEl === document.documentElement\n\t\t\t&& (\n\t\t\t\t   vendor.requestFn === 'msRequestFullscreen'   // IE 11\n\t\t\t\t|| Browser.isOpera && Browser.operaVersion < 15 // Opera 12 (Presto)\n\t\t\t)\n\t\t) {\n\t\t\tselectedEl = document.body;\n\t\t}\n\n\t\treturn selectedEl;\n\t}\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getElement() {\n\t\treturn (vendor || null) && document[vendor.element];\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor && document[vendor.isEnabled]);\n\t}\n\n\tfunction isFullscreen() {\n\t\treturn Boolean(vendor && document[vendor.element]);\n\t}\n\n\tfunction requestFullscreen(options, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (typeof element[vendor.requestFn] !== 'function') {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\t\tif (isFullscreen()) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn element[vendor.requestFn](options);\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_requestFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(element)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen request error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\telement[vendor.requestFn](options);\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction exitFullscreen() {\n\t\tif (!vendor || typeof document[vendor.exitFn] !== 'function') {\n\t\t\treturn Promise.reject(new TypeError('fullscreen not supported'));\n\t\t}\n\t\tif (!isFullscreen()) {\n\t\t\treturn Promise.reject(new TypeError('fullscreen mode not active'));\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn document[vendor.exitFn]();\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_exitFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen exit error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tdocument[vendor.exitFn]();\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction toggleFullscreen(options, requestedEl) {\n\t\treturn isFullscreen() ? exitFullscreen() : requestFullscreen(options, requestedEl);\n\t}\n\n\tfunction onChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.changeEvent, handlerFn);\n\t}\n\n\tfunction offChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.changeEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.changeEvent);\n\t\t}\n\t}\n\n\tfunction onError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.errorEvent, handlerFn);\n\t}\n\n\tfunction offError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.errorEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.errorEvent);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tvendor       : { get : getVendor },\n\t\telement      : { get : getElement },\n\t\tisEnabled    : { value : isEnabled },\n\t\tisFullscreen : { value : isFullscreen },\n\t\trequest      : { value : requestFullscreen },\n\t\texit         : { value : exitFullscreen },\n\t\ttoggle       : { value : toggleFullscreen },\n\t\tonChange     : { value : onChange },\n\t\toffChange    : { value : offChange },\n\t\tonError      : { value : onError },\n\t\toffError     : { value : offError }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/helpers.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, State, Story, Util, Wikifier */\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tclone,\n\tconvertBreaks,\n\tsafeActiveElement,\n\tsetDisplayTitle,\n\tsetPageElement,\n\tthrowError,\n\ttoStringOrDefault\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _getTextContent(source) {\n\t\tconst copy = source.cloneNode(true);\n\t\tconst frag = document.createDocumentFragment();\n\t\tlet node;\n\n\t\twhile ((node = copy.firstChild) !== null) {\n\t\t\t// Insert spaces before various elements.\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tswitch (node.nodeName.toUpperCase()) {\n\t\t\t\tcase 'BR':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'P':\n\t\t\t\t\tfrag.appendChild(document.createTextNode(' '));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfrag.appendChild(node);\n\t\t}\n\n\t\treturn frag.textContent;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a deep copy of the given object.\n\n\t\tNOTE:\n\t\t\t1. `clone()` does not clone functions, however, since function definitions\n\t\t\t   are immutable, the only issues are with expando properties and scope.\n\t\t\t   The former really should not be done.  The latter is problematic either\n\t\t\t   way—damned if you do, damned if you don't.\n\t\t\t2. `clone()` does not maintain referential relationships—e.g. multiple\n\t\t\t   references to the same object will, post-cloning, refer to different\n\t\t\t   equivalent objects; i.e. each reference will receive its own clone\n\t\t\t   of the original object.\n\t*/\n\tfunction clone(orig) {\n\t\t/*\n\t\t\tImmediately return the primitives and functions.\n\t\t*/\n\t\tif (typeof orig !== 'object' || orig === null) {\n\t\t\treturn orig;\n\t\t}\n\n\t\t/*\n\t\t\tUnbox instances of the primitive exemplar objects.\n\t\t*/\n\t\tif (orig instanceof String) {\n\t\t\treturn String(orig);\n\t\t}\n\t\tif (orig instanceof Number) {\n\t\t\treturn Number(orig);\n\t\t}\n\t\tif (orig instanceof Boolean) {\n\t\t\treturn Boolean(orig);\n\t\t}\n\n\t\t/*\n\t\t\tHonor native clone methods.\n\t\t*/\n\t\tif (typeof orig.clone === 'function') {\n\t\t\treturn orig.clone(true);\n\t\t}\n\t\tif (orig.nodeType && typeof orig.cloneNode === 'function') {\n\t\t\treturn orig.cloneNode(true);\n\t\t}\n\n\t\t/*\n\t\t\tCreate a copy of the original object.\n\n\t\t\tNOTE: Each non-generic object that we wish to support must be\n\t\t\texplicitly handled below.\n\t\t*/\n\t\tlet copy;\n\n\t\t// Handle instances of the core supported object types.\n\t\tif (orig instanceof Array) {\n\t\t\tcopy = new Array(orig.length);\n\t\t}\n\t\telse if (orig instanceof Date) {\n\t\t\tcopy = new Date(orig.getTime());\n\t\t}\n\t\telse if (orig instanceof Map) {\n\t\t\tcopy = new Map();\n\t\t\torig.forEach((val, key) => copy.set(key, clone(val)));\n\t\t}\n\t\telse if (orig instanceof RegExp) {\n\t\t\tcopy = new RegExp(orig);\n\t\t}\n\t\telse if (orig instanceof Set) {\n\t\t\tcopy = new Set();\n\t\t\torig.forEach(val => copy.add(clone(val)));\n\t\t}\n\n\t\t// Handle instances of unknown or generic objects.\n\t\telse {\n\t\t\t// We try to ensure that the returned copy has the same prototype as\n\t\t\t// the original, but this will probably produce less than satisfactory\n\t\t\t// results on non-generics.\n\t\t\tcopy = Object.create(Object.getPrototypeOf(orig));\n\t\t}\n\n\t\t/*\n\t\t\tDuplicate the original object's own enumerable properties, which will\n\t\t\tinclude expando properties on non-generic objects.\n\n\t\t\tNOTE: This preserves neither symbol properties nor ES5 property attributes.\n\t\t\tNeither does the delta coding or serialization code, however, so it's not\n\t\t\treally an issue at the moment.\n\t\t*/\n\t\tObject.keys(orig).forEach(name => copy[name] = clone(orig[name]));\n\n\t\treturn copy;\n\t}\n\n\t/*\n\t\tConverts <br> elements to <p> elements within the given node tree.\n\t*/\n\tfunction convertBreaks(source) {\n\t\tconst output = document.createDocumentFragment();\n\t\tlet para = document.createElement('p');\n\t\tlet node;\n\n\t\twhile ((node = source.firstChild) !== null) {\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase 'BR':\n\t\t\t\t\tif (\n\t\t\t\t\t\t   node.nextSibling !== null\n\t\t\t\t\t\t&& node.nextSibling.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t\t&& node.nextSibling.nodeName.toUpperCase() === 'BR'\n\t\t\t\t\t) {\n\t\t\t\t\t\tsource.removeChild(node.nextSibling);\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!para.hasChildNodes()) {\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'ADDRESS':\n\t\t\t\tcase 'ARTICLE':\n\t\t\t\tcase 'ASIDE':\n\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\tcase 'CENTER':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'DL':\n\t\t\t\tcase 'FIGURE':\n\t\t\t\tcase 'FOOTER':\n\t\t\t\tcase 'FORM':\n\t\t\t\tcase 'H1':\n\t\t\t\tcase 'H2':\n\t\t\t\tcase 'H3':\n\t\t\t\tcase 'H4':\n\t\t\t\tcase 'H5':\n\t\t\t\tcase 'H6':\n\t\t\t\tcase 'HEADER':\n\t\t\t\tcase 'HR':\n\t\t\t\tcase 'MAIN':\n\t\t\t\tcase 'NAV':\n\t\t\t\tcase 'OL':\n\t\t\t\tcase 'P':\n\t\t\t\tcase 'PRE':\n\t\t\t\tcase 'SECTION':\n\t\t\t\tcase 'TABLE':\n\t\t\t\tcase 'UL':\n\t\t\t\t\tif (para.hasChildNodes()) {\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.appendChild(node);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpara.appendChild(node);\n\t\t}\n\n\t\tif (para.hasChildNodes()) {\n\t\t\toutput.appendChild(para);\n\t\t}\n\n\t\tsource.appendChild(output);\n\t}\n\n\t/*\n\t\tReturns `document.activeElement` or `null`.\n\t*/\n\tfunction safeActiveElement() {\n\t\t/*\n\t\t\tIE9 contains a bug where trying to access the active element of an iframe's\n\t\t\tparent document (i.e. `window.parent.document.activeElement`) will throw an\n\t\t\texception, so we must allow for an exception to be thrown.\n\n\t\t\tWe could simply return `undefined` here, but since the API's default behavior\n\t\t\tshould be to return `document.body` or `null` when there is no selection, we\n\t\t\tchoose to return `null` in all non-element cases (i.e. whether it returns\n\t\t\t`null` or throws an exception).  Just a bit of normalization.\n\t\t*/\n\t\ttry {\n\t\t\treturn document.activeElement || null;\n\t\t}\n\t\tcatch (ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/*\n\t\tSets the display title.\n\t*/\n\tfunction setDisplayTitle(title) {\n\t\tif (typeof title !== 'string') {\n\t\t\tthrow new TypeError(`story display title must be a string (received: ${Util.getType(title)})`);\n\t\t}\n\n\t\tconst render = document.createDocumentFragment();\n\t\tnew Wikifier(render, title);\n\n\t\tconst text = _getTextContent(render).trim();\n\n\t\t// if (text === '') {\n\t\t// \tthrow new Error('story display title must not render to an empty string or consist solely of whitespace');\n\t\t// }\n\n\t\tdocument.title = Config.passages.displayTitles && State.passage !== '' && State.passage !== Config.passages.start\n\t\t\t? `${State.passage} | ${text}`\n\t\t\t: text;\n\n\t\tconst storyTitle = document.getElementById('story-title');\n\n\t\tif (storyTitle !== null) {\n\t\t\tjQuery(storyTitle).empty().append(render);\n\t\t}\n\t}\n\n\t/*\n\t\tWikifies a passage into a DOM element corresponding to the passed ID and returns the element.\n\t*/\n\tfunction setPageElement(idOrElement, titles, defaultText) {\n\t\tconst el = typeof idOrElement === 'object'\n\t\t\t? idOrElement\n\t\t\t: document.getElementById(idOrElement);\n\n\t\tif (el == null) { // lazy equality for null\n\t\t\treturn null;\n\t\t}\n\n\t\tconst ids = Array.isArray(titles) ? titles : [titles];\n\n\t\tjQuery(el).empty();\n\n\t\tfor (let i = 0, iend = ids.length; i < iend; ++i) {\n\t\t\tif (Story.has(ids[i])) {\n\t\t\t\tnew Wikifier(el, Story.get(ids[i]).processText().trim());\n\t\t\t\treturn el;\n\t\t\t}\n\t\t}\n\n\t\tif (defaultText != null) { // lazy equality for null\n\t\t\tconst text = String(defaultText).trim();\n\n\t\t\tif (text !== '') {\n\t\t\t\tnew Wikifier(el, text);\n\t\t\t}\n\t\t}\n\n\t\treturn el;\n\t}\n\n\t/*\n\t\tAppends an error view to the passed DOM element.\n\t*/\n\tfunction throwError(place, message, source, stack) {\n\t\tconst $wrapper = jQuery(document.createElement('div'));\n\t\tconst $toggle  = jQuery(document.createElement('button'));\n\t\tconst $source  = jQuery(document.createElement('pre'));\n\t\tconst mesg     = `${L10n.get('errorTitle')}: ${message || 'unknown error'} ${Config.saves.version}`;\n\n\t\t$toggle\n\t\t\t.addClass('error-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('errorToggle')\n\t\t\t}, () => {\n\t\t\t\tif ($toggle.hasClass('enabled')) {\n\t\t\t\t\t$toggle.removeClass('enabled');\n\t\t\t\t\t$source.attr({\n\t\t\t\t\t\t'aria-hidden' : true,\n\t\t\t\t\t\thidden        : 'hidden'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$toggle.addClass('enabled');\n\t\t\t\t\t$source.removeAttr('aria-hidden hidden');\n\t\t\t\t}\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('span'))\n\t\t\t.addClass('error')\n\t\t\t.text(mesg)\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('code'))\n\t\t\t.text(source)\n\t\t\t.appendTo($source);\n\t\t$source\n\t\t\t.addClass('error-source')\n\t\t\t.attr({\n\t\t\t\t'aria-hidden' : true,\n\t\t\t\thidden        : 'hidden'\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tif (stack) {\n\t\t\tconst lines = stack.split('\\n');\n\t\t\tfor (const ll of lines) {\n\t\t\t\tconst div = document.createElement('div');\n\t\t\t\tdiv.append(ll.replace(/file:.*\\//, '<path>/'));\n\t\t\t\t$source.append(div);\n\t\t\t}\n\t\t}\n\t\t$wrapper\n\t\t\t.addClass('error-view')\n\t\t\t.appendTo(place);\n\n\t\tconsole.warn(`${mesg}\\n\\t${source.replace(/\\n/g, '\\n\\t')}`);\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the simple string representation of the passed value or, if there is none,\n\t\tthe passed default value.\n\t*/\n\tfunction toStringOrDefault(value, defValue) {\n\t\tconst tSOD = toStringOrDefault;\n\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\t// TODO: Perhaps NaN should be printed instead?\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\tif (value === null) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\telse if (Array.isArray(value)) {\n\t\t\t\treturn value.map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Set) {\n\t\t\t\treturn [...value].map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Map) {\n\t\t\t\tconst result = [...value].map(([key, val]) => `${tSOD(key, defValue)} \\u2192 ${tSOD(val, defValue)}`);\n\t\t\t\treturn `{\\u202F${result.join(', ')}\\u202F}`;\n\t\t\t}\n\t\t\telse if (value instanceof Date) {\n\t\t\t\treturn value.toLocaleString();\n\t\t\t}\n\t\t\telse if (typeof value.toString === 'function') {\n\t\t\t\treturn value.toString();\n\t\t\t}\n\t\t\treturn Object.prototype.toString.call(value);\n\n\t\tcase 'function':\n\t\tcase 'undefined':\n\t\t\treturn defValue;\n\t\t}\n\n\t\treturn String(value);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tclone             : { value : clone },\n\t\tconvertBreaks     : { value : convertBreaks },\n\t\tsafeActiveElement : { value : safeActiveElement },\n\t\tsetDisplayTitle   : { value : setDisplayTitle },\n\t\tsetPageElement    : { value : setPageElement },\n\t\tthrowError        : { value : throwError },\n\t\ttoStringOrDefault : { value : toStringOrDefault }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/jquery-plugins.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Wikifier, errorPrologRegExp, safeActiveElement */\n\n/*\n\tWAI-ARIA methods plugin.\n\n\t`<jQuery>.ariaClick([options,] handler)`\n\t    Makes the target element(s) WAI-ARIA compatible clickables.\n\n\t`<jQuery>.ariaDisabled(state)`\n\t    Changes the disabled state of the target WAI-ARIA-compatible clickable element(s).\n\n\t`<jQuery>.ariaIsDisabled()`\n\t    Checks the disabled status of the target WAI-ARIA-compatible clickable element(s).\n*/\n(() => {\n\t'use strict';\n\n\t/*\n\t\tEvent handler & utility functions.\n\n\t\tNOTE: Do not replace the anonymous functions herein with arrow functions.\n\t*/\n\tfunction onKeypressFn(ev) {\n\t\t// 13 is Enter/Return, 32 is Space.\n\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\tev.preventDefault();\n\n\t\t\t// To allow delegation, attempt to trigger the event on `document.activeElement`,\n\t\t\t// if possible, elsewise on `this`.\n\t\t\tjQuery(safeActiveElement() || this).trigger('click');\n\t\t}\n\t}\n\n\tfunction onClickFnWrapper(fn) {\n\t\treturn function () {\n\t\t\tconst $this = jQuery(this);\n\n\t\t\tconst dataPassage = $this.attr('data-passage');\n\t\t\tconst initialDataPassage = window && window.SugarCube && window.SugarCube.State && window.SugarCube.State.passage;\n\t\t\tconst savedYOffset = window.pageYOffset;\n\n\t\t\t// Toggle \"aria-pressed\" status, if the attribute exists.\n\t\t\tif ($this.is('[aria-pressed]')) {\n\t\t\t\t$this.attr('aria-pressed', $this.attr('aria-pressed') === 'true' ? 'false' : 'true');\n\t\t\t}\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\n\t\t\tconst doJump = function(){ window.scrollTo(0, savedYOffset); }\n\t\t\tif ( dataPassage && (window.lastDataPassageLink === dataPassage || initialDataPassage === dataPassage))\n\t\t\t\tdoJump();\n\t\t\twindow.lastDataPassageLink = dataPassage;\n\t\t};\n\t}\n\n\tfunction oneClickFnWrapper(fn) {\n\t\treturn onClickFnWrapper(function () {\n\t\t\t// Remove both event handlers (keypress & click) and the other components.\n\t\t\tjQuery(this)\n\t\t\t\t.off('.aria-clickable')\n\t\t\t\t.removeAttr('tabindex aria-controls aria-pressed')\n\t\t\t\t.not('a,button')\n\t\t\t\t.removeAttr('role')\n\t\t\t\t.end()\n\t\t\t\t.filter('button')\n\t\t\t\t.prop('disabled', true);\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\t\t});\n\t}\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaClick()` method.\n\t\t*/\n\t\tariaClick(options, handler) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tlet opts = options;\n\t\t\tlet fn   = handler;\n\n\t\t\tif (fn == null) { // lazy equality for null\n\t\t\t\tfn   = opts;\n\t\t\t\topts = undefined;\n\t\t\t}\n\n\t\t\topts = jQuery.extend({\n\t\t\t\tnamespace : undefined,\n\t\t\t\tone       : false,\n\t\t\t\tselector  : undefined,\n\t\t\t\tdata      : undefined,\n\t\t\t\tcontrols  : undefined,\n\t\t\t\tpressed   : undefined,\n\t\t\t\tlabel     : undefined\n\t\t\t}, opts);\n\n\t\t\tif (typeof opts.namespace !== 'string') {\n\t\t\t\topts.namespace = '';\n\t\t\t}\n\t\t\telse if (opts.namespace[0] !== '.') {\n\t\t\t\topts.namespace = `.${opts.namespace}`;\n\t\t\t}\n\n\t\t\tif (typeof opts.pressed === 'boolean') {\n\t\t\t\topts.pressed = opts.pressed ? 'true' : 'false';\n\t\t\t}\n\n\t\t\t// Set `type` to `button` to suppress \"submit\" semantics, for <button> elements.\n\t\t\tthis.filter('button').prop('type', 'button');\n\n\t\t\t// Set `role` to `button`, for non-<a>/-<button> elements.\n\t\t\tthis.not('a,button').attr('role', 'button');\n\n\t\t\t// Set `tabindex` to `0` to make them focusable (unnecessary on <button> elements, but it doesn't hurt).\n\t\t\tthis.attr('tabindex', 0);\n\n\t\t\t// Set `aria-controls`.\n\t\t\tif (opts.controls != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-controls', opts.controls);\n\t\t\t}\n\n\t\t\t// Set `aria-pressed`.\n\t\t\tif (opts.pressed != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-pressed', opts.pressed);\n\t\t\t}\n\n\t\t\t// Set `aria-label` and `title`.\n\t\t\tif (opts.label != null) { // lazy equality for null\n\t\t\t\tthis.attr({\n\t\t\t\t\t'aria-label' : opts.label,\n\t\t\t\t\ttitle        : opts.label\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Set the keypress handlers, for non-<button> elements.\n\t\t\t// NOTE: For the single-use case, the click handler will also remove this handler.\n\t\t\tthis.not('button').on(\n\t\t\t\t`keypress.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\tonKeypressFn\n\t\t\t);\n\n\t\t\t// Set the click handlers.\n\t\t\t// NOTE: To ensure both handlers are properly removed, `one()` must not be used here.\n\t\t\tthis.on(\n\t\t\t\t`click.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\topts.data,\n\t\t\t\topts.one ? oneClickFnWrapper(fn) : onClickFnWrapper(fn)\n\t\t\t);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaDisabled()` method.\n\t\t*/\n\t\tariaDisabled(disable) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE: We use `<jQuery>.each()` callbacks to invoke the `<Element>.setAttribute()`\n\t\t\t\tmethods in the following because the `<jQuery>.attr()` method does not allow you\n\t\t\t\tto set a content attribute without a value, which is recommended for boolean\n\t\t\t\tcontent attributes by the HTML specification.\n\t\t\t*/\n\n\t\t\tconst $nonDisableable = this.not('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\t\t\tconst $disableable    = this.filter('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\n\t\t\tif (disable) {\n\t\t\t\t// Add boolean content attribute `disabled` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.setAttribute('disabled', '');\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `true` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = true;\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Remove content attributes `disabled` and `aria-disabled`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.removeAttribute('disabled');\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `false` and remove content attribute `aria-disabled`,\n\t\t\t\t// for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = false;\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaIsDisabled()` method.\n\t\t*/\n\t\tariaIsDisabled() {\n\t\t\t// Check content attribute `disabled`.\n\t\t\t//\n\t\t\t// NOTE: We simply check the `disabled` content attribute for all elements\n\t\t\t// since we have to check it for non-disableable elements and it may also\n\t\t\t// be used for disableable elements since their `disabled` IDL attribute\n\t\t\t// is required to reflect the status of their `disabled` content attribute,\n\t\t\t// and vice versa, by the HTML specification.\n\t\t\t// return this.toArray().some(el => el.hasAttribute('disabled'));\n\t\t\treturn this.is('[disabled]');\n\t\t}\n\t});\n})();\n\n/*\n\tWikifier methods plugin.\n\n\t`jQuery.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s), as directed by the given options.\n\n\t`jQuery.wiki(sources…)`\n\t    Wikifies the given content source(s).\n\n\t`<jQuery>.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s), as directed by the given options.\n\n\t`<jQuery>.wiki(sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s).\n*/\n(() => {\n\t'use strict';\n\n\tjQuery.extend({\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out, if there are no content sources.\n\t\t\tif (sources.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\tthis.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out if there are no target element(s) or content sources.\n\t\t\tif (this.length === 0 || sources.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Append the fragment to the target element(s).\n\t\t\tthis.append(frag);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\treturn this.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/util.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, Scripting */\n\nvar Util = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tType Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value yielded by `typeof` (for primitives), the `@@toStringTag`\n\t\tinternal property (for objects), and `'null'` for `null`.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot for objects.\n\t*/\n\tfunction utilGetType(obj) {\n\t\tif (obj === null) { return 'null'; }\n\n\t\tconst baseType = typeof obj;\n\t\treturn baseType === 'object'\n\t\t\t? Object.prototype.toString.call(obj).slice(8, -1)\n\t\t\t: baseType;\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a boolean or one of the strings \"true\"\n\t\tor \"false\".\n\t*/\n\tfunction utilIsBoolean(obj) {\n\t\treturn typeof obj === 'boolean' || typeof obj === 'string' && (obj === 'true' || obj === 'false');\n\t}\n\n\t/*\n\t\tReturns whether the passed value is iterable.\n\t*/\n\tfunction utilIsIterable(obj) {\n\t\treturn obj != null && typeof obj[Symbol.iterator] === 'function'; // lazy equality for null\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a finite number or a numeric string which\n\t\tyields a finite number when parsed.\n\t*/\n\tfunction utilIsNumeric(obj) {\n\t\tlet num;\n\n\t\tswitch (typeof obj) {\n\t\tcase 'number':\n\t\t\tnum = obj;\n\t\t\tbreak;\n\n\t\tcase 'string':\n\t\t\tnum = Number(obj);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !Number.isNaN(num) && Number.isFinite(num);\n\t}\n\n\t/*\n\t\tReturns whether the passed values pass a SameValueZero comparison.\n\n\t\tSEE: http://ecma-international.org/ecma-262/8.0/#sec-samevaluezero\n\t*/\n\tfunction utilSameValueZero(a, b) {\n\t\t/*\n\t\t\tNOTE: This comparison could also be implemented thus:\n\n\t\t\t\t```\n\t\t\t\ta === b ||\n\t\t\t\ttypeof a === 'number' && typeof b === 'number' &&\n\t\t\t\tNumber.isNaN(a) && Number.isNaN(b)\n\t\t\t\t```\n\n\t\t\tThat's needlessly verbose, however, as `NaN` is the only value in\n\t\t\tthe language which is not reflexive.\n\t\t*/\n\t\treturn a === b || a !== a && b !== b;\n\t}\n\n\t/*\n\t\tReturns a pseudo-enumeration created from the given Array, Map, Set, or generic object.\n\t*/\n\tfunction utilToEnum(obj) {\n\t\tconst pEnum = Object.create(null);\n\n\t\tif (obj instanceof Array) {\n\t\t\tobj.forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Set) {\n\t\t\t// NOTE: Use `<Array>.forEach()` here rather than `<Set>.forEach()`\n\t\t\t// as the latter does not provide the indices we require.\n\t\t\tArray.from(obj).forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Map) {\n\t\t\tobj.forEach((val, key) => pEnum[String(key)] = val);\n\t\t}\n\t\telse if (\n\t\t\t   typeof obj === 'object'\n\t\t\t&& obj !== null\n\t\t\t&& Object.getPrototypeOf(obj) === Object.prototype\n\t\t) {\n\t\t\tObject.assign(pEnum, obj);\n\t\t}\n\t\telse {\n\t\t\tthrow new TypeError('Util.toEnum obj parameter must be an Array, Map, Set, or generic object');\n\t\t}\n\n\t\treturn Object.freeze(pEnum);\n\t}\n\n\t/*\n\t\tReturns the value of the `@@toStringTag` property of the given object.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot.\n\t*/\n\tfunction utilToStringTag(obj) {\n\t\treturn Object.prototype.toString.call(obj).slice(8, -1);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tString Encoding Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a trimmed and encoded slug of the passed string that should be safe\n\t\tfor use as a DOM ID or class name.\n\n\t\tNOTE: The range of illegal characters consists of: C0 controls, space, exclamation,\n\t\tdouble quote, number, dollar, percent, ampersand, single quote, left paren, right\n\t\tparen, asterisk, plus, comma, hyphen, period, forward slash, colon, semi-colon,\n\t\tless-than, equals, greater-than, question, at, left bracket, backslash, right\n\t\tbracket, caret, backquote/grave, left brace, pipe/vertical-bar, right brace, tilde,\n\t\tdelete, C1 controls.\n\t*/\n\tconst _illegalSlugCharsRe = /[\\x00-\\x20!-/:-@[-^`{-\\x9f]+/g; // eslint-disable-line no-control-regex\n\t/* legacy */\n\tconst _isInvalidSlugRe = /^-*$/; // Matches the empty string or one comprised solely of hyphens.\n\t/* /legacy */\n\n\tfunction utilSlugify(str) {\n\t\tconst base = String(str).trim();\n\n\t\t/* legacy */\n\t\tconst _legacy = base\n\t\t\t.replace(/[^\\w\\s\\u2013\\u2014-]+/g, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-')\n\t\t\t.toLocaleLowerCase();\n\n\t\tif (!_isInvalidSlugRe.test(_legacy)) {\n\t\t\treturn _legacy;\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn base\n\t\t\t.replace(_illegalSlugCharsRe, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-');\n\n\t\t// For v3.\n\t\t// return base.replace(_illegalSlugCharsRe, '-');\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Escapes the five primary HTML special characters, the backquote,\n\t\tand SugarCube markup metacharacters.\n\t*/\n\tconst _markupCharsRe    = /[!\"#$&'*\\-/<=>?@[\\\\\\]^_`{|}~]/g;\n\tconst _hasMarkupCharsRe = new RegExp(_markupCharsRe.source); // to drop the global flag\n\tconst _markupCharsMap   = utilToEnum({\n\t\t/* eslint-disable quote-props */\n\t\t'!'  : '&#33;',\n\t\t'\"'  : '&quot;',\n\t\t'#'  : '&#35;',\n\t\t'$'  : '&#36;',\n\t\t'&'  : '&amp;',\n\t\t\"'\"  : '&#39;',\n\t\t'*'  : '&#42;',\n\t\t'-'  : '&#45;',\n\t\t'/'  : '&#47;',\n\t\t'<'  : '&lt;',\n\t\t'='  : '&#61;',\n\t\t'>'  : '&gt;',\n\t\t'?'  : '&#63;',\n\t\t'@'  : '&#64;',\n\t\t'['  : '&#91;',\n\t\t'\\\\' : '&#92;',\n\t\t']'  : '&#93;',\n\t\t'^'  : '&#94;',\n\t\t'_'  : '&#95;',\n\t\t'`'  : '&#96;',\n\t\t'{'  : '&#123;',\n\t\t'|'  : '&#124;',\n\t\t'}'  : '&#125;',\n\t\t'~'  : '&#126;'\n\t\t/* eslint-enable quote-props */\n\t});\n\n\tfunction utilEscapeMarkup(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasMarkupCharsRe.test(val)\n\t\t\t? val.replace(_markupCharsRe, ch => _markupCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Only escapes the five primary special characters and the backquote.\n\t*/\n\tconst _htmlCharsRe    = /[&<>\"'`]/g;\n\tconst _hasHtmlCharsRe = new RegExp(_htmlCharsRe.source); // to drop the global flag\n\tconst _htmlCharsMap   = utilToEnum({\n\t\t'&' : '&amp;',\n\t\t'<' : '&lt;',\n\t\t'>' : '&gt;',\n\t\t'\"' : '&quot;',\n\t\t\"'\" : '&#39;',\n\t\t'`' : '&#96;'\n\t});\n\n\tfunction utilEscape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasHtmlCharsRe.test(val)\n\t\t\t? val.replace(_htmlCharsRe, ch => _htmlCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns a decoded version of the passed entity encoded string.\n\n\t\tNOTE: The extended replacement set here, in contrast to `utilEscape()`,\n\t\tis required due to observed stupidity from various sources.\n\t*/\n\tconst _escapedHtmlRe    = /&(?:amp|#38|#x26|lt|#60|#x3c|gt|#62|#x3e|quot|#34|#x22|apos|#39|#x27|#96|#x60);/gi;\n\tconst _hasEscapedHtmlRe = new RegExp(_escapedHtmlRe.source, 'i'); // to drop the global flag\n\tconst _escapedHtmlMap   = utilToEnum({\n\t\t'&amp;'  : '&', // ampersand (HTML character entity, XML predefined entity)\n\t\t'&#38;'  : '&', // ampersand (decimal numeric character reference)\n\t\t'&#x26;' : '&', // ampersand (hexadecimal numeric character reference)\n\t\t'&lt;'   : '<', // less-than (HTML character entity, XML predefined entity)\n\t\t'&#60;'  : '<', // less-than (decimal numeric character reference)\n\t\t'&#x3c;' : '<', // less-than (hexadecimal numeric character reference)\n\t\t'&gt;'   : '>', // greater-than (HTML character entity, XML predefined entity)\n\t\t'&#62;'  : '>', // greater-than (decimal numeric character reference)\n\t\t'&#x3e;' : '>', // greater-than (hexadecimal numeric character reference)\n\t\t'&quot;' : '\"', // double quote (HTML character entity, XML predefined entity)\n\t\t'&#34;'  : '\"', // double quote (decimal numeric character reference)\n\t\t'&#x22;' : '\"', // double quote (hexadecimal numeric character reference)\n\t\t'&apos;' : \"'\", // apostrophe (XML predefined entity)\n\t\t'&#39;'  : \"'\", // apostrophe (decimal numeric character reference)\n\t\t'&#x27;' : \"'\", // apostrophe (hexadecimal numeric character reference)\n\t\t'&#96;'  : '`', // backquote (decimal numeric character reference)\n\t\t'&#x60;' : '`'  // backquote (hexadecimal numeric character reference)\n\t});\n\n\tfunction utilUnescape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasEscapedHtmlRe.test(val)\n\t\t\t? val.replace(_escapedHtmlRe, entity => _escapedHtmlMap[entity.toLowerCase()])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Returns the individual code units of invalid surrogate pairs as-is.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction utilCharAndPosAt(text, position) {\n\t\tconst str  = String(text);\n\t\tconst pos  = Math.trunc(position);\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\tconst retval = {\n\t\t\tchar  : str.charAt(pos),\n\t\t\tstart : pos,\n\t\t\tend   : pos\n\t\t};\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tretval.char = retval.char + str.charAt(nextPos);\n\t\t\tretval.end = nextPos;\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tretval.char = str.charAt(prevPos) + retval.char;\n\t\tretval.start = prevPos;\n\t\treturn retval;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTime Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of milliseconds elapsed since a reference epoch.\n\n\t\tNOTE: Use the Performance API, if available, elsewise use Date as a\n\t\tfailover.  The Performance API is preferred for its monotonic clock—\n\t\tmeaning, it's not subject to the vagaries of timezone changes and leap\n\t\tperiods, as is Date.\n\t*/\n\tconst _nowSource = Has.performance ? performance : Date;\n\n\tfunction utilNow() {\n\t\treturn _nowSource.now();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tConversion Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of miliseconds represented by the passed CSS time string.\n\t*/\n\tconst _cssTimeRe = /^([+-]?(?:\\d*\\.)?\\d+)([Mm]?[Ss])$/;\n\n\tfunction utilFromCssTime(cssTime) {\n\t\tconst match = _cssTimeRe.exec(String(cssTime));\n\n\t\tif (match === null) {\n\t\t\tthrow new SyntaxError(`invalid time value syntax: \"${cssTime}\"`);\n\t\t}\n\n\t\tlet msec = Number(match[1]);\n\n\t\tif (match[2].length === 1) {\n\t\t\tmsec *= 1000;\n\t\t}\n\n\t\tif (Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tthrow new RangeError(`invalid time value: \"${cssTime}\"`);\n\t\t}\n\n\t\treturn msec;\n\t}\n\n\t/*\n\t\tReturns the CSS time string represented by the passed number of milliseconds.\n\t*/\n\tfunction utilToCssTime(msec) {\n\t\tif (typeof msec !== 'number' || Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tlet what;\n\n\t\t\tswitch (typeof msec) {\n\t\t\tcase 'string':\n\t\t\t\twhat = `\"${msec}\"`;\n\t\t\t\tbreak;\n\n\t\t\tcase 'number':\n\t\t\t\twhat = String(msec);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\twhat = utilToStringTag(msec);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow new Error(`invalid milliseconds: ${what}`);\n\t\t}\n\n\t\treturn `${msec}ms`;\n\t}\n\n\t/*\n\t\tReturns the DOM property name represented by the passed CSS property name.\n\t*/\n\tfunction utilFromCssProperty(cssName) {\n\t\tif (!cssName.includes('-')) {\n\t\t\tswitch (cssName) {\n\t\t\tcase 'bgcolor': return 'backgroundColor';\n\t\t\tcase 'float':   return 'cssFloat';\n\t\t\tdefault:        return cssName;\n\t\t\t}\n\t\t}\n\n\t\t// Strip the leading hyphen from the `-ms-` vendor prefix, so it stays lowercased.\n\t\tconst normalized = cssName.slice(0, 4) === '-ms-' ? cssName.slice(1) : cssName;\n\n\t\treturn normalized\n\t\t\t.split('-')\n\t\t\t.map((part, i) => i === 0 ? part : part.toUpperFirst())\n\t\t\t.join('');\n\t}\n\n\t/*\n\t\tReturns an object containing the component properties parsed from the passed URL.\n\t*/\n\tfunction utilParseUrl(url) {\n\t\tconst el       = document.createElement('a');\n\t\tconst queryObj = Object.create(null);\n\n\t\t// Let the `<a>` element parse the URL.\n\t\tel.href = url;\n\n\t\t// Populate the `queryObj` object with the query string attributes.\n\t\tif (el.search) {\n\t\t\tel.search\n\t\t\t\t.replace(/^\\?/, '')\n\t\t\t\t.splitOrEmpty(/(?:&(?:amp;)?|;)/)\n\t\t\t\t.forEach(query => {\n\t\t\t\t\tconst [key, value] = query.split('=');\n\t\t\t\t\tqueryObj[key] = value;\n\t\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tCaveats by browser:\n\t\t\t\tEdge and Internet Explorer (≥8) do not support authentication\n\t\t\t\tinformation within a URL at all and will throw a security exception\n\t\t\t\ton *any* property access if it's included.\n\n\t\t\t\tInternet Explorer does not include the leading forward slash on\n\t\t\t\t`pathname` when required.\n\n\t\t\t\tOpera (Presto) strips the authentication information from `href`\n\t\t\t\tand does not supply `username` or `password`.\n\n\t\t\t\tSafari (ca. v5.1.x) does not supply `username` or `password` and\n\t\t\t\tpeforms URI decoding on `pathname`.\n\t\t*/\n\n\t\t// Patch for IE not including the leading slash on `pathname` when required.\n\t\tconst pathname = el.host && el.pathname[0] !== '/' ? `/${el.pathname}` : el.pathname;\n\n\t\treturn {\n\t\t\t// The full URL that was originally parsed.\n\t\t\thref : el.href,\n\n\t\t\t// The request protocol, lowercased.\n\t\t\tprotocol : el.protocol,\n\n\t\t\t// // The full authentication information.\n\t\t\t// auth : el.username || el.password // eslint-disable-line no-nested-ternary\n\t\t\t// \t? `${el.username}:${el.password}`\n\t\t\t// \t: typeof el.username === 'string' ? '' : undefined,\n\t\t\t//\n\t\t\t// // The username portion of the auth info.\n\t\t\t// username : el.username,\n\t\t\t//\n\t\t\t// // The password portion of the auth info.\n\t\t\t// password : el.password,\n\n\t\t\t// The full host information, including port number, lowercased.\n\t\t\thost : el.host,\n\n\t\t\t// The hostname portion of the host info, lowercased.\n\t\t\thostname : el.hostname,\n\n\t\t\t// The port number portion of the host info.\n\t\t\tport : el.port,\n\n\t\t\t// The full path information, including query info.\n\t\t\tpath : `${pathname}${el.search}`,\n\n\t\t\t// The pathname portion of the path info.\n\t\t\tpathname,\n\n\t\t\t// The query string portion of the path info, including the leading question mark.\n\t\t\tquery  : el.search,\n\t\t\tsearch : el.search,\n\n\t\t\t// The attributes portion of the query string, parsed into an object.\n\t\t\tqueries  : queryObj,\n\t\t\tsearches : queryObj,\n\n\t\t\t// The fragment string, including the leading hash/pound sign.\n\t\t\thash : el.hash\n\t\t};\n\t}\n\n\t/*\n\t\tReturns a new exception based on the given exception.\n\n\t\tNOTE: Mostly useful for making a standard JavaScript exception type copy\n\t\tof a host exception type—e.g. `DOMException` → `Error`.\n\t*/\n\tfunction utilNewExceptionFrom(original, exceptionType, override) {\n\t\tif (typeof original !== 'object' || original === null) {\n\t\t\tthrow new Error('Util.newExceptionFrom original parameter must be an object');\n\t\t}\n\t\tif (typeof exceptionType !== 'function') {\n\t\t\tthrow new Error('Util.newExceptionFrom exceptionType parameter must be an error type constructor');\n\t\t}\n\n\t\tconst ex = new exceptionType(original.message); // eslint-disable-line new-cap\n\n\t\tif (typeof original.name !== 'undefined') {\n\t\t\tex.name = original.name;\n\t\t}\n\t\tif (typeof original.code !== 'undefined') {\n\t\t\tex.code = original.code;\n\t\t}\n\t\tif (typeof original.columnNumber !== 'undefined') {\n\t\t\tex.columnNumber = original.columnNumber;\n\t\t}\n\t\tif (typeof original.description !== 'undefined') {\n\t\t\tex.description = original.description;\n\t\t}\n\t\tif (typeof original.fileName !== 'undefined') {\n\t\t\tex.fileName = original.fileName;\n\t\t}\n\t\tif (typeof original.lineNumber !== 'undefined') {\n\t\t\tex.lineNumber = original.lineNumber;\n\t\t}\n\t\tif (typeof original.number !== 'undefined') {\n\t\t\tex.number = original.number;\n\t\t}\n\t\tif (typeof original.stack !== 'undefined') {\n\t\t\tex.stack = original.stack;\n\t\t}\n\n\t\tconst overrideType = typeof override;\n\n\t\tif (overrideType !== 'undefined') {\n\t\t\tif (overrideType === 'object' && override !== null) {\n\t\t\t\tObject.assign(ex, override);\n\t\t\t}\n\t\t\telse if (overrideType === 'string') {\n\t\t\t\tex.message = override;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('Util.newExceptionFrom override parameter must be an object or string');\n\t\t\t}\n\t\t}\n\n\t\treturn ex;\n\t}\n\n\t/*\n\t\tReturns a sanitized version of the passed `KeyboardEvent.key` value from\n\t\tprevious incarnations of the specification that should better reflect the\n\t\tcurrent incarnation.\n\t*/\n\tconst utilScrubEventKey = (() => {\n\t\tlet separatorKey;\n\t\tlet decimalKey;\n\n\t\t// Attempt to determine the player's 'Separator' and 'Decimal' key values\n\t\t// based on their current locale.\n\t\tif (typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function') {\n\t\t\tconst match = new Intl.NumberFormat().format(111111.5).match(/(\\D*)\\d+(\\D*)/);\n\n\t\t\tif (match) {\n\t\t\t\tseparatorKey = match[1];\n\t\t\t\tdecimalKey   = match[2];\n\t\t\t}\n\t\t}\n\n\t\t// Failover to US-centric values, if using `Intl.NumberFormat` failed.\n\t\tif (!separatorKey && !decimalKey) {\n\t\t\tseparatorKey = ',';\n\t\t\tdecimalKey   = '.';\n\t\t}\n\n\t\t// Maps older `KeyboardEvent.key` values to more current/correct ones.\n\t\tfunction utilScrubEventKey(key) {\n\t\t\tswitch (key) {\n\t\t\t// case 'OS':                 return 'Meta'; // Unreliable.\n\t\t\tcase 'Scroll':             return 'ScrollLock';\n\t\t\tcase 'Spacebar':           return '\\x20';\n\t\t\tcase 'Left':               return 'ArrowLeft';\n\t\t\tcase 'Right':              return 'ArrowRight';\n\t\t\tcase 'Up':                 return 'ArrowUp';\n\t\t\tcase 'Down':               return 'ArrowDown';\n\t\t\tcase 'Del':                return 'Delete';\n\t\t\tcase 'Crsel':              return 'CrSel';\n\t\t\tcase 'Exsel':              return 'ExSel';\n\t\t\tcase 'Esc':                return 'Escape';\n\t\t\tcase 'Apps':               return 'ContextMenu';\n\t\t\tcase 'Nonconvert':         return 'NonConvert';\n\t\t\tcase 'MediaNextTrack':     return 'MediaTrackNext';\n\t\t\tcase 'MediaPreviousTrack': return 'MediaTrackPrevious';\n\t\t\tcase 'VolumeUp':           return 'AudioVolumeUp';\n\t\t\tcase 'VolumeDown':         return 'AudioVolumeDown';\n\t\t\tcase 'VolumeMute':         return 'AudioVolumeMute';\n\t\t\tcase 'Zoom':               return 'ZoomToggle';\n\t\t\tcase 'SelectMedia':        /* see below */\n\t\t\tcase 'MediaSelect':        return 'LaunchMediaPlayer';\n\t\t\tcase 'Add':                return '+';\n\t\t\tcase 'Divide':             return '/';\n\t\t\tcase 'Multiply':           return '*';\n\t\t\tcase 'Subtract':           return '-';\n\t\t\tcase 'Decimal':            return decimalKey;\n\t\t\tcase 'Separator':          return separatorKey;\n\t\t\t}\n\n\t\t\treturn key;\n\t\t}\n\n\t\treturn utilScrubEventKey;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tType Functions.\n\t\t*/\n\t\tgetType       : { value : utilGetType },\n\t\tisBoolean     : { value : utilIsBoolean },\n\t\tisIterable    : { value : utilIsIterable },\n\t\tisNumeric     : { value : utilIsNumeric },\n\t\tsameValueZero : { value : utilSameValueZero },\n\t\ttoEnum        : { value : utilToEnum },\n\t\ttoStringTag   : { value : utilToStringTag },\n\n\t\t/*\n\t\t\tString Encoding Functions.\n\t\t*/\n\t\tslugify      : { value : utilSlugify },\n\t\tescapeMarkup : { value : utilEscapeMarkup },\n\t\tescape       : { value : utilEscape },\n\t\tunescape     : { value : utilUnescape },\n\t\tcharAndPosAt : { value : utilCharAndPosAt },\n\n\t\t/*\n\t\t\tTime Functions.\n\t\t*/\n\t\tnow : { value : utilNow },\n\n\t\t/*\n\t\t\tConversion Functions.\n\t\t*/\n\t\tfromCssTime      : { value : utilFromCssTime },\n\t\ttoCssTime        : { value : utilToCssTime },\n\t\tfromCssProperty  : { value : utilFromCssProperty },\n\t\tparseUrl         : { value : utilParseUrl },\n\t\tnewExceptionFrom : { value : utilNewExceptionFrom },\n\t\tscrubEventKey    : { value : utilScrubEventKey },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\trandom         : { value : Math.random },\n\t\tentityEncode   : { value : utilEscape },\n\t\tentityDecode   : { value : utilUnescape },\n\t\tevalExpression : { value : (...args) => Scripting.evalJavaScript(...args) }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) }  // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/simplestore.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar SimpleStore = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// In-order list of database adapters.\n\tconst _adapters = [];\n\n\t// The initialized adapter.\n\tlet _initialized = null;\n\n\n\t/*******************************************************************************************************************\n\t\tSimpleStore Functions.\n\t*******************************************************************************************************************/\n\tfunction storeCreate(storageId, persistent) {\n\t\tif (_initialized) {\n\t\t\treturn _initialized.create(storageId, persistent);\n\t\t}\n\n\t\t// Return the first adapter which successfully initializes, elsewise throw an exception.\n\t\tfor (let i = 0; i < _adapters.length; ++i) {\n\t\t\tif (_adapters[i].init(storageId, persistent)) {\n\t\t\t\t_initialized = _adapters[i];\n\t\t\t\treturn _initialized.create(storageId, persistent);\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error('no valid storage adapters found');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tAdapters List.\n\n\t\t\tTODO: This should probably have a getter, rather than being exported directly.\n\t\t*/\n\t\tadapters : { value : _adapters },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tcreate : { value : storeCreate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/FCHost.Storage.js\n\n\tCopyright © 2013–2019 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_FCHostStorageAdapter Class.\n        Note that FCHost is only intended for a single document, so we ignore both prefixing and storageID\n\t*******************************************************************************************************************/\n\tclass _FCHostStorageAdapter {\n\t\tconstructor(persistent) {\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.FCHostPersistent;\n\t\t\t\tname   = 'FCHostPersistent';\n\t\t\t}\n\t\t\telse {\n\t\t\t    engine = window.FCHostSession;\n\t\t\t\tname   = 'FCHostSession';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n                \n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\treturn this._engine.keys();\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn this._engine.has(key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.get(key);\n\n\t\t\treturn value == null ? null : _FCHostStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.set(key, _FCHostStorageAdapter._serialize(value));\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.remove(key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tthis._engine.clear();\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(str);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// FCHost feature test.\n\t\tfunction hasFCHostStorage() {\n\t\t\ttry {\n\t\t\t    if (typeof window.FCHostPersistent !== 'undefined')\n\t\t\t        return true;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t_ok = hasFCHostStorage();\n\t\t\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _FCHostStorageAdapter(persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/webstorage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_WebStorageAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _WebStorageAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}.`;\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.localStorage;\n\t\t\t\tname   = 'localStorage';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tengine = window.sessionStorage;\n\t\t\t\tname   = 'sessionStorage';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tconst keys = [];\n\n\t\t\tfor (let i = 0; i < this._engine.length; ++i) {\n\t\t\t\tconst key = this._engine.key(i);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// // FIXME: This method should probably check for the key, rather than comparing its value.\n\t\t\t// return this._engine.getItem(this._prefix + key) != null; // lazy equality for null\n\n\t\t\treturn this._engine.hasOwnProperty(this._prefix + key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.getItem(this._prefix + key);\n\n\t\t\treturn value == null ? null : _WebStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value, compression = true) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis._engine.setItem(this._prefix + key,\n\t\t\t\t\t_WebStorageAdapter._serialize(value, this.persistent && compression));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t/*\n\t\t\t\t\tIf the exception is a quota exceeded error, massage it into something\n\t\t\t\t\ta bit nicer for the player.\n\n\t\t\t\t\tNOTE: Ideally, we could simply do something like checking `ex.code`, but\n\t\t\t\t\tit's a non-standard property and not supported in all browsers.  Thus,\n\t\t\t\t\twe have to resort to pattern matching the name and message—the latter being\n\t\t\t\t\trequired by Opera (Presto).  I hate the parties responsible for this snafu\n\t\t\t\t\tso much.\n\t\t\t\t*/\n\t\t\t\tif (/quota.?(?:exceeded|reached)/i.test(ex.name + ex.message)) {\n\t\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `${this.name} quota exceeded`);\n\t\t\t\t}\n\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.removeItem(this._prefix + key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// return this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj, compression) {\n\t\t\tif (compression) {\n\t\t\t\treturn LZString.compressToUTF16(JSON.stringify(obj));\n\t\t\t}\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse((!str || str[0] == \"{\") ? str : LZString.decompressFromUTF16(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// Web Storage feature test.\n\t\tfunction hasWebStorage(storeId) {\n\t\t\ttry {\n\t\t\t\tconst store = window[storeId];\n\t\t\t\tconst tid   = `_sc_${String(Date.now())}`;\n\t\t\t\tstore.setItem(tid, tid);\n\t\t\t\tconst result = store.getItem(tid) === tid;\n\t\t\t\tstore.removeItem(tid);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t\tJust to be safe, we feature test for both `localStorage` and `sessionStorage`,\n\t\t\tas you never know what browser implementation bugs you're going to run into.\n\t\t*/\n\t\t_ok = hasWebStorage('localStorage') && hasWebStorage('sessionStorage');\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _WebStorageAdapter(storageId, persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/cookie.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Expiry constants.\n\tconst _MAX_EXPIRY = 'Tue, 19 Jan 2038 03:14:07 GMT'; // (new Date((Math.pow(2, 31) - 1) * 1000)).toUTCString()\n\tconst _MIN_EXPIRY = 'Thu, 01 Jan 1970 00:00:00 GMT'; // (new Date(0)).toUTCString()\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_CookieAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _CookieAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}${persistent ? '!' : '*'}.`;\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : 'cookie'\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tif (document.cookie === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\t\t\tconst keys    = [];\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should omit such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\tif (value !== '') {\n\t\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn _CookieAdapter._getCookie(this._prefix + key) !== null;\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = _CookieAdapter._getCookie(this._prefix + key);\n\n\t\t\treturn value === null ? null : _CookieAdapter._deserialize(value);\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\t\t\t\t\t_CookieAdapter._serialize(value),\n\n\t\t\t\t\t// An undefined expiry denotes a session cookie.\n\t\t\t\t\tthis.persistent ? _MAX_EXPIRY : undefined\n\t\t\t\t);\n\n\t\t\t\tif (!this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during set');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\t/*\n\t\t\t\tAttempting to delete a cookie implies setting it, so we test for its existence\n\t\t\t\tbeforehand, to avoid creating it in the event that it does not already exist.\n\t\t\t*/\n\t\t\tif (typeof key !== 'string' || !key || !this.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\n\t\t\t\t\t// Use `undefined` as the value.\n\t\t\t\t\tundefined,\n\n\t\t\t\t\t// Use the epoch as the expiry.\n\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t);\n\n\t\t\t\tif (this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during delete');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _getCookie(prefixedKey) {\n\t\t\tif (!prefixedKey || document.cookie === '') {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (prefixedKey === key) {\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should yield `null` for such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\treturn value || null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic _setCookie(prefixedKey, value, expiry) {\n\t\t\tif (!prefixedKey) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet payload = `${encodeURIComponent(prefixedKey)}=`;\n\n\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\tpayload += encodeURIComponent(value);\n\t\t\t}\n\n\t\t\tif (expiry != null) { // lazy equality for null\n\t\t\t\tpayload += `; expires=${expiry}`;\n\t\t\t}\n\n\t\t\tpayload += '; path=/';\n\t\t\tdocument.cookie = payload;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn LZString.compressToBase64(JSON.stringify(obj));\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(LZString.decompressFromBase64(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit(\n\t\t// Only used for stores updates.\n\t\tstorageId\n\t) {\n\t\t// Cookie feature test.\n\t\ttry {\n\t\t\tconst tid = `_sc_${String(Date.now())}`;\n\n\t\t\t// We only test a session cookie as that should suffice.\n\t\t\t_CookieAdapter._setCookie(tid, _CookieAdapter._serialize(tid), undefined);\n\t\t\t_ok = _CookieAdapter._deserialize(_CookieAdapter._getCookie(tid)) === tid;\n\t\t\t_CookieAdapter._setCookie(tid, undefined, _MIN_EXPIRY);\n\t\t}\n\t\tcatch (ex) {\n\t\t\t_ok = false;\n\t\t}\n\n\t\t/* legacy */\n\t\t// Attempt to update the cookie stores, if necessary.  This should happen only during initialization.\n\t\tif (_ok) {\n\t\t\t_updateCookieStores(storageId);\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _CookieAdapter(storageId, persistent);\n\t}\n\n\t/* legacy */\n\t// Updates old non-segmented cookie stores into segmented stores.\n\tfunction _updateCookieStores(storageId) {\n\t\tif (document.cookie === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldPrefix     = `${storageId}.`;\n\t\tconst oldPrefixRe   = new RegExp(`^${RegExp.escape(oldPrefix)}`);\n\t\tconst persistPrefix = `${storageId}!.`;\n\t\tconst sessionPrefix = `${storageId}*.`;\n\t\tconst sessionTestRe = /\\.(?:state|rcWarn)$/;\n\t\tconst cookies       = document.cookie.split(/;\\s*/);\n\n\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\tif (oldPrefixRe.test(key)) {\n\t\t\t\t/*\n\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\tnot a serialized empty string, so we should skip processing such pairs.\n\t\t\t\t*/\n\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\tif (value !== '') {\n\t\t\t\t\tconst persist = !sessionTestRe.test(key);\n\n\t\t\t\t\t// Delete the old k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t\t);\n\n\t\t\t\t\t// Set the new k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey.replace(oldPrefixRe, () => persist ? persistPrefix : sessionPrefix),\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tpersist ? _MAX_EXPIRY : undefined\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t/* /legacy */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/debugview.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: Make this use jQuery throughout.\n*/\nvar DebugView = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDebugView Class.\n\t*******************************************************************************************************************/\n\tclass DebugView {\n\t\tconstructor(parent, type, name, title) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : parent\n\t\t\t\t},\n\n\t\t\t\tview : {\n\t\t\t\t\tvalue : document.createElement('span')\n\t\t\t\t},\n\n\t\t\t\tbreak : {\n\t\t\t\t\tvalue : document.createElement('wbr')\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up the wrapper (`<span>`) element.\n\t\t\tjQuery(this.view)\n\t\t\t\t.attr({\n\t\t\t\t\ttitle,\n\t\t\t\t\t'aria-label' : title,\n\t\t\t\t\t'data-type'  : type != null ? type : '', // lazy equality for null\n\t\t\t\t\t'data-name'  : name != null ? name : ''  // lazy equality for null\n\t\t\t\t})\n\t\t\t\t.addClass('debug');\n\n\t\t\t// Set up the word break (`<wbr>`) element.\n\t\t\tjQuery(this.break).addClass('debug hidden');\n\n\t\t\t// Add the wrapper (`<span>`) and word break (`<wbr>`) elements to the `parent` element.\n\t\t\tthis.parent.appendChild(this.view);\n\t\t\tthis.parent.appendChild(this.break);\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this.view;\n\t\t}\n\n\t\tget type() {\n\t\t\treturn this.view.getAttribute('data-type');\n\t\t}\n\t\tset type(type) {\n\t\t\tthis.view.setAttribute('data-type', type != null ? type : ''); // lazy equality for null\n\t\t}\n\n\t\tget name() {\n\t\t\treturn this.view.getAttribute('data-name');\n\t\t}\n\t\tset name(name) {\n\t\t\tthis.view.setAttribute('data-name', name != null ? name : ''); // lazy equality for null\n\t\t}\n\n\t\tget title() {\n\t\t\treturn this.view.title;\n\t\t}\n\t\tset title(title) {\n\t\t\tthis.view.title = title;\n\t\t}\n\n\t\tappend(el) {\n\t\t\tjQuery(this.view).append(el);\n\t\t\treturn this;\n\t\t}\n\n\t\tmodes(options) {\n\t\t\tif (options == null) { // lazy equality for null\n\t\t\t\tconst current = {};\n\n\t\t\t\tthis.view.className.splitOrEmpty(/\\s+/).forEach(name => {\n\t\t\t\t\tif (name !== 'debug') {\n\t\t\t\t\t\tcurrent[name] = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\telse if (typeof options === 'object') {\n\t\t\t\tObject.keys(options).forEach(function (name) {\n\t\t\t\t\tthis[options[name] ? 'addClass' : 'removeClass'](name);\n\t\t\t\t}, jQuery(this.view));\n\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tthrow new Error('DebugView.prototype.modes options parameter must be an object or null/undefined');\n\t\t}\n\n\t\tremove() {\n\t\t\tconst $view = jQuery(this.view);\n\n\t\t\tif (this.view.hasChildNodes()) {\n\t\t\t\t$view.contents().appendTo(this.parent);\n\t\t\t}\n\n\t\t\t$view.remove();\n\t\t\tjQuery(this.break).remove();\n\t\t}\n\n\t\tstatic isEnabled() {\n\t\t\treturn jQuery(document.documentElement).attr('data-debug-view') === 'enabled';\n\t\t}\n\n\t\tstatic enable() {\n\t\t\tjQuery(document.documentElement).attr('data-debug-view', 'enabled');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic disable() {\n\t\t\tjQuery(document.documentElement).removeAttr('data-debug-view');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic toggle() {\n\t\t\tif (jQuery(document.documentElement).attr('data-debug-view') === 'enabled') {\n\t\t\t\tDebugView.disable();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDebugView.enable();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn DebugView;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/nodetyper.js\n\n\tCopyright © 2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar NodeTyper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tNodeTyper Class.\n\t*******************************************************************************************************************/\n\tclass NodeTyper {\n\t\tconstructor(config) {\n\t\t\tif (typeof config !== 'object' || config === null) {\n\t\t\t\tthrow new Error(`config parameter must be an object (received: ${Util.getType(config)})`);\n\t\t\t}\n\t\t\tif (!config.hasOwnProperty('targetNode') || !(config.targetNode instanceof Node)) {\n\t\t\t\tthrow new Error('config parameter object \"targetNode\" property must be a node');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tnode : {\n\t\t\t\t\tvalue : config.targetNode\n\t\t\t\t},\n\n\t\t\t\tchildNodes : {\n\t\t\t\t\tvalue : []\n\t\t\t\t},\n\n\t\t\t\tnodeValue : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t},\n\n\t\t\t\tappendTo : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.parentNode || null\n\t\t\t\t},\n\n\t\t\t\tclassNames : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.classNames || null\n\t\t\t\t},\n\n\t\t\t\tfinished : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst node = this.node;\n\n\t\t\tif (node.nodeValue) {\n\t\t\t\tthis.nodeValue = node.nodeValue;\n\t\t\t\tnode.nodeValue = '';\n\t\t\t}\n\n\t\t\tlet childNode;\n\n\t\t\twhile ((childNode = node.firstChild) !== null) {\n\t\t\t\tthis.childNodes.push(new NodeTyper({\n\t\t\t\t\ttargetNode : childNode,\n\t\t\t\t\tparentNode : node,\n\t\t\t\t\tclassNames : this.classNames\n\t\t\t\t}));\n\n\t\t\t\tnode.removeChild(childNode);\n\t\t\t}\n\t\t}\n\n\t\tfinish() {\n\t\t\twhile (this.type(true)) /* no-op */;\n\t\t\treturn false;\n\t\t}\n\n\t\ttype(flush) {\n\t\t\tif (this.finished) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (this.appendTo) {\n\t\t\t\tthis.appendTo.appendChild(this.node);\n\t\t\t\tthis.appendTo = null;\n\n\t\t\t\t// Immediately finish typing this node if….\n\t\t\t\tif (\n\t\t\t\t\t// …it's neither a element or text node.\n\t\t\t\t\tthis.node.nodeType !== Node.ELEMENT_NODE && this.node.nodeType !== Node.TEXT_NODE\n\n\t\t\t\t\t// …or the computed value of its parent node's `display` property is `'none'`.\n\t\t\t\t\t|| jQuery(this.node.parentNode).css('display') === 'none'\n\t\t\t\t) {\n\t\t\t\t\treturn this.finish();\n\t\t\t\t}\n\n\t\t\t\tif (this.node.parentNode && this.classNames) {\n\t\t\t\t\tjQuery(this.node.parentNode).addClass(this.classNames);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.nodeValue) {\n\t\t\t\tif (flush) {\n\t\t\t\t\t// Concatenate here in case we've already done some processing.\n\t\t\t\t\tthis.node.nodeValue += this.nodeValue;\n\t\t\t\t\tthis.nodeValue = '';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Use `Util.charAndPosAt()` here to properly handle Unicode code points\n\t\t\t\t\t// that are comprised of surrogate pairs.\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(this.nodeValue, 0);\n\t\t\t\t\tthis.node.nodeValue += char;\n\t\t\t\t\tthis.nodeValue = this.nodeValue.slice(1 + end - start);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (this.classNames) {\n\t\t\t\tjQuery(this.node.parentNode).removeClass(this.classNames);\n\t\t\t\tthis.classNames = null;\n\t\t\t}\n\n\t\t\tconst childNodes = this.childNodes;\n\n\t\t\twhile (childNodes.length > 0) {\n\t\t\t\tif (childNodes[0].type()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tchildNodes.shift();\n\t\t\t}\n\n\t\t\tthis.finished = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn NodeTyper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/prngwrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar PRNGWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPRNGWrapper Class.\n\t*******************************************************************************************************************/\n\tclass PRNGWrapper {\n\t\tconstructor(seed, useEntropy) {\n\t\t\t/* eslint-disable new-cap */\n\t\t\tObject.defineProperties(this, new Math.seedrandom(seed, useEntropy, (prng, seed) => ({\n\t\t\t\t_prng : {\n\t\t\t\t\tvalue : prng\n\t\t\t\t},\n\n\t\t\t\tseed : {\n\t\t\t\t\t/*\n\t\t\t\t\t\tTODO: Make this non-writable.\n\t\t\t\t\t*/\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : seed\n\t\t\t\t},\n\n\t\t\t\tpull : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\trandom : {\n\t\t\t\t\tvalue() {\n\t\t\t\t\t\t++this.pull;\n\t\t\t\t\t\treturn this._prng();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})));\n\t\t\t/* eslint-enable new-cap */\n\t\t}\n\n\t\tstatic marshal(prng) {\n\t\t\tif (!prng || !prng.hasOwnProperty('seed') || !prng.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG is missing required data');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tseed : prng.seed,\n\t\t\t\tpull : prng.pull\n\t\t\t};\n\t\t}\n\n\t\tstatic unmarshal(prngObj) {\n\t\t\tif (!prngObj || !prngObj.hasOwnProperty('seed') || !prngObj.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG object is missing required data');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCreate a new PRNG using the original seed and pull values from it until it\n\t\t\t\thas reached the original pull count.\n\t\t\t*/\n\t\t\tconst prng = new PRNGWrapper(prngObj.seed, false);\n\n\t\t\tfor (let i = prngObj.pull; i > 0; --i) {\n\t\t\t\tprng.random();\n\t\t\t}\n\n\t\t\treturn prng;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn PRNGWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/stylewrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Story, Wikifier */\n\nvar StyleWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _imageMarkupRe    = new RegExp(Patterns.cssImage, 'g');\n\tconst _hasImageMarkupRe = new RegExp(Patterns.cssImage);\n\n\n\t/*******************************************************************************************************************\n\t\tStyleWrapper Class.\n\t*******************************************************************************************************************/\n\tclass StyleWrapper {\n\t\tconstructor(style) {\n\t\t\tif (style == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('StyleWrapper style parameter must be an HTMLStyleElement object');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tstyle : {\n\t\t\t\t\tvalue : style\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tisEmpty() {\n\t\t\t// This should work in all supported browsers.\n\t\t\treturn this.style.cssRules.length === 0;\n\t\t}\n\n\t\tset(rawCss) {\n\t\t\tthis.clear();\n\t\t\tthis.add(rawCss);\n\t\t}\n\n\t\tadd(rawCss) {\n\t\t\tlet css = rawCss;\n\n\t\t\t// Check for wiki image transclusion.\n\t\t\tif (_hasImageMarkupRe.test(css)) {\n\t\t\t\t/*\n\t\t\t\t\tThe JavaScript specifications, since at least ES3, say that `<String>.replace()`\n\t\t\t\t\tshould reset a global-flagged regular expression's `lastIndex` property to `0`\n\t\t\t\t\tupon invocation.  Buggy browser versions exist, however, which do not reset\n\t\t\t\t\t`lastIndex`, so we should do so manually to support those browsers.\n\n\t\t\t\t\tNOTE: I do not think this is actually necessary, since `_imageMarkupRe` is\n\t\t\t\t\tscoped to this module—meaning users should not be able to access it.  That\n\t\t\t\t\tbeing the case, and since we search to exhaustion which should also cause\n\t\t\t\t\t`lastIndex` to be reset, there should never be an instance where we invoke\n\t\t\t\t\t`css.replace()` and `_imageMarkupRe.lastIndex` is not already `0`.  Still,\n\t\t\t\t\tconsidering the other bug, better safe than sorry.\n\t\t\t\t*/\n\t\t\t\t_imageMarkupRe.lastIndex = 0;\n\n\t\t\t\tcss = css.replace(_imageMarkupRe, wikiImage => {\n\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t});\n\n\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t*/\n\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For IE ≤ 10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText += css;\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥ 11).\n\t\t\telse {\n\t\t\t\tthis.style.appendChild(document.createTextNode(css));\n\t\t\t}\n\t\t}\n\n\t\tclear() {\n\t\t\t// For IE ≤10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText = '';\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥11).\n\t\t\telse {\n\t\t\t\tjQuery(this.style).empty();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn StyleWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/diff.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, clone */\n\nvar Diff = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDiff Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDiff operations object (pseudo-enumeration).\n\t*/\n\tconst Op = Util.toEnum({\n\t\tDelete      : 0,\n\t\tSpliceArray : 1,\n\t\tCopy        : 2,\n\t\tCopyDate    : 3\n\t});\n\n\t/*\n\t\tReturns a difference object generated from comparing the the orig and dest objects.\n\t*/\n\tfunction diff(orig, dest) /* diff object */ {\n\t\tconst objToString = Object.prototype.toString;\n\t\tconst origIsArray = orig instanceof Array;\n\t\tconst keys        = []\n\t\t\t.concat(Object.keys(orig), Object.keys(dest))\n\t\t\t.sort()\n\t\t\t.filter((val, i, arr) => i === 0 || arr[i - 1] !== val);\n\t\tconst diffed      = {};\n\t\tlet aOpRef;\n\n\t\tconst keyIsAOpRef = key => key === aOpRef;\n\n\t\t/* eslint-disable max-depth */\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key   = keys[i];\n\t\t\tconst origP = orig[key];\n\t\t\tconst destP = dest[key];\n\n\t\t\tif (orig.hasOwnProperty(key)) {\n\t\t\t\t// Key exists in both.\n\t\t\t\tif (dest.hasOwnProperty(key)) {\n\t\t\t\t\t// Values are exactly the same, so do nothing.\n\t\t\t\t\tif (origP === destP) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Values are of the same basic type.\n\t\t\t\t\tif (typeof origP === typeof destP) { // eslint-disable-line valid-typeof\n\t\t\t\t\t\t// Values are functions.\n\t\t\t\t\t\tif (typeof origP === 'function') {\n\t\t\t\t\t\t\t/* diffed[key] = [Op.Copy, destP]; */\n\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are primitives.\n\t\t\t\t\t\telse if (typeof origP !== 'object' || origP === null) {\n\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are objects.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst origPType = objToString.call(origP);\n\t\t\t\t\t\t\tconst destPType = objToString.call(destP);\n\n\t\t\t\t\t\t\t// Values are objects of the same reported type.\n\t\t\t\t\t\t\tif (origPType === destPType) {\n\t\t\t\t\t\t\t\t// Various special cases to handle supported non-generic objects.\n\t\t\t\t\t\t\t\tif (origP instanceof Date) {\n\t\t\t\t\t\t\t\t\tif (Number(origP) !== Number(destP)) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Map) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof RegExp) {\n\t\t\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Set) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Unknown non-generic objects (custom or unsupported natives).\n\t\t\t\t\t\t\t\telse if (origPType !== '[object Object]') {\n\t\t\t\t\t\t\t\t\t// We cannot know how to process these objects,\n\t\t\t\t\t\t\t\t\t// so we simply accept them as-is.\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Generic objects.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tconst recurse = diff(origP, destP);\n\n\t\t\t\t\t\t\t\t\tif (recurse !== null) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = recurse;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Values are objects of different reported types.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Values are of different types.\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = [\n\t\t\t\t\t\t\tOp.Copy,\n\t\t\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Key only exists in orig.\n\t\t\t\telse {\n\t\t\t\t\tif (origIsArray && Util.isNumeric(key)) {\n\t\t\t\t\t\tconst nKey = Number(key);\n\n\t\t\t\t\t\tif (!aOpRef) {\n\t\t\t\t\t\t\taOpRef = '';\n\n\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\taOpRef += '~';\n\t\t\t\t\t\t\t} while (keys.some(keyIsAOpRef));\n\n\t\t\t\t\t\t\tdiffed[aOpRef] = [Op.SpliceArray, nKey, nKey];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey < diffed[aOpRef][1]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][1] = nKey;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey > diffed[aOpRef][2]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][2] = nKey;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = Op.Delete;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Key only exists in dest.\n\t\t\telse {\n\t\t\t\tdiffed[key] = [\n\t\t\t\t\tOp.Copy,\n\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\t/* eslint-enable max-depth */\n\n\t\treturn Object.keys(diffed).length > 0 ? diffed : null;\n\t}\n\n\t/*\n\t\tReturns the object resulting from updating the orig object with the diffed object.\n\t*/\n\tfunction patch(orig, diffed) /* patched object */ {\n\t\tconst keys    = Object.keys(diffed || {});\n\t\tconst patched = clone(orig);\n\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key     = keys[i];\n\t\t\tconst diffedP = diffed[key];\n\n\t\t\tif (diffedP === Op.Delete) {\n\t\t\t\tdelete patched[key];\n\t\t\t}\n\t\t\telse if (diffedP instanceof Array) {\n\t\t\t\tswitch (diffedP[0]) {\n\t\t\t\tcase Op.SpliceArray:\n\t\t\t\t\tpatched.splice(diffedP[1], 1 + (diffedP[2] - diffedP[1]));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.Copy:\n\t\t\t\t\tpatched[key] = clone(diffedP[1]);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.CopyDate:\n\t\t\t\t\tpatched[key] = new Date(diffedP[1]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpatched[key] = patch(patched[key], diffedP);\n\t\t\t}\n\t\t}\n\n\t\treturn patched;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tOp    : { value : Op },\n\t\tdiff  : { value : diff },\n\t\tpatch : { value : patch }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/l10n.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global l10nStrings, strings */\n\nvar L10n = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Replacement pattern regular expressions.\n\tconst _patternRe    = /\\{\\w+\\}/g;\n\tconst _hasPatternRe = new RegExp(_patternRe.source); // to drop the global flag\n\n\n\t/*******************************************************************************************************************\n\t\tLocalization Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nInit() {\n\t\t/* legacy */\n\t\t_mapStringsToL10nStrings();\n\t\t/* /legacy */\n\t}\n\n\t/*******************************************************************************************************************\n\t\tLocalized String Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nGet(ids, overrides) {\n\t\tif (!ids) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst id = (idList => {\n\t\t\tlet selectedId;\n\t\t\tidList.some(id => {\n\t\t\t\tif (l10nStrings.hasOwnProperty(id)) {\n\t\t\t\t\tselectedId = id;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\treturn selectedId;\n\t\t})(Array.isArray(ids) ? ids : [ids]);\n\n\t\tif (!id) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst maxIterations = 50;\n\t\tlet processed = l10nStrings[id];\n\t\tlet iteration = 0;\n\n\t\twhile (_hasPatternRe.test(processed)) {\n\t\t\tif (++iteration > maxIterations) {\n\t\t\t\tthrow new Error('L10n.get exceeded maximum replacement iterations, probable infinite loop');\n\t\t\t}\n\n\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t_patternRe.lastIndex = 0;\n\n\t\t\tprocessed = processed.replace(_patternRe, pat => {\n\t\t\t\tconst subId = pat.slice(1, -1);\n\n\t\t\t\tif (overrides && overrides.hasOwnProperty(subId)) {\n\t\t\t\t\treturn overrides[subId];\n\t\t\t\t}\n\t\t\t\telse if (l10nStrings.hasOwnProperty(subId)) {\n\t\t\t\t\treturn l10nStrings[subId];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn processed;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tAttempt to map legacy `strings` object properties to the `l10nStrings` object.\n\t*/\n\tfunction _mapStringsToL10nStrings() {\n\t\tif (strings && Object.keys(strings).length > 0) {\n\t\t\tObject.keys(l10nStrings).forEach(id => {\n\t\t\t\ttry {\n\t\t\t\t\tlet value;\n\n\t\t\t\t\tswitch (id) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tGeneral.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'identity': value = strings.identity; break;\n\t\t\t\t\tcase 'aborting': value = strings.aborting; break;\n\t\t\t\t\tcase 'cancel':   value = strings.cancel; break;\n\t\t\t\t\tcase 'close':    value = strings.close; break;\n\t\t\t\t\tcase 'ok':       value = strings.ok; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tErrors.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'errorTitle':              value = strings.errors.title; break;\n\t\t\t\t\tcase 'errorNonexistentPassage': value = strings.errors.nonexistentPassage; break;\n\t\t\t\t\tcase 'errorSaveMissingData':    value = strings.errors.saveMissingData; break;\n\t\t\t\t\tcase 'errorSaveIdMismatch':     value = strings.errors.saveIdMismatch; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tWarnings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'warningDegraded': value = strings.warnings.degraded; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tDebug View.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'debugViewTitle':  value = strings.debugView.title; break;\n\t\t\t\t\tcase 'debugViewToggle': value = strings.debugView.toggle; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tUI bar.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'uiBarToggle':   value = strings.uiBar.toggle; break;\n\t\t\t\t\tcase 'uiBarBackward': value = strings.uiBar.backward; break;\n\t\t\t\t\tcase 'uiBarForward':  value = strings.uiBar.forward; break;\n\t\t\t\t\tcase 'uiBarJumpto':   value = strings.uiBar.jumpto; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tJump To.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'jumptoTitle':       value = strings.jumpto.title; break;\n\t\t\t\t\tcase 'jumptoTurn':        value = strings.jumpto.turn; break;\n\t\t\t\t\tcase 'jumptoUnavailable': value = strings.jumpto.unavailable; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSaves.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'savesTitle':       value = strings.saves.title; break;\n\t\t\t\t\tcase 'savesDisallowed':  value = strings.saves.disallowed; break;\n\t\t\t\t\tcase 'savesIncapable':   value = strings.saves.incapable; break;\n\t\t\t\t\tcase 'savesLabelAuto':   value = strings.saves.labelAuto; break;\n\t\t\t\t\tcase 'savesLabelDelete': value = strings.saves.labelDelete; break;\n\t\t\t\t\tcase 'savesLabelExport': value = strings.saves.labelExport; break;\n\t\t\t\t\tcase 'savesLabelImport': value = strings.saves.labelImport; break;\n\t\t\t\t\tcase 'savesLabelLoad':   value = strings.saves.labelLoad; break;\n\t\t\t\t\tcase 'savesLabelClear':  value = strings.saves.labelClear; break;\n\t\t\t\t\tcase 'savesLabelSave':   value = strings.saves.labelSave; break;\n\t\t\t\t\tcase 'savesLabelSlot':   value = strings.saves.labelSlot; break;\n\t\t\t\t\tcase 'savesUnavailable': value = strings.saves.unavailable; break;\n\t\t\t\t\tcase 'savesUnknownDate': value = strings.saves.unknownDate; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSettings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'settingsTitle': value = strings.settings.title; break;\n\t\t\t\t\tcase 'settingsOff':   value = strings.settings.off; break;\n\t\t\t\t\tcase 'settingsOn':    value = strings.settings.on; break;\n\t\t\t\t\tcase 'settingsReset': value = strings.settings.reset; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tRestart.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'restartTitle':  value = strings.restart.title; break;\n\t\t\t\t\tcase 'restartPrompt': value = strings.restart.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tShare.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'shareTitle': value = strings.share.title; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAlert.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'alertTitle': /* none */ break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAutoload.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'autoloadTitle':  value = strings.autoload.title; break;\n\t\t\t\t\tcase 'autoloadCancel': value = strings.autoload.cancel; break;\n\t\t\t\t\tcase 'autoloadOk':     value = strings.autoload.ok; break;\n\t\t\t\t\tcase 'autoloadPrompt': value = strings.autoload.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tMacros.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'macroBackText':   value = strings.macros.back.text; break;\n\t\t\t\t\tcase 'macroReturnText': value = strings.macros.return.text; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tl10nStrings[id] = value.replace(/%\\w+%/g, pat => `{${pat.slice(1, -1)}}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tLocalization Functions.\n\t\t*/\n\t\tinit : { value : l10nInit },\n\n\t\t/*\n\t\t\tLocalized String Functions.\n\t\t*/\n\t\tget : { value : l10nGet }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/legacy.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\t[DEPRECATED] The `strings` object is deprecated and should no longer be used.\n\tAll new or updated translations should be based upon the `l10nStrings` object\n\t(see: `l10n/strings.js`).\n\n\tLegacy/existing uses of the `strings` object will be mapped to the `l10nStrings`\n\tobject after user script evaluation.\n*/\nvar strings = { // eslint-disable-line no-unused-vars, no-var\n\terrors    : {},\n\twarnings  : {},\n\tdebugView : {},\n\tuiBar     : {},\n\tjumpto    : {},\n\tsaves     : {},\n\tsettings  : {},\n\trestart   : {},\n\tshare     : {},\n\tautoload  : {},\n\tmacros    : {\n\t\tback   : {},\n\t\treturn : {}\n\t}\n};\n\n/***********************************************************************************************************************\n\n\tl10n/strings.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* eslint-disable max-len, prefer-template */\n\n/*\n\tATTENTION TRANSLATORS\n\n\tPlease use the `locale/l10n-template.js` file, from the root of the repository,\n\tas the template for your translation rather than this file.\n\n\tSEE: https://github.com/tmedwards/sugarcube-2/tree/develop/locale\n*/\nvar l10nStrings = { // eslint-disable-line no-unused-vars, no-var\n\t/*\n\t\tGeneral.\n\t*/\n\tidentity : 'game',\n\taborting : 'Aborting',\n\tcancel   : 'Cancel',\n\tclose    : 'Close',\n\tok       : 'OK',\n\n\t/*\n\t\tErrors.\n\t*/\n\terrorTitle              : 'Error',\n\terrorToggle             : 'Toggle the error view',\n\terrorNonexistentPassage : 'the passage \"{passage}\" does not exist', // NOTE: `passage` is supplied locally\n\terrorSaveMissingData    : 'save is missing required data. Either the loaded file is not a save or the save has become corrupted',\n\terrorSaveIdMismatch     : 'save is from the wrong {identity}',\n\n\t/*\n\t\tWarnings.\n\t*/\n\t_warningIntroLacking  : 'Your browser either lacks or has disabled',\n\t_warningOutroDegraded : ', so this {identity} is running in a degraded mode. You may be able to continue, however, some parts may not work properly.',\n\twarningNoWebStorage   : '{_warningIntroLacking} the Web Storage API{_warningOutroDegraded}',\n\twarningDegraded       : '{_warningIntroLacking} some of the capabilities required by this {identity}{_warningOutroDegraded}',\n\n\t/*\n\t\tDebug bar.\n\t*/\n\tdebugBarToggle      : 'Toggle the debug bar',\n\tdebugBarNoWatches   : '\\u2014 no watches set \\u2014',\n\tdebugBarAddWatch    : 'Add watch',\n\tdebugBarDeleteWatch : 'Delete watch',\n\tdebugBarWatchAll    : 'Watch all',\n\tdebugBarWatchNone   : 'Delete all',\n\tdebugBarLabelAdd    : 'Add',\n\tdebugBarLabelWatch  : 'Watch',\n\tdebugBarLabelTurn   : 'Turn', // (noun) chance to act (in a game), moment, period\n\tdebugBarLabelViews  : 'Views',\n\tdebugBarViewsToggle : 'Toggle the debug views',\n\tdebugBarWatchToggle : 'Toggle the watch panel',\n\n\t/*\n\t\tUI bar.\n\t*/\n\tuiBarToggle   : 'Toggle the UI bar',\n\tuiBarBackward : 'Go backward within the {identity} history',\n\tuiBarForward  : 'Go forward within the {identity} history',\n\tuiBarJumpto   : 'Jump to a specific point within the {identity} history',\n\n\t/*\n\t\tJump To.\n\t*/\n\tjumptoTitle       : 'Jump To',\n\tjumptoTurn        : 'Turn', // (noun) chance to act (in a game), moment, period\n\tjumptoUnavailable : 'No jump points currently available\\u2026',\n\n\t/*\n\t\tSaves.\n\t*/\n\tsavesTitle       : 'Saves',\n\tsavesDisallowed  : 'Saving has been disallowed on this passage.',\n\tsavesIncapable   : '{_warningIntroLacking} the capabilities required to support saves, so saves have been disabled for this session.',\n\tsavesLabelAuto   : 'Autosave',\n\tsavesLabelDelete : 'Delete',\n\tsavesLabelExport : 'Save to Disk\\u2026',\n\tsavesLabelImport : 'Load from Disk\\u2026',\n\tsavesLabelLoad   : 'Load',\n\tsavesLabelClear  : 'Delete All',\n\tsavesLabelSave   : 'Save',\n\tsavesLabelSlot   : 'Slot',\n\tsavesUnavailable : 'No save slots found\\u2026',\n\tsavesUnknownDate : 'unknown',\n\n\t/*\n\t\tSettings.\n\t*/\n\tsettingsTitle : 'Settings',\n\tsettingsOff   : 'Off',\n\tsettingsOn    : 'On',\n\tsettingsReset : 'Reset to Defaults',\n\n\t/*\n\t\tRestart.\n\t*/\n\trestartTitle  : 'Restart',\n\trestartPrompt : 'Are you sure that you want to restart? Unsaved progress will be lost.',\n\n\t/*\n\t\tShare.\n\t*/\n\tshareTitle : 'Share',\n\n\t/*\n\t\tAlert.\n\t*/\n\talertTitle : 'Alert',\n\n\t/*\n\t\tAutoload.\n\t*/\n\tautoloadTitle  : 'Autoload',\n\tautoloadCancel : 'Go to start',\n\tautoloadOk     : 'Load autosave',\n\tautoloadPrompt : 'An autosave exists. Load it now or go to the start?',\n\n\t/*\n\t\tMacros.\n\t*/\n\tmacroBackText   : 'Back',  // (verb) rewind, revert\n\tmacroReturnText : 'Return' // (verb) go/send back\n};\n\n/***********************************************************************************************************************\n\n\tconfig.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar Config = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// General settings.\n\tlet _debug                 = false;\n\tlet _addVisitedLinkClass   = false;\n\tlet _cleanupWikifierOutput = false;\n\tlet _loadDelay             = 0;\n\n\t// Audio settings.\n\tlet _audioPauseOnFadeToZero = true;\n\tlet _audioPreloadMetadata   = true;\n\n\t// State history settings.\n\tlet _historyControls  = true;\n\tlet _historyMaxStates = 100;\n\n\t// Macros settings.\n\tlet _macrosIfAssignmentError   = true;\n\tlet _macrosMaxLoopIterations   = 1000;\n\tlet _macrosTypeSkipKey         = '\\x20'; // Space\n\tlet _macrosTypeVisitedPassages = true;\n\n\t// Navigation settings.\n\tlet _navigationOverride;\n\n\t// Passages settings.\n\tlet _passagesDescriptions;\n\tlet _passagesDisplayTitles = false;\n\tlet _passagesNobr          = false;\n\tlet _passagesStart; // set by `Story.load()`\n\tlet _passagesOnProcess;\n\tlet _passagesTransitionOut;\n\n\t// Saves settings.\n\tlet _savesAutoload;\n\tlet _savesAutosave;\n\tlet _savesId        = 'untitled-story';\n\tlet _savesIsAllowed;\n\tlet _savesOnLoad;\n\tlet _savesOnSave;\n\tlet _savesSlots     = 8;\n\tlet _savesVersion;\n\n\t// UI settings.\n\tlet _uiStowBarInitially    = 800;\n\tlet _uiUpdateStoryElements = true;\n\n\n\t/*******************************************************************************\n\t\tError Constants.\n\t*******************************************************************************/\n\n\tconst _errHistoryModeDeprecated     = 'Config.history.mode has been deprecated and is no longer used by SugarCube, please remove it from your code';\n\tconst _errHistoryTrackingDeprecated = 'Config.history.tracking has been deprecated, use Config.history.maxStates instead';\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze({\n\t\t/*\n\t\t\tGeneral settings.\n\t\t*/\n\t\tget debug() { return _debug; },\n\t\tset debug(value) { _debug = Boolean(value); },\n\n\t\tget addVisitedLinkClass() { return _addVisitedLinkClass; },\n\t\tset addVisitedLinkClass(value) { _addVisitedLinkClass = Boolean(value); },\n\n\t\tget cleanupWikifierOutput() { return _cleanupWikifierOutput; },\n\t\tset cleanupWikifierOutput(value) { _cleanupWikifierOutput = Boolean(value); },\n\n\t\tget loadDelay() { return _loadDelay; },\n\t\tset loadDelay(value) {\n\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\tthrow new RangeError('Config.loadDelay must be a non-negative integer');\n\t\t\t}\n\n\t\t\t_loadDelay = value;\n\t\t},\n\n\t\t/*\n\t\t\tAudio settings.\n\t\t*/\n\t\taudio : Object.freeze({\n\t\t\tget pauseOnFadeToZero() { return _audioPauseOnFadeToZero; },\n\t\t\tset pauseOnFadeToZero(value) { _audioPauseOnFadeToZero = Boolean(value); },\n\n\t\t\tget preloadMetadata() { return _audioPreloadMetadata; },\n\t\t\tset preloadMetadata(value) { _audioPreloadMetadata = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tState history settings.\n\t\t*/\n\t\thistory : Object.freeze({\n\t\t\t// TODO: (v3) This should be under UI settings → `Config.ui.historyControls`.\n\t\t\tget controls() { return _historyControls; },\n\t\t\tset controls(value) {\n\t\t\t\tconst controls = Boolean(value);\n\n\t\t\t\tif (_historyMaxStates === 1 && controls) {\n\t\t\t\t\tthrow new Error('Config.history.controls must be false when Config.history.maxStates is 1');\n\t\t\t\t}\n\n\t\t\t\t_historyControls = controls;\n\t\t\t},\n\n\t\t\tget maxStates() { return _historyMaxStates; },\n\t\t\tset maxStates(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.history.maxStates must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_historyMaxStates = value;\n\n\t\t\t\t// Force `Config.history.controls` to `false`, when limited to `1` moment.\n\t\t\t\tif (_historyControls && value === 1) {\n\t\t\t\t\t_historyControls = false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// legacy\n\t\t\t// Die if deprecated state history settings are accessed.\n\t\t\tget mode()  { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tset mode(_) { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tget tracking()  { throw new Error(_errHistoryTrackingDeprecated); },\n\t\t\tset tracking(_) { throw new Error(_errHistoryTrackingDeprecated); }\n\t\t\t// /legacy\n\t\t}),\n\n\t\t/*\n\t\t\tMacros settings.\n\t\t*/\n\t\tmacros : Object.freeze({\n\t\t\tget ifAssignmentError() { return _macrosIfAssignmentError; },\n\t\t\tset ifAssignmentError(value) { _macrosIfAssignmentError = Boolean(value); },\n\n\t\t\tget maxLoopIterations() { return _macrosMaxLoopIterations; },\n\t\t\tset maxLoopIterations(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.macros.maxLoopIterations must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_macrosMaxLoopIterations = value;\n\t\t\t},\n\n\t\t\tget typeSkipKey() { return _macrosTypeSkipKey; },\n\t\t\tset typeSkipKey(value) { _macrosTypeSkipKey = String(value); },\n\n\t\t\tget typeVisitedPassages() { return _macrosTypeVisitedPassages; },\n\t\t\tset typeVisitedPassages(value) { _macrosTypeVisitedPassages = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tNavigation settings.\n\t\t*/\n\t\tnavigation : Object.freeze({\n\t\t\tget override() { return _navigationOverride; },\n\t\t\tset override(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.navigation.override must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_navigationOverride = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tPassages settings.\n\t\t*/\n\t\tpassages : Object.freeze({\n\t\t\tget descriptions() { return _passagesDescriptions; },\n\t\t\tset descriptions(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'Object' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.descriptions must be a boolean, object, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesDescriptions = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.updateTitle`.\n\t\t\tget displayTitles() { return _passagesDisplayTitles; },\n\t\t\tset displayTitles(value) { _passagesDisplayTitles = Boolean(value); },\n\n\t\t\tget nobr() { return _passagesNobr; },\n\t\t\tset nobr(value) { _passagesNobr = Boolean(value); },\n\n\t\t\tget onProcess() { return _passagesOnProcess; },\n\t\t\tset onProcess(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.onProcess must be a function or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesOnProcess = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.(start|startingPassage)`.\n\t\t\tget start() { return _passagesStart; },\n\t\t\tset start(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.start must be a string or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesStart = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.transitionOut`.\n\t\t\tget transitionOut() { return _passagesTransitionOut; },\n\t\t\tset transitionOut(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'string'\n\t\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.transitionOut must be a string, non-negative integer, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesTransitionOut = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tSaves settings.\n\t\t*/\n\t\tsaves : Object.freeze({\n\t\t\tget autoload() { return _savesAutoload; },\n\t\t\tset autoload(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'string' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autoload must be a boolean, string, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutoload = value;\n\t\t\t},\n\n\t\t\tget autosave() { return _savesAutosave; },\n\t\t\tset autosave(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\t// legacy\n\t\t\t\t\t// Convert a string value to an Array of string.\n\t\t\t\t\tif (valueType === 'string') {\n\t\t\t\t\t\t_savesAutosave = [value];\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// /legacy\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t\t&& (valueType !== 'Array' || !value.every(item => typeof item === 'string'))\n\t\t\t\t\t\t&& valueType !== 'function'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autosave must be a boolean, Array of strings, function, or null/undefined (received: ${valueType}${valueType === 'Array' ? ' of mixed' : ''})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutosave = value;\n\t\t\t},\n\n\t\t\tget id() { return _savesId; },\n\t\t\tset id(value) {\n\t\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\t\tthrow new TypeError(`Config.saves.id must be a non-empty string (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesId = value;\n\t\t\t},\n\n\t\t\tget isAllowed() { return _savesIsAllowed; },\n\t\t\tset isAllowed(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.isAllowed must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesIsAllowed = value;\n\t\t\t},\n\n\t\t\tget onLoad() { return _savesOnLoad; },\n\t\t\tset onLoad(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onLoad must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnLoad = value;\n\t\t\t},\n\n\t\t\tget onSave() { return _savesOnSave; },\n\t\t\tset onSave(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onSave must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnSave = value;\n\t\t\t},\n\n\t\t\tget slots() { return _savesSlots; },\n\t\t\tset slots(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new TypeError(`Config.saves.slots must be a non-negative integer (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesSlots = value;\n\t\t\t},\n\n\t\t\tget version() { return _savesVersion; },\n\t\t\tset version(value) { _savesVersion = value; }\n\t\t}),\n\n\t\t/*\n\t\t\tUI settings.\n\t\t*/\n\t\tui : Object.freeze({\n\t\t\tget stowBarInitially() { return _uiStowBarInitially; },\n\t\t\tset stowBarInitially(value) {\n\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\tif (\n\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError(`Config.ui.stowBarInitially must be a boolean or non-negative integer (received: ${valueType})`);\n\t\t\t\t}\n\n\t\t\t\t_uiStowBarInitially = value;\n\t\t\t},\n\n\t\t\tget updateStoryElements() { return _uiUpdateStoryElements; },\n\t\t\tset updateStoryElements(value) { _uiUpdateStoryElements = Boolean(value); }\n\t\t})\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tsimpleaudio.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser, Config, Has, LoadScreen, Story, Util, Visibility, clone */\n\nvar SimpleAudio = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tEvents that count as user activation—i.e. \"user gestures\", \"activation behavior\".\n\n\t\tNOTE (ca. Dec, 2018): This not an exhaustive list and varies significantly by browser.\n\t\tProposals for a specification/standard are still very much in flux at this point.\n\n\t\tTODO (ca. Dec, 2018): Revisit this topic.\n\n\t\tSEE: (too many to list)\n\t\t\thttps://github.com/whatwg/html/issues/3849\n\t\t\thttps://github.com/whatwg/html/issues/1903\n\t\t\thttps://html.spec.whatwg.org/#activation\n\t\t\thttps://docs.google.com/spreadsheets/d/1DGXjhQ6D3yZXIePOMo0dsd2agz0t5W7rYH1NwJ-QGJo/edit#gid=0\n\t*/\n\tconst _gestureEventNames = Object.freeze(['click', 'contextmenu', 'dblclick', 'keyup', 'mouseup', 'pointerup', 'touchend']);\n\n\t// Special group IDs.\n\tconst _specialIds = Object.freeze([':not', ':all', ':looped', ':muted', ':paused', ':playing']);\n\n\t// Format specifier regular expression.\n\tconst _formatSpecRe = /^([\\w-]+)\\s*\\|\\s*(\\S.*)$/; // e.g. 'mp3|https://audiohost.tld/id'\n\n\t// ID verification regular expressions.\n\tconst _badIdRe = /[:\\s]/;\n\n\t// Tracks collection.\n\tconst _tracks = new Map();\n\n\t// Groups collection.\n\tconst _groups = new Map();\n\n\t// Playlists collection.\n\tconst _lists = new Map();\n\n\t// Subscriber collection.\n\tconst _subscribers = new Map();\n\n\t// Master playback rate.\n\tlet _masterRate = 1;\n\n\t// Master playback volume.\n\tlet _masterVolume = 1;\n\n\t// Master mute state.\n\tlet _masterMute = false;\n\n\t// Master mute on tab/window visibility state.\n\tlet _masterMuteOnHidden = false;\n\n\n\t/*******************************************************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************************************************/\n\t// Return whether the `<HTMLAudioElement>.play()` method returns a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _playReturnsPromise = (function () {\n\t\t// Cache of whether `<HTMLAudioElement>.play()` returns a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _playReturnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (Has.audio) {\n\t\t\t\ttry {\n\t\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t\t// NOTE (ca. Jan 01, 2020): Firefox will still log an \"Autoplay is only allowed\n\t\t\t\t\t// when […] media is muted.\" message to the console when attempting the test\n\t\t\t\t\t// below, even though the audio has been muted.  Stay classy, Firefox.\n\t\t\t\t\t//\n\t\t\t\t\t// QUESTION (ca. Jan 01, 2020): Keep this?  It's only here to appease Firefox,\n\t\t\t\t\t// but doesn't seem to work as Firefox seems to ignore mute in violation of the\n\t\t\t\t\t// `HTMLAudioElement` specification—willfully or simply a bug, I can't say.\n\t\t\t\t\taudio.muted = true;\n\n\t\t\t\t\tconst value = audio.play();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since there's no source, and we don't actually\n\t\t\t\t\t// care about the error, since we just want the return value, so we consign it\n\t\t\t\t\t// to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _playReturnsPromise;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tAudioTrack Class.\n\t*******************************************************************************************************************/\n\tclass AudioTrack {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of sources or AudioTrack object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioTrack) {\n\t\t\t\tthis._copy(obj);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('sources parameter must be either an array, of URIs or source objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(sourceList) {\n\t\t\tconst dataUriRe   = /^data:\\s*audio\\/([^;,]+)\\s*[;,]/i;\n\t\t\tconst extRe       = /\\.([^./\\\\]+)$/;\n\t\t\tconst getType     = AudioTrack.getType;\n\t\t\tconst usedSources = [];\n\t\t\t/*\n\t\t\t\tHTMLAudioElement: DOM factory method vs. constructor\n\n\t\t\t\tUse of the DOM factory method, `document.createElement('audio')`, should be\n\t\t\t\tpreferred over use of the constructor, `new Audio()`.  The reason being that\n\t\t\t\tobjects created by the latter are, erroneously, treated differently, often\n\t\t\t\tunfavorably, by certain browser engines—e.g. within some versions of the iOS\n\t\t\t\tbrowser core.\n\n\t\t\t\tNotably, the only difference between the two, per the specification, is that\n\t\t\t\tobjects created via the constructor should have their `preload` property\n\t\t\t\tautomatically set to 'auto'.  Thus, there's no technical reason to prefer\n\t\t\t\tusage of the constructor, even discounting buggy browser implementations.\n\t\t\t*/\n\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t// Initially set the `preload` attribute to `'none'`.\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Process the array of sources, adding any valid sources to the `usedSources`\n\t\t\t// array and to the audio element as source elements.\n\t\t\tsourceList.forEach(src => {\n\t\t\t\tlet srcObj = null;\n\n\t\t\t\tswitch (typeof src) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t{\n\t\t\t\t\t\tlet match;\n\n\t\t\t\t\t\tif (src.slice(0, 5) === 'data:') {\n\t\t\t\t\t\t\tmatch = dataUriRe.exec(src);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source data URI missing media type');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tmatch = extRe.exec(Util.parseUrl(src).pathname);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source URL missing file extension');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(match[1]);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\t{\n\t\t\t\t\t\tif (src === null) {\n\t\t\t\t\t\t\tthrow new Error('source object cannot be null');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('src')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"src\" property');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('format')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"format\" property');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(src.format);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src : src.src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`invalid source value (type: ${typeof src})`);\n\t\t\t\t}\n\n\t\t\t\tif (srcObj !== null) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tOpera (Blink; ca. Jul 2017) fails to play audio from some sources\n\t\t\t\t\t\twith MIME-types containing a `codecs` parameter, despite the fact\n\t\t\t\t\t\tthat `canPlayType()` blessed the full MIME-type including `codecs`.\n\n\t\t\t\t\t\tBizarrely, this only affects some MIME-types—e.g. MP3s are affected,\n\t\t\t\t\t\twhile WAVEs are not.\n\t\t\t\t\t\t\tFails: 'audio/mpeg; codecs=\"mp3\"'\n\t\t\t\t\t\t\tPlays: 'audio/mpeg'\n\t\t\t\t\t\t\tPlays: 'audio/wav; codecs=\"1\"'\n\n\t\t\t\t\t\tTo workaround this we remove all parameters from the MIME-type in\n\t\t\t\t\t\tOpera.\n\t\t\t\t\t*/\n\t\t\t\t\tif (Browser.isOpera) {\n\t\t\t\t\t\tsrcObj.type =  srcObj.type.replace(/;.*$/, '');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\taudio.appendChild(source);\n\t\t\t\t\tusedSources.push(srcObj);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (audio.hasChildNodes()) {\n\t\t\t\t// Set the `preload` attribute to `'metadata'`, unless preloading has been disabled.\n\t\t\t\tif (Config.audio.preloadMetadata) {\n\t\t\t\t\taudio.preload = 'metadata';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._finalize(audio, usedSources, clone(sourceList));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(\n\t\t\t\tobj.audio.cloneNode(true), // deep clone of the audio element & its children\n\t\t\t\tclone(obj.sources),\n\t\t\t\tclone(obj.originals)\n\t\t\t);\n\t\t}\n\n\t\t_finalize(audio, sources, originals) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\taudio : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : audio\n\t\t\t\t},\n\n\t\t\t\tsources : {\n\t\t\t\t\tvalue : Object.freeze(sources)\n\t\t\t\t},\n\n\t\t\t\toriginals : {\n\t\t\t\t\tvalue : Object.freeze(originals)\n\t\t\t\t},\n\n\t\t\t\t_error : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_faderId : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up event handlers on the audio and source elements.\n\t\t\tjQuery(this.audio)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving a `loadstart` event on the audio element, set `_error` to\n\t\t\t\t\t`false`.\n\t\t\t\t*/\n\t\t\t\t.on('loadstart.AudioTrack', () => this._error = false)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the audio element, set `_error` to\n\t\t\t\t\t`true`.\n\n\t\t\t\t\tCaveats by browser:\n\t\t\t\t\t\tEdge violates the specification by triggering `error` events from source\n\t\t\t\t\t\telements on their parent media element, rather than the source element.\n\t\t\t\t\t\tTo enable error handling in all browsers, we set the error handler on the\n\t\t\t\t\t\taudio element and have the final source element forward its `error` event.\n\n\t\t\t\t\t\tIE does not trigger, at least some, `error` events from source elements at\n\t\t\t\t\t\tall, not on the source element or its parent media element.  AFAIK, nothing\n\t\t\t\t\t\tcan be done about this lossage.\n\t\t\t\t*/\n\t\t\t\t.on('error.AudioTrack', () => this._error = true)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the final source element (if any), trigger\n\t\t\t\t\tan `error` event on the audio element—that being necessary because the source\n\t\t\t\t\t`error` event does not bubble.\n\t\t\t\t*/\n\t\t\t\t.find('source:last-of-type')\n\t\t\t\t.on('error.AudioTrack', () => this._trigger('error'));\n\n\t\t\t// Subscribe to command messages.\n\t\t\tsubscribe(this, mesg => {\n\t\t\t\tif (!this.audio) {\n\t\t\t\t\tunsubscribe(this);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tswitch (mesg) {\n\t\t\t\tcase 'loadwithscreen':\n\t\t\t\t\tif (this.hasSource()) {\n\t\t\t\t\t\tconst lockId = LoadScreen.lock();\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t// NOTE: Do not use an arrow function here.\n\t\t\t\t\t\t\t.one(\n\t\t\t\t\t\t\t\t'canplaythrough.AudioTrack_loadwithscreen error.AudioTrack_loadwithscreen',\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\tjQuery(this).off('.AudioTrack_loadwithscreen');\n\t\t\t\t\t\t\t\t\tLoadScreen.unlock(lockId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.load();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'load':   this.load();               break;\n\t\t\t\tcase 'mute':   this._updateAudioMute();   break;\n\t\t\t\tcase 'rate':   this._updateAudioRate();   break;\n\t\t\t\tcase 'stop':   this.stop();               break;\n\t\t\t\tcase 'volume': this._updateAudioVolume(); break;\n\t\t\t\tcase 'unload': this.unload();             break;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Synchronize with the current master audio settings.\n\t\t\tthis._updateAudioMute();\n\t\t\tthis._updateAudioRate();\n\t\t\tthis._updateAudioVolume();\n\t\t}\n\n\t\t_trigger(eventName) {\n\t\t\t// Do not use `trigger()` here as we do not want these events to bubble.\n\t\t\tjQuery(this.audio).triggerHandler(eventName);\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.  That said, since the audio element contains\n\t\t\t\tdata buffers for the selected audio source, which may be quite large, manually\n\t\t\t\tpurging them as soon as we know that they're no longer needed is not a bad idea.\n\t\t\t*/\n\t\t\tunsubscribe(this);\n\n\t\t\tif (!this.audio) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(this.audio).off();\n\t\t\tthis.unload();\n\t\t\tthis._error = true;\n\n\t\t\t// Delete the audio element property.\n\t\t\tdelete this.audio;\n\t\t}\n\n\t\tclone() {\n\t\t\treturn new AudioTrack(this);\n\t\t}\n\n\t\tload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.audio.pause();\n\n\t\t\tif (!this.audio.hasChildNodes()) {\n\t\t\t\tif (this.sources.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.sources.forEach(srcObj => {\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\tthis.audio.appendChild(source);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tif (!this.isLoading()) {\n\t\t\t\tthis.audio.load();\n\t\t\t}\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.stop();\n\n\t\t\tconst audio = this.audio;\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Remove all source elements.\n\t\t\twhile (audio.hasChildNodes()) {\n\t\t\t\taudio.removeChild(audio.firstChild);\n\t\t\t}\n\n\t\t\t// Force the audio element to drop any existing data buffers.\n\t\t\taudio.load();\n\t\t}\n\n\t\tplay() {\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tconst namespace = '.AudioTrack_play';\n\n\t\t\treturn _playReturnsPromise()\n\t\t\t\t? this.audio.play()\n\t\t\t\t: new Promise((resolve, reject) => {\n\t\t\t\t\tif (this.isPlaying()) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t\t\t.off(namespace)\n\t\t\t\t\t\t\t.one(`error${namespace} playing${namespace} timeupdate${namespace}`, ev => {\n\t\t\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\t\t\tif (ev.type === 'error') {\n\t\t\t\t\t\t\t\t\treject(new Error('unknown audio play error'));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.audio.play();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioTrack_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioTrack_playWhenAllowed');\n\t\t\t\t\tthis.audio.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tthis.audio.pause();\n\t\t}\n\n\t\tstop() {\n\t\t\tthis.audio.pause();\n\t\t\tthis.time(0);\n\t\t\tthis._trigger(':stopped');\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tthis.fadeStop();\n\n\t\t\tconst from = Math.clamp(fromVol == null ? this.volume() : fromVol, 0, 1); // lazy equality for null\n\t\t\tconst to   = Math.clamp(toVol, 0, 1);\n\n\t\t\tif (from === to) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.volume(from);\n\n\t\t\t/*\n\t\t\t\tWe listen for the `timeupdate` event here, rather than `playing`, because\n\t\t\t\tvarious browsers (notably, mobile browsers) are poor at firing media events\n\t\t\t\tin a timely fashion, so we use `timeupdate` to ensure that we don't start\n\t\t\t\tthe fade until the track is actually progressing.\n\t\t\t*/\n\t\t\tjQuery(this.audio)\n\t\t\t\t.off('timeupdate.AudioTrack_fade')\n\t\t\t\t.one('timeupdate.AudioTrack_fade', () => {\n\t\t\t\t\tlet min;\n\t\t\t\t\tlet max;\n\n\t\t\t\t\t// Fade in.\n\t\t\t\t\tif (from < to) {\n\t\t\t\t\t\tmin = from;\n\t\t\t\t\t\tmax = to;\n\t\t\t\t\t}\n\t\t\t\t\t// Fade out.\n\t\t\t\t\telse {\n\t\t\t\t\t\tmin = to;\n\t\t\t\t\t\tmax = from;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst time     = Math.max(duration, 1);\n\t\t\t\t\tconst interval = 25; // in milliseconds\n\t\t\t\t\tconst delta    = (to - from) / (time / (interval / 1000));\n\n\t\t\t\t\tthis._trigger(':fading');\n\t\t\t\t\tthis._faderId = setInterval(() => {\n\t\t\t\t\t\tif (!this.isPlaying()) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWhile it may seem like a good idea to also set the track volume\n\t\t\t\t\t\t\t\tto the `to` value here, we should not do so.  We cannot know why\n\t\t\t\t\t\t\t\tthe track is no longer playing, nor if the volume has been modified\n\t\t\t\t\t\t\t\tin the interim, so doing so now may clobber an end-user set volume.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.volume(Math.clamp(this.volume() + delta, min, max));\n\n\t\t\t\t\t\tif (Config.audio.pauseOnFadeToZero && this.volume() === 0) {\n\t\t\t\t\t\t\tthis.pause();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.volume() === to) {\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\tthis._trigger(':faded');\n\t\t\t\t\t\t}\n\t\t\t\t\t}, interval);\n\t\t\t\t});\n\n\t\t\treturn this.play();\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this._faderId !== null) {\n\t\t\t\tclearInterval(this._faderId);\n\t\t\t\tthis._faderId = null;\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this.audio.loop;\n\t\t\t}\n\n\t\t\tthis.audio.loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\t\t\tthis._updateAudioMute();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioMute() {\n\t\t\tthis.audio.muted = this._mute || _masterMute;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tClamp the playback rate to sane values—some browsers also do this to varying degrees.\n\n\t\t\t\tNOTE (ca. Aug 2016): The specification allows negative values for reverse playback,\n\t\t\t\thowever, most browsers either completely ignore negative values or clamp them to\n\t\t\t\tsome positive value.  In some (notably, IE & Edge), setting a negative playback\n\t\t\t\trate breaks the associated controls, if displayed.\n\t\t\t*/\n\t\t\t/*\n\t\t\tthis._rate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\t\tthis._updateAudioRate();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioRate() {\n\t\t\t/*\n\t\t\tconst rate = this._rate * _masterRate;\n\t\t\tthis.audio.playbackRate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis.audio.playbackRate = Math.clamp(this._rate * _masterRate, 0.2, 5); // clamp to 5× slower & faster\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\treturn this.audio.currentTime;\n\t\t\t}\n\n\t\t\tif (typeof time !== 'number') {\n\t\t\t\tthrow new TypeError('time parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE (historic): If we try to modify the audio clip's `.currentTime` property\n\t\t\t\tbefore its metadata has been loaded, it will throw an `InvalidStateError`\n\t\t\t\t(since it doesn't know its duration, allowing `.currentTime` to be set would\n\t\t\t\tbe undefined behavior), so in case an exception is thrown we provide a fallback\n\t\t\t\tusing the `loadedmetadata` event.\n\n\t\t\t\tNOTE (ca. 2016): This workaround should no longer be necessary in most browsers.\n\t\t\t\tThat said, it will still be required for some time to service legacy browsers.\n\n\t\t\t\tNOTE (ca. Dec 09, 2018): Firefox will still log an `InvalidStateError` to the\n\t\t\t\tconsole when attempting to modify the clip's `.currentTime` property before its\n\t\t\t\tmetadata has been loaded, even though it handles the situation properly—by waiting\n\t\t\t\tfor the metadata, as all browsers do now.  To prevent this spurious logging, we\n\t\t\t\tmust now manually check for the existence of the metadata and always failover to\n\t\t\t\tan event regardless of if the browser needs it or not—because I don't want to\n\t\t\t\tintroduce a browser check here.  Stay classy, Firefox.\n\t\t\t*/\n\t\t\tif (this.hasMetadata()) {\n\t\t\t\tthis.audio.currentTime = time;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t.off('loadedmetadata.AudioTrack_time')\n\t\t\t\t\t.one('loadedmetadata.AudioTrack_time', () => this.audio.currentTime = time);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\t\tthis._updateAudioVolume();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioVolume() {\n\t\t\tthis.audio.volume = Math.clamp(this._volume * _masterVolume, 0, 1);\n\t\t}\n\n\t\tduration() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration;\n\t\t}\n\n\t\tremaining() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration - this.audio.currentTime;\n\t\t}\n\n\t\tisFailed() {\n\t\t\treturn this._error;\n\t\t}\n\n\t\tisLoading() {\n\t\t\treturn this.audio.networkState === HTMLMediaElement.NETWORK_LOADING;\n\t\t}\n\n\t\tisUnloaded() {\n\t\t\treturn !this.audio.hasChildNodes();\n\t\t}\n\n\t\tisUnavailable() {\n\t\t\treturn !this.hasSource() || this.isUnloaded() || this.isFailed();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\t// NOTE: The `this.hasSomeData()` check is probably no longer necessary.\n\t\t\treturn !this.audio.paused && this.hasSomeData();\n\t\t}\n\n\t\tisPaused() {\n\t\t\t/*\n\t\t\t\tIf the selected audio resource is a stream, `currentTime` may return a non-zero\n\t\t\t\tvalue even at the earliest available position within the stream as the browser\n\t\t\t\tmay have dropped the earliest chunks of buffered data or the stream may have a\n\t\t\t\ttimeline which does not start at zero.\n\n\t\t\t\tIn an attempt to guard against these possiblities, as best as we can, we test\n\t\t\t\t`duration` against `Infinity` first, which should yield true for actual streams.\n\t\t\t*/\n\t\t\treturn this.audio.paused\n\t\t\t\t&& (this.audio.duration === Infinity || this.audio.currentTime > 0)\n\t\t\t\t&& !this.audio.ended;\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.audio.paused && this.audio.currentTime === 0;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.audio.ended;\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this._faderId !== null;\n\t\t}\n\n\t\tisSeeking() {\n\t\t\treturn this.audio.seeking;\n\t\t}\n\n\t\thasSource() {\n\t\t\treturn this.sources.length > 0;\n\t\t}\n\n\t\thasNoData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_NOTHING;\n\t\t}\n\n\t\thasMetadata() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_METADATA;\n\t\t}\n\n\t\thasSomeData() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA;\n\t\t}\n\n\t\thasData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tjQuery.fn.on.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tjQuery.fn.one.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tjQuery.fn.off.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\t/*\n\t\t\tVerifies that the browser supports the given MIME-type and then retuns either\n\t\t\tthe MIME-type, if it is supported, or `null`, if it is not.\n\t\t*/\n\t\tstatic _verifyType(type) {\n\t\t\tif (!type || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cache = AudioTrack._types;\n\n\t\t\tif (!cache.hasOwnProperty(type)) {\n\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t// Some early implementations return 'no' instead of the empty string.\n\t\t\t\tcache[type] = audio.canPlayType(type).replace(/^no$/i, '') !== '';\n\t\t\t}\n\n\t\t\treturn cache[type] ? type : null;\n\t\t}\n\n\t\t/*\n\t\t\tRetuns the MIME-type associated with the given format-ID, if it is supported,\n\t\t\telsewise `null`.\n\t\t*/\n\t\tstatic getType(format) {\n\t\t\tif (!format || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst known = AudioTrack.formats;\n\t\t\tconst id    = format.toLowerCase();\n\t\t\tconst type  = known.hasOwnProperty(id) ? known[id] : `audio/${id}`;\n\n\t\t\treturn AudioTrack._verifyType(type);\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a format.\n\t\t*/\n\t\tstatic canPlayFormat(format) {\n\t\t\treturn AudioTrack.getType(format) !== null;\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a MIME-type.\n\t\t*/\n\t\tstatic canPlayType(type) {\n\t\t\treturn AudioTrack._verifyType(type) !== null;\n\t\t}\n\t}\n\n\t// Attach the static data members.\n\tObject.defineProperties(AudioTrack, {\n\t\t/*\n\t\t\tFormat-ID to MIME-type mappings for common audio types.\n\n\t\t\tIn most cases, the codecs property should not be included with the MIME-type,\n\t\t\tas we have no way of knowing which codec was used—and the browser will figure\n\t\t\tit out.  Conversely, in cases where the relationship relationship between a\n\t\t\tformat-ID and a specific codec is strong, we should include the codecs property.\n\n\t\t\tNOTE: Caveats by browser/engine:\n\t\t\t\tOpera ≤12 (Presto) will return a false-negative if the codecs value is quoted\n\t\t\t\twith single quotes, requiring the use of either double quotes or no quotes.\n\n\t\t\t\tBlink-based browsers (e.g. Chrome, Opera ≥15) will return a false-negative\n\t\t\t\tfor WAVE audio if the preferred MIME-type of 'audio/wave' is specified,\n\t\t\t\trequiring the use of 'audio/wav' instead.\n\t\t*/\n\t\tformats : {\n\t\t\tvalue : { // Leave this object extensible for users.\n\t\t\t\t// AAC — MPEG-2 AAC audio; specific profiles vary, but commonly \"AAC-LC\".\n\t\t\t\taac : 'audio/aac',\n\n\t\t\t\t// CAF — Codecs vary.\n\t\t\t\tcaf     : 'audio/x-caf',\n\t\t\t\t'x-caf' : 'audio/x-caf',\n\n\t\t\t\t// MP3 — MPEG-1/-2 Layer-III audio.\n\t\t\t\tmp3  : 'audio/mpeg; codecs=\"mp3\"',\n\t\t\t\tmpeg : 'audio/mpeg; codecs=\"mp3\"',\n\n\t\t\t\t// MP4 — Codecs vary, but commonly \"mp4a.40.2\" (a.k.a. \"AAC-LC\").\n\t\t\t\tm4a     : 'audio/mp4',\n\t\t\t\tmp4     : 'audio/mp4',\n\t\t\t\t'x-m4a' : 'audio/mp4',\n\t\t\t\t'x-mp4' : 'audio/mp4',\n\n\t\t\t\t// OGG — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\toga : 'audio/ogg',\n\t\t\t\togg : 'audio/ogg',\n\n\t\t\t\t// OPUS — Opus audio in an Ogg container.\n\t\t\t\topus : 'audio/ogg; codecs=\"opus\"',\n\n\t\t\t\t// WAVE — Codecs vary, but commonly \"1\" (1 is the FourCC for PCM/LPCM).\n\t\t\t\twav  : 'audio/wav',\n\t\t\t\twave : 'audio/wav',\n\n\t\t\t\t// WEBM — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\tweba : 'audio/webm',\n\t\t\t\twebm : 'audio/webm'\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tCache of supported MIME-types.\n\t\t*/\n\t\t_types : {\n\t\t\tvalue : {}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudioList Class.\n\t*******************************************************************************************************************/\n\tclass AudioList {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of track objects or AudioList object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioList) {\n\t\t\t\tthis._copy(obj);\n\t\t\t\t// this._create(obj.tracks);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('tracks parameter must be either an array, of track objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(trackList) {\n\t\t\t// Map the array of tracks to playlist track objects.\n\t\t\tthis._finalize(trackList.map(trackObj => {\n\t\t\t\tif (typeof trackObj !== 'object') { // lazy equality for null\n\t\t\t\t\tthrow new Error('tracks parameter array members must be objects');\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\tlet rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (trackObj instanceof AudioTrack) {\n\t\t\t\t\town    = true;\n\t\t\t\t\trate   = trackObj.rate();\n\t\t\t\t\ttrack  = trackObj.clone();\n\t\t\t\t\tvolume = trackObj.volume();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (!trackObj.hasOwnProperty('track')) {\n\t\t\t\t\t\tthrow new Error('track object missing required \"track\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (!(trackObj.track instanceof AudioTrack)) {\n\t\t\t\t\t\tthrow new Error('track object\\'s \"track\" property must be an AudioTrack object');\n\t\t\t\t\t}\n\t\t\t\t\t// else if (!trackObj.hasOwnProperty('volume')) {\n\t\t\t\t\t// \tthrow new Error('track object missing required \"volume\" property');\n\t\t\t\t\t// }\n\n\t\t\t\t\town    = trackObj.hasOwnProperty('own') && trackObj.own;\n\t\t\t\t\trate   = trackObj.hasOwnProperty('rate') ? trackObj.rate : trackObj.track.rate();\n\t\t\t\t\ttrack  = trackObj.track;\n\t\t\t\t\tvolume = trackObj.hasOwnProperty('volume') ? trackObj.volume : trackObj.track.volume();\n\t\t\t\t}\n\n\t\t\t\ttrack.stop();\n\t\t\t\ttrack.loop(false);\n\t\t\t\ttrack.mute(false);\n\t\t\t\ttrack.rate(rate);\n\t\t\t\ttrack.volume(volume);\n\t\t\t\ttrack.on('ended.AudioList', () => this._onEnd());\n\n\t\t\t\treturn { own, track, volume, rate };\n\t\t\t}));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(clone(obj.tracks));\n\t\t}\n\n\t\t_finalize(tracks) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttracks : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : Object.freeze(tracks)\n\t\t\t\t},\n\n\t\t\t\tqueue : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : []\n\t\t\t\t},\n\n\t\t\t\tcurrent : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_loop : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_shuffle : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.\n\t\t\t*/\n\t\t\t// Stop playback.\n\t\t\tthis.stop();\n\n\t\t\t// Destroy all owned tracks.\n\t\t\tthis.tracks\n\t\t\t\t.filter(trackObj => trackObj.own)\n\t\t\t\t.forEach(trackObj => trackObj.track._destroy());\n\n\t\t\t// Delete the reference-type properties.\n\t\t\tdelete this.tracks;\n\t\t\tdelete this.queue;\n\t\t}\n\n\t\tload() {\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.load());\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.stop();\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.unload());\n\t\t}\n\n\t\tplay() {\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (this.queue.length === 0) {\n\t\t\t\t\tthis._fillQueue();\n\t\t\t\t}\n\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn Promise.reject(new Error('no tracks were available'));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.current.track.play();\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioList_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioList_playWhenAllowed');\n\t\t\t\t\tthis.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.pause();\n\t\t\t}\n\t\t}\n\n\t\tstop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tthis._drainQueue();\n\t\t}\n\n\t\tskip() {\n\t\t\tif (this._next()) {\n\t\t\t\tthis.current.track.play();\n\t\t\t}\n\t\t\telse if (this._loop) {\n\t\t\t\tthis.play();\n\t\t\t}\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adjToVol = Math.clamp(toVol, 0, 1) * this.current.volume;\n\t\t\tlet adjFromVol;\n\n\t\t\tif (fromVol != null) { // lazy equality for null\n\t\t\t\tadjFromVol = Math.clamp(fromVol, 0, 1) * this.current.volume;\n\t\t\t}\n\n\t\t\tthis._volume = toVol; // NOTE: Kludgey, but necessary.\n\n\t\t\treturn this.current.track.fade(duration, adjToVol, adjFromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.fadeStop();\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this._loop;\n\t\t\t}\n\n\t\t\tthis._loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.mute(this._mute);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tshuffle(shuffle) {\n\t\t\tif (shuffle == null) { // lazy equality for null\n\t\t\t\treturn this._shuffle;\n\t\t\t}\n\n\t\t\tthis._shuffle = !!shuffle;\n\n\t\t\tif (this.queue.length > 0) {\n\t\t\t\tthis._fillQueue();\n\n\t\t\t\t// Try not to immediately replay the last track when not shuffling.\n\t\t\t\tif (!this._shuffle && this.current !== null && this.queue.length > 1) {\n\t\t\t\t\tconst firstIdx = this.queue.findIndex(trackObj => trackObj === this.current);\n\n\t\t\t\t\tif (firstIdx !== -1) {\n\t\t\t\t\t\tthis.queue.push(...this.queue.splice(0, firstIdx + 1));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tduration() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('duration takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.tracks\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\t\t}\n\n\t\tremaining() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('remaining takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\tlet remainingTime = this.queue\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tremainingTime += this.current.track.remaining();\n\t\t\t}\n\n\t\t\treturn remainingTime;\n\t\t}\n\n\t\ttime() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('time takes no parameters');\n\t\t\t}\n\n\t\t\treturn this.duration() - this.remaining();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\treturn this.current !== null && this.current.track.isPlaying();\n\t\t}\n\n\t\tisPaused() {\n\t\t\treturn this.current === null || this.current.track.isPaused();\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.queue.length === 0 && this.current === null;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.queue.length === 0 && (this.current === null || this.current.track.isEnded());\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this.current !== null && this.current.track.isFading();\n\t\t}\n\n\t\t_next() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tlet nextTrack;\n\n\t\t\twhile ((nextTrack = this.queue.shift())) {\n\t\t\t\tif (!nextTrack.track.isUnavailable()) {\n\t\t\t\t\tthis.current = nextTrack;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.current === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.current.track.mute(this._mute);\n\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\n\t\t\t// Attempt to protect against the `loop` state being reenabled\n\t\t\t// outside of the playlist.  Mostly for unowned tracks.\n\t\t\t//\n\t\t\t// TODO: Should we reapply the `ended` event handler too?\n\t\t\tthis.current.track.loop(false);\n\n\t\t\treturn true;\n\t\t}\n\n\t\t_onEnd() {\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tif (!this._loop) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (!this._next()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.current.track.play();\n\t\t}\n\n\t\t_drainQueue() {\n\t\t\tthis.queue.splice(0);\n\t\t}\n\n\t\t_fillQueue() {\n\t\t\tthis._drainQueue();\n\t\t\tthis.queue.push(...this.tracks.filter(trackObj => !trackObj.track.isUnavailable()));\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this._shuffle) {\n\t\t\t\tthis.queue.shuffle();\n\n\t\t\t\t// Try not to immediately replay the last track when shuffling.\n\t\t\t\tif (this.queue.length > 1 && this.queue[0] === this.current) {\n\t\t\t\t\tthis.queue.push(this.queue.shift());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAudioRunner Class.\n\t*******************************************************************************************************************/\n\tclass AudioRunner {\n\t\tconstructor(list) {\n\t\t\tif (!(list instanceof Set || list instanceof AudioRunner)) {\n\t\t\t\tthrow new TypeError('list parameter must be a Set or a AudioRunner instance');\n\t\t\t}\n\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttrackIds : {\n\t\t\t\t\tvalue : new Set(list instanceof AudioRunner ? list.trackIds : list)\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.load);\n\t\t}\n\n\t\tunload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.unload);\n\t\t}\n\n\t\tplay() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.play);\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.playWhenAllowed);\n\t\t}\n\n\t\tpause() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.pause);\n\t\t}\n\n\t\tstop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.stop);\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (duration == null || toVol == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fade requires parameters');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fade, duration, toVol, fromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeIn requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeIn, duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeOut requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeOut, duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeStop);\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\tthrow new Error('loop requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.loop, loop);\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\tthrow new Error('mute requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.mute, mute);\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\tthrow new Error('rate requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.rate, rate);\n\t\t\treturn this;\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\tthrow new Error('time requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.time, time);\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\tthrow new Error('volume requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.volume, volume);\n\t\t\treturn this;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.on, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.one, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.off, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tstatic _run(ids, fn, ...args) {\n\t\t\tids.forEach(id => {\n\t\t\t\tconst track = _tracks.get(id);\n\n\t\t\t\tif (track) {\n\t\t\t\t\tfn.apply(track, args);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTrack Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.tracks.add(trackId, sources…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.tracks.add(\n\t\t\t\t'over_the_top',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.mp3',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.ogg'\n\t\t\t);\n\t*/\n\tfunction trackAdd(/* trackId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('track ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('sources'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `track ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\tthrow new Error(`invalid ${what}: track IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst sources = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet track;\n\n\t\ttry {\n\t\t\ttrack = _newTrack(sources);\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during track initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If in Test Mode and no supported sources were specified, throw an error.\n\t\tif (Config.debug && !track.hasSource()) {\n\t\t\tthrow new Error(`${what}: no supported audio sources found`);\n\t\t}\n\n\t\t// If a track by the given ID already exists, destroy it.\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// Add the track to the cache.\n\t\t_tracks.set(id, track);\n\t}\n\n\tfunction trackDelete(id) {\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// TODO: Should this also remove references to the track from groups and playlists?\n\n\t\treturn _tracks.delete(id);\n\t}\n\n\tfunction trackClear() {\n\t\t_tracks.forEach(track => track._destroy());\n\t\t_tracks.clear();\n\t}\n\n\tfunction trackHas(id) {\n\t\treturn _tracks.has(id);\n\t}\n\n\tfunction trackGet(id) {\n\t\treturn _tracks.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tGroup Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.groups.add(groupId, trackIds…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.groups.add(':ui', 'beep', 'boop', 'boing');\n\t*/\n\tfunction groupAdd(/* groupId , trackIds… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('group ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `group ID \"${id}\"`;\n\n\t\tif (id[0] !== ':' || _badIdRe.test(id.slice(1))) {\n\t\t\tthrow new Error(`invalid ${what}: group IDs must start with a colon and must not contain colons or whitespace`);\n\t\t}\n\n\t\tif (_specialIds.includes(id)) {\n\t\t\tthrow new Error(`cannot clobber special ${what}`);\n\t\t}\n\n\t\tconst trackIds = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet group;\n\n\t\ttry {\n\t\t\tgroup = new Set(trackIds.map(trackId => {\n\t\t\t\tif (!_tracks.has(trackId)) {\n\t\t\t\t\tthrow new Error(`track \"${trackId}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\treturn trackId;\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during group initialization: ${ex.message}`);\n\t\t}\n\n\t\t// Add the group to the cache.\n\t\t_groups.set(id, Object.freeze(Array.from(group)));\n\t}\n\n\tfunction groupDelete(id) {\n\t\treturn _groups.delete(id);\n\t}\n\n\tfunction groupClear() {\n\t\t_groups.clear();\n\t}\n\n\tfunction groupHas(id) {\n\t\treturn _groups.has(id);\n\t}\n\n\tfunction groupGet(id) {\n\t\treturn _groups.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPlaylist Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.lists.add(listId, sources…);\n\t\t\tWhere `sources` may be either a track ID or descriptor (object).\n\t\t\tTrack descriptors are either { id, [own], [rate], [volume] } or { sources, [rate], [volume] }.\n\n\t\tNOTE: Rate properties are currently unsupported due to poor browser support.\n\n\t\tE.g.\n\t\t\tSimpleAudio.lists.add(\n\t\t\t\t'bgm',\n\t\t\t\t'over_the_top',\n\t\t\t\t{\n\t\t\t\t\tid     : 'heavens_a_lie',\n\t\t\t\t\tvolume : 0.5,\n\t\t\t\t\town    : true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tsources : [\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.mp3',\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.ogg'\n\t\t\t\t\t],\n\t\t\t\t\tvolume  : 0.75\n\t\t\t\t}\n\t\t\t);\n\t*/\n\tfunction listAdd(/* listId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('list ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `list ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\treturn this.error(`invalid ${what}: list IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst descriptors = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet list;\n\n\t\ttry {\n\t\t\tlist = new AudioList(descriptors.map(desc => {\n\t\t\t\tif (desc === null) {\n\t\t\t\t\tthrow new Error('track descriptor must be a string or object (type: null)');\n\t\t\t\t}\n\n\t\t\t\tswitch (typeof desc) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t// Simply a track ID, so convert it into an object.\n\t\t\t\t\tdesc = { id : desc }; // eslint-disable-line no-param-reassign\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (!desc.hasOwnProperty('id') && !desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain one of either an \"id\" or a \"sources\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (desc.hasOwnProperty('id') && desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain either an \"id\" or a \"sources\" property, not both');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`track descriptor must be a string or object (type: ${typeof desc})`);\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\t// let rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (desc.hasOwnProperty('id')) {\n\t\t\t\t\tif (typeof desc.id !== 'string') {\n\t\t\t\t\t\tthrow new Error('\"id\" property must be a string');\n\t\t\t\t\t}\n\t\t\t\t\tif (!_tracks.has(desc.id)) {\n\t\t\t\t\t\tthrow new Error(`track \"${desc.id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\ttrack = _tracks.get(desc.id);\n\t\t\t\t}\n\t\t\t\telse if (desc.hasOwnProperty('sources')) {\n\t\t\t\t\tif (!Array.isArray(desc.sources) || desc.sources.length === 0) {\n\t\t\t\t\t\tthrow new Error('\"sources\" property must be a non-empty array');\n\t\t\t\t\t}\n\t\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\t\tthrow new Error('\"own\" property is not allowed with the \"sources\" property');\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttrack = _newTrack(desc.sources);\n\t\t\t\t\t\town = true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`error during track initialization: ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\t\tif (Config.debug && !track.hasSource()) {\n\t\t\t\t\t\tthrow new Error('no supported audio sources found');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\tif (typeof desc.own !== 'boolean') {\n\t\t\t\t\t\tthrow new Error('\"own\" property must be a boolean');\n\t\t\t\t\t}\n\n\t\t\t\t\town = desc.own;\n\n\t\t\t\t\tif (own) {\n\t\t\t\t\t\ttrack = track.clone();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if (desc.hasOwnProperty('rate')) {\n\t\t\t\t// \tif (\n\t\t\t\t// \t\t   typeof desc.rate !== 'number'\n\t\t\t\t// \t\t|| Number.isNaN(desc.rate)\n\t\t\t\t// \t\t|| !Number.isFinite(desc.rate)\n\t\t\t\t// \t) {\n\t\t\t\t// \t\tthrow new Error('\"rate\" property must be a finite number');\n\t\t\t\t// \t}\n\t\t\t\t//\n\t\t\t\t// \trate = desc.rate;\n\t\t\t\t// }\n\n\t\t\t\tif (desc.hasOwnProperty('volume')) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t   typeof desc.volume !== 'number'\n\t\t\t\t\t\t|| Number.isNaN(desc.volume)\n\t\t\t\t\t\t|| !Number.isFinite(desc.volume)\n\t\t\t\t\t\t|| desc.volume < 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error('\"volume\" property must be a non-negative finite number');\n\t\t\t\t\t}\n\n\t\t\t\t\tvolume = desc.volume;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\town    : own != null ? own : false, // lazy equality for null,\n\t\t\t\t\t// rate   : rate != null ? rate : track.rate(), // lazy equality for null,\n\t\t\t\t\ttrack,\n\t\t\t\t\tvolume : volume != null ? volume : track.volume() // lazy equality for null\n\t\t\t\t};\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during playlist initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If a playlist by the given ID already exists, destroy it.\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\t// Add the playlist to the cache.\n\t\t_lists.set(id, list);\n\t}\n\n\tfunction listDelete(id) {\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\treturn _lists.delete(id);\n\t}\n\n\tfunction listClear() {\n\t\t_lists.forEach(list => list._destroy());\n\t\t_lists.clear();\n\t}\n\n\tfunction listHas(id) {\n\t\treturn _lists.has(id);\n\t}\n\n\tfunction listGet(id) {\n\t\treturn _lists.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tRunner Functions.\n\t*******************************************************************************************************************/\n\tconst _runnerParseSelector = (() => {\n\t\tconst notWsRe = /\\S/g;\n\t\tconst parenRe = /[()]/g;\n\n\t\tfunction processNegation(str, startPos) {\n\t\t\tlet match;\n\n\t\t\tnotWsRe.lastIndex = startPos;\n\t\t\tmatch = notWsRe.exec(str);\n\n\t\t\tif (match === null || match[0] !== '(') {\n\t\t\t\tthrow new Error('invalid \":not()\" syntax: missing parentheticals');\n\t\t\t}\n\n\t\t\tparenRe.lastIndex = notWsRe.lastIndex;\n\t\t\tconst start  = notWsRe.lastIndex;\n\t\t\tconst result = { str : '', nextMatch : -1 };\n\t\t\tlet depth = 1;\n\n\t\t\twhile ((match = parenRe.exec(str)) !== null) {\n\t\t\t\tif (match[0] === '(') {\n\t\t\t\t\t++depth;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t--depth;\n\t\t\t\t}\n\n\t\t\t\tif (depth < 1) {\n\t\t\t\t\tresult.nextMatch = parenRe.lastIndex;\n\t\t\t\t\tresult.str = str.slice(start, result.nextMatch - 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tfunction parseSelector(idArg) {\n\t\t\tconst ids  = [];\n\t\t\tconst idRe = /:?[^\\s:()]+/g;\n\t\t\tlet match;\n\n\t\t\twhile ((match = idRe.exec(idArg)) !== null) {\n\t\t\t\tconst id = match[0];\n\n\t\t\t\t// Group negation.\n\t\t\t\tif (id === ':not') {\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error('invalid negation: no group ID preceded \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst parent = ids[ids.length - 1];\n\n\t\t\t\t\tif (parent.id[0] !== ':') {\n\t\t\t\t\t\tthrow new Error(`invalid negation of track \"${parent.id}\": only groups may be negated with \":not()\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst negation = processNegation(idArg, idRe.lastIndex);\n\n\t\t\t\t\tif (negation.nextMatch === -1) {\n\t\t\t\t\t\tthrow new Error('unknown error parsing \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tidRe.lastIndex = negation.nextMatch;\n\t\t\t\t\tparent.not = parseSelector(negation.str);\n\t\t\t\t}\n\n\t\t\t\t// Group or track ID.\n\t\t\t\telse {\n\t\t\t\t\tids.push({ id });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ids;\n\t\t}\n\n\t\treturn parseSelector;\n\t})();\n\n\t/*\n\t\tSimpleAudio.select(selector).…;\n\n\t\tE.g.\n\t\t\tSimpleAudio.select(':ui').…\n\t\t\tSimpleAudio.select(':ui:not(boop)').…\n\t\t\tSimpleAudio.select('boop beep').…\n\t\t\tSimpleAudio.select(':ui :sfx').…\n\t\t\tSimpleAudio.select(':ui:not(boop) :sfx overthetop').…\n\t*/\n\tfunction runnerGet(/* selector */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('no track selector specified');\n\t\t}\n\n\t\tconst selector = String(arguments[0]).trim();\n\t\tconst trackIds = new Set();\n\n\t\ttry {\n\t\t\tconst allIds = Array.from(_tracks.keys());\n\n\t\t\tfunction renderIds(idObj) {\n\t\t\t\tconst id = idObj.id;\n\t\t\t\tlet ids;\n\n\t\t\t\tswitch (id) {\n\t\t\t\tcase ':all':     ids = allIds; break;\n\t\t\t\tcase ':looped':  ids = allIds.filter(id => _tracks.get(id).loop()); break;\n\t\t\t\tcase ':muted':   ids = allIds.filter(id => _tracks.get(id).mute()); break;\n\t\t\t\tcase ':paused':  ids = allIds.filter(id => _tracks.get(id).isPaused()); break;\n\t\t\t\tcase ':playing': ids = allIds.filter(id => _tracks.get(id).isPlaying()); break;\n\t\t\t\tdefault:         ids = id[0] === ':' ? _groups.get(id) : [id]; break;\n\t\t\t\t}\n\n\t\t\t\tif (idObj.hasOwnProperty('not')) {\n\t\t\t\t\tconst negated = idObj.not.map(idObj => renderIds(idObj)).flat(Infinity);\n\t\t\t\t\tids = ids.filter(id => !negated.includes(id));\n\t\t\t\t}\n\n\t\t\t\treturn ids;\n\t\t\t}\n\n\t\t\t_runnerParseSelector(selector).forEach(idObj => renderIds(idObj).forEach(id => {\n\t\t\t\tif (!_tracks.has(id)) {\n\t\t\t\t\tthrow new Error(`track \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\ttrackIds.add(id);\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`error during runner initialization: ${ex.message}`);\n\t\t}\n\n\t\treturn new AudioRunner(trackIds);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMaster Audio Functions.\n\t*******************************************************************************************************************/\n\tfunction masterLoad() {\n\t\tpublish('load');\n\t}\n\n\tfunction masterLoadWithScreen() {\n\t\tpublish('loadwithscreen');\n\t}\n\n\tfunction masterMute(mute) {\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMute;\n\t\t}\n\n\t\t_masterMute = !!mute;\n\t\tpublish('mute', _masterMute);\n\t}\n\n\tfunction masterMuteOnHidden(mute) {\n\t\t// NOTE: Some older browsers—notably: IE 9—do not support the Page Visibility API.\n\t\tif (!Visibility.isEnabled()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMuteOnHidden;\n\t\t}\n\n\t\t_masterMuteOnHidden = !!mute;\n\n\t\tconst namespace = '.SimpleAudio_masterMuteOnHidden';\n\n\t\tif (_masterMuteOnHidden) {\n\t\t\tconst visibilityChange = `${Visibility.changeEvent}${namespace}`;\n\t\t\tjQuery(document)\n\t\t\t\t.off(namespace)\n\t\t\t\t.on(visibilityChange, () => masterMute(Visibility.isHidden()));\n\n\t\t\t// Only change the mute state initially if hidden.\n\t\t\tif (Visibility.isHidden()) {\n\t\t\t\tmasterMute(true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tjQuery(document).off(namespace);\n\t\t}\n\t}\n\n\tfunction masterRate(rate) {\n\t\tif (rate == null) { // lazy equality for null\n\t\t\treturn _masterRate;\n\t\t}\n\n\t\tif (typeof rate !== 'number' || Number.isNaN(rate) || !Number.isFinite(rate)) {\n\t\t\tthrow new Error('rate must be a finite number');\n\t\t}\n\n\t\t_masterRate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\tpublish('rate', _masterRate);\n\t}\n\n\tfunction masterStop() {\n\t\tpublish('stop');\n\t}\n\n\tfunction masterUnload() {\n\t\tpublish('unload');\n\t}\n\n\tfunction masterVolume(volume) {\n\t\tif (volume == null) { // lazy equality for null\n\t\t\treturn _masterVolume;\n\t\t}\n\n\t\tif (typeof volume !== 'number' || Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\tthrow new Error('volume must be a finite number');\n\t\t}\n\n\t\t_masterVolume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\tpublish('volume', _masterVolume);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSubscription Functions.\n\t*******************************************************************************************************************/\n\tfunction subscribe(id, callback) {\n\t\tif (typeof callback !== 'function') {\n\t\t\tthrow new Error('callback parameter must be a function');\n\t\t}\n\n\t\t_subscribers.set(id, callback);\n\t}\n\n\tfunction unsubscribe(id) {\n\t\t_subscribers.delete(id);\n\t}\n\n\tfunction publish(mesg, data) {\n\t\t_subscribers.forEach(fn => fn(mesg, data));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _newTrack(sources) {\n\t\treturn new AudioTrack(sources.map(source => {\n\t\t\t// Handle audio passages.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.audio')) {\n\t\t\t\t\treturn passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle URIs—possibly prefixed with a format specifier.\n\t\t\tconst match = _formatSpecRe.exec(source);\n\t\t\treturn match === null ? source : {\n\t\t\t\tformat : match[1],\n\t\t\t\tsrc    : match[2]\n\t\t\t};\n\t\t}));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Track Functions.\n\t\ttracks : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : trackAdd },\n\t\t\t\tdelete : { value : trackDelete },\n\t\t\t\tclear  : { value : trackClear },\n\t\t\t\thas    : { value : trackHas },\n\t\t\t\tget    : { value : trackGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Group Functions.\n\t\tgroups : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : groupAdd },\n\t\t\t\tdelete : { value : groupDelete },\n\t\t\t\tclear  : { value : groupClear },\n\t\t\t\thas    : { value : groupHas },\n\t\t\t\tget    : { value : groupGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Playlist Functions.\n\t\tlists : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : listAdd },\n\t\t\t\tdelete : { value : listDelete },\n\t\t\t\tclear  : { value : listClear },\n\t\t\t\thas    : { value : listHas },\n\t\t\t\tget    : { value : listGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Runner Functions.\n\t\tselect : { value : runnerGet },\n\n\t\t// Master Audio Functions.\n\t\tload           : { value : masterLoad },\n\t\tloadWithScreen : { value : masterLoadWithScreen },\n\t\tmute           : { value : masterMute },\n\t\tmuteOnHidden   : { value : masterMuteOnHidden },\n\t\trate           : { value : masterRate },\n\t\tstop           : { value : masterStop },\n\t\tunload         : { value : masterUnload },\n\t\tvolume         : { value : masterVolume }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstate.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Diff, Engine, PRNGWrapper, Scripting, clone, session, storage */\n\nvar State = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// History moment stack.\n\tlet _history = [];\n\n\t// Currently active/played moment.\n\tlet _active = momentCreate();\n\n\t// Currently active/played moment index.\n\tlet _activeIndex = -1;\n\n\t// Titles of all moments which have expired (i.e. fallen off the bottom of the stack).\n\tlet _expired = [];\n\n\t// (optional) Seedable PRNG object.\n\tlet _prng = null;\n\n\t// Temporary variables object.\n\tlet _tempVariables = {};\n\n\n\t/*******************************************************************************************************************\n\t\tState Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tResets the story state.\n\t*/\n\tfunction stateReset() {\n\t\tif (DEBUG) { console.log('[State/stateReset()]'); }\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tsession.delete('state');\n\n\t\t/*\n\t\t\tReset the properties.\n\t\t*/\n\t\t_history     = [];\n\t\t_active      = momentCreate();\n\t\t_activeIndex = -1;\n\t\t_expired     = [];\n\t\t_prng        = _prng === null ? null : new PRNGWrapper(_prng.seed, false);\n\t}\n\n\t/*\n\t\tRestores the story state from the active session.\n\t*/\n\tfunction stateRestore() {\n\t\tif (DEBUG) { console.log('[State/stateRestore()]'); }\n\n\t\t/*\n\t\t\tAttempt to restore an active session.\n\t\t*/\n\t\tif (session.has('state')) {\n\t\t\t/*\n\t\t\t\tRetrieve the session.\n\t\t\t*/\n\t\t\tconst stateObj = session.get('state');\n\n\t\t\tif (DEBUG) { console.log('\\tsession state:', stateObj); }\n\n\t\t\tif (stateObj == null) { // lazy equality for null\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tRestore the session.\n\t\t\t*/\n\t\t\tstateUnmarshal(stateObj);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a serializable object.\n\t*/\n\tfunction stateMarshal(noDelta) {\n\t\t/*\n\t\t\tGather the properties.\n\t\t*/\n\t\tconst stateObj = {\n\t\t\tindex : _activeIndex\n\t\t};\n\n\t\tif (noDelta) {\n\t\t\tstateObj.history = clone(_history);\n\t\t}\n\t\telse {\n\t\t\tstateObj.delta = historyDeltaEncode(_history);\n\t\t}\n\n\t\tif (_expired.length > 0) {\n\t\t\tstateObj.expired = [];\n\t\t}\n\n\t\tif (_prng !== null) {\n\t\t\tstateObj.seed = _prng.seed;\n\t\t}\n\n\t\treturn stateObj;\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled story state serialization object.\n\t*/\n\tfunction stateUnmarshal(stateObj, noDelta) {\n\t\tif (stateObj == null) { // lazy equality for null\n\t\t\tthrow new Error('state object is null or undefined');\n\t\t}\n\n\t\tif (\n\t\t\t   !stateObj.hasOwnProperty(noDelta ? 'history' : 'delta')\n\t\t\t|| stateObj[noDelta ? 'history' : 'delta'].length === 0\n\t\t) {\n\t\t\tthrow new Error('state object has no history or history is empty');\n\t\t}\n\n\t\tif (!stateObj.hasOwnProperty('index')) {\n\t\t\tthrow new Error('state object has no index');\n\t\t}\n\n\t\tif (_prng !== null && !stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has no seed, but PRNG is enabled');\n\t\t}\n\n\t\tif (_prng === null && stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has seed, but PRNG is disabled');\n\t\t}\n\n\t\t/*\n\t\t\tRestore the properties.\n\t\t*/\n\t\t_history     = noDelta ? clone(stateObj.history) : historyDeltaDecode(stateObj.delta);\n\t\t_activeIndex = stateObj.index;\n\t\t_expired     = stateObj.hasOwnProperty('expired') ? [...stateObj.expired] : [];\n\n\t\tif (stateObj.hasOwnProperty('seed')) {\n\t\t\t/*\n\t\t\t\tWe only need to restore the PRNG's seed here as `momentActivate()` will handle\n\t\t\t\tfully restoring the PRNG to its proper state.\n\t\t\t*/\n\t\t\t_prng.seed = stateObj.seed;\n\t\t}\n\n\t\t/*\n\t\t\tActivate the current moment (do this only after all properties have been restored).\n\t\t*/\n\t\tmomentActivate(_activeIndex);\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a save-compatible serializable object.\n\t*/\n\tfunction stateMarshalForSave() {\n\t\treturn stateMarshal(true);\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled save-compatible story state serialization object.\n\t*/\n\tfunction stateUnmarshalForSave(stateObj) {\n\t\treturn stateUnmarshal(stateObj, true);\n\t}\n\n\t/*\n\t\tReturns the titles of expired moments.\n\t*/\n\tfunction stateExpired() {\n\t\treturn _expired;\n\t}\n\n\t/*\n\t\tReturns the total number of played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTurns() {\n\t\treturn _expired.length + historyLength();\n\t}\n\n\t/*\n\t\tReturns the passage titles of all played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTitles() {\n\t\treturn _expired.concat(_history.slice(0, historyLength()).map(moment => moment.title));\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title has been played (expired + in-play history moments).\n\t*/\n\tfunction stateHasPlayed(title) {\n\t\tif (title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tif (_expired.includes(title)) {\n\t\t\treturn true;\n\t\t}\n\t\telse if (_history.slice(0, historyLength()).some(moment => moment.title === title)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMoment Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a new moment object created from the given passage title and variables object.\n\t*/\n\tfunction momentCreate(title, variables) {\n\t\treturn {\n\t\t\ttitle     : title == null ? '' : String(title), // lazy equality for null\n\t\t\tvariables : variables == null ? {} : variables   // lazy equality for null\n\t\t};\n\t}\n\n\t/*\n\t\tReturns the active (present) moment.\n\t*/\n\tfunction momentActive() {\n\t\treturn _active;\n\t}\n\n\t/*\n\t\tReturns the index within the history of the active (present) moment.\n\t*/\n\tfunction momentActiveIndex() {\n\t\treturn _activeIndex;\n\t}\n\n\t/*\n\t\tReturns the title from the active (present) moment.\n\t*/\n\tfunction momentActiveTitle() {\n\t\treturn _active.title;\n\t}\n\n\t/*\n\t\tReturns the variables from the active (present) moment.\n\t*/\n\tfunction momentActiveVariables() {\n\t\treturn _active.variables;\n\t}\n\n\t/*\n\t\tReturns the active (present) moment after setting it to either the given moment object\n\t\tor the moment object at the given history index.  Additionally, updates the active session\n\t\tand triggers a history update event.\n\t*/\n\tfunction momentActivate(moment) {\n\t\tif (moment == null) { // lazy equality for null\n\t\t\tthrow new Error('moment activation attempted with null or undefined');\n\t\t}\n\n\t\t/*\n\t\t\tSet the active moment.\n\t\t*/\n\t\tswitch (typeof moment) {\n\t\tcase 'object':\n\t\t\t_active = clone(moment);\n\t\t\tbreak;\n\n\t\tcase 'number':\n\t\t\tif (historyIsEmpty()) {\n\t\t\t\tthrow new Error('moment activation attempted with index on empty history');\n\t\t\t}\n\n\t\t\tif (moment < 0 || moment >= historySize()) {\n\t\t\t\tthrow new RangeError(`moment activation attempted with out-of-bounds index; need [0, ${historySize() - 1}], got ${moment}`);\n\t\t\t}\n\n\t\t\t_active = clone(_history[moment]);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new TypeError(`moment activation attempted with a \"${typeof moment}\"; must be an object or valid history stack index`);\n\t\t}\n\n\t\t/*\n\t\t\tRestore the seedable PRNG.\n\n\t\t\tNOTE: We cannot simply set `_prng.pull` to `_active.pull` as that would\n\t\t\tnot properly mutate the PRNG's internal state.\n\t\t*/\n\t\tif (_prng !== null) {\n\t\t\t_prng = PRNGWrapper.unmarshal({\n\t\t\t\tseed : _prng.seed,\n\t\t\t\tpull : _active.pull\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tUpdate the active session.\n\t\t*/\n\t\tsession.set('state', stateMarshal());\n\n\t\t/*\n\t\t\tTrigger a global `:historyupdate` event.\n\n\t\t\tNOTE: We do this here because setting a new active moment is a core component\n\t\t\tof, virtually, all history updates.\n\t\t*/\n\t\tjQuery.event.trigger(':historyupdate');\n\n\t\treturn _active;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHistory Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the moment history.\n\t*/\n\tfunction historyGet() {\n\t\treturn _history;\n\t}\n\n\t/*\n\t\tReturns the number of active history moments (past only).\n\t*/\n\tfunction historyLength() {\n\t\treturn _activeIndex + 1;\n\t}\n\n\t/*\n\t\tReturns the total number of history moments (past + future).\n\t*/\n\tfunction historySize() {\n\t\treturn _history.length;\n\t}\n\n\t/*\n\t\tReturns whether the history is empty.\n\t*/\n\tfunction historyIsEmpty() {\n\t\treturn _history.length === 0;\n\t}\n\n\t/*\n\t\tReturns the current (pre-play version of the active) moment within the history.\n\t*/\n\tfunction historyCurrent() {\n\t\treturn _history.length > 0 ? _history[_activeIndex] : null;\n\t}\n\n\t/*\n\t\tReturns the topmost (most recent) moment within the history.\n\t*/\n\tfunction historyTop() {\n\t\treturn _history.length > 0 ? _history[_history.length - 1] : null;\n\t}\n\n\t/*\n\t\tReturns the bottommost (least recent) moment within the history.\n\t*/\n\tfunction historyBottom() {\n\t\treturn _history.length > 0 ? _history[0] : null;\n\t}\n\n\t/*\n\t\tReturns the moment at the given index within the history.\n\t*/\n\tfunction historyIndex(index) {\n\t\tif (historyIsEmpty() || index < 0 || index > _activeIndex) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[index];\n\t}\n\n\t/*\n\t\tReturns the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyPeek(offset) {\n\t\tif (historyIsEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst lengthOffset = 1 + (offset ? Math.abs(offset) : 0);\n\n\t\tif (lengthOffset > historyLength()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[historyLength() - lengthOffset];\n\t}\n\n\t/*\n\t\tReturns whether a moment with the given title exists within the history.\n\t*/\n\tfunction historyHas(title) {\n\t\tif (historyIsEmpty() || title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = _activeIndex; i >= 0; --i) {\n\t\t\tif (_history[i].title === title) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tCreates a new moment and pushes it onto the history, discarding future moments if necessary.\n\t*/\n\tfunction historyCreate(title) {\n\t\tif (DEBUG) { console.log(`[State/historyCreate(title: \"${title}\")]`); }\n\n\t\t/*\n\t\t\tTODO: It might be good to have some assertions about the passage title here.\n\t\t*/\n\n\t\t/*\n\t\t\tIf we're not at the top of the stack, discard the future moments.\n\t\t*/\n\t\tif (historyLength() < historySize()) {\n\t\t\tif (DEBUG) { console.log(`\\tnon-top push; discarding ${historySize() - historyLength()} future moments`); }\n\n\t\t\t_history.splice(historyLength(), historySize() - historyLength());\n\t\t}\n\n\t\t/*\n\t\t\tPush the new moment onto the history stack.\n\t\t*/\n\t\t_history.push(momentCreate(title, _active.variables));\n\n\t\tif (_prng) {\n\t\t\thistoryTop().pull = _prng.pull;\n\t\t}\n\n\t\t/*\n\t\t\tTruncate the history, if necessary, by discarding moments from the bottom.\n\t\t*/\n\t\tif (Config.history.maxStates > 0) {\n\t\t\twhile (historySize() > Config.history.maxStates) {\n\t\t\t\t_expired.push(_history.shift().title);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tActivate the new top moment.\n\t\t*/\n\t\t_activeIndex = historySize() - 1;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn historyLength();\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the history.\n\t*/\n\tfunction historyGoTo(index) {\n\t\tif (DEBUG) { console.log(`[State/historyGoTo(index: ${index})]`); }\n\n\t\tif (\n\t\t\t   index == null /* lazy equality for null */\n\t\t\t|| index < 0\n\t\t\t|| index >= historySize()\n\t\t\t|| index === _activeIndex\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t_activeIndex = index;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyGo(offset) {\n\t\tif (DEBUG) { console.log(`[State/historyGo(offset: ${offset})]`); }\n\n\t\tif (offset == null || offset === 0) { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\treturn historyGoTo(_activeIndex + offset);\n\t}\n\n\t/*\n\t\tReturns the delta encoded form of the given history array.\n\t*/\n\tfunction historyDeltaEncode(historyArr) {\n\t\tif (!Array.isArray(historyArr)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (historyArr.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst delta = [historyArr[0]];\n\n\t\tfor (let i = 1, iend = historyArr.length; i < iend; ++i) {\n\t\t\tdelta.push(Diff.diff(historyArr[i - 1], historyArr[i]));\n\t\t}\n\n\t\treturn delta;\n\t}\n\n\t/*\n\t\tReturns a history array from the given delta encoded history array.\n\t*/\n\tfunction historyDeltaDecode(delta) {\n\t\tif (!Array.isArray(delta)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (delta.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst historyArr = [clone(delta[0])];\n\n\t\tfor (let i = 1, iend = delta.length; i < iend; ++i) {\n\t\t\thistoryArr.push(Diff.patch(historyArr[i - 1], delta[i]));\n\t\t}\n\n\t\treturn historyArr;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPRNG Functions.\n\t*******************************************************************************************************************/\n\tfunction prngInit(seed, useEntropy) {\n\t\tif (DEBUG) { console.log(`[State/prngInit(seed: ${seed}, useEntropy: ${useEntropy})]`); }\n\n\t\tif (!historyIsEmpty()) {\n\t\t\tlet scriptSection;\n\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tscriptSection = 'a script-tagged passage';\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tscriptSection = 'the Story JavaScript';\n\t\t\t}\n\n\t\t\tthrow new Error(`State.initPRNG must be called during initialization, within either ${scriptSection} or the StoryInit special passage`);\n\t\t}\n\n\t\t_prng = new PRNGWrapper(seed, useEntropy);\n\t\t_active.pull = _prng.pull;\n\t}\n\n\tfunction prngIsEnabled() {\n\t\treturn _prng !== null;\n\t}\n\n\tfunction prngPull() {\n\t\treturn _prng ? _prng.pull : NaN;\n\t}\n\n\tfunction prngSeed() {\n\t\treturn _prng ? _prng.seed : null;\n\t}\n\n\tfunction prngRandom() {\n\t\tif (DEBUG) { console.log('[State/prngRandom()]'); }\n\n\t\treturn _prng ? _prng.random() : Math.random();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTemporary Variables Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tClear the temporary variables.\n\t*/\n\tfunction tempVariablesClear() {\n\t\tif (DEBUG) { console.log('[State/tempVariablesReset()]'); }\n\n\t\t_tempVariables = {};\n\n\t\t/* legacy */\n\t\tTempVariables = _tempVariables; // eslint-disable-line no-undef\n\t\t/* /legacy */\n\t}\n\n\t/*\n\t\tReturns the current temporary variables.\n\t*/\n\tfunction tempVariables() {\n\t\treturn _tempVariables;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tVariable Chain Parsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value of the given story/temporary variable.\n\t*/\n\tfunction variableGet(varExpression) {\n\t\ttry {\n\t\t\treturn Scripting.evalTwineScript(varExpression);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\t}\n\n\t/*\n\t\tSets the value of the given story/temporary variable.\n\t*/\n\tfunction variableSet(varExpression, value) {\n\t\ttry {\n\t\t\tScripting.evalTwineScript(`${varExpression} = evalTwineScript$Data$`, null, value);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tStory Metadata Functions.\n\t*******************************************************************************************************************/\n\tconst _METADATA_STORE = 'metadata';\n\n\tfunction metadataClear() {\n\t\tstorage.delete(_METADATA_STORE);\n\t}\n\n\tfunction metadataDelete(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.delete key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\n\t\tif (store && store.hasOwnProperty(key)) {\n\t\t\tif (Object.keys(store).length === 1) {\n\t\t\t\tstorage.delete(_METADATA_STORE);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelete store[key];\n\t\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction metadataGet(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.get key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key) ? store[key] : undefined;\n\t}\n\n\tfunction metadataHas(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.has key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key);\n\t}\n\n\tfunction metadataSet(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.set key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tif (typeof value === 'undefined') {\n\t\t\tmetadataDelete(key);\n\t\t}\n\t\telse {\n\t\t\tconst store = storage.get(_METADATA_STORE) || {};\n\t\t\tstore[key] = value;\n\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t}\n\t}\n\n\tfunction metadataSize() {\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store ? Object.keys(store).length : 0;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tState Functions.\n\t\t*/\n\t\treset            : { value : stateReset },\n\t\trestore          : { value : stateRestore },\n\t\tmarshalForSave   : { value : stateMarshalForSave },\n\t\tunmarshalForSave : { value : stateUnmarshalForSave },\n\t\texpired          : { get : stateExpired },\n\t\tturns            : { get : stateTurns },\n\t\tpassages         : { get : stateTitles },\n\t\thasPlayed        : { value : stateHasPlayed },\n\n\t\t/*\n\t\t\tMoment Functions.\n\t\t*/\n\t\tactive      : { get : momentActive },\n\t\tactiveIndex : { get : momentActiveIndex },\n\t\tpassage     : { get : momentActiveTitle },     // shortcut for `State.active.title`\n\t\tvariables   : { get : momentActiveVariables }, // shortcut for `State.active.variables`\n\n\t\t/*\n\t\t\tHistory Functions.\n\t\t*/\n\t\thistory     : { get : historyGet },\n\t\tlength      : { get : historyLength },\n\t\tsize        : { get : historySize },\n\t\tisEmpty     : { value : historyIsEmpty },\n\t\tcurrent     : { get : historyCurrent },\n\t\ttop         : { get : historyTop },\n\t\tbottom      : { get : historyBottom },\n\t\tindex       : { value : historyIndex },\n\t\tpeek        : { value : historyPeek },\n\t\thas         : { value : historyHas },\n\t\tcreate      : { value : historyCreate },\n\t\tgoTo        : { value : historyGoTo },\n\t\tgo          : { value : historyGo },\n\t\tdeltaEncode : { value : historyDeltaEncode },\n\t\tdeltaDecode : { value : historyDeltaDecode },\n\n\t\t/*\n\t\t\tPRNG Functions.\n\t\t*/\n\t\tprng : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tinit      : { value : prngInit },\n\t\t\t\tisEnabled : { value : prngIsEnabled },\n\t\t\t\tpull      : { get : prngPull },\n\t\t\t\tseed      : { get : prngSeed }\n\t\t\t}))\n\t\t},\n\t\trandom : { value : prngRandom },\n\n\t\t/*\n\t\t\tTemporary Variables Functions.\n\t\t*/\n\t\tclearTemporary : { value : tempVariablesClear },\n\t\ttemporary      : { get : tempVariables },\n\n\t\t/*\n\t\t\tVariable Chain Parsing Functions.\n\t\t*/\n\t\tgetVar : { value : variableGet },\n\t\tsetVar : { value : variableSet },\n\n\t\t/*\n\t\t\tStory Metadata Functions.\n\t\t*/\n\t\tmetadata : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tclear  : { value : metadataClear },\n\t\t\t\tdelete : { value : metadataDelete },\n\t\t\t\tget    : { value : metadataGet },\n\t\t\t\thas    : { value : metadataHas },\n\t\t\t\tset    : { value : metadataSet },\n\t\t\t\tsize   : { get : metadataSize }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tinitPRNG : { value : prngInit },\n\t\trestart  : { value : () => Engine.restart() },\n\t\tbackward : { value : () => Engine.backward() },\n\t\tforward  : { value : () => Engine.forward() },\n\t\tdisplay  : { value : (...args) => Engine.display(...args) },\n\t\tshow     : { value : (...args) => Engine.show(...args) },\n\t\tplay     : { value : (...args) => Engine.play(...args) }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/scripting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Engine, Patterns, State, Story, Util */\n\nvar Scripting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable no-unused-vars */\n\n\t/*******************************************************************************************************************\n\t\tDeprecated Legacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns the jQuery-wrapped target element(s) after making them accessible\n\t\tclickables (ARIA compatibility).\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction addAccessibleClickHandler(targets, selector, handler, one, namespace) {\n\t\tif (arguments.length < 2) {\n\t\t\tthrow new Error('addAccessibleClickHandler insufficient number of parameters');\n\t\t}\n\n\t\tlet fn;\n\t\tlet opts;\n\n\t\tif (typeof selector === 'function') {\n\t\t\tfn = selector;\n\t\t\topts = {\n\t\t\t\tnamespace : one,\n\t\t\t\tone       : !!handler\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tfn = handler;\n\t\t\topts = {\n\t\t\t\tnamespace,\n\t\t\t\tone : !!one,\n\t\t\t\tselector\n\t\t\t};\n\t\t}\n\n\t\tif (typeof fn !== 'function') {\n\t\t\tthrow new TypeError('addAccessibleClickHandler handler parameter must be a function');\n\t\t}\n\n\t\treturn jQuery(targets).ariaClick(opts, fn);\n\t}\n\n\t/*\n\t\t[DEPRECATED] Returns a new DOM element, optionally appending it to the passed DOM element, if any.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertElement(place, type, id, classNames, text, title) { // eslint-disable-line max-params\n\t\tconst $el = jQuery(document.createElement(type));\n\n\t\t// Add attributes/properties.\n\t\tif (id) {\n\t\t\t$el.attr('id', id);\n\t\t}\n\n\t\tif (classNames) {\n\t\t\t$el.addClass(classNames);\n\t\t}\n\n\t\tif (title) {\n\t\t\t$el.attr('title', title);\n\t\t}\n\n\t\t// Add text content.\n\t\tif (text) {\n\t\t\t$el.text(text);\n\t\t}\n\n\t\t// Append it to the given node.\n\t\tif (place) {\n\t\t\t$el.appendTo(place);\n\t\t}\n\n\t\treturn $el[0];\n\t}\n\n\t/*\n\t\t[DEPRECATED] Creates a new text node and appends it to the passed DOM element.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertText(place, text) {\n\t\tjQuery(place).append(document.createTextNode(text));\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes all children from the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeChildren(node) {\n\t\tjQuery(node).empty();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeElement(node) {\n\t\tjQuery(node).remove();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Fades a DOM element in or out.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction fade(el, options) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tconst direction = options.fade === 'in' ? 1 : -1;\n\t\tlet current;\n\t\tlet proxy      = el.cloneNode(true);\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tcurrent += 0.05 * direction;\n\t\t\tsetOpacity(proxy, Math.easeInOut(current));\n\n\t\t\tif (direction === 1 && current >= 1 || direction === -1 && current <= 0) {\n\t\t\t\tel.style.visibility = options.fade === 'in' ? 'visible' : 'hidden';\n\t\t\t\tproxy.parentNode.replaceChild(el, proxy);\n\t\t\t\tproxy = null;\n\t\t\t\twindow.clearInterval(intervalId);\n\n\t\t\t\tif (options.onComplete) {\n\t\t\t\t\toptions.onComplete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction setOpacity(el, opacity) {\n\t\t\t// Old IE.\n\t\t\tel.style.zoom = 1;\n\t\t\tel.style.filter = `alpha(opacity=${Math.floor(opacity * 100)})`;\n\n\t\t\t// CSS.\n\t\t\tel.style.opacity = opacity;\n\t\t}\n\n\t\tel.parentNode.replaceChild(proxy, el);\n\n\t\tif (options.fade === 'in') {\n\t\t\tcurrent = 0;\n\t\t\tproxy.style.visibility = 'visible';\n\t\t}\n\t\telse {\n\t\t\tcurrent = 1;\n\t\t}\n\n\t\tsetOpacity(proxy, current);\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\t/*\n\t\t[DEPRECATED] Scrolls the browser window to ensure that a DOM element is in view.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction scrollWindowTo(el, incrementBy) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tlet increment = incrementBy != null ? Number(incrementBy) : 0.1; // lazy equality for null\n\n\t\tif (Number.isNaN(increment) || !Number.isFinite(increment) || increment < 0) {\n\t\t\tincrement = 0.1;\n\t\t}\n\t\telse if (increment > 1) {\n\t\t\tincrement = 1;\n\t\t}\n\n\t\tconst start     = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\tconst end       = ensureVisible(el);\n\t\tconst distance  = Math.abs(start - end);\n\t\tconst direction = start > end ? -1 : 1;\n\t\tlet progress   = 0;\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tprogress += increment;\n\t\t\twindow.scroll(0, start + direction * (distance * Math.easeInOut(progress)));\n\n\t\t\tif (progress >= 1) {\n\t\t\t\twindow.clearInterval(intervalId);\n\t\t\t}\n\t\t}\n\n\t\tfunction findPosY(el) { // eslint-disable-line no-shadow\n\t\t\tlet curtop = 0;\n\n\t\t\twhile (el.offsetParent) {\n\t\t\t\tcurtop += el.offsetTop;\n\t\t\t\tel = el.offsetParent;\n\t\t\t}\n\n\t\t\treturn curtop;\n\t\t}\n\n\t\tfunction ensureVisible(el) { // eslint-disable-line no-shadow\n\t\t\tconst posTop    = findPosY(el);\n\t\t\tconst posBottom = posTop + el.offsetHeight;\n\t\t\tconst winTop    = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\t\tconst winHeight = window.innerHeight ? window.innerHeight : document.body.clientHeight;\n\t\t\tconst winBottom = winTop + winHeight;\n\n\t\t\treturn posTop >= winTop && posBottom > winBottom && el.offsetHeight < winHeight\n\t\t\t\t? posTop - (winHeight - el.offsetHeight) + 20\n\t\t\t\t: posTop;\n\t\t}\n\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUser Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a random value from its given arguments.\n\t*/\n\tfunction either(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn Array.prototype.concat.apply([], arguments).random();\n\t}\n\n\t/*\n\t\tRemoves the given key, and its value, from the story metadata store.\n\t*/\n\tfunction forget(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`forget key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.delete(key);\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the logical-AND\n\t\taggregate of the set.\n\t*/\n\tfunction hasVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('hasVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend; ++i) {\n\t\t\tif (!played.includes(needles[i])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tReturns the number of turns that have passed since the last instance of the given passage\n\t\toccurred within the story history or `-1` if it does not exist.  If multiple passages are\n\t\tgiven, returns the lowest count (which can be `-1`).\n\t*/\n\tfunction lastVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('lastVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\t\tconst uBound  = played.length - 1;\n\t\tlet turns = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && turns > -1; ++i) {\n\t\t\tconst lastIndex = played.lastIndexOf(needles[i]);\n\t\t\tturns = Math.min(turns, lastIndex === -1 ? -1 : uBound - lastIndex);\n\t\t}\n\n\t\treturn turns;\n\t}\n\n\t/*\n\t\tSets the given key/value pair within the story metadata store.\n\t*/\n\tfunction memorize(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`memorize key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.set(key, value);\n\t}\n\n\t/*\n\t\tReturns the title of the current passage.\n\t*/\n\tfunction passage() {\n\t\treturn State.passage;\n\t}\n\n\t/*\n\t\tReturns the title of a previous passage, either the most recent one whose title does not\n\t\tmatch that of the active passage or the one at the optional offset, or an empty string,\n\t\tif there is no such passage.\n\t*/\n\tfunction previous(/* legacy: offset */) {\n\t\tconst passages = State.passages;\n\n\t\t/* legacy: behavior with an offset */\n\t\tif (arguments.length > 0) {\n\t\t\tconst offset = Number(arguments[0]);\n\n\t\t\tif (!Number.isSafeInteger(offset) || offset < 1) {\n\t\t\t\tthrow new RangeError('previous offset parameter must be a positive integer greater than zero');\n\t\t\t}\n\n\t\t\treturn passages.length > offset ? passages[passages.length - 1 - offset] : '';\n\t\t}\n\t\t/* /legacy */\n\n\t\tfor (let i = passages.length - 2; i >= 0; --i) {\n\t\t\tif (passages[i] !== State.passage) {\n\t\t\t\treturn passages[i];\n\t\t\t}\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the range of the given bounds.\n\t*/\n\tfunction random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(arguments[0]);\n\t\t\tmax = Math.trunc(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!Number.isInteger(min)) {\n\t\t\tthrow new Error('random min parameter must be an integer');\n\t\t}\n\t\tif (!Number.isInteger(max)) {\n\t\t\tthrow new Error('random max parameter must be an integer');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(State.random() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a pseudo-random real number (floating-point) within the range of the given bounds.\n\n\t\tNOTE: Unlike with its sibling function `random()`, the `max` parameter\n\t\tis exclusive, not inclusive—i.e. the range goes to, but does not include,\n\t\tthe given value.\n\t*/\n\tfunction randomFloat(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('randomFloat called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0.0;\n\t\t\tmax = Number(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Number(arguments[0]);\n\t\t\tmax = Number(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min) || !Number.isFinite(min)) {\n\t\t\tthrow new Error('randomFloat min parameter must be a number');\n\t\t}\n\t\tif (Number.isNaN(max) || !Number.isFinite(max)) {\n\t\t\tthrow new Error('randomFloat max parameter must be a number');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn State.random() * (max - min) + min;\n\t}\n\n\t/*\n\t\tReturns the value of the given key from the story metadata store\n\t\tor the given default value if the key does not exist.\n\t*/\n\tfunction recall(key, defaultValue) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`recall key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\treturn State.metadata.has(key) ? State.metadata.get(key) : defaultValue;\n\t}\n\n\t/*\n\t\tReturns a new array consisting of all of the tags of the given passages.\n\t*/\n\tfunction tags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn Story.get(State.passage).tags.slice(0);\n\t\t}\n\n\t\tconst passages = Array.prototype.concat.apply([], arguments);\n\t\tlet tags = [];\n\n\t\tfor (let i = 0, iend = passages.length; i < iend; ++i) {\n\t\t\ttags = tags.concat(Story.get(passages[i]).tags);\n\t\t}\n\n\t\treturn tags;\n\t}\n\n\t/*\n\t\tReturns a reference to the current temporary _variables store.\n\t*/\n\tfunction temporary() {\n\t\treturn State.temporary;\n\t}\n\n\t/*\n\t\tReturns the number of milliseconds which have passed since the current passage was rendered.\n\t*/\n\tfunction time() {\n\t\treturn Engine.lastPlay === null ? 0 : Util.now() - Engine.lastPlay;\n\t}\n\n\t/*\n\t\tReturns the number of passages that the player has visited.\n\n\t\tNOTE: Passages which were visited but have been undone—e.g. via the backward\n\t\tbutton or the `<<back>>` macro—are no longer part of the in-play story\n\t\thistory and thus are not tallied.  Passages which were visited but have\n\t\texpired from the story history, on the other hand, are tallied.\n\t*/\n\tfunction turns() {\n\t\treturn State.turns;\n\t}\n\n\t/*\n\t\tReturns a reference to the current story $variables store.\n\t*/\n\tfunction variables() {\n\t\treturn State.variables;\n\t}\n\n\t/*\n\t\tReturns the number of times that the passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the lowest count.\n\t*/\n\tfunction visited(/* variadic */) {\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments.length === 0 ? [State.passage] : arguments);\n\t\tconst played  = State.passages;\n\t\tlet count = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && count > 0; ++i) {\n\t\t\tcount = Math.min(count, played.count(needles[i]));\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/*\n\t\tReturns the number of passages within the story history which are tagged with all of the given tags.\n\t*/\n\tfunction visitedTags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('visitedTags called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst nLength = needles.length;\n\t\tconst played  = State.passages;\n\t\tconst seen    = new Map();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0, iend = played.length; i < iend; ++i) {\n\t\t\tconst title = played[i];\n\n\t\t\tif (seen.has(title)) {\n\t\t\t\tif (seen.get(title)) {\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst tags = Story.get(title).tags;\n\n\t\t\t\tif (tags.length > 0) {\n\t\t\t\t\tlet found = 0;\n\n\t\t\t\t\tfor (let j = 0; j < nLength; ++j) {\n\t\t\t\t\t\tif (tags.includes(needles[j])) {\n\t\t\t\t\t\t\t++found;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (found === nLength) {\n\t\t\t\t\t\t++count;\n\t\t\t\t\t\tseen.set(title, true);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tseen.set(title, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/* eslint-enable no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tImport Functions.\n\t*******************************************************************************************************************/\n\tvar { // eslint-disable-line no-var\n\t\t/* eslint-disable no-unused-vars */\n\t\timportScripts,\n\t\timportStyles\n\t\t/* eslint-enable no-unused-vars */\n\t} = (() => {\n\t\t// Slugify the given URL.\n\t\tfunction slugifyUrl(url) {\n\t\t\treturn Util.parseUrl(url).path\n\t\t\t\t.replace(/^[^\\w]+|[^\\w]+$/g, '')\n\t\t\t\t.replace(/[^\\w]+/g, '-')\n\t\t\t\t.toLocaleLowerCase();\n\t\t}\n\n\t\t// Add a <script> element which will load the script from the given URL.\n\t\tfunction addScript(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('script'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importScripts failed to load the script \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `script-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\ttype : 'text/javascript',\n\t\t\t\t\t\tsrc  : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Add a <link> element which will load the stylesheet from the given URL.\n\t\tfunction addStyle(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('link'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importStyles failed to load the stylesheet \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `style-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\trel  : 'stylesheet',\n\t\t\t\t\t\thref : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Turn a list of callbacks into a sequential chain of `Promise` objects.\n\t\tfunction sequence(callbacks) {\n\t\t\treturn callbacks.reduce((seq, fn) => seq = seq.then(fn), Promise.resolve()); // eslint-disable-line no-param-reassign\n\t\t}\n\n\t\t/*\n\t\t\tImport scripts from a URL.\n\t\t*/\n\t\tfunction importScripts(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addScript(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addScript(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t/*\n\t\t\tImport stylesheets from a URL.\n\t\t*/\n\t\tfunction importStyles(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addStyle(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addStyle(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t// Exports.\n\t\treturn {\n\t\t\timportScripts,\n\t\t\timportStyles\n\t\t};\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tParsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the given string after converting all TwineScript syntactical sugars to\n\t\ttheir native JavaScript counterparts.\n\t*/\n\tconst parse = (() => {\n\t\tconst tokenTable = Util.toEnum({\n\t\t\t/* eslint-disable quote-props */\n\t\t\t// Story $variable sigil-prefix.\n\t\t\t'$'     : 'State.variables.',\n\t\t\t// Temporary _variable sigil-prefix.\n\t\t\t'_'     : 'State.temporary.',\n\t\t\t// Assignment operators.\n\t\t\t'to'    : '=',\n\t\t\t// Equality operators.\n\t\t\t'eq'    : '==',\n\t\t\t'neq'   : '!=',\n\t\t\t'is'    : '===',\n\t\t\t'isnot' : '!==',\n\t\t\t// Relational operators.\n\t\t\t'gt'    : '>',\n\t\t\t'gte'   : '>=',\n\t\t\t'lt'    : '<',\n\t\t\t'lte'   : '<=',\n\t\t\t// Logical operators.\n\t\t\t'and'   : '&&',\n\t\t\t'or'    : '||',\n\t\t\t// Unary operators.\n\t\t\t'not'   : '!',\n\t\t\t'def'   : '\"undefined\" !== typeof',\n\t\t\t'ndef'  : '\"undefined\" === typeof'\n\t\t\t/* eslint-enable quote-props */\n\t\t});\n\t\tconst parseRe = new RegExp([\n\t\t\t'(\"\"|\\'\\')',                                          // 1=Empty quotes\n\t\t\t'(\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\")',                            // 2=Double quoted, non-empty\n\t\t\t\"('(?:\\\\\\\\.|[^'\\\\\\\\])+')\",                            // 3=Single quoted, non-empty\n\t\t\t'([=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}]+)',       // 4=Operator delimiters\n\t\t\t'([^\"\\'=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}\\\\s]+)' // 5=Barewords\n\t\t].join('|'), 'g');\n\t\tconst notSpaceRe      = /\\S/;\n\t\tconst varTest         = new RegExp(`^${Patterns.variable}`);\n\t\tconst withColonTestRe = /^\\s*:/;\n\t\tconst withNotTestRe   = /^\\s+not\\b/;\n\n\t\tfunction parse(rawCodeString) {\n\t\t\tif (parseRe.lastIndex !== 0) {\n\t\t\t\tthrow new RangeError('Scripting.parse last index is non-zero at start');\n\t\t\t}\n\n\t\t\tlet code  = rawCodeString;\n\t\t\tlet match;\n\n\t\t\twhile ((match = parseRe.exec(code)) !== null) {\n\t\t\t\t// no-op: Empty quotes | Double quoted | Single quoted | Operator delimiters\n\n\t\t\t\t// Barewords.\n\t\t\t\tif (match[5]) {\n\t\t\t\t\tlet token = match[5];\n\n\t\t\t\t\t// If the token is simply a dollar-sign or underscore, then it's either\n\t\t\t\t\t// just the raw character or, probably, a function alias, so skip it.\n\t\t\t\t\tif (token === '$' || token === '_') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is a story $variable or temporary _variable, reset it\n\t\t\t\t\t// to just its sigil—for later mapping.\n\t\t\t\t\telse if (varTest.test(token)) {\n\t\t\t\t\t\ttoken = token[0];\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is `is`, check to see if it's followed by `not`, if so,\n\t\t\t\t\t// convert them into the `isnot` operator.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: This is a safety feature, since `$a is not $b` probably sounds\n\t\t\t\t\t// reasonable to most users.\n\t\t\t\t\telse if (token === 'is') {\n\t\t\t\t\t\tconst start = parseRe.lastIndex;\n\t\t\t\t\t\tconst ahead = code.slice(start);\n\n\t\t\t\t\t\tif (withNotTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcode = code.splice(start, ahead.search(notSpaceRe));\n\t\t\t\t\t\t\ttoken = 'isnot';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is followed by a colon, then it's likely to be an object\n\t\t\t\t\t// property, so skip it.\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst ahead = code.slice(parseRe.lastIndex);\n\n\t\t\t\t\t\tif (withColonTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the finalized token has a mapping, replace it within the code string\n\t\t\t\t\t// with its counterpart.\n\t\t\t\t\tif (tokenTable[token]) {\n\t\t\t\t\t\tcode = code.splice(\n\t\t\t\t\t\t\tmatch.index,      // starting index\n\t\t\t\t\t\t\ttoken.length,     // replace how many\n\t\t\t\t\t\t\ttokenTable[token] // replacement string\n\t\t\t\t\t\t);\n\t\t\t\t\t\tparseRe.lastIndex += tokenTable[token].length - token.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn code;\n\t\t}\n\n\t\treturn parse;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tEval Functions.\n\t*******************************************************************************************************************/\n\t/* eslint-disable no-eval, no-extra-parens, no-unused-vars */\n\t/*\n\t\tEvaluates the given JavaScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalJavaScript(code, output, data) {\n\t\treturn (function (code, output, evalJavaScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, String(code), output, data);\n\t}\n\n\t/*\n\t\tEvaluates the given TwineScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalTwineScript(code, output, data) {\n\t\t// NOTE: Do not move the dollar sign to the front of `evalTwineScript$Data$`,\n\t\t// as `parse()` will break references to it within the code string.\n\t\treturn (function (code, output, evalTwineScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, parse(String(code)), output, data);\n\t}\n\t/* eslint-enable no-eval, no-extra-parens, no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tparse           : { value : parse },\n\t\tevalJavaScript  : { value : evalJavaScript },\n\t\tevalTwineScript : { value : evalTwineScript }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/lexer.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tEOF,\n\tLexer\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\t// End of file (string, actually).\n\tconst EOF = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tLexer Class.\n\t*******************************************************************************************************************/\n\tclass Lexer {\n\t\tconstructor(source, initialState) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer constructor called with too few parameters (source:string , initialState:function)');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tthis.source  → the string to be scanned\n\t\t\t\tthis.initial → initial state\n\t\t\t\tthis.state   → current state\n\t\t\t\tthis.start   → start position of an item\n\t\t\t\tthis.pos     → current position in the source string\n\t\t\t\tthis.depth   → current brace/bracket/parenthesis nesting depth\n\t\t\t\tthis.items   → scanned item queue\n\t\t\t\tthis.data    → lexing data\n\t\t\t*/\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : source\n\t\t\t\t},\n\n\t\t\t\tinitial : {\n\t\t\t\t\tvalue : initialState\n\t\t\t\t},\n\n\t\t\t\tstate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : initialState\n\t\t\t\t},\n\n\t\t\t\tstart : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tpos : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tdepth : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\titems : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : []\n\t\t\t\t},\n\n\t\t\t\tdata : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : {}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treset() {\n\t\t\tthis.state  = this.initial;\n\t\t\tthis.start  = 0;\n\t\t\tthis.pos    = 0;\n\t\t\tthis.depth  = 0;\n\t\t\tthis.items  = [];\n\t\t\tthis.data   = {};\n\t\t}\n\n\t\trun() {\n\t\t\t// scan the source string until no states remain\n\t\t\twhile (this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the array of items\n\t\t\treturn this.items;\n\t\t}\n\n\t\tnextItem() {\n\t\t\t// scan the source string until we have an item or no states remain\n\t\t\twhile (this.items.length === 0 && this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the current item\n\t\t\treturn this.items.shift();\n\t\t}\n\n\t\tnext() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos++];\n\t\t}\n\n\t\tpeek() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos];\n\t\t}\n\n\t\tbackup(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos -= num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t--this.pos;\n\t\t\t// }\n\t\t\tthis.pos -= num || 1;\n\t\t}\n\n\t\tforward(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos += num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t++this.pos;\n\t\t\t// }\n\t\t\tthis.pos += num || 1;\n\t\t}\n\n\t\tignore() {\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\taccept(valid) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (valid.includes(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRe(validRe) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (validRe.test(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRun(valid) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!valid.includes(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\tacceptRunRe(validRe) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!validRe.test(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\temit(type) {\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\terror(type, message) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer.prototype.error called with too few parameters (type:number , message:string)');\n\t\t\t}\n\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\tmessage,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic enumFromNames(names) {\n\t\t\tconst obj = names.reduce((obj, name, i) => {\n\t\t\t\tobj[name] = i; // eslint-disable-line no-param-reassign\n\t\t\t\treturn obj;\n\t\t\t}, {});\n\t\t\treturn Object.freeze(Object.assign(Object.create(null), obj));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn {\n\t\tEOF,\n\t\tLexer\n\t};\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/wikifier.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, EOF, Engine, Lexer, Patterns, Scripting, State, Story, TempState, Util, convertBreaks,\n\t       errorPrologRegExp\n*/\n\n/*\n\tTODO: The Wikifier, and associated code, could stand to receive a serious refactoring.\n*/\n/* eslint-disable max-len */\nvar Wikifier = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Wikifier call depth.\n\tlet _callDepth = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tWikifier Class.\n\t*******************************************************************************************************************/\n\tclass Wikifier {\n\t\tconstructor(destination, source, options) {\n\t\t\tif (Wikifier.Parser.Profile.isEmpty()) {\n\t\t\t\tWikifier.Parser.Profile.compile();\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// General Wikifier properties.\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : String(source)\n\t\t\t\t},\n\n\t\t\t\toptions : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Object.assign({\n\t\t\t\t\t\tprofile : 'all'\n\t\t\t\t\t}, options)\n\t\t\t\t},\n\n\t\t\t\tnextMatch : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\toutput : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t// Macro parser ('macro') related properties.\n\t\t\t\t_rawArgs : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// No destination specified.  Create a fragment to act as the output buffer.\n\t\t\tif (destination == null) { // lazy equality for null\n\t\t\t\tthis.output = document.createDocumentFragment();\n\t\t\t}\n\n\t\t\t// jQuery-wrapped destination.  Grab the first element.\n\t\t\telse if (destination.jquery) { // cannot use `hasOwnProperty()` here as `jquery` is from jQuery's prototype\n\t\t\t\tthis.output = destination[0];\n\t\t\t}\n\n\t\t\t// Normal destination.\n\t\t\telse {\n\t\t\t\tthis.output = destination;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tWikify the source into the output buffer element, possibly converting line\n\t\t\t\tbreaks into paragraphs.\n\n\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\tto ensure that the call depth is properly restored in the event that an\n\t\t\t\tuncaught exception is thrown during the call to `subWikify()`.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\t++_callDepth;\n\n\t\t\t\tthis.subWikify(this.output);\n\n\t\t\t\t// Limit line break conversion to non-recursive calls.\n\t\t\t\tif (_callDepth === 1 && Config.cleanupWikifierOutput) {\n\t\t\t\t\tconvertBreaks(this.output);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t--_callDepth;\n\t\t\t}\n\t\t}\n\n\t\tsubWikify(output, terminator, options) {\n\t\t\t// Cache and temporarily replace the current output buffer.\n\t\t\tconst oldOutput = this.output;\n\t\t\tthis.output = output;\n\n\t\t\tlet newOptions;\n\t\t\tlet oldOptions;\n\n\t\t\t// Parser option overrides.\n\t\t\tif (Wikifier.Option.length > 0) {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, Wikifier.Option.options);\n\t\t\t}\n\t\t\t// Local parameter option overrides.\n\t\t\tif (options !== null && typeof options === 'object') {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, options);\n\t\t\t}\n\t\t\t// If new options exist, cache and temporarily replace the current options.\n\t\t\tif (newOptions) {\n\t\t\t\toldOptions = this.options;\n\t\t\t\tthis.options = Object.assign({}, this.options, newOptions);\n\t\t\t}\n\n\t\t\tconst parsersProfile   = Wikifier.Parser.Profile.get(this.options.profile);\n\t\t\tconst terminatorRegExp = terminator\n\t\t\t\t? new RegExp(`(?:${terminator})`, this.options.ignoreTerminatorCase ? 'gim' : 'gm')\n\t\t\t\t: null;\n\t\t\tlet terminatorMatch;\n\t\t\tlet parserMatch;\n\n\t\t\tdo {\n\t\t\t\t// Prepare the RegExp match positions.\n\t\t\t\tparsersProfile.parserRegExp.lastIndex = this.nextMatch;\n\n\t\t\t\tif (terminatorRegExp) {\n\t\t\t\t\tterminatorRegExp.lastIndex = this.nextMatch;\n\t\t\t\t}\n\n\t\t\t\t// Get the first matches.\n\t\t\t\tparserMatch     = parsersProfile.parserRegExp.exec(this.source);\n\t\t\t\tterminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;\n\n\t\t\t\t// Try for a terminator match, unless there's a closer parser match.\n\t\t\t\tif (terminatorMatch && (!parserMatch || terminatorMatch.index <= parserMatch.index)) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (terminatorMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, terminatorMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = terminatorMatch.index;\n\t\t\t\t\tthis.matchLength = terminatorMatch[0].length;\n\t\t\t\t\tthis.matchText   = terminatorMatch[0];\n\t\t\t\t\tthis.nextMatch   = terminatorRegExp.lastIndex;\n\n\t\t\t\t\t// Restore the original output buffer and options.\n\t\t\t\t\tthis.output = oldOutput;\n\n\t\t\t\t\tif (oldOptions) {\n\t\t\t\t\t\tthis.options = oldOptions;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Exit.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try for a parser match.\n\t\t\t\telse if (parserMatch) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (parserMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, parserMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = parserMatch.index;\n\t\t\t\t\tthis.matchLength = parserMatch[0].length;\n\t\t\t\t\tthis.matchText   = parserMatch[0];\n\t\t\t\t\tthis.nextMatch   = parsersProfile.parserRegExp.lastIndex;\n\n\t\t\t\t\t// Figure out which parser matched.\n\t\t\t\t\tlet matchingParser;\n\n\t\t\t\t\tfor (let i = 1, iend = parserMatch.length; i < iend; ++i) {\n\t\t\t\t\t\tif (parserMatch[i]) {\n\t\t\t\t\t\t\tmatchingParser = i - 1;\n\t\t\t\t\t\t\tbreak; // stop once we've found the matching parser\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call the parser.\n\t\t\t\t\tparsersProfile.parsers[matchingParser].handler(this);\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (terminatorMatch || parserMatch);\n\n\t\t\t// Output any text after the last match.\n\t\t\tif (TempState.break == null) { // lazy equality for null\n\t\t\t\tif (this.nextMatch < this.source.length) {\n\t\t\t\t\tthis.outputText(this.output, this.nextMatch, this.source.length);\n\t\t\t\t\tthis.nextMatch = this.source.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In case of <<break>>/<<continue>>, remove the last <br>.\n\t\t\telse if (\n\t\t\t\t   this.output.lastChild\n\t\t\t\t&& this.output.lastChild.nodeType === Node.ELEMENT_NODE\n\t\t\t\t&& this.output.lastChild.nodeName.toUpperCase() === 'BR'\n\t\t\t) {\n\t\t\t\tjQuery(this.output.lastChild).remove();\n\t\t\t}\n\n\t\t\t// Restore the original output buffer and options.\n\t\t\tthis.output = oldOutput;\n\n\t\t\tif (oldOptions) {\n\t\t\t\tthis.options = oldOptions;\n\t\t\t}\n\t\t}\n\n\t\toutputText(destination, startPos, endPos) {\n\t\t\tdestination.appendChild(document.createTextNode(this.source.substring(startPos, endPos)));\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the raw, unprocessed\n\t\t\ttext given to the currently executing macro.\n\t\t*/\n\t\trawArgs() {\n\t\t\treturn this._rawArgs;\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the text given to\n\t\t\tthe currently executing macro after doing TwineScript-to-JavaScript transformations.\n\t\t*/\n\t\tfullArgs() {\n\t\t\treturn Scripting.parse(this._rawArgs);\n\t\t}\n\n\t\t/*\n\t\t\tReturns the output generated by wikifying the given text, throwing if there were errors.\n\t\t*/\n\t\tstatic wikifyEval(text) {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\tnew Wikifier(output, text);\n\n\t\t\tconst errors = output.querySelector('.error');\n\n\t\t\tif (errors !== null) {\n\t\t\t\tthrow new Error(errors.textContent.replace(errorPrologRegExp, ''));\n\t\t\t}\n\n\t\t\treturn output;\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an internal link.\n\t\t*/\n\t\tstatic createInternalLink(destination, passage, text, callback) {\n\t\t\tconst $link = jQuery(document.createElement('a'));\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\n\t\t\t\t$link.ariaClick({ one : true }, () => {\n\t\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\n\t\t\t\t\tEngine.play(passage);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (text) {\n\t\t\t\t$link.append(document.createTextNode(text));\n\t\t\t}\n\n\t\t\tif (destination) {\n\t\t\t\t$link.appendTo(destination);\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an external link.\n\t\t*/\n\t\tstatic createExternalLink(destination, url, text) {\n\t\t\tconst $link = jQuery(document.createElement('a'))\n\t\t\t\t.attr('target', '_blank')\n\t\t\t\t.addClass('link-external')\n\t\t\t\t.text(text)\n\t\t\t\t.appendTo(destination);\n\n\t\t\tif (url != null) { // lazy equality for null\n\t\t\t\t$link.attr({\n\t\t\t\t\thref     : url,\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the given link source is external (probably).\n\t\t*/\n\t\tstatic isExternalLink(link) {\n\t\t\tif (Story.has(link)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst urlRegExp = new RegExp(`^${Patterns.url}`, 'gim');\n\t\t\treturn urlRegExp.test(link) || /[/.?#]/.test(link);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tOption Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Option', {\n\t\tvalue : (() => {\n\t\t\t// Options array (stack).\n\t\t\tlet _optionsStack = [];\n\n\n\t\t\t/*\n\t\t\t\tGlobalOption Functions.\n\t\t\t*/\n\t\t\tfunction optionLength() {\n\t\t\t\treturn _optionsStack.length;\n\t\t\t}\n\n\t\t\tfunction optionGetter() {\n\t\t\t\treturn Object.assign({}, ..._optionsStack);\n\t\t\t}\n\n\t\t\tfunction optionClear() {\n\t\t\t\t_optionsStack = [];\n\t\t\t}\n\n\t\t\tfunction optionGet(idx) {\n\t\t\t\treturn _optionsStack[idx];\n\t\t\t}\n\n\t\t\tfunction optionPop() {\n\t\t\t\treturn _optionsStack.pop();\n\t\t\t}\n\n\t\t\tfunction optionPush(options) {\n\t\t\t\tif (typeof options !== 'object' || options === null) {\n\t\t\t\t\tthrow new TypeError(`Wikifier.Option.push options parameter must be an object (received: ${Util.getType(options)})`);\n\t\t\t\t}\n\n\t\t\t\treturn _optionsStack.push(options);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\tlength  : { get : optionLength },\n\t\t\t\toptions : { get : optionGetter },\n\t\t\t\tclear   : { value : optionClear },\n\t\t\t\tget     : { value : optionGet },\n\t\t\t\tpop     : { value : optionPop },\n\t\t\t\tpush    : { value : optionPush }\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tParser Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Parser', {\n\t\tvalue : (() => {\n\t\t\t// Parser definition array.  Ordering matters, so this must be an ordered list.\n\t\t\tconst _parsers = [];\n\n\t\t\t// Parser profiles object.\n\t\t\tlet _profiles;\n\n\n\t\t\t/*\n\t\t\t\tParser Functions.\n\t\t\t*/\n\t\t\tfunction parsersGetter() {\n\t\t\t\treturn _parsers;\n\t\t\t}\n\n\t\t\tfunction parsersAdd(parser) {\n\t\t\t\t// Parser object sanity checks.\n\t\t\t\tif (typeof parser !== 'object') {\n\t\t\t\t\tthrow new Error('Wikifier.Parser.add parser parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('name')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"name\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.name !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"name\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('match')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"match\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.match !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"match\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('handler')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"handler\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.handler !== 'function') {\n\t\t\t\t\tthrow new Error('parser object \"handler\" property must be a function');\n\t\t\t\t}\n\n\t\t\t\tif (parser.hasOwnProperty('profiles') && !Array.isArray(parser.profiles)) {\n\t\t\t\t\tthrow new Error('parser object \"profiles\" property must be an array');\n\t\t\t\t}\n\n\t\t\t\t// Check for an existing parser with the same name.\n\t\t\t\tif (parsersHas(parser.name)) {\n\t\t\t\t\tthrow new Error(`cannot clobber existing parser \"${parser.name}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Add the parser to the end of the array.\n\t\t\t\t_parsers.push(parser);\n\t\t\t}\n\n\t\t\tfunction parsersDelete(name) {\n\t\t\t\tconst parser = _parsers.find(parser => parser.name === name);\n\n\t\t\t\tif (parser) {\n\t\t\t\t\t_parsers.delete(parser);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction parsersIsEmpty() {\n\t\t\t\treturn _parsers.length === 0;\n\t\t\t}\n\n\t\t\tfunction parsersHas(name) {\n\t\t\t\treturn !!_parsers.find(parser => parser.name === name);\n\t\t\t}\n\n\t\t\tfunction parsersGet(name) {\n\t\t\t\treturn _parsers.find(parser => parser.name === name) || null;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tParser Profile Functions.\n\t\t\t*/\n\t\t\tfunction profilesGetter() {\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesCompile() {\n\t\t\t\tif (DEBUG) { console.log('[Wikifier.Parser/profilesCompile()]'); }\n\n\t\t\t\tconst all  = _parsers;\n\t\t\t\tconst core = all.filter(parser => !Array.isArray(parser.profiles) || parser.profiles.includes('core'));\n\n\t\t\t\t_profiles = Object.freeze({\n\t\t\t\t\tall : {\n\t\t\t\t\t\tparsers      : all,\n\t\t\t\t\t\tparserRegExp : new RegExp(all.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t},\n\t\t\t\t\tcore : {\n\t\t\t\t\t\tparsers      : core,\n\t\t\t\t\t\tparserRegExp : new RegExp(core.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesIsEmpty() {\n\t\t\t\treturn typeof _profiles !== 'object' || Object.keys(_profiles).length === 0;\n\t\t\t}\n\n\t\t\tfunction profilesGet(profile) {\n\t\t\t\tif (typeof _profiles !== 'object' || !_profiles.hasOwnProperty(profile)) {\n\t\t\t\t\tthrow new Error(`nonexistent parser profile \"${profile}\"`);\n\t\t\t\t}\n\n\t\t\t\treturn _profiles[profile];\n\t\t\t}\n\n\t\t\tfunction profilesHas(profile) {\n\t\t\t\treturn typeof _profiles === 'object' && _profiles.hasOwnProperty(profile);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\t/*\n\t\t\t\t\tParser Containers.\n\t\t\t\t*/\n\t\t\t\tparsers : { get : parsersGetter },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Functions.\n\t\t\t\t*/\n\t\t\t\tadd     : { value : parsersAdd },\n\t\t\t\tdelete  : { value : parsersDelete },\n\t\t\t\tisEmpty : { value : parsersIsEmpty },\n\t\t\t\thas     : { value : parsersHas },\n\t\t\t\tget     : { value : parsersGet },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Profile.\n\t\t\t\t*/\n\t\t\t\tProfile : {\n\t\t\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Containers.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tprofiles : { get : profilesGetter },\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Functions.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tcompile : { value : profilesCompile },\n\t\t\t\t\t\tisEmpty : { value : profilesIsEmpty },\n\t\t\t\t\t\thas     : { value : profilesHas },\n\t\t\t\t\t\tget     : { value : profilesGet }\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAdditional Static Properties.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier, {\n\t\thelpers : { value : {} },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tgetValue       : { value : State.getVar },              // SEE: `state.js`.\n\t\tsetValue       : { value : State.setVar },              // SEE: `state.js`.\n\t\tparse          : { value : Scripting.parse },           // SEE: `markup/scripting.js`.\n\t\tevalExpression : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\ttextPrimitives : { value : Patterns }                   // SEE: `lib/patterns.js`.\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Static Methods.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier.helpers, {\n\t\tinlineCss : {\n\t\t\tvalue : (() => {\n\t\t\t\tconst lookaheadRe = new RegExp(Patterns.inlineCss, 'gm');\n\t\t\t\tconst idOrClassRe = new RegExp(`(${Patterns.cssIdOrClassSigil})(${Patterns.anyLetter}+)`, 'g');\n\n\t\t\t\tfunction helperInlineCss(w) {\n\t\t\t\t\tconst css = { classes : [], id : '', styles : {} };\n\t\t\t\t\tlet matched;\n\n\t\t\t\t\tdo {\n\t\t\t\t\t\tlookaheadRe.lastIndex = w.nextMatch;\n\n\t\t\t\t\t\tconst match = lookaheadRe.exec(w.source);\n\n\t\t\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\tif (match[1]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[1])] = match[2].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[3]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[3])] = match[4].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[5]) {\n\t\t\t\t\t\t\t\tlet subMatch;\n\n\t\t\t\t\t\t\t\tidOrClassRe.lastIndex = 0; // NOTE: Guard against buggy implementations.\n\n\t\t\t\t\t\t\t\twhile ((subMatch = idOrClassRe.exec(match[5])) !== null) {\n\t\t\t\t\t\t\t\t\tif (subMatch[1] === '.') {\n\t\t\t\t\t\t\t\t\t\tcss.classes.push(subMatch[2]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tcss.id = subMatch[2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tw.nextMatch = lookaheadRe.lastIndex; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (matched);\n\n\t\t\t\t\treturn css;\n\t\t\t\t}\n\n\t\t\t\treturn helperInlineCss;\n\t\t\t})()\n\t\t},\n\n\t\tevalText : {\n\t\t\tvalue(text) {\n\t\t\t\tlet result;\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = Scripting.evalTwineScript(text);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAttempt to prevent the leakage of auto-globals by enforcing that\n\t\t\t\t\t\tthe resultant value be either a string or a number.\n\n\t\t\t\t\t\tNOTE: This is not a foolproof solution to the problem of auto-global\n\t\t\t\t\t\tleakage.  Various auto-globals, which return strings or numbers, can\n\t\t\t\t\t\tstill leak through—e.g. `window.status` → string.\n\t\t\t\t\t*/\n\t\t\t\t\tswitch (typeof result) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tif (result.trim() === '') {\n\t\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tresult = String(result);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tresult = text;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t},\n\n\t\tevalPassageId : {\n\t\t\tvalue(passage) {\n\t\t\t\tif (passage == null || Story.has(passage)) { // lazy equality for null; `0` is a valid name, so we cannot simply evaluate `passage`\n\t\t\t\t\treturn passage;\n\t\t\t\t}\n\n\t\t\t\treturn Wikifier.helpers.evalText(passage);\n\t\t\t}\n\t\t},\n\n\t\thasBlockContext : {\n\t\t\tvalue(nodes) {\n\t\t\t\tconst hasGCS = typeof window.getComputedStyle === 'function';\n\n\t\t\t\tfor (let i = nodes.length - 1; i >= 0; --i) {\n\t\t\t\t\tconst node = nodes[i];\n\n\t\t\t\t\tswitch (node.nodeType) {\n\t\t\t\t\tcase Node.ELEMENT_NODE:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\t\t\t\tif (tagName === 'BR') {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst styles = hasGCS ? window.getComputedStyle(node, null) : node.currentStyle;\n\n\t\t\t\t\t\t\tif (styles && styles.display) {\n\t\t\t\t\t\t\t\tif (styles.display === 'none') {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn styles.display === 'block';\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWebKit/Blink-based browsers do not attach any computed style\n\t\t\t\t\t\t\t\tinformation to elements until they're inserted into the DOM\n\t\t\t\t\t\t\t\t(and probably visible), not even the default browser styles\n\t\t\t\t\t\t\t\tand any user styles.  So, we make an assumption based on the\n\t\t\t\t\t\t\t\telement.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\t\tcase 'ADDRESS':\n\t\t\t\t\t\t\tcase 'ARTICLE':\n\t\t\t\t\t\t\tcase 'ASIDE':\n\t\t\t\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\t\t\t\tcase 'CENTER':\n\t\t\t\t\t\t\tcase 'DIV':\n\t\t\t\t\t\t\tcase 'DL':\n\t\t\t\t\t\t\tcase 'FIGURE':\n\t\t\t\t\t\t\tcase 'FOOTER':\n\t\t\t\t\t\t\tcase 'FORM':\n\t\t\t\t\t\t\tcase 'H1':\n\t\t\t\t\t\t\tcase 'H2':\n\t\t\t\t\t\t\tcase 'H3':\n\t\t\t\t\t\t\tcase 'H4':\n\t\t\t\t\t\t\tcase 'H5':\n\t\t\t\t\t\t\tcase 'H6':\n\t\t\t\t\t\t\tcase 'HEADER':\n\t\t\t\t\t\t\tcase 'HR':\n\t\t\t\t\t\t\tcase 'MAIN':\n\t\t\t\t\t\t\tcase 'NAV':\n\t\t\t\t\t\t\tcase 'OL':\n\t\t\t\t\t\t\tcase 'P':\n\t\t\t\t\t\t\tcase 'PRE':\n\t\t\t\t\t\t\tcase 'SECTION':\n\t\t\t\t\t\t\tcase 'TABLE':\n\t\t\t\t\t\t\tcase 'UL':\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tcase Node.COMMENT_NODE:\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\n\t\tcreateShadowSetterCallback : {\n\t\t\tvalue : (() => {\n\t\t\t\tlet macroParser = null;\n\n\t\t\t\tfunction cacheMacroParser() {\n\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\tmacroParser = Wikifier.Parser.get('macro');\n\n\t\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\t\tthrow new Error('cannot find \"macro\" parser');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn macroParser;\n\t\t\t\t}\n\n\t\t\t\tfunction getMacroContextShadowView() {\n\t\t\t\t\tconst macro = macroParser || cacheMacroParser();\n\t\t\t\t\tconst view  = new Set();\n\n\t\t\t\t\tfor (let context = macro.context; context !== null; context = context.parent) {\n\t\t\t\t\t\tif (context._shadows) {\n\t\t\t\t\t\t\tcontext._shadows.forEach(name => view.add(name));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [...view];\n\t\t\t\t}\n\n\t\t\t\tfunction helperCreateShadowSetterCallback(code) {\n\t\t\t\t\tconst shadowStore = {};\n\n\t\t\t\t\tgetMacroContextShadowView().forEach(varName => {\n\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t\t});\n\n\t\t\t\t\treturn function () {\n\t\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\t\tevaluation.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Evaluate the JavaScript.\n\t\t\t\t\t\t\treturn Scripting.evalJavaScript(code);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn helperCreateShadowSetterCallback;\n\t\t\t})()\n\t\t},\n\n\t\tparseSquareBracketedMarkup : {\n\t\t\tvalue : (() => {\n\t\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t\t'Error',     // error\n\t\t\t\t\t'DelimLTR',  // '|' or '->'\n\t\t\t\t\t'DelimRTL',  // '<-'\n\t\t\t\t\t'InnerMeta', // ']['\n\t\t\t\t\t'ImageMeta', // '[img[', '[<img[', or '[>img['\n\t\t\t\t\t'LinkMeta',  // '[['\n\t\t\t\t\t'Link',      // link destination\n\t\t\t\t\t'RightMeta', // ']]'\n\t\t\t\t\t'Setter',    // setter expression\n\t\t\t\t\t'Source',    // image source\n\t\t\t\t\t'Text'       // link text or image alt text\n\t\t\t\t]);\n\t\t\t\tconst Delim = Lexer.enumFromNames([ // delimiter state object (pseudo-enumeration)\n\t\t\t\t\t'None', // no delimiter encountered\n\t\t\t\t\t'LTR',  // '|' or '->'\n\t\t\t\t\t'RTL'   // '<-'\n\t\t\t\t]);\n\n\t\t\t\t// Lexing functions.\n\t\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\t\tloop: for (;;) {\n\t\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* falls through */\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t\t}\n\n\t\t\t\t\treturn lexer.pos;\n\t\t\t\t}\n\n\t\t\t\tfunction lexLeftMeta(lexer) {\n\t\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t}\n\n\t\t\t\t\t// Is link markup.\n\t\t\t\t\tif (lexer.accept('[')) {\n\t\t\t\t\t\tlexer.data.isLink = true;\n\t\t\t\t\t\tlexer.emit(Item.LinkMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\t// May be image markup.\n\t\t\t\t\telse {\n\t\t\t\t\t\tlexer.accept('<>'); // aligner syntax\n\n\t\t\t\t\t\tif (!lexer.accept('Ii') || !lexer.accept('Mm') || !lexer.accept('Gg') || !lexer.accept('[')) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlexer.data.isLink = false;\n\t\t\t\t\t\tlexer.emit(Item.ImageMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\t\t\t\t\treturn lexCoreComponents;\n\t\t\t\t}\n\n\t\t\t\tfunction lexCoreComponents(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\t\t\t\t\tlet delim = Delim.None;\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '|': // possible pipe ('|') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None) {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward();\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '-': // possible right arrow ('->') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '>') {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '<': // possible left arrow ('<-') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '-') {\n\t\t\t\t\t\t\t\tdelim = Delim.RTL;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimRTL);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexer.data.isLink ? lexSetter : lexImageLink;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexImageLink(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup link component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexSetter;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexSetter(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated single quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tif (lexer.peek() !== ']') {\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Setter);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Parse function.\n\t\t\t\tfunction parseSquareBracketedMarkup(w) {\n\t\t\t\t\t// Initialize the lexer.\n\t\t\t\t\tconst lexer  = new Lexer(w.source, lexLeftMeta);\n\n\t\t\t\t\t// Set the initial positions within the source string.\n\t\t\t\t\tlexer.start = lexer.pos = w.matchStart;\n\n\t\t\t\t\t// Lex the raw argument string.\n\t\t\t\t\tconst markup = {};\n\t\t\t\t\tconst items  = lexer.run();\n\t\t\t\t\tconst last   = items.last();\n\n\t\t\t\t\tif (last && last.type === Item.Error) {\n\t\t\t\t\t\tmarkup.error = last.message;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\titems.forEach(item => {\n\t\t\t\t\t\t\tconst text = item.text.trim();\n\n\t\t\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\t\t\tcase Item.ImageMeta:\n\t\t\t\t\t\t\t\tmarkup.isImage = true;\n\n\t\t\t\t\t\t\t\tif (text[1] === '<') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'left';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (text[1] === '>') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'right';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.LinkMeta:\n\t\t\t\t\t\t\t\tmarkup.isLink = true;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Link:\n\t\t\t\t\t\t\t\tif (text[0] === '~') {\n\t\t\t\t\t\t\t\t\tmarkup.forceInternal = true;\n\t\t\t\t\t\t\t\t\tmarkup.link = text.slice(1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tmarkup.link = text;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Setter:\n\t\t\t\t\t\t\t\tmarkup.setter = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Source:\n\t\t\t\t\t\t\t\tmarkup.source = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Text:\n\t\t\t\t\t\t\t\tmarkup.text = text;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tmarkup.pos = lexer.pos;\n\t\t\t\t\treturn markup;\n\t\t\t\t}\n\n\t\t\t\treturn parseSquareBracketedMarkup;\n\t\t\t\t/* eslint-enable no-param-reassign */\n\t\t\t})()\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Wikifier;\n})();\n/* eslint-enable max-len */\n\n/***********************************************************************************************************************\n\n\tmarkup/parserlib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, EOF, Engine, Lexer, Macro, MacroContext, Patterns, Scripting, State, Story, Template,\n\t       Wikifier, toStringOrDefault, throwError\n*/\n/* eslint \"no-param-reassign\": [ 2, { \"props\" : false } ] */\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _verbatimTagHandler(w) {\n\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\tconst match = this.lookahead.exec(w.source);\n\n\t\tif (match && match.index === w.matchStart) {\n\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(match[1])\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tParsers.\n\t*******************************************************************************************************************/\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByBlock',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^<<<\\\\n',\n\t\tterminator : '^<<<\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t.appendTo(w.output)\n\t\t\t\t\t.get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByLine',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^>+',\n\t\tlookahead  : /^>+/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curLevel = 0;\n\t\t\tlet newLevel = w.matchLength;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcurLevel = newLevel;\n\t\t\t\tw.subWikify(destStack[destStack.length - 1], this.terminator);\n\t\t\t\tjQuery(document.createElement('br')).appendTo(destStack[destStack.length - 1]);\n\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tnewLevel = match[0].length;\n\t\t\t\t\tw.nextMatch += match[0].length;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'macro',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<<',\n\t\tlookahead : new RegExp(`<<(/?${Patterns.macroName})(?:\\\\s*)((?:(?:\\`(?:\\\\\\\\.|[^\\`\\\\\\\\])*\\`)|(?:\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")|(?:'(?:\\\\\\\\.|[^'\\\\\\\\])*')|(?:\\\\[(?:[<>]?[Ii][Mm][Gg])?\\\\[[^\\\\r\\\\n]*?\\\\]\\\\]+)|[^>]|(?:>(?!>)))*)>>`, 'gm'),\n\t\tworking   : { source : '', name : '', arguments : '', index : 0 }, // the working parse object\n\t\tcontext   : null, // last execution context object (top-level macros, hierarchically, have a null context)\n\n\t\thandler(w) {\n\t\t\tconst matchStart = this.lookahead.lastIndex = w.matchStart;\n\n\t\t\tif (this.parseTag(w)) {\n\t\t\t\t/*\n\t\t\t\t\tIf `parseBody()` is called below, it will modify the current working\n\t\t\t\t\tvalues, so we must cache them now.\n\t\t\t\t*/\n\t\t\t\tconst nextMatch = w.nextMatch;\n\t\t\t\tconst name      = this.working.name;\n\t\t\t\tconst rawArgs   = this.working.arguments;\n\t\t\t\tlet macro;\n\n\t\t\t\ttry {\n\t\t\t\t\tmacro = Macro.get(name);\n\n\t\t\t\t\tif (macro) {\n\t\t\t\t\t\tlet payload = null;\n\n\t\t\t\t\t\tif (typeof macro.tags !== 'undefined') {\n\t\t\t\t\t\t\tpayload = this.parseBody(w, macro);\n\n\t\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\t\tw.nextMatch = nextMatch; // we must reset `w.nextMatch` here, as `parseBody()` modifies it\n\t\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t\t`cannot find a closing tag for macro <<${name}>>`,\n\t\t\t\t\t\t\t\t\t`${w.source.slice(matchStart, w.nextMatch)}\\u2026`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (typeof macro.handler === 'function') {\n\t\t\t\t\t\t\tconst args = !payload\n\t\t\t\t\t\t\t\t? this.createArgs(rawArgs, this.skipArgs(macro, macro.name))\n\t\t\t\t\t\t\t\t: payload[0].args;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tNew-style macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (typeof macro._MACRO_API !== 'undefined') {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tAdd the macro's execution context to the context chain.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tthis.context = new MacroContext({\n\t\t\t\t\t\t\t\t\tmacro,\n\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\t\t\tsource : w.source.slice(matchStart, w.nextMatch),\n\t\t\t\t\t\t\t\t\tparent : this.context,\n\t\t\t\t\t\t\t\t\tparser : w\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the execution context is properly restored in the event\n\t\t\t\t\t\t\t\t\tthat an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler.call(this.context);\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\tQUESTION: Swap to the following, which passes macro arguments in\n\t\t\t\t\t\t\t\t\t\tas parameters to the handler function, in addition to them being\n\t\t\t\t\t\t\t\t\t\tavailable on its `this`?  If so, it might still be something to\n\t\t\t\t\t\t\t\t\t\thold off on until v3, when the legacy macro API is removed.\n\n\t\t\t\t\t\t\t\t\t\tmacro.handler.apply(this.context, this.context.args);\n\t\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tthis.context = this.context.parent;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t[DEPRECATED] Old-style/legacy macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tSet up the raw arguments string.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tconst prevRawArgs = w._rawArgs;\n\t\t\t\t\t\t\t\tw._rawArgs = rawArgs;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the previous raw arguments string is properly restored in\n\t\t\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler(w.output, name, args, w, payload);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tw._rawArgs = prevRawArgs;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`macro <<${name}>> handler function ${typeof macro.handler === 'undefined' ? 'does not exist' : 'is not a function'}`,\n\t\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (Macro.tags.has(name)) {\n\t\t\t\t\t\tconst tags = Macro.tags.get(name);\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`child tag <<${name}>> was found outside of a call to its parent macro${tags.length === 1 ? '' : 's'} <<${tags.join('>>, <<')}>>`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`macro <<${name}>> does not exist`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute ${macro && macro.isWidget ? 'widget' : 'macro'} <<${name}>>: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.working.source    = '';\n\t\t\t\t\tthis.working.name      = '';\n\t\t\t\t\tthis.working.arguments = '';\n\t\t\t\t\tthis.working.index     = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t}\n\t\t},\n\n\t\tparseTag(w) {\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart && match[1]) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tthis.working.source    = w.source.slice(match.index, this.lookahead.lastIndex);\n\t\t\t\tthis.working.name      = match[1];\n\t\t\t\tthis.working.arguments = match[2];\n\t\t\t\tthis.working.index     = match.index;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseBody(w, macro) {\n\t\t\tconst openTag  = this.working.name;\n\t\t\tconst closeTag = `/${openTag}`;\n\t\t\tconst closeAlt = `end${openTag}`;\n\t\t\tconst bodyTags = Array.isArray(macro.tags) ? macro.tags : false;\n\t\t\tconst payload  = [];\n\t\t\tlet end          = -1;\n\t\t\tlet opened       = 1;\n\t\t\tlet curSource    = this.working.source;\n\t\t\tlet curTag       = this.working.name;\n\t\t\tlet curArgument  = this.working.arguments;\n\t\t\tlet contentStart = w.nextMatch;\n\n\t\t\twhile ((w.matchStart = w.source.indexOf(this.match, w.nextMatch)) !== -1) {\n\t\t\t\tif (!this.parseTag(w)) {\n\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = w.matchStart + this.match.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst tagSource = this.working.source;\n\t\t\t\tconst tagName   = this.working.name;\n\t\t\t\tconst tagArgs   = this.working.arguments;\n\t\t\t\tconst tagBegin  = this.working.index;\n\t\t\t\tconst tagEnd    = w.nextMatch;\n\t\t\t\tconst hasArgs   = tagArgs.trim() !== '';\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase openTag:\n\t\t\t\t\t++opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase closeAlt:\n\t\t\t\tcase closeTag:\n\t\t\t\t\tif (hasArgs) {\n\t\t\t\t\t\t// Skip over malformed closing tags and throw.\n\t\t\t\t\t\tw.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tthrow new Error(`malformed closing tag: \"${tagSource}\"`);\n\t\t\t\t\t}\n\t\t\t\t\t--opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (hasArgs && (tagName.startsWith('/') || tagName.startsWith('end'))) {\n\t\t\t\t\t\t// Skip over malformed alien closing tags.\n\t\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (opened === 1 && bodyTags) {\n\t\t\t\t\t\tfor (let i = 0, iend = bodyTags.length; i < iend; ++i) {\n\t\t\t\t\t\t\tif (tagName === bodyTags[i]) {\n\t\t\t\t\t\t\t\tpayload.push({\n\t\t\t\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tcurSource    = tagSource;\n\t\t\t\t\t\t\t\tcurTag       = tagName;\n\t\t\t\t\t\t\t\tcurArgument  = tagArgs;\n\t\t\t\t\t\t\t\tcontentStart = tagEnd;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (opened === 0) {\n\t\t\t\t\tpayload.push({\n\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t});\n\t\t\t\t\tend = tagEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (end !== -1) {\n\t\t\t\tw.nextMatch = end;\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tcreateArgs(rawArgsString, skipArgs) {\n\t\t\tconst args = skipArgs ? [] : this.parseArgs(rawArgsString);\n\n\t\t\t// Extend the args array with the raw and full argument strings.\n\t\t\tObject.defineProperties(args, {\n\t\t\t\traw : {\n\t\t\t\t\tvalue : rawArgsString\n\t\t\t\t},\n\t\t\t\tfull : {\n\t\t\t\t\tvalue : Scripting.parse(rawArgsString)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn args;\n\t\t},\n\n\t\tskipArgs(macro, tagName) {\n\t\t\tif (typeof macro.skipArgs !== 'undefined') {\n\t\t\t\tconst sa = macro.skipArgs;\n\n\t\t\t\treturn typeof sa === 'boolean' && sa || Array.isArray(sa) && sa.includes(tagName);\n\t\t\t}\n\t\t\t/* legacy */\n\t\t\telse if (typeof macro.skipArg0 !== 'undefined') {\n\t\t\t\treturn macro.skipArg0 && macro.name === tagName;\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseArgs : (() => {\n\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t'Error',        // error\n\t\t\t\t'Bareword',     // bare identifier\n\t\t\t\t'Expression',   // expression (backquoted)\n\t\t\t\t'String',       // quoted string (single or double)\n\t\t\t\t'SquareBracket' // [[…]] or [img[…]]\n\t\t\t]);\n\t\t\tconst spaceRe    = new RegExp(Patterns.space);\n\t\t\tconst notSpaceRe = new RegExp(Patterns.notSpace);\n\t\t\tconst varTest    = new RegExp(`^${Patterns.variable}`);\n\n\t\t\t// Lexing functions.\n\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\treturn lexer.pos;\n\t\t\t}\n\n\t\t\tfunction lexSpace(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(notSpaceRe);\n\n\t\t\t\tif (offset === EOF) {\n\t\t\t\t\t// no non-whitespace characters, so bail\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\telse if (offset !== 0) {\n\t\t\t\t\tlexer.pos += offset;\n\t\t\t\t\tlexer.ignore();\n\t\t\t\t}\n\n\t\t\t\t// determine what the next state is\n\t\t\t\tswitch (lexer.next()) {\n\t\t\t\tcase '`':\n\t\t\t\t\treturn lexExpression;\n\t\t\t\tcase '\"':\n\t\t\t\t\treturn lexDoubleQuote;\n\t\t\t\tcase \"'\":\n\t\t\t\t\treturn lexSingleQuote;\n\t\t\t\tcase '[':\n\t\t\t\t\treturn lexSquareBracket;\n\t\t\t\tdefault:\n\t\t\t\t\treturn lexBareword;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction lexExpression(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '`') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated backquote expression');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.Expression);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexDoubleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated double quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSingleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated single quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSquareBracket(lexer) {\n\t\t\t\tconst imgMeta = '<>IiMmGg';\n\t\t\t\tlet what;\n\n\t\t\t\tif (lexer.accept(imgMeta)) {\n\t\t\t\t\twhat = 'image';\n\t\t\t\t\tlexer.acceptRun(imgMeta);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\twhat = 'link';\n\t\t\t\t}\n\n\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t}\n\n\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\tcase '[':\n\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ']':\n\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\tif (lexer.depth < 0) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, \"unexpected right square bracket ']'\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\tif (lexer.next() === ']') {\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.SquareBracket);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexBareword(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(spaceRe);\n\t\t\t\tlexer.pos = offset === EOF ? lexer.source.length : lexer.pos + offset;\n\t\t\t\tlexer.emit(Item.Bareword);\n\t\t\t\treturn offset === EOF ? null : lexSpace;\n\t\t\t}\n\n\t\t\t// Parse function.\n\t\t\tfunction parseMacroArgs(rawArgsString) {\n\t\t\t\t// Initialize the lexer.\n\t\t\t\tconst lexer = new Lexer(rawArgsString, lexSpace);\n\t\t\t\tconst args  = [];\n\n\t\t\t\t// Lex the raw argument string.\n\t\t\t\tlexer.run().forEach(item => {\n\t\t\t\t\tlet arg = item.text;\n\n\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\tcase Item.Error:\n\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${item.message}`);\n\n\t\t\t\t\tcase Item.Bareword:\n\t\t\t\t\t\t// A variable, so substitute its value.\n\t\t\t\t\t\tif (varTest.test(arg)) {\n\t\t\t\t\t\t\targ = State.getVar(arg);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Property access on the settings or setup objects, so try to evaluate it.\n\t\t\t\t\t\telse if (/^(?:settings|setup)[.[]/.test(arg)) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(arg);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Null literal, so convert it into null.\n\t\t\t\t\t\telse if (arg === 'null') {\n\t\t\t\t\t\t\targ = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Undefined literal, so convert it into undefined.\n\t\t\t\t\t\telse if (arg === 'undefined') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean true literal, so convert it into true.\n\t\t\t\t\t\telse if (arg === 'true') {\n\t\t\t\t\t\t\targ = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean false literal, so convert it into false.\n\t\t\t\t\t\telse if (arg === 'false') {\n\t\t\t\t\t\t\targ = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// NaN literal, so convert it into NaN.\n\t\t\t\t\t\telse if (arg === 'NaN') {\n\t\t\t\t\t\t\targ = NaN;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Attempt to convert it into a number, in case it's a numeric literal.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst argAsNum = Number(arg);\n\n\t\t\t\t\t\t\tif (!Number.isNaN(argAsNum)) {\n\t\t\t\t\t\t\t\targ = argAsNum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.Expression:\n\t\t\t\t\t\targ = arg.slice(1, -1).trim(); // remove the backquotes and trim the expression\n\n\t\t\t\t\t\t// Empty backquotes.\n\t\t\t\t\t\tif (arg === '') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Evaluate the expression.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tThe enclosing parenthesis here are necessary to force a code string\n\t\t\t\t\t\t\t\t\tconsisting solely of an object literal to be evaluated as such, rather\n\t\t\t\t\t\t\t\t\tthan as a code block.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(`(${arg})`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument expression \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.String:\n\t\t\t\t\t\t// Evaluate the string to handle escaped characters.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targ = Scripting.evalJavaScript(arg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument string \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.SquareBracket:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\t\tsource     : arg,\n\t\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${markup.error}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (markup.pos < arg.length) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": unexpected character(s) \"${arg.slice(markup.pos)}\" (pos: ${markup.pos})`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Convert to a link or image object.\n\t\t\t\t\t\t\tif (markup.isLink) {\n\t\t\t\t\t\t\t\t// .isLink, [.text], [.forceInternal], .link, [.setter]\n\t\t\t\t\t\t\t\targ = { isLink : true };\n\t\t\t\t\t\t\t\targ.count    = markup.hasOwnProperty('text') ? 2 : 1;\n\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\targ.text     = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : arg.link;\n\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\targ.setFn    = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (markup.isImage) {\n\t\t\t\t\t\t\t\t// .isImage, [.align], [.title], .source, [.forceInternal], [.link], [.setter]\n\t\t\t\t\t\t\t\targ = (source => {\n\t\t\t\t\t\t\t\t\tconst imgObj = {\n\t\t\t\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t\t\t\tisImage : true\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t// Check for Twine 1.4 Base64 image passage transclusion.\n\t\t\t\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\t\t\t\timgObj.source  = passage.text;\n\t\t\t\t\t\t\t\t\t\t\timgObj.passage = passage.title;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn imgObj;\n\t\t\t\t\t\t\t\t})(Wikifier.helpers.evalPassageId(markup.source));\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\t\t\t\t\t\targ.align = markup.align;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\t\t\t\t\t\targ.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\targ.setFn = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\targs.push(arg);\n\t\t\t\t});\n\n\t\t\t\treturn args;\n\t\t\t}\n\n\t\t\treturn parseMacroArgs;\n\t\t})()\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'link',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[\\\\[[^[]',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// text=(text), forceInternal=(~), link=link, setter=(setter)\n\t\t\tconst link  = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\tconst text  = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : link;\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\n\t\t\t// Debug view setup.\n\t\t\tconst output = (Config.debug\n\t\t\t\t? new DebugView(w.output, 'link-markup', '[[link]]', w.source.slice(w.matchStart, w.nextMatch))\n\t\t\t\t: w\n\t\t\t).output;\n\n\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\tWikifier.createInternalLink(output, link, text, setFn);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tWikifier.createExternalLink(output, link, text);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'urlLink',\n\t\tprofiles : ['core'],\n\t\tmatch    : Patterns.url,\n\n\t\thandler(w) {\n\t\t\tw.outputText(Wikifier.createExternalLink(w.output, w.matchText), w.matchStart, w.nextMatch);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'image',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[[<>]?[Ii][Mm][Gg]\\\\[',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// Debug view setup.\n\t\t\tlet debugView;\n\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tw.output,\n\t\t\t\t\t'image-markup',\n\t\t\t\t\tmarkup.hasOwnProperty('link') ? '[img[][link]]' : '[img[]]',\n\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// align=(left|right), title=(title), source=source, forceInternal=(~), link=(link), setter=(setter)\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\t\t\tlet el     = (Config.debug ? debugView : w).output;\n\t\t\tlet source;\n\n\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\tconst link = Wikifier.helpers.evalPassageId(markup.link);\n\n\t\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\t\tel = Wikifier.createInternalLink(el, link, null, setFn);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tel = Wikifier.createExternalLink(el, link);\n\t\t\t\t}\n\n\t\t\t\tel.classList.add('link-image');\n\t\t\t}\n\n\t\t\tel = jQuery(document.createElement('img'))\n\t\t\t\t.appendTo(el)\n\t\t\t\t.get(0);\n\t\t\tsource = Wikifier.helpers.evalPassageId(markup.source);\n\n\t\t\t// Check for image passage transclusion.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\tel.setAttribute('data-passage', passage.title);\n\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tel.src = source;\n\n\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\tel.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t}\n\n\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\tel.align = markup.align;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'monospacedByBlock',\n\t\tprofiles  : ['block'],\n\t\tmatch     : '^\\\\{\\\\{\\\\{\\\\n',\n\t\tlookahead : /^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tconst pre = jQuery(document.createElement('pre'));\n\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t.text(match[1])\n\t\t\t\t\t.appendTo(pre);\n\t\t\t\tpre.appendTo(w.output);\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'formatByChar',\n\t\tprofiles : ['core'],\n\t\tmatch    : \"''|//|__|\\\\^\\\\^|~~|==|\\\\{\\\\{\\\\{\",\n\n\t\thandler(w) {\n\t\t\tswitch (w.matchText) {\n\t\t\tcase \"''\":\n\t\t\t\tw.subWikify(jQuery(document.createElement('strong')).appendTo(w.output).get(0), \"''\");\n\t\t\t\tbreak;\n\n\t\t\tcase '//':\n\t\t\t\tw.subWikify(jQuery(document.createElement('em')).appendTo(w.output).get(0), '//');\n\t\t\t\tbreak;\n\n\t\t\tcase '__':\n\t\t\t\tw.subWikify(jQuery(document.createElement('u')).appendTo(w.output).get(0), '__');\n\t\t\t\tbreak;\n\n\t\t\tcase '^^':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sup')).appendTo(w.output).get(0), '\\\\^\\\\^');\n\t\t\t\tbreak;\n\n\t\t\tcase '~~':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sub')).appendTo(w.output).get(0), '~~');\n\t\t\t\tbreak;\n\n\t\t\tcase '==':\n\t\t\t\tw.subWikify(jQuery(document.createElement('s')).appendTo(w.output).get(0), '==');\n\t\t\t\tbreak;\n\n\t\t\tcase '{{{':\n\t\t\t\t{\n\t\t\t\t\tconst lookahead = /\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}/gm;\n\n\t\t\t\t\tlookahead.lastIndex = w.matchStart;\n\n\t\t\t\t\tconst match = lookahead.exec(w.source);\n\n\t\t\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t\t\t.text(match[1])\n\t\t\t\t\t\t\t.appendTo(w.output);\n\t\t\t\t\t\tw.nextMatch = lookahead.lastIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'customStyle',\n\t\tprofiles   : ['core'],\n\t\tmatch      : '@@',\n\t\tterminator : '@@',\n\t\tblockRe    : /\\s*\\n/gm,\n\n\t\thandler(w) {\n\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\n\t\t\tthis.blockRe.lastIndex = w.nextMatch; // must follow the call to `inlineCss()`\n\n\t\t\tconst blockMatch = this.blockRe.exec(w.source);\n\t\t\tconst blockLevel = blockMatch && blockMatch.index === w.nextMatch;\n\t\t\tconst $el        = jQuery(document.createElement(blockLevel ? 'div' : 'span'))\n\t\t\t\t.appendTo(w.output);\n\n\t\t\tif (css.classes.length === 0 && css.id === '' && Object.keys(css.styles).length === 0) {\n\t\t\t\t$el.addClass('marked');\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcss.classes.forEach(className => $el.addClass(className));\n\n\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t$el.attr('id', css.id);\n\t\t\t\t}\n\n\t\t\t\t$el.css(css.styles);\n\t\t\t}\n\n\t\t\tif (blockLevel) {\n\t\t\t\t// Skip the leading and, if it exists, trailing newlines.\n\t\t\t\tw.nextMatch += blockMatch[0].length;\n\t\t\t\tw.subWikify($el[0], `\\\\n?${this.terminator}`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.subWikify($el[0], this.terminator);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimText',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '\"{3}|<[Nn][Oo][Ww][Ii][Kk][Ii]>',\n\t\tlookahead : /(?:\"{3}((?:.|\\n)*?)\"{3})|(?:<[Nn][Oo][Ww][Ii][Kk][Ii]>((?:.|\\n)*?)<\\/[Nn][Oo][Ww][Ii][Kk][Ii]>)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('verbatim')\n\t\t\t\t\t.text(match[1] || match[2])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'horizontalRule',\n\t\tprofiles : ['core'],\n\t\tmatch    : '^----+$\\\\n?|<[Hh][Rr]\\\\s*/?>\\\\n?',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createElement('hr')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'emdash',\n\t\tprofiles : ['core'],\n\t\tmatch    : '--',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('\\u2014')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'doubleDollarSign',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\${2}', // eslint-disable-line no-template-curly-in-string\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('$')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tSupported syntax:\n\t\t\t\t$variable\n\t\t\t\t$variable.property\n\t\t\t\t$variable[numericIndex]\n\t\t\t\t$variable[\"property\"]\n\t\t\t\t$variable['property']\n\t\t\t\t$variable[$indexOrPropertyVariable]\n\t\t*/\n\t\tname     : 'nakedVariable',\n\t\tprofiles : ['core'],\n\t\tmatch    : `${Patterns.variable}(?:(?:\\\\.${Patterns.identifier})|(?:\\\\[\\\\d+\\\\])|(?:\\\\[\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\"\\\\])|(?:\\\\['(?:\\\\\\\\.|[^'\\\\\\\\])+'\\\\])|(?:\\\\[${Patterns.variable}\\\\]))*`,\n\n\t\thandler(w) {\n\t\t\tconst result = toStringOrDefault(State.getVar(w.matchText), null);\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'variable', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'template',\n\t\tprofiles : ['core'],\n\t\tmatch    : `\\\\?${Patterns.templateName}`,\n\n\t\thandler(w) {\n\t\t\tconst name = w.matchText.slice(1);\n\t\t\tlet template = Template.get(name);\n\t\t\tlet result   = null;\n\n\t\t\t// If we have an array of templates, randomly choose one.\n\t\t\tif (template instanceof Array) {\n\t\t\t\ttemplate = template.random();\n\t\t\t}\n\n\t\t\tswitch (typeof template) {\n\t\t\tcase 'function':\n\t\t\t\ttry {\n\t\t\t\t\tresult = toStringOrDefault(template.call({ name }), null);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute function template ?${name}: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tresult = template;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'template', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'heading',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^!{1,6}',\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement(`h${w.matchLength}`)).appendTo(w.output).get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'table',\n\t\tprofiles       : ['block'],\n\t\tmatch          : '^\\\\|(?:[^\\\\n]*)\\\\|(?:[fhck]?)$',\n\t\tlookahead      : /^\\|([^\\n]*)\\|([fhck]?)$/gm,\n\t\trowTerminator  : '\\\\|(?:[cfhk]?)$\\\\n?',\n\t\tcellPattern    : '(?:\\\\|([^\\\\n\\\\|]*)\\\\|)|(\\\\|[cfhk]?$\\\\n?)',\n\t\tcellTerminator : '(?:\\\\u0020*)\\\\|',\n\t\trowTypes       : { c : 'caption', f : 'tfoot', h : 'thead', '' : 'tbody' }, // eslint-disable-line id-length\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst table       = jQuery(document.createElement('table')).appendTo(w.output).get(0);\n\t\t\tconst prevColumns = [];\n\t\t\tlet curRowType    = null;\n\t\t\tlet $rowContainer = null;\n\t\t\tlet rowCount      = 0;\n\t\t\tlet matched;\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst nextRowType = match[2];\n\n\t\t\t\t\tif (nextRowType === 'k') {\n\t\t\t\t\t\ttable.className = match[1];\n\t\t\t\t\t\tw.nextMatch += match[0].length + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (nextRowType !== curRowType) {\n\t\t\t\t\t\t\tcurRowType = nextRowType;\n\t\t\t\t\t\t\t$rowContainer = jQuery(document.createElement(this.rowTypes[nextRowType]))\n\t\t\t\t\t\t\t\t.appendTo(table);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (curRowType === 'c') {\n\t\t\t\t\t\t\t$rowContainer.css('caption-side', rowCount === 0 ? 'top' : 'bottom');\n\t\t\t\t\t\t\tw.nextMatch += 1;\n\t\t\t\t\t\t\tw.subWikify($rowContainer[0], this.rowTerminator);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.rowHandler(\n\t\t\t\t\t\t\t\tw,\n\t\t\t\t\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t\t\t\t\t.appendTo($rowContainer)\n\t\t\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\t\t\tprevColumns\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t++rowCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t},\n\n\t\trowHandler(w, rowEl, prevColumns) {\n\t\t\tconst cellRe = new RegExp(this.cellPattern, 'gm');\n\t\t\tlet col         = 0;\n\t\t\tlet curColCount = 1;\n\t\t\tlet matched;\n\n\t\t\tdo {\n\t\t\t\tcellRe.lastIndex = w.nextMatch;\n\n\t\t\t\tconst cellMatch = cellRe.exec(w.source);\n\n\t\t\t\tmatched = cellMatch && cellMatch.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tif (cellMatch[1] === '~') {\n\t\t\t\t\t\tconst last = prevColumns[col];\n\n\t\t\t\t\t\tif (last) {\n\t\t\t\t\t\t\t++last.rowCount;\n\t\t\t\t\t\t\tlast.$element\n\t\t\t\t\t\t\t\t.attr('rowspan', last.rowCount)\n\t\t\t\t\t\t\t\t.css('vertical-align', 'middle');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[1] === '>') {\n\t\t\t\t\t\t++curColCount;\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[2]) {\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t++w.nextMatch;\n\n\t\t\t\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\t\t\t\t\t\tlet spaceLeft  = false;\n\t\t\t\t\t\tlet spaceRight = false;\n\t\t\t\t\t\tlet $cell;\n\n\t\t\t\t\t\twhile (w.source.substr(w.nextMatch, 1) === ' ') {\n\t\t\t\t\t\t\tspaceLeft = true;\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (w.source.substr(w.nextMatch, 1) === '!') {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('th')).appendTo(rowEl);\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('td')).appendTo(rowEl);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevColumns[col] = {\n\t\t\t\t\t\t\trowCount : 1,\n\t\t\t\t\t\t\t$element : $cell\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (curColCount > 1) {\n\t\t\t\t\t\t\t$cell.attr('colspan', curColCount);\n\t\t\t\t\t\t\tcurColCount = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.subWikify($cell[0], this.cellTerminator);\n\n\t\t\t\t\t\tif (w.matchText.substr(w.matchText.length - 2, 1) === ' ') {\n\t\t\t\t\t\t\tspaceRight = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcss.classes.forEach(className => $cell.addClass(className));\n\n\t\t\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t\t\t$cell.attr('id', css.id);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (spaceLeft && spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'center';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceLeft) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'right';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'left';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$cell.css(css.styles);\n\n\t\t\t\t\t\tw.nextMatch = w.nextMatch - 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'list',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^(?:(?:\\\\*+)|(?:#+))',\n\t\tlookahead  : /^(?:(\\*+)|(#+))/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curType  = null;\n\t\t\tlet curLevel = 0;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst newType  = match[2] ? 'ol' : 'ul';\n\t\t\t\t\tconst newLevel = match[0].length;\n\n\t\t\t\t\tw.nextMatch += match[0].length;\n\n\t\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel === curLevel && newType !== curType) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tcurLevel = newLevel;\n\t\t\t\t\tcurType = newType;\n\t\t\t\t\tw.subWikify(\n\t\t\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\tthis.terminator\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'commentByBlock',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '(?:/(?:%|\\\\*))|(?:<!--)',\n\t\tlookahead : /(?:\\/(%|\\*)(?:(?:.|\\n)*?)\\1\\/)|(?:<!--(?:(?:.|\\n)*?)-->)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineContinuation',\n\t\tprofiles : ['core'],\n\n\t\t// WARNING: The ordering here is important: end-of-line, start-of-line, end-of-string, start-of-string.\n\t\tmatch : `\\\\\\\\${Patterns.spaceNoTerminator}*\\\\n|\\\\n${Patterns.spaceNoTerminator}*\\\\\\\\|\\\\n?\\\\\\\\${Patterns.spaceNoTerminator}*$|^${Patterns.spaceNoTerminator}*\\\\\\\\\\\\n?`,\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineBreak',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\n|<[Bb][Rr]\\\\s*/?>',\n\n\t\thandler(w) {\n\t\t\tif (!w.options.nobr) {\n\t\t\t\tjQuery(document.createElement('br')).appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'htmlCharacterReference',\n\t\tprofiles : ['core'],\n\t\tmatch    : '(?:(?:&#?[0-9A-Za-z]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9A-Fa-f]|1D[C-Fc-f][0-9A-Fa-f]|20[D-Fd-f][0-9A-Fa-f]|FE2[0-9A-Fa-f])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[0-9A-Za-z]{2,8};)',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(w.matchText)\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'xmlProlog',\n\t\tprofiles : ['core'],\n\t\tmatch    : '<\\\\?[Xx][Mm][Ll][^>]*\\\\?>',\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimHtml',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Hh][Tt][Mm][Ll]>',\n\t\tlookahead : /<[Hh][Tt][Mm][Ll]>((?:.|\\n)*?)<\\/[Hh][Tt][Mm][Ll]>/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimScriptTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Cc][Rr][Ii][Pp][Tt][^>]*>',\n\t\tlookahead : /(<[Ss][Cc][Rr][Ii][Pp][Tt]*>(?:.|\\n)*?<\\/[Ss][Cc][Rr][Ii][Pp][Tt]>)/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'styleTag',\n\t\tprofiles       : ['core'],\n\t\tmatch          : '<[Ss][Tt][Yy][Ll][Ee][^>]*>',\n\t\tlookahead      : /(<[Ss][Tt][Yy][Ll][Ee]*>)((?:.|\\n)*?)(<\\/[Ss][Tt][Yy][Ll][Ee]>)/gm,\n\t\timageMarkup    : new RegExp(Patterns.cssImage, 'g'),\n\t\thasImageMarkup : new RegExp(Patterns.cssImage),\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tlet css = match[2];\n\n\t\t\t\t// Check for wiki image transclusion.\n\t\t\t\tif (this.hasImageMarkup.test(css)) {\n\t\t\t\t\tthis.imageMarkup.lastIndex = 0;\n\n\t\t\t\t\tcss = css.replace(this.imageMarkup, wikiImage => {\n\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\tsource = passage.text;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t\t.append(match[1] + css + match[3])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'svgTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Vv][Gg][^>]*>',\n\t\tlookahead : /(<[Ss][Vv][Gg][^>]*>(?:.|\\n)*?<\\/[Ss][Vv][Gg]>)/gm,\n\t\tnamespace : 'http://www.w3.org/2000/svg',\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tconst $frag = jQuery(document.createDocumentFragment()).append(match[1]);\n\n\t\t\t\t// Postprocess the relevant SVG element nodes.\n\t\t\t\t$frag.find('a[data-passage],image[data-passage]').each((_, el) => {\n\t\t\t\t\tconst tagName = el.tagName.toLowerCase();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`svg|<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$frag.appendTo(w.output);\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// '<image>' element, so attempt media passage transclusion.\n\t\t\t\tif (tagName === 'image') {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t// NOTE: SVG `.href` IDL attribute is read-only,\n\t\t\t\t\t\t\t// so set its `href` content attribute instead.\n\t\t\t\t\t\t\tel.setAttribute('href', passage.text.trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>'.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tNOTE: This parser MUST come after any parser which handles HTML tag-\n\t\t\tlike constructs—e.g. 'verbatimText', 'horizontalRule', 'lineBreak',\n\t\t\t'xmlProlog', 'verbatimHtml', 'verbatimSvgTag', 'verbatimScriptTag',\n\t\t\tand 'styleTag'.\n\t\t*/\n\t\tname      : 'htmlTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<\\\\w+(?:\\\\s+[^\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\s\"\\'>\\\\/=]+(?:\\\\s*=\\\\s*(?:\"[^\"]*?\"|\\'[^\\']*?\\'|[^\\\\s\"\\'=<>`]+))?)*\\\\s*\\\\/?>',\n\t\ttagRe     : /^<(\\w+)/,\n\t\tmediaTags : ['audio', 'img', 'source', 'track', 'video'], // NOTE: The `<picture>` element should not be in this list.\n\t\tnobrTags  : ['audio', 'colgroup', 'datalist', 'dl', 'figure', 'meter', 'ol', 'optgroup', 'picture', 'progress', 'ruby', 'select', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'ul', 'video'],\n\t\tvoidTags  : ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],\n\n\t\thandler(w) {\n\t\t\tconst tagMatch = this.tagRe.exec(w.matchText);\n\t\t\tconst tag      = tagMatch && tagMatch[1];\n\t\t\tconst tagName  = tag && tag.toLowerCase();\n\n\t\t\tif (tagName) {\n\t\t\t\tconst isVoid = this.voidTags.includes(tagName) || w.matchText.endsWith('/>');\n\t\t\t\tconst isNobr = this.nobrTags.includes(tagName);\n\t\t\t\tlet terminator;\n\t\t\t\tlet terminatorMatch;\n\n\t\t\t\tif (!isVoid) {\n\t\t\t\t\tterminator = `<\\\\/${tagName}\\\\s*>`;\n\n\t\t\t\t\tconst terminatorRe = new RegExp(terminator, 'gim'); // ignore case during match\n\n\t\t\t\t\tterminatorRe.lastIndex = w.matchStart;\n\t\t\t\t\tterminatorMatch = terminatorRe.exec(w.source);\n\t\t\t\t}\n\n\t\t\t\tif (isVoid || terminatorMatch) {\n\t\t\t\t\tlet output    = w.output;\n\t\t\t\t\tlet el        = document.createElement(w.output.tagName);\n\t\t\t\t\tlet debugView;\n\n\t\t\t\t\tel.innerHTML = w.matchText;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of a `while` statement here is curious, however,\n\t\t\t\t\t\tI'm hesitant to change it for fear of breaking some edge case.\n\t\t\t\t\t*/\n\t\t\t\t\twhile (el.firstChild) {\n\t\t\t\t\t\tel = el.firstChild;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\n\t\t\t\t\t\t// Debug view setup.\n\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`html-${tagName}`,\n\t\t\t\t\t\t\t\ttagName,\n\t\t\t\t\t\t\t\tw.matchText\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tdebugView.modes({\n\t\t\t\t\t\t\t\tblock   : tagName === 'img',\n\t\t\t\t\t\t\t\tnonvoid : terminatorMatch\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toutput = debugView.output;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (terminatorMatch) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists\n\t\t\t\t\t\t\tsolely to ensure that the options stack is properly restored in\n\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the call to\n\t\t\t\t\t\t\t`subWikify()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tWikifier.Option.push({ nobr : isNobr });\n\t\t\t\t\t\t\tw.subWikify(el, terminator, { ignoreTerminatorCase : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tWikifier.Option.pop();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tDebug view modification.  If the current element has any debug\n\t\t\t\t\t\t\tview descendants who have \"block\" mode set, then set its debug\n\t\t\t\t\t\t\tview to the same.  It just makes things look a bit nicer.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif (debugView && jQuery(el).find('.debug.block').length > 0) {\n\t\t\t\t\t\t\tdebugView.modes({ block : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of `cloneNode(true)` here for `<track>` elements\n\t\t\t\t\t\tis necessary to workaround a poorly understood rehoming issue.\n\t\t\t\t\t*/\n\t\t\t\t\toutput.appendChild(tagName === 'track' ? el.cloneNode(true) : el);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot find a closing tag for HTML <${tag}>`,\n\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// Media element, so attempt media passage transclusion.\n\t\t\t\tif (this.mediaTags.includes(tagName)) {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tlet parentName;\n\t\t\t\t\t\tlet twineTag;\n\n\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\tcase 'audio':\n\t\t\t\t\t\tcase 'video':\n\t\t\t\t\t\t\ttwineTag = `Twine.${tagName}`;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'img':\n\t\t\t\t\t\t\ttwineTag = 'Twine.image';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'track':\n\t\t\t\t\t\t\ttwineTag = 'Twine.vtt';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'source':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst $parent = $(el).closest('audio,picture,video');\n\n\t\t\t\t\t\t\t\tif ($parent.length) {\n\t\t\t\t\t\t\t\t\tparentName = $parent.get(0).tagName.toLowerCase();\n\t\t\t\t\t\t\t\t\ttwineTag = `Twine.${parentName === 'picture' ? 'image' : parentName}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (passage.tags.includes(twineTag)) {\n\t\t\t\t\t\t\tel[parentName === 'picture' ? 'srcset' : 'src'] = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>', '<area>', '<button>', etc.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/template.js\n\n\tCopyright © 2019–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\nvar Template = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Template definitions.\n\tconst _templates = new Map();\n\n\t// Valid template name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.templateName})$`);\n\n\t// Valid template type predicate.\n\tconst _validType = template => {\n\t\tconst templateType = typeof template;\n\t\treturn templateType === 'function' || templateType === 'string';\n\t};\n\n\n\t/*******************************************************************************\n\t\tTemplate Functions.\n\t*******************************************************************************/\n\n\tfunction templateAdd(name, template) {\n\t\tif (\n\t\t\t   !_validType(template)\n\t\t\t&& !(template instanceof Array && template.length > 0 && template.every(_validType))\n\t\t) {\n\t\t\tthrow new TypeError(`invalid template type (${name}); templates must be: functions, strings, or an array of either`);\n\t\t}\n\n\t\t(name instanceof Array ? name : [name]).forEach(name => {\n\t\t\tif (!_validNameRe.test(name)) {\n\t\t\t\tthrow new Error(`invalid template name \"${name}\"`);\n\t\t\t}\n\t\t\tif (_templates.has(name)) {\n\t\t\t\tthrow new Error(`cannot clobber existing template ?${name}`);\n\t\t\t}\n\n\t\t\t_templates.set(name, template);\n\t\t});\n\t}\n\n\tfunction templateDelete(name) {\n\t\t(name instanceof Array ? name : [name]).forEach(name => _templates.delete(name));\n\t}\n\n\tfunction templateGet(name) {\n\t\treturn _templates.has(name) ? _templates.get(name) : null;\n\t}\n\n\tfunction templateHas(name) {\n\t\treturn _templates.has(name);\n\t}\n\n\tfunction templateSize() {\n\t\treturn _templates.size;\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tadd    : { value : templateAdd },\n\t\tdelete : { value : templateDelete },\n\t\tget    : { value : templateGet },\n\t\thas    : { value : templateHas },\n\t\tsize   : { get : templateSize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macro.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Scripting, macros */\n\nvar Macro = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Macro definitions.\n\tconst _macros = {};\n\n\t// Map of all macro tags and their parents (key: 'tag name' => value: ['list of parent names']).\n\tconst _tags = {};\n\n\t// Valid macro name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.macroName})$`);\n\n\n\t/*******************************************************************************************************************\n\t\tMacros Functions.\n\t*******************************************************************************************************************/\n\tfunction macrosAdd(name, def) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosAdd(name, def));\n\t\t\treturn;\n\t\t}\n\n\t\tif (!_validNameRe.test(name)) {\n\t\t\tthrow new Error(`invalid macro name \"${name}\"`);\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing macro <<${name}>>`);\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber child tag <<${name}>> of parent macro${_tags[name].length === 1 ? '' : 's'} <<${_tags[name].join('>>, <<')}>>`);\n\t\t}\n\n\t\ttry {\n\t\t\tif (typeof def === 'object') {\n\t\t\t\t// Add the macro definition.\n\t\t\t\t//\n\t\t\t\t// NOTE: Since `macrosGet()` may return legacy macros, we add the `_MACRO_API`\n\t\t\t\t// flag to (modern) API macros, so that the macro formatter will know how to\n\t\t\t\t// call the macro.  This should be removed in v3.\n\t\t\t\t_macros[name] = Object.assign(Object.create(null), def, { _MACRO_API : true });\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Add the macro alias.\n\t\t\t\tif (macrosHas(def)) {\n\t\t\t\t\t_macros[name] = Object.create(_macros[def], {\n\t\t\t\t\t\t_ALIAS_OF : {\n\t\t\t\t\t\t\tenumerable : true,\n\t\t\t\t\t\t\tvalue      : def\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`cannot create alias of nonexistent macro <<${def}>>`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(_macros, name, { writable : false });\n\t\t}\n\t\tcatch (ex) {\n\t\t\tif (ex.name === 'TypeError') {\n\t\t\t\tthrow new Error(`cannot clobber protected macro <<${name}>>`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`unknown error when attempting to add macro <<${name}>>: [${ex.name}] ${ex.message}`);\n\t\t\t}\n\t\t}\n\n\t\t// Tags post-processing.\n\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\tif (_macros[name].tags == null) { // lazy equality for null\n\t\t\t\ttagsRegister(name);\n\t\t\t}\n\t\t\telse if (Array.isArray(_macros[name].tags)) {\n\t\t\t\ttagsRegister(name, _macros[name].tags);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`bad value for \"tags\" property of macro <<${name}>>`);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction macrosDelete(name) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosDelete(name));\n\t\t\treturn;\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\t// Tags pre-processing.\n\t\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\t\ttagsUnregister(name);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Remove the macro definition.\n\t\t\t\tObject.defineProperty(_macros, name, { writable : true });\n\t\t\t\tdelete _macros[name];\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tthrow new Error(`unknown error removing macro <<${name}>>: ${ex.message}`);\n\t\t\t}\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot remove child tag <<${name}>> of parent macro <<${_tags[name]}>>`);\n\t\t}\n\t}\n\n\tfunction macrosIsEmpty() {\n\t\treturn Object.keys(_macros).length === 0;\n\t}\n\n\tfunction macrosHas(name) {\n\t\treturn _macros.hasOwnProperty(name);\n\t}\n\n\tfunction macrosGet(name) {\n\t\tlet macro = null;\n\n\t\tif (macrosHas(name) && typeof _macros[name].handler === 'function') {\n\t\t\tmacro = _macros[name];\n\t\t}\n\t\t/* legacy macro support */\n\t\telse if (macros.hasOwnProperty(name) && typeof macros[name].handler === 'function') {\n\t\t\tmacro = macros[name];\n\t\t}\n\t\t/* /legacy macro support */\n\n\t\treturn macro;\n\t}\n\n\tfunction macrosInit(handler = 'init') { // eslint-disable-line no-unused-vars\n\t\tObject.keys(_macros).forEach(name => {\n\t\t\tif (typeof _macros[name][handler] === 'function') {\n\t\t\t\t_macros[name][handler](name);\n\t\t\t}\n\t\t});\n\n\t\t/* legacy macro support */\n\t\tObject.keys(macros).forEach(name => {\n\t\t\tif (typeof macros[name][handler] === 'function') {\n\t\t\t\tmacros[name][handler](name);\n\t\t\t}\n\t\t});\n\t\t/* /legacy macro support */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTags Functions.\n\t*******************************************************************************************************************/\n\tfunction tagsRegister(parent, bodyTags) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tconst endTags = [`/${parent}`, `end${parent}`]; // automatically create the closing tags\n\t\tconst allTags = [].concat(endTags, Array.isArray(bodyTags) ? bodyTags : []);\n\n\t\tfor (let i = 0; i < allTags.length; ++i) {\n\t\t\tconst tag = allTags[i];\n\n\t\t\tif (macrosHas(tag)) {\n\t\t\t\tthrow new Error('cannot register tag for an existing macro');\n\t\t\t}\n\n\t\t\tif (tagsHas(tag)) {\n\t\t\t\tif (!_tags[tag].includes(parent)) {\n\t\t\t\t\t_tags[tag].push(parent);\n\t\t\t\t\t_tags[tag].sort();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_tags[tag] = [parent];\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction tagsUnregister(parent) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tObject.keys(_tags).forEach(tag => {\n\t\t\tconst i = _tags[tag].indexOf(parent);\n\n\t\t\tif (i !== -1) {\n\t\t\t\tif (_tags[tag].length === 1) {\n\t\t\t\t\tdelete _tags[tag];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_tags[tag].splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction tagsHas(name) {\n\t\treturn _tags.hasOwnProperty(name);\n\t}\n\n\tfunction tagsGet(name) {\n\t\treturn tagsHas(name) ? _tags[name] : null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tMacro Functions.\n\t\t*/\n\t\tadd     : { value : macrosAdd },\n\t\tdelete  : { value : macrosDelete },\n\t\tisEmpty : { value : macrosIsEmpty },\n\t\thas     : { value : macrosHas },\n\t\tget     : { value : macrosGet },\n\t\tinit    : { value : macrosInit },\n\n\t\t/*\n\t\t\tTags Functions.\n\t\t*/\n\t\ttags : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tregister   : { value : tagsRegister },\n\t\t\t\tunregister : { value : tagsUnregister },\n\t\t\t\thas        : { value : tagsHas },\n\t\t\t\tget        : { value : tagsGet }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) } // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrocontext.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, DebugView, Patterns, State, Wikifier, throwError */\n\nvar MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tMacroContext Class.\n\t*******************************************************************************************************************/\n\tclass MacroContext {\n\t\tconstructor(contextData) {\n\t\t\tconst context = Object.assign({\n\t\t\t\tparent      : null,\n\t\t\t\tmacro       : null,\n\t\t\t\tname        : '',\n\t\t\t\tdisplayName : '',\n\t\t\t\targs        : null,\n\t\t\t\tpayload     : null,\n\t\t\t\tparser      : null,\n\t\t\t\tsource      : ''\n\t\t\t}, contextData);\n\n\t\t\tif (context.macro === null || context.name === '' || context.parser === null) {\n\t\t\t\tthrow new TypeError('context object missing required properties');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tself : {\n\t\t\t\t\tvalue : context.macro\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : typeof context.macro._ALIAS_OF === 'undefined' ? context.name : context.macro._ALIAS_OF\n\t\t\t\t},\n\n\t\t\t\tdisplayName : {\n\t\t\t\t\tvalue : context.name\n\t\t\t\t},\n\n\t\t\t\targs : {\n\t\t\t\t\tvalue : context.args\n\t\t\t\t},\n\n\t\t\t\tpayload : {\n\t\t\t\t\tvalue : context.payload\n\t\t\t\t},\n\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : context.source\n\t\t\t\t},\n\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : context.parent\n\t\t\t\t},\n\n\t\t\t\tparser : {\n\t\t\t\t\tvalue : context.parser\n\t\t\t\t},\n\n\t\t\t\t_output : {\n\t\t\t\t\tvalue : context.parser.output\n\t\t\t\t},\n\n\t\t\t\t_shadows : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugView : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugViewEnabled : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Config.debug\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this._debugViewEnabled ? this.debugView.output : this._output;\n\t\t}\n\n\t\tget shadows() {\n\t\t\treturn [...this._shadows];\n\t\t}\n\n\t\tget shadowView() {\n\t\t\tconst view = new Set();\n\t\t\tthis.contextSelectAll(ctx => ctx._shadows)\n\t\t\t\t.forEach(ctx => ctx._shadows.forEach(name => view.add(name)));\n\t\t\treturn [...view];\n\t\t}\n\n\t\tget debugView() {\n\t\t\tif (this._debugViewEnabled) {\n\t\t\t\treturn this._debugView !== null ? this._debugView : this.createDebugView();\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextHas(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tcontextSelect(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextSelectAll(filter) {\n\t\t\tconst result = [];\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\tresult.push(context);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\taddShadow(...names) {\n\t\t\tif (!this._shadows) {\n\t\t\t\tthis._shadows = new Set();\n\t\t\t}\n\n\t\t\tconst varRe = new RegExp(`^${Patterns.variable}$`);\n\n\t\t\tnames\n\t\t\t\t.flat(Infinity)\n\t\t\t\t.forEach(name => {\n\t\t\t\t\tif (typeof name !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`variable name must be a string; type: ${typeof name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!varRe.test(name)) {\n\t\t\t\t\t\tthrow new Error(`invalid variable name \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._shadows.add(name);\n\t\t\t\t});\n\t\t}\n\n\t\tcreateShadowWrapper(callback, doneCallback, startCallback) {\n\t\t\tconst shadowContext = this;\n\t\t\tlet shadowStore;\n\n\t\t\tif (typeof callback === 'function') {\n\t\t\t\tshadowStore = {};\n\t\t\t\tthis.shadowView.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn function (...args) {\n\t\t\t\tif (typeof startCallback === 'function') {\n\t\t\t\t\tstartCallback.apply(this, args);\n\t\t\t\t}\n\n\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\t\t\t\t\tconst macroParser = Wikifier.Parser.get('macro');\n\t\t\t\t\tlet contextCache;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\tcallback.\n\t\t\t\t\t*/\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Cache the existing macro execution context and assign the shadow context.\n\t\t\t\t\t\tcontextCache = macroParser.context;\n\t\t\t\t\t\tmacroParser.context = shadowContext;\n\n\t\t\t\t\t\t// Call the callback function.\n\t\t\t\t\t\tcallback.apply(this, args);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\t// Revert the macro execution context shadowing.\n\t\t\t\t\t\tif (contextCache !== undefined) {\n\t\t\t\t\t\t\tmacroParser.context = contextCache;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (typeof doneCallback === 'function') {\n\t\t\t\t\tdoneCallback.apply(this, args);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tcreateDebugView(name, title) {\n\t\t\tthis._debugView = new DebugView(\n\t\t\t\tthis._output,\n\t\t\t\t'macro',\n\t\t\t\tname ? name : this.displayName,\n\t\t\t\ttitle ? title : this.source\n\t\t\t);\n\n\t\t\tif (this.payload !== null && this.payload.length > 0) {\n\t\t\t\tthis._debugView.modes({ nonvoid : true });\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = true;\n\t\t\treturn this._debugView;\n\t\t}\n\n\t\tremoveDebugView() {\n\t\t\tif (this._debugView !== null) {\n\t\t\t\tthis._debugView.remove();\n\t\t\t\tthis._debugView = null;\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = false;\n\t\t}\n\n\t\terror(message, source, stack) {\n\t\t\treturn throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source, stack);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn MacroContext;\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrolib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, Engine, Has, L10n, Macro, NodeTyper, Patterns, Scripting, SimpleAudio, State,\n\t       Story, TempState, Util, Wikifier, postdisplay, prehistory, storage, toStringOrDefault\n*/\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tVariables Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<capture>>\n\t*/\n\tMacro.add('capture', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\t\ttsVarRe  : new RegExp(`(${Patterns.variable})`,'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.raw.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst valueCache = {};\n\n\t\t\t/*\n\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t`Wikifier` call.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\tconst tsVarRe = this.self.tsVarRe;\n\t\t\t\tlet match;\n\n\t\t\t\t/*\n\t\t\t\t\tCache the existing values of the variables and add a shadow.\n\t\t\t\t*/\n\t\t\t\twhile ((match = tsVarRe.exec(this.args.raw)) !== null) {\n\t\t\t\t\tconst varName = match[1];\n\t\t\t\t\tconst varKey  = varName.slice(1);\n\t\t\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.addShadow(varName);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier(this.output, this.payload[0].contents);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t// Revert the variable shadowing.\n\t\t\t\tthis.shadows.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<set>>\n\t*/\n\tMacro.add('set', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<unset>>\n\t*/\n\tMacro.add('unset', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(\n\t\t\t`State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})`,\n\t\t\t'g'\n\t\t),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst jsVarRe = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst store = State[match[1]];\n\t\t\t\tconst name  = match[2];\n\n\t\t\t\tif (store.hasOwnProperty(name)) {\n\t\t\t\t\tdelete store[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remember>>\n\t*/\n\tMacro.add('remember', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember') || {};\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\t\t\t\tremember[name] = State.variables[name];\n\t\t\t}\n\n\t\t\tif (!storage.set('remember', remember)) {\n\t\t\t\treturn this.error(`unknown error, cannot remember: ${this.args.raw}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t},\n\n\t\tinit() {\n\t\t\tconst remember = storage.get('remember');\n\n\t\t\tif (remember) {\n\t\t\t\tObject.keys(remember).forEach(name => State.variables[name] = remember[name]);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<forget>>\n\t*/\n\tMacro.add('forget', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story variable list specified');\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember');\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\t\t\tlet needStore = false;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\n\t\t\t\tif (State.variables.hasOwnProperty(name)) {\n\t\t\t\t\tdelete State.variables[name];\n\t\t\t\t}\n\n\t\t\t\tif (remember && remember.hasOwnProperty(name)) {\n\t\t\t\t\tneedStore = true;\n\t\t\t\t\tdelete remember[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needStore) {\n\t\t\t\tif (Object.keys(remember).length === 0) {\n\t\t\t\t\tif (!storage.delete('remember')) {\n\t\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!storage.set('remember', remember)) {\n\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tScripting Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<run>>\n\t*/\n\tMacro.add('run', 'set'); // add <<run>> as an alias of <<set>>\n\n\t/*\n\t\t<<script>>\n\t*/\n\tMacro.add('script', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.payload[0].contents, output);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.createDebugView();\n\t\t\t}\n\n\t\t\tif (output.hasChildNodes()) {\n\t\t\t\tthis.output.appendChild(output);\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDisplay Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<include>>\n\t*/\n\tMacro.add('include', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tpassage = Story.get(passage);\n\t\t\tlet $el;\n\n\t\t\tif (this.args[1]) {\n\t\t\t\t$el = jQuery(document.createElement(this.args[1]))\n\t\t\t\t\t.addClass(`${passage.domId} macro-${this.name}`)\n\t\t\t\t\t.attr('data-passage', passage.title)\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(this.output);\n\t\t\t}\n\n\t\t\t$el.wiki(passage.processText());\n\t\t}\n\t});\n\n\t/*\n\t\t<<nobr>>\n\t*/\n\tMacro.add('nobr', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\t/*\n\t\t\t\tWikify the contents, after removing all leading & trailing newlines and compacting\n\t\t\t\tall internal sequences of newlines into single spaces.\n\t\t\t*/\n\t\t\tnew Wikifier(this.output, this.payload[0].contents.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' '));\n\t\t}\n\t});\n\n\t/*\n\t\t<<print>>, <<=>>, & <<->>\n\t*/\n\tMacro.add(['print', '=', '-'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = toStringOrDefault(Scripting.evalJavaScript(this.args.full), null);\n\n\t\t\t\tif (result !== null) {\n\t\t\t\t\tnew Wikifier(this.output, this.name === '-' ? Util.escape(result) : result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<silently>>\n\t*/\n\tMacro.add('silently', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.payload[0].contents.trim());\n\n\t\t\tif (Config.debug) {\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tthis.debugView.modes({ block : true, hidden : true });\n\t\t\t\tthis.output.appendChild(frag);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Discard the output, unless there were errors.\n\t\t\t\tconst errList = [...frag.querySelectorAll('.error')].map(errEl => errEl.textContent);\n\n\t\t\t\tif (errList.length > 0) {\n\t\t\t\t\treturn this.error(`error${errList.length === 1 ? '' : 's'} within contents (${errList.join('; ')})`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<type speed [start delay] [class classes] [element tag] [id ID] [keep|none] [skipkey key]>>\n\t*/\n\tMacro.add('type', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no speed specified');\n\t\t\t}\n\n\t\t\tconst speed = Util.fromCssTime(this.args[0]); // in milliseconds\n\n\t\t\tif (speed < 0) {\n\t\t\t\treturn this.error(`speed time value must be non-negative (received: ${this.args[0]})`);\n\t\t\t}\n\n\t\t\tlet cursor;\n\t\t\tlet elClass = '';\n\t\t\tlet elId    = '';\n\t\t\tlet elTag   = 'div';\n\t\t\tlet skipKey = Config.macros.typeSkipKey;\n\t\t\tlet start   = 400; // in milliseconds\n\n\t\t\t// Process optional arguments.\n\t\t\tconst options = this.args.slice(1);\n\n\t\t\twhile (options.length > 0) {\n\t\t\t\tconst option = options.shift();\n\n\t\t\t\tswitch (option) {\n\t\t\t\tcase 'class': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('class option missing required class name(s)');\n\t\t\t\t\t}\n\n\t\t\t\t\telClass = options.shift();\n\n\t\t\t\t\tif (elClass === '') {\n\t\t\t\t\t\tthrow new Error('class option class name(s) must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'element': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('element option missing required element tag name');\n\t\t\t\t\t}\n\n\t\t\t\t\telTag = options.shift();\n\n\t\t\t\t\tif (elTag === '') {\n\t\t\t\t\t\tthrow new Error('element option tag name must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'id': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('id option missing required ID');\n\t\t\t\t\t}\n\n\t\t\t\t\telId = options.shift();\n\n\t\t\t\t\tif (elId === '') {\n\t\t\t\t\t\tthrow new Error('id option ID must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'keep':\n\t\t\t\t\tcursor = 'keep';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'none':\n\t\t\t\t\tcursor = 'none';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'skipkey': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('skipkey option missing required key value');\n\t\t\t\t\t}\n\n\t\t\t\t\tskipKey = options.shift();\n\n\t\t\t\t\tif (skipKey === '') {\n\t\t\t\t\t\tthrow new Error('skipkey option key value must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'start': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('start option missing required time value');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = options.shift();\n\t\t\t\t\tstart = Util.fromCssTime(value);\n\n\t\t\t\t\tif (start < 0) {\n\t\t\t\t\t\tthrow new Error(`start option time value must be non-negative (received: ${value})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.error(`unknown option: ${option}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contents = this.payload[0].contents;\n\n\t\t\t// Do nothing if there's no content to type out.\n\t\t\tif (contents.trim() === '') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// Set up our base class name and event namespace.\n\t\t\tconst className = `macro-${this.name}`;\n\t\t\tconst namespace = `.${className}`;\n\n\t\t\t// Create a target to be later replaced by the typing wrapper.\n\t\t\tconst $target = jQuery(document.createElement(elTag))\n\t\t\t\t.addClass(`${className} ${className}-target`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Initialize the queue and clean up handlers.\n\t\t\tif (!TempState.macroTypeQueue) {\n\t\t\t\t// Set up the typing handler queue for all invocations.\n\t\t\t\tTempState.macroTypeQueue = [];\n\n\t\t\t\t// Immediately clear any existing handlers from our namespace and set up a\n\t\t\t\t// `:passageinit` event handler to clean up after navigation.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`:passageinit${namespace}`, () => $(document).off(namespace));\n\t\t\t}\n\n\t\t\t// If the queue is empty at this point, set the start typing flag.\n\t\t\tconst startTyping = TempState.macroTypeQueue.length === 0;\n\n\t\t\t// Push our typing handler onto the queue.\n\t\t\tTempState.macroTypeQueue.push(() => {\n\t\t\t\tconst $wrapper = jQuery(document.createElement(elTag))\n\t\t\t\t\t.addClass(className);\n\n\t\t\t\t// Add the user ID, if any.\n\t\t\t\tif (elId) {\n\t\t\t\t\t$wrapper.attr('id', elId);\n\t\t\t\t}\n\n\t\t\t\t// Add the user class(es), if any.\n\t\t\t\tif (elClass) {\n\t\t\t\t\t$wrapper.addClass(elClass);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier($wrapper, contents);\n\n\t\t\t\tconst passage = State.passage;\n\n\t\t\t\t// Skip typing if….\n\t\t\t\tif (\n\t\t\t\t\t// …we've visited the passage before.\n\t\t\t\t\t!Config.macros.typeVisitedPassages\n\t\t\t\t\t&& State.passages.slice(0, -1).some(title => title === passage)\n\n\t\t\t\t\t// …there were any content errors.\n\t\t\t\t\t|| $wrapper.find('.error').length > 0\n\t\t\t\t) {\n\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t// Run the next typing handler in the queue, if any.\n\t\t\t\t\tif (TempState.macroTypeQueue.length > 0) {\n\t\t\t\t\t\tTempState.macroTypeQueue.first()();\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Create a new `NodeTyper` instance for the wrapper's contents and\n\t\t\t\t// replace the target with the typing wrapper.\n\t\t\t\tconst typer = new NodeTyper({\n\t\t\t\t\ttargetNode : $wrapper.get(0),\n\t\t\t\t\tclassNames : cursor === 'none' ? null : `${className}-cursor`\n\t\t\t\t});\n\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t// Set up event IDs.\n\t\t\t\tconst typingCompleteId = ':typingcomplete';\n\t\t\t\tconst typingStartId    = ':typingstart';\n\t\t\t\tconst typingStopId     = ':typingstop';\n\t\t\t\tconst keydownAndNS     = `keydown${namespace}`;\n\t\t\t\tconst typingStopAndNS  = `${typingStopId}${namespace}`;\n\n\t\t\t\t// Set up handlers for spacebar aborting and continuations.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(keydownAndNS)\n\t\t\t\t\t.on(keydownAndNS, ev => {\n\t\t\t\t\t\t// Finish typing if the player aborts via the skip key.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tUtil.scrubEventKey(ev.key) === skipKey\n\t\t\t\t\t\t\t&& (ev.target === document.body || ev.target === document.documentElement)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$(document).off(keydownAndNS);\n\t\t\t\t\t\t\ttyper.finish();\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.one(typingStopAndNS, () => {\n\t\t\t\t\t\t// Fire the typing complete event and return, if the queue is empty.\n\t\t\t\t\t\tif (TempState.macroTypeQueue.length === 0) {\n\t\t\t\t\t\t\tjQuery.event.trigger(typingCompleteId);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Run the next typing handler in the queue.\n\t\t\t\t\t\tTempState.macroTypeQueue.first()();\n\t\t\t\t\t});\n\n\t\t\t\t// Set up the typing interval and start/stop event firing.\n\t\t\t\tconst typeNode = function typeNode() {\n\t\t\t\t\t// Fire the typing start event.\n\t\t\t\t\t$wrapper.trigger(typingStartId);\n\n\t\t\t\t\tconst typeNodeId = setInterval(() => {\n\t\t\t\t\t\t// Stop typing if….\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t// …we've navigated away.\n\t\t\t\t\t\t\tState.passage !== passage\n\n\t\t\t\t\t\t\t// …we're done typing.\n\t\t\t\t\t\t\t|| !typer.type()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tclearInterval(typeNodeId);\n\n\t\t\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t\t\t// Fire the typing stop event.\n\t\t\t\t\t\t\t$wrapper.trigger(typingStopId);\n\n\t\t\t\t\t\t\t// Add the done class to the wrapper.\n\t\t\t\t\t\t\t$wrapper.addClass(`${className}-done`);\n\n\t\t\t\t\t\t\t// Add the cursor class to the wrapper, if we're keeping it.\n\t\t\t\t\t\t\tif (cursor === 'keep') {\n\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-cursor`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}, speed);\n\t\t\t\t};\n\n\t\t\t\t// Kick off typing the node.\n\t\t\t\tif (start) {\n\t\t\t\t\tsetTimeout(typeNode, start);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\ttypeNode();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we're to start typing, then either set up a `:passageend` event handler\n\t\t\t// to do so or start it immediately, depending on the engine state.\n\t\t\tif (startTyping) {\n\t\t\t\tif (Engine.isPlaying()) {\n\t\t\t\t\t$(document).one(`:passageend${namespace}`, () => TempState.macroTypeQueue.first()());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tTempState.macroTypeQueue.first()();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<display>>\n\t*/\n\tMacro.add('display', 'include'); // add <<display>> as an alias of <<include>>\n\n\n\t/*******************************************************************************************************************\n\t\tControl Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<if>>, <<elseif>>, & <<else>>\n\t*/\n\tMacro.add('if', {\n\t\tskipArgs   : true,\n\t\ttags       : ['elseif', 'else'],\n\t\telseifWsRe : /^\\s*if\\b/i,\n\t\tifAssignRe : /[^!=&^|<>*/%+-]=[^=>]/,\n\n\t\thandler() {\n\t\t\tlet i;\n\n\t\t\ttry {\n\t\t\t\tconst len = this.payload.length;\n\n\t\t\t\t// Sanity checks.\n\t\t\t\tconst elseifWsRe = this.self.elseifWsRe;\n\t\t\t\tconst ifAssignRe = this.self.ifAssignRe;\n\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t/* eslint-disable prefer-template */\n\t\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\t\tcase 'else':\n\t\t\t\t\t\tif (this.payload[i].args.raw.length > 0) {\n\t\t\t\t\t\t\tif (elseifWsRe.test(this.payload[i].args.raw)) {\n\t\t\t\t\t\t\t\treturn this.error(`whitespace is not allowed between the \"else\" and \"if\" in <<elseif>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn this.error(`<<else>> does not accept a conditional expression (perhaps you meant to use <<elseif>>), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\t\treturn this.error('<<else>> must be the final clause');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (this.payload[i].args.full.length === 0) {\n\t\t\t\t\t\t\treturn this.error(`no conditional expression specified for <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t   Config.macros.ifAssignmentError\n\t\t\t\t\t\t\t&& ifAssignRe.test(this.payload[i].args.full)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn this.error(`assignment operator found within <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''} (perhaps you meant to use an equality operator: ==, ===, eq, is), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable prefer-template */\n\t\t\t\t}\n\n\t\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\t\tlet success = false;\n\n\t\t\t\t// Evaluate the clauses.\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t// Custom debug view setup for the current clause.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Conditional test.\n\t\t\t\t\tif (this.payload[i].name === 'else' || !!evalJavaScript(this.payload[i].args.full)) {\n\t\t\t\t\t\tsuccess = true;\n\t\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t\t// Custom debug view setup for a failed conditional.\n\t\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup for the remaining clauses.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tFake a debug view for `<</if>>`.  We do this to aid the checking of nesting\n\t\t\t\t\t\tand as a quick indicator of if any of the clauses matched.\n\t\t\t\t\t*/\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : !success,\n\t\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression in <<${i === 0 ? 'if' : 'elseif'}>> clause${i > 0 ? ' (#' + i + ')' : ''}: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack); // eslint-disable-line prefer-template\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<switch>>, <<case>>, & <<default>>\n\t*/\n\tMacro.add('switch', {\n\t\tskipArgs : ['switch'],\n\t\ttags     : ['case', 'default'],\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\tconst len = this.payload.length;\n\n\t\t\t// if (len === 1 || !this.payload.some(p => p.name === 'case')) {\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no cases specified');\n\t\t\t}\n\n\t\t\tlet i;\n\n\t\t\t// Sanity checks.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\tcase 'default':\n\t\t\t\t\tif (this.payload[i].args.length > 0) {\n\t\t\t\t\t\treturn this.error(`<<default>> does not accept values, invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\treturn this.error('<<default>> must be the final case');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no value(s) specified for <<${this.payload[i].name}>> (#${i})`);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet result;\n\n\t\t\ttry {\n\t\t\t\tresult = Scripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst debugView = this.debugView; // cache it now, to be modified later\n\t\t\tlet success = false;\n\n\t\t\t// Initial debug view setup for `<<switch>>`.\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Evaluate the clauses.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\t// Custom debug view setup for the current case.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t}\n\n\t\t\t\t// Case test(s).\n\t\t\t\tif (this.payload[i].name === 'default' || this.payload[i].args.some(val => val === result)) {\n\t\t\t\t\tsuccess = true;\n\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t// Custom debug view setup for a failed case.\n\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup for the remaining cases.\n\t\t\tif (Config.debug) {\n\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t\tFinalize the debug view for `<<switch>>` and fake a debug view for `<</switch>>`.\n\t\t\t\t\tWe do both as a quick indicator of if any of the cases matched and the latter\n\t\t\t\t\tto aid the checking of nesting.\n\t\t\t\t*/\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t\tthis\n\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<for>>, <<break>>, & <<continue>>\n\t*/\n\tMacro.add('for', {\n\t\t/* eslint-disable max-len */\n\t\tskipArgs    : true,\n\t\ttags        : null,\n\t\thasRangeRe  : new RegExp(`^\\\\S${Patterns.anyChar}*?\\\\s+range\\\\s+\\\\S${Patterns.anyChar}*?$`),\n\t\trangeRe     : new RegExp(`^(?:State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s*,\\\\s*)?State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s+range\\\\s+(\\\\S${Patterns.anyChar}*?)$`),\n\t\tthreePartRe : /^([^;]*?)\\s*;\\s*([^;]*?)\\s*;\\s*([^;]*?)$/,\n\t\tforInRe     : /^\\S+\\s+in\\s+\\S+/i,\n\t\tforOfRe     : /^\\S+\\s+of\\s+\\S+/i,\n\t\t/* eslint-enable max-len */\n\n\t\thandler() {\n\t\t\tconst argsStr = this.args.full.trim();\n\t\t\tconst payload = this.payload[0].contents.replace(/\\n$/, '');\n\n\t\t\t// Empty form.\n\t\t\tif (argsStr.length === 0) {\n\t\t\t\tthis.self.handleFor.call(this, payload, null, true, null);\n\t\t\t}\n\n\t\t\t// Range form.\n\t\t\telse if (this.self.hasRangeRe.test(argsStr)) {\n\t\t\t\tconst parts = argsStr.match(this.self.rangeRe);\n\n\t\t\t\tif (parts === null) {\n\t\t\t\t\treturn this.error('invalid range form syntax, format: [index ,] value range collection');\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleForRange.call(\n\t\t\t\t\tthis,\n\t\t\t\t\tpayload,\n\t\t\t\t\t{ type : parts[1], name : parts[2] },\n\t\t\t\t\t{ type : parts[3], name : parts[4] },\n\t\t\t\t\tparts[5]\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Conditional forms.\n\t\t\telse {\n\t\t\t\tlet init;\n\t\t\t\tlet condition;\n\t\t\t\tlet post;\n\n\t\t\t\t// Conditional-only form.\n\t\t\t\tif (argsStr.indexOf(';') === -1) {\n\t\t\t\t\t// Sanity checks.\n\t\t\t\t\tif (this.self.forInRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…in is not supported; see: for…range');\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.self.forOfRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…of is not supported; see: for…range');\n\t\t\t\t\t}\n\n\t\t\t\t\tcondition = argsStr;\n\t\t\t\t}\n\n\t\t\t\t// 3-part conditional form.\n\t\t\t\telse {\n\t\t\t\t\tconst parts = argsStr.match(this.self.threePartRe);\n\n\t\t\t\t\tif (parts === null) {\n\t\t\t\t\t\treturn this.error('invalid 3-part conditional form syntax, format: [init] ; [condition] ; [post]');\n\t\t\t\t\t}\n\n\t\t\t\t\tinit      = parts[1];\n\t\t\t\t\tcondition = parts[2].trim();\n\t\t\t\t\tpost      = parts[3];\n\n\t\t\t\t\tif (condition.length === 0) {\n\t\t\t\t\t\tcondition = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleFor.call(this, payload, init, condition, post);\n\t\t\t}\n\t\t},\n\n\t\thandleFor(payload, init, condition, post) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet first  = true;\n\t\t\tlet safety = Config.macros.maxLoopIterations;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tif (init) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tevalJavaScript(init);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad init expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile (evalJavaScript(condition)) {\n\t\t\t\t\tif (--safety < 0) {\n\t\t\t\t\t\treturn this.error(`exceeded configured maximum loop iterations (${Config.macros.maxLoopIterations})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (post) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevalJavaScript(post);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\treturn this.error(`bad post expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\thandleForRange(payload, indexVar, valueVar, rangeExp) {\n\t\t\tlet first     = true;\n\t\t\tlet rangeList;\n\n\t\t\ttry {\n\t\t\t\trangeList = this.self.toRangeList(rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tfor (let i = 0; i < rangeList.length; ++i) {\n\t\t\t\t\tif (indexVar.name) {\n\t\t\t\t\t\tState[indexVar.type][indexVar.name] = rangeList[i][0];\n\t\t\t\t\t}\n\n\t\t\t\t\tState[valueVar.type][valueVar.name] = rangeList[i][1];\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\ttoRangeList(rangeExp) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet value;\n\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t*/\n\t\t\t\tvalue = evalJavaScript(rangeExp[0] === '{' ? `(${rangeExp})` : rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tif (typeof ex !== 'object') {\n\t\t\t\t\tthrow new Error(`bad range expression: ${ex}`);\n\t\t\t\t}\n\n\t\t\t\tex.message = `bad range expression: ${ex.message}`;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tlet list;\n\n\t\t\tswitch (typeof value) {\n\t\t\tcase 'string':\n\t\t\t\tlist = [];\n\t\t\t\tfor (let i = 0; i < value.length; /* empty */) {\n\t\t\t\t\tconst obj = Util.charAndPosAt(value, i);\n\t\t\t\t\tlist.push([i, obj.char]);\n\t\t\t\t\ti = 1 + obj.end;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'object':\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tlist = value.map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Set) {\n\t\t\t\t\tlist = [...value].map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Map) {\n\t\t\t\t\tlist = [...value.entries()];\n\t\t\t\t}\n\t\t\t\telse if (Util.toStringTag(value) === 'Object') {\n\t\t\t\t\tlist = Object.keys(value).map(key => [key, value[key]]);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`unsupported range expression type: ${Util.toStringTag(value)}`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`unsupported range expression type: ${typeof value}`);\n\t\t\t}\n\n\t\t\treturn list;\n\t\t}\n\t});\n\tMacro.add(['break', 'continue'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.contextHas(ctx => ctx.name === 'for')) {\n\t\t\t\tTempState.break = this.name === 'continue' ? 1 : 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<for>>');\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tInteractive Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<button>> & <<link>>\n\t*/\n\tMacro.add(['button', 'link'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error(`no ${this.name === 'button' ? 'button' : 'link'} text specified`);\n\t\t\t}\n\n\t\t\tconst $link = jQuery(document.createElement(this.name === 'button' ? 'button' : 'a'));\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\tconst $image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t.attr('src', this.args[0].source)\n\t\t\t\t\t\t.appendTo($link);\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t}\n\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t$link.append(document.createTextNode(this.args[0].text));\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the link text.\n\t\t\t\t$link.wikiWithOptions({ profile : 'core' }, this.args[0]);\n\t\t\t\tpassage = this.args.length > 1 ? this.args[1] : undefined;\n\t\t\t}\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$link.addClass('link-internal');\n\t\t\t}\n\n\t\t\t$link\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : passage != null // lazy equality for null\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\tthis.payload[0].contents !== ''\n\t\t\t\t\t\t? () => Wikifier.wikifyEval(this.payload[0].contents.trim())\n\t\t\t\t\t\t: null,\n\t\t\t\t\tpassage != null // lazy equality for null\n\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t: null\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<checkbox>>\n\t*/\n\tMacro.add('checkbox', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 3) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('unchecked value'); }\n\t\t\t\tif (this.args.length < 3) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst uncheckValue = this.args[1];\n\t\t\tconst checkValue   = this.args[2];\n\t\t\tconst el           = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'checkbox',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.checked ? checkValue : uncheckValue);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and input element to the appropriate value and state, as requested.\n\t\t\t*/\n\t\t\tswitch (this.args[3]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<cycle>>, <<listbox>>, <<option>>, & <<optionsfrom>>\n\t*/\n\tMacro.add(['cycle', 'listbox'], {\n\t\tisAsync  : true,\n\t\tskipArgs : ['optionsfrom'],\n\t\ttags     : ['option', 'optionsfrom'],\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no variable name specified');\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst len   = this.payload.length;\n\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no options specified');\n\t\t\t}\n\n\t\t\tconst autoselect = this.args.length > 1 && this.args[1] === 'autoselect';\n\t\t\tconst options    = [];\n\t\t\tconst tagCount   = { option : 0, optionsfrom : 0 };\n\t\t\tlet selectedIdx = -1;\n\n\t\t\t// Get the options and selected index, if any.\n\t\t\tfor (let i = 1; i < len; ++i) {\n\t\t\t\tconst payload = this.payload[i];\n\n\t\t\t\t// <<option label value [selected]>>\n\t\t\t\tif (payload.name === 'option') {\n\t\t\t\t\t++tagCount.option;\n\n\t\t\t\t\tif (payload.args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no arguments specified for <<${payload.name}>> (#${tagCount.option})`);\n\t\t\t\t\t}\n\n\t\t\t\t\toptions.push({\n\t\t\t\t\t\tlabel : String(payload.args[0]),\n\t\t\t\t\t\tvalue : payload.args.length === 1 ? payload.args[0] : payload.args[1]\n\t\t\t\t\t});\n\n\t\t\t\t\tif (payload.args.length > 2 && payload.args[2] === 'selected') {\n\t\t\t\t\t\tif (autoselect) {\n\t\t\t\t\t\t\treturn this.error('cannot specify both the autoselect and selected keywords');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (selectedIdx !== -1) {\n\t\t\t\t\t\t\treturn this.error(`multiple selected keywords specified for <<${payload.name}>> (#${selectedIdx + 1} & #${tagCount.option})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tselectedIdx = options.length - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// <<optionsfrom expression>>\n\t\t\t\telse {\n\t\t\t\t\t++tagCount.optionsfrom;\n\n\t\t\t\t\tif (payload.args.full.length === 0) {\n\t\t\t\t\t\treturn this.error(`no expression specified for <<${payload.name}>> (#${tagCount.optionsfrom})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tconst exp = payload.args.full;\n\t\t\t\t\t\tresult = Scripting.evalJavaScript(exp[0] === '{' ? `(${exp})` : exp);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (typeof result !== 'object' || result === null) {\n\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (type: ${result === null ? 'null' : typeof result})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result instanceof Array || result instanceof Set) {\n\t\t\t\t\t\tresult.forEach(val => options.push({ label : String(val), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse if (result instanceof Map) {\n\t\t\t\t\t\tresult.forEach((val, key) => options.push({ label : String(key), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst oType = Util.toStringTag(result);\n\n\t\t\t\t\t\tif (oType !== 'Object') {\n\t\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (object type: ${oType})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tObject.keys(result).forEach(key => options.push({ label : key, value : result[key] }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No options were selected by the user, so we must select one.\n\t\t\tif (selectedIdx === -1) {\n\t\t\t\t// Attempt to automatically select an option by matching the variable's current value.\n\t\t\t\tif (autoselect) {\n\t\t\t\t\t// NOTE: This will usually fail for objects due to a variety of reasons.\n\t\t\t\t\tconst sameValueZero = Util.sameValueZero;\n\t\t\t\t\tconst curValue      = State.getVar(varName);\n\t\t\t\t\tconst curValueIdx   = options.findIndex(opt => sameValueZero(opt.value, curValue));\n\t\t\t\t\tselectedIdx = curValueIdx === -1 ? 0 : curValueIdx;\n\t\t\t\t}\n\n\t\t\t\t// Simply select the first option.\n\t\t\t\telse {\n\t\t\t\t\tselectedIdx = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set up and append the appropriate element to the output buffer.\n\t\t\tif (this.name === 'cycle') {\n\t\t\t\tlet cycleIdx = selectedIdx;\n\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t.wikiWithOptions({ profile : 'core' }, options[selectedIdx].label)\n\t\t\t\t\t.attr('id', `${this.name}-${varId}`)\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () {\n\t\t\t\t\t\tcycleIdx = (cycleIdx + 1) % options.length;\n\t\t\t\t\t\t$(this).empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label);\n\t\t\t\t\t\tState.setVar(varName, options[cycleIdx].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse { // this.name === 'listbox'\n\t\t\t\tconst $select = jQuery(document.createElement('select'));\n\n\t\t\t\toptions.forEach((opt, i) => {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(opt.label)\n\t\t\t\t\t\t.appendTo($select);\n\t\t\t\t});\n\n\t\t\t\t$select\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t\t})\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.val(selectedIdx)\n\t\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\t\tState.setVar(varName, options[Number(this.value)].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\n\t\t\t// Set the variable to the appropriate value, as requested.\n\t\t\tState.setVar(varName, options[selectedIdx].value);\n\t\t}\n\t});\n\n\t/*\n\t\t<<linkappend>>, <<linkprepend>>, & <<linkreplace>>\n\t*/\n\tMacro.add(['linkappend', 'linkprepend', 'linkreplace'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no link text specified');\n\t\t\t}\n\n\t\t\tconst $link      = jQuery(document.createElement('a'));\n\t\t\tconst $insert    = jQuery(document.createElement('span'));\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\n\t\t\t$link\n\t\t\t\t.wikiWithOptions({ profile : 'core' }, this.args[0])\n\t\t\t\t.addClass(`link-internal macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : true\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tif (this.name === 'linkreplace') {\n\t\t\t\t\t\t\t$link.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$link\n\t\t\t\t\t\t\t\t.wrap(`<span class=\"macro-${this.name}\"></span>`)\n\t\t\t\t\t\t\t\t.replaceWith(() => $link.html());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\t\t\t\t\t\t\t$insert.append(frag);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (transition) {\n\t\t\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t$insert.addClass(`macro-${this.name}-insert`);\n\n\t\t\tif (transition) {\n\t\t\t\t$insert.addClass(`macro-${this.name}-in`);\n\t\t\t}\n\n\t\t\tif (this.name === 'linkprepend') {\n\t\t\t\t$insert.insertBefore($link);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$insert.insertAfter($link);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<numberbox>> & <<textbox>>\n\t*/\n\tMacro.add(['numberbox', 'textbox'], {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst asNumber     = this.name === 'numberbox';\n\t\t\tconst defaultValue = asNumber ? Number(this.args[1]) : this.args[1];\n\n\t\t\tif (asNumber && Number.isNaN(defaultValue)) {\n\t\t\t\treturn this.error(`default value \"${this.args[1]}\" is neither a number nor can it be parsed into a number`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst el    = document.createElement('input');\n\t\t\tlet autofocus = false;\n\t\t\tlet passage;\n\n\t\t\tif (this.args.length > 3) {\n\t\t\t\tpassage   = this.args[2];\n\t\t\t\tautofocus = this.args[3] === 'autofocus';\n\t\t\t}\n\t\t\telse if (this.args.length > 2) {\n\t\t\t\tif (this.args[2] === 'autofocus') {\n\t\t\t\t\tautofocus = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpassage = this.args[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (typeof passage === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = passage.link;\n\t\t\t}\n\n\t\t\t// Set up and append the input element to the output buffer.\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : asNumber ? 'number' : 'text',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\t\t\t\t}))\n\t\t\t\t.on('keypress.macros', this.createShadowWrapper(function (ev) {\n\t\t\t\t\t// If Return/Enter is pressed, set the variable and, optionally, forward to another passage.\n\t\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\n\t\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Set the variable and input element to the default value.\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\tel.value = defaultValue;\n\n\t\t\t// Autofocus the input element, if requested.\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<radiobutton>>\n\t*/\n\tMacro.add('radiobutton', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId      = Util.slugify(varName);\n\t\t\tconst checkValue = this.args[1];\n\t\t\tconst el         = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and initialize the group counter.\n\t\t\t*/\n\t\t\tif (!TempState.hasOwnProperty(this.name)) {\n\t\t\t\tTempState[this.name] = {};\n\t\t\t}\n\n\t\t\tif (!TempState[this.name].hasOwnProperty(varId)) {\n\t\t\t\tTempState[this.name][varId] = 0;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}-${TempState[this.name][varId]++}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'radio',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tif (this.checked) {\n\t\t\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable to the checked value and the input element to checked, if requested.\n\t\t\t*/\n\t\t\tswitch (this.args[2]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<textarea>>\n\t*/\n\tMacro.add('textarea', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst defaultValue = this.args[1];\n\t\t\tconst autofocus    = this.args[2] === 'autofocus';\n\t\t\tconst el           = document.createElement('textarea');\n\n\t\t\t/*\n\t\t\t\tSet up and append the textarea element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\trows     : 4,\n\t\t\t\t\t// cols     : 68, // instead of setting \"cols\" we set the `min-width` in CSS\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.value);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and textarea element to the default value.\n\t\t\t*/\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\t// Ideally, we should be setting `.defaultValue` here, but IE doesn't support it,\n\t\t\t// so we have to use `.textContent`, which is equivalent.\n\t\t\tel.textContent = defaultValue;\n\n\t\t\t/*\n\t\t\t\tAutofocus the textarea element, if requested.\n\t\t\t*/\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<click>>\n\t*/\n\tMacro.add('click', 'link'); // add <<click>> as an alias of <<link>>\n\n\n\t/*******************************************************************************************************************\n\t\tLinks Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<actions>>\n\t*/\n\tMacro.add('actions', {\n\t\thandler() {\n\t\t\tconst $list = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass(this.name)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\tfor (let i = 0; i < this.args.length; ++i) {\n\t\t\t\tlet passage;\n\t\t\t\tlet text;\n\t\t\t\tlet $image;\n\t\t\t\tlet setFn;\n\n\t\t\t\tif (typeof this.args[i] === 'object') {\n\t\t\t\t\tif (this.args[i].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[i].source);\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[i].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[i].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[i].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[i].text;\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[i];\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t   State.variables.hasOwnProperty('#actions')\n\t\t\t\t\t&& State.variables['#actions'].hasOwnProperty(passage)\n\t\t\t\t\t&& State.variables['#actions'][passage]\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tjQuery(Wikifier.createInternalLink(\n\t\t\t\t\tjQuery(document.createElement('li')).appendTo($list),\n\t\t\t\t\tpassage,\n\t\t\t\t\tnull,\n\t\t\t\t\t((passage, fn) => () => {\n\t\t\t\t\t\tif (!State.variables.hasOwnProperty('#actions')) {\n\t\t\t\t\t\t\tState.variables['#actions'] = {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tState.variables['#actions'][passage] = true;\n\n\t\t\t\t\t\tif (typeof fn === 'function') {\n\t\t\t\t\t\t\tfn();\n\t\t\t\t\t\t}\n\t\t\t\t\t})(passage, setFn)\n\t\t\t\t))\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.append($image || document.createTextNode(text));\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<back>> & <<return>>\n\t*/\n\tMacro.add(['back', 'return'], {\n\t\thandler() {\n\t\t\t/* legacy */\n\t\t\tif (this.args.length > 1) {\n\t\t\t\treturn this.error('too many arguments specified, check the documentation for details');\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\tlet momentIndex = -1;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('link')) {\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\tif (this.args[0].count === 1) {\n\t\t\t\t\t\t\t// Simple link syntax: `[[...]]`.\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Pretty link syntax: `[[...|...]]`.\n\t\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (this.args.length === 1) {\n\t\t\t\t\t// Argument was simply the link text.\n\t\t\t\t\ttext = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\t/*\n\t\t\t\t\tFind the index and title of the most recent moment whose title does not match\n\t\t\t\t\tthat of the active (present) moment's.\n\t\t\t\t*/\n\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\tif (State.history[i].title !== State.passage) {\n\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\tpassage = State.history[i].title;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we failed to find a passage and we're `<<return>>`, fallback to `State.expired`.\n\t\t\t\tif (passage == null && this.name === 'return') { // lazy equality for null\n\t\t\t\t\tfor (let i = State.expired.length - 1; i >= 0; --i) {\n\t\t\t\t\t\tif (State.expired[i] !== State.passage) {\n\t\t\t\t\t\t\tpassage = State.expired[i];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tif (this.name === 'back') {\n\t\t\t\t\t/*\n\t\t\t\t\t\tFind the index of the most recent moment whose title matches that of the\n\t\t\t\t\t\tspecified passage.\n\t\t\t\t\t*/\n\t\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\t\tif (State.history[i].title === passage) {\n\t\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (momentIndex === -1) {\n\t\t\t\t\t\treturn this.error(`cannot find passage \"${passage}\" in the current story history`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn this.error('cannot find passage');\n\t\t\t}\n\n\t\t\t// if (this.name === \"back\" && momentIndex === -1) {\n\t\t\t// \t// no-op; we're already at the first passage in the current story history\n\t\t\t// \treturn;\n\t\t\t// }\n\n\t\t\tlet $el;\n\n\t\t\tif (this.name !== 'back' || momentIndex !== -1) {\n\t\t\t\t$el = jQuery(document.createElement('a'))\n\t\t\t\t\t.addClass('link-internal')\n\t\t\t\t\t.ariaClick(\n\t\t\t\t\t\t{ one : true },\n\t\t\t\t\t\tthis.name === 'return'\n\t\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t\t: () => Engine.goTo(momentIndex)\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('link-disabled');\n\t\t\t}\n\n\t\t\t$el\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text || L10n.get(`macro${this.name.toUpperFirst()}Text`)))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<choice>>\n\t*/\n\tMacro.add('choice', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tconst choiceId = State.passage;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\t\t\tlet setFn;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// NOTE: The arguments here are backwards.\n\t\t\t\tpassage = this.args[0];\n\t\t\t\ttext    = this.args[1];\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t   State.variables.hasOwnProperty('#choice')\n\t\t\t\t&& State.variables['#choice'].hasOwnProperty(choiceId)\n\t\t\t\t&& State.variables['#choice'][choiceId]\n\t\t\t) {\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass(`link-disabled macro-${this.name}`)\n\t\t\t\t\t.attr('tabindex', -1)\n\t\t\t\t\t.append($image || document.createTextNode(text))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(Wikifier.createInternalLink(this.output, passage, null, () => {\n\t\t\t\tif (!State.variables.hasOwnProperty('#choice')) {\n\t\t\t\t\tState.variables['#choice'] = {};\n\t\t\t\t}\n\n\t\t\t\tState.variables['#choice'][choiceId] = true;\n\n\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\tsetFn();\n\t\t\t\t}\n\t\t\t}))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text));\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDOM Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<addclass>> & <<toggleclass>>\n\t*/\n\tMacro.add(['addclass', 'toggleclass'], {\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('selector'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('class names'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tswitch (this.name) {\n\t\t\tcase 'addclass':\n\t\t\t\t$targets.addClass(this.args[1].trim());\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggleclass':\n\t\t\t\t$targets.toggleClass(this.args[1].trim());\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<removeclass>>\n\t*/\n\tMacro.add('removeclass', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.args.length > 1) {\n\t\t\t\t$targets.removeClass(this.args[1].trim());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$targets.removeClass();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<copy>>\n\t*/\n\tMacro.add('copy', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tjQuery(this.output).append($targets.html());\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<append>>, <<prepend>>, & <<replace>>\n\t*/\n\tMacro.add(['append', 'prepend', 'replace'], {\n\t\ttags  : null,\n\t\tt8nRe : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\t\tlet $insert;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$insert = jQuery(document.createElement('span'));\n\t\t\t\t\t$insert.addClass(`macro-${this.name}-insert macro-${this.name}-in`);\n\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$insert = jQuery(document.createDocumentFragment());\n\t\t\t\t}\n\n\t\t\t\t$insert.wiki(this.payload[0].contents);\n\n\t\t\t\tswitch (this.name) {\n\t\t\t\tcase 'replace':\n\t\t\t\t\t$targets.empty();\n\t\t\t\t\t/* falls through */\n\n\t\t\t\tcase 'append':\n\t\t\t\t\t$targets.append($insert);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'prepend':\n\t\t\t\t\t$targets.prepend($insert);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (this.name === 'replace') {\n\t\t\t\t$targets.empty();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remove>>\n\t*/\n\tMacro.add('remove', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\t$targets.remove();\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudio Macros.\n\t*******************************************************************************************************************/\n\tif (Has.audio) {\n\t\tconst errorOnePlaybackAction = (cur, prev) => `only one playback action allowed per invocation, \"${cur}\" cannot be combined with \"${prev}\"`;\n\n\t\t/*\n\t\t\t<<audio>>\n\t\t*/\n\t\tMacro.add('audio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track and/or group IDs'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tlet selected;\n\n\t\t\t\t// Process the track and/or group IDs.\n\t\t\t\ttry {\n\t\t\t\t\tselected = SimpleAudio.select(this.args[0]);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(1);\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet passage;\n\t\t\t\tlet time;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'time':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('time missing required seconds value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\ttime = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(time) || !Number.isFinite(time)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse time: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'goto':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('goto missing required passage title');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\n\t\t\t\t\t\tif (typeof raw === 'object') {\n\t\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\t\tpassage = raw.link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\t\t\tpassage = raw;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tselected.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (time != null) { // lazy equality for null\n\t\t\t\t\t\tselected.time(time);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tselected.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tselected.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\tconst nsEnded = `ended.macros.macro-${this.name}_goto`;\n\t\t\t\t\t\tselected\n\t\t\t\t\t\t\t.off(nsEnded)\n\t\t\t\t\t\t\t.one(nsEnded, () => {\n\t\t\t\t\t\t\t\tselected.off(nsEnded);\n\t\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tselected.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tselected.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tselected.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tselected.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tselected.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tselected.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<cacheaudio track_id source_list>>\n\t\t*/\n\t\tMacro.add('cacheaudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track ID'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('sources'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tconst id       = String(this.args[0]).trim();\n\t\t\t\tconst oldFmtRe = /^format:\\s*([\\w-]+)\\s*;\\s*/i;\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.tracks.add(id, this.args.slice(1).map(source => {\n\t\t\t\t\t\t/* legacy */\n\t\t\t\t\t\t// Transform an old format specifier into the new style.\n\t\t\t\t\t\tif (oldFmtRe.test(source)) {\n\t\t\t\t\t\t\t// If in Test Mode, return an error.\n\t\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\t\treturn this.error(`track ID \"${id}\": format specifier migration required, \"format:formatId;\" \\u2192 \"formatId|\"`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsource = source.replace(oldFmtRe, '$1|'); // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn source;\n\t\t\t\t\t\t/* /legacy */\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\tif (Config.debug && !SimpleAudio.tracks.get(id).hasSource()) {\n\t\t\t\t\treturn this.error(`track ID \"${id}\": no supported audio sources found`);\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createaudiogroup group_id>>\n\t\t\t\t<<track track_id>>\n\t\t\t\t…\n\t\t\t<</createaudiogroup>>\n\t\t*/\n\t\tMacro.add('createaudiogroup', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst groupId  = String(this.args[0]).trim();\n\t\t\t\tconst trackIds = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length < 1) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackIds.push(String(this.payload[i].args[0]).trim());\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.groups.add(groupId, trackIds);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createplaylist list_id>>\n\t\t\t\t<<track track_id action_list>>\n\t\t\t\t…\n\t\t\t<</createplaylist>>\n\t\t*/\n\t\tMacro.add('createplaylist', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'createplaylist') {\n\t\t\t\t\treturn this.error('a playlist has already been defined with <<setplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst listId    = String(this.args[0]).trim();\n\t\t\t\tconst trackObjs = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst trackObj = { id : String(this.payload[i].args[0]).trim() };\n\t\t\t\t\tconst args     = this.payload[i].args.slice(1);\n\n\t\t\t\t\t// Process arguments.\n\t\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\t\tlet raw;\n\t\t\t\t\t\tlet parsed;\n\n\t\t\t\t\t\tswitch (arg) {\n\t\t\t\t\t\tcase 'copy': // [DEPRECATED]\n\t\t\t\t\t\tcase 'own':\n\t\t\t\t\t\t\ttrackObj.own = true;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'rate':\n\t\t\t\t\t\t\t// if (args.length === 0) {\n\t\t\t\t\t\t\t// \treturn this.error('rate missing required speed value');\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// raw = args.shift();\n\t\t\t\t\t\t\t// parsed = Number.parseFloat(raw);\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// if (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t// \treturn this.error(`cannot parse rate: ${raw}`);\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// trackObj.rate = parsed;\n\t\t\t\t\t\t\tif (args.length > 0) {\n\t\t\t\t\t\t\t\targs.shift();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\t\tparsed = Number.parseFloat(raw);\n\n\t\t\t\t\t\t\tif (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttrackObj.volume = parsed;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackObjs.push(trackObj);\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add(listId, trackObjs);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'createplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<masteraudio action_list>>\n\t\t*/\n\t\tMacro.add('masteraudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(0);\n\t\t\t\tlet action;\n\t\t\t\tlet mute;\n\t\t\t\tlet muteOnHide;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'muteonhide':\n\t\t\t\t\tcase 'nomuteonhide':\n\t\t\t\t\t\tmuteOnHide = arg === 'muteonhide';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (muteOnHide != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.muteOnHidden(muteOnHide);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tSimpleAudio.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tSimpleAudio.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tSimpleAudio.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<playlist list_id action_list>>  ← <<createplaylist>> syntax\n\t\t\t<<playlist action_list>>          ← <<setplaylist>> syntax\n\t\t*/\n\t\tMacro.add('playlist', {\n\t\t\tfrom : null,\n\n\t\t\thandler() {\n\t\t\t\tconst from = this.self.from;\n\n\t\t\t\tif (from === null) {\n\t\t\t\t\treturn this.error('no playlists have been created');\n\t\t\t\t}\n\n\t\t\t\tlet list;\n\t\t\t\tlet args;\n\n\t\t\t\tif (from === 'createplaylist') {\n\t\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\tif (this.args.length < 1) { errors.push('list ID'); }\n\t\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get(id);\n\t\t\t\t\targs = this.args.slice(1);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get('setplaylist');\n\t\t\t\t\targs = this.args.slice(0);\n\t\t\t\t}\n\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet shuffle;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'shuffle':\n\t\t\t\t\tcase 'unshuffle':\n\t\t\t\t\t\tshuffle = arg === 'shuffle';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tlist.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tlist.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tlist.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (shuffle != null) { // lazy equality for null\n\t\t\t\t\t\tlist.shuffle(shuffle);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tlist.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tlist.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tlist.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tlist.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\t\tlist.skip();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tlist.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tlist.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeaudiogroup group_id>>\n\t\t*/\n\t\tMacro.add('removeaudiogroup', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.groups.has(id)) {\n\t\t\t\t\treturn this.error(`group \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.groups.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeplaylist list_id>>\n\t\t*/\n\t\tMacro.add('removeplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.lists.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<waitforaudio>>\n\t\t*/\n\t\tMacro.add('waitforaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.loadWithScreen();\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<setplaylist track_id_list>>\n\t\t*/\n\t\tMacro.add('setplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no track ID(s) specified');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'setplaylist') {\n\t\t\t\t\treturn this.error('playlists have already been defined with <<createplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Create the new playlist.\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add('setplaylist', this.args.slice(0));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'setplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<stopallaudio>>\n\t\t*/\n\t\tMacro.add('stopallaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.select(':all').stop();\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\telse {\n\t\t/* The HTML5 <audio> API appears to be missing or disabled, set up no-op macros. */\n\t\tMacro.add([\n\t\t\t'audio',\n\t\t\t'cacheaudio',\n\t\t\t'createaudiogroup',\n\t\t\t'createplaylist',\n\t\t\t'masteraudio',\n\t\t\t'playlist',\n\t\t\t'removeaudiogroup',\n\t\t\t'removeplaylist',\n\t\t\t'waitforaudio',\n\n\t\t\t// Deprecated.\n\t\t\t'setplaylist',\n\t\t\t'stopallaudio'\n\t\t], {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\t/* no-op */\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMiscellaneous Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<goto>>\n\t*/\n\tMacro.add('goto', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCall `Engine.play()` asynchronously.\n\n\t\t\t\tNOTE: This does not terminate the current Wikifier call chain,\n\t\t\t\tthough, ideally, it should.  Doing so would not be trivial, however,\n\t\t\t\tand there's also the question of whether that behavior would be\n\t\t\t\tunwanted by users, who are used to the current behavior from\n\t\t\t\tsimilar macros and constructs.\n\t\t\t*/\n\t\t\tsetTimeout(() => Engine.play(passage), Engine.minDomActionDelay);\n\t\t}\n\t});\n\n\t/*\n\t\t<<repeat>> & <<stop>>\n\t*/\n\tMacro.add('repeat', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified');\n\t\t\t}\n\n\t\t\tlet delay;\n\n\t\t\ttry {\n\t\t\t\tdelay = Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0]));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerInterval(this.createShadowWrapper(() => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-repeat-insert macro-repeat-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-repeat-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), delay);\n\t\t},\n\n\t\tregisterInterval(callback, delay) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\tconst turnId = State.turns;\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId = null;\n\n\t\t\t// Set up the interval.\n\t\t\ttimerId = setInterval(() => {\n\t\t\t\t// Terminate the timer if the turn IDs do not match.\n\t\t\t\tif (turnId !== State.turns) {\n\t\t\t\t\tclearInterval(timerId);\n\t\t\t\t\ttimers.delete(timerId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet timerIdCache;\n\t\t\t\t/*\n\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t`Wikifier` call.\n\t\t\t\t*/\n\t\t\t\ttry {\n\t\t\t\t\tTempState.break = null;\n\n\t\t\t\t\t// Set up the `repeatTimerId` value, caching the existing value, if necessary.\n\t\t\t\t\tif (TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\t\t\ttimerIdCache = TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.repeatTimerId = timerId;\n\n\t\t\t\t\t// Execute the callback.\n\t\t\t\t\tcallback.call(this);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\t// Teardown the `repeatTimerId` property, restoring the cached value, if necessary.\n\t\t\t\t\tif (typeof timerIdCache !== 'undefined') {\n\t\t\t\t\t\tTempState.repeatTimerId = timerIdCache;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.break = null;\n\t\t\t\t}\n\t\t\t}, delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#repeat-timers-cleanup')) {\n\t\t\t\tprehistory['#repeat-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearInterval(timerId));\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\tMacro.add('stop', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (!TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<repeat>>');\n\t\t\t}\n\n\t\t\tconst timers  = Macro.get('repeat').timers;\n\t\t\tconst timerId = TempState.repeatTimerId;\n\t\t\tclearInterval(timerId);\n\t\t\ttimers.delete(timerId);\n\t\t\tTempState.break = 2;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<timed>> & <<next>>\n\t*/\n\tMacro.add('timed', {\n\t\tisAsync : true,\n\t\ttags    : ['next'],\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified in <<timed>>');\n\t\t\t}\n\n\t\t\tconst items = [];\n\n\t\t\ttry {\n\t\t\t\titems.push({\n\t\t\t\t\tname    : this.name,\n\t\t\t\t\tsource  : this.source,\n\t\t\t\t\tdelay   : Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0])),\n\t\t\t\t\tcontent : this.payload[0].contents\n\t\t\t\t});\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`${ex.message} in <<timed>>`);\n\t\t\t}\n\n\t\t\tif (this.payload.length > 1) {\n\t\t\t\tlet i;\n\n\t\t\t\ttry {\n\t\t\t\t\tlet len;\n\n\t\t\t\t\tfor (i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tname   : this.payload[i].name,\n\t\t\t\t\t\t\tsource : this.payload[i].source,\n\t\t\t\t\t\t\tdelay  : this.payload[i].args.length === 0\n\t\t\t\t\t\t\t\t? items[items.length - 1].delay\n\t\t\t\t\t\t\t\t: Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.payload[i].args[0])),\n\t\t\t\t\t\t\tcontent : this.payload[i].contents\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`${ex.message} in <<next>> (#${i})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerTimeout(this.createShadowWrapper(item => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, item.content);\n\n\t\t\t\t// Output.\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\t// Custom debug view setup for `<<next>>`.\n\t\t\t\tif (Config.debug && item.name === 'next') {\n\t\t\t\t\t$output = jQuery(new DebugView( // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t$output[0],\n\t\t\t\t\t\t'macro',\n\t\t\t\t\t\titem.name,\n\t\t\t\t\t\titem.source\n\t\t\t\t\t).output);\n\t\t\t\t}\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-timed-insert macro-timed-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-timed-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), items);\n\t\t},\n\n\t\tregisterTimeout(callback, items) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\tconst turnId = State.turns;\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId  = null;\n\t\t\tlet nextItem = items.shift();\n\n\t\t\tconst worker = function () {\n\t\t\t\t// Bookkeeping.\n\t\t\t\ttimers.delete(timerId);\n\n\t\t\t\tif (turnId !== State.turns) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the current item and set up the next worker, if any.\n\t\t\t\tconst curItem = nextItem;\n\n\t\t\t\tif ((nextItem = items.shift()) != null) { // lazy equality for null\n\t\t\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\t\t\ttimers.add(timerId);\n\t\t\t\t}\n\n\t\t\t\t// Execute the callback.\n\t\t\t\tcallback.call(this, curItem);\n\t\t\t};\n\n\t\t\t// Setup the timeout.\n\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#timed-timers-cleanup')) {\n\t\t\t\tprehistory['#timed-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearTimeout(timerId)); // eslint-disable-line no-shadow\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<widget>>\n\t*/\n\tMacro.add('widget', {\n\t\ttags : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no widget name specified');\n\t\t\t}\n\n\t\t\tconst widgetName = this.args[0];\n\n\t\t\tif (Macro.has(widgetName)) {\n\t\t\t\tif (!Macro.get(widgetName).isWidget) {\n\t\t\t\t\treturn this.error(`cannot clobber existing macro \"${widgetName}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Delete the existing widget.\n\t\t\t\tMacro.delete(widgetName);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMacro.add(widgetName, {\n\t\t\t\t\tisWidget : true,\n\t\t\t\t\thandler  : (function (contents) {\n\t\t\t\t\t\treturn function () {\n\t\t\t\t\t\t\tlet argsCache;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Cache the existing value of the `$args` variable, if necessary.\n\t\t\t\t\t\t\t\tif (State.variables.hasOwnProperty('args')) {\n\t\t\t\t\t\t\t\t\targsCache = State.variables.args;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set up the widget `$args` variable and add a shadow.\n\t\t\t\t\t\t\t\tState.variables.args = [...this.args];\n\t\t\t\t\t\t\t\tState.variables.args.raw = this.args.raw;\n\t\t\t\t\t\t\t\tState.variables.args.full = this.args.full;\n\t\t\t\t\t\t\t\tthis.addShadow('$args');\n\n\t\t\t\t\t\t\t\t// Set up the error trapping variables.\n\t\t\t\t\t\t\t\tconst resFrag = document.createDocumentFragment();\n\t\t\t\t\t\t\t\tconst errList = [];\n\n\t\t\t\t\t\t\t\t// Wikify the widget contents.\n\t\t\t\t\t\t\t\tnew Wikifier(resFrag, contents);\n\n\t\t\t\t\t\t\t\t// Carry over the output, unless there were errors.\n\t\t\t\t\t\t\t\tArray.from(resFrag.querySelectorAll('.error')).forEach(errEl => {\n\t\t\t\t\t\t\t\t\terrList.push(errEl.textContent);\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tif (errList.length === 0) {\n\t\t\t\t\t\t\t\t\tthis.output.appendChild(resFrag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\treturn this.error(`error${errList.length > 1 ? 's' : ''} within widget contents (${errList.join('; ')})`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot execute widget: ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t// Revert the `$args` variable shadowing.\n\t\t\t\t\t\t\t\tif (typeof argsCache !== 'undefined') {\n\t\t\t\t\t\t\t\t\tState.variables.args = argsCache;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete State.variables.args;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t})(this.payload[0].contents)\n\t\t\t\t});\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`cannot create widget macro \"${widgetName}\": ${ex.message}`);\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tdialog.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, L10n, safeActiveElement */\n\nvar Dialog = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Dialog element caches.\n\tlet _$overlay       = null;\n\tlet _$dialog        = null;\n\tlet _$dialogTitle   = null;\n\tlet _$dialogBody    = null;\n\n\t// The last active/focused non-dialog element.\n\tlet _lastActive     = null;\n\n\t// The width of the browser's scrollbars.\n\tlet _scrollbarWidth = 0;\n\n\t// Dialog mutation resize handler.\n\tlet _dialogObserver = null;\n\n\n\t/*******************************************************************************\n\t\tDialog Functions.\n\t*******************************************************************************/\n\n\t/*\n\t\t[DEPRECATED] Adds a click hander to the target element(s) which opens the dialog modal.\n\t*/\n\tfunction dialogAddClickHandler(targets, options, startFn, doneFn, closeFn) {\n\t\treturn jQuery(targets).ariaClick(ev => {\n\t\t\tev.preventDefault();\n\n\t\t\t// Call the start function.\n\t\t\tif (typeof startFn === 'function') {\n\t\t\t\tstartFn(ev);\n\t\t\t}\n\n\t\t\t// Open the dialog.\n\t\t\tdialogOpen(options, closeFn);\n\n\t\t\t// Call the done function.\n\t\t\tif (typeof doneFn === 'function') {\n\t\t\t\tdoneFn(ev);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction dialogBodyAppend(...args) {\n\t\t_$dialogBody.append(...args);\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogBody() {\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogClose(ev) {\n\t\t// Trigger a `:dialogclosing` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogclosing');\n\n\t\t// Largely reverse the actions taken in `dialogOpen()`.\n\t\tjQuery(document)\n\t\t\t.off('.dialog-close');\n\t\tif (_dialogObserver) {\n\t\t\t_dialogObserver.disconnect();\n\t\t\t_dialogObserver = null;\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.off('.dialog-resize');\n\t\t}\n\t\tjQuery(window)\n\t\t\t.off('.dialog-resize');\n\t\t_$dialog\n\t\t\t.removeClass('open')\n\t\t\t.css({ left : '', right : '', top : '', bottom : '' });\n\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex=-2]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.attr('tabindex', 0);\n\t\tjQuery('body>[tabindex=-3]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.removeAttr('tabindex');\n\n\t\t_$overlay\n\t\t\t.removeClass('open');\n\t\tjQuery(document.documentElement)\n\t\t\t.removeAttr('data-dialog');\n\n\t\t// Clear the dialog's content.\n\t\t_$dialogTitle\n\t\t\t.empty();\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\t// Attempt to restore focus to whichever element had it prior to opening the dialog.\n\t\tif (_lastActive !== null) {\n\t\t\tjQuery(_lastActive).focus();\n\t\t\t_lastActive = null;\n\t\t}\n\n\t\t// Call the given \"on close\" callback function, if any.\n\t\tif (ev && ev.data && typeof ev.data.closeFn === 'function') {\n\t\t\tev.data.closeFn(ev);\n\t\t}\n\n\t\t// Trigger a `:dialogclosed` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogclose');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogclosed');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogInit() {\n\t\tif (DEBUG) { console.log('[Dialog/dialogInit()]'); }\n\n\t\tif (document.getElementById('ui-dialog')) {\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t\tCalculate and cache the width of scrollbars.\n\t\t*/\n\t\t_scrollbarWidth = (() => {\n\t\t\tlet scrollbarWidth;\n\n\t\t\ttry {\n\t\t\t\tconst inner = document.createElement('p');\n\t\t\t\tconst outer = document.createElement('div');\n\n\t\t\t\tinner.style.width      = '100%';\n\t\t\t\tinner.style.height     = '200px';\n\t\t\t\touter.style.position   = 'absolute';\n\t\t\t\touter.style.left       = '0px';\n\t\t\t\touter.style.top        = '0px';\n\t\t\t\touter.style.width      = '100px';\n\t\t\t\touter.style.height     = '100px';\n\t\t\t\touter.style.visibility = 'hidden';\n\t\t\t\touter.style.overflow   = 'hidden';\n\n\t\t\t\touter.appendChild(inner);\n\t\t\t\tdocument.body.appendChild(outer);\n\n\t\t\t\tconst w1 = inner.offsetWidth;\n\t\t\t\t/*\n\t\t\t\t\tThe `overflow: scroll` style property value does not work consistently\n\t\t\t\t\twith scrollbars which are styled with `::-webkit-scrollbar`, so we use\n\t\t\t\t\t`overflow: auto` with dimensions guaranteed to force a scrollbar.\n\t\t\t\t*/\n\t\t\t\touter.style.overflow = 'auto';\n\t\t\t\tlet w2 = inner.offsetWidth;\n\n\t\t\t\tif (w1 === w2) {\n\t\t\t\t\tw2 = outer.clientWidth;\n\t\t\t\t}\n\n\t\t\t\tdocument.body.removeChild(outer);\n\n\t\t\t\tscrollbarWidth = w1 - w2;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn scrollbarWidth || 17; // 17px is a reasonable failover\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate the dialog elements.\n\t\t*/\n\t\tconst $elems = jQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"ui-overlay\" class=\"ui-close\"></div>'\n\t\t\t\t+ '<div id=\"ui-dialog\" tabindex=\"0\" role=\"dialog\" aria-labelledby=\"ui-dialog-title\">'\n\t\t\t\t+     '<div id=\"ui-dialog-titlebar\">'\n\t\t\t\t+         '<h1 id=\"ui-dialog-title\"></h1>'\n\t\t\t\t+         `<button id=\"ui-dialog-close\" class=\"ui-close\" tabindex=\"0\" aria-label=\"${L10n.get('close')}\">\\uE804</button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div id=\"ui-dialog-body\"></div>'\n\t\t\t\t+ '</div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t/*\n\t\t\tCache the dialog elements, since they're going to be used often.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$overlay     = jQuery($elems.find('#ui-overlay').get(0));\n\t\t_$dialog      = jQuery($elems.find('#ui-dialog').get(0));\n\t\t_$dialogTitle = jQuery($elems.find('#ui-dialog-title').get(0));\n\t\t_$dialogBody  = jQuery($elems.find('#ui-dialog-body').get(0));\n\n\t\t/*\n\t\t\tInsert the dialog elements into the page before the main script.\n\t\t*/\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t}\n\n\tfunction dialogIsOpen(classNames) {\n\t\treturn _$dialog.hasClass('open')\n\t\t\t&& (classNames ? classNames.splitOrEmpty(/\\s+/).every(cn => _$dialogBody.hasClass(cn)) : true);\n\t}\n\n\tfunction dialogOpen(options, closeFn) {\n\t\t// Trigger a `:dialogopening` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogopening');\n\n\t\t// Grab the options we care about.\n\t\tconst { top } = jQuery.extend({ top : 50 }, options);\n\n\t\t// Record the last active/focused non-dialog element.\n\t\tif (!dialogIsOpen()) {\n\t\t\t_lastActive = safeActiveElement();\n\t\t}\n\n\t\t// Add the `data-dialog` attribute to <html> (mostly used to style <body>).\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-dialog', 'open');\n\n\t\t// Display the overlay.\n\t\t_$overlay\n\t\t\t.addClass('open');\n\n\t\t/*\n\t\t\tAdd the imagesLoaded handler to the dialog body, if necessary.\n\n\t\t\tNOTE: We use `querySelector()` here as jQuery has no simple way to\n\t\t\tcheck if, and only if, at least one element of the specified type\n\t\t\texists.  The best that jQuery offers is analogous to `querySelectorAll()`,\n\t\t\twhich enumerates all elements of the specified type.\n\t\t*/\n\t\tif (_$dialogBody[0].querySelector('img') !== null) {\n\t\t\t_$dialogBody\n\t\t\t\t.imagesLoaded()\n\t\t\t\t.always(() => _resizeHandler({ data : { top } }));\n\t\t}\n\n\t\t// Add `aria-hidden=true` to all direct non-dialog-children of <body> to\n\t\t// hide the underlying page from screen readers while the dialog is open.\n\t\tjQuery('body>:not(script,#store-area,tw-storydata,#ui-bar,#ui-overlay,#ui-dialog)')\n\t\t\t.attr('tabindex', -3)\n\t\t\t.attr('aria-hidden', true);\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex]:not([tabindex^=-])')\n\t\t\t.attr('tabindex', -2)\n\t\t\t.attr('aria-hidden', true);\n\n\t\t// Display the dialog.\n\t\t_$dialog\n\t\t\t.css(_calcPosition(top))\n\t\t\t.addClass('open')\n\t\t\t.focus();\n\n\t\t// Add the UI resize handler.\n\t\tjQuery(window)\n\t\t\t.on('resize.dialog-resize', null, { top }, jQuery.throttle(40, _resizeHandler));\n\n\t\t// Add the dialog mutation resize handler.\n\t\tif (Has.mutationObserver) {\n\t\t\t_dialogObserver = new MutationObserver(mutations => {\n\t\t\t\tfor (let i = 0; i < mutations.length; ++i) {\n\t\t\t\t\tif (mutations[i].type === 'childList') {\n\t\t\t\t\t\t_resizeHandler({ data : { top } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t_dialogObserver.observe(_$dialogBody[0], {\n\t\t\t\tchildList : true,\n\t\t\t\tsubtree   : true\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.on(\n\t\t\t\t\t'DOMNodeInserted.dialog-resize DOMNodeRemoved.dialog-resize',\n\t\t\t\t\tnull,\n\t\t\t\t\t{ top },\n\t\t\t\t\tjQuery.throttle(40, _resizeHandler)\n\t\t\t\t);\n\t\t}\n\n\t\t// Set up the delegated UI close handler.\n\t\tjQuery(document)\n\t\t\t.one('click.dialog-close', '.ui-close', { closeFn }, ev => {\n\t\t\t\t// NOTE: Do not allow this event handler to return the `Dialog` static object,\n\t\t\t\t// as doing so causes Edge (ca. 18) to throw a \"Number expected\" exception due\n\t\t\t\t// to `Dialog` not having a prototype.\n\t\t\t\tdialogClose(ev);\n\t\t\t\t/* implicit `return undefined;` */\n\t\t\t})\n\t\t\t.one('keypress.dialog-close', '.ui-close', function (ev) {\n\t\t\t\t// 13 is Enter/Return, 32 is Space.\n\t\t\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\t\t\tjQuery(this).trigger('click');\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Trigger a `:dialogopened` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogopen');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogopened');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogResize(data) {\n\t\treturn _resizeHandler(typeof data === 'object' ? { data } : undefined);\n\t}\n\n\tfunction dialogSetup(title, classNames) {\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\tif (classNames != null) { // lazy equality for null\n\t\t\t_$dialogBody.addClass(classNames);\n\t\t}\n\n\t\t_$dialogTitle\n\t\t\t.empty()\n\t\t\t.append((title != null ? String(title) : '') || '\\u00A0'); // lazy equality for null\n\n\t\t// TODO: In v3 this should return `Dialog` for chaining.\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogBodyWiki(...args) {\n\t\t_$dialogBody.wiki(...args);\n\t\treturn Dialog;\n\t}\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _calcPosition(topPos) {\n\t\tconst top       = topPos != null ? topPos : 50; // lazy equality for null\n\t\tconst $parent   = jQuery(window);\n\t\tconst dialogPos = { left : '', right : '', top : '', bottom : '' };\n\n\t\t// Unset the dialog's positional properties before checking its dimensions.\n\t\t_$dialog.css(dialogPos);\n\n\t\tlet horzSpace = $parent.width() - _$dialog.outerWidth(true) - 1;   // -1 to address a Firefox issue\n\t\tlet vertSpace = $parent.height() - _$dialog.outerHeight(true) - 1; // -1 to address a Firefox issue\n\n\t\tif (horzSpace <= 32 + _scrollbarWidth) {\n\t\t\tvertSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (vertSpace <= 32 + _scrollbarWidth) {\n\t\t\thorzSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (horzSpace <= 32) {\n\t\t\tdialogPos.left = dialogPos.right = 16;\n\t\t}\n\t\telse {\n\t\t\tdialogPos.left = dialogPos.right = horzSpace / 2 >> 0;\n\t\t}\n\n\t\tif (vertSpace <= 32) {\n\t\t\tdialogPos.top = dialogPos.bottom = 16;\n\t\t}\n\t\telse {\n\t\t\tif (vertSpace / 2 > top) {\n\t\t\t\tdialogPos.top = top;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdialogPos.top = dialogPos.bottom = vertSpace / 2 >> 0;\n\t\t\t}\n\t\t}\n\n\t\tObject.keys(dialogPos).forEach(key => {\n\t\t\tif (dialogPos[key] !== '') {\n\t\t\t\tdialogPos[key] += 'px';\n\t\t\t}\n\t\t});\n\n\t\treturn dialogPos;\n\t}\n\n\tfunction _resizeHandler(ev) {\n\t\tconst top = ev && ev.data && typeof ev.data.top !== 'undefined' ? ev.data.top : 50;\n\n\t\tif (_$dialog.css('display') === 'block') {\n\t\t\t// Stow the dialog.\n\t\t\t_$dialog.css({ display : 'none' });\n\n\t\t\t// Restore the dialog with its new positional properties.\n\t\t\t_$dialog.css(jQuery.extend({ display : '' }, _calcPosition(top)));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tappend : { value : dialogBodyAppend },\n\t\tbody   : { value : dialogBody },\n\t\tclose  : { value : dialogClose },\n\t\tinit   : { value : dialogInit },\n\t\tisOpen : { value : dialogIsOpen },\n\t\topen   : { value : dialogOpen },\n\t\tresize : { value : dialogResize },\n\t\tsetup  : { value : dialogSetup },\n\t\twiki   : { value : dialogBodyWiki },\n\n\t\t// Legacy Functions.\n\t\taddClickHandler : { value : dialogAddClickHandler }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tengine.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Config, DebugView, Dialog, Has, LoadScreen, Save, State, Story, StyleWrapper, UI, UIBar, Util,\n\t       Wikifier, postdisplay, postrender, predisplay, prehistory, prerender, setDisplayTitle\n*/\n\nvar Engine = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Engine state types object (pseudo-enumeration).\n\tconst States = Util.toEnum({\n\t\tIdle      : 'idle',\n\t\tPlaying   : 'playing',\n\t\tRendering : 'rendering'\n\t});\n\n\t// Minimum delay for DOM actions (in milliseconds).\n\tconst minDomActionDelay = 40;\n\n\t// Current state of the engine (default: `Engine.States.Idle`).\n\tlet _state = States.Idle;\n\n\t// Last time `enginePlay()` was called (in milliseconds).\n\tlet _lastPlay = null;\n\n\t// Cache of the debug view for the StoryInit special passage.\n\tlet _storyInitDebugView = null;\n\n\t// Cache of the outline patching <style> element (`StyleWrapper`-wrapped).\n\tlet _outlinePatch = null;\n\n\t// List of objects describing `StoryInterface` elements to update via passages during navigation.\n\tlet _updating = null;\n\n\n\t/*******************************************************************************************************************\n\t\tEngine Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize the core story elements and perform some bookkeeping.\n\t*/\n\tfunction engineInit() {\n\t\tif (DEBUG) { console.log('[Engine/engineInit()]'); }\n\n\t\t/*\n\t\t\tRemove #init-no-js & #init-lacking from #init-screen.\n\t\t*/\n\t\tjQuery('#init-no-js,#init-lacking').remove();\n\n\t\t/*\n\t\t\tGenerate the core story elements and insert them into the page before the store area.\n\t\t*/\n\t\t(() => {\n\t\t\tconst $elems = jQuery(document.createDocumentFragment());\n\t\t\tconst markup = Story.has('StoryInterface') && Story.get('StoryInterface').text.trim();\n\n\t\t\tif (markup) {\n\t\t\t\t// Remove the UI bar, its styles, and events.\n\t\t\t\tUIBar.destroy();\n\n\t\t\t\t// Remove the core display area styles.\n\t\t\t\tjQuery(document.head).find('#style-core-display').remove();\n\n\t\t\t\t$elems.append(markup);\n\n\t\t\t\tif ($elems.find('#passages').length === 0) {\n\t\t\t\t\tthrow new Error('no element with ID \"passages\" found within \"StoryInterface\" special passage');\n\t\t\t\t}\n\n\t\t\t\tconst updating = [];\n\n\t\t\t\t$elems.find('[data-passage]').each((i, el) => {\n\t\t\t\t\tif (el.id === 'passages') {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} id=\"passages\"> must not contain a \"data-passage\" content attribute`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst passage = el.getAttribute('data-passage').trim();\n\n\t\t\t\t\tif (el.firstElementChild !== null) {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} data-passage=\"${passage}\"> contains child elements`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tupdating.push({\n\t\t\t\t\t\t\tpassage,\n\t\t\t\t\t\t\telement : el\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (updating.length > 0) {\n\t\t\t\t\t_updating = updating;\n\t\t\t\t}\n\n\t\t\t\tConfig.ui.updateStoryElements = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$elems.append('<div id=\"story\" role=\"main\"><div id=\"passages\"></div></div>');\n\t\t\t}\n\n\t\t\t// Insert the core UI elements into the page before the main script.\n\t\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate and cache the ARIA outlines <style> element (`StyleWrapper`-wrapped)\n\t\t\tand set up the handler to manipulate the outlines.\n\n\t\t\tIDEA: http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/\n\t\t*/\n\t\t_outlinePatch = new StyleWrapper((\n\t\t\t() => jQuery(document.createElement('style'))\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-aria-outlines',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t})\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.get(0) // return the <style> element itself\n\t\t)());\n\t\tlet _lastOutlineEvent;\n\t\tjQuery(document).on(\n\t\t\t'mousedown.aria-outlines keydown.aria-outlines',\n\t\t\tev => {\n\t\t\t\tif (ev.type !== _lastOutlineEvent) {\n\t\t\t\t\t_lastOutlineEvent = ev.type;\n\n\t\t\t\t\tif (ev.type === 'keydown') {\n\t\t\t\t\t\t_showOutlines();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t_hideOutlines();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\n\t/*\n\t\tStarts the story.\n\t*/\n\tfunction engineStart() {\n\t\tif (DEBUG) { console.log('[Engine/engineStart()]'); }\n\n\t\t/*\n\t\t\tExecute the StoryInit special passage.\n\t\t*/\n\t\tif (Story.has('StoryInit')) {\n\t\t\ttry {\n\t\t\t\tconst debugBuffer = Wikifier.wikifyEval(Story.get('StoryInit').text);\n\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tconst debugView = new DebugView(\n\t\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t\t'special',\n\t\t\t\t\t\t'StoryInit',\n\t\t\t\t\t\t'StoryInit'\n\t\t\t\t\t);\n\t\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\t\tdebugView.append(debugBuffer);\n\t\t\t\t\t_storyInitDebugView = debugView.output;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('StoryInit', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Sanity checks.\n\t\tif (Config.passages.start == null) { // lazy equality for null\n\t\t\tthrow new Error('starting passage not selected');\n\t\t}\n\t\tif (!Story.has(Config.passages.start)) {\n\t\t\tthrow new Error(`starting passage (\"${Config.passages.start}\") not found`);\n\t\t}\n\n\t\t// Focus the document element initially.\n\t\tjQuery(document.documentElement).focus();\n\n\t\t/*\n\t\t\tAttempt to restore an active session.  Failing that, attempt to autoload the autosave,\n\t\t\tif requested.  Failing that, display the starting passage.\n\t\t*/\n\t\tif (State.restore()) {\n\t\t\tengineShow();\n\t\t}\n\t\telse {\n\t\t\tlet loadStart = true;\n\n\t\t\tswitch (typeof Config.saves.autoload) {\n\t\t\tcase 'boolean':\n\t\t\t\tif (Config.saves.autoload && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tif (Config.saves.autoload === 'prompt' && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tloadStart = false;\n\t\t\t\t\tUI.buildAutoload();\n\t\t\t\t\tDialog.open();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'function':\n\t\t\t\tif (Save.autosave.ok() && Save.autosave.has() && !!Config.saves.autoload()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (loadStart) {\n\t\t\t\tif (DEBUG) { console.log(`\\tstarting passage: \"${Config.passages.start}\"`); }\n\n\t\t\t\tenginePlay(Config.passages.start);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t\tRestarts the story.\n\t*/\n\tfunction engineRestart() {\n\t\tif (DEBUG) { console.log('[Engine/engineRestart()]'); }\n\n\t\t/*\n\t\t\tShow the loading screen to hide any unsightly rendering shenanigans during the\n\t\t\tpage reload.\n\t\t*/\n\t\tLoadScreen.show();\n\n\t\t/*\n\t\t\tScroll the window to the top.\n\n\t\t\tThis is required by most browsers for the starting passage or it will remain at\n\t\t\twhatever its current scroll position is after the page reload.  We do it generally,\n\t\t\trather than only for the currently set starting passage, since the starting passage\n\t\t\tmay be dynamically manipulated.\n\t\t*/\n\t\twindow.scroll(0, 0);\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tState.reset();\n\n\t\t/*\n\t\t\tTrigger an ':enginerestart' event.\n\t\t*/\n\t\tjQuery.event.trigger(':enginerestart');\n\n\t\t/*\n\t\t\tReload the page.\n\t\t*/\n\t\twindow.location.reload();\n\t}\n\n\t/*\n\t\tReturns the current state of the engine.\n\t*/\n\tfunction engineState() {\n\t\treturn _state;\n\t}\n\n\t/*\n\t\tReturns whether the engine is idle.\n\t*/\n\tfunction engineIsIdle() {\n\t\treturn _state === States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is playing.\n\t*/\n\tfunction engineIsPlaying() {\n\t\treturn _state !== States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is rendering.\n\t*/\n\tfunction engineIsRendering() {\n\t\treturn _state === States.Rendering;\n\t}\n\n\t/*\n\t\tReturns a timestamp representing the last time `Engine.play()` was called.\n\t*/\n\tfunction engineLastPlay() {\n\t\treturn _lastPlay;\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the state history and show it.\n\t*/\n\tfunction engineGoTo(idx) {\n\t\tconst succeded = State.goTo(idx);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the state history\n\t\tand show it.\n\t*/\n\tfunction engineGo(offset) {\n\t\tconst succeded = State.go(offset);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tGo to the moment which directly precedes the active moment and show it.\n\t*/\n\tfunction engineBackward() {\n\t\treturn engineGo(-1);\n\t}\n\n\t/*\n\t\tGo to the moment which directly follows the active moment and show it.\n\t*/\n\tfunction engineForward() {\n\t\treturn engineGo(1);\n\t}\n\n\t/*\n\t\tRenders and displays the active (present) moment's associated passage without adding\n\t\ta new moment to the history.\n\t*/\n\tfunction engineShow() {\n\t\treturn enginePlay(State.passage, true);\n\t}\n\n\t/*\n\t\tRenders and displays the passage referenced by the given title, optionally without\n\t\tadding a new moment to the history.\n\t*/\n\tfunction enginePlay(title, noHistory) {\n\t\tif (DEBUG) { console.log(`[Engine/enginePlay(title: \"${title}\", noHistory: ${noHistory})]`); }\n\n\t\tlet passageTitle = title;\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Reset the temporary state and variables objects.\n\t\tTempState = {}; // eslint-disable-line no-undef\n\t\tState.clearTemporary();\n\n\t\t// Debug view setup.\n\t\tlet passageReadyOutput;\n\t\tlet passageDoneOutput;\n\n\t\t// Execute the navigation override callback.\n\t\tif (typeof Config.navigation.override === 'function') {\n\t\t\ttry {\n\t\t\t\tconst overrideTitle = Config.navigation.override(passageTitle);\n\n\t\t\t\tif (overrideTitle) {\n\t\t\t\t\tpassageTitle = overrideTitle;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\t\t}\n\n\t\t// Retrieve the passage by the given title.\n\t\t//\n\t\t// NOTE: The values of the `title` parameter and `passageTitle` variable\n\t\t// may be empty, strings, or numbers (though using a number as reference\n\t\t// to a numeric title should be discouraged), so after loading the passage,\n\t\t// always refer to `passage.title` and never to the others.\n\t\tconst passage = Story.get(passageTitle);\n\n\t\t// Execute the pre-history events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype : ':passageinit',\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prehistory).forEach(task => {\n\t\t\tif (typeof prehistory[task] === 'function') {\n\t\t\t\tprehistory[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Create a new entry in the history.\n\t\tif (!noHistory) {\n\t\t\tState.create(passage.title);\n\t\t}\n\n\t\t// Clear the document body's classes.\n\t\tif (document.body.className) {\n\t\t\tdocument.body.className = '';\n\t\t}\n\n\t\t// Update the last play time.\n\t\t//\n\t\t// NOTE: This is mostly for event, task, and special passage code,\n\t\t// though the likelihood of it being needed this early is low.  This\n\t\t// will be updated again later at the end.\n\t\t_lastPlay = Util.now();\n\n\t\t// Execute pre-display tasks and the `PassageReady` special passage.\n\t\tObject.keys(predisplay).forEach(task => {\n\t\t\tif (typeof predisplay[task] === 'function') {\n\t\t\t\tpredisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\tif (Story.has('PassageReady')) {\n\t\t\ttry {\n\t\t\t\tpassageReadyOutput = Wikifier.wikifyEval(Story.get('PassageReady').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageReady', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Update the engine state.\n\t\t_state = States.Rendering;\n\n\t\t// Get the passage's tags as a string, or `null` if there aren't any.\n\t\tconst dataTags = passage.tags.length > 0 ? passage.tags.join(' ') : null;\n\n\t\t// Create and set up the incoming passage element.\n\t\tconst passageEl = document.createElement('div');\n\t\tjQuery(passageEl)\n\t\t\t.attr({\n\t\t\t\tid             : passage.domId,\n\t\t\t\t'data-passage' : passage.title,\n\t\t\t\t'data-tags'    : dataTags\n\t\t\t})\n\t\t\t.addClass(`passage ${passage.className}`);\n\n\t\t// Add the passage's classes and tags to the document body.\n\t\tjQuery(document.body)\n\t\t\t.attr('data-tags', dataTags)\n\t\t\t.addClass(passage.className);\n\n\t\t// Add the passage's tags to the document element.\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-tags', dataTags);\n\n\t\t// Execute pre-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagestart',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prerender).forEach(task => {\n\t\t\tif (typeof prerender[task] === 'function') {\n\t\t\t\tprerender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Render the `PassageHeader` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageHeader')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageHeader').processText());\n\t\t}\n\n\t\t// Render the passage into its element.\n\t\tpassageEl.appendChild(passage.render());\n\n\t\t// Render the `PassageFooter` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageFooter')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageFooter').processText());\n\t\t}\n\n\t\t// Execute post-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagerender',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postrender).forEach(task => {\n\t\t\tif (typeof postrender[task] === 'function') {\n\t\t\t\tpostrender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Cache the passage container.\n\t\tconst containerEl = document.getElementById('passages');\n\n\t\t// Empty the passage container.\n\t\tif (containerEl.hasChildNodes()) {\n\t\t\tif (\n\t\t\t\t   typeof Config.passages.transitionOut === 'number'\n\t\t\t\t|| typeof Config.passages.transitionOut === 'string'\n\t\t\t\t&& Config.passages.transitionOut !== ''\n\t\t\t\t&& Has.transitionEndEvent\n\t\t\t) {\n\t\t\t\t[...containerEl.childNodes].forEach(outgoing => {\n\t\t\t\t\tconst $outgoing = jQuery(outgoing);\n\n\t\t\t\t\tif (outgoing.nodeType === Node.ELEMENT_NODE && $outgoing.hasClass('passage')) {\n\t\t\t\t\t\tif ($outgoing.hasClass('passage-out')) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$outgoing\n\t\t\t\t\t\t\t.attr('id', `out-${$outgoing.attr('id')}`)\n\t\t\t\t\t\t\t.addClass('passage-out');\n\n\t\t\t\t\t\tif (typeof Config.passages.transitionOut === 'string') {\n\t\t\t\t\t\t\t$outgoing.on(Has.transitionEndEvent, ev => {\n\t\t\t\t\t\t\t\tif (ev.originalEvent.propertyName === Config.passages.transitionOut) {\n\t\t\t\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t\t() => $outgoing.remove(),\n\t\t\t\t\t\t\t\tMath.max(minDomActionDelay, Config.passages.transitionOut)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(containerEl).empty();\n\t\t\t}\n\t\t}\n\n\t\t// Append the passage element to the passage container and set up its transition.\n\t\tjQuery(passageEl)\n\t\t\t.addClass('passage-in')\n\t\t\t.appendTo(containerEl);\n\t\tsetTimeout(() => jQuery(passageEl).removeClass('passage-in'), minDomActionDelay);\n\n\t\t// Update the story display title, if necessary.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\t// NOTE: We don't have an `else` here because that case will be handled later (below).\n\t\t\tif (_updating !== null || !Config.ui.updateStoryElements) {\n\t\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t\t}\n\t\t}\n\t\telse if (Config.passages.displayTitles && passage.title !== Config.passages.start) {\n\t\t\tdocument.title = `${passage.title} | ${Story.title}`;\n\t\t}\n\n\t\t// Scroll the window to the top.\n\t\twindow.scroll(0, 0);\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Execute post-display events, tasks, and the `PassageDone` special passage.\n\t\tif (Story.has('PassageDone')) {\n\t\t\ttry {\n\t\t\t\tpassageDoneOutput = Wikifier.wikifyEval(Story.get('PassageDone').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageDone', ex.message);\n\t\t\t}\n\t\t}\n\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagedisplay',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postdisplay).forEach(task => {\n\t\t\tif (typeof postdisplay[task] === 'function') {\n\t\t\t\tpostdisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Update the other interface elements, if necessary.\n\t\tif (_updating !== null) {\n\t\t\t_updating.forEach(pair => {\n\t\t\t\tjQuery(pair.element).empty();\n\t\t\t\tnew Wikifier(pair.element, Story.get(pair.passage).processText().trim());\n\t\t\t});\n\t\t}\n\t\telse if (Config.ui.updateStoryElements) {\n\t\t\tUIBar.update();\n\t\t}\n\n\t\t// Add the completed debug views for `StoryInit`, `PassageReady`, and `PassageDone`\n\t\t// to the incoming passage element.\n\t\tif (Config.debug) {\n\t\t\tlet debugView;\n\n\t\t\t// Prepend the `PassageReady` debug view.\n\t\t\tif (passageReadyOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageReady',\n\t\t\t\t\t'PassageReady'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageReadyOutput);\n\t\t\t\tjQuery(passageEl).prepend(debugView.output);\n\t\t\t}\n\n\t\t\t// Append the `PassageDone` debug view.\n\t\t\tif (passageDoneOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageDone',\n\t\t\t\t\t'PassageDone'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageDoneOutput);\n\t\t\t\tjQuery(passageEl).append(debugView.output);\n\t\t\t}\n\n\t\t\t// Prepend the cached `StoryInit` debug view, if we're showing the first moment/turn.\n\t\t\tif (State.turns === 1 && _storyInitDebugView != null) { // lazy equality for null\n\t\t\t\tjQuery(passageEl).prepend(_storyInitDebugView);\n\t\t\t}\n\t\t}\n\n\t\t// Last second post-processing for accessibility and other things.\n\t\t_hideOutlines(); // initially hide outlines\n\t\tjQuery('#story')\n\t\t\t// Add `link-external` to all `href` bearing `<a>` elements which don't have it.\n\t\t\t.find('a[href]:not(.link-external)')\n\t\t\t.addClass('link-external')\n\t\t\t.end()\n\t\t\t// Add `tabindex=0` to all interactive elements which don't have it.\n\t\t\t.find('a,link,button,input,select,textarea')\n\t\t\t.not('[tabindex]')\n\t\t\t.attr('tabindex', 0);\n\n\t\t// Handle autosaves.\n\t\tswitch (typeof Config.saves.autosave) {\n\t\tcase 'boolean':\n\t\t\tif (Config.saves.autosave) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'object':\n\t\t\tif (passage.tags.some(tag => Config.saves.autosave.includes(tag))) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'function':\n\t\t\tif (Config.saves.autosave()) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// Execute post-play events.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passageend',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\n\t\t// Reset the engine state.\n\t\t_state = States.Idle;\n\n\t\t// Update the last play time.\n\t\t_lastPlay = Util.now();\n\n\t\treturn passageEl;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Play the given passage, optionally without altering the history.\n\t*/\n\tfunction engineDisplay(title, link, option) {\n\t\tif (DEBUG) { console.log('[Engine/engineDisplay()]'); }\n\n\t\tlet noHistory = false;\n\n\t\t// Process the option parameter.\n\t\tswitch (option) {\n\t\tcase undefined:\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'replace':\n\t\tcase 'back':\n\t\t\tnoHistory = true;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`Engine.display option parameter called with obsolete value \"${option}\"; please notify the developer`);\n\t\t}\n\n\t\tenginePlay(title, noHistory);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _hideOutlines() {\n\t\t_outlinePatch.set('*:focus{outline:none;}');\n\t}\n\n\tfunction _showOutlines() {\n\t\t_outlinePatch.clear();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tConstants.\n\t\t*/\n\t\tStates            : { value : States },\n\t\tminDomActionDelay : { value : minDomActionDelay },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tinit        : { value : engineInit },\n\t\tstart       : { value : engineStart },\n\t\trestart     : { value : engineRestart },\n\t\tstate       : { get : engineState },\n\t\tisIdle      : { value : engineIsIdle },\n\t\tisPlaying   : { value : engineIsPlaying },\n\t\tisRendering : { value : engineIsRendering },\n\t\tlastPlay    : { get : engineLastPlay },\n\t\tgoTo        : { value : engineGoTo },\n\t\tgo          : { value : engineGo },\n\t\tbackward    : { value : engineBackward },\n\t\tforward     : { value : engineForward },\n\t\tshow        : { value : engineShow },\n\t\tplay        : { value : enginePlay },\n\n\t\t/*\n\t\t\tLegacy Functions.\n\t\t*/\n\t\tdisplay : { value : engineDisplay }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tpassage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, Util, Wikifier */\n\nvar Passage = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tlet _tagsToSkip;\n\tlet _twine1Unescape;\n\n\t/*\n\t\tTags which should not be transformed into classes:\n\t\t\tdebug      → special tag\n\t\t\tnobr       → special tag\n\t\t\tpassage    → the default class\n\t\t\tscript     → special tag (only in Twine 1)\n\t\t\tstylesheet → special tag (only in Twine 1)\n\t\t\ttwine.*    → special tag\n\t\t\twidget     → special tag\n\t*/\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|script|stylesheet|widget|twine\\..*)$/i;\n\t}\n\t// For Twine 2\n\telse {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|widget|twine\\..*)$/i;\n\t}\n\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t/*\n\t\t\tReturns a decoded version of the passed Twine 1 passage store encoded string.\n\t\t*/\n\t\tconst _twine1EscapesRe    = /(?:\\\\n|\\\\t|\\\\s|\\\\|\\r)/g;\n\t\tconst _hasTwine1EscapesRe = new RegExp(_twine1EscapesRe.source); // to drop the global flag\n\t\tconst _twine1EscapesMap   = Object.freeze({\n\t\t\t'\\\\n' : '\\n',\n\t\t\t'\\\\t' : '\\t',\n\t\t\t'\\\\s' : '\\\\',\n\t\t\t'\\\\'  : '\\\\',\n\t\t\t'\\r'  : ''\n\t\t});\n\n\t\t_twine1Unescape = function (str) {\n\t\t\tif (str == null) { // lazy equality for null\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst val = String(str);\n\t\t\treturn val && _hasTwine1EscapesRe.test(val)\n\t\t\t\t? val.replace(_twine1EscapesRe, esc => _twine1EscapesMap[esc])\n\t\t\t\t: val;\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Class.\n\t*******************************************************************************************************************/\n\tclass Passage {\n\t\tconstructor(title, el) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage title/ID.\n\t\t\t\ttitle : {\n\t\t\t\t\tvalue : Util.unescape(title)\n\t\t\t\t},\n\n\t\t\t\t// Passage data element (within the story data element; i.e. T1: '[tiddler]', T2: 'tw-passagedata').\n\t\t\t\telement : {\n\t\t\t\t\tvalue : el || null\n\t\t\t\t},\n\n\t\t\t\t// Passage tags array (sorted and unique).\n\t\t\t\ttags : {\n\t\t\t\t\tvalue : Object.freeze(el && el.hasAttribute('tags')\n\t\t\t\t\t\t? el.getAttribute('tags')\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.splitOrEmpty(/\\s+/)\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.filter((tag, i, aref) => i === 0 || aref[i - 1] !== tag)\n\t\t\t\t\t\t: [])\n\t\t\t\t},\n\n\t\t\t\t// Passage excerpt.  Used by the `description()` method.\n\t\t\t\t_excerpt : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Properties dependant upon the above set.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage DOM-compatible ID.\n\t\t\t\tdomId : {\n\t\t\t\t\tvalue : `passage-${Util.slugify(this.title)}`\n\t\t\t\t},\n\n\t\t\t\t// Passage classes array (sorted and unique).\n\t\t\t\tclasses : {\n\t\t\t\t\tvalue : Object.freeze(this.tags.length === 0 ? [] : (() =>\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tReturn the sorted list of unique classes.\n\n\t\t\t\t\t\t\tNOTE: The `this.tags` array is already sorted and unique,\n\t\t\t\t\t\t\tso we only need to filter and map here.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tthis.tags\n\t\t\t\t\t\t\t.filter(tag => !_tagsToSkip.test(tag))\n\t\t\t\t\t\t\t.map(tag => Util.slugify(tag))\n\t\t\t\t\t)())\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Getters.\n\t\tget className() {\n\t\t\treturn this.classes.join(' ');\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get source`.\n\t\tget text() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\tconst passage = Util.escapeMarkup(this.title);\n\t\t\t\tconst mesg    = `${L10n.get('errorTitle')}: ${L10n.get('errorNonexistentPassage', { passage })}`;\n\t\t\t\treturn `<div class=\"error-view\"><span class=\"error\">${mesg}</span></div>`;\n\t\t\t}\n\n\t\t\t// For Twine 1\n\t\t\tif (TWINE1) {\n\t\t\t\treturn _twine1Unescape(this.element.textContent);\n\t\t\t}\n\t\t\t// For Twine 2\n\t\t\telse { // eslint-disable-line no-else-return\n\t\t\t\treturn this.element.textContent.replace(/\\r/g, '');\n\t\t\t}\n\t\t}\n\n\t\tdescription() {\n\t\t\tconst descriptions = Config.passages.descriptions;\n\n\t\t\tif (descriptions != null) { // lazy equality for null\n\t\t\t\tswitch (typeof descriptions) {\n\t\t\t\tcase 'boolean':\n\t\t\t\t\tif (descriptions) {\n\t\t\t\t\t\treturn this.title;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (descriptions instanceof Map && descriptions.has(this.title)) {\n\t\t\t\t\t\treturn descriptions.get(this.title);\n\t\t\t\t\t}\n\t\t\t\t\telse if (descriptions.hasOwnProperty(this.title)) {\n\t\t\t\t\t\treturn descriptions[this.title];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'function':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst result = descriptions.call(this);\n\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new TypeError('Config.passages.descriptions must be a boolean, object, or function');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Initialize the excerpt cache from the raw passage text, if necessary.\n\t\t\tif (this._excerpt === null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromText(this.text);\n\t\t\t}\n\n\t\t\treturn this._excerpt;\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get text`.\n\t\tprocessText() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\treturn this.text;\n\t\t\t}\n\n\t\t\t// Handle image passage transclusion.\n\t\t\tif (this.tags.includes('Twine.image')) {\n\t\t\t\treturn `[img[${this.text}]]`;\n\t\t\t}\n\n\t\t\tlet processed = this.text;\n\n\t\t\t// Handle `Config.passages.onProcess`.\n\t\t\tif (Config.passages.onProcess) {\n\t\t\t\tprocessed = Config.passages.onProcess.call(null, {\n\t\t\t\t\ttitle : this.title,\n\t\t\t\t\ttags  : this.tags,\n\t\t\t\t\ttext  : processed\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle `Config.passages.nobr` and the `nobr` tag.\n\t\t\tif (Config.passages.nobr || this.tags.includes('nobr')) {\n\t\t\t\t// Remove all leading & trailing newlines and compact all internal sequences\n\t\t\t\t// of newlines into single spaces.\n\t\t\t\tprocessed = processed.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' ');\n\t\t\t}\n\n\t\t\treturn processed;\n\t\t}\n\n\t\trender(options) {\n\t\t\t// Wikify the passage into a document fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.processText(), options);\n\n\t\t\t// Update the excerpt cache to reflect the rendered text, if we need it for the passage description\n\t\t\tif (Config.passages.descriptions == null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromNode(frag);\n\t\t\t}\n\n\t\t\treturn frag;\n\t\t}\n\n\t\tstatic getExcerptFromNode(node, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromNode(node=…, count=${count})]`, node); }\n\n\t\t\tif (!node.hasChildNodes()) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t// WARNING: es5-shim's `<String>.trim()` can cause \"too much recursion\" errors\n\t\t\t// here on very large strings (e.g., ≥40 KiB), at least in Firefox, for unknown\n\t\t\t// reasons.\n\t\t\t//\n\t\t\t// To fix the issue, we're removed `\\u180E` from es5-shim's whitespace pattern\n\t\t\t// to prevent it from erroneously shimming `<String>.trim()` in the first place.\n\t\t\tlet excerpt = node.textContent.trim();\n\n\t\t\tif (excerpt !== '') {\n\t\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\t\texcerpt = excerpt\n\t\t\t\t\t// Compact whitespace.\n\t\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t\t.match(excerptRe);\n\t\t\t}\n\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\n\t\tstatic getExcerptFromText(text, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromText(text=…, count=${count})]`, text); }\n\n\t\t\tif (text === '') {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\tconst excerpt   = text\n\t\t\t\t// Strip macro tags (replace with a space).\n\t\t\t\t.replace(/<<.*?>>/g, ' ')\n\t\t\t\t// Strip html tags (replace with a space).\n\t\t\t\t.replace(/<.*?>/g, ' ')\n\t\t\t\t// The above might have left problematic whitespace, so trim.\n\t\t\t\t.trim()\n\t\t\t\t// Strip table markup.\n\t\t\t\t.replace(/^\\s*\\|.*\\|.*?$/gm, '')\n\t\t\t\t// Strip image markup.\n\t\t\t\t.replace(/\\[[<>]?img\\[[^\\]]*\\]\\]/g, '')\n\t\t\t\t// Clean link markup (remove all but the link text).\n\t\t\t\t.replace(/\\[\\[([^|\\]]*?)(?:(?:\\||->|<-)[^\\]]*)?\\]\\]/g, '$1')\n\t\t\t\t// Clean heading markup.\n\t\t\t\t.replace(/^\\s*!+(.*?)$/gm, '$1')\n\t\t\t\t// Clean bold/italic/underline/highlight styles.\n\t\t\t\t.replace(/'{2}|\\/{2}|_{2}|@{2}/g, '')\n\t\t\t\t// A final trim.\n\t\t\t\t.trim()\n\t\t\t\t// Compact whitespace.\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t.match(excerptRe);\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Passage;\n})();\n\n/***********************************************************************************************************************\n\n\tsave.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Dialog, Engine, L10n, State, Story, UI, Util, storage */\n\nvar Save = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Save operation type pseudo-enumeration.\n\tconst Type = Util.toEnum({\n\t\tAutosave  : 'autosave',\n\t\tDisk      : 'disk',\n\t\tSerialize : 'serialize',\n\t\tSlot      : 'slot'\n\t});\n\n\t// The upper bound of the saves slots.\n\tlet _slotsUBound = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tSaves Functions.\n\t*******************************************************************************************************************/\n\tfunction savesInit() {\n\t\tif (DEBUG) { console.log('[Save/savesInit()]'); }\n\n\t\t// Disable save slots and the autosave when Web Storage is unavailable.\n\t\tif (storage.name === 'cookie') {\n\t\t\tsavesObjClear();\n\t\t\tConfig.saves.autoload = undefined;\n\t\t\tConfig.saves.autosave = undefined;\n\t\t\tConfig.saves.slots = 0;\n\t\t\treturn false;\n\t\t}\n\n\n\t\t// convert the single big saves object into the new smaller objects\n\t\tconst saves = storage.get('saves');\n\t\tif (saves !== null) {\n\t\t\tstorage.delete('saves'); // Make sure we have enough space to store the saves in the new format\n\t\t\tconst container = Dialog.setup('Backup');\n\t\t\tconst message = document.createElement('span');\n\t\t\tmessage.className = 'red';\n\t\t\tmessage.append('Due to changes to the saves system your existing saves were converted. Backup all saves' +\n\t\t\t\t' that are important to you as they may have been lost during conversion. Once you close this dialog' +\n\t\t\t\t' it will be IMPOSSIBLE to get your old saves back.');\n\t\t\tcontainer.append(message);\n\t\t\tconst index = indexGet();\n\t\t\t// we store the index every time we store a save, so in case we exceed local storage only the\n\t\t\t// last save is lost\n\t\t\tif (saves.autosave !== null) {\n\t\t\t\tcontainer.append(savesLink('autosave', saves.autosave));\n\t\t\t\tindex.autosave = { title : saves.autosave.title, date : saves.autosave.date };\n\t\t\t\ttry {\n\t\t\t\t\tstorage.set('autosave', saves.autosave);\n\t\t\t\t\tindexSave(index);\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tstorage.delete('autosave');\n\t\t\t\t\tindex.autosave = null;\n\t\t\t\t\tindexSave(index);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (let i = 0; i < saves.slots.length; i++) {\n\t\t\t\tif (saves.slots[i] !== null) {\n\t\t\t\t\tcontainer.append(savesLink(`slot${i}`, saves.slots[i]));\n\t\t\t\t\tindex.slots[i] = { title : saves.slots[i].title, date : saves.slots[i].date };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstorage.set(`slot${i}`, saves.slots[i]);\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tstorage.delete(`slot${i}`);\n\t\t\t\t\t\tindex.slots[i] = null;\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tDialog.open();\n\t\t}\n\n\t\tfunction savesLink(name, save) {\n\t\t\tconst div = document.createElement('div');\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.append(`Backup ${name}`);\n\t\t\ta.onclick = () => {\n\t\t\t\texportToDisk(`backup-${name}`, {}, save);\n\t\t\t};\n\t\t\tdiv.append(a);\n\t\t\treturn div;\n\t\t}\n\n\t\t/* All SC2 compatibility converters are disabled, too much work */\n\n\t\t/* legacy */\n\t\t// Convert an ancient saves array into a new saves object.\n\t\t/* if (Array.isArray(saves)) {\n\t\t\tsaves = {\n\t\t\t\tautosave : null,\n\t\t\t\tslots    : saves\n\t\t\t};\n\t\t\tupdated = true;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Handle the author changing the number of save slots.\n\t\t/*\n\t\tif (Config.saves.slots !== saves.slots.length) {\n\t\t\tif (Config.saves.slots < saves.slots.length) {\n\t\t\t\t// Attempt to decrease the number of slots; this will only compact\n\t\t\t\t// the slots array, by removing empty slots, no saves will be deleted.\n\t\t\t\tsaves.slots.reverse();\n\n\t\t\t\tsaves.slots = saves.slots.filter(function (val) {\n\t\t\t\t\tif (val === null && this.count > 0) {\n\t\t\t\t\t\t--this.count;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}, { count : saves.slots.length - Config.saves.slots });\n\n\t\t\t\tsaves.slots.reverse();\n\t\t\t}\n\t\t\telse if (Config.saves.slots > saves.slots.length) {\n\t\t\t\t// Attempt to increase the number of slots.\n\t\t\t\t_appendSlots(saves.slots, Config.saves.slots - saves.slots.length);\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t */\n\n\t\t/* legacy */\n\t\t// Update saves with old/obsolete properties.\n\t\t/* if (_savesObjUpdate(saves.autosave)) {\n\t\t\tupdated = true;\n\t\t}\n\n\t\tfor (let i = 0; i < saves.slots.length; ++i) {\n\t\t\tif (_savesObjUpdate(saves.slots[i])) {\n\t\t\t\tupdated = true;\n\t\t\t}\n\t\t}\n\n\t\t// Remove save stores which are empty.\n\t\tif (_savesObjIsEmpty(saves)) {\n\t\t\tstorage.delete('saves');\n\t\t\tupdated = false;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// If the saves object was updated, then update the store.\n\t\t/* if (updated) {\n\t\t\t_savesObjSave(saves);\n\t\t}\n\t\t*/\n\n\t\t_slotsUBound = indexGet().slots.length - 1;\n\n\t\treturn true;\n\t}\n\n\tfunction indexCreate() {\n\t\treturn {\n\t\t\tautosave : null,\n\t\t\tslots    : _appendSlots([], Config.saves.slots)\n\t\t};\n\t}\n\n\tfunction indexGet() {\n\t\tconst index = storage.get('index');\n\t\treturn index === null ? indexCreate() : index;\n\t}\n\n\tfunction indexSave(index) {\n\t\treturn storage.set('index', index);\n\t}\n\n\tfunction savesObjClear() {\n\t\tstorage.delete('autosave');\n\t\tconst index = indexGet();\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tstorage.delete(`slot${i}`);\n\t\t}\n\t\tstorage.delete('index');\n\t\treturn true;\n\t}\n\n\tfunction savesOk() {\n\t\treturn autosaveOk() || slotsOk();\n\t}\n\n\t/*******************************************************************************************************************\n\t\tAutosave Functions.\n\t*******************************************************************************************************************/\n\tfunction autosaveOk() {\n\t\treturn storage.name !== 'cookie' && typeof Config.saves.autosave !== 'undefined';\n\t}\n\n\tfunction autosaveHas() {\n\t\treturn indexGet().autosave !== null;\n\t}\n\n\tfunction autosaveGet() {\n\t\treturn storage.get('autosave');\n\t}\n\n\tfunction autosaveLoad() {\n\t\tconst autosave = autosaveGet();\n\n\t\tif (autosave === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn _unmarshal(autosave);\n\t}\n\n\tfunction autosaveSave(title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle : title || Story.get(State.passage).description(),\n\t\t\tdate  : Date.now()\n\t\t};\n\t\tconst index = indexGet();\n\t\tindex.autosave = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set('autosave', _marshal(supplemental, { type : Type.Autosave }), false);\n\t\t} catch (ex) {\n\t\t\tindex.autosave = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction autosaveDelete() {\n\t\tconst index = indexGet();\n\t\tindex.autosave = null;\n\t\tindexSave(index);\n\t\treturn storage.delete('autosave');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSlots Functions.\n\t*******************************************************************************************************************/\n\tfunction slotsOk() {\n\t\treturn storage.name !== 'cookie' && _slotsUBound !== -1;\n\t}\n\n\tfunction slotsLength() {\n\t\treturn _slotsUBound + 1;\n\t}\n\n\tfunction slotsCount() {\n\t\tif (!slotsOk()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst index = indexGet();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tif (index.slots[i] !== null) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\tfunction slotsIsEmpty() {\n\t\treturn slotsCount() === 0;\n\t}\n\n\tfunction slotsHas(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length || index.slots[slot] === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction slotsGet(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn storage.get(`slot${slot}`);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tfunction slotsLoad(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn _unmarshal(storage.get(`slot${slot}`));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction slotsSave(slot, title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle: title || Story.get(State.passage).description(),\n\t\t\tdate: Date.now()\n\t\t};\n\n\t\tindex.slots[slot] = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set(`slot${slot}`, _marshal(supplemental, { type : Type.Slot }));\n\t\t} catch (ex) {\n\t\t\tindex.slots[slot] = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction slotsDelete(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tindex.slots[slot] = null;\n\t\tindexSave(index);\n\n\t\treturn storage.delete(`slot${slot}`);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDisk Import/Export Functions.\n\t*******************************************************************************************************************/\n\tfunction exportToDisk(filename, metadata, marshaledSave) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tfunction datestamp() {\n\t\t\tconst now = new Date();\n\t\t\tlet MM = now.getMonth() + 1;\n\t\t\tlet DD = now.getDate();\n\t\t\tlet hh = now.getHours();\n\t\t\tlet mm = now.getMinutes();\n\t\t\tlet ss = now.getSeconds();\n\n\t\t\tif (MM < 10) { MM = `0${MM}`; }\n\t\t\tif (DD < 10) { DD = `0${DD}`; }\n\t\t\tif (hh < 10) { hh = `0${hh}`; }\n\t\t\tif (mm < 10) { mm = `0${mm}`; }\n\t\t\tif (ss < 10) { ss = `0${ss}`; }\n\n\t\t\treturn `${now.getFullYear()}${MM}${DD}-${hh}${mm}${ss}`;\n\t\t}\n\n\t\tfunction legalizeName(str) {\n\t\t\t/*\n\t\t\t\tNOTE: The range of illegal characters consists of: C0 controls, double quote,\n\t\t\t\tnumber, dollar, percent, ampersand, single quote, asterisk, plus, comma,\n\t\t\t\tforward slash, colon, semi-colon, less-than, equals, greater-than, question,\n\t\t\t\tbackslash, caret, backquote/grave, pipe/vertical-bar, delete, C1 controls.\n\t\t\t*/\n\t\t\treturn String(str).trim()\n\t\t\t\t.replace(/[\\x00-\\x1f\"#$%&'*+,/:;<=>?\\\\^`|\\x7f-\\x9f]+/g, '') // eslint-disable-line no-control-regex\n\t\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-'); // legacy\n\t\t}\n\n\t\tconst baseName     = filename == null ? Story.domId : legalizeName(filename); // lazy equality for null\n\t\tconst saveName     = `${baseName}-${datestamp()}.save`;\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\tconst saveObj      = LZString.compressToBase64(JSON.stringify(\n\t\t\tmarshaledSave == null ? _marshal(supplemental, { type : Type.Disk }) : marshaledSave\n\t\t));\n\t\tsaveAs(new Blob([saveObj], { type : 'text/plain;charset=UTF-8' }), saveName);\n\t}\n\n\tfunction importFromDisk(event) {\n\t\tconst file   = event.target.files[0];\n\t\tconst reader = new FileReader();\n\n\t\t// Add the handler that will capture the file information once the load is finished.\n\t\tjQuery(reader).on('load', ev => {\n\t\t\tconst target = ev.currentTarget;\n\n\t\t\tif (!target.result) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet saveObj;\n\n\t\t\ttry {\n\t\t\t\tsaveObj = JSON.parse(\n\t\t\t\t\t/* legacy */ /\\.json$/i.test(file.name) || /^\\{/.test(target.result)\n\t\t\t\t\t\t? target.result\n\t\t\t\t\t\t: /* /legacy */ LZString.decompressFromBase64(target.result)\n\t\t\t\t);\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\t\t_unmarshal(saveObj);\n\t\t});\n\n\t\t// Initiate the file load.\n\t\treader.readAsText(file);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSerialization Functions.\n\t*******************************************************************************************************************/\n\tfunction serialize(metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\treturn LZString.compressToBase64(JSON.stringify(_marshal(supplemental, { type : Type.Serialize })));\n\t}\n\n\tfunction deserialize(base64Str) {\n\t\t/*\n\t\t\tNOTE: We purposefully do not attempt to catch parameter shenanigans\n\t\t\there, instead relying on `_unmarshal()` to do the heavy lifting.\n\t\t*/\n\n\t\tlet saveObj;\n\n\t\ttry {\n\t\t\tsaveObj = JSON.parse(LZString.decompressFromBase64(base64Str));\n\t\t}\n\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\tif (!_unmarshal(saveObj)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn saveObj.metadata;\n\t}\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _storageAlert() {\n\t\tif (Dialog.isOpen()) {\n\t\t\t$(document).one(':dialogclosed', () => UI.alert('Local storage full, delete saves or increase local storage'));\n\t\t} else {\n\t\t\tUI.alert('Local storage full, delete saves or increase local storage');\n\t\t}\n\t}\n\n\tfunction _appendSlots(array, num) {\n\t\tfor (let i = 0; i < num; ++i) {\n\t\t\tarray.push(null);\n\t\t}\n\n\t\treturn array;\n\t}\n\n\tfunction _savesObjUpdate(saveObj) {\n\t\tif (saveObj == null || typeof saveObj !== \"object\") { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tlet updated = false;\n\n\t\t/* eslint-disable no-param-reassign */\n\t\tif (\n\t\t\t   !saveObj.hasOwnProperty('state')\n\t\t\t|| !saveObj.state.hasOwnProperty('delta')\n\t\t\t|| !saveObj.state.hasOwnProperty('index')\n\t\t) {\n\t\t\tif (saveObj.hasOwnProperty('data')) {\n\t\t\t\tdelete saveObj.mode;\n\t\t\t\tsaveObj.state = {\n\t\t\t\t\tdelta : State.deltaEncode(saveObj.data)\n\t\t\t\t};\n\t\t\t\tdelete saveObj.data;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('delta')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\t\t\tdelete saveObj.state.history;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('index')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t}\n\n\t\t\tsaveObj.state.index = saveObj.state.delta.length - 1;\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (saveObj.state.hasOwnProperty('rseed')) {\n\t\t\tsaveObj.state.seed = saveObj.state.rseed;\n\t\t\tdelete saveObj.state.rseed;\n\n\t\t\tsaveObj.state.delta.forEach((_, i, delta) => {\n\t\t\t\tif (delta[i].hasOwnProperty('rcount')) {\n\t\t\t\t\tdelta[i].pull = delta[i].rcount;\n\t\t\t\t\tdelete delta[i].rcount;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (\n\t\t\t   saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number'\n\t\t\t||  saveObj.state.hasOwnProperty('unique')\n\t\t\t||  saveObj.state.hasOwnProperty('last')\n\t\t) {\n\t\t\tif (saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number') {\n\t\t\t\tdelete saveObj.state.expired;\n\t\t\t}\n\n\t\t\tif (saveObj.state.hasOwnProperty('unique') || saveObj.state.hasOwnProperty('last')) {\n\t\t\t\tsaveObj.state.expired = [];\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('unique')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.unique);\n\t\t\t\t\tdelete saveObj.state.unique;\n\t\t\t\t}\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('last')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.last);\n\t\t\t\t\tdelete saveObj.state.last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t/* eslint-enable no-param-reassign */\n\n\t\treturn updated;\n\t}\n\n\tfunction _marshal(supplemental, details) {\n\t\tif (DEBUG) { console.log(`[Save/_marshal(…, { type : '${details.type}' })]`); }\n\n\t\tif (supplemental != null && typeof supplemental !== 'object') { // lazy equality for null\n\t\t\tthrow new Error('supplemental parameter must be an object');\n\t\t}\n\n\t\tconst saveObj = Object.assign({}, supplemental, {\n\t\t\tid    : Config.saves.id,\n\t\t\tstate : State.marshalForSave()\n\t\t});\n\n\t\tif (Config.saves.version) {\n\t\t\tsaveObj.version = Config.saves.version;\n\t\t}\n\n\t\tif (typeof Config.saves.onSave === 'function') {\n\t\t\tConfig.saves.onSave(saveObj, details);\n\t\t}\n\n\t\t// Delta encode the state history and delete the non-encoded property.\n\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\tdelete saveObj.state.history;\n\n\t\treturn saveObj;\n\t}\n\n\tfunction _unmarshal(saveObj) {\n\t\tif (DEBUG) { console.log('[Save/_unmarshal()]'); }\n\n\t\ttry {\n\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t/* legacy */\n\t\t\t// Update saves with old/obsolete properties.\n\t\t\t_savesObjUpdate(saveObj);\n\t\t\t/* /legacy */\n\n\t\t\tif (!saveObj || !saveObj.hasOwnProperty('id') || !saveObj.hasOwnProperty('state')) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveMissingData'));\n\t\t\t}\n\n\t\t\t// Delta decode the state history and delete the encoded property.\n\t\t\tsaveObj.state.history = State.deltaDecode(saveObj.state.delta);\n\t\t\tdelete saveObj.state.delta;\n\n\t\t\tif (typeof Config.saves.onLoad === 'function') {\n\t\t\t\tConfig.saves.onLoad(saveObj);\n\t\t\t}\n\n\t\t\tif (saveObj.id !== Config.saves.id) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveIdMismatch'));\n\t\t\t}\n\n\t\t\t// Restore the state.\n\t\t\tState.unmarshalForSave(saveObj.state); // may also throw exceptions\n\n\t\t\t// Show the active moment.\n\t\t\tEngine.show();\n\t\t\t/* eslint-enable no-param-reassign */\n\t\t}\n\t\tcatch (ex) {\n\t\t\tUI.alert(`${ex.message.toUpperFirst()}.</p><p>${L10n.get('aborting')}.`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tSave Functions.\n\t\t*/\n\t\tinit  : { value : savesInit },\n\t\tclear : { value : savesObjClear },\n\t\tok    : { value : savesOk },\n\t\tindex : { value : indexGet },\n\n\t\t/*\n\t\t\tAutosave Functions.\n\t\t*/\n\t\tautosave : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok     : { value : autosaveOk },\n\t\t\t\thas    : { value : autosaveHas },\n\t\t\t\tget    : { value : autosaveGet },\n\t\t\t\tload   : { value : autosaveLoad },\n\t\t\t\tsave   : { value : autosaveSave },\n\t\t\t\tdelete : { value : autosaveDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tSlots Functions.\n\t\t*/\n\t\tslots : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok      : { value : slotsOk },\n\t\t\t\tlength  : { get : slotsLength },\n\t\t\t\tisEmpty : { value : slotsIsEmpty },\n\t\t\t\tcount   : { value : slotsCount },\n\t\t\t\thas     : { value : slotsHas },\n\t\t\t\tget     : { value : slotsGet },\n\t\t\t\tload    : { value : slotsLoad },\n\t\t\t\tsave    : { value : slotsSave },\n\t\t\t\tdelete  : { value : slotsDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tDisk Import/Export Functions.\n\t\t*/\n\t\texport : { value : exportToDisk },\n\t\timport : { value : importFromDisk },\n\n\t\t/*\n\t\t\tSerialization Functions.\n\t\t*/\n\t\tserialize   : { value : serialize },\n\t\tdeserialize : { value : deserialize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsetting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, settings:true, storage */\n\nvar Setting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Setting control types object (pseudo-enumeration).\n\tconst Types = Util.toEnum({\n\t\tHeader : 0,\n\t\tToggle : 1,\n\t\tList   : 2,\n\t\tRange  : 3\n\t});\n\n\t// Setting definition array.\n\tconst _definitions = [];\n\n\n\t/*******************************************************************************************************************\n\t\tSettings Functions.\n\t*******************************************************************************************************************/\n\tfunction settingsInit() {\n\t\tif (DEBUG) { console.log('[Setting/settingsInit()]'); }\n\n\t\t/* legacy */\n\t\t// Attempt to migrate an existing `options` store to `settings`.\n\t\tif (storage.has('options')) {\n\t\t\tconst old = storage.get('options');\n\n\t\t\tif (old !== null) {\n\t\t\t\twindow.SugarCube.settings = settings = Object.assign(settingsCreate(), old);\n\t\t\t}\n\n\t\t\tsettingsSave();\n\t\t\tstorage.delete('options');\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Load existing settings.\n\t\tsettingsLoad();\n\n\t\t// Execute `onInit` callbacks.\n\t\t_definitions.forEach(def => {\n\t\t\tif (def.hasOwnProperty('onInit')) {\n\t\t\t\tconst thisArg = {\n\t\t\t\t\tname    : def.name,\n\t\t\t\t\tvalue   : settings[def.name],\n\t\t\t\t\tdefault : def.default\n\t\t\t\t};\n\n\t\t\t\tif (def.hasOwnProperty('list')) {\n\t\t\t\t\tthisArg.list = def.list;\n\t\t\t\t}\n\n\t\t\t\tdef.onInit.call(thisArg);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction settingsCreate() {\n\t\treturn Object.create(null);\n\t}\n\n\tfunction settingsSave() {\n\t\tconst savedSettings = settingsCreate();\n\n\t\tif (Object.keys(settings).length > 0) {\n\t\t\t_definitions\n\t\t\t\t.filter(def => def.type !== Types.Header && settings[def.name] !== def.default)\n\t\t\t\t.forEach(def => savedSettings[def.name] = settings[def.name]);\n\t\t}\n\n\t\tif (Object.keys(savedSettings).length === 0) {\n\t\t\tstorage.delete('settings');\n\t\t\treturn true;\n\t\t}\n\n\t\treturn storage.set('settings', savedSettings);\n\t}\n\n\tfunction settingsLoad() {\n\t\tconst defaultSettings = settingsCreate();\n\t\tconst loadedSettings  = storage.get('settings') || settingsCreate();\n\n\t\t// Load the defaults.\n\t\t_definitions\n\t\t\t.filter(def => def.type !== Types.Header)\n\t\t\t.forEach(def => defaultSettings[def.name] = def.default);\n\n\t\t// Assign to the `settings` object while overwriting the defaults with the loaded settings.\n\t\twindow.SugarCube.settings = settings = Object.assign(defaultSettings, loadedSettings);\n\t}\n\n\tfunction settingsClear() {\n\t\twindow.SugarCube.settings = settings = settingsCreate();\n\t\tstorage.delete('settings');\n\t\treturn true;\n\t}\n\n\tfunction settingsReset(name) {\n\t\tif (arguments.length === 0) {\n\t\t\tsettingsClear();\n\t\t\tsettingsLoad();\n\t\t}\n\t\telse {\n\t\t\tif (name == null || !definitionsHas(name)) { // lazy equality for null\n\t\t\t\tthrow new Error(`nonexistent setting \"${name}\"`);\n\t\t\t}\n\n\t\t\tconst def = definitionsGet(name);\n\n\t\t\tif (def.type !== Types.Header) {\n\t\t\t\tsettings[name] = def.default;\n\t\t\t}\n\t\t}\n\n\t\treturn settingsSave();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDefinitions Functions.\n\t*******************************************************************************************************************/\n\tfunction definitionsForEach(callback, thisArg) {\n\t\t_definitions.forEach(callback, thisArg);\n\t}\n\n\tfunction definitionsAdd(type, name, def) {\n\t\tif (arguments.length < 3) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('type'); }\n\t\t\tif (arguments.length < 2) { errors.push('name'); }\n\t\t\tif (arguments.length < 3) { errors.push('definition'); }\n\t\t\tthrow new Error(`missing parameters, no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tif (typeof def !== 'object') {\n\t\t\tthrow new TypeError('definition parameter must be an object');\n\t\t}\n\n\t\tif (definitionsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing setting \"${name}\"`);\n\t\t}\n\n\t\t/*\n\t\t\tDefinition object properties and types:\n\t\t\t\ttype      →  (all)   → Setting.Types\n\t\t\t\tname      →  (all)   → string\n\t\t\t\tlabel     →  (all)   → string\n\t\t\t\tdesc      →  (all)   → string\n\t\t\t\tdefault\n\t\t\t\t\t(if defined)\n \t\t\t\t\t\t  →  Toggle  → boolean\n\t\t\t\t\t\t  →  List    → Array\n\t\t\t\t\t\t  →  Range   → number\n\t\t\t\t\t(if undefined)\n\t\t\t\t\t\t  →  Toggle  → false\n\t\t\t\t\t\t  →  List    → list[0]\n\t\t\t\t\t\t  →  Range   → max\n\t\t\t\tlist      →  List    → Array\n\t\t\t\tmin       →  Range   → number\n\t\t\t\tmax       →  Range   → number\n\t\t\t\tstep      →  Range   → number\n\t\t\t\tonInit    →  (all)   → function\n\t\t\t\tonChange  →  (all)   → function\n\t\t*/\n\t\tconst definition = {\n\t\t\ttype,\n\t\t\tname,\n\t\t\tlabel : typeof def.label === 'string' ? def.label.trim() : ''\n\t\t};\n\n\t\tif (typeof def.desc === 'string') {\n\t\t\tconst desc = def.desc.trim();\n\n\t\t\tif (desc !== '') {\n\t\t\t\tdefinition.desc = desc;\n\t\t\t}\n\t\t}\n\n\t\tswitch (type) {\n\t\tcase Types.Header:\n\t\t\tbreak;\n\n\t\tcase Types.Toggle:\n\t\t\tdefinition.default = !!def.default;\n\t\t\tbreak;\n\n\t\tcase Types.List:\n\t\t\tif (!def.hasOwnProperty('list')) {\n\t\t\t\tthrow new Error('no list specified');\n\t\t\t}\n\t\t\telse if (!Array.isArray(def.list)) {\n\t\t\t\tthrow new TypeError('list must be an array');\n\t\t\t}\n\t\t\telse if (def.list.length === 0) {\n\t\t\t\tthrow new Error('list must not be empty');\n\t\t\t}\n\n\t\t\tdefinition.list = Object.freeze(def.list);\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.list[0];\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst defaultIndex = def.list.indexOf(def.default);\n\n\t\t\t\tif (defaultIndex === -1) {\n\t\t\t\t\tthrow new Error('list does not contain default');\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.list[defaultIndex];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase Types.Range:\n\t\t\tif (!def.hasOwnProperty('min')) {\n\t\t\t\tthrow new Error('no min specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.min !== 'number'\n\t\t\t\t|| Number.isNaN(def.min)\n\t\t\t\t|| !Number.isFinite(def.min)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('min must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('max')) {\n\t\t\t\tthrow new Error('no max specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.max !== 'number'\n\t\t\t\t|| Number.isNaN(def.max)\n\t\t\t\t|| !Number.isFinite(def.max)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('max must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('step')) {\n\t\t\t\tthrow new Error('no step specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.step !== 'number'\n\t\t\t\t|| Number.isNaN(def.step)\n\t\t\t\t|| !Number.isFinite(def.step)\n\t\t\t\t|| def.step <= 0\n\t\t\t) {\n\t\t\t\tthrow new TypeError('step must be a finite number greater than zero');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Determine how many fractional digits we need to be concerned with based on the step value.\n\t\t\t\tconst fracDigits = (() => {\n\t\t\t\t\tconst str = String(def.step);\n\t\t\t\t\tconst pos = str.lastIndexOf('.');\n\t\t\t\t\treturn pos === -1 ? 0 : str.length - pos - 1;\n\t\t\t\t})();\n\n\t\t\t\t// Set up a function to validate a given value against the step value.\n\t\t\t\tfunction stepValidate(value) {\n\t\t\t\t\tif (fracDigits > 0) {\n\t\t\t\t\t\tconst ma = Number(`${def.min}e${fracDigits}`);\n\t\t\t\t\t\tconst sa = Number(`${def.step}e${fracDigits}`);\n\t\t\t\t\t\tconst va = Number(`${value}e${fracDigits}`) - ma;\n\t\t\t\t\t\treturn Number(`${va - va % sa + ma}e-${fracDigits}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst va = value - def.min;\n\t\t\t\t\treturn va - va % def.step + def.min;\n\t\t\t\t}\n\n\t\t\t\t// Sanity check the max value against the step value.\n\t\t\t\tif (stepValidate(def.max) !== def.max) {\n\t\t\t\t\tthrow new RangeError(`max (${def.max}) is not a multiple of the step (${def.step}) plus the min (${def.min})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefinition.max = def.max;\n\t\t\tdefinition.min = def.min;\n\t\t\tdefinition.step = def.step;\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.max;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (\n\t\t\t\t\t   typeof def.default !== 'number'\n\t\t\t\t\t|| Number.isNaN(def.default)\n\t\t\t\t\t|| !Number.isFinite(def.default)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError('default must be a finite number');\n\t\t\t\t}\n\t\t\t\telse if (def.default < def.min) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is less than min (${def.min})`);\n\t\t\t\t}\n\t\t\t\telse if (def.default > def.max) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is greater than max (${def.max})`);\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.default;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`unknown Setting type: ${type}`);\n\t\t}\n\n\t\tif (typeof def.onInit === 'function') {\n\t\t\tdefinition.onInit = Object.freeze(def.onInit);\n\t\t}\n\n\t\tif (typeof def.onChange === 'function') {\n\t\t\tdefinition.onChange = Object.freeze(def.onChange);\n\t\t}\n\n\t\t_definitions.push(Object.freeze(definition));\n\t}\n\n\tfunction definitionsAddHeader(name, desc) {\n\t\tdefinitionsAdd(Types.Header, name, { desc });\n\t}\n\n\tfunction definitionsAddToggle(...args) {\n\t\tdefinitionsAdd(Types.Toggle, ...args);\n\t}\n\n\tfunction definitionsAddList(...args) {\n\t\tdefinitionsAdd(Types.List, ...args);\n\t}\n\n\tfunction definitionsAddRange(...args) {\n\t\tdefinitionsAdd(Types.Range, ...args);\n\t}\n\n\tfunction definitionsIsEmpty() {\n\t\treturn _definitions.length === 0;\n\t}\n\n\tfunction definitionsHas(name) {\n\t\treturn _definitions.some(definition => definition.name === name);\n\t}\n\n\tfunction definitionsGet(name) {\n\t\treturn _definitions.find(definition => definition.name === name);\n\t}\n\n\tfunction definitionsDelete(name) {\n\t\tif (definitionsHas(name)) {\n\t\t\tdelete settings[name];\n\t\t}\n\n\t\tfor (let i = 0; i < _definitions.length; ++i) {\n\t\t\tif (_definitions[i].name === name) {\n\t\t\t\t_definitions.splice(i, 1);\n\t\t\t\tdefinitionsDelete(name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tEnumerations.\n\t\t*/\n\t\tTypes : { value : Types },\n\n\t\t/*\n\t\t\tSettings Functions.\n\t\t*/\n\t\tinit   : { value : settingsInit },\n\t\tcreate : { value : settingsCreate },\n\t\tsave   : { value : settingsSave },\n\t\tload   : { value : settingsLoad },\n\t\tclear  : { value : settingsClear },\n\t\treset  : { value : settingsReset },\n\n\t\t/*\n\t\t\tDefinitions Functions.\n\t\t*/\n\t\tforEach   : { value : definitionsForEach },\n\t\tadd       : { value : definitionsAdd },\n\t\taddHeader : { value : definitionsAddHeader },\n\t\taddToggle : { value : definitionsAddToggle },\n\t\taddList   : { value : definitionsAddList },\n\t\taddRange  : { value : definitionsAddRange },\n\t\tisEmpty   : { value : definitionsIsEmpty },\n\t\thas       : { value : definitionsHas },\n\t\tget       : { value : definitionsGet },\n\t\tdelete    : { value : definitionsDelete }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstory.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Alert, Config, Passage, Scripting, StyleWrapper, Util, Wikifier */\n\nvar Story = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Map of normal passages.\n\tconst _passages = {};\n\n\t// List of script passages.\n\tconst _scripts = [];\n\n\t// List of style passages.\n\tconst _styles = [];\n\n\t// List of widget passages.\n\tconst _widgets = [];\n\n\t// Story title.\n\tlet _title = '';\n\n\t// Story IFID.\n\tlet _ifId = '';\n\n\t// DOM-compatible ID.\n\tlet _domId = '';\n\n\n\t/*******************************************************************************************************************\n\t\tStory Functions.\n\t*******************************************************************************************************************/\n\tfunction storyLoad() {\n\t\tif (DEBUG) { console.log('[Story/storyLoad()]'); }\n\n\t\tconst validationCodeTags = [\n\t\t\t'widget'\n\t\t];\n\t\tconst validationNoCodeTagPassages = [\n\t\t\t'PassageDone',\n\t\t\t'PassageFooter',\n\t\t\t'PassageHeader',\n\t\t\t'PassageReady',\n\t\t\t'StoryAuthor',\n\t\t\t'StoryBanner',\n\t\t\t'StoryCaption',\n\t\t\t'StoryInit',\n\t\t\t'StoryMenu',\n\t\t\t'StoryShare',\n\t\t\t'StorySubtitle'\n\t\t];\n\n\t\tfunction validateStartingPassage(passage) {\n\t\t\tif (passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`starting passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\tfunction validateSpecialPassages(passage) {\n\t\t\tif (validationNoCodeTagPassages.includes(passage.title) && passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`special passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\t// For Twine 1.\n\t\tif (TWINE1) {\n\t\t\t/*\n\t\t\t\tAdditional Twine 1 validation setup.\n\t\t\t*/\n\t\t\tvalidationCodeTags.unshift('script', 'stylesheet');\n\t\t\tvalidationNoCodeTagPassages.push('StoryTitle');\n\n\t\t\tfunction validateTwine1CodePassages(passage) {\n\t\t\t\tconst codeTags  = [...validationCodeTags];\n\t\t\t\tconst foundTags = [];\n\n\t\t\t\tpassage.tags.forEach(tag => {\n\t\t\t\t\tif (codeTags.includes(tag)) {\n\t\t\t\t\t\tfoundTags.push(...codeTags.delete(tag));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (foundTags.length > 1) {\n\t\t\t\t\tthrow new Error(`code passage \"${passage.title}\" contains multiple code tags; invalid: \"${foundTags.sort().join('\", \"')}\"`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = (() => {\n\t\t\t\t/*\n\t\t\t\t\tHandle the Twine 1.4+ Test Play From Here feature.\n\n\t\t\t\t\tWARNING: Do not remove the `String()` wrapper from or change the quote\n\t\t\t\t\tstyle of the `\"START_AT\"` replacement target.  The former is there to\n\t\t\t\t\tkeep UglifyJS from pruning the code into oblivion—i.e. minifying the\n\t\t\t\t\tcode into something broken.  The latter is there because the Twine 1\n\t\t\t\t\tpattern that matches it depends upon the double quotes.\n\n\t\t\t\t*/\n\t\t\t\tconst testPlay = String(\"START_AT\"); // eslint-disable-line quotes\n\n\t\t\t\tif (testPlay !== '') {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tTest play; starting passage: \"${testPlay}\"`); }\n\n\t\t\t\t\tConfig.debug = true;\n\t\t\t\t\treturn testPlay;\n\t\t\t\t}\n\n\t\t\t\t// In the absence of a `testPlay` value, return 'Start'.\n\t\t\t\treturn 'Start';\n\t\t\t})();\n\n\t\t\t/*\n\t\t\t\tProcess the passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\tjQuery('#store-area')\n\t\t\t\t.children(':not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst passage = new Passage($this.attr('tiddler'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (passage.title === Config.passages.start) {\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('stylesheet')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_styles.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('script')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_scripts.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tSet the story title or throw an exception.\n\t\t\t*/\n\t\t\tif (_passages.hasOwnProperty('StoryTitle')) {\n\t\t\t\tconst buf = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(buf, _passages.StoryTitle.processText().trim());\n\t\t\t\t_storySetTitle(buf.textContent);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('cannot find the \"StoryTitle\" special passage');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\n\t\t// For Twine 2.\n\t\telse {\n\t\t\tconst $storydata = jQuery('tw-storydata');\n\t\t\tconst startNode  = $storydata.attr('startnode') || '';\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = null; // no default in Twine 2\n\n\t\t\t/*\n\t\t\t\tProcess story options.\n\n\t\t\t\tNOTE: Currently, the only option of interest is 'debug', so we\n\t\t\t\tsimply use a regular expression to check for it.\n\t\t\t*/\n\t\t\tConfig.debug = /\\bdebug\\b/.test($storydata.attr('options'));\n\n\t\t\t/*\n\t\t\t\tProcess stylesheet passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('style') // alternatively: '[type=\"text/twine-css\"]' or '#twine-user-stylesheet'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_styles.push(new Passage(`tw-user-style-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess script passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('script') // alternatively: '[type=\"text/twine-javascript\"]' or '#twine-user-script'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_scripts.push(new Passage(`tw-user-script-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess normal passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('tw-passagedata:not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst pid     = $this.attr('pid') || '';\n\t\t\t\t\tconst passage = new Passage($this.attr('name'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (pid === startNode && startNode !== '') {\n\t\t\t\t\t\tConfig.passages.start = passage.title;\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tGet the story IFID.\n\t\t\t*/\n\t\t\t_ifId = $storydata.attr('ifid');\n\n\t\t\t/*\n\t\t\t\tSet the story title.\n\n\t\t\t\tFIXME: Maybe `$storydata.attr('name')` should be used instead of `'{{STORY_NAME}}'`?\n\t\t\t*/\n\t\t\t// _storySetTitle($storydata.attr('name'));\n\t\t\t_storySetTitle('{{STORY_NAME}}');\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\t}\n\n\tfunction storyInit() {\n\t\tif (DEBUG) { console.log('[Story/storyInit()]'); }\n\n\t\t/*\n\t\t\tAdd the story styles.\n\t\t*/\n\t\t(() => {\n\t\t\tconst storyStyle = document.createElement('style');\n\n\t\t\tnew StyleWrapper(storyStyle)\n\t\t\t\t.add(_styles.map(style => style.text.trim()).join('\\n'));\n\n\t\t\tjQuery(storyStyle)\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-story',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t});\n\t\t})();\n\n\t\t/*\n\t\t\tEvaluate the story scripts.\n\t\t*/\n\t\tfor (let i = 0; i < _scripts.length; ++i) {\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(_scripts[i].text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_scripts[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tProcess the story widgets.\n\t\t*/\n\t\tfor (let i = 0; i < _widgets.length; ++i) {\n\t\t\ttry {\n\t\t\t\tWikifier.wikifyEval(_widgets[i].processText());\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_widgets[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction _storySetTitle(rawTitle) {\n\t\tif (rawTitle == null) { // lazy equality for null\n\t\t\tthrow new Error('story title must not be null or undefined');\n\t\t}\n\n\t\tconst title = Util.unescape(String(rawTitle)).trim();\n\n\t\tif (title === '') { // lazy equality for null\n\t\t\tthrow new Error('story title must not be empty or consist solely of whitespace');\n\t\t}\n\n\t\tdocument.title = _title = title;\n\n\t\t// TODO: In v3 the `_domId` should be created from a combination of the\n\t\t// `_title` slug and the IFID, if available, to avoid collisions between\n\t\t// stories whose titles generate identical slugs.\n\t\t_domId = Util.slugify(_title);\n\n\t\t// [v2] Protect the `_domId` against being an empty string.\n\t\t//\n\t\t// If `_domId` is empty, attempt a failover.\n\t\tif (_domId === '') {\n\t\t\t// If `_ifId` is not empty, then use it.\n\t\t\tif (_ifId !== '') {\n\t\t\t\t_domId = _ifId;\n\t\t\t}\n\n\t\t\t// Elsewise generate a string from the `_title`'s code points (in hexadecimal).\n\t\t\telse {\n\t\t\t\tfor (let i = 0, len = _title.length; i < len; ++i) {\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(_title, i);\n\t\t\t\t\t_domId += char.codePointAt(0).toString(16);\n\t\t\t\t\ti += end - start;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction storyTitle() {\n\t\treturn _title;\n\t}\n\n\tfunction storyDomId() {\n\t\treturn _domId;\n\t}\n\n\tfunction storyIfId() {\n\t\treturn _ifId;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Functions.\n\t*******************************************************************************************************************/\n\tfunction passagesAdd(passage) {\n\t\tif (!(passage instanceof Passage)) {\n\t\t\tthrow new TypeError('Story.add passage parameter must be an instance of Passage');\n\t\t}\n\n\t\tconst title = passage.title;\n\n\t\tif (!_passages.hasOwnProperty(title)) {\n\t\t\t_passages[title] = passage;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction passagesHas(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t\treturn _passages.hasOwnProperty(String(title));\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.has title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGet(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t/* eslint-disable indent */\n\t\t\t{\n\t\t\t\tconst id = String(title);\n\t\t\t\treturn _passages.hasOwnProperty(id) ? _passages[id] : new Passage(id || '(unknown)');\n\t\t\t}\n\t\t/* eslint-enable indent */\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.get title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGetAllRegular() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Object.assign({}, _passages));\n\t}\n\n\tfunction passagesGetAllScript() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_scripts));\n\t}\n\n\tfunction passagesGetAllStylesheet() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_styles));\n\t}\n\n\tfunction passagesGetAllWidget() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_widgets));\n\t}\n\n\tfunction passagesLookup(key, value  /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\t// Objects (sans `null`).\n\t\t\tif (typeof passage[key] === 'object' && passage[key] !== null) {\n\t\t\t\t// The only object type currently supported is `Array`, since the\n\t\t\t\t// non-method `Passage` object properties currently yield only either\n\t\t\t\t// primitives or arrays.\n\t\t\t\tif (passage[key] instanceof Array && passage[key].some(m => Util.sameValueZero(m, value))) {\n\t\t\t\t\tresults.push(passage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// All other types (incl. `null`).\n\t\t\telse if (Util.sameValueZero(passage[key], value)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\tfunction passagesLookupWith(predicate /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tif (typeof predicate !== 'function') {\n\t\t\tthrow new TypeError('Story.lookupWith predicate parameter must be a function');\n\t\t}\n\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\tif (predicate(passage)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Story Functions.\n\t\tload  : { value : storyLoad },\n\t\tinit  : { value : storyInit },\n\t\ttitle : { get : storyTitle },\n\t\tdomId : { get : storyDomId },\n\t\tifId  : { get : storyIfId },\n\n\t\t// Passage Functions.\n\t\tadd              : { value : passagesAdd },\n\t\thas              : { value : passagesHas },\n\t\tget              : { value : passagesGet },\n\t\tgetAllRegular    : { value : passagesGetAllRegular },\n\t\tgetAllScript     : { value : passagesGetAllScript },\n\t\tgetAllStylesheet : { value : passagesGetAllStylesheet },\n\t\tgetAllWidget     : { value : passagesGetAllWidget },\n\t\tlookup           : { value : passagesLookup },\n\t\tlookupWith       : { value : passagesLookupWith }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tui.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, Has, L10n, Save, Setting, State, Story, Util, Wikifier, Config, errorPrologRegExp,\n\t       settings\n*/\n\nvar UI = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Core.\n\t*******************************************************************************************************************/\n\tfunction uiAssembleLinkList(passage, listEl) {\n\t\tlet list = listEl;\n\n\t\t// Cache the values of `Config.debug` and `Config.cleanupWikifierOutput`,\n\t\t// then disable them during this method's run.\n\t\tconst debugState = Config.debug;\n\t\tconst cleanState = Config.cleanupWikifierOutput;\n\t\tConfig.debug = false;\n\t\tConfig.cleanupWikifierOutput = false;\n\n\t\ttry {\n\t\t\tif (list == null) { // lazy equality for null\n\t\t\t\tlist = document.createElement('ul');\n\t\t\t}\n\n\t\t\t// Wikify the content of the given source passage into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, Story.get(passage).processText().trim());\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\n\t\t\twhile (frag.hasChildNodes()) {\n\t\t\t\tconst node = frag.firstChild;\n\n\t\t\t\t// Create list items for <a>-element nodes.\n\t\t\t\tif (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toUpperCase() === 'A') {\n\t\t\t\t\tconst li = document.createElement('li');\n\t\t\t\t\tlist.appendChild(li);\n\t\t\t\t\tli.appendChild(node);\n\t\t\t\t}\n\n\t\t\t\t// Discard non-<a>-element nodes.\n\t\t\t\telse {\n\t\t\t\t\tfrag.removeChild(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t// Restore the `Config` settings to their original values.\n\t\t\tConfig.cleanupWikifierOutput = cleanState;\n\t\t\tConfig.debug = debugState;\n\t\t}\n\n\t\treturn list;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Built-ins.\n\t*******************************************************************************************************************/\n\tfunction uiOpenAlert(message, /* options, closeFn */ ...args) {\n\t\tjQuery(Dialog.setup(L10n.get('alertTitle'), 'alert'))\n\t\t\t.append(\n\t\t\t\t  `<p>${message}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"alert-ok\" class=\"ui-close\">${L10n.get(['alertOk', 'ok'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t);\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenJumpto(/* options, closeFn */ ...args) {\n\t\tuiBuildJumpto();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenRestart(/* options, closeFn */ ...args) {\n\t\tuiBuildRestart();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSaves(/* options, closeFn */ ...args) {\n\t\tuiBuildSaves();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSettings(/* options, closeFn */ ...args) {\n\t\tuiBuildSettings();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenShare(/* options, closeFn */ ...args) {\n\t\tuiBuildShare();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiBuildAutoload() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildAutoload()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('autoloadTitle'), 'autoload'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('autoloadPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"autoload-ok\" class=\"ui-close\">${L10n.get(['autoloadOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"autoload-cancel\" class=\"ui-close\">${L10n.get(['autoloadCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t// Add an additional delegated click handler for the `.ui-close` elements to handle autoloading.\n\t\tjQuery(document).one('click.autoload', '.ui-close', ev => {\n\t\t\tconst isAutoloadOk = ev.target.id === 'autoload-ok';\n\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\tif (!isAutoloadOk || !Save.autosave.load()) {\n\t\t\t\t\tEngine.play(Config.passages.start);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildJumpto() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildJumpto()]'); }\n\n\t\tconst list = document.createElement('ul');\n\n\t\tjQuery(Dialog.setup(L10n.get('jumptoTitle'), 'jumpto list'))\n\t\t\t.append(list);\n\n\t\tconst expired = State.expired.length;\n\n\t\tfor (let i = State.size - 1; i >= 0; --i) {\n\t\t\tif (i === State.activeIndex) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst passage = Story.get(State.history[i].title);\n\n\t\t\tif (passage && passage.tags.includes('bookmark')) {\n\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t.append(\n\t\t\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t\t\t.ariaClick({ one : true }, (function (idx) {\n\t\t\t\t\t\t\t\treturn () => jQuery(document).one(':dialogclosed', () => Engine.goTo(idx));\n\t\t\t\t\t\t\t})(i))\n\t\t\t\t\t\t\t.addClass('ui-close')\n\t\t\t\t\t\t\t.text(`${L10n.get('jumptoTurn')} ${expired + i + 1}: ${passage.description()}`)\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo(list);\n\t\t\t}\n\t\t}\n\n\t\tif (!list.hasChildNodes()) {\n\t\t\tjQuery(list).append(`<li><a><em>${L10n.get('jumptoUnavailable')}</em></a></li>`);\n\t\t}\n\t}\n\n\tfunction uiBuildRestart() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildRestart()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('restartTitle'), 'restart'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('restartPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"restart-ok\">${L10n.get(['restartOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"restart-cancel\" class=\"ui-close\">${L10n.get(['restartCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.find('#restart-ok')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#restart-ok' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `Engine.restart()` happens after the dialog\n\t\t\t\thas fully closed.  If we did not, then a race condition could occur, causing display\n\t\t\t\tshenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => Engine.restart());\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildSaves() {\n\t\tfunction createActionItem(bId, bClass, bText, bAction) {\n\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t.attr('id', `saves-${bId}`)\n\t\t\t\t.html(bText);\n\n\t\t\tif (bClass) {\n\t\t\t\t$btn.addClass(bClass);\n\t\t\t}\n\n\t\t\tif (bAction) {\n\t\t\t\t$btn.ariaClick(bAction);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('li'))\n\t\t\t\t.append($btn);\n\t\t}\n\n\t\tfunction createSaveList() {\n\t\t\tfunction createButton(bId, bClass, bText, bSlot, bAction) {\n\t\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t\t.attr('id', `saves-${bId}-${bSlot}`)\n\t\t\t\t\t.addClass(bId)\n\t\t\t\t\t.html(bText);\n\n\t\t\t\tif (bClass) {\n\t\t\t\t\t$btn.addClass(bClass);\n\t\t\t\t}\n\n\t\t\t\tif (bAction) {\n\t\t\t\t\tif (bSlot === 'auto') {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelAuto')}`\n\t\t\t\t\t\t}, () => bAction());\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelSlot')} ${bSlot + 1}`\n\t\t\t\t\t\t}, () => bAction(bSlot));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t\t}\n\n\t\t\t\treturn $btn;\n\t\t\t}\n\n\t\t\tconst index  = Save.index();\n\t\t\tconst $tbody = jQuery(document.createElement('tbody'));\n\n\t\t\tif (Save.autosave.ok()) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\tjQuery(document.createElement('b'))\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttitle        : L10n.get('savesLabelAuto'),\n\t\t\t\t\t\t'aria-label' : L10n.get('savesLabelAuto')\n\t\t\t\t\t})\n\t\t\t\t\t.text('A') // '\\u25C6' Black Diamond\n\t\t\t\t\t.appendTo($tdSlot);\n\n\t\t\t\tif (index.autosave) {\n\t\t\t\t\t// Add the load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), 'auto', () => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.autosave.load());\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.autosave.title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.autosave.date\n\t\t\t\t\t\t\t\t? `${new Date(index.autosave.date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto', () => {\n\t\t\t\t\t\t\tSave.autosave.delete();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the disabled load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', null, L10n.get('savesLabelLoad'), 'auto')\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto')\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = index.slots.length; i < iend; ++i) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\t$tdSlot.append(document.createTextNode(i + 1));\n\n\t\t\t\tif (index.slots[i]) {\n\t\t\t\t\t// Add the save & load buttons.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, Save.slots.save),\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), i, slot => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.slots.load(slot));\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.slots[i].title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.slots[i].date\n\t\t\t\t\t\t\t\t? `${new Date(index.slots[i].date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i, slot => {\n\t\t\t\t\t\t\tSave.slots.delete(slot);\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the save button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, Save.slots.save)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i)\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('table'))\n\t\t\t\t.attr('id', 'saves-list')\n\t\t\t\t.append($tbody);\n\t\t}\n\n\t\tif (DEBUG) { console.log('[UI/uiBuildSaves()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('savesTitle'), 'saves'));\n\t\tconst savesOk     = Save.ok();\n\n\t\t// Add saves list.\n\t\tif (savesOk) {\n\t\t\t$dialogBody.append(createSaveList());\n\t\t}\n\n\t\t// Add button bar items (export, import, and clear).\n\t\tif (savesOk || Has.fileAPI) {\n\t\t\tconst $btnBar = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass('buttons')\n\t\t\t\t.appendTo($dialogBody);\n\n\t\t\tif (Has.fileAPI) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'export',\n\t\t\t\t\t'ui-close',\n\t\t\t\t\tL10n.get('savesLabelExport'),\n\t\t\t\t\t() => Save.export()\n\t\t\t\t));\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'import',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelImport'),\n\t\t\t\t\t() => $dialogBody.find('#saves-import-file').trigger('click')\n\t\t\t\t));\n\n\t\t\t\t// Add the hidden `input[type=file]` element which will be triggered by the `#saves-import` button.\n\t\t\t\tjQuery(document.createElement('input'))\n\t\t\t\t\t.css({\n\t\t\t\t\t\tdisplay    : 'block',\n\t\t\t\t\t\tvisibility : 'hidden',\n\t\t\t\t\t\tposition   : 'fixed',\n\t\t\t\t\t\tleft       : '-9999px',\n\t\t\t\t\t\ttop        : '-9999px',\n\t\t\t\t\t\twidth      : '1px',\n\t\t\t\t\t\theight     : '1px'\n\t\t\t\t\t})\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype          : 'file',\n\t\t\t\t\t\tid            : 'saves-import-file',\n\t\t\t\t\t\ttabindex      : -1,\n\t\t\t\t\t\t'aria-hidden' : true\n\t\t\t\t\t})\n\t\t\t\t\t.on('change', ev => {\n\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.import(ev));\n\t\t\t\t\t\tDialog.close();\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t}\n\n\t\t\tif (savesOk) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'clear',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelClear'),\n\t\t\t\t\tSave.autosave.has() || !Save.slots.isEmpty()\n\t\t\t\t\t\t? () => {\n\t\t\t\t\t\t\tSave.clear();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null\n\t\t\t\t));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tuiOpenAlert(L10n.get('savesIncapable'));\n\t\treturn false;\n\t}\n\n\tfunction uiBuildSettings() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildSettings()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('settingsTitle'), 'settings'));\n\n\t\tSetting.forEach(control => {\n\t\t\tif (control.type === Setting.Types.Header) {\n\t\t\t\tconst name     = control.name;\n\t\t\t\tconst id       = Util.slugify(name);\n\t\t\t\tconst $header  = jQuery(document.createElement('div'));\n\t\t\t\tconst $heading = jQuery(document.createElement('h2'));\n\n\t\t\t\t$header\n\t\t\t\t\t.attr('id', `header-body-${id}`)\n\t\t\t\t\t.append($heading)\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t\t$heading\n\t\t\t\t\t.attr('id', `header-heading-${id}`)\n\t\t\t\t\t.wiki(name);\n\n\t\t\t\t// Set up the description, if any.\n\t\t\t\tif (control.desc) {\n\t\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t\t.attr('id', `header-desc-${id}`)\n\t\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t\t.appendTo($header);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst name        = control.name;\n\t\t\tconst id          = Util.slugify(name);\n\t\t\tconst $setting    = jQuery(document.createElement('div'));\n\t\t\tconst $label      = jQuery(document.createElement('label'));\n\t\t\tconst $controlBox = jQuery(document.createElement('div'));\n\t\t\tlet $control;\n\n\t\t\t// Set up the label+control wrapper.\n\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t.append($label)\n\t\t\t\t.append($controlBox)\n\t\t\t\t.appendTo($setting);\n\n\t\t\t// Set up the description, if any.\n\t\t\tif (control.desc) {\n\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t.attr('id', `setting-desc-${id}`)\n\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t.appendTo($setting);\n\t\t\t}\n\n\t\t\t// Set up the label.\n\t\t\t$label\n\t\t\t\t.attr({\n\t\t\t\t\tid  : `setting-label-${id}`,\n\t\t\t\t\tfor : `setting-control-${id}` // must be in sync with $control's ID (see below)\n\t\t\t\t})\n\t\t\t\t.wiki(control.label);\n\n\t\t\t// Set up the control.\n\t\t\tif (settings[name] == null) { // lazy equality for null\n\t\t\t\tsettings[name] = control.default;\n\t\t\t}\n\n\t\t\tswitch (control.type) {\n\t\t\tcase Setting.Types.Toggle:\n\t\t\t\t$control = jQuery(document.createElement('button'));\n\n\t\t\t\tif (settings[name]) {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t}\n\n\t\t\t\t$control.ariaClick(function () {\n\t\t\t\t\tif (settings[name]) {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.removeClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t\t\tsettings[name] = false;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t\t\tsettings[name] = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tSetting.save();\n\n\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\tdefault : control.default\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.List:\n\t\t\t\t$control = jQuery(document.createElement('select'));\n\n\t\t\t\tfor (let i = 0, iend = control.list.length; i < iend; ++i) {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(control.list[i])\n\t\t\t\t\t\t.appendTo($control);\n\t\t\t\t}\n\n\t\t\t\t$control\n\t\t\t\t\t.val(control.list.indexOf(settings[name]))\n\t\t\t\t\t.attr('tabindex', 0)\n\t\t\t\t\t.on('change', function () {\n\t\t\t\t\t\tsettings[name] = control.list[Number(this.value)];\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tlist    : control.list\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.Range:\n\t\t\t\t$control = jQuery(document.createElement('input'));\n\n\t\t\t\t// NOTE: Setting the value with `<jQuery>.val()` can cause odd behavior\n\t\t\t\t// in Edge if it's called before the type is set, so we use the `value`\n\t\t\t\t// content attribute here to dodge the entire issue.\n\t\t\t\t$control\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype     : 'range',\n\t\t\t\t\t\tmin      : control.min,\n\t\t\t\t\t\tmax      : control.max,\n\t\t\t\t\t\tstep     : control.step,\n\t\t\t\t\t\tvalue    : settings[name],\n\t\t\t\t\t\ttabindex : 0\n\t\t\t\t\t})\n\t\t\t\t\t.on('change input', function () {\n\t\t\t\t\t\tsettings[name] = Number(this.value);\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tmin     : control.min,\n\t\t\t\t\t\t\t\tmax     : control.max,\n\t\t\t\t\t\t\t\tstep    : control.step\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.on('keypress', ev => {\n\t\t\t\t\t\tif (ev.which === 13) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$control.trigger('change');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$control\n\t\t\t\t.attr('id', `setting-control-${id}`)\n\t\t\t\t.appendTo($controlBox);\n\n\t\t\t$setting\n\t\t\t\t.attr('id', `setting-body-${id}`)\n\t\t\t\t.appendTo($dialogBody);\n\t\t});\n\n\t\t// Add the button bar.\n\t\t$dialogBody\n\t\t\t.append(\n\t\t\t\t  '<ul class=\"buttons\">'\n\t\t\t\t+     `<li><button id=\"settings-ok\" class=\"ui-close\">${L10n.get(['settingsOk', 'ok'])}</button></li>`\n\t\t\t\t+     `<li><button id=\"settings-reset\">${L10n.get('settingsReset')}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t)\n\t\t\t.find('#settings-reset')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#settings-reset' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `window.location.reload()` happens after the\n\t\t\t\tdialog has fully closed.  If we did not, then a race condition could occur, causing\n\t\t\t\tdisplay shenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\t\tSetting.reset();\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t});\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildShare() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildShare()]'); }\n\n\t\ttry {\n\t\t\tjQuery(Dialog.setup(L10n.get('shareTitle'), 'share list'))\n\t\t\t\t.append(uiAssembleLinkList('StoryShare'));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tconsole.error(ex);\n\t\t\tAlert.error('StoryShare', ex.message);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tUI Functions, Core.\n\t\t*/\n\t\tassembleLinkList : { value : uiAssembleLinkList },\n\n\t\t/*\n\t\t\tUI Functions, Built-ins.\n\t\t*/\n\t\talert         : { value : uiOpenAlert },\n\t\tjumpto        : { value : uiOpenJumpto },\n\t\trestart       : { value : uiOpenRestart },\n\t\tsaves         : { value : uiOpenSaves },\n\t\tsettings      : { value : uiOpenSettings },\n\t\tshare         : { value : uiOpenShare },\n\t\tbuildAutoload : { value : uiBuildAutoload },\n\t\tbuildJumpto   : { value : uiBuildJumpto },\n\t\tbuildRestart  : { value : uiBuildRestart },\n\t\tbuildSaves    : { value : uiBuildSaves },\n\t\tbuildSettings : { value : uiBuildSettings },\n\t\tbuildShare    : { value : uiBuildShare },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\t// `UIBar` methods.\n\t\t/* global UIBar */\n\t\tstow                     : { value : () => UIBar.stow() },\n\t\tunstow                   : { value : () => UIBar.unstow() },\n\t\tsetStoryElements         : { value : () => UIBar.update() },\n\t\t// `Dialog` methods.\n\t\tisOpen                   : { value : (...args) => Dialog.isOpen(...args) },\n\t\tbody                     : { value : () => Dialog.body() },\n\t\tsetup                    : { value : (...args) => Dialog.setup(...args) },\n\t\taddClickHandler          : { value : (...args) => Dialog.addClickHandler(...args) },\n\t\topen                     : { value : (...args) => Dialog.open(...args) },\n\t\tclose                    : { value : (...args) => Dialog.close(...args) },\n\t\tresize                   : { value : () => Dialog.resize() },\n\t\t// Deprecated method names.\n\t\tbuildDialogAutoload      : { value : uiBuildAutoload },\n\t\tbuildDialogJumpto        : { value : uiBuildJumpto },\n\t\tbuildDialogRestart       : { value : uiBuildRestart },\n\t\tbuildDialogSaves         : { value : uiBuildSaves },\n\t\tbuildDialogSettings      : { value : uiBuildSettings },\n\t\tbuildDialogShare         : { value : uiBuildShare },\n\t\tbuildLinkListFromPassage : { value : uiAssembleLinkList }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tuibar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, L10n, Setting, State, Story, UI, Config, setDisplayTitle, setPageElement\n*/\n\nvar UIBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// UI bar element cache.\n\tlet _$uiBar = null;\n\n\n\t/*******************************************************************************\n\t\tUI Bar Functions.\n\t*******************************************************************************/\n\n\tfunction uiBarDestroy() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarDestroy()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Hide the UI bar.\n\t\t_$uiBar.hide();\n\n\t\t// Remove its namespaced events.\n\t\tjQuery(document).off('.ui-bar');\n\n\t\t// Remove its styles.\n\t\tjQuery(document.head).find('#style-ui-bar').remove();\n\n\t\t// Remove it from the DOM.\n\t\t_$uiBar.remove();\n\n\t\t// Drop the reference to the element.\n\t\t_$uiBar = null;\n\t}\n\n\tfunction uiBarHide() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.hide();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarInit() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarInit()]'); }\n\n\t\tif (document.getElementById('ui-bar')) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Generate the UI bar elements.\n\t\tconst $elems = (() => {\n\t\t\tconst toggleLabel   = L10n.get('uiBarToggle');\n\t\t\tconst backwardLabel = L10n.get('uiBarBackward');\n\t\t\tconst jumptoLabel   = L10n.get('uiBarJumpto');\n\t\t\tconst forwardLabel  = L10n.get('uiBarForward');\n\n\t\t\treturn jQuery(document.createDocumentFragment())\n\t\t\t\t.append(\n\t\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t\t  '<div id=\"ui-bar\">'\n\t\t\t\t\t+     '<div id=\"ui-bar-tray\">'\n\t\t\t\t\t+         `<button id=\"ui-bar-toggle\" tabindex=\"0\" title=\"${toggleLabel}\" aria-label=\"${toggleLabel}\"></button>`\n\t\t\t\t\t+         '<div id=\"ui-bar-history\">'\n\t\t\t\t\t+             `<button id=\"history-backward\" tabindex=\"0\" title=\"${backwardLabel}\" aria-label=\"${backwardLabel}\">\\uE821</button>`\n\t\t\t\t\t+             `<button id=\"history-jumpto\" tabindex=\"0\" title=\"${jumptoLabel}\" aria-label=\"${jumptoLabel}\">\\uE839</button>`\n\t\t\t\t\t+             `<button id=\"history-forward\" tabindex=\"0\" title=\"${forwardLabel}\" aria-label=\"${forwardLabel}\">\\uE822</button>`\n\t\t\t\t\t+         '</div>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+     '<div id=\"ui-bar-body\">'\n\t\t\t\t\t+         '<header id=\"title\" role=\"banner\">'\n\t\t\t\t\t+             '<div id=\"story-banner\"></div>'\n\t\t\t\t\t+             '<h1 id=\"story-title\"></h1>'\n\t\t\t\t\t+             '<div id=\"story-subtitle\"></div>'\n\t\t\t\t\t+             '<div id=\"story-title-separator\"></div>'\n\t\t\t\t\t+             '<p id=\"story-author\"></p>'\n\t\t\t\t\t+         '</header>'\n\t\t\t\t\t+         '<div id=\"story-caption\"></div>'\n\t\t\t\t\t+         '<nav id=\"menu\" role=\"navigation\">'\n\t\t\t\t\t+             '<ul id=\"menu-story\"></ul>'\n\t\t\t\t\t+             '<ul id=\"menu-core\">'\n\t\t\t\t\t+                 `<li id=\"menu-item-saves\"><a tabindex=\"0\">${L10n.get('savesTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-settings\"><a tabindex=\"0\">${L10n.get('settingsTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-restart\"><a tabindex=\"0\">${L10n.get('restartTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-share\"><a tabindex=\"0\">${L10n.get('shareTitle')}</a></li>`\n\t\t\t\t\t+             '</ul>'\n\t\t\t\t\t+         '</nav>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+ '</div>'\n\t\t\t\t\t/* eslint-enable max-len */\n\t\t\t\t);\n\t\t})();\n\n\t\t/*\n\t\t\tCache the UI bar element, since its going to be used often.\n\n\t\t\tNOTE: We rewrap the element itself, rather than simply using the result\n\t\t\tof `find()`, so that we cache an uncluttered jQuery-wrapper (i.e. `context`\n\t\t\trefers to the element and there is no `prevObject`).\n\t\t*/\n\t\t_$uiBar = jQuery($elems.find('#ui-bar').get(0));\n\n\t\t// Insert the UI bar elements into the page before the main script.\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\n\t\t// Set up the UI bar's global event handlers.\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history-backward/-forward buttons.\n\t\t\t.on(':historyupdate.ui-bar', (($backward, $forward) => () => {\n\t\t\t\t$backward.ariaDisabled(State.length < 2);\n\t\t\t\t$forward.ariaDisabled(State.length === State.size);\n\t\t\t})(jQuery('#history-backward'), jQuery('#history-forward')));\n\t}\n\n\tfunction uiBarIsHidden() {\n\t\treturn _$uiBar && _$uiBar.css('display') === 'none';\n\t}\n\n\tfunction uiBarIsStowed() {\n\t\treturn _$uiBar && _$uiBar.hasClass('stowed');\n\t}\n\n\tfunction uiBarShow() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.show();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarStart() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarStart()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the #ui-bar's initial state.\n\t\tif (\n\t\t\ttypeof Config.ui.stowBarInitially === 'boolean'\n\t\t\t\t? Config.ui.stowBarInitially\n\t\t\t\t: jQuery(window).width() <= Config.ui.stowBarInitially\n\t\t) {\n\t\t\tuiBarStow(true);\n\t\t}\n\n\t\t// Set up the #ui-bar-toggle and #ui-bar-history widgets.\n\t\tjQuery('#ui-bar-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('uiBarToggle')\n\t\t\t}, () => _$uiBar.toggleClass('stowed'));\n\n\t\tif (Config.history.controls) {\n\t\t\tjQuery('#history-backward')\n\t\t\t\t.ariaDisabled(State.length < 2)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarBackward')\n\t\t\t\t}, () => Engine.backward());\n\n\t\t\tif (Story.lookup('tags', 'bookmark').length > 0) {\n\t\t\t\tjQuery('#history-jumpto')\n\t\t\t\t\t.ariaClick({\n\t\t\t\t\t\tlabel : L10n.get('uiBarJumpto')\n\t\t\t\t\t}, () => UI.jumpto());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery('#history-jumpto').remove();\n\t\t\t}\n\n\t\t\tjQuery('#history-forward')\n\t\t\t\t.ariaDisabled(State.length === State.size)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarForward')\n\t\t\t\t}, () => Engine.forward());\n\t\t}\n\t\telse {\n\t\t\tjQuery('#ui-bar-history').remove();\n\t\t}\n\n\t\t// Set up the story display title.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\telse {\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tsetPageElement('story-title', 'StoryTitle', Story.title);\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tjQuery('#story-title').text(Story.title);\n\t\t\t}\n\t\t}\n\n\t\t// Set up the dynamic page elements.\n\t\tif (!Story.has('StoryCaption')) {\n\t\t\tjQuery('#story-caption').remove();\n\t\t}\n\n\t\tif (!Story.has('StoryMenu')) {\n\t\t\tjQuery('#menu-story').remove();\n\t\t}\n\n\t\tif (!Config.ui.updateStoryElements) {\n\t\t\t// We only need to set the story elements here if `Config.ui.updateStoryElements`\n\t\t\t// is falsy, since otherwise they will be set by `Engine.play()`.\n\t\t\tuiBarUpdate();\n\t\t}\n\n\t\t// Set up the Saves menu item.\n\t\tjQuery('#menu-item-saves a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildSaves();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('savesTitle'));\n\n\t\t// Set up the Settings menu item.\n\t\tif (!Setting.isEmpty()) {\n\t\t\tjQuery('#menu-item-settings a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildSettings();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('settingsTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-settings').remove();\n\t\t}\n\n\t\t// Set up the Restart menu item.\n\t\tjQuery('#menu-item-restart a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildRestart();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('restartTitle'));\n\n\t\t// Set up the Share menu item.\n\t\tif (Story.has('StoryShare')) {\n\t\t\tjQuery('#menu-item-share a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildShare();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('shareTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-share').remove();\n\t\t}\n\t}\n\n\tfunction uiBarStow(noAnimation) {\n\t\tif (_$uiBar && !_$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.addClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUnstow(noAnimation) {\n\t\tif (_$uiBar && _$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.removeClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUpdate() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarUpdate()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the (non-navigation) dynamic page elements.\n\t\tsetPageElement('story-banner', 'StoryBanner');\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\tsetPageElement('story-subtitle', 'StorySubtitle');\n\t\tsetPageElement('story-author', 'StoryAuthor');\n\t\tsetPageElement('story-caption', 'StoryCaption');\n\n\t\t// Set up the #menu-story items.\n\t\tconst menuStory = document.getElementById('menu-story');\n\n\t\tif (menuStory !== null) {\n\t\t\tjQuery(menuStory).empty();\n\n\t\t\tif (Story.has('StoryMenu')) {\n\t\t\t\ttry {\n\t\t\t\t\tUI.assembleLinkList('StoryMenu', menuStory);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tconsole.error(ex);\n\t\t\t\t\tAlert.error('StoryMenu', ex.message);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tdestroy  : { value : uiBarDestroy },\n\t\thide     : { value : uiBarHide },\n\t\tinit     : { value : uiBarInit },\n\t\tisHidden : { value : uiBarIsHidden },\n\t\tisStowed : { value : uiBarIsStowed },\n\t\tshow     : { value : uiBarShow },\n\t\tstart    : { value : uiBarStart },\n\t\tstow     : { value : uiBarStow },\n\t\tunstow   : { value : uiBarUnstow },\n\t\tupdate   : { value : uiBarUpdate },\n\n\t\t// Legacy Functions.\n\t\tsetStoryElements : { value : uiBarUpdate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tdebugbar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal DebugView, Engine, L10n, Patterns, State, Util, session\n*/\n\nvar DebugBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _variableRe   = new RegExp(`^${Patterns.variable}$`);\n\tconst _numericKeyRe = /^\\d+$/;\n\tconst _watchList    = [];\n\tlet _$debugBar   = null;\n\tlet _$watchBody  = null;\n\tlet _$watchList  = null;\n\tlet _$turnSelect = null;\n\tlet _stowed      = true;\n\n\n\t/*******************************************************************************************************************\n\t\tDebug Bar Functions.\n\t*******************************************************************************************************************/\n\tfunction debugBarInit() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarInit()]'); }\n\n\t\t/*\n\t\t\tGenerate the debug bar elements and append them to the `<body>`.\n\t\t*/\n\t\tconst barToggleLabel   = L10n.get('debugBarToggle');\n\t\tconst watchAddLabel    = L10n.get('debugBarAddWatch');\n\t\tconst watchAllLabel    = L10n.get('debugBarWatchAll');\n\t\tconst watchNoneLabel   = L10n.get('debugBarWatchNone');\n\t\tconst watchToggleLabel = L10n.get('debugBarWatchToggle');\n\t\tconst viewsToggleLabel = L10n.get('debugBarViewsToggle');\n\n\t\tjQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"debug-bar\">'\n\t\t\t\t+     '<div id=\"debug-bar-watch\">'\n\t\t\t\t+         `<div>${L10n.get('debugBarNoWatches')}</div>>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-toggle\" tabindex=\"0\" title=\"${watchToggleLabel}\" aria-label=\"${watchToggleLabel}\">${L10n.get('debugBarLabelWatch')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-watch-label\" for=\"debug-bar-watch-input\">${L10n.get('debugBarLabelAdd')}</label>`\n\t\t\t\t+         '<input id=\"debug-bar-watch-input\" name=\"debug-bar-watch-input\" type=\"text\" list=\"debug-bar-watch-list\" tabindex=\"0\">'\n\t\t\t\t+         '<datalist id=\"debug-bar-watch-list\" aria-hidden=\"true\" hidden=\"hidden\"></datalist>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-add\" tabindex=\"0\" title=\"${watchAddLabel}\" aria-label=\"${watchAddLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-all\" tabindex=\"0\" title=\"${watchAllLabel}\" aria-label=\"${watchAllLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-none\" tabindex=\"0\" title=\"${watchNoneLabel}\" aria-label=\"${watchNoneLabel}\"></button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-views-toggle\" tabindex=\"0\" title=\"${viewsToggleLabel}\" aria-label=\"${viewsToggleLabel}\">${L10n.get('debugBarLabelViews')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-turn-label\" for=\"debug-bar-turn-select\">${L10n.get('debugBarLabelTurn')}</label>`\n\t\t\t\t+         '<select id=\"debug-bar-turn-select\" tabindex=\"0\"></select>'\n\t\t\t\t+     '</div>'\n\t\t\t\t+     `<button id=\"debug-bar-toggle\" tabindex=\"0\" title=\"${barToggleLabel}\" aria-label=\"${barToggleLabel}\"></button>`\n\t\t\t\t+ '</div>'\n\t\t\t\t+ '<div id=\"debug-bar-hint\"></div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.appendTo('body');\n\n\t\t/*\n\t\t\tCache various oft used elements.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$debugBar   = jQuery('#debug-bar');\n\t\t_$watchBody  = jQuery(_$debugBar.find('#debug-bar-watch').get(0));\n\t\t_$watchList  = jQuery(_$debugBar.find('#debug-bar-watch-list').get(0));\n\t\t_$turnSelect = jQuery(_$debugBar.find('#debug-bar-turn-select').get(0));\n\n\t\tconst $barToggle   = jQuery(_$debugBar.find('#debug-bar-toggle').get(0));\n\t\tconst $watchToggle = jQuery(_$debugBar.find('#debug-bar-watch-toggle').get(0));\n\t\tconst $watchInput  = jQuery(_$debugBar.find('#debug-bar-watch-input').get(0));\n\t\tconst $watchAdd    = jQuery(_$debugBar.find('#debug-bar-watch-add').get(0));\n\t\tconst $watchAll    = jQuery(_$debugBar.find('#debug-bar-watch-all').get(0));\n\t\tconst $watchNone   = jQuery(_$debugBar.find('#debug-bar-watch-none').get(0));\n\t\tconst $viewsToggle = jQuery(_$debugBar.find('#debug-bar-views-toggle').get(0));\n\n\t\t/*\n\t\t\tSet up the debug bar's local event handlers.\n\t\t*/\n\t\t$barToggle\n\t\t\t.ariaClick(debugBarToggle);\n\t\t$watchToggle\n\t\t\t.ariaClick(debugBarWatchToggle);\n\t\t$watchInput\n\t\t\t.on(':addwatch', function () {\n\t\t\t\tdebugBarWatchAdd(this.value.trim());\n\t\t\t\tthis.value = '';\n\t\t\t})\n\t\t\t.on('keypress', ev => {\n\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t$watchInput.trigger(':addwatch');\n\t\t\t\t}\n\t\t\t});\n\t\t$watchAdd\n\t\t\t.ariaClick(() => $watchInput.trigger(':addwatch'));\n\t\t$watchAll\n\t\t\t.ariaClick(debugBarWatchAddAll);\n\t\t$watchNone\n\t\t\t.ariaClick(debugBarWatchClear);\n\t\t_$turnSelect\n\t\t\t.on('change', function () {\n\t\t\t\tEngine.goTo(Number(this.value));\n\t\t\t});\n\t\t$viewsToggle\n\t\t\t.ariaClick(() => {\n\t\t\t\tDebugView.toggle();\n\t\t\t\t_updateSession();\n\t\t\t});\n\n\t\t/*\n\t\t\tSet up the debug bar's global event handlers.\n\t\t*/\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history select.\n\t\t\t.on(':historyupdate.debug-bar', _updateTurnSelect)\n\t\t\t// Set up a handler for the variables watch.\n\t\t\t.on(':passageend.debug-bar', () => {\n\t\t\t\t_updateWatchBody();\n\t\t\t\t_updateWatchList();\n\t\t\t})\n\t\t\t// Set up a handler for engine resets to clear the active debug session.\n\t\t\t.on(':enginerestart.debug-bar', _clearSession);\n\n\t\t/*\n\t\t\tInitially enable debug views if there's no active debug session.\n\t\t*/\n\t\tif (!_hasSession()) {\n\t\t\tDebugView.enable();\n\t\t}\n\t}\n\n\tfunction debugBarStart() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarStart()]'); }\n\n\t\t// Attempt to restore an existing session.\n\t\t_restoreSession();\n\n\t\t// Update the UI.\n\t\t_updateBar();\n\t\t_updateTurnSelect();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t}\n\n\tfunction debugBarIsStowed() {\n\t\treturn _stowed;\n\t}\n\n\tfunction debugBarStow() {\n\t\t_debugBarStowNoUpdate();\n\t\t_stowed = true;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarUnstow() {\n\t\t_debugBarUnstowNoUpdate();\n\t\t_stowed = false;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarToggle() {\n\t\tif (_stowed) {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarStow();\n\t\t}\n\t}\n\n\tfunction debugBarWatchAdd(varName) {\n\t\tif (!_variableRe.test(varName)) {\n\t\t\treturn;\n\t\t}\n\n\t\t_watchList.pushUnique(varName);\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchAddAll() {\n\t\tObject.keys(State.variables).map(name => _watchList.pushUnique(`$${name}`));\n\t\tObject.keys(State.temporary).map(name => _watchList.pushUnique(`_${name}`));\n\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchClear() {\n\t\tfor (let i = _watchList.length - 1; i >= 0; --i) {\n\t\t\t_watchList.pop();\n\t\t}\n\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDelete(varName) {\n\t\t_watchList.delete(varName);\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDisable() {\n\t\t_debugBarWatchDisableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchEnable() {\n\t\t_debugBarWatchEnableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchIsEnabled() {\n\t\treturn !_$watchBody.attr('hidden');\n\t}\n\n\tfunction debugBarWatchToggle() {\n\t\tif (_$watchBody.attr('hidden')) {\n\t\t\tdebugBarWatchEnable();\n\t\t}\n\t\telse {\n\t\t\tdebugBarWatchDisable();\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _debugBarStowNoUpdate() {\n\t\t_$debugBar.css('right', `-${_$debugBar.outerWidth()}px`);\n\t}\n\n\tfunction _debugBarUnstowNoUpdate() {\n\t\t_$debugBar.css('right', 0);\n\t}\n\n\tfunction _debugBarWatchDisableNoUpdate() {\n\t\t_$watchBody.attr({\n\t\t\t'aria-hidden' : true,\n\t\t\thidden        : 'hidden'\n\t\t});\n\t}\n\n\tfunction _debugBarWatchEnableNoUpdate() {\n\t\t_$watchBody.removeAttr('aria-hidden hidden');\n\t}\n\n\tfunction _clearSession() {\n\t\tsession.delete('debugState');\n\t}\n\n\tfunction _hasSession() {\n\t\treturn session.has('debugState');\n\t}\n\n\tfunction _restoreSession() {\n\t\tif (!_hasSession()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst debugState = session.get('debugState');\n\n\t\t_stowed = debugState.stowed;\n\n\t\t_watchList.push(...debugState.watchList);\n\n\t\tif (debugState.watchEnabled) {\n\t\t\t_debugBarWatchEnableNoUpdate();\n\t\t}\n\t\telse {\n\t\t\t_debugBarWatchDisableNoUpdate();\n\t\t}\n\n\t\tif (debugState.viewsEnabled) {\n\t\t\tDebugView.enable();\n\t\t}\n\t\telse {\n\t\t\tDebugView.disable();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction _updateSession() {\n\t\tsession.set('debugState', {\n\t\t\tstowed       : _stowed,\n\t\t\twatchList    : _watchList,\n\t\t\twatchEnabled : debugBarWatchIsEnabled(),\n\t\t\tviewsEnabled : DebugView.isEnabled()\n\t\t});\n\t}\n\n\tfunction _updateBar() {\n\t\tif (_stowed) {\n\t\t\tdebugBarStow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t}\n\n\tfunction _updateWatchBody() {\n\t\tif (_watchList.length === 0) {\n\t\t\t_$watchBody\n\t\t\t\t.empty()\n\t\t\t\t.append(`<div>${L10n.get('debugBarNoWatches')}</div>`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst delLabel = L10n.get('debugBarDeleteWatch');\n\t\tconst $table   = jQuery(document.createElement('table'));\n\t\tconst $tbody   = jQuery(document.createElement('tbody'));\n\n\t\tfor (let i = 0, len = _watchList.length; i < len; ++i) {\n\t\t\tconst varName = _watchList[i];\n\t\t\tconst varKey  = varName.slice(1);\n\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\tconst $row    = jQuery(document.createElement('tr'));\n\t\t\tconst $delBtn = jQuery(document.createElement('button'));\n\t\t\tconst $code   = jQuery(document.createElement('code'));\n\n\t\t\t$delBtn\n\t\t\t\t.addClass('watch-delete')\n\t\t\t\t.attr('data-name', varName)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tone   : true,\n\t\t\t\t\tlabel : delLabel\n\t\t\t\t}, () => debugBarWatchDelete(varName));\n\t\t\t$code\n\t\t\t\t.text(_toWatchString(store[varKey]));\n\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($delBtn)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.text(varName)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($code)\n\t\t\t\t.appendTo($row);\n\t\t\t$row\n\t\t\t\t.appendTo($tbody);\n\t\t}\n\n\t\t$table\n\t\t\t.append($tbody);\n\t\t_$watchBody\n\t\t\t.empty()\n\t\t\t.append($table);\n\t}\n\n\tfunction _updateWatchList() {\n\t\tconst svn = Object.keys(State.variables);\n\t\tconst tvn = Object.keys(State.temporary);\n\n\t\tif (svn.length === 0 && tvn.length === 0) {\n\t\t\t_$watchList.empty();\n\t\t\treturn;\n\t\t}\n\n\t\tconst names   = [...svn.map(name => `$${name}`), ...tvn.map(name => `_${name}`)].sort();\n\t\tconst options = document.createDocumentFragment();\n\n\t\tnames.delete(_watchList);\n\n\t\tfor (let i = 0, len = names.length; i < len; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(names[i])\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$watchList\n\t\t\t.empty()\n\t\t\t.append(options);\n\t}\n\n\tfunction _updateTurnSelect() {\n\t\tconst histLen = State.size;\n\t\tconst expLen  = State.expired.length;\n\t\tconst options = document.createDocumentFragment();\n\n\t\tfor (let i = 0; i < histLen; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(i)\n\t\t\t\t.text(`${expLen + i + 1}. ${Util.escape(State.history[i].title)}`)\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$turnSelect\n\t\t\t.empty()\n\t\t\t.ariaDisabled(histLen < 2)\n\t\t\t.append(options)\n\t\t\t.val(State.activeIndex);\n\t}\n\n\tfunction _toWatchString(value) {\n\t\t/*\n\t\t\tHandle the `null` primitive.\n\t\t*/\n\t\tif (value === null) {\n\t\t\treturn 'null';\n\t\t}\n\n\t\t/*\n\t\t\tHandle the rest of the primitives and functions.\n\t\t*/\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn 'NaN';\n\t\t\t}\n\t\t\telse if (!Number.isFinite(value)) {\n\t\t\t\treturn 'Infinity';\n\t\t\t}\n\t\t\t/* falls through */\n\t\tcase 'boolean':\n\t\tcase 'symbol':\n\t\tcase 'undefined':\n\t\t\treturn String(value);\n\n\t\tcase 'string':\n\t\t\treturn JSON.stringify(value);\n\n\t\tcase 'function':\n\t\t\treturn 'Function';\n\t\t}\n\n\t\tconst objType = Util.toStringTag(value);\n\n\t\t// /*\n\t\t// \tHandle instances of the primitive exemplar objects (`Boolean`, `Number`, `String`).\n\t\t// */\n\t\t// if (objType === 'Boolean') {\n\t\t// \treturn `Boolean\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'Number') {\n\t\t// \treturn `Number\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'String') {\n\t\t// \treturn `String\\u202F{\"${String(value)}\"}`;\n\t\t// }\n\n\t\t/*\n\t\t\tHandle `Date` objects.\n\t\t*/\n\t\tif (objType === 'Date') {\n\t\t\t// return `Date\\u202F${value.toISOString()}`;\n\t\t\treturn `Date\\u202F{${value.toLocaleString()}}`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `RegExp` objects.\n\t\t*/\n\t\tif (objType === 'RegExp') {\n\t\t\treturn `RegExp\\u202F${value.toString()}`;\n\t\t}\n\n\t\tconst result = [];\n\n\t\t/*\n\t\t\tHandle `Array` & `Set` objects.\n\t\t*/\n\t\tif (value instanceof Array || value instanceof Set) {\n\t\t\tconst list = value instanceof Array ? value : Array.from(value);\n\n\t\t\t// own numeric properties\n\t\t\t// NOTE: Do not use `<Array>.forEach()` here as it skips undefined members.\n\t\t\tfor (let i = 0, len = list.length; i < len; ++i) {\n\t\t\t\tresult.push(list.hasOwnProperty(i) ? _toWatchString(list[i]) : '<empty>');\n\t\t\t}\n\n\t\t\t// own enumerable non-numeric expando properties\n\t\t\tObject.keys(list)\n\t\t\t\t.filter(key => !_numericKeyRe.test(key))\n\t\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(list[key])}`));\n\n\t\t\treturn `${objType}(${list.length})\\u202F[${result.join(', ')}]`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `Map` objects.\n\t\t*/\n\t\tif (value instanceof Map) {\n\t\t\tvalue.forEach((val, key) => result.push(`${_toWatchString(key)} \\u2192 ${_toWatchString(val)}`));\n\n\t\t\treturn `${objType}(${value.size})\\u202F{${result.join(', ')}}`;\n\t\t}\n\n\t\t/*\n\t\t\tGeneral object handling.\n\t\t*/\n\t\t// own enumerable properties\n\t\tObject.keys(value)\n\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(value[key])}`));\n\n\t\treturn `${objType}\\u202F{${result.join(', ')}}`;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tDebug Bar Functions.\n\t\t*/\n\t\tinit     : { value : debugBarInit },\n\t\tisStowed : { value : debugBarIsStowed },\n\t\tstart    : { value : debugBarStart },\n\t\tstow     : { value : debugBarStow },\n\t\ttoggle   : { value : debugBarToggle },\n\t\tunstow   : { value : debugBarUnstow },\n\n\t\t/*\n\t\t\tWatch Functions.\n\t\t*/\n\t\twatch : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd       : { value : debugBarWatchAdd },\n\t\t\t\tall       : { value : debugBarWatchAddAll },\n\t\t\t\tclear     : { value : debugBarWatchClear },\n\t\t\t\tdelete    : { value : debugBarWatchDelete },\n\t\t\t\tdisable   : { value : debugBarWatchDisable },\n\t\t\t\tenable    : { value : debugBarWatchEnable },\n\t\t\t\tisEnabled : { value : debugBarWatchIsEnabled },\n\t\t\t\ttoggle    : { value : debugBarWatchToggle }\n\t\t\t}))\n\t\t}\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tloadscreen.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Engine */\n\nvar LoadScreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Locks collection.\n\tconst _locks = new Set();\n\n\t// Auto-incrementing lock ID.\n\tlet _autoId = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tLoadScreen Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize management of the loading screen.\n\t*/\n\tfunction loadScreenInit() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenInit()]'); }\n\n\t\t// Add a `readystatechange` listener for hiding/showing the loading screen.\n\t\tjQuery(document).on('readystatechange.SugarCube', () => {\n\t\t\tif (DEBUG) { console.log(`[LoadScreen/<readystatechange>] document.readyState: \"${document.readyState}\"; locks(${_locks.size}):`, _locks); }\n\n\t\t\tif (_locks.size > 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The value of `document.readyState` may be: 'loading' -> 'interactive' -> 'complete'.\n\t\t\t// Though, to reach this point, it must already be in, at least, the 'interactive' state.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\tif (jQuery(document.documentElement).attr('data-init') === 'loading') {\n\t\t\t\t\tif (Config.loadDelay > 0) {\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (_locks.size === 0) {\n\t\t\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, Math.max(Engine.minDomActionDelay, Config.loadDelay));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadScreenShow();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\tClear the loading screen.\n\t*/\n\tfunction loadScreenClear() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenClear()]'); }\n\n\t\t// Remove the event listener.\n\t\tjQuery(document).off('readystatechange.SugarCube');\n\n\t\t// Clear all locks.\n\t\t_locks.clear();\n\n\t\t// Hide the loading screen.\n\t\tloadScreenHide();\n\t}\n\n\t/*\n\t\tHide the loading screen.\n\t*/\n\tfunction loadScreenHide() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenHide()]'); }\n\n\t\tjQuery(document.documentElement).removeAttr('data-init');\n\t}\n\n\t/*\n\t\tShow the loading screen.\n\t*/\n\tfunction loadScreenShow() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenShow()]'); }\n\n\t\tjQuery(document.documentElement).attr('data-init', 'loading');\n\t}\n\n\t/*\n\t\tReturns a new lock ID after locking and showing the loading screen.\n\t*/\n\tfunction loadScreenLock() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenLock()]'); }\n\n\t\t++_autoId;\n\t\t_locks.add(_autoId);\n\n\t\tif (DEBUG) { console.log(`\\tacquired loading screen lock; id: ${_autoId}`); }\n\n\t\tloadScreenShow();\n\t\treturn _autoId;\n\t}\n\n\t/*\n\t\tRemove the lock associated with the given lock ID and, if no locks remain,\n\t\ttrigger a `readystatechange` event.\n\t*/\n\tfunction loadScreenUnlock(id) {\n\t\tif (DEBUG) { console.log(`[LoadScreen/loadScreenUnlock(id: ${id})]`); }\n\n\t\tif (id == null) { // lazy equality for null\n\t\t\tthrow new Error('LoadScreen.unlock called with a null or undefined ID');\n\t\t}\n\n\t\tif (_locks.has(id)) {\n\t\t\t_locks.delete(id);\n\n\t\t\tif (DEBUG) { console.log(`\\treleased loading screen lock; id: ${id}`); }\n\t\t}\n\n\t\tif (_locks.size === 0) {\n\t\t\tjQuery(document).trigger('readystatechange');\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : loadScreenInit },\n\t\tclear  : { value : loadScreenClear },\n\t\thide   : { value : loadScreenHide },\n\t\tshow   : { value : loadScreenShow },\n\t\tlock   : { value : loadScreenLock },\n\t\tunlock : { value : loadScreenUnlock }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsugarcube.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Browser, Config, Dialog, Engine, Fullscreen, Has, LoadScreen, SimpleStore, L10n, Macro, Passage,\n\t       Save, Scripting, Setting, SimpleAudio, State, Story, UI, UIBar, DebugBar, Util, Visibility, Wikifier\n*/\n/* eslint-disable no-var */\n\n/*\n\tVersion object.\n*/\nvar version = Object.freeze({\n\ttitle      : 'SugarCube',\n\tmajor      : 2,\n\tminor      : 33,\n\tpatch      : 1,\n\tprerelease : null,\n\tbuild      : 52,\n\tdate       : new Date(\"2020-08-01T12:25:33.326Z\"),\n\t/* legacy */\n\textensions : {},\n\t/* /legacy */\n\n\ttoString() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.major}.${this.minor}.${this.patch}${prerelease}+${this.build}`;\n\t},\n\n\tshort() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.title} (v${this.major}.${this.minor}.${this.patch}${prerelease})`;\n\t},\n\n\tlong() {\n\t\t'use strict';\n\n\t\treturn `${this.title} v${this.toString()} (${this.date.toUTCString()})`;\n\t}\n});\n\n/* eslint-disable no-unused-vars */\n/*\n\tInternal variables.\n*/\n// Temporary state object.\nvar TempState = {};\n\n// Legacy macros object.\nvar macros = {};\n\n// Post-display task callbacks object.\nvar postdisplay = {};\n\n// Post-render task callbacks object.\nvar postrender = {};\n\n// Pre-display task callbacks object.\nvar predisplay = {};\n\n// Pre-history task callbacks object.\nvar prehistory = {};\n\n// Pre-render task callbacks object.\nvar prerender = {};\n\n// Session storage manager object.\nvar session = null;\n\n// Settings object.\nvar settings = {};\n\n// Setup object.\nvar setup = {};\n\n// Persistant storage manager object.\nvar storage = null;\n\n/*\n\tLegacy aliases.\n*/\nvar browser       = Browser;\nvar config        = Config;\nvar has           = Has;\nvar History       = State;\nvar state         = State;\nvar tale          = Story;\nvar TempVariables = State.temporary;\n/* eslint-enable no-unused-vars */\n\n/*\n\tGlobal `SugarCube` object.  Allows scripts to detect if they're running in SugarCube by\n\ttesting for the object (e.g. `\"SugarCube\" in window`) and contains exported identifiers\n\tfor debugging purposes.\n*/\nwindow.SugarCube = {};\n\n/*\n\tMain function, entry point for the story.\n*/\njQuery(() => {\n\t'use strict';\n\n\tif (DEBUG) { console.log('[SugarCube/main()] Document loaded; beginning startup.'); }\n\n\t/*\n\t\tWARNING!\n\n\t\tThe ordering of the code within this function is critically important,\n\t\tso be careful when mucking around with it.\n\t*/\n\ttry {\n\t\t// Acquire an initial lock for and initialize the loading screen.\n\t\tconst lockId = LoadScreen.lock();\n\t\tLoadScreen.init();\n\n\t\t// Normalize the document.\n\t\tif (document.normalize) {\n\t\t\tdocument.normalize();\n\t\t}\n\n\t\t// Load the story data (must be done before most anything else).\n\t\tStory.load();\n\n\t\t// Instantiate the storage and session objects.\n\t\t// NOTE: `SimpleStore.create(storageId, persistent)`\n\t\tstorage = SimpleStore.create(Story.domId, true);\n\t\tsession = SimpleStore.create(Story.domId, false);\n\n\t\t// Initialize the user interface (must be done before story initialization, specifically before scripts).\n\t\tDialog.init();\n\t\tUIBar.init();\n\t\tEngine.init();\n\n\t\t// Initialize the story (largely load the user styles, scripts, and widgets).\n\t\tStory.init();\n\n\t\t// Initialize the localization (must be done after story initialization).\n\t\tL10n.init();\n\n\t\t// Alert when the browser is degrading required capabilities (must be done after localization initialization).\n\t\tif (!session.has('rcWarn') && storage.name === 'cookie') {\n\t\t\t/* eslint-disable no-alert */\n\t\t\tsession.set('rcWarn', 1);\n\t\t\twindow.alert(L10n.get('warningNoWebStorage'));\n\t\t\t/* eslint-enable no-alert */\n\t\t}\n\n\t\t// Initialize the saves (must be done after story initialization, but before engine start).\n\t\tSave.init();\n\n\t\t// Initialize the settings.\n\t\tSetting.init();\n\n\t\t// Initialize the macros.\n\t\tMacro.init();\n\n\t\t// Start the engine (should be done as late as possible, but before interface startup).\n\t\tEngine.start();\n\n\t\t// Initialize the debug bar interface (should be done as late as possible, but before interface startup).\n\t\tif (Config.debug) {\n\t\t\tDebugBar.init();\n\t\t}\n\n\t\t// Set a recurring timer to start the interfaces (necessary due to DOM readiness issues in some browsers).\n\t\tconst $window    = $(window);\n\t\tconst vprCheckId = setInterval(() => {\n\t\t\t// If `$window.width()` returns a zero value, bail out and wait.\n\t\t\tif (!$window.width()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear the recurring timer.\n\t\t\tclearInterval(vprCheckId);\n\n\t\t\t// Start the UI bar interface.\n\t\t\tUIBar.start();\n\n\t\t\t// Start the debug bar interface.\n\t\t\tif (Config.debug) {\n\t\t\t\tDebugBar.start();\n\t\t\t}\n\n\t\t\t// Trigger the `:storyready` global synthetic event.\n\t\t\tjQuery.event.trigger({ type : ':storyready' });\n\n\t\t\t// Release the loading screen lock after a short delay.\n\t\t\tsetTimeout(() => LoadScreen.unlock(lockId), Engine.minDomActionDelay * 2);\n\t\t}, Engine.minDomActionDelay);\n\n\t\t// Finally, export identifiers for debugging purposes.\n\t\tObject.defineProperty(window, 'SugarCube', {\n\t\t\t// WARNING: We need to assign new values at points, so seal it, do not freeze it.\n\t\t\tvalue : Object.seal(Object.assign(Object.create(null), {\n\t\t\t\tBrowser,\n\t\t\t\tConfig,\n\t\t\t\tDialog,\n\t\t\t\tEngine,\n\t\t\t\tFullscreen,\n\t\t\t\tHas,\n\t\t\t\tL10n,\n\t\t\t\tMacro,\n\t\t\t\tPassage,\n\t\t\t\tSave,\n\t\t\t\tScripting,\n\t\t\t\tSetting,\n\t\t\t\tSimpleAudio,\n\t\t\t\tState,\n\t\t\t\tStory,\n\t\t\t\tUI,\n\t\t\t\tUIBar,\n\t\t\t\tDebugBar,\n\t\t\t\tUtil,\n\t\t\t\tVisibility,\n\t\t\t\tWikifier,\n\t\t\t\tsession,\n\t\t\t\tsettings,\n\t\t\t\tsetup,\n\t\t\t\tstorage,\n\t\t\t\tversion\n\t\t\t}))\n\t\t});\n\n\t\tif (DEBUG) { console.log('[SugarCube/main()] Startup complete; story ready.'); }\n\t}\n\tcatch (ex) {\n\t\tconsole.error(ex);\n\t\tLoadScreen.clear();\n\t\treturn Alert.fatal(null, ex.message, ex);\n\t}\n});\n\n})(window, window.document, jQuery);\n}\n\t</script>\n</body>\n</html>\n"});
\ No newline at end of file
+window.storyFormat({"name":"SugarCube","version":"2.33.2","description":"A full featured, highly customizable story format.  See its <a href=\"http://www.motoslave.net/sugarcube/2/#documentation\" target=\"_blank\">documentation</a>.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"<!DOCTYPE html>\n<html data-init=\"no-js\">\n<head>\n<meta charset=\"UTF-8\" />\n<title>{{STORY_NAME}}</title>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<!--\n\nSugarCube (v2.33.2): A free (gratis and libre) story format.\n\nCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-->\n<script id=\"script-libraries\" type=\"text/javascript\">\nif(document.head&&document.addEventListener&&document.querySelector&&Object.create&&Object.freeze&&JSON){document.documentElement.setAttribute(\"data-init\", \"loading\");\n/*! @source http://purl.eligrey.com/github/classList.js/blob/1.2.20171210/classList.js */\n\"document\"in self&&(\"classList\"in document.createElement(\"_\")&&(!document.createElementNS||\"classList\"in document.createElementNS(\"http://www.w3.org/2000/svg\",\"g\"))||!function(t){\"use strict\";if(\"Element\"in t){var e=\"classList\",n=\"prototype\",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\\s+|\\s+$/g,\"\")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(\"\"===e)throw new c(\"SYNTAX_ERR\",\"The token must not be empty.\");if(/\\s/.test(e))throw new c(\"INVALID_CHARACTER_ERR\",\"The token must not contain space characters.\");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute(\"class\")||\"\"),n=e?e.split(/\\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute(\"class\",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+\"\")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+\"\",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+\"\",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&\"remove\":e!==!1&&\"add\";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+\"\");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(\" \")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){\"use strict\";var t=document.createElement(\"_\");if(t.classList.add(\"c1\",\"c2\"),!t.classList.contains(\"c2\")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e(\"add\"),e(\"remove\")}if(t.classList.toggle(\"c3\",!1),t.classList.contains(\"c3\")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}\"replace\"in document.createElement(\"_\").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(\" \"),i=n.indexOf(t+\"\");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());\n/*!\n * https://github.com/es-shims/es5-shim\n * @license es5-shim Copyright 2009-2020 by contributors, MIT License\n * see https://github.com/es-shims/es5-shim/blob/v4.5.14/LICENSE\n */\n(function(t,r){\"use strict\";if(typeof define===\"function\"&&define.amd){define(r)}else if(typeof exports===\"object\"){module.exports=r()}else{t.returnExports=r()}})(this,function(){var t=Array;var r=t.prototype;var e=Object;var n=e.prototype;var i=Function;var a=i.prototype;var o=String;var f=o.prototype;var u=Number;var l=u.prototype;var s=r.slice;var c=r.splice;var v=r.push;var h=r.unshift;var p=r.concat;var y=r.join;var d=a.call;var g=a.apply;var w=Math.max;var b=Math.min;var T=n.toString;var m=typeof Symbol===\"function\"&&typeof Symbol.toStringTag===\"symbol\";var D;var S=Function.prototype.toString,x=/^\\s*class /,O=function isES6ClassFn(t){try{var r=S.call(t);var e=r.replace(/\\/\\/.*\\n/g,\"\");var n=e.replace(/\\/\\*[.\\s\\S]*\\*\\//g,\"\");var i=n.replace(/\\n/gm,\" \").replace(/ {2}/g,\" \");return x.test(i)}catch(a){return false}},E=function tryFunctionObject(t){try{if(O(t)){return false}S.call(t);return true}catch(r){return false}},j=\"[object Function]\",I=\"[object GeneratorFunction]\",D=function isCallable(t){if(!t){return false}if(typeof t!==\"function\"&&typeof t!==\"object\"){return false}if(m){return E(t)}if(O(t)){return false}var r=T.call(t);return r===j||r===I};var M;var U=RegExp.prototype.exec,$=function tryRegexExec(t){try{U.call(t);return true}catch(r){return false}},F=\"[object RegExp]\";M=function isRegex(t){if(typeof t!==\"object\"){return false}return m?$(t):T.call(t)===F};var N;var C=String.prototype.valueOf,k=function tryStringObject(t){try{C.call(t);return true}catch(r){return false}},A=\"[object String]\";N=function isString(t){if(typeof t===\"string\"){return true}if(typeof t!==\"object\"){return false}return m?k(t):T.call(t)===A};var R=e.defineProperty&&function(){try{var t={};e.defineProperty(t,\"x\",{enumerable:false,value:t});for(var r in t){return false}return t.x===t}catch(n){return false}}();var P=function(t){var r;if(R){r=function(t,r,n,i){if(!i&&r in t){return}e.defineProperty(t,r,{configurable:true,enumerable:false,writable:true,value:n})}}else{r=function(t,r,e,n){if(!n&&r in t){return}t[r]=e}}return function defineProperties(e,n,i){for(var a in n){if(t.call(n,a)){r(e,a,n[a],i)}}}}(n.hasOwnProperty);var J=function isPrimitive(t){var r=typeof t;return t===null||r!==\"object\"&&r!==\"function\"};var Y=u.isNaN||function isActualNaN(t){return t!==t};var z={ToInteger:function ToInteger(t){var r=+t;if(Y(r)){r=0}else if(r!==0&&r!==1/0&&r!==-(1/0)){r=(r>0||-1)*Math.floor(Math.abs(r))}return r},ToPrimitive:function ToPrimitive(t){var r,e,n;if(J(t)){return t}e=t.valueOf;if(D(e)){r=e.call(t);if(J(r)){return r}}n=t.toString;if(D(n)){r=n.call(t);if(J(r)){return r}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError(\"can't convert \"+t+\" to object\")}return e(t)},ToUint32:function ToUint32(t){return t>>>0}};var Z=function Empty(){};P(a,{bind:function bind(t){var r=this;if(!D(r)){throw new TypeError(\"Function.prototype.bind called on incompatible \"+r)}var n=s.call(arguments,1);var a;var o=function(){if(this instanceof a){var i=g.call(r,this,p.call(n,s.call(arguments)));if(e(i)===i){return i}return this}else{return g.call(r,t,p.call(n,s.call(arguments)))}};var f=w(0,r.length-n.length);var u=[];for(var l=0;l<f;l++){v.call(u,\"$\"+l)}a=i(\"binder\",\"return function (\"+y.call(u,\",\")+\"){ return binder.apply(this, arguments); }\")(o);if(r.prototype){Z.prototype=r.prototype;a.prototype=new Z;Z.prototype=null}return a}});var G=d.bind(n.hasOwnProperty);var H=d.bind(n.toString);var W=d.bind(s);var B=g.bind(s);if(typeof document===\"object\"&&document&&document.documentElement){try{W(document.documentElement.childNodes)}catch(X){var L=W;var q=B;W=function arraySliceIE(t){var r=[];var e=t.length;while(e-- >0){r[e]=t[e]}return q(r,L(arguments,1))};B=function arraySliceApplyIE(t,r){return q(W(t),r)}}}var K=d.bind(f.slice);var Q=d.bind(f.split);var V=d.bind(f.indexOf);var _=d.bind(v);var tt=d.bind(n.propertyIsEnumerable);var rt=d.bind(r.sort);var et=t.isArray||function isArray(t){return H(t)===\"[object Array]\"};var nt=[].unshift(0)!==1;P(r,{unshift:function(){h.apply(this,arguments);return this.length}},nt);P(t,{isArray:et});var it=e(\"a\");var at=it[0]!==\"a\"||!(0 in it);var ot=function properlyBoxed(t){var r=true;var e=true;var n=false;if(t){try{t.call(\"foo\",function(t,e,n){if(typeof n!==\"object\"){r=false}});t.call([1],function(){\"use strict\";e=typeof this===\"string\"},\"x\")}catch(i){n=true}}return!!t&&!n&&r&&e};P(r,{forEach:function forEach(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=-1;var i=z.ToUint32(e.length);var a;if(arguments.length>1){a=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.forEach callback must be a function\")}while(++n<i){if(n in e){if(typeof a===\"undefined\"){t(e[n],n,r)}else{t.call(a,e[n],n,r)}}}}},!ot(r.forEach));P(r,{map:function map(r){var e=z.ToObject(this);var n=at&&N(this)?Q(this,\"\"):e;var i=z.ToUint32(n.length);var a=t(i);var o;if(arguments.length>1){o=arguments[1]}if(!D(r)){throw new TypeError(\"Array.prototype.map callback must be a function\")}for(var f=0;f<i;f++){if(f in n){if(typeof o===\"undefined\"){a[f]=r(n[f],f,e)}else{a[f]=r.call(o,n[f],f,e)}}}return a}},!ot(r.map));P(r,{filter:function filter(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.filter callback must be a function\")}for(var f=0;f<n;f++){if(f in e){a=e[f];if(typeof o===\"undefined\"?t(a,f,r):t.call(o,a,f,r)){_(i,a)}}}return i}},!ot(r.filter));P(r,{every:function every(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.every callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&!(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return false}}return true}},!ot(r.every));P(r,{some:function some(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.some callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return true}}return false}},!ot(r.some));var ft=false;if(r.reduce){ft=typeof r.reduce.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduce:function reduce(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduce callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduce of empty array with no initial value\")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in e){a=e[i++];break}if(++i>=n){throw new TypeError(\"reduce of empty array with no initial value\")}}while(true)}for(;i<n;i++){if(i in e){a=t(a,e[i],i,r)}}return a}},!ft);var ut=false;if(r.reduceRight){ut=typeof r.reduceRight.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduceRight:function reduceRight(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduceRight callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduceRight of empty array with no initial value\")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in e){i=e[a--];break}if(--a<0){throw new TypeError(\"reduceRight of empty array with no initial value\")}}while(true)}if(a<0){return i}do{if(a in e){i=t(i,e[a],a,r)}}while(a--);return i}},!ut);var lt=r.indexOf&&[0,1].indexOf(1,2)!==-1;P(r,{indexOf:function indexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=0;if(arguments.length>1){n=z.ToInteger(arguments[1])}n=n>=0?n:w(0,e+n);for(;n<e;n++){if(n in r&&r[n]===t){return n}}return-1}},lt);var st=r.lastIndexOf&&[0,1].lastIndexOf(0,-3)!==-1;P(r,{lastIndexOf:function lastIndexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=e-1;if(arguments.length>1){n=b(n,z.ToInteger(arguments[1]))}n=n>=0?n:e-Math.abs(n);for(;n>=0;n--){if(n in r&&t===r[n]){return n}}return-1}},st);var ct=function(){var t=[1,2];var r=t.splice();return t.length===2&&et(r)&&r.length===0}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}else{return c.apply(this,arguments)}}},!ct);var vt=function(){var t={};r.splice.call(t,0,0,1);return t.length===1}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}var e=arguments;this.length=w(z.ToInteger(this.length),0);if(arguments.length>0&&typeof r!==\"number\"){e=W(arguments);if(e.length<2){_(e,this.length-t)}else{e[1]=z.ToInteger(r)}}return c.apply(this,e)}},!vt);var ht=function(){var r=new t(1e5);r[8]=\"x\";r.splice(1,1);return r.indexOf(\"x\")===7}();var pt=function(){var t=256;var r=[];r[t]=\"a\";r.splice(t+1,0,\"b\");return r[t]===\"a\"}();P(r,{splice:function splice(t,r){var e=z.ToObject(this);var n=[];var i=z.ToUint32(e.length);var a=z.ToInteger(t);var f=a<0?w(i+a,0):b(a,i);var u=arguments.length===0?0:arguments.length===1?i-f:b(w(z.ToInteger(r),0),i-f);var l=0;var s;while(l<u){s=o(f+l);if(G(e,s)){n[l]=e[s]}l+=1}var c=W(arguments,2);var v=c.length;var h;if(v<u){l=f;var p=i-u;while(l<p){s=o(l+u);h=o(l+v);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l+=1}l=i;var y=i-u+v;while(l>y){delete e[l-1];l-=1}}else if(v>u){l=i-u;while(l>f){s=o(l+u-1);h=o(l+v-1);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l-=1}}l=f;for(var d=0;d<c.length;++d){e[l]=c[d];l+=1}e.length=i-u+v;return n}},!ht||!pt);var yt=r.join;var dt;try{dt=Array.prototype.join.call(\"123\",\",\")!==\"1,2,3\"}catch(X){dt=true}if(dt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(N(this)?Q(this,\"\"):this,r)}},dt)}var gt=[1,2].join(undefined)!==\"1,2\";if(gt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(this,r)}},gt)}var wt=function push(t){var r=z.ToObject(this);var e=z.ToUint32(r.length);var n=0;while(n<arguments.length){r[e+n]=arguments[n];n+=1}r.length=e+n;return e+n};var bt=function(){var t={};var r=Array.prototype.push.call(t,undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:function push(t){if(et(this)){return v.apply(this,arguments)}return wt.apply(this,arguments)}},bt);var Tt=function(){var t=[];var r=t.push(undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:wt},Tt);P(r,{slice:function(t,r){var e=N(this)?Q(this,\"\"):this;return B(e,arguments)}},at);var mt=function(){try{[1,2].sort(null)}catch(t){try{[1,2].sort({})}catch(r){return false}}return true}();var Dt=function(){try{[1,2].sort(/a/);return false}catch(t){}return true}();var St=function(){try{[1,2].sort(undefined);return true}catch(t){}return false}();P(r,{sort:function sort(t){if(typeof t===\"undefined\"){return rt(this)}if(!D(t)){throw new TypeError(\"Array.prototype.sort callback must be a function\")}return rt(this,t)}},mt||!St||!Dt);var xt=!tt({toString:null},\"toString\");var Ot=tt(function(){},\"prototype\");var Et=!G(\"x\",\"0\");var jt=function(t){var r=t.constructor;return r&&r.prototype===t};var It={$applicationCache:true,$console:true,$external:true,$frame:true,$frameElement:true,$frames:true,$innerHeight:true,$innerWidth:true,$onmozfullscreenchange:true,$onmozfullscreenerror:true,$outerHeight:true,$outerWidth:true,$pageXOffset:true,$pageYOffset:true,$parent:true,$scrollLeft:true,$scrollTop:true,$scrollX:true,$scrollY:true,$self:true,$webkitIndexedDB:true,$webkitStorageInfo:true,$window:true,$width:true,$height:true,$top:true,$localStorage:true};var Mt=function(){if(typeof window===\"undefined\"){return false}for(var t in window){try{if(!It[\"$\"+t]&&G(window,t)&&window[t]!==null&&typeof window[t]===\"object\"){jt(window[t])}}catch(r){return true}}return false}();var Ut=function(t){if(typeof window===\"undefined\"||!Mt){return jt(t)}try{return jt(t)}catch(r){return false}};var $t=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"];var Ft=$t.length;var Nt=function isArguments(t){return H(t)===\"[object Arguments]\"};var Ct=function isArguments(t){return t!==null&&typeof t===\"object\"&&typeof t.length===\"number\"&&t.length>=0&&!et(t)&&D(t.callee)};var kt=Nt(arguments)?Nt:Ct;P(e,{keys:function keys(t){var r=D(t);var e=kt(t);var n=t!==null&&typeof t===\"object\";var i=n&&N(t);if(!n&&!r&&!e){throw new TypeError(\"Object.keys called on a non-object\")}var a=[];var f=Ot&&r;if(i&&Et||e){for(var u=0;u<t.length;++u){_(a,o(u))}}if(!e){for(var l in t){if(!(f&&l===\"prototype\")&&G(t,l)){_(a,o(l))}}}if(xt){var s=Ut(t);for(var c=0;c<Ft;c++){var v=$t[c];if(!(s&&v===\"constructor\")&&G(t,v)){_(a,v)}}}return a}});var At=e.keys&&function(){return e.keys(arguments).length===2}(1,2);var Rt=e.keys&&function(){var t=e.keys(arguments);return arguments.length!==1||t.length!==1||t[0]!==1}(1);var Pt=e.keys;P(e,{keys:function keys(t){if(kt(t)){return Pt(W(t))}else{return Pt(t)}}},!At||Rt);var Jt=new Date(-0xc782b5b342b24).getUTCMonth()!==0;var Yt=new Date(-0x55d318d56a724);var zt=new Date(14496624e5);var Zt=Yt.toUTCString()!==\"Mon, 01 Jan -45875 11:59:59 GMT\";var Gt;var Ht;var Wt=Yt.getTimezoneOffset();if(Wt<-720){Gt=Yt.toDateString()!==\"Tue Jan 02 -45875\";Ht=!/^Thu Dec 10 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}else{Gt=Yt.toDateString()!==\"Mon Jan 01 -45875\";Ht=!/^Wed Dec 09 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}var Bt=d.bind(Date.prototype.getFullYear);var Xt=d.bind(Date.prototype.getMonth);var Lt=d.bind(Date.prototype.getDate);var qt=d.bind(Date.prototype.getUTCFullYear);var Kt=d.bind(Date.prototype.getUTCMonth);var Qt=d.bind(Date.prototype.getUTCDate);var Vt=d.bind(Date.prototype.getUTCDay);var _t=d.bind(Date.prototype.getUTCHours);var tr=d.bind(Date.prototype.getUTCMinutes);var rr=d.bind(Date.prototype.getUTCSeconds);var er=d.bind(Date.prototype.getUTCMilliseconds);var nr=[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];var ir=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];var ar=function daysInMonth(t,r){return Lt(new Date(r,t,0))};P(Date.prototype,{getFullYear:function getFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);if(t<0&&Xt(this)>11){return t+1}return t},getMonth:function getMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);if(t<0&&r>11){return 0}return r},getDate:function getDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);var e=Lt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e},getUTCFullYear:function getUTCFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);if(t<0&&Kt(this)>11){return t+1}return t},getUTCMonth:function getUTCMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);if(t<0&&r>11){return 0}return r},getUTCDate:function getUTCDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);var e=Qt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e}},Jt);P(Date.prototype,{toUTCString:function toUTCString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Vt(this);var r=Qt(this);var e=Kt(this);var n=qt(this);var i=_t(this);var a=tr(this);var o=rr(this);return nr[t]+\", \"+(r<10?\"0\"+r:r)+\" \"+ir[e]+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"}},Jt||Zt);P(Date.prototype,{toDateString:function toDateString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n}},Jt||Gt);if(Jt||Ht){Date.prototype.toString=function toString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();var i=this.getHours();var a=this.getMinutes();var o=this.getSeconds();var f=this.getTimezoneOffset();var u=Math.floor(Math.abs(f)/60);var l=Math.floor(Math.abs(f)%60);return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"+(f>0?\"-\":\"+\")+(u<10?\"0\"+u:u)+(l<10?\"0\"+l:l)};if(R){e.defineProperty(Date.prototype,\"toString\",{configurable:true,enumerable:false,writable:true})}}var or=-621987552e5;var fr=\"-000001\";var ur=Date.prototype.toISOString&&new Date(or).toISOString().indexOf(fr)===-1;var lr=Date.prototype.toISOString&&new Date(-1).toISOString()!==\"1969-12-31T23:59:59.999Z\";var sr=d.bind(Date.prototype.getTime);P(Date.prototype,{toISOString:function toISOString(){if(!isFinite(this)||!isFinite(sr(this))){throw new RangeError(\"Date.prototype.toISOString called on non-finite value.\")}var t=qt(this);var r=Kt(this);t+=Math.floor(r/12);r=(r%12+12)%12;var e=[r+1,Qt(this),_t(this),tr(this),rr(this)];t=(t<0?\"-\":t>9999?\"+\":\"\")+K(\"00000\"+Math.abs(t),0<=t&&t<=9999?-4:-6);for(var n=0;n<e.length;++n){e[n]=K(\"00\"+e[n],-2)}return t+\"-\"+W(e,0,2).join(\"-\")+\"T\"+W(e,2).join(\":\")+\".\"+K(\"000\"+er(this),-3)+\"Z\"}},ur||lr);var cr=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(or).toJSON().indexOf(fr)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!cr){Date.prototype.toJSON=function toJSON(t){var r=e(this);var n=z.ToPrimitive(r);if(typeof n===\"number\"&&!isFinite(n)){return null}var i=r.toISOString;if(!D(i)){throw new TypeError(\"toISOString property is not callable\")}return i.call(r)}}var vr=Date.parse(\"+033658-09-27T01:46:40.000Z\")===1e15;var hr=!isNaN(Date.parse(\"2012-04-04T24:00:00.500Z\"))||!isNaN(Date.parse(\"2012-11-31T23:59:59.000Z\"))||!isNaN(Date.parse(\"2012-12-31T23:59:60.000Z\"));var pr=isNaN(Date.parse(\"2000-01-01T00:00:00.000Z\"));if(pr||hr||!vr){var yr=Math.pow(2,31)-1;var dr=Y(new Date(1970,0,1,0,0,0,yr+1).getTime());Date=function(t){var r=function Date(e,n,i,a,f,u,l){var s=arguments.length;var c;if(this instanceof t){var v=u;var h=l;if(dr&&s>=7&&l>yr){var p=Math.floor(l/yr)*yr;var y=Math.floor(p/1e3);v+=y;h-=y*1e3}c=s===1&&o(e)===e?new t(r.parse(e)):s>=7?new t(e,n,i,a,f,v,h):s>=6?new t(e,n,i,a,f,v):s>=5?new t(e,n,i,a,f):s>=4?new t(e,n,i,a):s>=3?new t(e,n,i):s>=2?new t(e,n):s>=1?new t(e instanceof t?+e:e):new t}else{c=t.apply(this,arguments)}if(!J(c)){P(c,{constructor:r},true)}return c};var e=new RegExp(\"^\"+\"(\\\\d{4}|[+-]\\\\d{6})\"+\"(?:-(\\\\d{2})\"+\"(?:-(\\\\d{2})\"+\"(?:\"+\"T(\\\\d{2})\"+\":(\\\\d{2})\"+\"(?:\"+\":(\\\\d{2})\"+\"(?:(\\\\.\\\\d{1,}))?\"+\")?\"+\"(\"+\"Z|\"+\"(?:\"+\"([-+])\"+\"(\\\\d{2})\"+\":(\\\\d{2})\"+\")\"+\")?)?)?)?\"+\"$\");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function dayFromMonth(t,r){var e=r>1?1:0;return n[r]+Math.floor((t-1969+e)/4)-Math.floor((t-1901+e)/100)+Math.floor((t-1601+e)/400)+365*(t-1970)};var a=function toUTC(r){var e=0;var n=r;if(dr&&n>yr){var i=Math.floor(n/yr)*yr;var a=Math.floor(i/1e3);e+=a;n-=a*1e3}return u(new t(1970,0,1,0,0,e,n))};for(var f in t){if(G(t,f)){r[f]=t[f]}}P(r,{now:t.now,UTC:t.UTC},true);r.prototype=t.prototype;P(r.prototype,{constructor:r},true);var l=function parse(r){var n=e.exec(r);if(n){var o=u(n[1]),f=u(n[2]||1)-1,l=u(n[3]||1)-1,s=u(n[4]||0),c=u(n[5]||0),v=u(n[6]||0),h=Math.floor(u(n[7]||0)*1e3),p=Boolean(n[4]&&!n[8]),y=n[9]===\"-\"?1:-1,d=u(n[10]||0),g=u(n[11]||0),w;var b=c>0||v>0||h>0;if(s<(b?24:25)&&c<60&&v<60&&h<1e3&&f>-1&&f<12&&d<24&&g<60&&l>-1&&l<i(o,f+1)-i(o,f)){w=((i(o,f)+l)*24+s+d*y)*60;w=((w+c+g*y)*60+v)*1e3+h;if(p){w=a(w)}if(-864e13<=w&&w<=864e13){return w}}return NaN}return t.parse.apply(this,arguments)};P(r,{parse:l});return r}(Date)}if(!Date.now){Date.now=function now(){return(new Date).getTime()}}var gr=l.toFixed&&(8e-5.toFixed(3)!==\"0.000\"||.9.toFixed(0)!==\"1\"||1.255.toFixed(2)!==\"1.25\"||(1000000000000000128).toFixed(0)!==\"1000000000000000128\");var wr={base:1e7,size:6,data:[0,0,0,0,0,0],multiply:function multiply(t,r){var e=-1;var n=r;while(++e<wr.size){n+=t*wr.data[e];wr.data[e]=n%wr.base;n=Math.floor(n/wr.base)}},divide:function divide(t){var r=wr.size;var e=0;while(--r>=0){e+=wr.data[r];wr.data[r]=Math.floor(e/t);e=e%t*wr.base}},numToString:function numToString(){var t=wr.size;var r=\"\";while(--t>=0){if(r!==\"\"||t===0||wr.data[t]!==0){var e=o(wr.data[t]);if(r===\"\"){r=e}else{r+=K(\"0000000\",0,7-e.length)+e}}}return r},pow:function pow(t,r,e){return r===0?e:r%2===1?pow(t,r-1,e*t):pow(t*t,r/2,e)},log:function log(t){var r=0;var e=t;while(e>=4096){r+=12;e/=4096}while(e>=2){r+=1;e/=2}return r}};var br=function toFixed(t){var r,e,n,i,a,f,l,s;r=u(t);r=Y(r)?0:Math.floor(r);if(r<0||r>20){throw new RangeError(\"Number.toFixed called with invalid number of decimals\")}e=u(this);if(Y(e)){return\"NaN\"}if(e<=-1e21||e>=1e21){return o(e)}n=\"\";if(e<0){n=\"-\";e=-e}i=\"0\";if(e>1e-21){a=wr.log(e*wr.pow(2,69,1))-69;f=a<0?e*wr.pow(2,-a,1):e/wr.pow(2,a,1);f*=4503599627370496;a=52-a;if(a>0){wr.multiply(0,f);l=r;while(l>=7){wr.multiply(1e7,0);l-=7}wr.multiply(wr.pow(10,l,1),0);l=a-1;while(l>=23){wr.divide(1<<23);l-=23}wr.divide(1<<l);wr.multiply(1,1);wr.divide(2);i=wr.numToString()}else{wr.multiply(0,f);wr.multiply(1<<-a,0);i=wr.numToString()+K(\"0.00000000000000000000\",2,2+r)}}if(r>0){s=i.length;if(s<=r){i=n+K(\"0.0000000000000000000\",0,r-s+2)+i}else{i=n+K(i,0,s-r)+\".\"+K(i,s-r)}}else{i=n+i}return i};P(l,{toFixed:br},gr);var Tr=function(){try{return 1..toPrecision(undefined)===\"1\"}catch(t){return true}}();var mr=l.toPrecision;P(l,{toPrecision:function toPrecision(t){return typeof t===\"undefined\"?mr.call(this):mr.call(this,t)}},Tr);if(\"ab\".split(/(?:ab)*/).length!==2||\".\".split(/(.?)(.?)/).length!==4||\"tesst\".split(/(s)*/)[1]===\"t\"||\"test\".split(/(?:)/,-1).length!==4||\"\".split(/.?/).length||\".\".split(/()()/).length>1){(function(){var t=typeof/()??/.exec(\"\")[1]===\"undefined\";var r=Math.pow(2,32)-1;f.split=function(e,n){var i=String(this);if(typeof e===\"undefined\"&&n===0){return[]}if(!M(e)){return Q(this,e,n)}var a=[];var o=(e.ignoreCase?\"i\":\"\")+(e.multiline?\"m\":\"\")+(e.unicode?\"u\":\"\")+(e.sticky?\"y\":\"\"),f=0,u,l,s,c;var h=new RegExp(e.source,o+\"g\");if(!t){u=new RegExp(\"^\"+h.source+\"$(?!\\\\s)\",o)}var p=typeof n===\"undefined\"?r:z.ToUint32(n);l=h.exec(i);while(l){s=l.index+l[0].length;if(s>f){_(a,K(i,f,l.index));if(!t&&l.length>1){l[0].replace(u,function(){for(var t=1;t<arguments.length-2;t++){if(typeof arguments[t]===\"undefined\"){l[t]=void 0}}})}if(l.length>1&&l.index<i.length){v.apply(a,W(l,1))}c=l[0].length;f=s;if(a.length>=p){break}}if(h.lastIndex===l.index){h.lastIndex++}l=h.exec(i)}if(f===i.length){if(c||!h.test(\"\")){_(a,\"\")}}else{_(a,K(i,f))}return a.length>p?W(a,0,p):a}})()}else if(\"0\".split(void 0,0).length){f.split=function split(t,r){if(typeof t===\"undefined\"&&r===0){return[]}return Q(this,t,r)}}var Dr=f.replace;var Sr=function(){var t=[];\"x\".replace(/x(.)?/g,function(r,e){_(t,e)});return t.length===1&&typeof t[0]===\"undefined\"}();if(!Sr){f.replace=function replace(t,r){var e=D(r);var n=M(t)&&/\\)[*?]/.test(t.source);if(!e||!n){return Dr.call(this,t,r)}else{var i=function(e){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(e)||[];t.lastIndex=i;_(a,arguments[n-2],arguments[n-1]);return r.apply(this,a)};return Dr.call(this,t,i)}}}var xr=f.substr;var Or=\"\".substr&&\"0b\".substr(-1)!==\"b\";P(f,{substr:function substr(t,r){var e=t;if(t<0){e=w(this.length+t,0)}return xr.call(this,e,r)}},Or);var Er=\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\"+\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\"+\"\\u2029\\ufeff\";var jr=\"\\u200b\";var Ir=\"[\"+Er+\"]\";var Mr=new RegExp(\"^\"+Ir+Ir+\"*\");var Ur=new RegExp(Ir+Ir+\"*$\");var $r=f.trim&&(Er.trim()||!jr.trim());P(f,{trim:function trim(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}return o(this).replace(Mr,\"\").replace(Ur,\"\")}},$r);var Fr=d.bind(String.prototype.trim);var Nr=f.lastIndexOf&&\"abc\\u3042\\u3044\".lastIndexOf(\"\\u3042\\u3044\",2)!==-1;P(f,{lastIndexOf:function lastIndexOf(t){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var r=o(this);var e=o(t);var n=arguments.length>1?u(arguments[1]):NaN;var i=Y(n)?Infinity:z.ToInteger(n);var a=b(w(i,0),r.length);var f=e.length;var l=a+f;while(l>0){l=w(0,l-f);var s=V(K(r,l,a+f),e);if(s!==-1){return l+s}}return-1}},Nr);var Cr=f.lastIndexOf;P(f,{lastIndexOf:function lastIndexOf(t){return Cr.apply(this,arguments)}},f.lastIndexOf.length!==1);if(parseInt(Er+\"08\")!==8||parseInt(Er+\"0x16\")!==22){parseInt=function(t){var r=/^[-+]?0[xX]/;return function parseInt(e,n){if(typeof e===\"symbol\"){\"\"+e}var i=Fr(String(e));var a=u(n)||(r.test(i)?16:10);return t(i,a)}}(parseInt)}if(1/parseFloat(\"-0\")!==-Infinity){parseFloat=function(t){return function parseFloat(r){var e=Fr(String(r));var n=t(e);return n===0&&K(e,0,1)===\"-\"?-0:n}}(parseFloat)}if(String(new RangeError(\"test\"))!==\"RangeError: test\"){var kr=function toString(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var t=this.name;if(typeof t===\"undefined\"){t=\"Error\"}else if(typeof t!==\"string\"){t=o(t)}var r=this.message;if(typeof r===\"undefined\"){r=\"\"}else if(typeof r!==\"string\"){r=o(r)}if(!t){return r}if(!r){return t}return t+\": \"+r};Error.prototype.toString=kr}if(R){var Ar=function(t,r){if(tt(t,r)){var e=Object.getOwnPropertyDescriptor(t,r);if(e.configurable){e.enumerable=false;Object.defineProperty(t,r,e)}}};Ar(Error.prototype,\"message\");if(Error.prototype.message!==\"\"){Error.prototype.message=\"\"}Ar(Error.prototype,\"name\")}if(String(/a/gim)!==\"/a/gim\"){var Rr=function toString(){var t=\"/\"+this.source+\"/\";if(this.global){t+=\"g\"}if(this.ignoreCase){t+=\"i\"}if(this.multiline){t+=\"m\"}return t};RegExp.prototype.toString=Rr}});\n/*!\n * https://github.com/paulmillr/es6-shim\n * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)\n *   and contributors,  MIT License\n * es6-shim: v0.35.4\n * see https://github.com/paulmillr/es6-shim/blob/0.35.4/LICENSE\n * Details and documentation:\n * https://github.com/paulmillr/es6-shim/\n */\n(function(e,t){if(typeof define===\"function\"&&define.amd){define(t)}else if(typeof exports===\"object\"){module.exports=t()}else{e.returnExports=t()}})(this,function(){\"use strict\";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=Object.keys;var o=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var i=function(e){try{e();return false}catch(t){return true}};var a=function valueOrFalseIfThrows(e){try{return e()}catch(t){return false}};var u=o(i);var f=function(){return!i(function(){return Object.defineProperty({},\"x\",{get:function(){}})})};var s=!!Object.defineProperty&&f();var c=function foo(){}.name===\"foo\";var l=Function.call.bind(Array.prototype.forEach);var p=Function.call.bind(Array.prototype.reduce);var v=Function.call.bind(Array.prototype.filter);var y=Function.call.bind(Array.prototype.some);var h=function(e,t,r,n){if(!n&&t in e){return}if(s){Object.defineProperty(e,t,{configurable:true,enumerable:false,writable:true,value:r})}else{e[t]=r}};var b=function(e,t,r){l(n(t),function(n){var o=t[n];h(e,n,o,!!r)})};var g=Function.call.bind(Object.prototype.toString);var d=typeof/abc/===\"function\"?function IsCallableSlow(e){return typeof e===\"function\"&&g(e)===\"[object Function]\"}:function IsCallableFast(e){return typeof e===\"function\"};var m={getter:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}Object.defineProperty(e,t,{configurable:true,enumerable:false,get:r})},proxy:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,{configurable:n.configurable,enumerable:n.enumerable,get:function getKey(){return e[t]},set:function setKey(r){e[t]=r}})},redefine:function(e,t,r){if(s){var n=Object.getOwnPropertyDescriptor(e,t);n.value=r;Object.defineProperty(e,t,n)}else{e[t]=r}},defineByDescriptor:function(e,t,r){if(s){Object.defineProperty(e,t,r)}else if(\"value\"in r){e[t]=r.value}},preserveToString:function(e,t){if(t&&d(t.toString)){h(e,\"toString\",t.toString.bind(t),true)}}};var O=Object.create||function(e,t){var r=function Prototype(){};r.prototype=e;var o=new r;if(typeof t!==\"undefined\"){n(t).forEach(function(e){m.defineByDescriptor(o,e,t[e])})}return o};var w=function(e,t){if(!Object.setPrototypeOf){return false}return a(function(){var r=function Subclass(t){var r=new e(t);Object.setPrototypeOf(r,Subclass.prototype);return r};Object.setPrototypeOf(r,e);r.prototype=O(e.prototype,{constructor:{value:r}});return t(r)})};var j=function(){if(typeof self!==\"undefined\"){return self}if(typeof window!==\"undefined\"){return window}if(typeof global!==\"undefined\"){return global}throw new Error(\"unable to locate global object\")};var S=j();var T=S.isFinite;var I=Function.call.bind(String.prototype.indexOf);var E=Function.apply.bind(Array.prototype.indexOf);var P=Function.call.bind(Array.prototype.concat);var C=Function.call.bind(String.prototype.slice);var M=Function.call.bind(Array.prototype.push);var x=Function.apply.bind(Array.prototype.push);var N=Function.call.bind(Array.prototype.shift);var A=Math.max;var R=Math.min;var _=Math.floor;var k=Math.abs;var L=Math.exp;var F=Math.log;var D=Math.sqrt;var z=Function.call.bind(Object.prototype.hasOwnProperty);var q;var W=function(){};var G=S.Map;var H=G&&G.prototype[\"delete\"];var V=G&&G.prototype.get;var B=G&&G.prototype.has;var U=G&&G.prototype.set;var $=S.Symbol||{};var J=$.species||\"@@species\";var X=Number.isNaN||function isNaN(e){return e!==e};var K=Number.isFinite||function isFinite(e){return typeof e===\"number\"&&T(e)};var Z=d(Math.sign)?Math.sign:function sign(e){var t=Number(e);if(t===0){return t}if(X(t)){return t}return t<0?-1:1};var Y=function log1p(e){var t=Number(e);if(t<-1||X(t)){return NaN}if(t===0||t===Infinity){return t}if(t===-1){return-Infinity}return 1+t-1===0?t:t*(F(1+t)/(1+t-1))};var Q=function isArguments(e){return g(e)===\"[object Arguments]\"};var ee=function isArguments(e){return e!==null&&typeof e===\"object\"&&typeof e.length===\"number\"&&e.length>=0&&g(e)!==\"[object Array]\"&&g(e.callee)===\"[object Function]\"};var te=Q(arguments)?Q:ee;var re={primitive:function(e){return e===null||typeof e!==\"function\"&&typeof e!==\"object\"},string:function(e){return g(e)===\"[object String]\"},regex:function(e){return g(e)===\"[object RegExp]\"},symbol:function(e){return typeof S.Symbol===\"function\"&&typeof e===\"symbol\"}};var ne=function overrideNative(e,t,r){var n=e[t];h(e,t,r,true);m.preserveToString(e[t],n)};var oe=typeof $===\"function\"&&typeof $[\"for\"]===\"function\"&&re.symbol($());var ie=re.symbol($.iterator)?$.iterator:\"_es6-shim iterator_\";if(S.Set&&typeof(new S.Set)[\"@@iterator\"]===\"function\"){ie=\"@@iterator\"}if(!S.Reflect){h(S,\"Reflect\",{},true)}var ae=S.Reflect;var ue=String;var fe=typeof document===\"undefined\"||!document?null:document.all;var se=fe==null?function isNullOrUndefined(e){return e==null}:function isNullOrUndefinedAndNotDocumentAll(e){return e==null&&e!==fe};var ce={Call:function Call(t,r){var n=arguments.length>2?arguments[2]:[];if(!ce.IsCallable(t)){throw new TypeError(t+\" is not a function\")}return e(t,r,n)},RequireObjectCoercible:function(e,t){if(se(e)){throw new TypeError(t||\"Cannot call method on \"+e)}return e},TypeIsObject:function(e){if(e===void 0||e===null||e===true||e===false){return false}return typeof e===\"function\"||typeof e===\"object\"||e===fe},ToObject:function(e,t){return Object(ce.RequireObjectCoercible(e,t))},IsCallable:d,IsConstructor:function(e){return ce.IsCallable(e)},ToInt32:function(e){return ce.ToNumber(e)>>0},ToUint32:function(e){return ce.ToNumber(e)>>>0},ToNumber:function(e){if(g(e)===\"[object Symbol]\"){throw new TypeError(\"Cannot convert a Symbol value to a number\")}return+e},ToInteger:function(e){var t=ce.ToNumber(e);if(X(t)){return 0}if(t===0||!K(t)){return t}return(t>0?1:-1)*_(k(t))},ToLength:function(e){var t=ce.ToInteger(e);if(t<=0){return 0}if(t>Number.MAX_SAFE_INTEGER){return Number.MAX_SAFE_INTEGER}return t},SameValue:function(e,t){if(e===t){if(e===0){return 1/e===1/t}return true}return X(e)&&X(t)},SameValueZero:function(e,t){return e===t||X(e)&&X(t)},IsIterable:function(e){return ce.TypeIsObject(e)&&(typeof e[ie]!==\"undefined\"||te(e))},GetIterator:function(e){if(te(e)){return new q(e,\"value\")}var t=ce.GetMethod(e,ie);if(!ce.IsCallable(t)){throw new TypeError(\"value is not an iterable\")}var r=ce.Call(t,e);if(!ce.TypeIsObject(r)){throw new TypeError(\"bad iterator\")}return r},GetMethod:function(e,t){var r=ce.ToObject(e)[t];if(se(r)){return void 0}if(!ce.IsCallable(r)){throw new TypeError(\"Method not callable: \"+t)}return r},IteratorComplete:function(e){return!!e.done},IteratorClose:function(e,t){var r=ce.GetMethod(e,\"return\");if(r===void 0){return}var n,o;try{n=ce.Call(r,e)}catch(i){o=i}if(t){return}if(o){throw o}if(!ce.TypeIsObject(n)){throw new TypeError(\"Iterator's return method returned a non-object.\")}},IteratorNext:function(e){var t=arguments.length>1?e.next(arguments[1]):e.next();if(!ce.TypeIsObject(t)){throw new TypeError(\"bad iterator\")}return t},IteratorStep:function(e){var t=ce.IteratorNext(e);var r=ce.IteratorComplete(t);return r?false:t},Construct:function(e,t,r,n){var o=typeof r===\"undefined\"?e:r;if(!n&&ae.construct){return ae.construct(e,t,o)}var i=o.prototype;if(!ce.TypeIsObject(i)){i=Object.prototype}var a=O(i);var u=ce.Call(e,a,t);return ce.TypeIsObject(u)?u:a},SpeciesConstructor:function(e,t){var r=e.constructor;if(r===void 0){return t}if(!ce.TypeIsObject(r)){throw new TypeError(\"Bad constructor\")}var n=r[J];if(se(n)){return t}if(!ce.IsConstructor(n)){throw new TypeError(\"Bad @@species\")}return n},CreateHTML:function(e,t,r,n){var o=ce.ToString(e);var i=\"<\"+t;if(r!==\"\"){var a=ce.ToString(n);var u=a.replace(/\"/g,\"&quot;\");i+=\" \"+r+'=\"'+u+'\"'}var f=i+\">\";var s=f+o;return s+\"</\"+t+\">\"},IsRegExp:function IsRegExp(e){if(!ce.TypeIsObject(e)){return false}var t=e[$.match];if(typeof t!==\"undefined\"){return!!t}return re.regex(e)},ToString:function ToString(e){return ue(e)}};if(s&&oe){var le=function defineWellKnownSymbol(e){if(re.symbol($[e])){return $[e]}var t=$[\"for\"](\"Symbol.\"+e);Object.defineProperty($,e,{configurable:false,enumerable:false,writable:false,value:t});return t};if(!re.symbol($.search)){var pe=le(\"search\");var ve=String.prototype.search;h(RegExp.prototype,pe,function search(e){return ce.Call(ve,e,[this])});var ye=function search(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,pe);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(ve,t,[ce.ToString(e)])};ne(String.prototype,\"search\",ye)}if(!re.symbol($.replace)){var he=le(\"replace\");var be=String.prototype.replace;h(RegExp.prototype,he,function replace(e,t){return ce.Call(be,e,[this,t])});var ge=function replace(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,he);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(be,r,[ce.ToString(e),t])};ne(String.prototype,\"replace\",ge)}if(!re.symbol($.split)){var de=le(\"split\");var me=String.prototype.split;h(RegExp.prototype,de,function split(e,t){return ce.Call(me,e,[this,t])});var Oe=function split(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,de);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(me,r,[ce.ToString(e),t])};ne(String.prototype,\"split\",Oe)}var we=re.symbol($.match);var je=we&&function(){var e={};e[$.match]=function(){return 42};return\"a\".match(e)!==42}();if(!we||je){var Se=le(\"match\");var Te=String.prototype.match;h(RegExp.prototype,Se,function match(e){return ce.Call(Te,e,[this])});var Ie=function match(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,Se);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(Te,t,[ce.ToString(e)])};ne(String.prototype,\"match\",Ie)}}var Ee=function wrapConstructor(e,t,r){m.preserveToString(t,e);if(Object.setPrototypeOf){Object.setPrototypeOf(e,t)}if(s){l(Object.getOwnPropertyNames(e),function(n){if(n in W||r[n]){return}m.proxy(e,n,t)})}else{l(Object.keys(e),function(n){if(n in W||r[n]){return}t[n]=e[n]})}t.prototype=e.prototype;m.redefine(e.prototype,\"constructor\",t)};var Pe=function(){return this};var Ce=function(e){if(s&&!z(e,J)){m.getter(e,J,Pe)}};var Me=function(e,t){var r=t||function iterator(){return this};h(e,ie,r);if(!e[ie]&&re.symbol(ie)){e[ie]=r}};var xe=function createDataProperty(e,t,r){if(s){Object.defineProperty(e,t,{configurable:true,enumerable:true,writable:true,value:r})}else{e[t]=r}};var Ne=function createDataPropertyOrThrow(e,t,r){xe(e,t,r);if(!ce.SameValue(e[t],r)){throw new TypeError(\"property is nonconfigurable\")}};var Ae=function(e,t,r,n){if(!ce.TypeIsObject(e)){throw new TypeError(\"Constructor requires `new`: \"+t.name)}var o=t.prototype;if(!ce.TypeIsObject(o)){o=r}var i=O(o);for(var a in n){if(z(n,a)){var u=n[a];h(i,a,u,true)}}return i};if(String.fromCodePoint&&String.fromCodePoint.length!==1){var Re=String.fromCodePoint;ne(String,\"fromCodePoint\",function fromCodePoint(e){return ce.Call(Re,this,arguments)})}var _e={fromCodePoint:function fromCodePoint(e){var t=[];var r;for(var n=0,o=arguments.length;n<o;n++){r=Number(arguments[n]);if(!ce.SameValue(r,ce.ToInteger(r))||r<0||r>1114111){throw new RangeError(\"Invalid code point \"+r)}if(r<65536){M(t,String.fromCharCode(r))}else{r-=65536;M(t,String.fromCharCode((r>>10)+55296));M(t,String.fromCharCode(r%1024+56320))}}return t.join(\"\")},raw:function raw(e){var t=ce.ToObject(e,\"bad callSite\");var r=ce.ToObject(t.raw,\"bad raw value\");var n=r.length;var o=ce.ToLength(n);if(o<=0){return\"\"}var i=[];var a=0;var u,f,s,c;while(a<o){u=ce.ToString(a);s=ce.ToString(r[u]);M(i,s);if(a+1>=o){break}f=a+1<arguments.length?arguments[a+1]:\"\";c=ce.ToString(f);M(i,c);a+=1}return i.join(\"\")}};if(String.raw&&String.raw({raw:{0:\"x\",1:\"y\",length:2}})!==\"xy\"){ne(String,\"raw\",_e.raw)}b(String,_e);var ke=function repeat(e,t){if(t<1){return\"\"}if(t%2){return repeat(e,t-1)+e}var r=repeat(e,t/2);return r+r};var Le=Infinity;var Fe={repeat:function repeat(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);if(r<0||r>=Le){throw new RangeError(\"repeat count must be less than infinity and not overflow maximum string size\")}return ke(t,r)},startsWith:function startsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"startsWith\" with a regex')}var r=ce.ToString(e);var n;if(arguments.length>1){n=arguments[1]}var o=A(ce.ToInteger(n),0);return C(t,o,o+r.length)===r},endsWith:function endsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"endsWith\" with a regex')}var r=ce.ToString(e);var n=t.length;var o;if(arguments.length>1){o=arguments[1]}var i=typeof o===\"undefined\"?n:ce.ToInteger(o);var a=R(A(i,0),n);return C(t,a-r.length,a)===r},includes:function includes(e){if(ce.IsRegExp(e)){throw new TypeError('\"includes\" does not accept a RegExp')}var t=ce.ToString(e);var r;if(arguments.length>1){r=arguments[1]}return I(this,t,r)!==-1},codePointAt:function codePointAt(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);var n=t.length;if(r>=0&&r<n){var o=t.charCodeAt(r);var i=r+1===n;if(o<55296||o>56319||i){return o}var a=t.charCodeAt(r+1);if(a<56320||a>57343){return o}return(o-55296)*1024+(a-56320)+65536}}};if(String.prototype.includes&&\"a\".includes(\"a\",Infinity)!==false){ne(String.prototype,\"includes\",Fe.includes)}if(String.prototype.startsWith&&String.prototype.endsWith){var De=i(function(){return\"/a/\".startsWith(/a/)});var ze=a(function(){return\"abc\".startsWith(\"a\",Infinity)===false});if(!De||!ze){ne(String.prototype,\"startsWith\",Fe.startsWith);ne(String.prototype,\"endsWith\",Fe.endsWith)}}if(oe){var qe=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".startsWith(e)});if(!qe){ne(String.prototype,\"startsWith\",Fe.startsWith)}var We=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".endsWith(e)});if(!We){ne(String.prototype,\"endsWith\",Fe.endsWith)}var Ge=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".includes(e)});if(!Ge){ne(String.prototype,\"includes\",Fe.includes)}}b(String.prototype,Fe);var He=[\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\",\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\",\"\\u2029\\ufeff\"].join(\"\");var Ve=new RegExp(\"(^[\"+He+\"]+)|([\"+He+\"]+$)\",\"g\");var Be=function trim(){return ce.ToString(ce.RequireObjectCoercible(this)).replace(Ve,\"\")};var Ue=[\"\\x85\",\"\\u200b\",\"\\ufffe\"].join(\"\");var $e=new RegExp(\"[\"+Ue+\"]\",\"g\");var Je=/^[-+]0x[0-9a-f]+$/i;var Xe=Ue.trim().length!==Ue.length;h(String.prototype,\"trim\",Be,Xe);var Ke=function(e){return{value:e,done:arguments.length===0}};var Ze=function(e){ce.RequireObjectCoercible(e);this._s=ce.ToString(e);this._i=0};Ze.prototype.next=function(){var e=this._s;var t=this._i;if(typeof e===\"undefined\"||t>=e.length){this._s=void 0;return Ke()}var r=e.charCodeAt(t);var n,o;if(r<55296||r>56319||t+1===e.length){o=1}else{n=e.charCodeAt(t+1);o=n<56320||n>57343?1:2}this._i=t+o;return Ke(e.substr(t,o))};Me(Ze.prototype);Me(String.prototype,function(){return new Ze(this)});var Ye={from:function from(e){var r=this;var n;if(arguments.length>1){n=arguments[1]}var o,i;if(typeof n===\"undefined\"){o=false}else{if(!ce.IsCallable(n)){throw new TypeError(\"Array.from: when provided, the second argument must be a function\")}if(arguments.length>2){i=arguments[2]}o=true}var a=typeof(te(e)||ce.GetMethod(e,ie))!==\"undefined\";var u,f,s;if(a){f=ce.IsConstructor(r)?Object(new r):[];var c=ce.GetIterator(e);var l,p;s=0;while(true){l=ce.IteratorStep(c);if(l===false){break}p=l.value;try{if(o){p=typeof i===\"undefined\"?n(p,s):t(n,i,p,s)}f[s]=p}catch(v){ce.IteratorClose(c,true);throw v}s+=1}u=s}else{var y=ce.ToObject(e);u=ce.ToLength(y.length);f=ce.IsConstructor(r)?Object(new r(u)):new Array(u);var h;for(s=0;s<u;++s){h=y[s];if(o){h=typeof i===\"undefined\"?n(h,s):t(n,i,h,s)}Ne(f,s,h)}}f.length=u;return f},of:function of(){var e=arguments.length;var t=this;var n=r(t)||!ce.IsCallable(t)?new Array(e):ce.Construct(t,[e]);for(var o=0;o<e;++o){Ne(n,o,arguments[o])}n.length=e;return n}};b(Array,Ye);Ce(Array);q=function(e,t){this.i=0;this.array=e;this.kind=t};b(q.prototype,{next:function(){var e=this.i;var t=this.array;if(!(this instanceof q)){throw new TypeError(\"Not an ArrayIterator\")}if(typeof t!==\"undefined\"){var r=ce.ToLength(t.length);for(;e<r;e++){var n=this.kind;var o;if(n===\"key\"){o=e}else if(n===\"value\"){o=t[e]}else if(n===\"entry\"){o=[e,t[e]]}this.i=e+1;return Ke(o)}}this.array=void 0;return Ke()}});Me(q.prototype);var Qe=Array.of===Ye.of||function(){var e=function Foo(e){this.length=e};e.prototype=[];var t=Array.of.apply(e,[1,2]);return t instanceof e&&t.length===2}();if(!Qe){ne(Array,\"of\",Ye.of)}var et={copyWithin:function copyWithin(e,t){var r=ce.ToObject(this);var n=ce.ToLength(r.length);var o=ce.ToInteger(e);var i=ce.ToInteger(t);var a=o<0?A(n+o,0):R(o,n);var u=i<0?A(n+i,0):R(i,n);var f;if(arguments.length>2){f=arguments[2]}var s=typeof f===\"undefined\"?n:ce.ToInteger(f);var c=s<0?A(n+s,0):R(s,n);var l=R(c-u,n-a);var p=1;if(u<a&&a<u+l){p=-1;u+=l-1;a+=l-1}while(l>0){if(u in r){r[a]=r[u]}else{delete r[a]}u+=p;a+=p;l-=1}return r},fill:function fill(e){var t;if(arguments.length>1){t=arguments[1]}var r;if(arguments.length>2){r=arguments[2]}var n=ce.ToObject(this);var o=ce.ToLength(n.length);t=ce.ToInteger(typeof t===\"undefined\"?0:t);r=ce.ToInteger(typeof r===\"undefined\"?o:r);var i=t<0?A(o+t,0):R(t,o);var a=r<0?o+r:r;for(var u=i;u<o&&u<a;++u){n[u]=e}return n},find:function find(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#find: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0,a;i<n;i++){a=r[i];if(o){if(t(e,o,a,i,r)){return a}}else if(e(a,i,r)){return a}}},findIndex:function findIndex(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#findIndex: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0;i<n;i++){if(o){if(t(e,o,r[i],i,r)){return i}}else if(e(r[i],i,r)){return i}}return-1},keys:function keys(){return new q(this,\"key\")},values:function values(){return new q(this,\"value\")},entries:function entries(){return new q(this,\"entry\")}};if(Array.prototype.keys&&!ce.IsCallable([1].keys().next)){delete Array.prototype.keys}if(Array.prototype.entries&&!ce.IsCallable([1].entries().next)){delete Array.prototype.entries}if(Array.prototype.keys&&Array.prototype.entries&&!Array.prototype.values&&Array.prototype[ie]){b(Array.prototype,{values:Array.prototype[ie]});if(re.symbol($.unscopables)){Array.prototype[$.unscopables].values=true}}if(c&&Array.prototype.values&&Array.prototype.values.name!==\"values\"){var tt=Array.prototype.values;ne(Array.prototype,\"values\",function values(){return ce.Call(tt,this,arguments)});h(Array.prototype,ie,Array.prototype.values,true)}b(Array.prototype,et);if(1/[true].indexOf(true,-0)<0){h(Array.prototype,\"indexOf\",function indexOf(e){var t=E(this,arguments);if(t===0&&1/t<0){return 0}return t},true)}Me(Array.prototype,function(){return this.values()});if(Object.getPrototypeOf){Me(Object.getPrototypeOf([].values()))}var rt=function(){return a(function(){return Array.from({length:-1}).length===0})}();var nt=function(){var e=Array.from([0].entries());return e.length===1&&r(e[0])&&e[0][0]===0&&e[0][1]===0}();if(!rt||!nt){ne(Array,\"from\",Ye.from)}var ot=function(){return a(function(){return Array.from([0],void 0)})}();if(!ot){var it=Array.from;ne(Array,\"from\",function from(e){if(arguments.length>1&&typeof arguments[1]!==\"undefined\"){return ce.Call(it,this,arguments)}else{return t(it,this,e)}})}var at=-(Math.pow(2,32)-1);var ut=function(e,r){var n={length:at};n[r?(n.length>>>0)-1:0]=true;return a(function(){t(e,n,function(){throw new RangeError(\"should not reach here\")},[]);return true})};if(!ut(Array.prototype.forEach)){var ft=Array.prototype.forEach;ne(Array.prototype,\"forEach\",function forEach(e){return ce.Call(ft,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.map)){var st=Array.prototype.map;ne(Array.prototype,\"map\",function map(e){return ce.Call(st,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.filter)){var ct=Array.prototype.filter;ne(Array.prototype,\"filter\",function filter(e){return ce.Call(ct,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.some)){var lt=Array.prototype.some;ne(Array.prototype,\"some\",function some(e){return ce.Call(lt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.every)){var pt=Array.prototype.every;ne(Array.prototype,\"every\",function every(e){return ce.Call(pt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduce)){var vt=Array.prototype.reduce;ne(Array.prototype,\"reduce\",function reduce(e){return ce.Call(vt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduceRight,true)){var yt=Array.prototype.reduceRight;ne(Array.prototype,\"reduceRight\",function reduceRight(e){return ce.Call(yt,this.length>=0?this:[],arguments)},true)}var ht=Number(\"0o10\")!==8;var bt=Number(\"0b10\")!==2;var gt=y(Ue,function(e){return Number(e+0+e)===0});if(ht||bt||gt){var dt=Number;var mt=/^0b[01]+$/i;var Ot=/^0o[0-7]+$/i;var wt=mt.test.bind(mt);var jt=Ot.test.bind(Ot);var St=function(e){var t;if(typeof e.valueOf===\"function\"){t=e.valueOf();if(re.primitive(t)){return t}}if(typeof e.toString===\"function\"){t=e.toString();if(re.primitive(t)){return t}}throw new TypeError(\"No default value\")};var Tt=$e.test.bind($e);var It=Je.test.bind(Je);var Et=function(){var e=function Number(t){var r;if(arguments.length>0){r=re.primitive(t)?t:St(t,\"number\")}else{r=0}if(typeof r===\"string\"){r=ce.Call(Be,r);if(wt(r)){r=parseInt(C(r,2),2)}else if(jt(r)){r=parseInt(C(r,2),8)}else if(Tt(r)||It(r)){r=NaN}}var n=this;var o=a(function(){dt.prototype.valueOf.call(n);return true});if(n instanceof e&&!o){return new dt(r)}return dt(r)};return e}();Ee(dt,Et,{});b(Et,{NaN:dt.NaN,MAX_VALUE:dt.MAX_VALUE,MIN_VALUE:dt.MIN_VALUE,NEGATIVE_INFINITY:dt.NEGATIVE_INFINITY,POSITIVE_INFINITY:dt.POSITIVE_INFINITY});Number=Et;m.redefine(S,\"Number\",Et)}var Pt=Math.pow(2,53)-1;b(Number,{MAX_SAFE_INTEGER:Pt,MIN_SAFE_INTEGER:-Pt,EPSILON:2.220446049250313e-16,parseInt:S.parseInt,parseFloat:S.parseFloat,isFinite:K,isInteger:function isInteger(e){return K(e)&&ce.ToInteger(e)===e},isSafeInteger:function isSafeInteger(e){return Number.isInteger(e)&&k(e)<=Number.MAX_SAFE_INTEGER},isNaN:X});h(Number,\"parseInt\",S.parseInt,Number.parseInt!==S.parseInt);if([,1].find(function(){return true})===1){ne(Array.prototype,\"find\",et.find)}if([,1].findIndex(function(){return true})!==0){ne(Array.prototype,\"findIndex\",et.findIndex)}var Ct=Function.bind.call(Function.bind,Object.prototype.propertyIsEnumerable);var Mt=function ensureEnumerable(e,t){if(s&&Ct(e,t)){Object.defineProperty(e,t,{enumerable:false})}};var xt=function sliceArgs(){var e=Number(this);var t=arguments.length;var r=t-e;var n=new Array(r<0?0:r);for(var o=e;o<t;++o){n[o-e]=arguments[o]}return n};var Nt=function assignTo(e){return function assignToSource(t,r){t[r]=e[r];return t}};var At=function(e,t){var r=n(Object(t));var o;if(ce.IsCallable(Object.getOwnPropertySymbols)){o=v(Object.getOwnPropertySymbols(Object(t)),Ct(t))}return p(P(r,o||[]),Nt(t),e)};var Rt={assign:function(e,t){var r=ce.ToObject(e,\"Cannot convert undefined or null to object\");return p(ce.Call(xt,1,arguments),At,r)},is:function is(e,t){return ce.SameValue(e,t)}};var _t=Object.assign&&Object.preventExtensions&&function(){var e=Object.preventExtensions({1:2});try{Object.assign(e,\"xy\")}catch(t){return e[1]===\"y\"}}();if(_t){ne(Object,\"assign\",Rt.assign)}b(Object,Rt);if(s){var kt={setPrototypeOf:function(e,r){var n;var o=function(e,t){if(!ce.TypeIsObject(e)){throw new TypeError(\"cannot set prototype on a non-object\")}if(!(t===null||ce.TypeIsObject(t))){throw new TypeError(\"can only set prototype to an object or null\"+t)}};var i=function(e,r){o(e,r);t(n,e,r);return e};try{n=e.getOwnPropertyDescriptor(e.prototype,r).set;t(n,{},null)}catch(a){if(e.prototype!=={}[r]){return}n=function(e){this[r]=e};i.polyfill=i(i({},null),e.prototype)instanceof e}return i}(Object,\"__proto__\")};b(Object,kt)}if(Object.setPrototypeOf&&Object.getPrototypeOf&&Object.getPrototypeOf(Object.setPrototypeOf({},null))!==null&&Object.getPrototypeOf(Object.create(null))===null){(function(){var e=Object.create(null);var t=Object.getPrototypeOf;var r=Object.setPrototypeOf;Object.getPrototypeOf=function(r){var n=t(r);return n===e?null:n};Object.setPrototypeOf=function(t,n){var o=n===null?e:n;return r(t,o)};Object.setPrototypeOf.polyfill=false})()}var Lt=!i(function(){return Object.keys(\"foo\")});if(!Lt){var Ft=Object.keys;ne(Object,\"keys\",function keys(e){return Ft(ce.ToObject(e))});n=Object.keys}var Dt=i(function(){return Object.keys(/a/g)});if(Dt){var zt=Object.keys;ne(Object,\"keys\",function keys(e){if(re.regex(e)){var t=[];for(var r in e){if(z(e,r)){M(t,r)}}return t}return zt(e)});n=Object.keys}if(Object.getOwnPropertyNames){var qt=!i(function(){return Object.getOwnPropertyNames(\"foo\")});if(!qt){var Wt=typeof window===\"object\"?Object.getOwnPropertyNames(window):[];var Gt=Object.getOwnPropertyNames;ne(Object,\"getOwnPropertyNames\",function getOwnPropertyNames(e){var t=ce.ToObject(e);if(g(t)===\"[object Window]\"){try{return Gt(t)}catch(r){return P([],Wt)}}return Gt(t)})}}if(Object.getOwnPropertyDescriptor){var Ht=!i(function(){return Object.getOwnPropertyDescriptor(\"foo\",\"bar\")});if(!Ht){var Vt=Object.getOwnPropertyDescriptor;ne(Object,\"getOwnPropertyDescriptor\",function getOwnPropertyDescriptor(e,t){return Vt(ce.ToObject(e),t)})}}if(Object.seal){var Bt=!i(function(){return Object.seal(\"foo\")});if(!Bt){var Ut=Object.seal;ne(Object,\"seal\",function seal(e){if(!ce.TypeIsObject(e)){return e}return Ut(e)})}}if(Object.isSealed){var $t=!i(function(){return Object.isSealed(\"foo\")});if(!$t){var Jt=Object.isSealed;ne(Object,\"isSealed\",function isSealed(e){if(!ce.TypeIsObject(e)){return true}return Jt(e)})}}if(Object.freeze){var Xt=!i(function(){return Object.freeze(\"foo\")});if(!Xt){var Kt=Object.freeze;ne(Object,\"freeze\",function freeze(e){if(!ce.TypeIsObject(e)){return e}return Kt(e)})}}if(Object.isFrozen){var Zt=!i(function(){return Object.isFrozen(\"foo\")});if(!Zt){var Yt=Object.isFrozen;ne(Object,\"isFrozen\",function isFrozen(e){if(!ce.TypeIsObject(e)){return true}return Yt(e)})}}if(Object.preventExtensions){var Qt=!i(function(){return Object.preventExtensions(\"foo\")});if(!Qt){var er=Object.preventExtensions;ne(Object,\"preventExtensions\",function preventExtensions(e){if(!ce.TypeIsObject(e)){return e}return er(e)})}}if(Object.isExtensible){var tr=!i(function(){return Object.isExtensible(\"foo\")});if(!tr){var rr=Object.isExtensible;ne(Object,\"isExtensible\",function isExtensible(e){if(!ce.TypeIsObject(e)){return false}return rr(e)})}}if(Object.getPrototypeOf){var nr=!i(function(){return Object.getPrototypeOf(\"foo\")});if(!nr){var or=Object.getPrototypeOf;ne(Object,\"getPrototypeOf\",function getPrototypeOf(e){return or(ce.ToObject(e))})}}var ir=s&&function(){var e=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\");return e&&ce.IsCallable(e.get)}();if(s&&!ir){var ar=function flags(){if(!ce.TypeIsObject(this)){throw new TypeError(\"Method called on incompatible type: must be an object.\")}var e=\"\";if(this.global){e+=\"g\"}if(this.ignoreCase){e+=\"i\"}if(this.multiline){e+=\"m\"}if(this.unicode){e+=\"u\"}if(this.sticky){e+=\"y\"}return e};m.getter(RegExp.prototype,\"flags\",ar)}var ur=s&&a(function(){return String(new RegExp(/a/g,\"i\"))===\"/a/i\"});var fr=oe&&s&&function(){var e=/./;e[$.match]=false;return RegExp(e)===e}();var sr=a(function(){return RegExp.prototype.toString.call({source:\"abc\"})===\"/abc/\"});var cr=sr&&a(function(){return RegExp.prototype.toString.call({source:\"a\",flags:\"b\"})===\"/a/b\"});if(!sr||!cr){var lr=RegExp.prototype.toString;h(RegExp.prototype,\"toString\",function toString(){var e=ce.RequireObjectCoercible(this);if(re.regex(e)){return t(lr,e)}var r=ue(e.source);var n=ue(e.flags);return\"/\"+r+\"/\"+n},true);m.preserveToString(RegExp.prototype.toString,lr)}if(s&&(!ur||fr)){var pr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\").get;var vr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"source\")||{};var yr=function(){return this.source};var hr=ce.IsCallable(vr.get)?vr.get:yr;var br=RegExp;var gr=function(){return function RegExp(e,t){var r=ce.IsRegExp(e);var n=this instanceof RegExp;if(!n&&r&&typeof t===\"undefined\"&&e.constructor===RegExp){return e}var o=e;var i=t;if(re.regex(e)){o=ce.Call(hr,e);i=typeof t===\"undefined\"?ce.Call(pr,e):t;return new RegExp(o,i)}else if(r){o=e.source;i=typeof t===\"undefined\"?e.flags:t}return new br(e,t)}}();Ee(br,gr,{$input:true});RegExp=gr;m.redefine(S,\"RegExp\",gr)}if(s){var dr={input:\"$_\",lastMatch:\"$&\",lastParen:\"$+\",leftContext:\"$`\",rightContext:\"$'\"};l(n(dr),function(e){if(e in RegExp&&!(dr[e]in RegExp)){m.getter(RegExp,dr[e],function get(){return RegExp[e]})}})}Ce(RegExp);var mr=1/Number.EPSILON;var Or=function roundTiesToEven(e){return e+mr-mr};var wr=Math.pow(2,-23);var jr=Math.pow(2,127)*(2-wr);var Sr=Math.pow(2,-126);var Tr=Math.E;var Ir=Math.LOG2E;var Er=Math.LOG10E;var Pr=Number.prototype.clz;delete Number.prototype.clz;var Cr={acosh:function acosh(e){var t=Number(e);if(X(t)||e<1){return NaN}if(t===1){return 0}if(t===Infinity){return t}var r=1/(t*t);if(t<2){return Y(t-1+D(1-r)*t)}var n=t/2;return Y(n+D(1-r)*n-1)+1/Ir},asinh:function asinh(e){var t=Number(e);if(t===0||!T(t)){return t}var r=k(t);var n=r*r;var o=Z(t);if(r<1){return o*Y(r+n/(D(n+1)+1))}return o*(Y(r/2+D(1+1/n)*r/2-1)+1/Ir)},atanh:function atanh(e){var t=Number(e);if(t===0){return t}if(t===-1){return-Infinity}if(t===1){return Infinity}if(X(t)||t<-1||t>1){return NaN}var r=k(t);return Z(t)*Y(2*r/(1-r))/2},cbrt:function cbrt(e){var t=Number(e);if(t===0){return t}var r=t<0;var n;if(r){t=-t}if(t===Infinity){n=Infinity}else{n=L(F(t)/3);n=(t/(n*n)+2*n)/3}return r?-n:n},clz32:function clz32(e){var t=Number(e);var r=ce.ToUint32(t);if(r===0){return 32}return Pr?ce.Call(Pr,r):31-_(F(r+.5)*Ir)},cosh:function cosh(e){var t=Number(e);if(t===0){return 1}if(X(t)){return NaN}if(!T(t)){return Infinity}var r=L(k(t)-1);return(r+1/(r*Tr*Tr))*(Tr/2)},expm1:function expm1(e){var t=Number(e);if(t===-Infinity){return-1}if(!T(t)||t===0){return t}if(k(t)>.5){return L(t)-1}var r=t;var n=0;var o=1;while(n+r!==n){n+=r;o+=1;r*=t/o}return n},hypot:function hypot(e,t){var r=0;var n=0;for(var o=0;o<arguments.length;++o){var i=k(Number(arguments[o]));if(n<i){r*=n/i*(n/i);r+=1;n=i}else{r+=i>0?i/n*(i/n):i}}return n===Infinity?Infinity:n*D(r)},log2:function log2(e){return F(e)*Ir},log10:function log10(e){return F(e)*Er},log1p:Y,sign:Z,sinh:function sinh(e){var t=Number(e);if(!T(t)||t===0){return t}var r=k(t);if(r<1){var n=Math.expm1(r);return Z(t)*n*(1+1/(n+1))/2}var o=L(r-1);return Z(t)*(o-1/(o*Tr*Tr))*(Tr/2)},tanh:function tanh(e){var t=Number(e);if(X(t)||t===0){return t}if(t>=20){return 1}if(t<=-20){return-1}return(Math.expm1(t)-Math.expm1(-t))/(L(t)+L(-t))},trunc:function trunc(e){var t=Number(e);return t<0?-_(-t):_(t)},imul:function imul(e,t){var r=ce.ToUint32(e);var n=ce.ToUint32(t);var o=r>>>16&65535;var i=r&65535;var a=n>>>16&65535;var u=n&65535;return i*u+(o*u+i*a<<16>>>0)|0},fround:function fround(e){var t=Number(e);if(t===0||t===Infinity||t===-Infinity||X(t)){return t}var r=Z(t);var n=k(t);if(n<Sr){return r*Or(n/Sr/wr)*Sr*wr}var o=(1+wr/Number.EPSILON)*n;var i=o-(o-n);if(i>jr||X(i)){return r*Infinity}return r*i}};var Mr=function withinULPDistance(e,t,r){return k(1-e/t)/Number.EPSILON<(r||8)};b(Math,Cr);h(Math,\"sinh\",Cr.sinh,Math.sinh(710)===Infinity);h(Math,\"cosh\",Cr.cosh,Math.cosh(710)===Infinity);h(Math,\"log1p\",Cr.log1p,Math.log1p(-1e-17)!==-1e-17);h(Math,\"asinh\",Cr.asinh,Math.asinh(-1e7)!==-Math.asinh(1e7));h(Math,\"asinh\",Cr.asinh,Math.asinh(1e300)===Infinity);h(Math,\"atanh\",Cr.atanh,Math.atanh(1e-300)===0);h(Math,\"tanh\",Cr.tanh,Math.tanh(-2e-17)!==-2e-17);\nh(Math,\"acosh\",Cr.acosh,Math.acosh(Number.MAX_VALUE)===Infinity);h(Math,\"acosh\",Cr.acosh,!Mr(Math.acosh(1+Number.EPSILON),Math.sqrt(2*Number.EPSILON)));h(Math,\"cbrt\",Cr.cbrt,!Mr(Math.cbrt(1e-300),1e-100));h(Math,\"sinh\",Cr.sinh,Math.sinh(-2e-17)!==-2e-17);var xr=Math.expm1(10);h(Math,\"expm1\",Cr.expm1,xr>22025.465794806718||xr<22025.465794806718);var Nr=Math.round;var Ar=Math.round(.5-Number.EPSILON/4)===0&&Math.round(-.5+Number.EPSILON/3.99)===1;var Rr=mr+1;var _r=2*mr-1;var kr=[Rr,_r].every(function(e){return Math.round(e)===e});h(Math,\"round\",function round(e){var t=_(e);var r=t===-1?-0:t+1;return e-t<.5?t:r},!Ar||!kr);m.preserveToString(Math.round,Nr);var Lr=Math.imul;if(Math.imul(4294967295,5)!==-5){Math.imul=Cr.imul;m.preserveToString(Math.imul,Lr)}if(Math.imul.length!==2){ne(Math,\"imul\",function imul(e,t){return ce.Call(Lr,Math,arguments)})}var Fr=function(){var e=S.setTimeout;if(typeof e!==\"function\"&&typeof e!==\"object\"){return}ce.IsPromise=function(e){if(!ce.TypeIsObject(e)){return false}if(typeof e._promise===\"undefined\"){return false}return true};var r=function(e){if(!ce.IsConstructor(e)){throw new TypeError(\"Bad promise constructor\")}var t=this;var r=function(e,r){if(t.resolve!==void 0||t.reject!==void 0){throw new TypeError(\"Bad Promise implementation!\")}t.resolve=e;t.reject=r};t.resolve=void 0;t.reject=void 0;t.promise=new e(r);if(!(ce.IsCallable(t.resolve)&&ce.IsCallable(t.reject))){throw new TypeError(\"Bad promise constructor\")}};var n;if(typeof window!==\"undefined\"&&ce.IsCallable(window.postMessage)){n=function(){var e=[];var t=\"zero-timeout-message\";var r=function(r){M(e,r);window.postMessage(t,\"*\")};var n=function(r){if(r.source===window&&r.data===t){r.stopPropagation();if(e.length===0){return}var n=N(e);n()}};window.addEventListener(\"message\",n,true);return r}}var o=function(){var e=S.Promise;var t=e&&e.resolve&&e.resolve();return t&&function(e){return t.then(e)}};var i=ce.IsCallable(S.setImmediate)?S.setImmediate:typeof process===\"object\"&&process.nextTick?process.nextTick:o()||(ce.IsCallable(n)?n():function(t){e(t,0)});var a=function(e){return e};var u=function(e){throw e};var f=0;var s=1;var c=2;var l=0;var p=1;var v=2;var y={};var h=function(e,t,r){i(function(){g(e,t,r)})};var g=function(e,t,r){var n,o;if(t===y){return e(r)}try{n=e(r);o=t.resolve}catch(i){n=i;o=t.reject}o(n)};var d=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.fulfillReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+l],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=s;r.reactionLength=0};var m=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.rejectReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+p],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=c;r.reactionLength=0};var O=function(e){var t=false;var r=function(r){var n;if(t){return}t=true;if(r===e){return m(e,new TypeError(\"Self resolution\"))}if(!ce.TypeIsObject(r)){return d(e,r)}try{n=r.then}catch(o){return m(e,o)}if(!ce.IsCallable(n)){return d(e,r)}i(function(){j(e,r,n)})};var n=function(r){if(t){return}t=true;return m(e,r)};return{resolve:r,reject:n}};var w=function(e,r,n,o){if(e===I){t(e,r,n,o,y)}else{t(e,r,n,o)}};var j=function(e,t,r){var n=O(e);var o=n.resolve;var i=n.reject;try{w(r,t,o,i)}catch(a){i(a)}};var T,I;var E=function(){var e=function Promise(t){if(!(this instanceof e)){throw new TypeError('Constructor Promise requires \"new\"')}if(this&&this._promise){throw new TypeError(\"Bad construction\")}if(!ce.IsCallable(t)){throw new TypeError(\"not a valid resolver\")}var r=Ae(this,e,T,{_promise:{result:void 0,state:f,reactionLength:0,fulfillReactionHandler0:void 0,rejectReactionHandler0:void 0,reactionCapability0:void 0}});var n=O(r);var o=n.reject;try{t(n.resolve,o)}catch(i){o(i)}return r};return e}();T=E.prototype;var P=function(e,t,r,n){var o=false;return function(i){if(o){return}o=true;t[e]=i;if(--n.count===0){var a=r.resolve;a(t)}}};var C=function(e,t,r){var n=e.iterator;var o=[];var i={count:1};var a,u;var f=0;while(true){try{a=ce.IteratorStep(n);if(a===false){e.done=true;break}u=a.value}catch(s){e.done=true;throw s}o[f]=void 0;var c=t.resolve(u);var l=P(f,o,r,i);i.count+=1;w(c.then,c,l,r.reject);f+=1}if(--i.count===0){var p=r.resolve;p(o)}return r.promise};var x=function(e,t,r){var n=e.iterator;var o,i,a;while(true){try{o=ce.IteratorStep(n);if(o===false){e.done=true;break}i=o.value}catch(u){e.done=true;throw u}a=t.resolve(i);w(a.then,a,r.resolve,r.reject)}return r.promise};b(E,{all:function all(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return C(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},race:function race(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return x(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},reject:function reject(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}var n=new r(t);var o=n.reject;o(e);return n.promise},resolve:function resolve(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}if(ce.IsPromise(e)){var n=e.constructor;if(n===t){return e}}var o=new r(t);var i=o.resolve;i(e);return o.promise}});b(T,{\"catch\":function(e){return this.then(null,e)},then:function then(e,t){var n=this;if(!ce.IsPromise(n)){throw new TypeError(\"not a promise\")}var o=ce.SpeciesConstructor(n,E);var i;var b=arguments.length>2&&arguments[2]===y;if(b&&o===E){i=y}else{i=new r(o)}var g=ce.IsCallable(e)?e:a;var d=ce.IsCallable(t)?t:u;var m=n._promise;var O;if(m.state===f){if(m.reactionLength===0){m.fulfillReactionHandler0=g;m.rejectReactionHandler0=d;m.reactionCapability0=i}else{var w=3*(m.reactionLength-1);m[w+l]=g;m[w+p]=d;m[w+v]=i}m.reactionLength+=1}else if(m.state===s){O=m.result;h(g,i,O)}else if(m.state===c){O=m.result;h(d,i,O)}else{throw new TypeError(\"unexpected Promise state\")}return i.promise}});y=new r(E);I=T.then;return E}();if(S.Promise){delete S.Promise.accept;delete S.Promise.defer;delete S.Promise.prototype.chain}if(typeof Fr===\"function\"){b(S,{Promise:Fr});var Dr=w(S.Promise,function(e){return e.resolve(42).then(function(){})instanceof e});var zr=!i(function(){return S.Promise.reject(42).then(null,5).then(null,W)});var qr=i(function(){return S.Promise.call(3,W)});var Wr=function(e){var t=e.resolve(5);t.constructor={};var r=e.resolve(t);try{r.then(null,W).then(null,W)}catch(n){return true}return t===r}(S.Promise);var Gr=s&&function(){var e=0;var t=Object.defineProperty({},\"then\",{get:function(){e+=1}});Promise.resolve(t);return e===1}();var Hr=function BadResolverPromise(e){var t=new Promise(e);e(3,function(){});this.then=t.then;this.constructor=BadResolverPromise};Hr.prototype=Promise.prototype;Hr.all=Promise.all;var Vr=a(function(){return!!Hr.all([1,2])});if(!Dr||!zr||!qr||Wr||!Gr||Vr){Promise=Fr;ne(S,\"Promise\",Fr)}if(Promise.all.length!==1){var Br=Promise.all;ne(Promise,\"all\",function all(e){return ce.Call(Br,this,arguments)})}if(Promise.race.length!==1){var Ur=Promise.race;ne(Promise,\"race\",function race(e){return ce.Call(Ur,this,arguments)})}if(Promise.resolve.length!==1){var $r=Promise.resolve;ne(Promise,\"resolve\",function resolve(e){return ce.Call($r,this,arguments)})}if(Promise.reject.length!==1){var Jr=Promise.reject;ne(Promise,\"reject\",function reject(e){return ce.Call(Jr,this,arguments)})}Mt(Promise,\"all\");Mt(Promise,\"race\");Mt(Promise,\"resolve\");Mt(Promise,\"reject\");Ce(Promise)}var Xr=function(e){var t=n(p(e,function(e,t){e[t]=true;return e},{}));return e.join(\":\")===t.join(\":\")};var Kr=Xr([\"z\",\"a\",\"bb\"]);var Zr=Xr([\"z\",1,\"a\",\"3\",2]);if(s){var Yr=function fastkey(e,t){if(!t&&!Kr){return null}if(se(e)){return\"^\"+ce.ToString(e)}else if(typeof e===\"string\"){return\"$\"+e}else if(typeof e===\"number\"){if(!Zr){return\"n\"+e}return e}else if(typeof e===\"boolean\"){return\"b\"+e}return null};var Qr=function emptyObject(){return Object.create?Object.create(null):{}};var en=function addIterableToMap(e,n,o){if(r(o)||re.string(o)){l(o,function(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"Iterator value \"+e+\" is not an entry object\")}n.set(e[0],e[1])})}else if(o instanceof e){t(e.prototype.forEach,o,function(e,t){n.set(t,e)})}else{var i,a;if(!se(o)){a=n.set;if(!ce.IsCallable(a)){throw new TypeError(\"bad map\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{if(!ce.TypeIsObject(f)){throw new TypeError(\"Iterator value \"+f+\" is not an entry object\")}t(a,n,f[0],f[1])}catch(s){ce.IteratorClose(i,true);throw s}}}}};var tn=function addIterableToSet(e,n,o){if(r(o)||re.string(o)){l(o,function(e){n.add(e)})}else if(o instanceof e){t(e.prototype.forEach,o,function(e){n.add(e)})}else{var i,a;if(!se(o)){a=n.add;if(!ce.IsCallable(a)){throw new TypeError(\"bad set\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{t(a,n,f)}catch(s){ce.IteratorClose(i,true);throw s}}}}};var rn={Map:function(){var e={};var r=function MapEntry(e,t){this.key=e;this.value=t;this.next=null;this.prev=null};r.prototype.isRemoved=function isRemoved(){return this.key===e};var n=function isMap(e){return!!e._es6map};var o=function requireMapSlot(e,t){if(!ce.TypeIsObject(e)||!n(e)){throw new TypeError(\"Method Map.prototype.\"+t+\" called on incompatible receiver \"+ce.ToString(e))}};var i=function MapIterator(e,t){o(e,\"[[MapIterator]]\");this.head=e._head;this.i=this.head;this.kind=t};i.prototype={isMapIterator:true,next:function next(){if(!this.isMapIterator){throw new TypeError(\"Not a MapIterator\")}var e=this.i;var t=this.kind;var r=this.head;if(typeof this.i===\"undefined\"){return Ke()}while(e.isRemoved()&&e!==r){e=e.prev}var n;while(e.next!==r){e=e.next;if(!e.isRemoved()){if(t===\"key\"){n=e.key}else if(t===\"value\"){n=e.value}else{n=[e.key,e.value]}this.i=e;return Ke(n)}}this.i=void 0;return Ke()}};Me(i.prototype);var a;var u=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}if(this&&this._es6map){throw new TypeError(\"Bad construction\")}var e=Ae(this,Map,a,{_es6map:true,_head:null,_map:G?new G:null,_size:0,_storage:Qr()});var t=new r(null,null);t.next=t.prev=t;e._head=t;if(arguments.length>0){en(Map,e,arguments[0])}return e};a=u.prototype;m.getter(a,\"size\",function(){if(typeof this._size===\"undefined\"){throw new TypeError(\"size method called on incompatible Map\")}return this._size});b(a,{get:function get(e){o(this,\"get\");var t;var r=Yr(e,true);if(r!==null){t=this._storage[r];if(t){return t.value}else{return}}if(this._map){t=V.call(this._map,e);if(t){return t.value}else{return}}var n=this._head;var i=n;while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){return i.value}}},has:function has(e){o(this,\"has\");var t=Yr(e,true);if(t!==null){return typeof this._storage[t]!==\"undefined\"}if(this._map){return B.call(this._map,e)}var r=this._head;var n=r;while((n=n.next)!==r){if(ce.SameValueZero(n.key,e)){return true}}return false},set:function set(e,t){o(this,\"set\");var n=this._head;var i=n;var a;var u=Yr(e,true);if(u!==null){if(typeof this._storage[u]!==\"undefined\"){this._storage[u].value=t;return this}else{a=this._storage[u]=new r(e,t);i=n.prev}}else if(this._map){if(B.call(this._map,e)){V.call(this._map,e).value=t}else{a=new r(e,t);U.call(this._map,e,a);i=n.prev}}while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){i.value=t;return this}}a=a||new r(e,t);if(ce.SameValue(-0,e)){a.key=+0}a.next=this._head;a.prev=this._head.prev;a.prev.next=a;a.next.prev=a;this._size+=1;return this},\"delete\":function(t){o(this,\"delete\");var r=this._head;var n=r;var i=Yr(t,true);if(i!==null){if(typeof this._storage[i]===\"undefined\"){return false}n=this._storage[i].prev;delete this._storage[i]}else if(this._map){if(!B.call(this._map,t)){return false}n=V.call(this._map,t).prev;H.call(this._map,t)}while((n=n.next)!==r){if(ce.SameValueZero(n.key,t)){n.key=e;n.value=e;n.prev.next=n.next;n.next.prev=n.prev;this._size-=1;return true}}return false},clear:function clear(){o(this,\"clear\");this._map=G?new G:null;this._size=0;this._storage=Qr();var t=this._head;var r=t;var n=r.next;while((r=n)!==t){r.key=e;r.value=e;n=r.next;r.next=r.prev=t}t.next=t.prev=t},keys:function keys(){o(this,\"keys\");return new i(this,\"key\")},values:function values(){o(this,\"values\");return new i(this,\"value\")},entries:function entries(){o(this,\"entries\");return new i(this,\"key+value\")},forEach:function forEach(e){o(this,\"forEach\");var r=arguments.length>1?arguments[1]:null;var n=this.entries();for(var i=n.next();!i.done;i=n.next()){if(r){t(e,r,i.value[1],i.value[0],this)}else{e(i.value[1],i.value[0],this)}}}});Me(a,a.entries);return u}(),Set:function(){var e=function isSet(e){return e._es6set&&typeof e._storage!==\"undefined\"};var r=function requireSetSlot(t,r){if(!ce.TypeIsObject(t)||!e(t)){throw new TypeError(\"Set.prototype.\"+r+\" called on incompatible receiver \"+ce.ToString(t))}};var o;var i=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}if(this&&this._es6set){throw new TypeError(\"Bad construction\")}var e=Ae(this,Set,o,{_es6set:true,\"[[SetData]]\":null,_storage:Qr()});if(!e._es6set){throw new TypeError(\"bad set\")}if(arguments.length>0){tn(Set,e,arguments[0])}return e};o=i.prototype;var a=function(e){var t=e;if(t===\"^null\"){return null}else if(t===\"^undefined\"){return void 0}else{var r=t.charAt(0);if(r===\"$\"){return C(t,1)}else if(r===\"n\"){return+C(t,1)}else if(r===\"b\"){return t===\"btrue\"}}return+t};var u=function ensureMap(e){if(!e[\"[[SetData]]\"]){var t=new rn.Map;e[\"[[SetData]]\"]=t;l(n(e._storage),function(e){var r=a(e);t.set(r,r)});e[\"[[SetData]]\"]=t}e._storage=null};m.getter(i.prototype,\"size\",function(){r(this,\"size\");if(this._storage){return n(this._storage).length}u(this);return this[\"[[SetData]]\"].size});b(i.prototype,{has:function has(e){r(this,\"has\");var t;if(this._storage&&(t=Yr(e))!==null){return!!this._storage[t]}u(this);return this[\"[[SetData]]\"].has(e)},add:function add(e){r(this,\"add\");var t;if(this._storage&&(t=Yr(e))!==null){this._storage[t]=true;return this}u(this);this[\"[[SetData]]\"].set(e,e);return this},\"delete\":function(e){r(this,\"delete\");var t;if(this._storage&&(t=Yr(e))!==null){var n=z(this._storage,t);return delete this._storage[t]&&n}u(this);return this[\"[[SetData]]\"][\"delete\"](e)},clear:function clear(){r(this,\"clear\");if(this._storage){this._storage=Qr()}if(this[\"[[SetData]]\"]){this[\"[[SetData]]\"].clear()}},values:function values(){r(this,\"values\");u(this);return new f(this[\"[[SetData]]\"].values())},entries:function entries(){r(this,\"entries\");u(this);return new f(this[\"[[SetData]]\"].entries())},forEach:function forEach(e){r(this,\"forEach\");var n=arguments.length>1?arguments[1]:null;var o=this;u(o);this[\"[[SetData]]\"].forEach(function(r,i){if(n){t(e,n,i,i,o)}else{e(i,i,o)}})}});h(i.prototype,\"keys\",i.prototype.values,true);Me(i.prototype,i.prototype.values);var f=function SetIterator(e){this.it=e};f.prototype={isSetIterator:true,next:function next(){if(!this.isSetIterator){throw new TypeError(\"Not a SetIterator\")}return this.it.next()}};Me(f.prototype);return i}()};var nn=S.Set&&!Set.prototype[\"delete\"]&&Set.prototype.remove&&Set.prototype.items&&Set.prototype.map&&Array.isArray((new Set).keys);if(nn){S.Set=rn.Set}if(S.Map||S.Set){var on=a(function(){return new Map([[1,2]]).get(1)===2});if(!on){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,S.Map.prototype);return e};S.Map.prototype=O(G.prototype);h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var an=new Map;var un=function(){var e=new Map([[1,0],[2,0],[3,0],[4,0]]);e.set(-0,e);return e.get(0)===e&&e.get(-0)===e&&e.has(0)&&e.has(-0)}();var fn=an.set(1,2)===an;if(!un||!fn){ne(Map.prototype,\"set\",function set(e,r){t(U,this,e===0?0:e,r);return this})}if(!un){b(Map.prototype,{get:function get(e){return t(V,this,e===0?0:e)},has:function has(e){return t(B,this,e===0?0:e)}},true);m.preserveToString(Map.prototype.get,V);m.preserveToString(Map.prototype.has,B)}var sn=new Set;var cn=Set.prototype[\"delete\"]&&Set.prototype.add&&Set.prototype.has&&function(e){e[\"delete\"](0);e.add(-0);return!e.has(0)}(sn);var ln=sn.add(1)===sn;if(!cn||!ln){var pn=Set.prototype.add;Set.prototype.add=function add(e){t(pn,this,e===0?0:e);return this};m.preserveToString(Set.prototype.add,pn)}if(!cn){var vn=Set.prototype.has;Set.prototype.has=function has(e){return t(vn,this,e===0?0:e)};m.preserveToString(Set.prototype.has,vn);var yn=Set.prototype[\"delete\"];Set.prototype[\"delete\"]=function SetDelete(e){return t(yn,this,e===0?0:e)};m.preserveToString(Set.prototype[\"delete\"],yn)}var hn=w(S.Map,function(e){var t=new e([]);t.set(42,42);return t instanceof e});var bn=Object.setPrototypeOf&&!hn;var gn=function(){try{return!(S.Map()instanceof S.Map)}catch(e){return e instanceof TypeError}}();if(S.Map.length!==0||bn||!gn){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Map.prototype);return e};S.Map.prototype=G.prototype;h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var dn=w(S.Set,function(e){var t=new e([]);t.add(42,42);return t instanceof e});var mn=Object.setPrototypeOf&&!dn;var On=function(){try{return!(S.Set()instanceof S.Set)}catch(e){return e instanceof TypeError}}();if(S.Set.length!==0||mn||!On){var wn=S.Set;S.Set=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}var e=new wn;if(arguments.length>0){tn(Set,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Set.prototype);return e};S.Set.prototype=wn.prototype;h(S.Set.prototype,\"constructor\",S.Set,true);m.preserveToString(S.Set,wn)}var jn=new S.Map;var Sn=!a(function(){return jn.keys().next().done});if(typeof S.Map.prototype.clear!==\"function\"||(new S.Set).size!==0||jn.size!==0||typeof S.Map.prototype.keys!==\"function\"||typeof S.Set.prototype.keys!==\"function\"||typeof S.Map.prototype.forEach!==\"function\"||typeof S.Set.prototype.forEach!==\"function\"||u(S.Map)||u(S.Set)||typeof jn.keys().next!==\"function\"||Sn||!hn){b(S,{Map:rn.Map,Set:rn.Set},true)}if(S.Set.prototype.keys!==S.Set.prototype.values){h(S.Set.prototype,\"keys\",S.Set.prototype.values,true)}Me(Object.getPrototypeOf((new S.Map).keys()));Me(Object.getPrototypeOf((new S.Set).keys()));if(c&&S.Set.prototype.has.name!==\"has\"){var Tn=S.Set.prototype.has;ne(S.Set.prototype,\"has\",function has(e){return t(Tn,this,e)})}}b(S,rn);Ce(S.Map);Ce(S.Set)}var In=function throwUnlessTargetIsObject(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"target must be an object\")}};var En={apply:function apply(){return ce.Call(ce.Call,null,arguments)},construct:function construct(e,t){if(!ce.IsConstructor(e)){throw new TypeError(\"First argument must be a constructor.\")}var r=arguments.length>2?arguments[2]:e;if(!ce.IsConstructor(r)){throw new TypeError(\"new.target must be a constructor.\")}return ce.Construct(e,t,r,\"internal\")},deleteProperty:function deleteProperty(e,t){In(e);if(s){var r=Object.getOwnPropertyDescriptor(e,t);if(r&&!r.configurable){return false}}return delete e[t]},has:function has(e,t){In(e);return t in e}};if(Object.getOwnPropertyNames){Object.assign(En,{ownKeys:function ownKeys(e){In(e);var t=Object.getOwnPropertyNames(e);if(ce.IsCallable(Object.getOwnPropertySymbols)){x(t,Object.getOwnPropertySymbols(e))}return t}})}var Pn=function ConvertExceptionToBoolean(e){return!i(e)};if(Object.preventExtensions){Object.assign(En,{isExtensible:function isExtensible(e){In(e);return Object.isExtensible(e)},preventExtensions:function preventExtensions(e){In(e);return Pn(function(){return Object.preventExtensions(e)})}})}if(s){var Cn=function get(e,t,r){var n=Object.getOwnPropertyDescriptor(e,t);if(!n){var o=Object.getPrototypeOf(e);if(o===null){return void 0}return Cn(o,t,r)}if(\"value\"in n){return n.value}if(n.get){return ce.Call(n.get,r)}return void 0};var Mn=function set(e,r,n,o){var i=Object.getOwnPropertyDescriptor(e,r);if(!i){var a=Object.getPrototypeOf(e);if(a!==null){return Mn(a,r,n,o)}i={value:void 0,writable:true,enumerable:true,configurable:true}}if(\"value\"in i){if(!i.writable){return false}if(!ce.TypeIsObject(o)){return false}var u=Object.getOwnPropertyDescriptor(o,r);if(u){return ae.defineProperty(o,r,{value:n})}else{return ae.defineProperty(o,r,{value:n,writable:true,enumerable:true,configurable:true})}}if(i.set){t(i.set,o,n);return true}return false};Object.assign(En,{defineProperty:function defineProperty(e,t,r){In(e);return Pn(function(){return Object.defineProperty(e,t,r)})},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(e,t){In(e);return Object.getOwnPropertyDescriptor(e,t)},get:function get(e,t){In(e);var r=arguments.length>2?arguments[2]:e;return Cn(e,t,r)},set:function set(e,t,r){In(e);var n=arguments.length>3?arguments[3]:e;return Mn(e,t,r,n)}})}if(Object.getPrototypeOf){var xn=Object.getPrototypeOf;En.getPrototypeOf=function getPrototypeOf(e){In(e);return xn(e)}}if(Object.setPrototypeOf&&En.getPrototypeOf){var Nn=function(e,t){var r=t;while(r){if(e===r){return true}r=En.getPrototypeOf(r)}return false};Object.assign(En,{setPrototypeOf:function setPrototypeOf(e,t){In(e);if(t!==null&&!ce.TypeIsObject(t)){throw new TypeError(\"proto must be an object or null\")}if(t===ae.getPrototypeOf(e)){return true}if(ae.isExtensible&&!ae.isExtensible(e)){return false}if(Nn(e,t)){return false}Object.setPrototypeOf(e,t);return true}})}var An=function(e,t){if(!ce.IsCallable(S.Reflect[e])){h(S.Reflect,e,t)}else{var r=a(function(){S.Reflect[e](1);S.Reflect[e](NaN);S.Reflect[e](true);return true});if(r){ne(S.Reflect,e,t)}}};Object.keys(En).forEach(function(e){An(e,En[e])});var Rn=S.Reflect.getPrototypeOf;if(c&&Rn&&Rn.name!==\"getPrototypeOf\"){ne(S.Reflect,\"getPrototypeOf\",function getPrototypeOf(e){return t(Rn,S.Reflect,e)})}if(S.Reflect.setPrototypeOf){if(a(function(){S.Reflect.setPrototypeOf(1,{});return true})){ne(S.Reflect,\"setPrototypeOf\",En.setPrototypeOf)}}if(S.Reflect.defineProperty){if(!a(function(){var e=!S.Reflect.defineProperty(1,\"test\",{value:1});var t=typeof Object.preventExtensions!==\"function\"||!S.Reflect.defineProperty(Object.preventExtensions({}),\"test\",{});return e&&t})){ne(S.Reflect,\"defineProperty\",En.defineProperty)}}if(S.Reflect.construct){if(!a(function(){var e=function F(){};return S.Reflect.construct(function(){},[],e)instanceof e})){ne(S.Reflect,\"construct\",En.construct)}}if(String(new Date(NaN))!==\"Invalid Date\"){var _n=Date.prototype.toString;var kn=function toString(){var e=+this;if(e!==e){return\"Invalid Date\"}return ce.Call(_n,this)};ne(Date.prototype,\"toString\",kn)}var Ln={anchor:function anchor(e){return ce.CreateHTML(this,\"a\",\"name\",e)},big:function big(){return ce.CreateHTML(this,\"big\",\"\",\"\")},blink:function blink(){return ce.CreateHTML(this,\"blink\",\"\",\"\")},bold:function bold(){return ce.CreateHTML(this,\"b\",\"\",\"\")},fixed:function fixed(){return ce.CreateHTML(this,\"tt\",\"\",\"\")},fontcolor:function fontcolor(e){return ce.CreateHTML(this,\"font\",\"color\",e)},fontsize:function fontsize(e){return ce.CreateHTML(this,\"font\",\"size\",e)},italics:function italics(){return ce.CreateHTML(this,\"i\",\"\",\"\")},link:function link(e){return ce.CreateHTML(this,\"a\",\"href\",e)},small:function small(){return ce.CreateHTML(this,\"small\",\"\",\"\")},strike:function strike(){return ce.CreateHTML(this,\"strike\",\"\",\"\")},sub:function sub(){return ce.CreateHTML(this,\"sub\",\"\",\"\")},sup:function sub(){return ce.CreateHTML(this,\"sup\",\"\",\"\")}};l(Object.keys(Ln),function(e){var r=String.prototype[e];var n=false;if(ce.IsCallable(r)){var o=t(r,\"\",' \" ');var i=P([],o.match(/\"/g)).length;n=o!==o.toLowerCase()||i>2}else{n=true}if(n){ne(String.prototype,e,Ln[e])}});var Fn=function(){if(!oe){return false}var e=typeof JSON===\"object\"&&typeof JSON.stringify===\"function\"?JSON.stringify:null;if(!e){return false}if(typeof e($())!==\"undefined\"){return true}if(e([$()])!==\"[null]\"){return true}var t={a:$()};t[$()]=true;if(e(t)!==\"{}\"){return true}return false}();var Dn=a(function(){if(!oe){return true}return JSON.stringify(Object($()))===\"{}\"&&JSON.stringify([Object($())])===\"[{}]\"});if(Fn||!Dn){var zn=JSON.stringify;ne(JSON,\"stringify\",function stringify(e){if(typeof e===\"symbol\"){return}var n;if(arguments.length>1){n=arguments[1]}var o=[e];if(!r(n)){var i=ce.IsCallable(n)?n:null;var a=function(e,r){var n=i?t(i,this,e,r):r;if(typeof n!==\"symbol\"){if(re.symbol(n)){return Nt({})(n)}else{return n}}};o.push(a)}else{o.push(n)}if(arguments.length>2){o.push(arguments[2])}return zn.apply(this,o)})}return S});\n/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(C,e){\"use strict\";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return\"function\"==typeof e&&\"number\"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement(\"script\");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?n[o.call(e)]||\"object\":typeof e}var f=\"3.5.1\",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&\"length\"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&(\"array\"===n||0===t||\"number\"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for(\"boolean\"==typeof a&&(l=a,a=arguments[s]||{},s++),\"object\"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],\"__proto__\"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:\"jQuery\"+(f+Math.random()).replace(/\\D/g,\"\"),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||\"[object Object]\"!==o.call(e))&&(!(t=r(e))||\"function\"==typeof(n=v.call(t,\"constructor\")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,\"string\"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),\"function\"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){n[\"[object \"+t+\"]\"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S=\"sizzle\"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R=\"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",M=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",I=\"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",W=\"\\\\[\"+M+\"*(\"+I+\")(?:\"+M+\"*([*^$|!~]?=)\"+M+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+I+\"))|)\"+M+\"*\\\\]\",F=\":(\"+I+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+W+\")*)|.*)\\\\)|)\",B=new RegExp(M+\"+\",\"g\"),$=new RegExp(\"^\"+M+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+M+\"+$\",\"g\"),_=new RegExp(\"^\"+M+\"*,\"+M+\"*\"),z=new RegExp(\"^\"+M+\"*([>+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp(F),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+F),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+M+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+M+\"*(?:([+-]|)\"+M+\"*(\\\\d+)|))\"+M+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+R+\")$\",\"i\"),needsContext:new RegExp(\"^\"+M+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+M+\"*((?:-\\\\d)?\\\\d*)\"+M+\"*\\\\)|)(?=[^-]|$)\",\"i\")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),ne=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?\"\\0\"===e?\"\\ufffd\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&\"fieldset\"===e.nodeName.toLowerCase()},{dir:\"parentNode\",next:\"legend\"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],\"string\"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?\"#\"+s:\":scope\")+\" \"+xe(l[o]);c=l.join(\",\")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute(\"id\")}}}return g(t.replace($,\"$1\"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+\" \")>b.cacheLength&&delete e[r.shift()],e[t+\" \"]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split(\"|\"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return\"input\"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return(\"input\"===t||\"button\"===t)&&e.type===n}}function ge(t){return function(e){return\"form\"in e?e.parentNode&&!1===e.disabled?\"label\"in e?\"label\"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:\"label\"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||\"HTML\")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement(\"div\")),\"undefined\"!=typeof e.querySelectorAll&&!e.querySelectorAll(\":scope fieldset div\").length}),d.attributes=ce(function(e){return e.className=\"i\",!e.getAttribute(\"className\")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute(\"id\")===t}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=\"undefined\"!=typeof e.getAttributeNode&&e.getAttributeNode(\"id\");return t&&t.value===n}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(\"undefined\"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=\"<a id='\"+S+\"'></a><select id='\"+S+\"-\\r\\\\' msallowcapture=''><option selected=''></option></select>\",e.querySelectorAll(\"[msallowcapture^='']\").length&&v.push(\"[*^$]=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\"[selected]\").length||v.push(\"\\\\[\"+M+\"*(?:value|\"+R+\")\"),e.querySelectorAll(\"[id~=\"+S+\"-]\").length||v.push(\"~=\"),(t=C.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||v.push(\"\\\\[\"+M+\"*name\"+M+\"*=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+S+\"+*\").length||v.push(\".#.+[+~]\"),e.querySelectorAll(\"\\\\\\f\"),v.push(\"[\\\\r\\\\n\\\\f]\")}),ce(function(e){e.innerHTML=\"<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>\";var t=C.createElement(\"input\");t.setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),e.querySelectorAll(\"[name=d]\").length&&v.push(\"name\"+M+\"*[*^$|!~]?=\"),2!==e.querySelectorAll(\":enabled\").length&&v.push(\":enabled\",\":disabled\"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&v.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),v.push(\",.*:\")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,\"*\"),c.call(e,\"[s!='']:x\"),s.push(\"!=\",F)}),v=v.length&&new RegExp(v.join(\"|\")),s=s.length&&new RegExp(s.join(\"|\")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+\" \"]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+\"\").replace(re,ie)},se.error=function(e){throw new Error(\"Syntax error, unrecognized expression: \"+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||\"\").replace(te,ne),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&m(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?\"!=\"===r:!r||(t+=\"\",\"=\"===r?t===i:\"!=\"===r?t!==i:\"^=\"===r?i&&0===t.indexOf(i):\"*=\"===r?i&&-1<t.indexOf(i):\"$=\"===r?i&&t.slice(-i.length)===i:\"~=\"===r?-1<(\" \"+t.replace(B,\" \")+\" \").indexOf(i):\"|=\"===r&&(t===i||t.slice(0,i.length+1)===i+\"-\"))}},CHILD:function(h,e,t,g,v){var y=\"nth\"!==h.slice(0,3),m=\"last\"!==h.slice(-4),x=\"of-type\"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?\"nextSibling\":\"previousSibling\",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l=\"only\"===h&&!u&&\"nextSibling\"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error(\"unsupported pseudo: \"+e);return a[S]?a(o):1<a.length?(t=[e,e,\"\",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,\"$1\"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||\"\")||se.error(\"unsupported lang: \"+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute(\"xml:lang\")||e.getAttribute(\"lang\"))return(t=t.toLowerCase())===n||0===t.indexOf(n+\"-\")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||\"text\"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r=\"\";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&\"parentNode\"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||\"*\",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[\" \"],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:\" \"===e[s-2].type?\"*\":\"\"})).replace($,\"$1\"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+\" \"];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($,\" \")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+\" \"];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l=\"0\",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG(\"*\",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l=\"function\"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&\"ID\"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split(\"\").sort(D).join(\"\")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement(\"fieldset\"))}),ce(function(e){return e.innerHTML=\"<a href='#'></a>\",\"#\"===e.firstChild.getAttribute(\"href\")})||fe(\"type|href|height|width\",function(e,t,n){if(!n)return e.getAttribute(t,\"type\"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML=\"<input/>\",e.firstChild.setAttribute(\"value\",\"\"),\"\"===e.firstChild.getAttribute(\"value\")})||fe(\"value\",function(e,t,n){if(!n&&\"input\"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute(\"disabled\")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[\":\"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,\"string\"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a=\"string\"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,\"parentNode\")},parentsUntil:function(e,t,n){return h(e,\"parentNode\",n)},next:function(e){return O(e,\"nextSibling\")},prev:function(e){return O(e,\"previousSibling\")},nextAll:function(e){return h(e,\"nextSibling\")},prevAll:function(e){return h(e,\"previousSibling\")},nextUntil:function(e,t,n){return h(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return h(e,\"previousSibling\",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,\"template\")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return\"Until\"!==r.slice(-5)&&(t=e),t&&\"string\"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\\x20\\t\\r\\n\\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r=\"string\"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:\"\")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&\"string\"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t=\"\",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=\"\"),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[[\"notify\",\"progress\",S.Callbacks(\"memory\"),S.Callbacks(\"memory\"),2],[\"resolve\",\"done\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),1,\"rejected\"]],i=\"pending\",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},\"catch\":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+\"With\"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError(\"Thenable self-resolution\");t=e&&(\"object\"==typeof e||\"function\"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+\"With\"](this===s?void 0:this,arguments),this},s[t[0]+\"With\"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),\"pending\"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn(\"jQuery.Deferred exception: \"+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener(\"DOMContentLoaded\",B),C.removeEventListener(\"load\",B),S.ready()}S.fn.ready=function(e){return F.then(e)[\"catch\"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,\"complete\"===E.readyState||\"loading\"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener(\"DOMContentLoaded\",B),C.addEventListener(\"load\",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if(\"object\"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,\"ms-\").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if(\"string\"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&\"string\"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(K,\"-$&\").toLowerCase(),\"string\"==typeof(n=e.getAttribute(r))){try{n=\"true\"===(i=n)||\"false\"!==i&&(\"null\"===i?null:i===+i+\"\"?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,\"hasDataAttrs\"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf(\"data-\")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,\"hasDataAttrs\",!0)}return i}return\"object\"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks(\"once memory\").add(function(){Y.remove(e,[t+\"queue\",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return\"string\"!=typeof t&&(n=t,t=\"fx\",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),\"fx\"===t&&\"inprogress\"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};\"string\"!=typeof e&&(t=e,e=void 0),e=e||\"fx\";while(a--)(n=Y.get(o[a],e+\"queueHooks\"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,te=new RegExp(\"^(?:([+-])=|)(\"+ee+\")([a-z%]*)$\",\"i\"),ne=[\"Top\",\"Right\",\"Bottom\",\"Left\"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return\"none\"===(e=t||e).style.display||\"\"===e.style.display&&ie(e)&&\"none\"===S.css(e,\"display\")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,\"\")},u=s(),l=n&&n[3]||(S.cssNumber[t]?\"\":\"px\"),c=e.nodeType&&(S.cssNumber[t]||\"px\"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?(\"none\"===n&&(l[c]=Y.get(r,\"display\")||null,l[c]||(r.style.display=\"\")),\"\"===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,\"display\"),o.parentNode.removeChild(o),\"none\"===u&&(u=\"block\"),ue[s]=u)))):\"none\"!==n&&(l[c]=\"none\",Y.set(r,\"display\",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(fe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),fe.setAttribute(\"checked\",\"checked\"),fe.setAttribute(\"name\",\"t\"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML=\"<textarea>x</textarea>\",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=\"<option></option>\",y.option=!!ce.lastChild;var ge={thead:[1,\"<table>\",\"</table>\"],col:[2,\"<table><colgroup>\",\"</colgroup></table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:[0,\"\",\"\"]};function ve(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],\"globalEval\",!t||Y.get(t[n],\"globalEval\"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,\"<select multiple='multiple'>\",\"</select>\"]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if(\"object\"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement(\"div\")),s=(de.exec(o)||[\"\",\"\"])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=\"\"}else p.push(t.createTextNode(o));f.textContent=\"\",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),\"script\"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||\"\")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==(\"focus\"===t)}function ke(e,t,n,r,i,o){var a,s;if(\"object\"==typeof t){for(s in\"string\"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&(\"string\"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return\"undefined\"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||\"\").match(P)||[\"\"]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(\".\")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||\"\").match(P)||[\"\"]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&(\"**\"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,\"events\")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!(\"click\"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(\"click\"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+\" \"]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Y.get(t,\"click\")||A(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,\"char\":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+\".\"+r.namespace:r.origType,r.selector,r.handler),this;if(\"object\"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&\"function\"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\\s*(?:[^=]|=\\s*.checked.)/i,je=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;function qe(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&S(e).children(\"tbody\")[0]||e}function Le(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function He(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&\"string\"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,\"script\"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,\"script\"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||\"\")&&!Y.access(u,\"globalEval\")&&S.contains(l,u)&&(u.src&&\"module\"!==(u.type||\"\").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute(\"nonce\")},l):b(u.textContent.replace(je,\"\"),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,\"script\")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,\"input\"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:\"input\"!==l&&\"textarea\"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,\"script\")).length&&ye(a,!f&&ve(e,\"script\")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp(\"^(\"+ee+\")(?!px)[a-z%]+$\",\"i\"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join(\"|\"),\"i\");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(\"\"!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+\"\":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText=\"position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0\",l.style.cssText=\"position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%\",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n=\"1%\"!==e.top,s=12===t(e.marginLeft),l.style.right=\"60%\",o=36===t(e.right),r=36===t(e.width),l.style.position=\"absolute\",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement(\"div\"),l=E.createElement(\"div\");l.style&&(l.style.backgroundClip=\"content-box\",l.cloneNode(!0).style.backgroundClip=\"\",y.clearCloneStyle=\"content-box\"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement(\"table\"),t=E.createElement(\"tr\"),n=E.createElement(\"div\"),e.style.cssText=\"position:absolute;left:-11111px\",t.style.height=\"1px\",n.style.height=\"9px\",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=[\"Webkit\",\"Moz\",\"ms\"],ze=E.createElement(\"div\").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Qe={letterSpacing:\"0\",fontWeight:\"400\"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ke(e,t,n,r,i,o){var a=\"width\"===t?1:0,s=0,u=0;if(n===(r?\"border\":\"content\"))return 0;for(;a<4;a+=2)\"margin\"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?(\"content\"===n&&(u-=S.css(e,\"padding\"+ne[a],!0,i)),\"margin\"!==n&&(u-=S.css(e,\"border\"+ne[a]+\"Width\",!0,i))):(u+=S.css(e,\"padding\"+ne[a],!0,i),\"padding\"!==n?u+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i):s+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,r),o=i,a=Be(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a=\"auto\"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===S.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===S.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?\"\":\"px\")),y.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),\"normal\"===i&&t in Qe&&(i=Qe[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each([\"height\",\"width\"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&\"absolute\"===i.position,a=(o||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e[\"offset\"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,\"border\",!1,i)-.5)),s&&(r=te.exec(t))&&\"px\"!==(r[3]||\"px\")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,\"marginLeft\"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+\"px\"}),S.each({margin:\"\",padding:\"\",border:\"Width\"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r=\"string\"==typeof e?e.split(\" \"):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},\"margin\"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?\"\":\"px\")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=ne[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners[\"*\"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&\"expand\"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=[\"*\"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f=\"width\"in t||\"height\"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,\"fxshow\");for(r in n.queue||(null==(a=S._queueHooks(e,\"fx\")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,\"fx\").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||\"toggle\"===i,i===(g?\"hide\":\"show\")){if(\"show\"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,\"display\")),\"none\"===(c=S.css(e,\"display\"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,\"display\"),le([e]))),(\"inline\"===c||\"inline-block\"===c&&null!=l)&&\"none\"===S.css(e,\"float\")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l=\"none\"===c?\"\":c)),h.display=\"inline-block\")),n.overflow&&(h.overflow=\"hidden\",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?\"hidden\"in v&&(g=v.hidden):v=Y.access(e,\"fxshow\",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,\"fxshow\"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&\"object\"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,\"finish\"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return\"string\"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||\"fx\",[]),this.each(function(){var e=!0,t=null!=i&&i+\"queueHooks\",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||\"fx\"),this.each(function(){var e,t=Y.get(this),n=t[a+\"queue\"],r=t[a+\"queueHooks\"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each([\"toggle\",\"show\",\"hide\"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||\"boolean\"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt(\"show\"),slideUp:lt(\"hide\"),slideToggle:lt(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||\"fx\",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement(\"input\"),it=E.createElement(\"select\").appendChild(E.createElement(\"option\")),rt.type=\"checkbox\",y.checkOn=\"\"!==rt.value,y.optSelected=it.selected,(rt=E.createElement(\"input\")).value=\"t\",rt.type=\"radio\",y.radioValue=\"t\"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return\"undefined\"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&\"radio\"===t&&A(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(\" \")}function yt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function mt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,\"tabindex\");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr(\"class\",\"\");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])while(-1<r.indexOf(\" \"+o+\" \"))r=r.replace(\" \"+o+\" \",\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},toggleClass:function(i,t){var o=typeof i,a=\"string\"===o||Array.isArray(i);return\"boolean\"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&\"boolean\"!==o||((e=yt(this))&&Y.set(this,\"__className__\",e),this.setAttribute&&this.setAttribute(\"class\",e||!1===i?\"\":Y.get(this,\"__className__\")||\"\"))})},hasClass:function(e){var t,n,r=0;t=\" \"+e+\" \";while(n=this[r++])if(1===n.nodeType&&-1<(\" \"+vt(yt(n))+\" \").indexOf(t))return!0;return!1}});var xt=/\\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t=\"\":\"number\"==typeof t?t+=\"\":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?\"\":e+\"\"})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&\"set\"in r&&void 0!==r.set(this,t,\"value\")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&\"get\"in r&&void 0!==(e=r.get(t,\"value\"))?e:\"string\"==typeof(e=t.value)?e.replace(xt,\"\"):null==e?\"\":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,\"value\");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,\"optgroup\"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each([\"radio\",\"checkbox\"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}),y.focusin=\"onfocusin\"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,\"type\")?e.type:e,h=v.call(e,\"namespace\")?e.namespace.split(\".\"):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(\".\")&&(d=(h=d.split(\".\")).shift(),h.sort()),u=d.indexOf(\":\")<0&&\"on\"+d,(e=e[S.expando]?e:new S.Event(d,\"object\"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join(\".\"),e.rnamespace=e.namespace?new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,\"events\")||Object.create(null))[e.type]&&Y.get(o,\"handle\"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:\"focusin\",blur:\"focusout\"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\\?/;S.parseXML=function(e){var t;if(!e||\"string\"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,\"text/xml\")}catch(e){t=void 0}return t&&!t.getElementsByTagName(\"parsererror\").length||S.error(\"Invalid XML: \"+e),t};var St=/\\[\\]$/,kt=/\\r?\\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+\"[\"+(\"object\"==typeof t&&null!=t?e:\"\")+\"]\",t,r,i)});else if(r||\"object\"!==w(e))i(n,e);else for(t in e)Dt(n+\"[\"+t+\"]\",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join(\"&\")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,\"elements\");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(\":disabled\")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,\"\\r\\n\")}}):{name:t.name,value:n.replace(kt,\"\\r\\n\")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\\/\\//,Rt={},Mt={},It=\"*/\".concat(\"*\"),Wt=E.createElement(\"a\");function Ft(o){return function(e,t){\"string\"!=typeof e&&(t=e,e=\"*\");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])\"+\"===n[0]?(n=n.slice(1)||\"*\",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return\"string\"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s[\"*\"]&&l(\"*\")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":It,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){\"object\"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks(\"once memory\"),w=v.statusCode||{},a={},s={},u=\"canceled\",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+\" \"]=(n[t[1].toLowerCase()+\" \"]||[]).concat(t[2])}t=n[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+\"\").replace(Pt,Tt.protocol+\"//\"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||\"*\").toLowerCase().match(P)||[\"\"],null==v.crossDomain){r=E.createElement(\"a\");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+\"//\"+Wt.host!=r.protocol+\"//\"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&\"string\"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger(\"ajaxStart\"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,\"\"),v.hasContent?v.data&&v.processData&&0===(v.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(v.data=v.data.replace(jt,\"+\")):(o=v.url.slice(f.length),v.data&&(v.processData||\"string\"==typeof v.data)&&(f+=(Et.test(f)?\"&\":\"?\")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,\"$1\"),o=(Et.test(f)?\"&\":\"?\")+\"_=\"+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader(\"If-Modified-Since\",S.lastModified[f]),S.etag[f]&&T.setRequestHeader(\"If-None-Match\",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader(\"Content-Type\",v.contentType),T.setRequestHeader(\"Accept\",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+(\"*\"!==v.dataTypes[0]?\", \"+It+\"; q=0.01\":\"\"):v.accepts[\"*\"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u=\"abort\",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger(\"ajaxSend\",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort(\"timeout\")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,\"No Transport\");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||\"\",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while(\"*\"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray(\"script\",v.dataTypes)&&(v.converters[\"text script\"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e[\"throws\"])t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader(\"Last-Modified\"))&&(S.lastModified[f]=u),(u=T.getResponseHeader(\"etag\"))&&(S.etag[f]=u)),204===e||\"HEAD\"===v.type?l=\"nocontent\":304===e?l=\"notmodified\":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l=\"error\",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+\"\",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?\"ajaxSuccess\":\"ajaxError\",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger(\"ajaxComplete\",[T,v]),--S.active||S.event.trigger(\"ajaxStop\")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,\"json\")},getScript:function(e,t){return S.get(e,void 0,t,\"script\")}}),S.each([\"get\",\"post\"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&\"withCredentials\"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e[\"X-Requested-With\"]||(e[\"X-Requested-With\"]=\"XMLHttpRequest\"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,\"abort\"===e?r.abort():\"error\"===e?\"number\"!=typeof r.status?t(0,\"error\"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,\"text\"!==(r.responseType||\"text\")||\"string\"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o(\"error\"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o(\"abort\");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")}),S.ajaxTransport(\"script\",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S(\"<script>\").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on(\"load error\",i=function(e){r.remove(),i=null,e&&t(\"error\"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=Xt.pop()||S.expando+\"_\"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Vt.test(e.data)&&\"data\");if(a||\"jsonp\"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,\"$1\"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||S.error(r+\" was not called\"),o[0]},e.dataTypes[0]=\"json\",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"<form></form><form></form>\",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return\"string\"!=typeof e?[]:(\"boolean\"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument(\"\")).createElement(\"base\")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&\"object\"==typeof t&&(i=\"POST\"),0<a.length&&S.ajax({url:e,type:i||\"GET\",dataType:\"html\",data:t}).done(function(e){o=arguments,a.html(r?S(\"<div>\").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,\"position\"),c=S(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=S.css(e,\"top\"),u=S.css(e,\"left\"),(\"absolute\"===l||\"fixed\"===l)&&-1<(o+u).indexOf(\"auto\")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),\"using\"in t?t.using.call(e,f):(\"number\"==typeof f.top&&(f.top+=\"px\"),\"number\"==typeof f.left&&(f.left+=\"px\"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if(\"fixed\"===S.css(r,\"position\"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&\"static\"===S.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,\"borderTopWidth\",!0),i.left+=S.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-S.css(r,\"marginTop\",!0),left:t.left-i.left-S.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===S.css(e,\"position\"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each([\"top\",\"left\"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+\"px\":t})}),S.each({Height:\"height\",Width:\"width\"},function(a,s){S.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||\"boolean\"!=typeof e),i=r||(!0===e||!0===t?\"margin\":\"border\");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf(\"outer\")?e[\"inner\"+a]:e.document.documentElement[\"client\"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body[\"scroll\"+a],r[\"scroll\"+a],e.body[\"offset\"+a],r[\"offset\"+a],r[\"client\"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?\"\":(e+\"\").replace(Gt,\"\")},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},\"undefined\"==typeof e&&(C.jQuery=C.$=S),S});\n/*\n * jQuery throttle / debounce - v1.1 - 3/7/2010\n * http://benalman.com/projects/jquery-throttle-debounce-plugin/\n * \n * Copyright (c) 2010 \"Cowboy\" Ben Alman\n * Dual licensed under the MIT and GPL licenses.\n * http://benalman.com/about/license/\n */\n(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!==\"boolean\"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);\n/*!\n * imagesLoaded PACKAGED v4.1.4\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n!function(e,t){\"function\"==typeof define&&define.amd?define(\"ev-emitter/ev-emitter\",t):\"object\"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}(\"undefined\"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o<i.length;o++){var r=i[o],s=n&&n[r];s&&(this.off(e,r),delete n[r]),r.apply(this,t)}return this}},t.allOff=function(){delete this._events,delete this._onceEvents},e}),function(e,t){\"use strict\";\"function\"==typeof define&&define.amd?define([\"ev-emitter/ev-emitter\"],function(i){return t(e,i)}):\"object\"==typeof module&&module.exports?module.exports=t(e,require(\"ev-emitter\")):e.imagesLoaded=t(e,e.EvEmitter)}(\"undefined\"!=typeof window?window:this,function(e,t){function i(e,t){for(var i in t)e[i]=t[i];return e}function n(e){if(Array.isArray(e))return e;var t=\"object\"==typeof e&&\"number\"==typeof e.length;return t?d.call(e):[e]}function o(e,t,r){if(!(this instanceof o))return new o(e,t,r);var s=e;return\"string\"==typeof e&&(s=document.querySelectorAll(e)),s?(this.elements=n(s),this.options=i({},this.options),\"function\"==typeof t?r=t:i(this.options,t),r&&this.on(\"always\",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(this.check.bind(this))):void a.error(\"Bad element for imagesLoaded \"+(s||e))}function r(e){this.img=e}function s(e,t){this.url=e,this.element=t,this.img=new Image}var h=e.jQuery,a=e.console,d=Array.prototype.slice;o.prototype=Object.create(t.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(e){\"IMG\"==e.nodeName&&this.addImage(e),this.options.background===!0&&this.addElementBackgroundImages(e);var t=e.nodeType;if(t&&u[t]){for(var i=e.querySelectorAll(\"img\"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if(\"string\"==typeof this.options.background){var r=e.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var u={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(e){var t=getComputedStyle(e);if(t)for(var i=/url\\((['\"])?(.*?)\\1\\)/gi,n=i.exec(t.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,e),n=i.exec(t.backgroundImage)}},o.prototype.addImage=function(e){var t=new r(e);this.images.push(t)},o.prototype.addBackground=function(e,t){var i=new s(e,t);this.images.push(i)},o.prototype.check=function(){function e(e,i,n){setTimeout(function(){t.progress(e,i,n)})}var t=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(t){t.once(\"progress\",e),t.check()}):void this.complete()},o.prototype.progress=function(e,t,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emitEvent(\"progress\",[this,e,t]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,e),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log(\"progress: \"+i,e,t)},o.prototype.complete=function(){var e=this.hasAnyBroken?\"fail\":\"done\";if(this.isComplete=!0,this.emitEvent(e,[this]),this.emitEvent(\"always\",[this]),this.jqDeferred){var t=this.hasAnyBroken?\"reject\":\"resolve\";this.jqDeferred[t](this)}},r.prototype=Object.create(t.prototype),r.prototype.check=function(){var e=this.getIsImageComplete();return e?void this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"):(this.proxyImage=new Image,this.proxyImage.addEventListener(\"load\",this),this.proxyImage.addEventListener(\"error\",this),this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},r.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.img,t])},r.prototype.handleEvent=function(e){var t=\"on\"+e.type;this[t]&&this[t](e)},r.prototype.onload=function(){this.confirm(!0,\"onload\"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,\"onerror\"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener(\"load\",this),this.proxyImage.removeEventListener(\"error\",this),this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),this.img.src=this.url;var e=this.getIsImageComplete();e&&(this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.element,t])},o.makeJQueryPlugin=function(t){t=t||e.jQuery,t&&(h=t,h.fn.imagesLoaded=function(e,t){var i=new o(this,e,t);return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o});\n/*! lz-string-1.3.3-min.js | (c) 2013 Pieroxy | Licensed under a WTFPL license */\nvar LZString={_keyStr:\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",_f:String.fromCharCode,compressToBase64:function(e){if(e==null)return\"\";var t=\"\";var n,r,i,s,o,u,a;var f=0;e=LZString.compress(e);while(f<e.length*2){if(f%2==0){n=e.charCodeAt(f/2)>>8;r=e.charCodeAt(f/2)&255;if(f/2+1<e.length)i=e.charCodeAt(f/2+1)>>8;else i=NaN}else{n=e.charCodeAt((f-1)/2)&255;if((f+1)/2<e.length){r=e.charCodeAt((f+1)/2)>>8;i=e.charCodeAt((f+1)/2)&255}else r=i=NaN}f+=3;s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+LZString._keyStr.charAt(s)+LZString._keyStr.charAt(o)+LZString._keyStr.charAt(u)+LZString._keyStr.charAt(a)}return t},decompressFromBase64:function(e){if(e==null)return\"\";var t=\"\",n=0,r,i,s,o,u,a,f,l,c=0,h=LZString._f;e=e.replace(/[^A-Za-z0-9\\+\\/\\=]/g,\"\");while(c<e.length){u=LZString._keyStr.indexOf(e.charAt(c++));a=LZString._keyStr.indexOf(e.charAt(c++));f=LZString._keyStr.indexOf(e.charAt(c++));l=LZString._keyStr.indexOf(e.charAt(c++));i=u<<2|a>>4;s=(a&15)<<4|f>>2;o=(f&3)<<6|l;if(n%2==0){r=i<<8;if(f!=64){t+=h(r|s)}if(l!=64){r=o<<8}}else{t=t+h(r|i);if(f!=64){r=s<<8}if(l!=64){t+=h(r|o)}}n+=3}return LZString.decompress(t)},compressToUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i,s=0,o=LZString._f;e=LZString.compress(e);for(n=0;n<e.length;n++){r=e.charCodeAt(n);switch(s++){case 0:t+=o((r>>1)+32);i=(r&1)<<14;break;case 1:t+=o(i+(r>>2)+32);i=(r&3)<<13;break;case 2:t+=o(i+(r>>3)+32);i=(r&7)<<12;break;case 3:t+=o(i+(r>>4)+32);i=(r&15)<<11;break;case 4:t+=o(i+(r>>5)+32);i=(r&31)<<10;break;case 5:t+=o(i+(r>>6)+32);i=(r&63)<<9;break;case 6:t+=o(i+(r>>7)+32);i=(r&127)<<8;break;case 7:t+=o(i+(r>>8)+32);i=(r&255)<<7;break;case 8:t+=o(i+(r>>9)+32);i=(r&511)<<6;break;case 9:t+=o(i+(r>>10)+32);i=(r&1023)<<5;break;case 10:t+=o(i+(r>>11)+32);i=(r&2047)<<4;break;case 11:t+=o(i+(r>>12)+32);i=(r&4095)<<3;break;case 12:t+=o(i+(r>>13)+32);i=(r&8191)<<2;break;case 13:t+=o(i+(r>>14)+32);i=(r&16383)<<1;break;case 14:t+=o(i+(r>>15)+32,(r&32767)+32);s=0;break}}return t+o(i+32)},decompressFromUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i=0,s=0,o=LZString._f;while(s<e.length){r=e.charCodeAt(s)-32;switch(i++){case 0:n=r<<1;break;case 1:t+=o(n|r>>14);n=(r&16383)<<2;break;case 2:t+=o(n|r>>13);n=(r&8191)<<3;break;case 3:t+=o(n|r>>12);n=(r&4095)<<4;break;case 4:t+=o(n|r>>11);n=(r&2047)<<5;break;case 5:t+=o(n|r>>10);n=(r&1023)<<6;break;case 6:t+=o(n|r>>9);n=(r&511)<<7;break;case 7:t+=o(n|r>>8);n=(r&255)<<8;break;case 8:t+=o(n|r>>7);n=(r&127)<<9;break;case 9:t+=o(n|r>>6);n=(r&63)<<10;break;case 10:t+=o(n|r>>5);n=(r&31)<<11;break;case 11:t+=o(n|r>>4);n=(r&15)<<12;break;case 12:t+=o(n|r>>3);n=(r&7)<<13;break;case 13:t+=o(n|r>>2);n=(r&3)<<14;break;case 14:t+=o(n|r>>1);n=(r&1)<<15;break;case 15:t+=o(n|r);i=0;break}s++}return LZString.decompress(t)},compress:function(e){if(e==null)return\"\";var t,n,r={},i={},s=\"\",o=\"\",u=\"\",a=2,f=3,l=2,c=\"\",h=0,p=0,d,v=LZString._f;for(d=0;d<e.length;d+=1){s=e.charAt(d);if(!Object.prototype.hasOwnProperty.call(r,s)){r[s]=f++;i[s]=true}o=u+s;if(Object.prototype.hasOwnProperty.call(r,o)){u=o}else{if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}r[o]=f++;u=String(s)}}if(u!==\"\"){if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}}n=2;for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}while(true){h=h<<1;if(p==15){c+=v(h);break}else p++}return c},decompress:function(e){if(e==null)return\"\";if(e==\"\")return null;var t=[],n,r=4,i=4,s=3,o=\"\",u=\"\",a,f,l,c,h,p,d,v=LZString._f,m={string:e,val:e.charCodeAt(0),position:32768,index:1};for(a=0;a<3;a+=1){t[a]=a}l=0;h=Math.pow(2,2);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(n=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 2:return\"\"}t[3]=d;f=u=d;while(true){if(m.index>m.string.length){return\"\"}l=0;h=Math.pow(2,s);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(d=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 2:return u}if(r==0){r=Math.pow(2,s);s++}if(t[d]){o=t[d]}else{if(d===i){o=f+f.charAt(0)}else{return null}}u+=o;t[i++]=f+o.charAt(0);r--;f=o;if(r==0){r=Math.pow(2,s);s++}}}};if(typeof module!==\"undefined\"&&module!=null){module.exports=LZString}\n/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */\nvar saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||function(e){\"use strict\";var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=e.URL||e.webkitURL||e,i=t.createElementNS(\"http://www.w3.org/1999/xhtml\",\"a\"),s=\"download\"in i,o=function(n){var r=t.createEvent(\"MouseEvents\");r.initMouseEvent(\"click\",true,false,e,0,0,0,0,0,false,false,false,false,0,null);n.dispatchEvent(r)},u=e.webkitRequestFileSystem,a=e.requestFileSystem||u||e.mozRequestFileSystem,f=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},l=\"application/octet-stream\",c=0,h=[],p=function(){var e=h.length;while(e--){var t=h[e];if(typeof t===\"string\"){r.revokeObjectURL(t)}else{t.remove()}}h.length=0},d=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var i=e[\"on\"+t[r]];if(typeof i===\"function\"){try{i.call(e,n||e)}catch(s){f(s)}}}},v=function(t,r){var f=this,p=t.type,v=false,m,g,y=function(){var e=n().createObjectURL(t);h.push(e);return e},b=function(){d(f,\"writestart progress write writeend\".split(\" \"))},w=function(){if(v||!m){m=y(t)}if(g){g.location.href=m}else{window.open(m,\"_blank\")}f.readyState=f.DONE;b()},E=function(e){return function(){if(f.readyState!==f.DONE){return e.apply(this,arguments)}}},S={create:true,exclusive:false},x;f.readyState=f.INIT;if(!r){r=\"download\"}if(s){m=y(t);i.href=m;i.download=r;o(i);f.readyState=f.DONE;b();return}if(e.chrome&&p&&p!==l){x=t.slice||t.webkitSlice;t=x.call(t,0,t.size,l);v=true}if(u&&r!==\"download\"){r+=\".download\"}if(p===l||u){g=e}if(!a){w();return}c+=t.size;a(e.TEMPORARY,c,E(function(e){e.root.getDirectory(\"saved\",S,E(function(e){var n=function(){e.getFile(r,S,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){g.location.href=e.toURL();h.push(e);f.readyState=f.DONE;d(f,\"writeend\",t)};n.onerror=function(){var e=n.error;if(e.code!==e.ABORT_ERR){w()}};\"writestart progress write abort\".split(\" \").forEach(function(e){n[\"on\"+e]=f[\"on\"+e]});n.write(t);f.abort=function(){n.abort();f.readyState=f.DONE};f.readyState=f.WRITING}),w)}),w)};e.getFile(r,{create:false},E(function(e){e.remove();n()}),E(function(e){if(e.code===e.NOT_FOUND_ERR){n()}else{w()}}))}),w)}),w)},m=v.prototype,g=function(e,t){return new v(e,t)};m.abort=function(){var e=this;e.readyState=e.DONE;d(e,\"abort\")};m.readyState=m.INIT=0;m.WRITING=1;m.DONE=2;m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null;e.addEventListener(\"unload\",p,false);return g}(self)\n/*! seedrandom.js v2.3.3 | (c) 2013 David Bau, all rights reserved. | Licensed under a BSD-style license */\n!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=r&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=r&f+1],c=c*d+h[r&(h[f]=h[g=r&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&\"object\"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:\"string\"==e?a:a+\"\\0\"}function l(a,b){for(var c,d=a+\"\",e=0;e<d.length;)b[r&e]=r&(c^=19*b[r&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return a.crypto.getRandomValues(c=new Uint8Array(d)),n(c)}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o=c.pow(d,e),p=c.pow(2,f),q=2*p,r=d-1,s=c[\"seed\"+i]=function(a,f,g){var h=[],r=l(k(f?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=o,c=0;p>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=q;)a/=2,b/=2,c>>>=1;return(a+c)/b},r,this==c)};l(c[i](),b),g&&g.exports?g.exports=s:h&&h.amd&&h(function(){return s})}(this,[],Math,256,6,52,\"object\"==typeof module&&module,\"function\"==typeof define&&define,\"random\");\n/*! console_hack.js | (c) 2015 Thomas Michael Edwards | Licensed under SugarCube's Simple BSD license */\n!function(){for(var methods=[\"assert\",\"clear\",\"count\",\"debug\",\"dir\",\"dirxml\",\"error\",\"exception\",\"group\",\"groupCollapsed\",\"groupEnd\",\"info\",\"log\",\"markTimeline\",\"profile\",\"profileEnd\",\"table\",\"time\",\"timeEnd\",\"timeline\",\"timelineEnd\",\"timeStamp\",\"trace\",\"warn\"],length=methods.length,noop=function(){},console=window.console=window.console||{};length--;){var method=methods[length];console[method]||(console[method]=noop)}}();\n}else{document.documentElement.setAttribute(\"data-init\", \"lacking\");}\n</script>\n<style id=\"style-normalize\" type=\"text/css\">/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS and IE text size adjust after device orientation change,\n *    without disabling user zoom.\n */\n\nhtml {\n  font-family: sans-serif; /* 1 */\n  -ms-text-size-adjust: 100%; /* 2 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n  margin: 0;\n}\n\n/* HTML5 display definitions\n   ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; /* 1 */\n  vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n/* Links\n   ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n  background-color: transparent;\n}\n\n/**\n * Improve readability of focused elements when they are also in an\n * active/hover state.\n */\n\na:active,\na:hover {\n  outline: 0;\n}\n\n/* Text-level semantics\n   ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n  font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n  font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n/* Embedded content\n   ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n  border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n/* Grouping content\n   ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n  margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n  overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n/* Forms\n   ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n *    Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; /* 1 */\n  font: inherit; /* 2 */\n  margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n  overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n *    and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n *    `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; /* 2 */\n  cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n  line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n */\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  box-sizing: content-box; /* 2 */\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n  border: 0; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n  overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n  font-weight: bold;\n}\n\n/* Tables\n   ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n</style>\n<style id=\"style-init-screen\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/init-screen.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@-webkit-keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@-o-keyframes init-loading-spin {\n\t0%   { -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n#init-screen {\n\tdisplay: none;\n\tz-index: 500000;\n\tposition: fixed;\n\ttop: 0px;\n\tleft: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tfont: 28px/1 Helmet, Freesans, sans-serif;\n\tfont-weight: bold;\n\tcolor: #eee;\n\tbackground-color: #111;\n\ttext-align: center;\n}\n#init-screen > div {\n\tdisplay: none;\n\tposition: relative;\n\tmargin: 0 auto;\n\tmax-width: 1136px;\n\ttop: 25%;\n}\nhtml[data-init=\"no-js\"] #init-screen, html[data-init=\"lacking\"] #init-screen, html[data-init=\"loading\"] #init-screen {\n\tdisplay: block;\n}\nhtml[data-init=\"no-js\"] #init-no-js, html[data-init=\"lacking\"] #init-lacking {\n\tdisplay: block;\n\tpadding: 0 1em;\n}\nhtml[data-init=\"no-js\"] #init-no-js {\n\tcolor: red;\n}\nhtml[data-init=\"loading\"] #init-loading {\n\tdisplay: block;\n\tborder: 24px solid transparent;\n\tborder-radius: 50%;\n\tborder-top-color: #7f7f7f;\n\tborder-bottom-color: #7f7f7f;\n\twidth: 100px;\n\theight: 100px;\n\t-webkit-animation: init-loading-spin 2s linear infinite;\n\t     -o-animation: init-loading-spin 2s linear infinite;\n\t        animation: init-loading-spin 2s linear infinite;\n}\nhtml[data-init=\"loading\"] #init-loading > div {\n\ttext-indent: 9999em;\n\toverflow: hidden;\n\twhite-space: nowrap;\n}\nhtml[data-init=\"loading\"] #ui-bar, html[data-init=\"loading\"] #passages {\n\tdisplay: none;\n}\n</style>\n<style id=\"style-font\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/font.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@font-face {\n\t/*\n\t\ttme-fa-icons (2020-03-27)\n\n\t\t`tme-fa-icons` is a subset of the Font Awesome font v4.7.0 by Dave Gandy (http://fontawesome.com)\n\t\tand is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL).\n\t*/\n\tfont-family: \"tme-fa-icons\";\n\tsrc: url('data:application/octet-stream;base64,d09GRgABAAAAADLAAA8AAAAAWHgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+IEkIY21hcAAAAdgAAAG8AAAF3rob9jFjdnQgAAADlAAAABMAAAAgBtX/BGZwZ20AAAOoAAAFkAAAC3CKkZBZZ2FzcAAACTgAAAAIAAAACAAAABBnbHlmAAAJQAAAI6gAADv+gJOpzGhlYWQAACzoAAAAMwAAADYY1IZaaGhlYQAALRwAAAAgAAAAJAfCBClobXR4AAAtPAAAAJEAAAFMBfb/0WxvY2EAAC3QAAAAqAAAAKhjiHI5bWF4cAAALngAAAAgAAAAIAFjDA9uYW1lAAAumAAAAY0AAAL94+zEpHBvc3QAADAoAAACHAAAA11cG/YjcHJlcAAAMkQAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZNZgnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF4EMgf9z2KIYg5imAYUZgTJAQDSIQumAHic7dTVbltRAETR7dpNCikzQ8rMzMxt/M39mHnsU1/TfZz5jFpaV7pXJunMDLAZmOqaZjD5y4Tx+uPTyeL5lG2L5zN+L94zG8+ztr7ulXH1fra4bvK9M79xiWW2sNXPbWeFHexkF7vZw172sZ8DHOQQhznCUY5xnBOc5BSnOcNZVjnHeS5wkUtc5gpX/f3r3OAmt7jNHe5yj/s84CGPeMwTnvKM57zgJa94zRve8o73fOAjn/jMF77yje/84Ce/WGPun1zi/2tlXKbp3Xyc44bFyZanSWokJDXOOjXSk/LUSXn+pEwCKTNBaqQqZU5IjX+XMjukTBEp80TKZJEyY6RMGylzR8oEkjKLpEwlKfNJyqSSMrOkTC8pc0zKRJMy26RMOSnzTsrkk7IDpGwDKXtByoaQsiukbA0p+0PKJpGyU6RsFyl7RmosQcrukbKFpOwjKZtJyo6Ssq2k7C0pG0zKLpOy1aTsNymbTsrOk7L9pNwBUi4CKbeBlCtByr0g5XKQckNIuSak3BVSLgwpt4aUq0PK/SHlEpFyk0i5TqTcKVIuFim3i5QrRso9I+WykXLjXOYNzP8BuAPUwHicY2BAAxIQyBz0PwuEARJsA90AeJytVml300YUHXlJnIQsJQstamHExGmwRiZswYAJQbJjIF2crZWgixQ76b7xid/gX/Nk2nPoN35a7xsvJJC053Cak6N3583VzNtlElqS2AvrkZSbL8XU1iaN7DwJ6YZNy1F8KDt7IWWKyd8FURCtltq3HYdERCJQta6wRBD7HlmaZHzoUUbLtqRXTcotPekuW+NBvVXffho6yrE7oaRmM3RoPbIlVRhVokimPVLSpmWo+itJK7y/wsxXzVDCiE4iabwZxtBI3htntMpoNbbjKIpsstwoUiSa4UEUeZTVEufkigkMygfNkPLKpxHlw/yIrNijnFawS7bT/L4vead3OT+xX29RtuRAH8iO7ODsdCVfhFtbYdy0k+0oVBF213dCbNnsVP9mj/KaRgO3KzK90IxgqXyFECs/ocz+IVktnE/5kkejWrKRE0HrZU7sSz6B1uOIKXHNGFnQ3dEJEdT9kjMM9pg+Hvzx3imWCxMCeBzLekclnAgTKWFzNEnaMHJgJWWLKqn1rpg45XVaxFvCfu3a0ZfOaONQd2I8Ww8dWzlRyfFoUqeZTJ3aSc2jKQ2ilHQmeMyvAyg/oklebWM1iZVH0zhmxoREIgIt3EtTQSw7saQpBM2jGb25G6a5di1apMkD9dyj9/TmVri501PaDvSzRn9Wp2I62AvT6WnkL/Fp2uUiRen66Rl+TOJB1gIykS02w5SDB2/9DtLL15YchdcG2O7t8yuofdZE8KQB+xvQHk/VKQlMhZhViFZAYq1rWZbJ1awWqcjUd0OaVr6s0wSKchwXx76Mcf1fMzOWmBK+34nTsyMuPXPtSwjTHHybdT2a16nFcgFxZnlOp1mW7+s0x/IDneZZntfpCEtbp6MsP9RpgeVHOh1jeUELmnTfwZCLMOQCDpAwhKUDQ1hegiEsFQxhuQhDWBZhCMslGMLyYxjCchmGsLysZdXUU0nj2plYBmxCYGKOHrnMReVqKrlUQrtoVGpDnhJulVQUz6p/ZaBePPKGObAWSJfIml8xzpWPRuX41hUtbxo7V8Cx6m8fjvY58VLWi4U/Bf/V1lQlvWLNw5Or8BuGnmwnqjapeHRNl89VPbr+X1RUWAv0G0iFWCjKsmxwZyKEjzqdhmqglUPMbMw8tOt1y5qfw/03MUIWUP34NxQaC9yDTllJWe3grNXX27LcO4NyOBMsSTE38/pW+CIjs9J+kVnKno98HnAFjEpl2GoDrRW82ScxD5neJM8EcVtRNkja2M4EiQ0c84B5850EJmHqqg3kTuGGDfgFYW7BeSdconqjLIfuRezzKKT8W6fiRPaoaIzAs9kbYa/vQspvcQwkNPmlfgxUFaGpGDUV0DRSbqgGX8bZum1Cxg70Iyp2w7Ks4sPHFveVkm0ZhHykiNWjo5/WXqJOqtx+ZhSX752+BcEgNTF/e990cZDKu1rJMkdtA1O3GpVT15pD41WH6uZR9b3j7BM5a5puuiceel/TqtvBxVwssPZtDtJSJhfU9WGFDaLLxaVQ6mU0Se+4BxgWGNDvUIqN/6v62HyeK1WF0XEk307Ut9HnYAz8D9h/R/UD0Pdj6HINLs/3mhOfbvThbJmuohfrp+g3MGutuVm6BtzQdAPiIUetjrjKDXynBnF6pLkc6SHgY90V4gHAJoDF4BPdtYzmUwCj+Yw5PsDnzGHQZA6DLeYw2GbOGsAOcxjsMofBHnMYfMGcdYAvmcMgZA6DiDkMnjAnAHjKHAZfMYfB18xh8A1z7gN8yxwGMXMYJMxhsK/p1jDMLV7QXaC2QVWgA1NPWNzD4lBTZcj+jheG/b1BzP7BIKb+qOn2kPoTLwz1Z4OY+otBTP1V050h9TdeGOrvBjH1D4OY+ky/GMtlBr+MfJcKB5RdbD7n74n3D9vFQLkAAQAB//8AD3icrXsLcFzlleZ//v+++nb37dfte/VotVrdrW5JltuKpO6WJVluPyXbsmOEbGRjO4KVjWPZlsM4QIU4IYGlgCE2yxqKIQkJg2GreKQCTCZb1CZkiswjzOwOmSTATNVWTZLdDUwSMrVDsomD23vOf69assFAqgbk+773P4//nPOdc/5mwNjFl8WjosbaWblmxiOmIlTGYXzb11M7Z2ohAMbZCcZ5iG9prZl4whfwGju05xtttitUdwXYEUgkbQtWga5lC+XBaqJI285qpb8dVEc8Gnm5L5QM/f58yAlB399a7dD0mWAmdAqaMvBmKPKX9TdDwSjod9yhx03FAPcvI6Gk2lV33XoXUtKgL8C6Wa0WSzXbVjhg6JoqIPShCK0FOvOuE48K1V4B1VUQAVevugmP7Fz2CmTzW5/8+ZGP/+Kp7h/8oI4MuOZ7M5B9IvujH2Wf+PnCAjzn8ZK6AieMKRcvXnxWWSWCzGARlPcqtqs21WZzEBFgYIVDAYWzVJIrXBlH+hXGlWNM00GAJmYZ8sKBzTJFVZVppijqDFMVdTIWLa0o5JrdaHusPZGIG1IjFhQGK2mAZEe56kJnR1bTY7ZT7eivFGODBTdma3pHtlCNDVbwmgMHx/aO4R8ffeet5/ZCG6TfuV03IaSJU3oIzKsGO9+5PV+BwU5xqnOQx1aO8fW71yvD9fPn55/fA22PmsaFvfSgwZ8wzPiFvZ2DUMnzJ2hHVDMmHucPsSRrq7UQo4DMAV/AW7CANw/Zru3NIx20bBEpXwukArlxxOPReibaG63/SyQyiftzcBy3k1HuOHgjEgFHnkYfh4VodDJC4138Ff8hv5PlWbrWmm2O6grNEgGNCWGnbFtRm1Z04gzQPPWvkqMWF4eWU8DB267Df+gNe84bBvfRpfNI5Ny5yAmHDh5/PPLuByMlekDS9DsRR73nWHetwBShSO5PqCBQONO4E2wGlSvYZG6gMzeQ09SWFZC0tWION3oOVVXGTbGM+hrAzQj0O25yoN8R8YzzRsaZdzLwhpsGPEm783hAJ9+kq286eDX5pn/VydDjvk7i8EuchU21JJ3DNOqFzeABmywPcNXxhu/wB+3whrqwVw7Cn1j8vPfdDH6O7PR3fDV/Cb85wua/waSit309grZJHHOhHEQ9CA7zTGVCUb0ZvXzc1lrRe5Af+4An0aSL9op0IU9y6vRFtAYGKy7KJGFbQrdg2dXqGAwWilld0zUy9xLqd6A/zeFuy7gzYFmBOwOhZ6NNhZakm8YTI7S1pyM1mM032V26qevXGlzZ8+TKvROlB/BBkO9ACDalB7OZuBnuC5tRcAItpalENNOfhajVH1A2a1HjbHZ4N4r54oWLz4qPoe4jrMo2sonapm5QRQBQ3XycKVxwRRxjQuVCnWc6mrzOZ5FvBiraOmia5FubYRpok8n0ioRTKOQMNbWiszxYWAFZrQ1sB+dDJTFYghzyiBN3oB857ndIAha4Se/+YGUtjAkXXUO2xPE2eoe3TWPeMOXmzNi+j9w1HghvVbSAmu4c6nFac6MgbzXFU2baDr1241+98TfHtU/9t7df+MzU4msmfPYj06Wbw8Gqohda0/FkSyiyvtPGG/FsMKq1pLqmPvndkye/+y+08eYIHEJZpFmJra2Nop/VWuKcCwPvgRhnGlOFps6iIdAEmFVAKh53jblZyBU6nAFdbfX03tGwioZ9oGnEyDSWTQCyFzhoR+s/jdjgWLmc3L2K21zEmXCsM5aDm4g9Ny5v0PZVb+dY9Rcd/sl5eTiPj0nTaeiU+BhhW2qbuzJcUy2Nc2gCrjBkxWCqZiArGgOuwSzjAlDb5L91XfpvfYbpij6ZRIUWYoVcLqC2+UqNLddsMrekY3GZztVl2uQjpl4xjedIM+Uybc/r5viShuHgcgXuwQtpuoMHz8utCe/s8S+Y/7xMaYu2DX1yDrez3lp3cyyKdop+HLcLi878Uh9SKOSlj73cOIUFRVKVKHhaSS9JP452RCcYoJ3w85bz8Ckp9VN4xeZj9atpH4FnPCV4OtiDNIVYCrWwsbauK81VxUlaQnA+riFBisoUsiU0LbQpASCmmRAwg1MNJoH19uQ6WpriUV1jIQjphA5QjosiTfiy1tF+kPJKMTFYpAu6lkx40odnTv7VjYtC/QtTJyGH9HmMnKamGrcaqmaagRsMUwR9WeLmwsQREvIRevSvYa8uVFXo9Sc0w5A8/Rp5+jcxhXLuYAOIx/pbY+ggFp0FV8lNoLNAEQvyDkwBZR9T1ZC6xR0qFgsybix3DJcR7iIGwCsCHHQM0nYKVSBXQZPnqG7KDZiqrn8K524gpN9ghAx42k4GsvF3nohnA0kbnglkC9mrl7h4Db2TommKcVE1QOPRd97K5WJxsKO5nIjHbHtx/og3kK8iWys1he/EkKlqCbFOB6iKQp5Q5Yp6DB0AsjmPvHEFSGsoFraPaVpI2zLc2lmudErjJ/eeQwbKPoRBZulcwhwXYc6i1SdiEpiCF+KrFOIrAzKaHdhQ79tw4MAGuJv4rt8sQQu80jloGnnDfNVJBa+vn1WjSg1d8NHrg44FbTgdJ5+T77yy/gDI5wY7633yTbI7+CdyMDyEL2paDd0BvZjyfIYXH2/H+aqjDfXUisgnk3rFYMePoyZBegWanQpM5hKdlUSU1JnoQH2iG1d90IYADi0JPV2/oxMEeB7apm+aBngFw/ObMjzHzv7dgzyOh48fHZnmO9c8Wv+2RAGwHiP20UNnzx46mvYxyaMixDpZX22lQqQgRDqG4kYcMo92cpypAOo0Ti+iSYXJRGc5l8wvQhPfqpE0SYa7nDwkeQyh26OOlbecqVumoCwJ8+mDm+9/5T4eOyOt+4wk8WjavYTIG+7nD5Li2cWv8zGkMYoz51q2u3Y1JQSwfcumSn9JUxW0CZIWQ5isqKCo8zoGFPybx/lyHD2w4AaCBw7Ap1HIxAaHyZlr1tXG1owMt7h5Ox5QmxF4Iv6vDhaQoTFAYADuYIlnLa7b7RwtiOBDtVK1dbyCMXXxn5azOIIKfLFKaQP9K3HKIMYEQosvhkIWH23TQ9wIpCq9M4WxycnJsQIUYrEJ/bPGuOZohfHVzdmMaAmHm418c7DU3xdoyYPebFktPJtpHu7fefjw4R0VHiNTa06ZUTPe09a1sdTUVNrYtbo3nth11VW7tBa1d/U1a1t71rdG2u1IJNkWDYdbUs0pnnFT+OloWzISsdsjqVpvy9prqrNjed41POfPx2+J7TIXaWP5Wgf5bvToBEvpppQYOXPOJoda4gTQE6TsNK8sAvUSSHBFygfnfM9wL+8aK/Bddv0tZ8SufyKZ7ml7s20iCWdsPpPu4YVaXuur/2M6WX8riReTE21vtPUAnn4iyTxdf0vJ+/QM0pwsUL4D5BmUBQEeXpaU4W6RtExzN1In7eT9qHPpIoUdqAzSrdyHIPo1JNRps1uioCWJyIm2U/JG8oO4aZMXozF8b4RupRd5JJv7Nn8ZeUyxjlrakhJHcwO2gHMUDgGLR0MmS0GrIoMRWos0K81LUnPZYgmkw6sM8HtCoQTaT9y0Wu3f/MZuCYfijhMPhUVQNdL2hY8kMroS/8UvEqqeSfC/xzPV07k3vsqyrL+2ykYnhJjEz45OLIdbu4ncrbrWhImzltU7oqqaXAEdMWiHxPsSdvuF70K6/xf6FejjL13YBvFVP+UXrkwnyelZjBcltoLypQIoEmsoiDUw4C2QFS/I/J6yx5ybWOOqaMad0kJlKo+ElAdLqvRTMqOsgucuM64j3hgHU1H1mIkQ084Oje3eXT1lZwL1nwaD0BZMNfFTcHpv+sf7v6LEo4oZMlRbFNqH9tb60nEN0UkQ0mYawZNpR878eFuD1lWsl/Jb9J5iMbPlDKnkh3raBmS66U1M9Jm5rKUmMel1dKLOcxrVSgf5dJqU4o0g0pJyTlV37x4bytpCATOG8VUT4+m9cPoURiakE34ZMes/wbB0Roun+2p7h9oLSlwzQqZq2eIr+xe2/Rhp5QF8hHn5Hh/FfM9ibs32UrNG+l22ZbrnJ8N+fERAcD6YCp6nuPc2Aq/vWXiMfzKaye/BDfzTLPwe33MT8ns4P/y0uiLBdyZw3qRvNvGZegjF6H+escXvreOnkD7zm7IkADTbJEkU4sdw1jmwzpTj42smHLB82ijenjfx9YsXkccR+B5+I1azGtQk+4maTpnLlsBL75E7O1IPea+mzfPmUwQt00FJoG+nPxN/zruZzZprTljyx2GpXiHdYWBZtcINeB+2xVP16/GT9euDwf00TbqgK5gK7QvCmfp/wPn15WDa3BcM1l/Hy8F9wZQ31nf4Q2IjjrXyG+BnzXnMmqXiTuB5CLa01gINjvb8mesSTwHuSAEtzm8anp+uvwbdprkfhUs0wCNIxH6TP1l/vf6aPDThK0TXI5I+tjj+SX/8wIcaPxWX4y+CqsCiEIiAIzhsKrgfh+6qv+4L4RETPl6/zqMKukki9AA96OseZb3Zk7UKVKxZKt65tpR1J9p1sTGgP5Z4ah+KEzl73R/xEfr+I8H5fchlN/Jr0n0c3fSGkrz+vbhd5m2ZWltThDeKUaKh3WTZXWauS+WomBdMkjGvOCRuT9YfcIZxk0x24/5cT3q8redxe8TpTsLn03b9LIaFo/I0eQ7uxtjQm6rffI4elnTcJfbwX/qRFyG+Vw+SaJdyFIpugk0WXCcnaZFo1323viNAWHZPvk1+ugfjTf2sbcPR5LDT442bhwPj6Z5z9qi9wr8B85Ji51yXb3tISxlpSUtaZEYnFX8ZbHI6C8vk4ll2h5/YdRQbM0CUbSmXHsd5rH5zWz7fBnc/5iA1NLANwyQZ2+5JjiYfQ4mle+BxJA1prT9g+770dv4zTz9RnTPp+JlY4DJKoroOucmyT4fnsGIeFC3H9EtmB+qnGyP6413tPvs4BhIwipskDKakeIg+KKTgbl92JBkkHn1J3c8t+9ia2vBKhMFkGBJtEjrGDIzKE1SeAgTJiDAVRYpKmSGwMlksJor5gUWgjKlXYS0gurS4neaYflXJnaWBUjDKNAVhkWoF1WhHz7uJ2PapE8NHJkulySPD62/qVmLapMq10a997JqvnphQarc8dO3UQ2smYr38pfOWszK6fTs+eBKfHy4j8t2uWNrWnbDx5CNfe+TkxrHVE/EEW4ynxM9H2FhtpAeE2tnGFUH1J4xXChxD5hACLNXbLgOA3WU353rI37Y44SipbwdxwED/mEC0rOmuIzmlGkw7SBzNxZ6NN3519+zXRhV1Uosp3TdtGD68s4eXJo8uzHVtjyXc85gC9MYmRh+euuaRk+vhAG43Tm3RLGW7Clp52Oesq3N7dKVjnW9KxCdWjyFvi/nUs+Ja5CnPxtn+2t4NnVwLrELw76JqDMz1MacMGJoR0I5RVsA1lR/DLEdonLJLQWnOMcSamKlrszLnWWZ3mzcVOjsrnYWynTfVNmQ6aQFxrWtL9SQtgjqsojbxf5lp+qquSF1rVGyk02qZZEEKRkPdlv9ff3LVQ6MTFMas8xSft3fNVbd+vqg1KaF5w7RwBsirUye24UVXDS3oIcj/nz+56mF6qQlUAQ++gGoNytcxFG7P98DWMXMoHIL/6l/Z7p1riv8ko2oiyiru129WsSrmUjO1XdtWc0Pr7miOoWOV9YUQ07WQPmsCisWYDge5puAUAI3NIk6EQACmaQ+BGRaAwOSemV1TH90+vnlDrZBNFOi/nEUlLL96lYx5VZLqB5zDQLFQzGm6KuUX89L6YqxRuaPsCwXYTlJE1EVJt9ycWTo8bereoW7Wv38ewfOzmgI/N42Kn53LatjTxUCv85zbEyg+Y5hTcDddq99M2ysc8/51lABfjZ++8KvSxvUlnpCj7U+mIG3vR8yhXSbXEbaB3cDmatdds4lrhi9Z8hskNo2hkI+RQHWN6fMIUwKGFZiNhDmCNq6BoR1gejCoTzNdD86woB6cPDh33YFr91w99dHJLePr1tp525NylKZkzJtthLplN+ADzhOxjpiN1trRPwb/vhKfqIcMg8Mr3DDqd38o4cM36y9KWa+Tsn7v4/ocj134Vcg2TZsf/ABFKH4tYwrtObZYYxoF3QiQC+PjATwUho5+W8MkQlOOqUDxnlMNjZpsfB8zjJCxZe2afKeTjXeuboqT2XcOlsACB6XROFhWch7AeNy/VsZfx4fO5Aj8EjTVRPjLdtrmTS1NX7Azce6kmjZnnHf+1iuBiG0duzsmQTiZPzfjEoTGAqZ7xqt/nmmai8gXOWb0/sG9z8vyyPNOZjKDf9DlRgmQR93keVlHOU/9RTkfH8W8ieTQzWpsc21DGRM0Xw4soAUWDEDPtMB0oS9I5qeXC0PhMySPyTWjuYFctn9JEgWLp6FSXdz71TeSgzuQBmnXVHPXGqWhIhXkF+H2lQXxVqiSO5OthN9CQQSazmBOhdycwSgoZRJvQ28Yz8SVltDiwd3PUxMLN9De1dWehinH5783RvA9xhpyoHgnWAfGvE3sqtqOFT25rGIoMB4GBa0NAaepg2KYyiwVijQqFKGtopEeUFFYgQCboj0jZ8cCk7U1Q2W3MBBLjMRi0SCKxO0od6gD6Muon6Q35JGLDZS9DEpfLNZTA4aqrmrD0rwHCAO8Cs/Ur4a3J0Lql9WU4RfBJiYySfg+cviqaczLvipt59LuhbjXflPdSuRLUUd/9VV422jRv6SF/NbehYrcQ1p2AZ6jd80L5+kSRwE3WV+OVGTs9GvnU6yLDbJsrZ2aE+iijskqPtuHgTAktgz0r+xtaUaslZRRH6FKlRJrBwO9bK+lef8YXipg7j8GSJjM+GRPmSApRv6n//T4VrH3qqbRaNxoqoxSNMfwD6MV18yPulftrX+xZ7gXeka7vMCPgf2aQ8+N4bPuaKz7lg2LIGjjTT3x4T4jvubPYKL+cFtPTxscwm0DA+yVNaMjbFNt/cE9k+sUpoxgas8Gu1qjhG7GqUu+oAFel8B2gTDcgt+54If2XXv1VVsmVvRkM4m4TnnrYCGLtt5foc4F2rWO/NrIbxENvBHUy0WJBYqEg8hbyklAxoATvupfHMCZ3wAHaASEAFz/Y7pUPh+ZumWK7z65G1KGftgMJro0NbIzrOvbm1sCuhL9tBGKtrof1aLaZkdRjS4zYhzSDTDVw4bldnrPGtubWgKGiH0adR1JuR9VI/qErSgB72HMk0emp2+anr6F7kfTydZ+zdKSO0EdDRuTqaip3xAIjapaLa1aWqg/kmqNQEiXzza3ZFbqId3euezR4Iiqbkj5j7ZEIeTX7X4n9vLvLmKL2mAXILVUp0WPoyLCVJVGQ+/yNlGhjP8PNLq4y9vNy9rPrn+eW36OaPmdX0nTF7EIpu9XPlvW1XMgOiHbSHIL1ji1nsbpBdbAyXtlTRwjSHuCqyJvkjfAVEiMe317oeLEURlNKa/CI7tfVLxLp5qb4tFwUFVYJ3R6/STHqy1Xi1R9RLvvd6lSPIba14rZoo6zwa3w/7L18OGzRwC+N7B52+HD2zYPfA8OP3iIH9kyjkd4Fdwj9x85skUPzfXhQd9cSN96mB+97yjgoYUXvZ7kxYu/UW7mL7Eo+rwKK9byTKWsSWWz6N8VBaZxB5ShgDJZHerscm0J6L1at2fNGPH4CqQNZ+gAGTeaPZVNBB52IJ7nt/uI3N/B0707Do+8vmEH37rpdTLX8eEDd47Xr564Y3aIj+67azM8Q4dwYHjpHbJoOu1/8OkH++lk4s59Y2Lo+tsevG1ukA/N3uHb9f9TbkFebNRET61I/GEuOEtdCkxKcdfISpPptmSn01kdVNF4Y4NjHF1rWnh0cxWZKImsJdKcKPPIkZR5RIoYkcJ7dh665dDOHqV/4jgc2ILXkYz77zgwypGsH1zKst/L+ir6nJVsPdtR2zaC4aQTZC9CR4p0jigDQ6gmexJcUeeXdbS0xY7WslzDdpPF8uryAPX0L+tqoVNFD5QrFi7rbKGXwYnkJGCpwra4XMZe1th6JZcOCL0VgV445HepqL+l5jVdKMHP1VeH89a/WtYaK2/9Z/g4noyFYduzjf6WpSS0FIKDRovrC4aaQ9ZArQ9b1r/K58P0Yhi/4Msl4WPhFbUuzCTBb1OitsjmCVsAn+Qs3eokTJ1FeEQlI1nGXNlfnbS8tcc/sYz4wU8vsbd+lr/UII5u7mncOYBJ+WKc+5jsETe/L03AXCfmN4IlTVnCOYiVSxyWr5h45up7d/Lpu568c7ey4zRcu6yjzk9P3Xvu3im5qb9yaf98ab2AwZIsy0Zrq6nXxkG22zi121ScGiC71IoiZwcZqlAmzUCmvaU5GgkkzaQfoNCjlKimWnxvGhtR5b4r0roYFX59BZLFsvVB5NMRIVAGQ65bJoBsRuaFbLJM/0n3nZCLlZaWYKj+efWy88WezKv+yiG5dKnN23mX2uUJbi5bY+Rc4XgxBrHXGnlYb637gwLPEuXVBmD1ViiIyzgRfl5f9NddvfoB9NDxw0clWD9KV0FbdhMMjzfc+H7uJXEG8fo6Ns4O1q5fU+KCCq16PhXG3JuJcczFw6GwEaLSBXkQwNwF4DgLs1AgHDqA2FToAXEgCDpj+jTudDaDgUmnGsaG9bWx1UOVctJGzBlDf2HJWgZyh1HHkeV53RK5WC6Gc8fr45AzKSByxwlWHShTsxydSgfVLhALUZsXOiS8w0TlVOdJ006bC4ZayA43j7cN9WCqeCgYDTvGJzKnKG0Jn7ou6KSC18Frs8FUk2Jch1frv65/kWLdMMbf2fWfDKac4HFdaYpb8HY9ZDXZhnEylEgHP7t2L6YMcO46M22b111HA113zoFBDJQUpy/WLx6B36K+MxQdmlHBKerm0BIJKqUu68GgoLY0VSpexdChJiDC1WpC9qwK1cQYpS7U0BW0JAzeMrX6P+hRI2Dy4z/hqqmb4gS3jG8GLT77P1Ue5E4wfOFTFoioAS8NYVYZhv9umJapaPV6hct6wHdkHiowcqVYnvUivt5Qa+3vK/X2dBXy2Uy6pclBE4pR4XkwxWHTtq93vH+tv0l2U6pFvbNRePWrv51uBFbBWmgHbw/upXv+0LnRc1AxL/RjLrVgmvx/yP0Fq1KJxarV2A+PHct2HDvWwbvxJIYX60/THfzHrcdGH7shQm/iC2l6E/fXRumtaPU/ybeyx+p34UkVL0LJv9PAUHOomzL1w8q9uZaYoV3SIOoujHBKKii60eKMxhJHXUMgTQW0InVqqErq22AbYN6BHOMkFCMtlpWLDDc/0NM23tYLZ1uGMX5ZrWfPtkQj+chQ61lZiH+gZSiai0Sbz4JhDbeswXd2PSVr8E/twqtr8KXdu690Q2KpI6jHMMuhBjdRdbG9iQslEae6YVhDV7gBdOiHgK4iKtSERkv4yOpAP8ZMpimmNqtSRuUFG50FDD2AeaYRNrasGS0P2LTwuGDncpRJNtZ5Fpev85SVhMY6T3ewBJrtjAFIs8XZi95eSXOXLJkW75wKaXmE7afkst1TsjBDJxP3v3I//kG6Z9R+ce7WnfcfrvHRo6fPnT46CpteTMJZ7yXKMb2XTlHieMpspgUYLz+k3UsJV/LFTWNH7vvT08eHlfWHHtx+69yLSbZMRhHWhDn2SG0oYCC3qE/B0qAKuZqPg+b5XkErlwWmYkKlMkNY2eLmUAqF3OJinmKjrPIhuK1/W/IJ6z8kh5K1D2aK+/UTWqOzkz1fC4xVOgKKKqgjZqKV9nhTGGOgUGnZjiK4Mq8jJFKOMwJ0UxSLdlO/Ymvrtq8H3/UG+nBNcG0e7Vh79xt/yOf37KklDMPYaeyc3LZ1y8hwT671KoP8BApooL9agOqYUm2FATeRptTcdeQGxexmC3pWyw2u5ZSr4l9xsLCKW+DaaeqxVskXZql+XdTggY9NDrcHk331MoTzqZSj3fGlCe3GxJQT6IsGjeBkQOGQO53v+VKSb9E1EVMQ5vKs2/R7axiimWAmiVCh4/MZ1eYrecvvMWx9oa71KppmNkVhBs6G6m+veHkw8amOFi0QFY4pTI7RrikRxSd1jgBaCeytDEHmYSsUN/HTEEyqQVQ6ppY4937ER/nPmMXaWK6W8TvQy1cj+s28wcIlS70LHqQsSoS5rB18abP70ubwv0VMqrRhJALnb2SJXZ76vg7pAPc9++oF2+s8XzLyZUMt/3ajhy35encPGy4n+lIy4Xvvou1JERMJFmTa8zqHldSwlaBa9k6VC63heDzM/3cYttfndDMiKlbIwCN7eV2F8FS61toUNZhCzHltUoKChwg/yah6WVnr8jIXJer+ultoizj14CWn/GfvvEU5uIjTdtnx8nw86K1TlzWF5QsXBhYXGlw+3rLk/5IB/G8+gzZuYUagPa8ByoUaWfiRIuA35I4CrJOMVariLtOCaKD+Ryq3gvWTwSDci1AAfVtQi7zzsmWE4F5Vq/+RPKDe8714vX5SVSVGuXjxa2K/iCyN42qyOkbLkXwt6FIjmt5sRsHC11VB2yANZQl1HL+GWjFC9Em4Vx7Q2oKTeB2+oKkeL3xEkBU015zWlri4VEBrpICIlwZ7Eso2mgSVKs8GLeJN5YsLnmn7Lh7/45KS6ncSg5I/Por8NcaWi7UW27Te2JeyTGPqPg3VS9j2CzJyeznjn1qaGXCrpsq8ro655n7+1zi3WwkBRkGuRl5e3eMyi6L0TvDJcnYwTnM14fWG5Xyh5nXVLya56BmNJRUi25oYoiGRJBGXdaO+Rb1Y9R861mlZYzodsf21K8/wZ1mC8I75Hr9viTsF+fsW5BzxTSO1IFQNz6DJvnC/lPv9L6Ab4KcvfJU6gC94y9NfMP31s7i5XY5RIn6LJqKRxjAKDuOVppiQA65ycUhaEvVeQya8dVuF6uCYKClVFADVGd5FyGt9RTWAAgg50ahiNFm2YkcCarHv3QTW/6Jna0TY0VA0FEpnMkbciKJ0RGSrrMN/XzzKfy7p3saOsq21cUl7FVQ2Dboqxg8D38S4hnxooL2bGRU1qurqAtP1Q1M7xzd3Sd4MKlx9aN7cxtWq/1OQAi25ohrK0s0ilVHwbgmKVNGWD1T/IMmcmd2u68h5MhPsLpW6Mf7ZEUPfceC+0zfhdXy9uTm5cQffujnZrMQFzmZdv+n0HyDOnl33Z4RjOcFoIL338N50IIpBxBEd9+2+7fV+vGGHQ5Y18NCTDw1EwkITYRu/JwZe9fPMI+IfxFUsyjbSqvKhAcwt168pdedsTBbTzZxTnRzRxQyV/if8uggP8y2VcpqWw7iyTmgJOy3GuEu5oK35P5xC0L6GZFag308hopD9/6r3KyoE7rKG5Tqi3D9z8s6TM/3+7mEeeCxiPDanxdWDjxmRxwKIeebmVFVePajGtTl5VaWLcGDdLdMVpbTvxD0n9pWUyvQtew1RfioQFOU/1vU/Lotg4KmyMEz9nnuM2OINTVu8ETPuuUeXPuNZUcFYEmd9rFRbYSxfVUjLiN9dFerqyvd2yKWFBEEB548Fup3m1PWnBQHUERnjyKYur0O1v1pBfvl30u2Hnj4EwydOw/CBOyd23vd4+YefpuUbvHb84elmO9HXD1P3Tq1f48YM5VZ17msH5/d1fPtmWQndeOwTd1EnZNeXbtwsoBRbcbJ29T3T0GbGDO+3J95aen6KxViWDVCHL8i5wsPAxPLfE1FDb55RmQnmCYgrEoijOyX+6KeDoE66Tt5ONjkSgheKBA9LqL6qpqchU0E7QaSYtJ3+irSVimrrmpLJU3ej0gsK6vTRXbdlEXtnb9u17Z9B+Un9m9Hg5rmoE93YF4zCPwZ31H9b/6f6b3cEgzvAgAIYO4IwfMe64Q03nOX3fXzD8Lo7brzrLtiCz85tCkajwb6N0b9LJD738MOfSxTs2x7mj3yGsEgjzzBYB9Xw2hGCYAZOzYWG2uR6nFlvxlJeEZfO732ziNsX84be4ffMGz7/gRmQb1dyTX8Q/dtArU8Hb8Wybmgod53NBlQOilxbg9mPCIstK3sTAzE7O5BMJuTKkvJgwfulBskXM0CBMymXLQrpkSoDMVmJ6aA2QjGG4SJkRk38g5sd61dtYGoQ4St+jHY+U23r4aVWOEjtseoMnDgvfwCEm2+hp6r/X91AK4zYkU3HMVEaxnR3qBfqPzn+/wHPnsOreJxjYGRgYABi1vIsoXh+m68M3MwvgCIMt5YoxkDp2P9f/2exVDAHAbkcDEwgUQAz1Qu/AHicY2BkYGAO+p/FwMBS9v/r/68sFQxAERQQDACh8QbyeJxdT0sVwzAMy49AkAxAkbT3UQiAITGAISmAYQiA9th4ttUs7Q568UeSlVidiwSkB3PUPg+ESd6ZD/+8v7HyrtzwghY89ZB6BcyrYqc6RZhw48YhaA1Wc/v1PQtdeXJ/HppUmFM597nvBVK7D+Z+E8/uQZLBgDyaz3Llvxx02S3c/Hv813JrznryZP4F+6lSfQAAAAAAAAAAUAC2ATABaAGyAfoCJAKwAzYDmgQSBFwExgUyBbQF/AZOBvwHRAe2B/YISgigCPIJGglCCWQJignACgAKQAp2CroLAAtGC4oL8gxcDPINng5iDuYPag/6EF4RIBGGEeQSShKYEyQTbhOyFAoUYhS+FVoVphYoFooXHBeGGFoYnhjGGOwZChlOGXgZrBneGhwaWhqgGtIbLhvyHHQc2B1QHZ4d/wABAAAAUwBtAAYAAAAAAAIAIAAwAHMAAAB2C3AAAAAAeJx1kN9q2zAUh39q0441YxcbjN3tXJWWEcc1lEGvWkLbXZeSu8JUV/6T2VKQlY48w95ifYa9zt6jd/vFESUUYiP5O5/O8ZEE4AP+QWH9nHKsWeEdozXv4A0uIu/Sf488IN9G3sMQPyLv0/+MfICv+BV5iI/4wz+owVtGM/yNrPBZfYm8g/fqW+Rd+svIA/Jd5D18UovI+/S/Ix9gqp4iD3GoniduvvR1WQU5mhxLlmap3C/FUdVWN6IXoXK+k3MpnA2maVySuza0ZlToUZ07292YctFov6k2eWp8VzsrJ0m6qa+NNV4H87Dq1j2WWQiFFN61chX7yNy7mclDUoUwPxuPN/tjAoc5lvCoUaJCgOCI9pjfDGk/BPfMEGaus2pYaDQ0GgtWVP1Kx/ico2BkaQ0zGnKCnHNL09KNuK451721rLqhLfmfht5vzdrmp7Sr3nUfC07YL92afU1r+wrd7/Dh5WwdHrmLjDawanUK3+9acPXqPML7Wq3NaHL6pL+1QHuGMd8t5/8PvPGO3QAAAHicbZLnlpswEIV9DRiwvZtseu89Ib333vvmBWRZYMVC0pHEEufpg8BO/kTncOfTaLgMOtPr97o17P1/baKPACEiDBAjQYohRhhjDevYhu3YwA7sxC7sxh7sxT7sxwEcxCEcxhEcxTEcxwmcxCmcxhmcxTmcxwVcxCVkuIwruIpruI4buIlbuI07uIt7uI8HeIhHeIwneIpneI4XeIlXeI03eIt3eI8P+IhP+Iwv+Ipv+I5N/OiF1hEz9JKxUrtFrDl1lWF9NR9QIikToRaVjUouKxvOmNBjLxnlhgo2DbnM1djLKrNGnGPScSUzItzGv93yPP2bSQSX84z9cqFQdJ56yZRmMhW8mLlJJSaBI0XYPDaZKDUviZmvr6DrNjJMi0WcK1MTM02mqpbZlJtEsNx5SI238jSodJtoS7qv+BpPw67IY9xU+dg5TXjROTWwdGrIOzWhT+uA0jolxqjaZrSOnCF2Nmq16651EYpMm1fakAul9SJQeR5QVYQlk1VkZ8SwoVNFIVjWnKQrlBGdMToftdoZjrs77DajqXKrS02YEFxbbtdWkG0x44JJVUS5aBqKSlJwmhDrmOF2Hv9Wqsy4TNqoKhfmSrrQKuNSL5nvPG6p0s0AkEWkSWVZMy1Kx3ljk03qLuZ14lTmB8gNGmByGrGfjLrhlhJV2f7SaIneNF1ypQNbybBUSgZswQaWEUNngeay1/sD4l/60HicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZWJ02MTAyaIEYm7mYGDkgLD4GMIvNaRfTAaA0J5DN7rSLwQHCZmZw2ajC2BEYscGhI2Ijc4rLRjUQbxdHAwMji0NHckgESEkkEGzmYWLk0drB+L91A0vvRiYGFwAMdiP0AAA=') format('woff');\n}\n</style>\n<style id=\"style-core\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\nhtml {\n\t/*\n\t\tWe define the base font size and line height here as they affect the layout\n\t\tof the base page elements (i.e. `#ui-bar`, `#ui-dialog`, and `#story`).\n\t*/\n\tfont: 16px/1 Helmet, Freesans, sans-serif;\n}\n\n/* Story data styling. */\n#store-area, tw-storydata {\n\tdisplay: none !important;\n\tz-index: 0;\n}\n\n/* Special no transition styling. */\n.no-transition {\n\t-webkit-transition: none !important;\n\t-o-transition: none !important;\n\ttransition: none !important;\n}\n\n\n/*\n\tFullscreen appearance styles.\n*/\n*:-webkit-full-screen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:-ms-fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\nbody::-ms-backdrop { /* Prevent IE 11 from hiding the `body` element's background. */\n\tbackground: none;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n*:focus {\n\toutline: thin dotted;\n}\n*:disabled {\n\tcursor: not-allowed !important;\n}\nbody {\n\tcolor: #eee;\n\tbackground-color: #111;\n\toverflow: auto;\n}\na {\n\tcursor: pointer;\n\tcolor: #68d;\n\ttext-decoration: none;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\na:hover {\n\tcolor: #8af;\n\ttext-decoration: underline;\n}\na.link-broken {\n\tcolor: #c22;\n}\na.link-broken:hover {\n\tcolor: #e44;\n}\na[disabled], span.link-disabled {\n\tcolor: #aaa;\n\tcursor: not-allowed !important;\n\t/*\n\t\tNOTE: Do not use `pointer-events` here as it disables\n\t\tthe display of a cursor in some browsers.\n\n\t\tpointer-events: none;\n\t*/\n\ttext-decoration: none;\n}\narea {\n\tcursor: pointer;\n}\nbutton {\n\tcursor: pointer;\n\tcolor: #eee;\n\tbackground-color: #35a;\n\tborder: 1px solid #57c;\n\tline-height: normal;\n\tpadding: 0.4em;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\nbutton:hover {\n\tbackground-color: #57c;\n\tborder-color: #79e;\n}\nbutton:disabled {\n\tbackground-color: #444;\n\tborder: 1px solid #666;\n}\ninput, select, textarea {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\nselect {\n\tpadding: 0.34em 0.4em;\n}\ninput[type=\"text\"] {\n\tmin-width: 18em;\n}\ntextarea {\n\tmin-width: 30em;\n\tresize: vertical;\n}\ninput[type=\"checkbox\"], input[type=\"file\"], input[type=\"radio\"], select {\n\tcursor: pointer;\n}\n/* BEGIN: input[type=\"range\"] */\ninput[type=\"range\"] {\n\t-webkit-appearance: none;\n\tmin-height: 1.2em;\n}\ninput[type=\"range\"]:focus {\n\toutline: none;\n}\ninput[type=\"range\"]::-webkit-slider-runnable-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n\t-webkit-appearance: none;\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\t/*\n\t\tNOTE: Ideally, `margin-top` should be `0` for Edge/Spartan (ca. v17), but\n\t\treal WebKit/Blink-based browsers need it.  Since there's more of them and\n\t\tEdge is co-opting the prefix anyway, we cater to them.  Edge will simply\n\t\thave to look ever so slightly off.\n\t*/\n\tmargin-top: -5px;\n\twidth: 33px;\n}\ninput[type=\"range\"]:focus::-webkit-slider-runnable-track {\n\tbackground: #222;\n}\ninput[type=\"range\"]::-moz-range-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\twidth: 33px;\n}\ninput[type=\"range\"]::-ms-track {\n\tbackground: transparent;\n\tborder-color: transparent;\n\tcolor: transparent;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: calc(100% - 1px);\n}\ninput[type=\"range\"]::-ms-fill-lower {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-fill-upper {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 16px;\n\twidth: 33px;\n}\n/* END: input[type=\"range\"] */\ninput:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,\ninput:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\nhr {\n\tdisplay: block;\n\theight: 1px;\n\tborder: none;\n\tborder-top: 1px solid #eee;\n\tmargin: 1em 0;\n\tpadding: 0;\n}\naudio, canvas, progress, video {\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.error-view {\n\tbackground-color: #511;\n\tborder-left: 0.5em solid #c22;\n\tdisplay: inline-block;\n\tmargin: 0.1em;\n\tmax-width: 100%;\n\tpadding: 0 0.25em;\n\tposition: relative;\n}\n.error-view > .error-toggle {\n\tbackground-color: transparent;\n\tborder: none;\n\tline-height: inherit;\n\tleft: 0;\n\tpadding: 0;\n\tposition: absolute;\n\ttop: 0;\n\twidth: 1.75em;\n}\n.error-view > .error {\n\tdisplay: inline-block;\n\tmargin-left: 0.25em;\n}\n.error-view > .error-toggle + .error {\n\tmargin-left: 1.5em;\n}\n.error-view > .error-source[hidden] {\n\tdisplay: none;\n}\n.error-view > .error-source:not([hidden]) {\n\tbackground-color: rgba(0, 0, 0, 0.2);\n\tdisplay: block;\n\tmargin: 0 0 0.25em;\n\toverflow-x: auto;\n\tpadding: 0.25em;\n}\n\n.highlight, .marked {\n\tcolor: yellow;\n\tfont-weight: bold;\n\tfont-style: italic;\n}\n.nobr {\n\twhite-space: nowrap;\n}\n\n[data-icon]:before,\n[data-icon-before]:before,\n[data-icon-after]:after,\n.error-view > .error-toggle:before,\n.error-view > .error:before,\na.link-external:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n[data-icon]:before {\n\tcontent: attr(data-icon);\n}\n[data-icon-before]:before {\n\tcontent: attr(data-icon-before) \"\\00a0\\00a0\";\n}\n[data-icon-after]:after {\n\tcontent: \"\\00a0\\00a0\" attr(data-icon-after);\n}\n.error-view > .error-toggle:before {\n\tcontent: \"\\e81a\";\n}\n.error-view > .error-toggle.enabled:before {\n\tcontent: \"\\e818\";\n}\n.error-view > .error:before {\n\tcontent: \"\\e80d\\00a0\\00a0\";\n}\na.link-external:after {\n\tcontent: \"\\00a0\\e80e\";\n}\n</style>\n<style id=\"style-core-display\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-display.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n#story {\n\tz-index: 10;\n\tmargin: 2.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-right: 1.5em;\n\t}\n}\n#passages {\n\tmax-width: 54em;\n\tmargin: 0 auto;\n}\n</style>\n<style id=\"style-core-passage\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-passage.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n.passage {\n\tline-height: 1.75;\n\ttext-align: left;\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.passage-in {\n\topacity: 0;\n}\n.passage ul, .passage ol {\n\tmargin-left: 0.5em;\n\tpadding-left: 1.5em;\n}\n.passage table {\n\tmargin: 1em 0;\n\tborder-collapse: collapse;\n\tfont-size: 100%;\n}\n.passage tr, .passage th, .passage td, .passage caption {\n\tpadding: 3px;\n}\n</style>\n<style id=\"style-core-macro\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-macro.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n@-webkit-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@-o-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n\n.macro-linkappend-insert,\n.macro-linkprepend-insert,\n.macro-linkreplace-insert,\n.macro-append-insert,\n.macro-prepend-insert,\n.macro-replace-insert,\n.macro-repeat-insert,\n.macro-timed-insert {\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.macro-linkappend-in,\n.macro-linkprepend-in,\n.macro-linkreplace-in,\n.macro-append-in,\n.macro-prepend-in,\n.macro-replace-in,\n.macro-repeat-in,\n.macro-timed-in {\n\topacity: 0;\n}\n\n.macro-type-cursor:after {\n\t-webkit-animation: cursor-blink 1s infinite;\n\t     -o-animation: cursor-blink 1s infinite;\n\t        animation: cursor-blink 1s infinite;\n\tcontent: \"\\2590\"; /* \"\\2588\" */\n\topacity: 1;\n}\n</style>\n<style id=\"style-ui-dialog\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-dialog.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\nhtml[data-dialog] body {\n\toverflow: hidden;\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-overlay.open {\n\tvisibility: visible;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n#ui-overlay:not(.open) {\n\t-webkit-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\t-o-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\ttransition: visibility 200ms step-end, opacity 200ms ease-in;\n}\n#ui-overlay {\n\tvisibility: hidden;\n\topacity: 0;\n\tz-index: 100000;\n\tposition: fixed;\n\t/*\n\ttop: -50vh;\n\tleft: -50vw;\n\theight: 200vh;\n\twidth: 200vw;\n\t*/\n\ttop: -50%;\n\tleft: -50%;\n\theight: 200%;\n\twidth: 200%;\n}\n#ui-dialog.open {\n\tdisplay: block;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n/*\n\tWe do not animate `#ui-dialog:not(.open)` for various reasons.  Chief among\n\tthem, however, is so that the dialog isn't in the middle of its animation\n\twhen other page updates happen.\n\n\te.g. The restoration of `overflow` on `body` would cause the still animating\n\t     dialog to jump around a little if a scrollbar were to pop in.\n\n\t     Any dialog action which performs a task which has its own animations\n\t     (e.g. passage display) or causes the page to reload in addition to\n\t     closing the dialog could cause display shenanigans.\n*/\n#ui-dialog {\n\tdisplay: none;\n\topacity: 0;\n\tz-index: 100100;\n\tposition: fixed;\n\ttop: 50px;\n\tmargin: 0;\n\tpadding: 0;\n}\n#ui-dialog > * {\n\t-webkit-box-sizing: border-box;\n\t        box-sizing: border-box;\n}\n#ui-dialog-titlebar {\n\tposition: relative;\n}\n#ui-dialog-close {\n\tdisplay: block;\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\twhite-space: nowrap;\n}\n#ui-dialog-body {\n\toverflow: auto;\n\tmin-width: 280px;\n\theight: 92%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.1em); /* parent - title(2.1em) */\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-overlay {\n\tbackground-color: #000;\n}\n#ui-overlay.open {\n\topacity: 0.8;\n}\n#ui-dialog {\n\tmax-width: 66em;\n}\n#ui-dialog.open {\n\topacity: 1;\n}\n#ui-dialog-titlebar {\n\tbackground-color: #444;\n\tmin-height: 24px;\n}\n#ui-dialog-title {\n\tmargin: 0;\n\tpadding: 0.2em 3.5em 0.2em 0.5em;\n\tfont-size: 1.5em;\n\ttext-align: center;\n\ttext-transform: uppercase;\n}\n#ui-dialog-close {\n\tcursor: pointer;\n\tfont-size: 120%;\n\tmargin: 0;\n\tpadding: 0;\n\twidth: 3.6em;\n\theight: 92%;\n\tbackground-color: transparent;\n\tborder: 1px solid transparent;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\n#ui-dialog-close:hover {\n\tbackground-color: #b44;\n\tborder-color: #d66;\n}\n#ui-dialog-body {\n\tbackground-color: #111;\n\tborder: 1px solid #444;\n\ttext-align: left;\n\tline-height: 1.5;\n\tpadding: 1em;\n}\n#ui-dialog-body > *:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body hr {\n\tbackground-color: #444;\n}\n\n/* Default dialog button bar styling. */\n#ui-dialog-body ul.buttons {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n#ui-dialog-body ul.buttons li {\n\tdisplay: inline-block;\n\tmargin: 0;\n\tpadding: 0.4em 0.4em 0 0;\n}\n#ui-dialog-body ul.buttons > li + li > button {\n\tmargin-left: 1em;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-close {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-close {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n</style>\n<style id=\"style-ui\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n/* Settings dialog styling. */\n#ui-dialog-body.settings [id|=\"setting-body\"] > div:first-child {\n\tdisplay: table;\n\twidth: 100%;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] {\n\tdisplay: table-cell;\n\tpadding: 0.4em 2em 0.4em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] + div {\n\tdisplay: table-cell;\n\tmin-width: 8em;\n\ttext-align: right;\n\tvertical-align: middle;\n\twhite-space: nowrap;\n}\n\n\n/*\n\tBuilt-in dialog appearance styles.\n*/\n/* List-based dialog styling (primarily for the Jumpto & Share dialogs). */\n#ui-dialog-body.list {\n\tpadding: 0;\n}\n#ui-dialog-body.list ul {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid transparent;\n}\n#ui-dialog-body.list li {\n\tmargin: 0;\n}\n#ui-dialog-body.list li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.list li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-decoration: none;\n}\n#ui-dialog-body.list li a:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n/* Saves dialog styling. */\n#ui-dialog-body.saves {\n\tpadding: 0 0 1px; /* Webkit/Blink need 1px bottom padding or they'll trigger the scroll bar */\n}\n#ui-dialog-body.saves > *:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves table {\n\tborder-spacing: 0;\n\twidth: 100%;\n}\n#ui-dialog-body.saves tr:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves td {\n\tpadding: 0.33em 0.33em;\n}\n#ui-dialog-body.saves td:first-child {\n\tmin-width: 1.5em;\n\ttext-align: center;\n}\n#ui-dialog-body.saves td:nth-child(3) {\n\tline-height: 1.2;\n}\n#ui-dialog-body.saves td:last-child {\n\ttext-align: right;\n}\n#ui-dialog-body.saves .empty {\n\tcolor: #999;\n\tspeak: none;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n#ui-dialog-body.saves .datestamp {\n\tfont-size: 75%;\n\tmargin-left: 1em;\n}\n#ui-dialog-body.saves ul.buttons li {\n\tpadding: 0.4em;\n}\n#ui-dialog-body.saves ul.buttons > li + li > button {\n\tmargin-left: 0.2em;\n}\n#ui-dialog-body.saves ul.buttons li:last-child {\n\t/*\n\t\tUsing `position:absolute;right:0;` here can produce poor results,\n\t\tso we use `float:right` instead.\n\t*/\n\tfloat: right;\n}\n\n/* Settings dialog styling. */\n#ui-dialog-body.settings div[id|=\"header-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:not(:first-child) {\n\tborder-top: 1px solid #444;\n\tpadding-top: 1em;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"] > * {\n\tmargin: 0;\n}\n#ui-dialog-body.settings h2[id|=\"header-heading\"] {\n\tfont-size: 1.375em;\n}\n#ui-dialog-body.settings p[id|=\"header-desc\"],\n#ui-dialog-body.settings p[id|=\"setting-desc\"] {\n\tfont-size: 87.5%;\n\tmargin: 0 0 0 0.5em;\n}\n#ui-dialog-body.settings div[id|=\"setting-body\"] + div[id|=\"setting-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-control\"] {\n\twhite-space: nowrap;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"] {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n#ui-dialog-body.settings input[type=\"range\"][id|=\"setting-control\"] {\n\tmax-width: 35vw;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-body.list a,\n#ui-dialog-body.settings span[id|=\"setting-input\"] {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-body.saves button[id=\"saves-export\"]:before,\n#ui-dialog-body.saves button[id=\"saves-import\"]:before,\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before,\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after,\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-dialog-body.saves button[id=\"saves-export\"]:before {\n\tcontent: \"\\e829\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-import\"]:before {\n\tcontent: \"\\e82a\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before {\n\tcontent: \"\\e827\\00a0\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n</style>\n<style id=\"style-ui-bar\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-bar.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\n#story {\n\tmargin-left: 20em;\n\t-webkit-transition: margin-left 200ms ease-in;\n\t-o-transition: margin-left 200ms ease-in;\n\ttransition: margin-left 200ms ease-in;\n}\n#ui-bar.stowed ~ #story {\n\tmargin-left: 4.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-left: 19em;\n\t}\n\t#ui-bar.stowed ~ #story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n/*\n\tAt very narrow viewport widths, set `#story{margin-left}` equal to the value\n\tof `#ui-bar.stowed~#story{margin-left}`, so that `#ui-bar` will side over top\n\tof `#story` when unstowed, rather than shoving it over.\n*/\n@media screen and (max-width: 768px) {\n\t#story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-bar {\n\tposition: fixed;\n\tz-index: 50;\n\ttop: 0;\n\tleft: 0;\n\twidth: 17.5em;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\t-webkit-transition: left 200ms ease-in;\n\t-o-transition: left 200ms ease-in;\n\ttransition: left 200ms ease-in;\n}\n#ui-bar.stowed {\n\tleft: -15.5em;\n}\n#ui-bar-tray {\n\tposition: absolute;\n\ttop: 0.2em;\n\tleft: 0;\n\tright: 0;\n}\n#ui-bar-body {\n\theight: 90%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.5em);\n\tmargin: 2.5em 0;\n\tpadding: 0 1.5em;\n}\n#ui-bar.stowed #ui-bar-history,\n#ui-bar.stowed #ui-bar-body {\n\tvisibility: hidden;\n\t-webkit-transition: visibility 200ms step-end;\n\t-o-transition: visibility 200ms step-end;\n\ttransition: visibility 200ms step-end;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-bar {\n\tbackground-color: #222;\n\tborder-right: 1px solid #444;\n\ttext-align: center;\n}\n#ui-bar a {\n\ttext-decoration: none;\n}\n#ui-bar hr {\n\tborder-color: #444;\n}\n#ui-bar-toggle,\n#ui-bar-history [id|=\"history\"] {\n\tfont-size: 1.2em;\n\tline-height: inherit;\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n}\n#ui-bar-toggle {\n\tdisplay: block;\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tborder-right: none;\n\tpadding: 0.3em 0.45em 0.25em;\n}\n#ui-bar.stowed #ui-bar-toggle {\n\tpadding: 0.3em 0.35em 0.25em 0.55em;\n}\n#ui-bar-toggle:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history {\n\tmargin: 0 auto;\n}\n#ui-bar-history [id|=\"history\"] {\n\tpadding: 0.2em 0.45em 0.35em;\n}\n#ui-bar-history #history-jumpto {\n\tpadding: 0.2em 0.665em 0.35em;\n}\n#ui-bar-history [id|=\"history\"]:not(:first-child) {\n\tmargin-left: 1.2em;\n}\n#ui-bar-history [id|=\"history\"]:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history [id|=\"history\"]:disabled {\n\tcolor: #444;\n\tbackground-color: transparent;\n\tborder-color: #444;\n}\n#ui-bar-body {\n\tline-height: 1.5;\n\toverflow: auto;\n}\n#ui-bar-body > :not(:first-child) {\n\tmargin-top: 2em;\n}\n#story-title {\n\tmargin: 0;\n\tfont-size: 162.5%;\n}\n#story-author {\n\tmargin-top: 2em;\n\tfont-weight: bold;\n}\n#menu ul {\n\tmargin: 1em 0 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid #444;\n}\n#menu ul:empty {\n\tdisplay: none;\n}\n#menu li {\n\tmargin: 0;\n}\n#menu li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#menu li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-transform: uppercase;\n}\n#menu li a:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-bar-history [id|=\"history\"],\n#ui-bar-toggle,\n#menu a {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-bar-toggle:before,\n#ui-bar-history [id|=\"history\"],\n#menu-core li[id|=\"menu-item\"] a:before {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-bar-toggle:before {\n\tcontent: \"\\e81d\";\n}\n#ui-bar.stowed #ui-bar-toggle:before {\n\tcontent: \"\\e81e\";\n}\n#menu-item-saves a:before {\n\tcontent: \"\\e82b\\00a0\";\n}\n#menu-item-settings a:before {\n\tcontent: \"\\e82d\\00a0\";\n}\n#menu-item-restart a:before {\n\tcontent: \"\\e82c\\00a0\";\n}\n#menu-item-share a:before {\n\tcontent: \"\\e82f\\00a0\";\n}\n</style>\n<style id=\"style-ui-debug\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-debug.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault debug bar styles.\n*/\n#debug-bar {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 0;\n\tmargin: 0;\n\tmax-height: 75%;\n\t/* max-width: 28em;\n\tmin-width: 22em; */\n\tpadding: 0.5em;\n\tposition: fixed;\n\tright: 0;\n\tz-index: 99900;\n}\n#debug-bar > div:not([id]) + div {\n\tmargin-top: 0.5em;\n}\n#debug-bar > div > label {\n\tmargin-right: 0.5em;\n}\n#debug-bar > div > input[type=\"text\"] {\n\tmin-width: 0;\n\twidth: 8em;\n}\n#debug-bar > div > select {\n\twidth: 15em;\n}\n\n#debug-bar-toggle {\n\tcolor: #eee;\n\tbackground-color: #222;\n\tborder: 1px solid #444;\n\theight: 101%; /* fallback for browsers without support for calc() */\n\theight: calc(100% + 1px);\n\tleft: -2em; /* fallback for browsers without support for calc() */\n\tleft: calc(-2em - 1px);\n\tposition: absolute;\n\ttop: -1px;\n\twidth: 2em;\n}\n#debug-bar-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n#debug-bar-hint {\n\tbottom: 0.175em;\n\tfont-size: 4.5em;\n\topacity: 0.33;\n\tpointer-events: none;\n\tposition: fixed;\n\tright: 0.6em;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n\twhite-space: nowrap;\n}\n\n#debug-bar-watch {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 102%; /* fallback for browsers without support for calc() */\n\tbottom: calc(100% + 1px);\n\tfont-size: 0.9em;\n\tleft: -1px;\n\tmax-height: 650%; /* fallback for browsers without support for vh units */\n\tmax-height: 65vh;\n\tposition: absolute;\n\toverflow-x: hidden;\n\toverflow-y: scroll;\n\tright: 0;\n\tz-index: 99800;\n}\n#debug-bar-watch[hidden] {\n\tdisplay: none;\n}\n#debug-bar-watch div {\n\tcolor: #999;\n\tfont-style: italic;\n\tmargin: 1em auto;\n\ttext-align: center;\n}\n#debug-bar-watch table {\n\twidth: 100%;\n}\n#debug-bar-watch tr:nth-child(2n) {\n\tbackground-color: rgba(127, 127, 127, 0.15);\n}\n#debug-bar-watch td {\n\tpadding: 0.2em 0;\n}\n#debug-bar-watch td:first-child + td {\n\tpadding: 0.2em 0.3em 0.2em 0.1em;\n}\n#debug-bar-watch .watch-delete {\n\tbackground-color: transparent;\n\tborder: none;\n\tcolor: #c00;\n}\n#debug-bar-watch-all,\n#debug-bar-watch-none {\n\tmargin-left: 0.5em;\n}\n#debug-bar-watch-toggle,\n#debug-bar-views-toggle {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tmargin-right: 1em;\n\tpadding: 0.4em;\n}\n#debug-bar-watch-toggle:hover,\n#debug-bar-views-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle,\nhtml[data-debug-view] #debug-bar-views-toggle {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:hover,\nhtml[data-debug-view] #debug-bar-views-toggle:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n\n#debug-bar-toggle:before,\n#debug-bar-hint:after,\n#debug-bar-watch .watch-delete:before,\n#debug-bar-watch-add:before,\n#debug-bar-watch-all:before,\n#debug-bar-watch-none:before,\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#debug-bar-toggle:before {\n\tcontent: \"\\e838\";\n}\n#debug-bar-hint:after {\n\tcontent: \"\\e838\\202f\\e822\";\n}\n#debug-bar-watch .watch-delete:before {\n\tcontent: \"\\e804\";\n}\n#debug-bar-watch-add:before {\n\tcontent: \"\\e805\";\n}\n#debug-bar-watch-all:before {\n\tcontent: \"\\e83a\";\n}\n#debug-bar-watch-none:before {\n\tcontent: \"\\e827\";\n}\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:after,\nhtml[data-debug-view] #debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n\n\n/*\n\tDefault debug view styles.\n*/\nhtml[data-debug-view] .debug {\n\tpadding: 0.25em;\n\tbackground-color: #234; /* #541, #151 */\n}\nhtml[data-debug-view] .debug[title] {\n\tcursor: help;\n}\nhtml[data-debug-view] .debug.block {\n\tdisplay: inline-block;\n\tvertical-align: middle;\n}\nhtml[data-debug-view] .debug.invalid {\n\ttext-decoration: line-through;\n}\nhtml[data-debug-view] .debug.hidden,\nhtml[data-debug-view] .debug.hidden .debug {\n\tbackground-color: #555;\n}\nhtml:not([data-debug-view]) .debug.hidden {\n\tdisplay: none;\n}\n\nhtml[data-debug-view] .debug[data-name][data-type]:before,\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:after {\n\tbackground-color: rgba(0,0,0,0.25);\n\tfont-family: monospace, monospace;\n\twhite-space: pre;\n}\nhtml[data-debug-view] .debug[data-name][data-type]:before {\n\tcontent: attr(data-name);\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"]:before {\n\tcontent: \"<<\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"].nonvoid:after {\n\tcontent: \"<</\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"]:before {\n\tcontent: \"<\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"].nonvoid:after {\n\tcontent: \"</\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type]:not(:empty):before {\n\tmargin-right: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:not(:empty):after {\n\tmargin-left: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"],\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"]:before {\n\tdisplay: block;\n}\n</style>\n</head>\n<body>\n\t<div id=\"init-screen\">\n\t\t<div id=\"init-no-js\"><noscript>JavaScript is required. Please enable it to continue.</noscript></div>\n\t\t<div id=\"init-lacking\">Your browser lacks required capabilities. Please upgrade it or switch to another to continue.</div>\n\t\t<div id=\"init-loading\"><div>Loading&hellip;</div></div>\n\t</div>\n\t{{STORY_DATA}}\n\t<script id=\"script-sugarcube\" type=\"text/javascript\">\n\t/*! SugarCube JS */\n\tif(document.documentElement.getAttribute(\"data-init\")===\"loading\"){window.TWINE1=false;\nwindow.DEBUG=false;\n(function (window, document, jQuery, undefined) {\n\"use strict\";\n\n/***********************************************************************************************************************\n\n\tlib/alert.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: This regular expression should be elsewhere.\n\n\tError prologs by engine/browser: (ca. 2018)\n\t\tChrome, Opera, & Vivaldi → `Uncaught \\w*Error: …`\n\t\tEdge & IE                → `…`\n\t\tFirefox                  → `Error: …`\n\t\tOpera (Presto)           → `Uncaught exception: \\w*(?:Error|Exception): …`\n\t\tSafari (ca. v5.1)        → `\\w*(?:Error|_ERR): …`\n*/\nvar errorPrologRegExp = /^(?:(?:uncaught\\s+(?:exception:\\s+)?)?\\w*(?:error|exception|_err):\\s+)+/i; // eslint-disable-line no-unused-vars, no-var\n\nvar Alert = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tError Functions.\n\t*******************************************************************************************************************/\n\tfunction _alertMesg(type, where, what, error) {\n\t\tconst isFatal = type === 'fatal';\n\t\tlet mesg = `Apologies! ${isFatal ? 'A fatal' : 'An'} error has occurred.`;\n\n\t\tif (isFatal) {\n\t\t\tmesg += ' Aborting.';\n\t\t}\n\t\telse {\n\t\t\tmesg += ' You may be able to continue, but some parts may not work properly.';\n\t\t}\n\n\t\tif (where != null || what != null) { // lazy equality for null\n\t\t\tmesg += '\\n\\nError';\n\n\t\t\tif (where != null) { // lazy equality for null\n\t\t\t\tmesg += ` [${where}]`;\n\t\t\t}\n\n\t\t\tif (what != null) { // lazy equality for null\n\t\t\t\tmesg += `: ${what.replace(errorPrologRegExp, '')}.`;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmesg += ': unknown error.';\n\t\t\t}\n\t\t}\n\n\t\tif (typeof error === 'object' && error !== null && error.stack) {\n\t\t\tmesg += `\\n\\nStack Trace:\\n${error.stack}`;\n\t\t}\n\n\t\twindow.alert(mesg); // eslint-disable-line no-alert\n\t}\n\n\tfunction alertError(where, what, error) {\n\t\t_alertMesg(null, where, what, error);\n\t}\n\n\tfunction alertFatal(where, what, error) {\n\t\t_alertMesg('fatal', where, what, error);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tError Event.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSet up a global error handler for uncaught exceptions.\n\t*/\n\t(origOnError => {\n\t\twindow.onerror = function (what, source, lineNum, colNum, error) {\n\t\t\t// Uncaught exceptions during play may be recoverable/ignorable.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\talertError(null, what, error);\n\t\t\t}\n\n\t\t\t// Uncaught exceptions during startup should be fatal.\n\t\t\telse {\n\t\t\t\talertFatal(null, what, error);\n\t\t\t\twindow.onerror = origOnError;\n\n\t\t\t\tif (typeof window.onerror === 'function') {\n\t\t\t\t\twindow.onerror.apply(this, arguments);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})(window.onerror);\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\terror : { value : alertError },\n\t\tfatal : { value : alertFatal }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/patterns.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tTODO: Move all markup patterns into here.\n*/\n\nvar Patterns = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPatterns.\n\t*******************************************************************************************************************/\n\t/*\n\t\tWhitespace patterns.\n\n\t\tSpace class (equivalent to `\\s`):\n\t\t\t[\\u0020\\f\\n\\r\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]\n\t\tSpace class, sans line terminators:\n\t\t\t[\\u0020\\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]\n\t\tLine Terminator class:\n\t\t\t[\\n\\r\\u2028\\u2029]\n\t*/\n\tconst space = (() => {\n\t\t/*\n\t\t\tSome browsers still supported by SugarCube have faulty space classes (`\\s`).\n\t\t\tWe check for that lossage here and, if necessary, build our own class from\n\t\t\tthe component pieces.\n\t\t*/\n\t\tconst wsMap = new Map([\n\t\t\t['\\u0020', '\\\\u0020'],\n\t\t\t['\\f', '\\\\f'],\n\t\t\t['\\n', '\\\\n'],\n\t\t\t['\\r', '\\\\r'],\n\t\t\t['\\t', '\\\\t'],\n\t\t\t['\\v', '\\\\v'],\n\t\t\t['\\u00a0', '\\\\u00a0'],\n\t\t\t['\\u1680', '\\\\u1680'],\n\t\t\t['\\u180e', '\\\\u180e'],\n\t\t\t['\\u2000', '\\\\u2000'],\n\t\t\t['\\u2001', '\\\\u2001'],\n\t\t\t['\\u2002', '\\\\u2002'],\n\t\t\t['\\u2003', '\\\\u2003'],\n\t\t\t['\\u2004', '\\\\u2004'],\n\t\t\t['\\u2005', '\\\\u2005'],\n\t\t\t['\\u2006', '\\\\u2006'],\n\t\t\t['\\u2007', '\\\\u2007'],\n\t\t\t['\\u2008', '\\\\u2008'],\n\t\t\t['\\u2009', '\\\\u2009'],\n\t\t\t['\\u200a', '\\\\u200a'],\n\t\t\t['\\u2028', '\\\\u2028'],\n\t\t\t['\\u2029', '\\\\u2029'],\n\t\t\t['\\u202f', '\\\\u202f'],\n\t\t\t['\\u205f', '\\\\u205f'],\n\t\t\t['\\u3000', '\\\\u3000'],\n\t\t\t['\\ufeff', '\\\\ufeff']\n\t\t]);\n\t\tconst wsRe = /^\\s$/;\n\t\tlet missing = '';\n\n\t\twsMap.forEach((pat, char) => {\n\t\t\tif (!wsRe.test(char)) {\n\t\t\t\tmissing += pat;\n\t\t\t}\n\t\t});\n\n\t\treturn missing ? `[\\\\s${missing}]` : '\\\\s';\n\t})();\n\tconst spaceNoTerminator = '[\\\\u0020\\\\f\\\\t\\\\v\\\\u00a0\\\\u1680\\\\u180e\\\\u2000-\\\\u200a\\\\u202f\\\\u205f\\\\u3000\\\\ufeff]';\n\tconst lineTerminator    = '[\\\\n\\\\r\\\\u2028\\\\u2029]';\n\tconst notSpace          = space === '\\\\s' ? '\\\\S' : space.replace(/^\\[/, '[^');\n\n\t/*\n\t\tCharacter patterns.\n\t*/\n\tconst anyChar = `(?:.|${lineTerminator})`;\n\n\t/*\n\t\tLetter patterns.\n\n\t\tFIXME:\n\t\t\t1. The existing set, which is a TiddlyWiki holdover, should probably\n\t\t\t   encompass a significantly greater range of BMP code points.\n\t\t\t2. Should we include the surrogate pair code units (\\uD800-\\uDBFF &\n\t\t\t   \\uDC00-\\uDFFF) to handle non-BMP code points?  Further, should we\n\t\t\t   simply be checking for the code units themselves or checking for\n\t\t\t   properly mated pairs?\n\t*/\n\tconst anyLetter       = '[0-9A-Z_a-z\\\\-\\\\u00c0-\\\\u00d6\\\\u00d8-\\\\u00f6\\\\u00f8-\\\\u00ff\\\\u0150\\\\u0170\\\\u0151\\\\u0171]';\n\tconst anyLetterStrict = anyLetter.replace('\\\\-', ''); // anyLetter sans hyphen\n\n\t/*\n\t\tIdentifier patterns.\n\n\t\tNOTE: Since JavaScript's RegExp syntax does not support Unicode character\n\t\tclasses, the correct regular expression to match a valid identifier name,\n\t\twithin the scope of our needs, would be on the order of approximately 5–6\n\t\tor 11–16 KiB, depending on how the pattern was built.  That being the case,\n\t\tfor the moment we restrict valid TwineScript identifiers to US-ASCII.\n\n\t\tFIXME: Fix this to, at least, approximate the correct range.\n\t*/\n\tconst identifierFirstChar = '[$A-Z_a-z]';\n\tconst identifierNextChar  = '[$0-9A-Z_a-z]';\n\tconst identifier          = `${identifierFirstChar}${identifierNextChar}*`;\n\n\t// Variable patterns.\n\tconst variableSigil = '[$_]';\n\tconst variable      = variableSigil + identifier;\n\n\t// Macro name pattern.\n\tconst macroName = '[A-Za-z][\\\\w-]*|[=-]';\n\n\t// Template name pattern.\n\tconst templateName = '[A-Za-z][\\\\w-]*';\n\n\t// CSS ID or class sigil pattern.\n\tconst cssIdOrClassSigil = '[#.]';\n\n\t// CSS image transclusion template pattern.\n\t//\n\t// NOTE: The alignment syntax isn't supported, but removing it might break uses\n\t// of the template in the wild, so we leave it alone for now.\n\tconst cssImage = '\\\\[[<>]?[Ii][Mm][Gg]\\\\[(?:\\\\s|\\\\S)*?\\\\]\\\\]+';\n\n\t// Inline CSS pattern.\n\tconst inlineCss = (() => {\n\t\t/* legacy */\n\t\tconst twStyle   = `(${anyLetter}+)\\\\(([^\\\\)\\\\|\\\\n]+)\\\\):`;\n\t\t/* /legacy */\n\t\tconst cssStyle  = `${spaceNoTerminator}*(${anyLetter}+)${spaceNoTerminator}*:([^;\\\\|\\\\n]+);`;\n\t\tconst idOrClass = `${spaceNoTerminator}*((?:${cssIdOrClassSigil}${anyLetter}+${spaceNoTerminator}*)+);`;\n\n\t\t// [1,2] = style(value):\n\t\t// [3,4] = style:value;\n\t\t// [5]   = #id.className;\n\t\treturn `${twStyle}|${cssStyle}|${idOrClass}`;\n\t})();\n\n\t// URL pattern.\n\tconst url = '(?:file|https?|mailto|ftp|javascript|irc|news|data):[^\\\\s\\'\"]+';\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze({\n\t\tspace,\n\t\tspaceNoTerminator,\n\t\tlineTerminator,\n\t\tnotSpace,\n\t\tanyChar,\n\t\tanyLetter,\n\t\tanyLetterStrict,\n\t\tidentifierFirstChar,\n\t\tidentifierNextChar,\n\t\tidentifier,\n\t\tvariableSigil,\n\t\tvariable,\n\t\tmacroName,\n\t\ttemplateName,\n\t\tcssIdOrClassSigil,\n\t\tcssImage,\n\t\tinlineCss,\n\t\turl\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/extensions.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\n/*\n\tJavaScript Polyfills.\n\n\tNOTE: The ES5 and ES6 polyfills come from the vendored `es5-shim.js` and `es6-shim.js` libraries.\n*/\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tTrims whitespace from either the start or end of the given string.\n\t*/\n\tconst _trimString = (() => {\n\t\t// Whitespace regular expressions.\n\t\tconst startWSRe = new RegExp(`^${Patterns.space}${Patterns.space}*`);\n\t\tconst endWSRe   = new RegExp(`${Patterns.space}${Patterns.space}*$`);\n\n\t\tfunction trimString(str, where) {\n\t\t\tconst val = String(str);\n\n\t\t\tif (!val) {\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tswitch (where) {\n\t\t\tcase 'start':\n\t\t\t\treturn startWSRe.test(val) ? val.replace(startWSRe, '') : val;\n\n\t\t\tcase 'end':\n\t\t\t\treturn endWSRe.test(val) ? val.replace(endWSRe, '') : val;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`_trimString called with incorrect where parameter value: \"${where}\"`);\n\t\t\t}\n\t\t}\n\n\t\treturn trimString;\n\t})();\n\n\t/*\n\t\tGenerates a pad string based upon the given string and length.\n\t*/\n\tfunction _createPadString(length, padding) {\n\t\tconst targetLength = Number.parseInt(length, 10) || 0;\n\n\t\tif (targetLength < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet padString = typeof padding === 'undefined' ? '' : String(padding);\n\n\t\tif (padString === '') {\n\t\t\tpadString = ' ';\n\t\t}\n\n\t\twhile (padString.length < targetLength) {\n\t\t\tconst curPadLength    = padString.length;\n\t\t\tconst remainingLength = targetLength - curPadLength;\n\n\t\t\tpadString += curPadLength > remainingLength\n\t\t\t\t? padString.slice(0, remainingLength)\n\t\t\t\t: padString;\n\t\t}\n\n\t\tif (padString.length > targetLength) {\n\t\t\tpadString = padString.slice(0, targetLength);\n\t\t}\n\n\t\treturn padString;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPolyfills.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[ES2019] Returns a new array consisting of the source array with all sub-array elements\n\t\tconcatenated into it recursively up to the given depth.\n\t*/\n\tif (!Array.prototype.flat) {\n\t\tObject.defineProperty(Array.prototype, 'flat', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\t\t\tvalue        : (() => {\n\t\t\t\tfunction flat(/* depth */) {\n\t\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\t\tthrow new TypeError('Array.prototype.flat called on null or undefined');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst depth = arguments.length === 0 ? 1 : Number(arguments[0]) || 0;\n\n\t\t\t\t\tif (depth < 1) {\n\t\t\t\t\t\treturn Array.prototype.slice.call(this);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Array.prototype.reduce.call(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\t(acc, cur) => {\n\t\t\t\t\t\t\tif (cur instanceof Array) {\n\t\t\t\t\t\t\t\t// acc.push.apply(acc, flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t\tacc.push(...flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc.push(cur);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn flat;\n\t\t\t})()\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new array consisting of the result of calling the given mapping function\n\t\ton every element in the source array and then concatenating all sub-array elements into it\n\t\trecursively up to a depth of `1`.  Identical to calling `<Array>.map(fn).flat()`.\n\t*/\n\tif (!Array.prototype.flatMap) {\n\t\tObject.defineProperty(Array.prototype, 'flatMap', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* callback [, thisArg] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.flatMap called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.map.apply(this, arguments).flat();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2016] Returns whether the given element was found within the array.\n\t*/\n\tif (!Array.prototype.includes) {\n\t\tObject.defineProperty(Array.prototype, 'includes', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.includes called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst length = this.length >>> 0;\n\n\t\t\t\tif (length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst needle = arguments[0];\n\t\t\t\tlet i = Number(arguments[1]) || 0;\n\n\t\t\t\tif (i < 0) {\n\t\t\t\t\ti = Math.max(0, length + i);\n\t\t\t\t}\n\n\t\t\t\tfor (/* empty */; i < length; ++i) {\n\t\t\t\t\tconst value = this[i];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property/value\n\t\tpairs as `[key, value]` arrays.\n\t*/\n\tif (!Object.entries) {\n\t\tObject.defineProperty(Object, 'entries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.entries object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => [key, obj[key]]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new generic object consisting of the given list's key/value pairs.\n\t*/\n\tif (!Object.fromEntries) {\n\t\tObject.defineProperty(Object, 'fromEntries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(iter) {\n\t\t\t\treturn Array.from(iter).reduce(\n\t\t\t\t\t(acc, pair) => {\n\t\t\t\t\t\tif (Object(pair) !== pair) {\n\t\t\t\t\t\t\tthrow new TypeError('Object.fromEntries iterable parameter must yield objects');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (pair[0] in acc) {\n\t\t\t\t\t\t\tObject.defineProperty(acc, pair[0], {\n\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\tvalue        : pair[1]\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tacc[pair[0]] = pair[1]; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns all own property descriptors of the given object.\n\t*/\n\tif (!Object.getOwnPropertyDescriptors) {\n\t\tObject.defineProperty(Object, 'getOwnPropertyDescriptors', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (obj == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Object.getOwnPropertyDescriptors object parameter is null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst O = Object(obj);\n\n\t\t\t\treturn Reflect.ownKeys(O).reduce(\n\t\t\t\t\t(acc, key) => {\n\t\t\t\t\t\tconst desc = Object.getOwnPropertyDescriptor(O, key);\n\n\t\t\t\t\t\tif (typeof desc !== 'undefined') {\n\t\t\t\t\t\t\tif (key in acc) {\n\t\t\t\t\t\t\t\tObject.defineProperty(acc, key, {\n\t\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\t\tvalue        : desc\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc[key] = desc; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property values.\n\t*/\n\tif (!Object.values) {\n\t\tObject.defineProperty(Object, 'values', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.values object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => obj[key]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the start of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padStart) {\n\t\tObject.defineProperty(String.prototype, 'padStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn _createPadString(targetLength - baseLength, padding) + baseString;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the end of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padEnd) {\n\t\tObject.defineProperty(String.prototype, 'padEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn baseString + _createPadString(targetLength - baseLength, padding);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the start of the string.\n\t*/\n\tif (!String.prototype.trimStart) {\n\t\tObject.defineProperty(String.prototype, 'trimStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimLeft) {\n\t\tObject.defineProperty(String.prototype, 'trimLeft', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimLeft called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the end of the string.\n\t*/\n\tif (!String.prototype.trimEnd) {\n\t\tObject.defineProperty(String.prototype, 'trimEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimRight) {\n\t\tObject.defineProperty(String.prototype, 'trimRight', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimRight called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n})();\n\n\n/*\n\tJavaScript Extensions.\n*/\n(() => {\n\t'use strict';\n\n\tconst _nativeMathRandom = Math.random;\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the given bounds.\n\t*/\n\tfunction _random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('_random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = arguments[0];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = arguments[0];\n\t\t\tmax = arguments[1];\n\t\t\tbreak;\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(_nativeMathRandom() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a randomly selected index within the given length and bounds.\n\t\tBounds may be negative.\n\t*/\n\tfunction _randomIndex(length, boundsArgs) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (boundsArgs.length) {\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = length - 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(boundsArgs[1]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(boundsArgs[1]);\n\t\t\tmax = Math.trunc(boundsArgs[2]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min)) {\n\t\t\tmin = 0;\n\t\t}\n\t\telse if (!Number.isFinite(min) || min >= length) {\n\t\t\tmin = length - 1;\n\t\t}\n\t\telse if (min < 0) {\n\t\t\tmin = length + min;\n\n\t\t\tif (min < 0) {\n\t\t\t\tmin = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (Number.isNaN(max)) {\n\t\t\tmax = 0;\n\t\t}\n\t\telse if (!Number.isFinite(max) || max >= length) {\n\t\t\tmax = length - 1;\n\t\t}\n\t\telse if (max < 0) {\n\t\t\tmax = length + max;\n\n\t\t\tif (max < 0) {\n\t\t\t\tmax = length - 1;\n\t\t\t}\n\t\t}\n\n\t\treturn _random(min, max);\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Will throw exceptions on invalid surrogate pairs.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction _getCodePointStartAndEnd(str, pos) {\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : pos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos) + str.charAt(nextPos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : nextPos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\treturn {\n\t\t\tchar  : str.charAt(prevPos) + str.charAt(pos),\n\t\t\tstart : prevPos,\n\t\t\tend   : pos\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, General.\n\t*******************************************************************************************************************/\n\t/*\n\t\tRandomly selects an element from the given array, or array-like object, and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(array /* DEPRECATED: [, [min ,] max] */) {\n\t\t\tif (\n\t\t\t\t   typeof array !== 'object'\n\t\t\t\t|| array === null\n\t\t\t\t|| !Object.prototype.hasOwnProperty.call(array, 'length')\n\t\t\t) {\n\t\t\t\tthrow new TypeError('Array.random array parameter must be an array or array-lke object');\n\t\t\t}\n\n\t\t\tconst length = array.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, Array.prototype.slice.call(arguments, 1));\n\n\t\t\treturn array[index];\n\t\t}\n\t});\n\n\t/*\n\t\tConcatenates one or more unique elements to the end of the base array\n\t\tand returns the result as a new array.  Elements which are arrays will\n\t\tbe merged—i.e. their elements will be concatenated, rather than the\n\t\tarray itself.\n\t*/\n\tObject.defineProperty(Array.prototype, 'concatUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.concatUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst result = Array.from(this);\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst items   = Array.prototype.reduce.call(arguments, (prev, cur) => prev.concat(cur), []);\n\t\t\tconst addSize = items.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = items[i];\n\n\t\t\t\tif (indexOf.call(result, value) === -1) {\n\t\t\t\t\tpush.call(result, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst needle  = arguments[0];\n\t\t\tlet pos   = Number(arguments[1]) || 0;\n\t\t\tlet count = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\t++pos;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the given elements from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'delete', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.delete called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst needles       = Array.prototype.concat.apply([], arguments);\n\t\t\tconst needlesLength = needles.length;\n\t\t\tconst indices       = [];\n\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tconst value = this[i];\n\n\t\t\t\tfor (let j = 0; j < needlesLength; ++j) {\n\t\t\t\t\tconst needle = needles[j];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\tindices.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0, iend = indices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[indices[i]];\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements at the given indices from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteAt', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* indices */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteAt called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice     = Array.prototype.splice;\n\t\t\tconst cpyIndices = [\n\t\t\t\t...new Set(\n\t\t\t\t\tArray.prototype.concat.apply([], arguments)\n\t\t\t\t\t\t// Map negative indices to their positive counterparts,\n\t\t\t\t\t\t// so the Set can properly filter out duplicates.\n\t\t\t\t\t\t.map(x => x < 0 ? Math.max(0, length + x) : x)\n\t\t\t\t).values()\n\t\t\t];\n\t\t\tconst delIndices = [...cpyIndices].sort((a, b) => b - a);\n\t\t\tconst result     = [];\n\n\t\t\t// Copy the elements (in originally specified order).\n\t\t\tfor (let i = 0, iend = cpyIndices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[cpyIndices[i]];\n\t\t\t}\n\n\t\t\t// Delete the elements (in descending numeric order).\n\t\t\tfor (let i = 0, iend = delIndices.length; i < iend; ++i) {\n\t\t\t\tsplice.call(this, delIndices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements that pass the test implemented\n\t\tby the given predicate function from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteWith', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(predicate, thisArg) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteWith called on null or undefined');\n\t\t\t}\n\t\t\tif (typeof predicate !== 'function') {\n\t\t\t\tthrow new Error('Array.prototype.deleteWith predicate parameter must be a function');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice  = Array.prototype.splice;\n\t\t\tconst indices = [];\n\t\t\tconst result  = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tif (predicate.call(thisArg, this[i], i, this)) {\n\t\t\t\t\tresult.push(this[i]);\n\t\t\t\t\tindices.push(i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[0];\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAll called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAll.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\t!Array.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAny called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAny.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\tArray.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[length - 1];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluck', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluck called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn Array.prototype.splice.call(this, index, 1)[0];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes the given number of unique elements from the base array\n\t\tand returns the removed elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluckMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluckMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.pluckMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\t\t\tconst result = [];\n\t\t\tlet max = length - 1;\n\n\t\t\tdo {\n\t\t\t\tresult.push(splice.call(this, _random(0, max--), 1)[0]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tAppends one or more unique elements to the end of the base array and\n\t\treturns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pushUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pushUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tpush.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.random called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn this[index];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects the given number of unique elements from the base array\n\t\tand returns the selected elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'randomMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.randomMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.randomMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst picked = new Map();\n\t\t\tconst result = [];\n\t\t\tconst max    = length - 1;\n\n\t\t\tdo {\n\t\t\t\tlet i;\n\t\t\t\tdo {\n\t\t\t\t\ti = _random(0, max);\n\t\t\t\t} while (picked.has(i));\n\t\t\t\tpicked.set(i, true);\n\t\t\t\tresult.push(this[i]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly shuffles the array and returns it.\n\t*/\n\tObject.defineProperty(Array.prototype, 'shuffle', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.shuffle called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tfor (let i = length - 1; i > 0; --i) {\n\t\t\t\tconst j = Math.floor(_nativeMathRandom() * (i + 1));\n\n\t\t\t\tif (i === j) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// [this[i], this[j]] = [this[j], this[i]];\n\t\t\t\tconst swap = this[i];\n\t\t\t\tthis[i] = this[j];\n\t\t\t\tthis[j] = swap;\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t});\n\n\t/*\n\t\tPrepends one or more unique elements to the beginning of the base array\n\t\tand returns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'unshiftUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.unshiftUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst unshift = Array.prototype.unshift;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tunshift.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a bound function that supplies the given arguments to the base\n\t\tfunction, followed by the arguments are supplied to the bound function,\n\t\twhenever it is called.\n\t*/\n\tObject.defineProperty(Function.prototype, 'partial', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Function.prototype.partial called on null or undefined');\n\t\t\t}\n\n\t\t\tconst slice = Array.prototype.slice;\n\t\t\tconst fn    = this;\n\t\t\tconst bound = slice.call(arguments, 0);\n\n\t\t\treturn function () {\n\t\t\t\tconst applied = [];\n\t\t\t\tlet argc = 0;\n\n\t\t\t\tfor (let i = 0; i < bound.length; ++i) {\n\t\t\t\t\tapplied.push(bound[i] === undefined ? arguments[argc++] : bound[i]);\n\t\t\t\t}\n\n\t\t\t\treturn fn.apply(this, applied.concat(slice.call(arguments, argc)));\n\t\t\t};\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the given numerical clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Math, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num, min, max) {\n\t\t\tconst value = Number(num);\n\t\t\treturn Number.isNaN(value) ? NaN : value.clamp(min, max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a decimal number eased from 0 to 1.\n\n\t\tNOTE: The magnitude of the returned value decreases if num < 0.5 or increases if num > 0.5.\n\t*/\n\tObject.defineProperty(Math, 'easeInOut', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num) {\n\t\t\treturn 1 - (Math.cos(Number(num) * Math.PI) + 1) / 2;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Number.prototype, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* min, max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Number.prototype.clamp called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length !== 2) {\n\t\t\t\tthrow new Error('Number.prototype.clamp called with an incorrect number of parameters');\n\t\t\t}\n\n\t\t\tlet min = Number(arguments[0]);\n\t\t\tlet max = Number(arguments[1]);\n\n\t\t\tif (min > max) {\n\t\t\t\t[min, max] = [max, min];\n\t\t\t}\n\n\t\t\treturn Math.min(Math.max(this, min), max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the given string with all RegExp metacharacters escaped.\n\t*/\n\tif (!RegExp.escape) {\n\t\t(() => {\n\t\t\tconst _regExpMetaCharsRe    = /[\\\\^$*+?.()|[\\]{}]/g;\n\t\t\tconst _hasRegExpMetaCharsRe = new RegExp(_regExpMetaCharsRe.source); // to drop the global flag\n\n\t\t\tObject.defineProperty(RegExp, 'escape', {\n\t\t\t\tconfigurable : true,\n\t\t\t\twritable     : true,\n\n\t\t\t\tvalue(str) {\n\t\t\t\t\tconst val = String(str);\n\t\t\t\t\treturn val && _hasRegExpMetaCharsRe.test(val)\n\t\t\t\t\t\t? val.replace(_regExpMetaCharsRe, '\\\\$&')\n\t\t\t\t\t\t: val;\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t}\n\n\t/*\n\t\tReturns a formatted string, after replacing each format item in the given\n\t\tformat string with the text equivalent of the corresponding argument's value.\n\t*/\n\t(() => {\n\t\tconst _formatRegExp    = /{(\\d+)(?:,([+-]?\\d+))?}/g;\n\t\tconst _hasFormatRegExp = new RegExp(_formatRegExp.source); // to drop the global flag\n\n\t\tObject.defineProperty(String, 'format', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(format) {\n\t\t\t\tfunction padString(str, align, pad) {\n\t\t\t\t\tif (!align) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst plen = Math.abs(align) - str.length;\n\n\t\t\t\t\tif (plen < 1) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\t// const padding = Array(plen + 1).join(pad);\n\t\t\t\t\tconst padding = String(pad).repeat(plen);\n\t\t\t\t\treturn align < 0 ? str + padding : padding + str;\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length < 2) {\n\t\t\t\t\treturn arguments.length === 0 ? '' : format;\n\t\t\t\t}\n\n\t\t\t\tconst args = arguments.length === 2 && Array.isArray(arguments[1])\n\t\t\t\t\t? [...arguments[1]]\n\t\t\t\t\t: Array.prototype.slice.call(arguments, 1);\n\n\t\t\t\tif (args.length === 0) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\tif (!_hasFormatRegExp.test(format)) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t\t_formatRegExp.lastIndex = 0;\n\n\t\t\t\treturn format.replace(_formatRegExp, (match, index, align) => {\n\t\t\t\t\tlet retval = args[index];\n\n\t\t\t\t\tif (retval == null) { // lazy equality for null\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\n\t\t\t\t\twhile (typeof retval === 'function') {\n\t\t\t\t\t\tretval = retval();\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (typeof retval) {\n\t\t\t\t\tcase 'string': /* no-op */ break;\n\t\t\t\t\tcase 'object': retval = JSON.stringify(retval); break;\n\t\t\t\t\tdefault:       retval = String(retval); break;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn padString(retval, !align ? 0 : Number.parseInt(align, 10), ' ');\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t})();\n\n\t/*\n\t\tReturns whether the given string was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn String.prototype.indexOf.apply(this, arguments) !== -1;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given substring was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst needle = String(arguments[0] || '');\n\n\t\t\tif (needle === '') {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tconst indexOf = String.prototype.indexOf;\n\t\t\tconst step    = needle.length;\n\t\t\tlet pos     = Number(arguments[1]) || 0;\n\t\t\tlet count   = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\tpos += step;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the last code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, str.length - 1);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with `delCount` characters replaced with\n\t\t`replacement`, starting at `startAt`.\n\t*/\n\tObject.defineProperty(String.prototype, 'splice', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(startAt, delCount, replacement) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splice called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tlet start = Number(startAt);\n\n\t\t\tif (!Number.isSafeInteger(start)) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\telse if (start < 0) {\n\t\t\t\tstart += length;\n\n\t\t\t\tif (start < 0) {\n\t\t\t\t\tstart = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (start > length) {\n\t\t\t\tstart = length;\n\t\t\t}\n\n\t\t\tlet count = Number(delCount);\n\n\t\t\tif (!Number.isSafeInteger(count) || count < 0) {\n\t\t\t\tcount = 0;\n\t\t\t}\n\n\t\t\tlet res = this.slice(0, start);\n\n\t\t\tif (typeof replacement !== 'undefined') {\n\t\t\t\tres += replacement;\n\t\t\t}\n\n\t\t\tif (start + count < length) {\n\t\t\t\tres += this.slice(start + count);\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns an array of strings, split from the string, or an empty array if the\n\t\tstring is empty.\n\t*/\n\tObject.defineProperty(String.prototype, 'splitOrEmpty', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* [ separator [, limit ]] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splitOrEmpty called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tif (String(this) === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\treturn String.prototype.split.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased,\n\t\taccording to any locale-specific rules.\n\t*/\n\tObject.defineProperty(String.prototype, 'toLocaleUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toLocaleUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toLocaleUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased.\n\t*/\n\tObject.defineProperty(String.prototype, 'toUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, JSON.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDefine `toJSON()` methods on each prototype we wish to support.\n\t*/\n\tObject.defineProperty(Date.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:date)', this.toISOString()];\n\t\t}\n\t});\n\tObject.defineProperty(Function.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\t/*\n\t\t\t\tThe enclosing parenthesis here are necessary to force the function expression code\n\t\t\t\tstring, returned by `this.toString()`, to be evaluated as an expression during\n\t\t\t\trevival.  Without them, the function expression, which is likely nameless, will be\n\t\t\t\tevaluated as a function definition—which will throw a syntax error exception, since\n\t\t\t\tfunction definitions must have a name.\n\t\t\t*/\n\t\t\treturn ['(revive:eval)', `(${this.toString()})`];\n\t\t}\n\t});\n\tObject.defineProperty(Map.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:map)', [...this]];\n\t\t}\n\t});\n\tObject.defineProperty(RegExp.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:eval)', this.toString()];\n\t\t}\n\t});\n\tObject.defineProperty(Set.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:set)', [...this]];\n\t\t}\n\t});\n\n\t/*\n\t\tUtility method to allow users to easily wrap their code in the revive wrapper.\n\t*/\n\tObject.defineProperty(JSON, 'reviveWrapper', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(code, data) {\n\t\t\tif (typeof code !== 'string') {\n\t\t\t\tthrow new TypeError('JSON.reviveWrapper code parameter must be a string');\n\t\t\t}\n\n\t\t\treturn ['(revive:eval)', [code, data]];\n\t\t}\n\t});\n\n\t/*\n\t\tBackup the original `JSON.parse()` and replace it with a revive wrapper aware version.\n\t*/\n\tObject.defineProperty(JSON, '_real_parse', {\n\t\tvalue : JSON.parse\n\t});\n\tObject.defineProperty(JSON, 'parse', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(text, reviver) {\n\t\t\treturn JSON._real_parse(text, (key, val) => {\n\t\t\t\tlet value = val;\n\n\t\t\t\t/*\n\t\t\t\t\tAttempt to revive wrapped values.\n\t\t\t\t*/\n\t\t\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\t\t\tswitch (value[0]) {\n\t\t\t\t\tcase '(revive:set)':\n\t\t\t\t\t\tvalue = new Set(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:map)':\n\t\t\t\t\t\tvalue = new Map(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:date)':\n\t\t\t\t\t\tvalue = new Date(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:eval)':\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/* eslint-disable no-eval */\n\t\t\t\t\t\t\t// For post-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\tif (Array.isArray(value[1])) {\n\t\t\t\t\t\t\t\tconst $ReviveData$ = value[1][1]; // eslint-disable-line no-unused-vars\n\t\t\t\t\t\t\t\tvalue = eval(value[1][0]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// For regular expressions, functions, and pre-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvalue = eval(value[1]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* eslint-enable no-eval */\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* legacy */\n\t\t\t\telse if (typeof value === 'string' && value.slice(0, 10) === '@@revive@@') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = eval(value.slice(10)); // eslint-disable-line no-eval\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\t\t\t\t/* /legacy */\n\n\t\t\t\t/*\n\t\t\t\t\tCall the custom reviver, if specified.\n\t\t\t\t*/\n\t\t\t\tif (typeof reviver === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = reviver(key, value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\n\t\t\t\treturn value;\n\t\t\t});\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, Deprecated.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns whether the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAll called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAll.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAny called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAny.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns a new array consisting of the flattened source array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'flatten', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.flatten called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.flat.call(this, Infinity);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns an array of link titles, parsed from the string.\n\n\t\tNOTE: Unused in SugarCube, only included for compatibility.\n\t*/\n\tObject.defineProperty(String.prototype, 'readBracketedList', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.readBracketedList called on null or undefined');\n\t\t\t}\n\n\t\t\t// RegExp groups: Double-square-bracket quoted | Unquoted.\n\t\t\tconst re    = new RegExp('(?:\\\\[\\\\[((?:\\\\s|\\\\S)*?)\\\\]\\\\])|([^\"\\'\\\\s]\\\\S*)', 'gm');\n\t\t\tconst names = [];\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.exec(this)) !== null) {\n\t\t\t\tif (match[1]) { // double-square-bracket quoted\n\t\t\t\t\tnames.push(match[1]);\n\t\t\t\t}\n\t\t\t\telse if (match[2]) { // unquoted\n\t\t\t\t\tnames.push(match[2]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn names;\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/browser.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Browser = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable max-len */\n\tconst userAgent = navigator.userAgent.toLowerCase();\n\n\tconst winPhone = userAgent.includes('windows phone');\n\tconst isMobile = Object.freeze({\n\t\tAndroid    : !winPhone && userAgent.includes('android'),\n\t\tBlackBerry : /blackberry|bb10/.test(userAgent),\n\t\tiOS        : !winPhone && /ip(?:hone|ad|od)/.test(userAgent),\n\t\tOpera      : !winPhone && (typeof window.operamini === 'object' || userAgent.includes('opera mini')),\n\t\tWindows    : winPhone || /iemobile|wpdesktop/.test(userAgent),\n\n\t\tany() {\n\t\t\treturn isMobile.Android || isMobile.BlackBerry || isMobile.iOS || isMobile.Opera || isMobile.Windows;\n\t\t}\n\t});\n\n\tconst isGecko = !isMobile.Windows && !/khtml|trident|edge/.test(userAgent) && userAgent.includes('gecko');\n\n\tconst isIE      = !userAgent.includes('opera') && /msie|trident/.test(userAgent);\n\tconst ieVersion = isIE\n\t\t? (() => {\n\t\t\tconst ver = /(?:msie\\s+|rv:)(\\d+\\.\\d)/.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\t// opera <= 12: \"opera/9.80 (windows nt 6.1; wow64) presto/2.12.388 version/12.16\"\n\t// opera >= 15: \"mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/28.0.1500.52 safari/537.36 opr/15.0.1147.130\"\n\tconst isOpera      = userAgent.includes('opera') || userAgent.includes(' opr/');\n\tconst operaVersion = isOpera\n\t\t? (() => {\n\t\t\tconst re  = new RegExp(`${/khtml|chrome/.test(userAgent) ? 'opr' : 'version'}\\\\/(\\\\d+\\\\.\\\\d+)`);\n\t\t\tconst ver = re.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\tconst isVivaldi = userAgent.includes('vivaldi');\n\t/* eslint-enable max-len */\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\tuserAgent,\n\t\tisMobile,\n\t\tisGecko,\n\t\tisIE,\n\t\tieVersion,\n\t\tisOpera,\n\t\toperaVersion,\n\t\tisVivaldi\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/has.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Has = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tNOTE: The aggressive try/catch feature tests are necessitated by implementation\n\t\tbugs in various browsers.\n\t*/\n\n\t// Is the `HTMLAudioElement` API available?\n\tconst hasAudioElement = (() => {\n\t\ttry {\n\t\t\treturn typeof document.createElement('audio').canPlayType === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `File` API available?\n\tconst hasFile = (() => {\n\t\ttry {\n\t\t\treturn 'Blob' in window &&\n\t\t\t\t'File' in window &&\n\t\t\t\t'FileList' in window &&\n\t\t\t\t'FileReader' in window &&\n\t\t\t\t!Browser.isMobile.any() &&\n\t\t\t\t(!Browser.isOpera || Browser.operaVersion >= 15);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `geolocation` API available?\n\tconst hasGeolocation = (() => {\n\t\ttry {\n\t\t\treturn 'geolocation' in navigator &&\n\t\t\t\ttypeof navigator.geolocation.getCurrentPosition === 'function' &&\n\t\t\t\ttypeof navigator.geolocation.watchPosition === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `MutationObserver` API available?\n\tconst hasMutationObserver = (() => {\n\t\ttry {\n\t\t\treturn 'MutationObserver' in window &&\n\t\t\t\ttypeof window.MutationObserver === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `performance` API available?\n\tconst hasPerformance = (() => {\n\t\ttry {\n\t\t\treturn 'performance' in window &&\n\t\t\t\ttypeof window.performance.now === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the platform a touch device?\n\tconst hasTouch = (() => {\n\t\ttry {\n\t\t\treturn 'ontouchstart' in window ||\n\t\t\t\t!!window.DocumentTouch &&\n\t\t\t\tdocument instanceof window.DocumentTouch ||\n\t\t\t\t!!navigator.maxTouchPoints ||\n\t\t\t\t!!navigator.msMaxTouchPoints;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the transition end event available and by what name?\n\tconst hasTransitionEndEvent = (() => {\n\t\ttry {\n\t\t\tconst teMap = new Map([\n\t\t\t\t['transition',       'transitionend'],\n\t\t\t\t['MSTransition',     'msTransitionEnd'],\n\t\t\t\t['WebkitTransition', 'webkitTransitionEnd'],\n\t\t\t\t['MozTransition',    'transitionend']\n\t\t\t]);\n\t\t\tconst teKeys = [...teMap.keys()];\n\t\t\tconst el     = document.createElement('div');\n\n\t\t\tfor (let i = 0; i < teKeys.length; ++i) {\n\t\t\t\tif (el.style[teKeys[i]] !== undefined) {\n\t\t\t\t\treturn teMap.get(teKeys[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\taudio              : hasAudioElement,\n\t\tfileAPI            : hasFile,\n\t\tgeolocation        : hasGeolocation,\n\t\tmutationObserver   : hasMutationObserver,\n\t\tperformance        : hasPerformance,\n\t\ttouch              : hasTouch,\n\t\ttransitionEndEvent : hasTransitionEndEvent\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/visibility.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Visibility = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tThere are two versions of the Page Visibility API: First Edition and, the current,\n\t\tSecond Edition (i.e. \"Level 2\").  First Edition is mentioned here only because some\n\t\tolder browsers implement it, rather than the current specification.\n\n\t\tSEE:\n\t\t\tSecond Edition : https://www.w3.org/TR/page-visibility/\n\t\t\tFirst Edition  : https://www.w3.org/TR/2013/REC-page-visibility-20130514/\n\n\t\tNOTE: Generally, all supported browsers change the visibility state when either switching tabs\n\t\twithin the browser or minimizing the browser window.  Exceptions are noted below:\n\t\t\t* IE 9 doesn't support either version of the Page Visibility API.\n\t\t\t* Opera 12 (Presto) doesn't change the visibility state when the browser is minimized.\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'hidden',          // boolean; historical in 2nd edition\n\t\t\t\t\tstateProperty  : 'visibilityState', // string, values: 'hidden', 'visible'; 1st edition had more values\n\t\t\t\t\tchangeEvent    : 'visibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink & WebKit.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'webkitHidden',\n\t\t\t\t\tstateProperty  : 'webkitVisibilityState',\n\t\t\t\t\tchangeEvent    : 'webkitvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'mozHidden',\n\t\t\t\t\tstateProperty  : 'mozVisibilityState',\n\t\t\t\t\tchangeEvent    : 'mozvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 10.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'msHidden',\n\t\t\t\t\tstateProperty  : 'msVisibilityState',\n\t\t\t\t\tchangeEvent    : 'msvisibilitychange'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.hiddenProperty in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getVisibility() {\n\t\treturn vendor && document[vendor.stateProperty] || 'visible';\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor);\n\t}\n\n\tfunction isHidden() {\n\t\t// return Boolean(vendor && document[vendor.stateProperty] === 'hidden');\n\t\treturn Boolean(vendor && document[vendor.hiddenProperty]); // NOTE: Historical, but probably better for 1st edition.\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Functions.\n\t\tvendor    : { get : getVendor },\n\t\tstate     : { get : getVisibility },\n\t\tisEnabled : { value : isEnabled },\n\t\tisHidden  : { value : isHidden },\n\n\t\t// Properties.\n\t\thiddenProperty : { value : vendor && vendor.hiddenProperty },\n\t\tstateProperty  : { value : vendor && vendor.stateProperty },\n\t\tchangeEvent    : { value : vendor && vendor.changeEvent }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/fullscreen.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Fullscreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tSEE:\n\t\t\thttps://fullscreen.spec.whatwg.org\n\t\t\thttps://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'fullscreenEnabled',\n\t\t\t\t\telement     : 'fullscreenElement',\n\t\t\t\t\trequestFn   : 'requestFullscreen',\n\t\t\t\t\texitFn      : 'exitFullscreen',\n\t\t\t\t\tchangeEvent : 'fullscreenchange', // prop: onfullscreenchange\n\t\t\t\t\terrorEvent  : 'fullscreenerror'   // prop: onfullscreenerror\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink, WebKit, & Edge.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'webkitFullscreenEnabled',\n\t\t\t\t\telement     : 'webkitFullscreenElement',\n\t\t\t\t\trequestFn   : 'webkitRequestFullscreen',\n\t\t\t\t\texitFn      : 'webkitExitFullscreen',\n\t\t\t\t\tchangeEvent : 'webkitfullscreenchange',\n\t\t\t\t\terrorEvent  : 'webkitfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'mozFullScreenEnabled',\n\t\t\t\t\telement     : 'mozFullScreenElement',\n\t\t\t\t\trequestFn   : 'mozRequestFullScreen',\n\t\t\t\t\texitFn      : 'mozCancelFullScreen',\n\t\t\t\t\tchangeEvent : 'mozfullscreenchange',\n\t\t\t\t\terrorEvent  : 'mozfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 11.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'msFullscreenEnabled',\n\t\t\t\t\telement     : 'msFullscreenElement',\n\t\t\t\t\trequestFn   : 'msRequestFullscreen',\n\t\t\t\t\texitFn      : 'msExitFullscreen',\n\t\t\t\t\tchangeEvent : 'MSFullscreenChange',\n\t\t\t\t\terrorEvent  : 'MSFullscreenError'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.isEnabled in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************/\n\n\t// Return whether the request and exit fullscreen methods return a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _returnsPromise = (function () {\n\t\t// Cache of whether the request and exit methods return a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _returnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (vendor) {\n\t\t\t\ttry {\n\t\t\t\t\tconst value = document.exitFullscreen();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since we shouldn't be in fullscreen yet,\n\t\t\t\t\t// and we don't actually care about the error, since we just want the return\n\t\t\t\t\t// value, so we consign it to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _returnsPromise;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _selectElement(requestedEl) {\n\t\tlet selectedEl = requestedEl || document.documentElement;\n\n\t\t// Document element scrolling workaround for older browsers.\n\t\tif (\n\t\t\t   selectedEl === document.documentElement\n\t\t\t&& (\n\t\t\t\t   vendor.requestFn === 'msRequestFullscreen'   // IE 11\n\t\t\t\t|| Browser.isOpera && Browser.operaVersion < 15 // Opera 12 (Presto)\n\t\t\t)\n\t\t) {\n\t\t\tselectedEl = document.body;\n\t\t}\n\n\t\treturn selectedEl;\n\t}\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getElement() {\n\t\treturn (vendor || null) && document[vendor.element];\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor && document[vendor.isEnabled]);\n\t}\n\n\tfunction isFullscreen() {\n\t\treturn Boolean(vendor && document[vendor.element]);\n\t}\n\n\tfunction requestFullscreen(options, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (typeof element[vendor.requestFn] !== 'function') {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\t\tif (isFullscreen()) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn element[vendor.requestFn](options);\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_requestFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(element)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen request error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\telement[vendor.requestFn](options);\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction exitFullscreen() {\n\t\tif (!vendor || typeof document[vendor.exitFn] !== 'function') {\n\t\t\treturn Promise.reject(new TypeError('fullscreen not supported'));\n\t\t}\n\t\tif (!isFullscreen()) {\n\t\t\treturn Promise.reject(new TypeError('fullscreen mode not active'));\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn document[vendor.exitFn]();\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_exitFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen exit error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tdocument[vendor.exitFn]();\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction toggleFullscreen(options, requestedEl) {\n\t\treturn isFullscreen() ? exitFullscreen() : requestFullscreen(options, requestedEl);\n\t}\n\n\tfunction onChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.changeEvent, handlerFn);\n\t}\n\n\tfunction offChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.changeEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.changeEvent);\n\t\t}\n\t}\n\n\tfunction onError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.errorEvent, handlerFn);\n\t}\n\n\tfunction offError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.errorEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.errorEvent);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tvendor       : { get : getVendor },\n\t\telement      : { get : getElement },\n\t\tisEnabled    : { value : isEnabled },\n\t\tisFullscreen : { value : isFullscreen },\n\t\trequest      : { value : requestFullscreen },\n\t\texit         : { value : exitFullscreen },\n\t\ttoggle       : { value : toggleFullscreen },\n\t\tonChange     : { value : onChange },\n\t\toffChange    : { value : offChange },\n\t\tonError      : { value : onError },\n\t\toffError     : { value : offError }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/helpers.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, State, Story, Util, Wikifier */\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tclone,\n\tconvertBreaks,\n\tsafeActiveElement,\n\tsetDisplayTitle,\n\tsetPageElement,\n\tthrowError,\n\ttoStringOrDefault\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _getTextContent(source) {\n\t\tconst copy = source.cloneNode(true);\n\t\tconst frag = document.createDocumentFragment();\n\t\tlet node;\n\n\t\twhile ((node = copy.firstChild) !== null) {\n\t\t\t// Insert spaces before various elements.\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tswitch (node.nodeName.toUpperCase()) {\n\t\t\t\tcase 'BR':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'P':\n\t\t\t\t\tfrag.appendChild(document.createTextNode(' '));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfrag.appendChild(node);\n\t\t}\n\n\t\treturn frag.textContent;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a deep copy of the given object.\n\n\t\tNOTE:\n\t\t\t1. `clone()` does not clone functions, however, since function definitions\n\t\t\t   are immutable, the only issues are with expando properties and scope.\n\t\t\t   The former really should not be done.  The latter is problematic either\n\t\t\t   way—damned if you do, damned if you don't.\n\t\t\t2. `clone()` does not maintain referential relationships—e.g. multiple\n\t\t\t   references to the same object will, post-cloning, refer to different\n\t\t\t   equivalent objects; i.e. each reference will receive its own clone\n\t\t\t   of the original object.\n\t*/\n\tfunction clone(orig) {\n\t\t/*\n\t\t\tImmediately return the primitives and functions.\n\t\t*/\n\t\tif (typeof orig !== 'object' || orig === null) {\n\t\t\treturn orig;\n\t\t}\n\n\t\t/*\n\t\t\tUnbox instances of the primitive exemplar objects.\n\t\t*/\n\t\tif (orig instanceof String) {\n\t\t\treturn String(orig);\n\t\t}\n\t\tif (orig instanceof Number) {\n\t\t\treturn Number(orig);\n\t\t}\n\t\tif (orig instanceof Boolean) {\n\t\t\treturn Boolean(orig);\n\t\t}\n\n\t\t/*\n\t\t\tHonor native clone methods.\n\t\t*/\n\t\tif (typeof orig.clone === 'function') {\n\t\t\treturn orig.clone(true);\n\t\t}\n\t\tif (orig.nodeType && typeof orig.cloneNode === 'function') {\n\t\t\treturn orig.cloneNode(true);\n\t\t}\n\n\t\t/*\n\t\t\tCreate a copy of the original object.\n\n\t\t\tNOTE: Each non-generic object that we wish to support must be\n\t\t\texplicitly handled below.\n\t\t*/\n\t\tlet copy;\n\n\t\t// Handle instances of the core supported object types.\n\t\tif (orig instanceof Array) {\n\t\t\tcopy = new Array(orig.length);\n\t\t}\n\t\telse if (orig instanceof Date) {\n\t\t\tcopy = new Date(orig.getTime());\n\t\t}\n\t\telse if (orig instanceof Map) {\n\t\t\tcopy = new Map();\n\t\t\torig.forEach((val, key) => copy.set(key, clone(val)));\n\t\t}\n\t\telse if (orig instanceof RegExp) {\n\t\t\tcopy = new RegExp(orig);\n\t\t}\n\t\telse if (orig instanceof Set) {\n\t\t\tcopy = new Set();\n\t\t\torig.forEach(val => copy.add(clone(val)));\n\t\t}\n\n\t\t// Handle instances of unknown or generic objects.\n\t\telse {\n\t\t\t// We try to ensure that the returned copy has the same prototype as\n\t\t\t// the original, but this will probably produce less than satisfactory\n\t\t\t// results on non-generics.\n\t\t\tcopy = Object.create(Object.getPrototypeOf(orig));\n\t\t}\n\n\t\t/*\n\t\t\tDuplicate the original object's own enumerable properties, which will\n\t\t\tinclude expando properties on non-generic objects.\n\n\t\t\tNOTE: This preserves neither symbol properties nor ES5 property attributes.\n\t\t\tNeither does the delta coding or serialization code, however, so it's not\n\t\t\treally an issue at the moment.\n\t\t*/\n\t\tObject.keys(orig).forEach(name => copy[name] = clone(orig[name]));\n\n\t\treturn copy;\n\t}\n\n\t/*\n\t\tConverts <br> elements to <p> elements within the given node tree.\n\t*/\n\tfunction convertBreaks(source) {\n\t\tconst output = document.createDocumentFragment();\n\t\tlet para = document.createElement('p');\n\t\tlet node;\n\n\t\twhile ((node = source.firstChild) !== null) {\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase 'BR':\n\t\t\t\t\tif (\n\t\t\t\t\t\t   node.nextSibling !== null\n\t\t\t\t\t\t&& node.nextSibling.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t\t&& node.nextSibling.nodeName.toUpperCase() === 'BR'\n\t\t\t\t\t) {\n\t\t\t\t\t\tsource.removeChild(node.nextSibling);\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!para.hasChildNodes()) {\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'ADDRESS':\n\t\t\t\tcase 'ARTICLE':\n\t\t\t\tcase 'ASIDE':\n\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\tcase 'CENTER':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'DL':\n\t\t\t\tcase 'FIGURE':\n\t\t\t\tcase 'FOOTER':\n\t\t\t\tcase 'FORM':\n\t\t\t\tcase 'H1':\n\t\t\t\tcase 'H2':\n\t\t\t\tcase 'H3':\n\t\t\t\tcase 'H4':\n\t\t\t\tcase 'H5':\n\t\t\t\tcase 'H6':\n\t\t\t\tcase 'HEADER':\n\t\t\t\tcase 'HR':\n\t\t\t\tcase 'MAIN':\n\t\t\t\tcase 'NAV':\n\t\t\t\tcase 'OL':\n\t\t\t\tcase 'P':\n\t\t\t\tcase 'PRE':\n\t\t\t\tcase 'SECTION':\n\t\t\t\tcase 'TABLE':\n\t\t\t\tcase 'UL':\n\t\t\t\t\tif (para.hasChildNodes()) {\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.appendChild(node);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpara.appendChild(node);\n\t\t}\n\n\t\tif (para.hasChildNodes()) {\n\t\t\toutput.appendChild(para);\n\t\t}\n\n\t\tsource.appendChild(output);\n\t}\n\n\t/*\n\t\tReturns `document.activeElement` or `null`.\n\t*/\n\tfunction safeActiveElement() {\n\t\t/*\n\t\t\tIE9 contains a bug where trying to access the active element of an iframe's\n\t\t\tparent document (i.e. `window.parent.document.activeElement`) will throw an\n\t\t\texception, so we must allow for an exception to be thrown.\n\n\t\t\tWe could simply return `undefined` here, but since the API's default behavior\n\t\t\tshould be to return `document.body` or `null` when there is no selection, we\n\t\t\tchoose to return `null` in all non-element cases (i.e. whether it returns\n\t\t\t`null` or throws an exception).  Just a bit of normalization.\n\t\t*/\n\t\ttry {\n\t\t\treturn document.activeElement || null;\n\t\t}\n\t\tcatch (ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/*\n\t\tSets the display title.\n\t*/\n\tfunction setDisplayTitle(title) {\n\t\tif (typeof title !== 'string') {\n\t\t\tthrow new TypeError(`story display title must be a string (received: ${Util.getType(title)})`);\n\t\t}\n\n\t\tconst render = document.createDocumentFragment();\n\t\tnew Wikifier(render, title);\n\n\t\tconst text = _getTextContent(render).trim();\n\n\t\t// if (text === '') {\n\t\t// \tthrow new Error('story display title must not render to an empty string or consist solely of whitespace');\n\t\t// }\n\n\t\tdocument.title = Config.passages.displayTitles && State.passage !== '' && State.passage !== Config.passages.start\n\t\t\t? `${State.passage} | ${text}`\n\t\t\t: text;\n\n\t\tconst storyTitle = document.getElementById('story-title');\n\n\t\tif (storyTitle !== null) {\n\t\t\tjQuery(storyTitle).empty().append(render);\n\t\t}\n\t}\n\n\t/*\n\t\tWikifies a passage into a DOM element corresponding to the passed ID and returns the element.\n\t*/\n\tfunction setPageElement(idOrElement, titles, defaultText) {\n\t\tconst el = typeof idOrElement === 'object'\n\t\t\t? idOrElement\n\t\t\t: document.getElementById(idOrElement);\n\n\t\tif (el == null) { // lazy equality for null\n\t\t\treturn null;\n\t\t}\n\n\t\tconst ids = Array.isArray(titles) ? titles : [titles];\n\n\t\tjQuery(el).empty();\n\n\t\tfor (let i = 0, iend = ids.length; i < iend; ++i) {\n\t\t\tif (Story.has(ids[i])) {\n\t\t\t\tnew Wikifier(el, Story.get(ids[i]).processText().trim());\n\t\t\t\treturn el;\n\t\t\t}\n\t\t}\n\n\t\tif (defaultText != null) { // lazy equality for null\n\t\t\tconst text = String(defaultText).trim();\n\n\t\t\tif (text !== '') {\n\t\t\t\tnew Wikifier(el, text);\n\t\t\t}\n\t\t}\n\n\t\treturn el;\n\t}\n\n\t/*\n\t\tAppends an error view to the passed DOM element.\n\t*/\n\tfunction throwError(place, message, source, stack) {\n\t\tconst $wrapper = jQuery(document.createElement('div'));\n\t\tconst $toggle  = jQuery(document.createElement('button'));\n\t\tconst $source  = jQuery(document.createElement('pre'));\n\t\tconst mesg     = `${L10n.get('errorTitle')}: ${message || 'unknown error'} ${Config.saves.version}`;\n\n\t\t$toggle\n\t\t\t.addClass('error-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('errorToggle')\n\t\t\t}, () => {\n\t\t\t\tif ($toggle.hasClass('enabled')) {\n\t\t\t\t\t$toggle.removeClass('enabled');\n\t\t\t\t\t$source.attr({\n\t\t\t\t\t\t'aria-hidden' : true,\n\t\t\t\t\t\thidden        : 'hidden'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$toggle.addClass('enabled');\n\t\t\t\t\t$source.removeAttr('aria-hidden hidden');\n\t\t\t\t}\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('span'))\n\t\t\t.addClass('error')\n\t\t\t.text(mesg)\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('code'))\n\t\t\t.text(source)\n\t\t\t.appendTo($source);\n\t\t$source\n\t\t\t.addClass('error-source')\n\t\t\t.attr({\n\t\t\t\t'aria-hidden' : true,\n\t\t\t\thidden        : 'hidden'\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tif (stack) {\n\t\t\tconst lines = stack.split('\\n');\n\t\t\tfor (const ll of lines) {\n\t\t\t\tconst div = document.createElement('div');\n\t\t\t\tdiv.append(ll.replace(/file:.*\\//, '<path>/'));\n\t\t\t\t$source.append(div);\n\t\t\t}\n\t\t}\n\t\t$wrapper\n\t\t\t.addClass('error-view')\n\t\t\t.appendTo(place);\n\n\t\tconsole.warn(`${mesg}\\n\\t${source.replace(/\\n/g, '\\n\\t')}`);\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the simple string representation of the passed value or, if there is none,\n\t\tthe passed default value.\n\t*/\n\tfunction toStringOrDefault(value, defValue) {\n\t\tconst tSOD = toStringOrDefault;\n\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\t// TODO: Perhaps NaN should be printed instead?\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\tif (value === null) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\telse if (Array.isArray(value)) {\n\t\t\t\treturn value.map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Set) {\n\t\t\t\treturn [...value].map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Map) {\n\t\t\t\tconst result = [...value].map(([key, val]) => `${tSOD(key, defValue)} \\u2192 ${tSOD(val, defValue)}`);\n\t\t\t\treturn `{\\u202F${result.join(', ')}\\u202F}`;\n\t\t\t}\n\t\t\telse if (value instanceof Date) {\n\t\t\t\treturn value.toLocaleString();\n\t\t\t}\n\t\t\telse if (typeof value.toString === 'function') {\n\t\t\t\treturn value.toString();\n\t\t\t}\n\t\t\treturn Object.prototype.toString.call(value);\n\n\t\tcase 'function':\n\t\tcase 'undefined':\n\t\t\treturn defValue;\n\t\t}\n\n\t\treturn String(value);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tclone             : { value : clone },\n\t\tconvertBreaks     : { value : convertBreaks },\n\t\tsafeActiveElement : { value : safeActiveElement },\n\t\tsetDisplayTitle   : { value : setDisplayTitle },\n\t\tsetPageElement    : { value : setPageElement },\n\t\tthrowError        : { value : throwError },\n\t\ttoStringOrDefault : { value : toStringOrDefault }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/jquery-plugins.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Wikifier, errorPrologRegExp, safeActiveElement */\n\n/*\n\tWAI-ARIA methods plugin.\n\n\t`<jQuery>.ariaClick([options,] handler)`\n\t    Makes the target element(s) WAI-ARIA compatible clickables.\n\n\t`<jQuery>.ariaDisabled(state)`\n\t    Changes the disabled state of the target WAI-ARIA-compatible clickable element(s).\n\n\t`<jQuery>.ariaIsDisabled()`\n\t    Checks the disabled status of the target WAI-ARIA-compatible clickable element(s).\n*/\n(() => {\n\t'use strict';\n\n\t/*\n\t\tEvent handler & utility functions.\n\n\t\tNOTE: Do not replace the anonymous functions herein with arrow functions.\n\t*/\n\tfunction onKeypressFn(ev) {\n\t\t// 13 is Enter/Return, 32 is Space.\n\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\tev.preventDefault();\n\n\t\t\t// To allow delegation, attempt to trigger the event on `document.activeElement`,\n\t\t\t// if possible, elsewise on `this`.\n\t\t\tjQuery(safeActiveElement() || this).trigger('click');\n\t\t}\n\t}\n\n\tfunction onClickFnWrapper(fn) {\n\t\treturn function () {\n\t\t\tconst $this = jQuery(this);\n\n\t\t\tconst dataPassage = $this.attr('data-passage');\n\t\t\tconst initialDataPassage = window && window.SugarCube && window.SugarCube.State && window.SugarCube.State.passage;\n\t\t\tconst savedYOffset = window.pageYOffset;\n\n\t\t\t// Toggle \"aria-pressed\" status, if the attribute exists.\n\t\t\tif ($this.is('[aria-pressed]')) {\n\t\t\t\t$this.attr('aria-pressed', $this.attr('aria-pressed') === 'true' ? 'false' : 'true');\n\t\t\t}\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\n\t\t\tconst doJump = function(){ window.scrollTo(0, savedYOffset); }\n\t\t\tif ( dataPassage && (window.lastDataPassageLink === dataPassage || initialDataPassage === dataPassage))\n\t\t\t\tdoJump();\n\t\t\twindow.lastDataPassageLink = dataPassage;\n\t\t};\n\t}\n\n\tfunction oneClickFnWrapper(fn) {\n\t\treturn onClickFnWrapper(function () {\n\t\t\t// Remove both event handlers (keypress & click) and the other components.\n\t\t\tjQuery(this)\n\t\t\t\t.off('.aria-clickable')\n\t\t\t\t.removeAttr('tabindex aria-controls aria-pressed')\n\t\t\t\t.not('a,button')\n\t\t\t\t.removeAttr('role')\n\t\t\t\t.end()\n\t\t\t\t.filter('button')\n\t\t\t\t.prop('disabled', true);\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\t\t});\n\t}\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaClick()` method.\n\t\t*/\n\t\tariaClick(options, handler) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tlet opts = options;\n\t\t\tlet fn   = handler;\n\n\t\t\tif (fn == null) { // lazy equality for null\n\t\t\t\tfn   = opts;\n\t\t\t\topts = undefined;\n\t\t\t}\n\n\t\t\topts = jQuery.extend({\n\t\t\t\tnamespace : undefined,\n\t\t\t\tone       : false,\n\t\t\t\tselector  : undefined,\n\t\t\t\tdata      : undefined,\n\t\t\t\tcontrols  : undefined,\n\t\t\t\tpressed   : undefined,\n\t\t\t\tlabel     : undefined\n\t\t\t}, opts);\n\n\t\t\tif (typeof opts.namespace !== 'string') {\n\t\t\t\topts.namespace = '';\n\t\t\t}\n\t\t\telse if (opts.namespace[0] !== '.') {\n\t\t\t\topts.namespace = `.${opts.namespace}`;\n\t\t\t}\n\n\t\t\tif (typeof opts.pressed === 'boolean') {\n\t\t\t\topts.pressed = opts.pressed ? 'true' : 'false';\n\t\t\t}\n\n\t\t\t// Set `type` to `button` to suppress \"submit\" semantics, for <button> elements.\n\t\t\tthis.filter('button').prop('type', 'button');\n\n\t\t\t// Set `role` to `button`, for non-<a>/-<button> elements.\n\t\t\tthis.not('a,button').attr('role', 'button');\n\n\t\t\t// Set `tabindex` to `0` to make them focusable (unnecessary on <button> elements, but it doesn't hurt).\n\t\t\tthis.attr('tabindex', 0);\n\n\t\t\t// Set `aria-controls`.\n\t\t\tif (opts.controls != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-controls', opts.controls);\n\t\t\t}\n\n\t\t\t// Set `aria-pressed`.\n\t\t\tif (opts.pressed != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-pressed', opts.pressed);\n\t\t\t}\n\n\t\t\t// Set `aria-label` and `title`.\n\t\t\tif (opts.label != null) { // lazy equality for null\n\t\t\t\tthis.attr({\n\t\t\t\t\t'aria-label' : opts.label,\n\t\t\t\t\ttitle        : opts.label\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Set the keypress handlers, for non-<button> elements.\n\t\t\t// NOTE: For the single-use case, the click handler will also remove this handler.\n\t\t\tthis.not('button').on(\n\t\t\t\t`keypress.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\tonKeypressFn\n\t\t\t);\n\n\t\t\t// Set the click handlers.\n\t\t\t// NOTE: To ensure both handlers are properly removed, `one()` must not be used here.\n\t\t\tthis.on(\n\t\t\t\t`click.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\topts.data,\n\t\t\t\topts.one ? oneClickFnWrapper(fn) : onClickFnWrapper(fn)\n\t\t\t);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaDisabled()` method.\n\t\t*/\n\t\tariaDisabled(disable) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE: We use `<jQuery>.each()` callbacks to invoke the `<Element>.setAttribute()`\n\t\t\t\tmethods in the following because the `<jQuery>.attr()` method does not allow you\n\t\t\t\tto set a content attribute without a value, which is recommended for boolean\n\t\t\t\tcontent attributes by the HTML specification.\n\t\t\t*/\n\n\t\t\tconst $nonDisableable = this.not('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\t\t\tconst $disableable    = this.filter('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\n\t\t\tif (disable) {\n\t\t\t\t// Add boolean content attribute `disabled` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.setAttribute('disabled', '');\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `true` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = true;\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Remove content attributes `disabled` and `aria-disabled`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.removeAttribute('disabled');\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `false` and remove content attribute `aria-disabled`,\n\t\t\t\t// for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = false;\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaIsDisabled()` method.\n\t\t*/\n\t\tariaIsDisabled() {\n\t\t\t// Check content attribute `disabled`.\n\t\t\t//\n\t\t\t// NOTE: We simply check the `disabled` content attribute for all elements\n\t\t\t// since we have to check it for non-disableable elements and it may also\n\t\t\t// be used for disableable elements since their `disabled` IDL attribute\n\t\t\t// is required to reflect the status of their `disabled` content attribute,\n\t\t\t// and vice versa, by the HTML specification.\n\t\t\t// return this.toArray().some(el => el.hasAttribute('disabled'));\n\t\t\treturn this.is('[disabled]');\n\t\t}\n\t});\n})();\n\n/*\n\tWikifier methods plugin.\n\n\t`jQuery.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s), as directed by the given options.\n\n\t`jQuery.wiki(sources…)`\n\t    Wikifies the given content source(s).\n\n\t`<jQuery>.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s), as directed by the given options.\n\n\t`<jQuery>.wiki(sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s).\n*/\n(() => {\n\t'use strict';\n\n\tjQuery.extend({\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out, if there are no content sources.\n\t\t\tif (sources.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\tthis.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out if there are no target element(s) or content sources.\n\t\t\tif (this.length === 0 || sources.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Append the fragment to the target element(s).\n\t\t\tthis.append(frag);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\treturn this.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/util.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, Scripting */\n\nvar Util = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tType Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value yielded by `typeof` (for primitives), the `@@toStringTag`\n\t\tinternal property (for objects), and `'null'` for `null`.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot for objects.\n\t*/\n\tfunction utilGetType(obj) {\n\t\tif (obj === null) { return 'null'; }\n\n\t\tconst baseType = typeof obj;\n\t\treturn baseType === 'object'\n\t\t\t? Object.prototype.toString.call(obj).slice(8, -1)\n\t\t\t: baseType;\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a boolean or one of the strings \"true\"\n\t\tor \"false\".\n\t*/\n\tfunction utilIsBoolean(obj) {\n\t\treturn typeof obj === 'boolean' || typeof obj === 'string' && (obj === 'true' || obj === 'false');\n\t}\n\n\t/*\n\t\tReturns whether the passed value is iterable.\n\t*/\n\tfunction utilIsIterable(obj) {\n\t\treturn obj != null && typeof obj[Symbol.iterator] === 'function'; // lazy equality for null\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a finite number or a numeric string which\n\t\tyields a finite number when parsed.\n\t*/\n\tfunction utilIsNumeric(obj) {\n\t\tlet num;\n\n\t\tswitch (typeof obj) {\n\t\tcase 'number':\n\t\t\tnum = obj;\n\t\t\tbreak;\n\n\t\tcase 'string':\n\t\t\tnum = Number(obj);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !Number.isNaN(num) && Number.isFinite(num);\n\t}\n\n\t/*\n\t\tReturns whether the passed values pass a SameValueZero comparison.\n\n\t\tSEE: http://ecma-international.org/ecma-262/8.0/#sec-samevaluezero\n\t*/\n\tfunction utilSameValueZero(a, b) {\n\t\t/*\n\t\t\tNOTE: This comparison could also be implemented thus:\n\n\t\t\t\t```\n\t\t\t\ta === b ||\n\t\t\t\ttypeof a === 'number' && typeof b === 'number' &&\n\t\t\t\tNumber.isNaN(a) && Number.isNaN(b)\n\t\t\t\t```\n\n\t\t\tThat's needlessly verbose, however, as `NaN` is the only value in\n\t\t\tthe language which is not reflexive.\n\t\t*/\n\t\treturn a === b || a !== a && b !== b;\n\t}\n\n\t/*\n\t\tReturns a pseudo-enumeration created from the given Array, Map, Set, or generic object.\n\t*/\n\tfunction utilToEnum(obj) {\n\t\tconst pEnum = Object.create(null);\n\n\t\tif (obj instanceof Array) {\n\t\t\tobj.forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Set) {\n\t\t\t// NOTE: Use `<Array>.forEach()` here rather than `<Set>.forEach()`\n\t\t\t// as the latter does not provide the indices we require.\n\t\t\tArray.from(obj).forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Map) {\n\t\t\tobj.forEach((val, key) => pEnum[String(key)] = val);\n\t\t}\n\t\telse if (\n\t\t\t   typeof obj === 'object'\n\t\t\t&& obj !== null\n\t\t\t&& Object.getPrototypeOf(obj) === Object.prototype\n\t\t) {\n\t\t\tObject.assign(pEnum, obj);\n\t\t}\n\t\telse {\n\t\t\tthrow new TypeError('Util.toEnum obj parameter must be an Array, Map, Set, or generic object');\n\t\t}\n\n\t\treturn Object.freeze(pEnum);\n\t}\n\n\t/*\n\t\tReturns the value of the `@@toStringTag` property of the given object.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot.\n\t*/\n\tfunction utilToStringTag(obj) {\n\t\treturn Object.prototype.toString.call(obj).slice(8, -1);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tString Encoding Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a trimmed and encoded slug of the passed string that should be safe\n\t\tfor use as a DOM ID or class name.\n\n\t\tNOTE: The range of illegal characters consists of: C0 controls, space, exclamation,\n\t\tdouble quote, number, dollar, percent, ampersand, single quote, left paren, right\n\t\tparen, asterisk, plus, comma, hyphen, period, forward slash, colon, semi-colon,\n\t\tless-than, equals, greater-than, question, at, left bracket, backslash, right\n\t\tbracket, caret, backquote/grave, left brace, pipe/vertical-bar, right brace, tilde,\n\t\tdelete, C1 controls.\n\t*/\n\tconst _illegalSlugCharsRe = /[\\x00-\\x20!-/:-@[-^`{-\\x9f]+/g; // eslint-disable-line no-control-regex\n\t/* legacy */\n\tconst _isInvalidSlugRe = /^-*$/; // Matches the empty string or one comprised solely of hyphens.\n\t/* /legacy */\n\n\tfunction utilSlugify(str) {\n\t\tconst base = String(str).trim();\n\n\t\t/* legacy */\n\t\tconst _legacy = base\n\t\t\t.replace(/[^\\w\\s\\u2013\\u2014-]+/g, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-')\n\t\t\t.toLocaleLowerCase();\n\n\t\tif (!_isInvalidSlugRe.test(_legacy)) {\n\t\t\treturn _legacy;\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn base\n\t\t\t.replace(_illegalSlugCharsRe, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-');\n\n\t\t// For v3.\n\t\t// return base.replace(_illegalSlugCharsRe, '-');\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Escapes the five primary HTML special characters, the backquote,\n\t\tand SugarCube markup metacharacters.\n\t*/\n\tconst _markupCharsRe    = /[!\"#$&'*\\-/<=>?@[\\\\\\]^_`{|}~]/g;\n\tconst _hasMarkupCharsRe = new RegExp(_markupCharsRe.source); // to drop the global flag\n\tconst _markupCharsMap   = utilToEnum({\n\t\t/* eslint-disable quote-props */\n\t\t'!'  : '&#33;',\n\t\t'\"'  : '&quot;',\n\t\t'#'  : '&#35;',\n\t\t'$'  : '&#36;',\n\t\t'&'  : '&amp;',\n\t\t\"'\"  : '&#39;',\n\t\t'*'  : '&#42;',\n\t\t'-'  : '&#45;',\n\t\t'/'  : '&#47;',\n\t\t'<'  : '&lt;',\n\t\t'='  : '&#61;',\n\t\t'>'  : '&gt;',\n\t\t'?'  : '&#63;',\n\t\t'@'  : '&#64;',\n\t\t'['  : '&#91;',\n\t\t'\\\\' : '&#92;',\n\t\t']'  : '&#93;',\n\t\t'^'  : '&#94;',\n\t\t'_'  : '&#95;',\n\t\t'`'  : '&#96;',\n\t\t'{'  : '&#123;',\n\t\t'|'  : '&#124;',\n\t\t'}'  : '&#125;',\n\t\t'~'  : '&#126;'\n\t\t/* eslint-enable quote-props */\n\t});\n\n\tfunction utilEscapeMarkup(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasMarkupCharsRe.test(val)\n\t\t\t? val.replace(_markupCharsRe, ch => _markupCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Only escapes the five primary special characters and the backquote.\n\t*/\n\tconst _htmlCharsRe    = /[&<>\"'`]/g;\n\tconst _hasHtmlCharsRe = new RegExp(_htmlCharsRe.source); // to drop the global flag\n\tconst _htmlCharsMap   = utilToEnum({\n\t\t'&' : '&amp;',\n\t\t'<' : '&lt;',\n\t\t'>' : '&gt;',\n\t\t'\"' : '&quot;',\n\t\t\"'\" : '&#39;',\n\t\t'`' : '&#96;'\n\t});\n\n\tfunction utilEscape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasHtmlCharsRe.test(val)\n\t\t\t? val.replace(_htmlCharsRe, ch => _htmlCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns a decoded version of the passed entity encoded string.\n\n\t\tNOTE: The extended replacement set here, in contrast to `utilEscape()`,\n\t\tis required due to observed stupidity from various sources.\n\t*/\n\tconst _escapedHtmlRe    = /&(?:amp|#38|#x26|lt|#60|#x3c|gt|#62|#x3e|quot|#34|#x22|apos|#39|#x27|#96|#x60);/gi;\n\tconst _hasEscapedHtmlRe = new RegExp(_escapedHtmlRe.source, 'i'); // to drop the global flag\n\tconst _escapedHtmlMap   = utilToEnum({\n\t\t'&amp;'  : '&', // ampersand (HTML character entity, XML predefined entity)\n\t\t'&#38;'  : '&', // ampersand (decimal numeric character reference)\n\t\t'&#x26;' : '&', // ampersand (hexadecimal numeric character reference)\n\t\t'&lt;'   : '<', // less-than (HTML character entity, XML predefined entity)\n\t\t'&#60;'  : '<', // less-than (decimal numeric character reference)\n\t\t'&#x3c;' : '<', // less-than (hexadecimal numeric character reference)\n\t\t'&gt;'   : '>', // greater-than (HTML character entity, XML predefined entity)\n\t\t'&#62;'  : '>', // greater-than (decimal numeric character reference)\n\t\t'&#x3e;' : '>', // greater-than (hexadecimal numeric character reference)\n\t\t'&quot;' : '\"', // double quote (HTML character entity, XML predefined entity)\n\t\t'&#34;'  : '\"', // double quote (decimal numeric character reference)\n\t\t'&#x22;' : '\"', // double quote (hexadecimal numeric character reference)\n\t\t'&apos;' : \"'\", // apostrophe (XML predefined entity)\n\t\t'&#39;'  : \"'\", // apostrophe (decimal numeric character reference)\n\t\t'&#x27;' : \"'\", // apostrophe (hexadecimal numeric character reference)\n\t\t'&#96;'  : '`', // backquote (decimal numeric character reference)\n\t\t'&#x60;' : '`'  // backquote (hexadecimal numeric character reference)\n\t});\n\n\tfunction utilUnescape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasEscapedHtmlRe.test(val)\n\t\t\t? val.replace(_escapedHtmlRe, entity => _escapedHtmlMap[entity.toLowerCase()])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Returns the individual code units of invalid surrogate pairs as-is.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction utilCharAndPosAt(text, position) {\n\t\tconst str  = String(text);\n\t\tconst pos  = Math.trunc(position);\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\tconst retval = {\n\t\t\tchar  : str.charAt(pos),\n\t\t\tstart : pos,\n\t\t\tend   : pos\n\t\t};\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tretval.char = retval.char + str.charAt(nextPos);\n\t\t\tretval.end = nextPos;\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tretval.char = str.charAt(prevPos) + retval.char;\n\t\tretval.start = prevPos;\n\t\treturn retval;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTime Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of milliseconds elapsed since a reference epoch.\n\n\t\tNOTE: Use the Performance API, if available, elsewise use Date as a\n\t\tfailover.  The Performance API is preferred for its monotonic clock—\n\t\tmeaning, it's not subject to the vagaries of timezone changes and leap\n\t\tperiods, as is Date.\n\t*/\n\tconst _nowSource = Has.performance ? performance : Date;\n\n\tfunction utilNow() {\n\t\treturn _nowSource.now();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tConversion Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of miliseconds represented by the passed CSS time string.\n\t*/\n\tconst _cssTimeRe = /^([+-]?(?:\\d*\\.)?\\d+)([Mm]?[Ss])$/;\n\n\tfunction utilFromCssTime(cssTime) {\n\t\tconst match = _cssTimeRe.exec(String(cssTime));\n\n\t\tif (match === null) {\n\t\t\tthrow new SyntaxError(`invalid time value syntax: \"${cssTime}\"`);\n\t\t}\n\n\t\tlet msec = Number(match[1]);\n\n\t\tif (match[2].length === 1) {\n\t\t\tmsec *= 1000;\n\t\t}\n\n\t\tif (Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tthrow new RangeError(`invalid time value: \"${cssTime}\"`);\n\t\t}\n\n\t\treturn msec;\n\t}\n\n\t/*\n\t\tReturns the CSS time string represented by the passed number of milliseconds.\n\t*/\n\tfunction utilToCssTime(msec) {\n\t\tif (typeof msec !== 'number' || Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tlet what;\n\n\t\t\tswitch (typeof msec) {\n\t\t\tcase 'string':\n\t\t\t\twhat = `\"${msec}\"`;\n\t\t\t\tbreak;\n\n\t\t\tcase 'number':\n\t\t\t\twhat = String(msec);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\twhat = utilToStringTag(msec);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow new Error(`invalid milliseconds: ${what}`);\n\t\t}\n\n\t\treturn `${msec}ms`;\n\t}\n\n\t/*\n\t\tReturns the DOM property name represented by the passed CSS property name.\n\t*/\n\tfunction utilFromCssProperty(cssName) {\n\t\tif (!cssName.includes('-')) {\n\t\t\tswitch (cssName) {\n\t\t\tcase 'bgcolor': return 'backgroundColor';\n\t\t\tcase 'float':   return 'cssFloat';\n\t\t\tdefault:        return cssName;\n\t\t\t}\n\t\t}\n\n\t\t// Strip the leading hyphen from the `-ms-` vendor prefix, so it stays lowercased.\n\t\tconst normalized = cssName.slice(0, 4) === '-ms-' ? cssName.slice(1) : cssName;\n\n\t\treturn normalized\n\t\t\t.split('-')\n\t\t\t.map((part, i) => i === 0 ? part : part.toUpperFirst())\n\t\t\t.join('');\n\t}\n\n\t/*\n\t\tReturns an object containing the component properties parsed from the passed URL.\n\t*/\n\tfunction utilParseUrl(url) {\n\t\tconst el       = document.createElement('a');\n\t\tconst queryObj = Object.create(null);\n\n\t\t// Let the `<a>` element parse the URL.\n\t\tel.href = url;\n\n\t\t// Populate the `queryObj` object with the query string attributes.\n\t\tif (el.search) {\n\t\t\tel.search\n\t\t\t\t.replace(/^\\?/, '')\n\t\t\t\t.splitOrEmpty(/(?:&(?:amp;)?|;)/)\n\t\t\t\t.forEach(query => {\n\t\t\t\t\tconst [key, value] = query.split('=');\n\t\t\t\t\tqueryObj[key] = value;\n\t\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tCaveats by browser:\n\t\t\t\tEdge and Internet Explorer (≥8) do not support authentication\n\t\t\t\tinformation within a URL at all and will throw a security exception\n\t\t\t\ton *any* property access if it's included.\n\n\t\t\t\tInternet Explorer does not include the leading forward slash on\n\t\t\t\t`pathname` when required.\n\n\t\t\t\tOpera (Presto) strips the authentication information from `href`\n\t\t\t\tand does not supply `username` or `password`.\n\n\t\t\t\tSafari (ca. v5.1.x) does not supply `username` or `password` and\n\t\t\t\tpeforms URI decoding on `pathname`.\n\t\t*/\n\n\t\t// Patch for IE not including the leading slash on `pathname` when required.\n\t\tconst pathname = el.host && el.pathname[0] !== '/' ? `/${el.pathname}` : el.pathname;\n\n\t\treturn {\n\t\t\t// The full URL that was originally parsed.\n\t\t\thref : el.href,\n\n\t\t\t// The request protocol, lowercased.\n\t\t\tprotocol : el.protocol,\n\n\t\t\t// // The full authentication information.\n\t\t\t// auth : el.username || el.password // eslint-disable-line no-nested-ternary\n\t\t\t// \t? `${el.username}:${el.password}`\n\t\t\t// \t: typeof el.username === 'string' ? '' : undefined,\n\t\t\t//\n\t\t\t// // The username portion of the auth info.\n\t\t\t// username : el.username,\n\t\t\t//\n\t\t\t// // The password portion of the auth info.\n\t\t\t// password : el.password,\n\n\t\t\t// The full host information, including port number, lowercased.\n\t\t\thost : el.host,\n\n\t\t\t// The hostname portion of the host info, lowercased.\n\t\t\thostname : el.hostname,\n\n\t\t\t// The port number portion of the host info.\n\t\t\tport : el.port,\n\n\t\t\t// The full path information, including query info.\n\t\t\tpath : `${pathname}${el.search}`,\n\n\t\t\t// The pathname portion of the path info.\n\t\t\tpathname,\n\n\t\t\t// The query string portion of the path info, including the leading question mark.\n\t\t\tquery  : el.search,\n\t\t\tsearch : el.search,\n\n\t\t\t// The attributes portion of the query string, parsed into an object.\n\t\t\tqueries  : queryObj,\n\t\t\tsearches : queryObj,\n\n\t\t\t// The fragment string, including the leading hash/pound sign.\n\t\t\thash : el.hash\n\t\t};\n\t}\n\n\t/*\n\t\tReturns a new exception based on the given exception.\n\n\t\tNOTE: Mostly useful for making a standard JavaScript exception type copy\n\t\tof a host exception type—e.g. `DOMException` → `Error`.\n\t*/\n\tfunction utilNewExceptionFrom(original, exceptionType, override) {\n\t\tif (typeof original !== 'object' || original === null) {\n\t\t\tthrow new Error('Util.newExceptionFrom original parameter must be an object');\n\t\t}\n\t\tif (typeof exceptionType !== 'function') {\n\t\t\tthrow new Error('Util.newExceptionFrom exceptionType parameter must be an error type constructor');\n\t\t}\n\n\t\tconst ex = new exceptionType(original.message); // eslint-disable-line new-cap\n\n\t\tif (typeof original.name !== 'undefined') {\n\t\t\tex.name = original.name;\n\t\t}\n\t\tif (typeof original.code !== 'undefined') {\n\t\t\tex.code = original.code;\n\t\t}\n\t\tif (typeof original.columnNumber !== 'undefined') {\n\t\t\tex.columnNumber = original.columnNumber;\n\t\t}\n\t\tif (typeof original.description !== 'undefined') {\n\t\t\tex.description = original.description;\n\t\t}\n\t\tif (typeof original.fileName !== 'undefined') {\n\t\t\tex.fileName = original.fileName;\n\t\t}\n\t\tif (typeof original.lineNumber !== 'undefined') {\n\t\t\tex.lineNumber = original.lineNumber;\n\t\t}\n\t\tif (typeof original.number !== 'undefined') {\n\t\t\tex.number = original.number;\n\t\t}\n\t\tif (typeof original.stack !== 'undefined') {\n\t\t\tex.stack = original.stack;\n\t\t}\n\n\t\tconst overrideType = typeof override;\n\n\t\tif (overrideType !== 'undefined') {\n\t\t\tif (overrideType === 'object' && override !== null) {\n\t\t\t\tObject.assign(ex, override);\n\t\t\t}\n\t\t\telse if (overrideType === 'string') {\n\t\t\t\tex.message = override;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('Util.newExceptionFrom override parameter must be an object or string');\n\t\t\t}\n\t\t}\n\n\t\treturn ex;\n\t}\n\n\t/*\n\t\tReturns a sanitized version of the passed `KeyboardEvent.key` value from\n\t\tprevious incarnations of the specification that should better reflect the\n\t\tcurrent incarnation.\n\t*/\n\tconst utilScrubEventKey = (() => {\n\t\tlet separatorKey;\n\t\tlet decimalKey;\n\n\t\t// Attempt to determine the player's 'Separator' and 'Decimal' key values\n\t\t// based on their current locale.\n\t\tif (typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function') {\n\t\t\tconst match = new Intl.NumberFormat().format(111111.5).match(/(\\D*)\\d+(\\D*)/);\n\n\t\t\tif (match) {\n\t\t\t\tseparatorKey = match[1];\n\t\t\t\tdecimalKey   = match[2];\n\t\t\t}\n\t\t}\n\n\t\t// Failover to US-centric values, if using `Intl.NumberFormat` failed.\n\t\tif (!separatorKey && !decimalKey) {\n\t\t\tseparatorKey = ',';\n\t\t\tdecimalKey   = '.';\n\t\t}\n\n\t\t// Maps older `KeyboardEvent.key` values to more current/correct ones.\n\t\tfunction utilScrubEventKey(key) {\n\t\t\tswitch (key) {\n\t\t\t// case 'OS':                 return 'Meta'; // Unreliable.\n\t\t\tcase 'Scroll':             return 'ScrollLock';\n\t\t\tcase 'Spacebar':           return '\\x20';\n\t\t\tcase 'Left':               return 'ArrowLeft';\n\t\t\tcase 'Right':              return 'ArrowRight';\n\t\t\tcase 'Up':                 return 'ArrowUp';\n\t\t\tcase 'Down':               return 'ArrowDown';\n\t\t\tcase 'Del':                return 'Delete';\n\t\t\tcase 'Crsel':              return 'CrSel';\n\t\t\tcase 'Exsel':              return 'ExSel';\n\t\t\tcase 'Esc':                return 'Escape';\n\t\t\tcase 'Apps':               return 'ContextMenu';\n\t\t\tcase 'Nonconvert':         return 'NonConvert';\n\t\t\tcase 'MediaNextTrack':     return 'MediaTrackNext';\n\t\t\tcase 'MediaPreviousTrack': return 'MediaTrackPrevious';\n\t\t\tcase 'VolumeUp':           return 'AudioVolumeUp';\n\t\t\tcase 'VolumeDown':         return 'AudioVolumeDown';\n\t\t\tcase 'VolumeMute':         return 'AudioVolumeMute';\n\t\t\tcase 'Zoom':               return 'ZoomToggle';\n\t\t\tcase 'SelectMedia':        /* see below */\n\t\t\tcase 'MediaSelect':        return 'LaunchMediaPlayer';\n\t\t\tcase 'Add':                return '+';\n\t\t\tcase 'Divide':             return '/';\n\t\t\tcase 'Multiply':           return '*';\n\t\t\tcase 'Subtract':           return '-';\n\t\t\tcase 'Decimal':            return decimalKey;\n\t\t\tcase 'Separator':          return separatorKey;\n\t\t\t}\n\n\t\t\treturn key;\n\t\t}\n\n\t\treturn utilScrubEventKey;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tType Functions.\n\t\t*/\n\t\tgetType       : { value : utilGetType },\n\t\tisBoolean     : { value : utilIsBoolean },\n\t\tisIterable    : { value : utilIsIterable },\n\t\tisNumeric     : { value : utilIsNumeric },\n\t\tsameValueZero : { value : utilSameValueZero },\n\t\ttoEnum        : { value : utilToEnum },\n\t\ttoStringTag   : { value : utilToStringTag },\n\n\t\t/*\n\t\t\tString Encoding Functions.\n\t\t*/\n\t\tslugify      : { value : utilSlugify },\n\t\tescapeMarkup : { value : utilEscapeMarkup },\n\t\tescape       : { value : utilEscape },\n\t\tunescape     : { value : utilUnescape },\n\t\tcharAndPosAt : { value : utilCharAndPosAt },\n\n\t\t/*\n\t\t\tTime Functions.\n\t\t*/\n\t\tnow : { value : utilNow },\n\n\t\t/*\n\t\t\tConversion Functions.\n\t\t*/\n\t\tfromCssTime      : { value : utilFromCssTime },\n\t\ttoCssTime        : { value : utilToCssTime },\n\t\tfromCssProperty  : { value : utilFromCssProperty },\n\t\tparseUrl         : { value : utilParseUrl },\n\t\tnewExceptionFrom : { value : utilNewExceptionFrom },\n\t\tscrubEventKey    : { value : utilScrubEventKey },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\trandom         : { value : Math.random },\n\t\tentityEncode   : { value : utilEscape },\n\t\tentityDecode   : { value : utilUnescape },\n\t\tevalExpression : { value : (...args) => Scripting.evalJavaScript(...args) }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) }  // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/simplestore.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar SimpleStore = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// In-order list of database adapters.\n\tconst _adapters = [];\n\n\t// The initialized adapter.\n\tlet _initialized = null;\n\n\n\t/*******************************************************************************************************************\n\t\tSimpleStore Functions.\n\t*******************************************************************************************************************/\n\tfunction storeCreate(storageId, persistent) {\n\t\tif (_initialized) {\n\t\t\treturn _initialized.create(storageId, persistent);\n\t\t}\n\n\t\t// Return the first adapter which successfully initializes, elsewise throw an exception.\n\t\tfor (let i = 0; i < _adapters.length; ++i) {\n\t\t\tif (_adapters[i].init(storageId, persistent)) {\n\t\t\t\t_initialized = _adapters[i];\n\t\t\t\treturn _initialized.create(storageId, persistent);\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error('no valid storage adapters found');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tAdapters List.\n\n\t\t\tTODO: This should probably have a getter, rather than being exported directly.\n\t\t*/\n\t\tadapters : { value : _adapters },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tcreate : { value : storeCreate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/FCHost.Storage.js\n\n\tCopyright © 2013–2019 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_FCHostStorageAdapter Class.\n        Note that FCHost is only intended for a single document, so we ignore both prefixing and storageID\n\t*******************************************************************************************************************/\n\tclass _FCHostStorageAdapter {\n\t\tconstructor(persistent) {\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.FCHostPersistent;\n\t\t\t\tname   = 'FCHostPersistent';\n\t\t\t}\n\t\t\telse {\n\t\t\t    engine = window.FCHostSession;\n\t\t\t\tname   = 'FCHostSession';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n                \n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\treturn this._engine.keys();\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn this._engine.has(key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.get(key);\n\n\t\t\treturn value == null ? null : _FCHostStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.set(key, _FCHostStorageAdapter._serialize(value));\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.remove(key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tthis._engine.clear();\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(str);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// FCHost feature test.\n\t\tfunction hasFCHostStorage() {\n\t\t\ttry {\n\t\t\t    if (typeof window.FCHostPersistent !== 'undefined')\n\t\t\t        return true;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t_ok = hasFCHostStorage();\n\t\t\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _FCHostStorageAdapter(persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/webstorage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_WebStorageAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _WebStorageAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}.`;\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.localStorage;\n\t\t\t\tname   = 'localStorage';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tengine = window.sessionStorage;\n\t\t\t\tname   = 'sessionStorage';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tconst keys = [];\n\n\t\t\tfor (let i = 0; i < this._engine.length; ++i) {\n\t\t\t\tconst key = this._engine.key(i);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// // FIXME: This method should probably check for the key, rather than comparing its value.\n\t\t\t// return this._engine.getItem(this._prefix + key) != null; // lazy equality for null\n\n\t\t\treturn this._engine.hasOwnProperty(this._prefix + key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.getItem(this._prefix + key);\n\n\t\t\treturn value == null ? null : _WebStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value, compression = true) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis._engine.setItem(this._prefix + key,\n\t\t\t\t\t_WebStorageAdapter._serialize(value, this.persistent && compression));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t/*\n\t\t\t\t\tIf the exception is a quota exceeded error, massage it into something\n\t\t\t\t\ta bit nicer for the player.\n\n\t\t\t\t\tNOTE: Ideally, we could simply do something like checking `ex.code`, but\n\t\t\t\t\tit's a non-standard property and not supported in all browsers.  Thus,\n\t\t\t\t\twe have to resort to pattern matching the name and message—the latter being\n\t\t\t\t\trequired by Opera (Presto).  I hate the parties responsible for this snafu\n\t\t\t\t\tso much.\n\t\t\t\t*/\n\t\t\t\tif (/quota.?(?:exceeded|reached)/i.test(ex.name + ex.message)) {\n\t\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `${this.name} quota exceeded`);\n\t\t\t\t}\n\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.removeItem(this._prefix + key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// return this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj, compression) {\n\t\t\tif (compression) {\n\t\t\t\treturn LZString.compressToUTF16(JSON.stringify(obj));\n\t\t\t}\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse((!str || str[0] == \"{\") ? str : LZString.decompressFromUTF16(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// Web Storage feature test.\n\t\tfunction hasWebStorage(storeId) {\n\t\t\ttry {\n\t\t\t\tconst store = window[storeId];\n\t\t\t\tconst tid   = `_sc_${String(Date.now())}`;\n\t\t\t\tstore.setItem(tid, tid);\n\t\t\t\tconst result = store.getItem(tid) === tid;\n\t\t\t\tstore.removeItem(tid);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t\tJust to be safe, we feature test for both `localStorage` and `sessionStorage`,\n\t\t\tas you never know what browser implementation bugs you're going to run into.\n\t\t*/\n\t\t_ok = hasWebStorage('localStorage') && hasWebStorage('sessionStorage');\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _WebStorageAdapter(storageId, persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/cookie.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Expiry constants.\n\tconst _MAX_EXPIRY = 'Tue, 19 Jan 2038 03:14:07 GMT'; // (new Date((Math.pow(2, 31) - 1) * 1000)).toUTCString()\n\tconst _MIN_EXPIRY = 'Thu, 01 Jan 1970 00:00:00 GMT'; // (new Date(0)).toUTCString()\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_CookieAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _CookieAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}${persistent ? '!' : '*'}.`;\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : 'cookie'\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tif (document.cookie === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\t\t\tconst keys    = [];\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should omit such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\tif (value !== '') {\n\t\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn _CookieAdapter._getCookie(this._prefix + key) !== null;\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = _CookieAdapter._getCookie(this._prefix + key);\n\n\t\t\treturn value === null ? null : _CookieAdapter._deserialize(value);\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\t\t\t\t\t_CookieAdapter._serialize(value),\n\n\t\t\t\t\t// An undefined expiry denotes a session cookie.\n\t\t\t\t\tthis.persistent ? _MAX_EXPIRY : undefined\n\t\t\t\t);\n\n\t\t\t\tif (!this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during set');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\t/*\n\t\t\t\tAttempting to delete a cookie implies setting it, so we test for its existence\n\t\t\t\tbeforehand, to avoid creating it in the event that it does not already exist.\n\t\t\t*/\n\t\t\tif (typeof key !== 'string' || !key || !this.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\n\t\t\t\t\t// Use `undefined` as the value.\n\t\t\t\t\tundefined,\n\n\t\t\t\t\t// Use the epoch as the expiry.\n\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t);\n\n\t\t\t\tif (this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during delete');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _getCookie(prefixedKey) {\n\t\t\tif (!prefixedKey || document.cookie === '') {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (prefixedKey === key) {\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should yield `null` for such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\treturn value || null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic _setCookie(prefixedKey, value, expiry) {\n\t\t\tif (!prefixedKey) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet payload = `${encodeURIComponent(prefixedKey)}=`;\n\n\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\tpayload += encodeURIComponent(value);\n\t\t\t}\n\n\t\t\tif (expiry != null) { // lazy equality for null\n\t\t\t\tpayload += `; expires=${expiry}`;\n\t\t\t}\n\n\t\t\tpayload += '; path=/';\n\t\t\tdocument.cookie = payload;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn LZString.compressToBase64(JSON.stringify(obj));\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(LZString.decompressFromBase64(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit(\n\t\t// Only used for stores updates.\n\t\tstorageId\n\t) {\n\t\t// Cookie feature test.\n\t\ttry {\n\t\t\tconst tid = `_sc_${String(Date.now())}`;\n\n\t\t\t// We only test a session cookie as that should suffice.\n\t\t\t_CookieAdapter._setCookie(tid, _CookieAdapter._serialize(tid), undefined);\n\t\t\t_ok = _CookieAdapter._deserialize(_CookieAdapter._getCookie(tid)) === tid;\n\t\t\t_CookieAdapter._setCookie(tid, undefined, _MIN_EXPIRY);\n\t\t}\n\t\tcatch (ex) {\n\t\t\t_ok = false;\n\t\t}\n\n\t\t/* legacy */\n\t\t// Attempt to update the cookie stores, if necessary.  This should happen only during initialization.\n\t\tif (_ok) {\n\t\t\t_updateCookieStores(storageId);\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _CookieAdapter(storageId, persistent);\n\t}\n\n\t/* legacy */\n\t// Updates old non-segmented cookie stores into segmented stores.\n\tfunction _updateCookieStores(storageId) {\n\t\tif (document.cookie === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldPrefix     = `${storageId}.`;\n\t\tconst oldPrefixRe   = new RegExp(`^${RegExp.escape(oldPrefix)}`);\n\t\tconst persistPrefix = `${storageId}!.`;\n\t\tconst sessionPrefix = `${storageId}*.`;\n\t\tconst sessionTestRe = /\\.(?:state|rcWarn)$/;\n\t\tconst cookies       = document.cookie.split(/;\\s*/);\n\n\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\tif (oldPrefixRe.test(key)) {\n\t\t\t\t/*\n\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\tnot a serialized empty string, so we should skip processing such pairs.\n\t\t\t\t*/\n\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\tif (value !== '') {\n\t\t\t\t\tconst persist = !sessionTestRe.test(key);\n\n\t\t\t\t\t// Delete the old k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t\t);\n\n\t\t\t\t\t// Set the new k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey.replace(oldPrefixRe, () => persist ? persistPrefix : sessionPrefix),\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tpersist ? _MAX_EXPIRY : undefined\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t/* /legacy */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/debugview.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: Make this use jQuery throughout.\n*/\nvar DebugView = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDebugView Class.\n\t*******************************************************************************************************************/\n\tclass DebugView {\n\t\tconstructor(parent, type, name, title) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : parent\n\t\t\t\t},\n\n\t\t\t\tview : {\n\t\t\t\t\tvalue : document.createElement('span')\n\t\t\t\t},\n\n\t\t\t\tbreak : {\n\t\t\t\t\tvalue : document.createElement('wbr')\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up the wrapper (`<span>`) element.\n\t\t\tjQuery(this.view)\n\t\t\t\t.attr({\n\t\t\t\t\ttitle,\n\t\t\t\t\t'aria-label' : title,\n\t\t\t\t\t'data-type'  : type != null ? type : '', // lazy equality for null\n\t\t\t\t\t'data-name'  : name != null ? name : ''  // lazy equality for null\n\t\t\t\t})\n\t\t\t\t.addClass('debug');\n\n\t\t\t// Set up the word break (`<wbr>`) element.\n\t\t\tjQuery(this.break).addClass('debug hidden');\n\n\t\t\t// Add the wrapper (`<span>`) and word break (`<wbr>`) elements to the `parent` element.\n\t\t\tthis.parent.appendChild(this.view);\n\t\t\tthis.parent.appendChild(this.break);\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this.view;\n\t\t}\n\n\t\tget type() {\n\t\t\treturn this.view.getAttribute('data-type');\n\t\t}\n\t\tset type(type) {\n\t\t\tthis.view.setAttribute('data-type', type != null ? type : ''); // lazy equality for null\n\t\t}\n\n\t\tget name() {\n\t\t\treturn this.view.getAttribute('data-name');\n\t\t}\n\t\tset name(name) {\n\t\t\tthis.view.setAttribute('data-name', name != null ? name : ''); // lazy equality for null\n\t\t}\n\n\t\tget title() {\n\t\t\treturn this.view.title;\n\t\t}\n\t\tset title(title) {\n\t\t\tthis.view.title = title;\n\t\t}\n\n\t\tappend(el) {\n\t\t\tjQuery(this.view).append(el);\n\t\t\treturn this;\n\t\t}\n\n\t\tmodes(options) {\n\t\t\tif (options == null) { // lazy equality for null\n\t\t\t\tconst current = {};\n\n\t\t\t\tthis.view.className.splitOrEmpty(/\\s+/).forEach(name => {\n\t\t\t\t\tif (name !== 'debug') {\n\t\t\t\t\t\tcurrent[name] = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\telse if (typeof options === 'object') {\n\t\t\t\tObject.keys(options).forEach(function (name) {\n\t\t\t\t\tthis[options[name] ? 'addClass' : 'removeClass'](name);\n\t\t\t\t}, jQuery(this.view));\n\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tthrow new Error('DebugView.prototype.modes options parameter must be an object or null/undefined');\n\t\t}\n\n\t\tremove() {\n\t\t\tconst $view = jQuery(this.view);\n\n\t\t\tif (this.view.hasChildNodes()) {\n\t\t\t\t$view.contents().appendTo(this.parent);\n\t\t\t}\n\n\t\t\t$view.remove();\n\t\t\tjQuery(this.break).remove();\n\t\t}\n\n\t\tstatic isEnabled() {\n\t\t\treturn jQuery(document.documentElement).attr('data-debug-view') === 'enabled';\n\t\t}\n\n\t\tstatic enable() {\n\t\t\tjQuery(document.documentElement).attr('data-debug-view', 'enabled');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic disable() {\n\t\t\tjQuery(document.documentElement).removeAttr('data-debug-view');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic toggle() {\n\t\t\tif (jQuery(document.documentElement).attr('data-debug-view') === 'enabled') {\n\t\t\t\tDebugView.disable();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDebugView.enable();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn DebugView;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/nodetyper.js\n\n\tCopyright © 2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar NodeTyper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tNodeTyper Class.\n\t*******************************************************************************************************************/\n\tclass NodeTyper {\n\t\tconstructor(config) {\n\t\t\tif (typeof config !== 'object' || config === null) {\n\t\t\t\tthrow new Error(`config parameter must be an object (received: ${Util.getType(config)})`);\n\t\t\t}\n\t\t\tif (!config.hasOwnProperty('targetNode') || !(config.targetNode instanceof Node)) {\n\t\t\t\tthrow new Error('config parameter object \"targetNode\" property must be a node');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tnode : {\n\t\t\t\t\tvalue : config.targetNode\n\t\t\t\t},\n\n\t\t\t\tchildNodes : {\n\t\t\t\t\tvalue : []\n\t\t\t\t},\n\n\t\t\t\tnodeValue : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t},\n\n\t\t\t\tappendTo : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.parentNode || null\n\t\t\t\t},\n\n\t\t\t\tclassNames : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.classNames || null\n\t\t\t\t},\n\n\t\t\t\tfinished : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst node = this.node;\n\n\t\t\tif (node.nodeValue) {\n\t\t\t\tthis.nodeValue = node.nodeValue;\n\t\t\t\tnode.nodeValue = '';\n\t\t\t}\n\n\t\t\tlet childNode;\n\n\t\t\twhile ((childNode = node.firstChild) !== null) {\n\t\t\t\tthis.childNodes.push(new NodeTyper({\n\t\t\t\t\ttargetNode : childNode,\n\t\t\t\t\tparentNode : node,\n\t\t\t\t\tclassNames : this.classNames\n\t\t\t\t}));\n\n\t\t\t\tnode.removeChild(childNode);\n\t\t\t}\n\t\t}\n\n\t\tfinish() {\n\t\t\twhile (this.type(true)) /* no-op */;\n\t\t\treturn false;\n\t\t}\n\n\t\ttype(flush) {\n\t\t\tif (this.finished) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (this.appendTo) {\n\t\t\t\tthis.appendTo.appendChild(this.node);\n\t\t\t\tthis.appendTo = null;\n\n\t\t\t\t// Immediately finish typing this node if….\n\t\t\t\tif (\n\t\t\t\t\t// …it's neither a element or text node.\n\t\t\t\t\tthis.node.nodeType !== Node.ELEMENT_NODE && this.node.nodeType !== Node.TEXT_NODE\n\n\t\t\t\t\t// …or the computed value of its parent node's `display` property is `'none'`.\n\t\t\t\t\t|| jQuery(this.node.parentNode).css('display') === 'none'\n\t\t\t\t) {\n\t\t\t\t\treturn this.finish();\n\t\t\t\t}\n\n\t\t\t\tif (this.node.parentNode && this.classNames) {\n\t\t\t\t\tjQuery(this.node.parentNode).addClass(this.classNames);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.nodeValue) {\n\t\t\t\tif (flush) {\n\t\t\t\t\t// Concatenate here in case we've already done some processing.\n\t\t\t\t\tthis.node.nodeValue += this.nodeValue;\n\t\t\t\t\tthis.nodeValue = '';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Use `Util.charAndPosAt()` here to properly handle Unicode code points\n\t\t\t\t\t// that are comprised of surrogate pairs.\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(this.nodeValue, 0);\n\t\t\t\t\tthis.node.nodeValue += char;\n\t\t\t\t\tthis.nodeValue = this.nodeValue.slice(1 + end - start);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (this.classNames) {\n\t\t\t\tjQuery(this.node.parentNode).removeClass(this.classNames);\n\t\t\t\tthis.classNames = null;\n\t\t\t}\n\n\t\t\tconst childNodes = this.childNodes;\n\n\t\t\twhile (childNodes.length > 0) {\n\t\t\t\tif (childNodes[0].type()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tchildNodes.shift();\n\t\t\t}\n\n\t\t\tthis.finished = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn NodeTyper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/prngwrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar PRNGWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPRNGWrapper Class.\n\t*******************************************************************************************************************/\n\tclass PRNGWrapper {\n\t\tconstructor(seed, useEntropy) {\n\t\t\t/* eslint-disable new-cap */\n\t\t\tObject.defineProperties(this, new Math.seedrandom(seed, useEntropy, (prng, seed) => ({\n\t\t\t\t_prng : {\n\t\t\t\t\tvalue : prng\n\t\t\t\t},\n\n\t\t\t\tseed : {\n\t\t\t\t\t/*\n\t\t\t\t\t\tTODO: Make this non-writable.\n\t\t\t\t\t*/\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : seed\n\t\t\t\t},\n\n\t\t\t\tpull : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\trandom : {\n\t\t\t\t\tvalue() {\n\t\t\t\t\t\t++this.pull;\n\t\t\t\t\t\treturn this._prng();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})));\n\t\t\t/* eslint-enable new-cap */\n\t\t}\n\n\t\tstatic marshal(prng) {\n\t\t\tif (!prng || !prng.hasOwnProperty('seed') || !prng.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG is missing required data');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tseed : prng.seed,\n\t\t\t\tpull : prng.pull\n\t\t\t};\n\t\t}\n\n\t\tstatic unmarshal(prngObj) {\n\t\t\tif (!prngObj || !prngObj.hasOwnProperty('seed') || !prngObj.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG object is missing required data');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCreate a new PRNG using the original seed and pull values from it until it\n\t\t\t\thas reached the original pull count.\n\t\t\t*/\n\t\t\tconst prng = new PRNGWrapper(prngObj.seed, false);\n\n\t\t\tfor (let i = prngObj.pull; i > 0; --i) {\n\t\t\t\tprng.random();\n\t\t\t}\n\n\t\t\treturn prng;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn PRNGWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/stylewrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Story, Wikifier */\n\nvar StyleWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _imageMarkupRe    = new RegExp(Patterns.cssImage, 'g');\n\tconst _hasImageMarkupRe = new RegExp(Patterns.cssImage);\n\n\n\t/*******************************************************************************************************************\n\t\tStyleWrapper Class.\n\t*******************************************************************************************************************/\n\tclass StyleWrapper {\n\t\tconstructor(style) {\n\t\t\tif (style == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('StyleWrapper style parameter must be an HTMLStyleElement object');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tstyle : {\n\t\t\t\t\tvalue : style\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tisEmpty() {\n\t\t\t// This should work in all supported browsers.\n\t\t\treturn this.style.cssRules.length === 0;\n\t\t}\n\n\t\tset(rawCss) {\n\t\t\tthis.clear();\n\t\t\tthis.add(rawCss);\n\t\t}\n\n\t\tadd(rawCss) {\n\t\t\tlet css = rawCss;\n\n\t\t\t// Check for wiki image transclusion.\n\t\t\tif (_hasImageMarkupRe.test(css)) {\n\t\t\t\t/*\n\t\t\t\t\tThe JavaScript specifications, since at least ES3, say that `<String>.replace()`\n\t\t\t\t\tshould reset a global-flagged regular expression's `lastIndex` property to `0`\n\t\t\t\t\tupon invocation.  Buggy browser versions exist, however, which do not reset\n\t\t\t\t\t`lastIndex`, so we should do so manually to support those browsers.\n\n\t\t\t\t\tNOTE: I do not think this is actually necessary, since `_imageMarkupRe` is\n\t\t\t\t\tscoped to this module—meaning users should not be able to access it.  That\n\t\t\t\t\tbeing the case, and since we search to exhaustion which should also cause\n\t\t\t\t\t`lastIndex` to be reset, there should never be an instance where we invoke\n\t\t\t\t\t`css.replace()` and `_imageMarkupRe.lastIndex` is not already `0`.  Still,\n\t\t\t\t\tconsidering the other bug, better safe than sorry.\n\t\t\t\t*/\n\t\t\t\t_imageMarkupRe.lastIndex = 0;\n\n\t\t\t\tcss = css.replace(_imageMarkupRe, wikiImage => {\n\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t});\n\n\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t*/\n\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For IE ≤ 10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText += css;\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥ 11).\n\t\t\telse {\n\t\t\t\tthis.style.appendChild(document.createTextNode(css));\n\t\t\t}\n\t\t}\n\n\t\tclear() {\n\t\t\t// For IE ≤10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText = '';\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥11).\n\t\t\telse {\n\t\t\t\tjQuery(this.style).empty();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn StyleWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/diff.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, clone */\n\nvar Diff = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDiff Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDiff operations object (pseudo-enumeration).\n\t*/\n\tconst Op = Util.toEnum({\n\t\tDelete      : 0,\n\t\tSpliceArray : 1,\n\t\tCopy        : 2,\n\t\tCopyDate    : 3\n\t});\n\n\t/*\n\t\tReturns a difference object generated from comparing the orig and dest objects.\n\t*/\n\tfunction diff(orig, dest) /* diff object */ {\n\t\tconst objToString = Object.prototype.toString;\n\t\tconst origIsArray = orig instanceof Array;\n\t\tconst keys        = []\n\t\t\t.concat(Object.keys(orig), Object.keys(dest))\n\t\t\t.sort()\n\t\t\t.filter((val, i, arr) => i === 0 || arr[i - 1] !== val);\n\t\tconst diffed      = {};\n\t\tlet aOpRef;\n\n\t\tconst keyIsAOpRef = key => key === aOpRef;\n\n\t\t/* eslint-disable max-depth */\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key   = keys[i];\n\t\t\tconst origP = orig[key];\n\t\t\tconst destP = dest[key];\n\n\t\t\tif (orig.hasOwnProperty(key)) {\n\t\t\t\t// Key exists in both.\n\t\t\t\tif (dest.hasOwnProperty(key)) {\n\t\t\t\t\t// Values are exactly the same, so do nothing.\n\t\t\t\t\tif (origP === destP) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Values are of the same basic type.\n\t\t\t\t\tif (typeof origP === typeof destP) { // eslint-disable-line valid-typeof\n\t\t\t\t\t\t// Values are functions.\n\t\t\t\t\t\tif (typeof origP === 'function') {\n\t\t\t\t\t\t\t/* diffed[key] = [Op.Copy, destP]; */\n\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are primitives.\n\t\t\t\t\t\telse if (typeof origP !== 'object' || origP === null) {\n\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are objects.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst origPType = objToString.call(origP);\n\t\t\t\t\t\t\tconst destPType = objToString.call(destP);\n\n\t\t\t\t\t\t\t// Values are objects of the same reported type.\n\t\t\t\t\t\t\tif (origPType === destPType) {\n\t\t\t\t\t\t\t\t// Various special cases to handle supported non-generic objects.\n\t\t\t\t\t\t\t\tif (origP instanceof Date) {\n\t\t\t\t\t\t\t\t\tif (Number(origP) !== Number(destP)) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Map) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof RegExp) {\n\t\t\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Set) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Unknown non-generic objects (custom or unsupported natives).\n\t\t\t\t\t\t\t\telse if (origPType !== '[object Object]') {\n\t\t\t\t\t\t\t\t\t// We cannot know how to process these objects,\n\t\t\t\t\t\t\t\t\t// so we simply accept them as-is.\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Generic objects.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tconst recurse = diff(origP, destP);\n\n\t\t\t\t\t\t\t\t\tif (recurse !== null) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = recurse;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Values are objects of different reported types.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Values are of different types.\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = [\n\t\t\t\t\t\t\tOp.Copy,\n\t\t\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Key only exists in orig.\n\t\t\t\telse {\n\t\t\t\t\tif (origIsArray && Util.isNumeric(key)) {\n\t\t\t\t\t\tconst nKey = Number(key);\n\n\t\t\t\t\t\tif (!aOpRef) {\n\t\t\t\t\t\t\taOpRef = '';\n\n\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\taOpRef += '~';\n\t\t\t\t\t\t\t} while (keys.some(keyIsAOpRef));\n\n\t\t\t\t\t\t\tdiffed[aOpRef] = [Op.SpliceArray, nKey, nKey];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey < diffed[aOpRef][1]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][1] = nKey;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey > diffed[aOpRef][2]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][2] = nKey;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = Op.Delete;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Key only exists in dest.\n\t\t\telse {\n\t\t\t\tdiffed[key] = [\n\t\t\t\t\tOp.Copy,\n\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\t/* eslint-enable max-depth */\n\n\t\treturn Object.keys(diffed).length > 0 ? diffed : null;\n\t}\n\n\t/*\n\t\tReturns the object resulting from updating the orig object with the diffed object.\n\t*/\n\tfunction patch(orig, diffed) /* patched object */ {\n\t\tconst keys    = Object.keys(diffed || {});\n\t\tconst patched = clone(orig);\n\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key     = keys[i];\n\t\t\tconst diffedP = diffed[key];\n\n\t\t\tif (diffedP === Op.Delete) {\n\t\t\t\tdelete patched[key];\n\t\t\t}\n\t\t\telse if (diffedP instanceof Array) {\n\t\t\t\tswitch (diffedP[0]) {\n\t\t\t\tcase Op.SpliceArray:\n\t\t\t\t\tpatched.splice(diffedP[1], 1 + (diffedP[2] - diffedP[1]));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.Copy:\n\t\t\t\t\tpatched[key] = clone(diffedP[1]);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.CopyDate:\n\t\t\t\t\tpatched[key] = new Date(diffedP[1]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpatched[key] = patch(patched[key], diffedP);\n\t\t\t}\n\t\t}\n\n\t\treturn patched;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tOp    : { value : Op },\n\t\tdiff  : { value : diff },\n\t\tpatch : { value : patch }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/l10n.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global l10nStrings, strings */\n\nvar L10n = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Replacement pattern regular expressions.\n\tconst _patternRe    = /\\{\\w+\\}/g;\n\tconst _hasPatternRe = new RegExp(_patternRe.source); // to drop the global flag\n\n\n\t/*******************************************************************************************************************\n\t\tLocalization Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nInit() {\n\t\t/* legacy */\n\t\t_mapStringsToL10nStrings();\n\t\t/* /legacy */\n\t}\n\n\t/*******************************************************************************************************************\n\t\tLocalized String Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nGet(ids, overrides) {\n\t\tif (!ids) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst id = (idList => {\n\t\t\tlet selectedId;\n\t\t\tidList.some(id => {\n\t\t\t\tif (l10nStrings.hasOwnProperty(id)) {\n\t\t\t\t\tselectedId = id;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\treturn selectedId;\n\t\t})(Array.isArray(ids) ? ids : [ids]);\n\n\t\tif (!id) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst maxIterations = 50;\n\t\tlet processed = l10nStrings[id];\n\t\tlet iteration = 0;\n\n\t\twhile (_hasPatternRe.test(processed)) {\n\t\t\tif (++iteration > maxIterations) {\n\t\t\t\tthrow new Error('L10n.get exceeded maximum replacement iterations, probable infinite loop');\n\t\t\t}\n\n\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t_patternRe.lastIndex = 0;\n\n\t\t\tprocessed = processed.replace(_patternRe, pat => {\n\t\t\t\tconst subId = pat.slice(1, -1);\n\n\t\t\t\tif (overrides && overrides.hasOwnProperty(subId)) {\n\t\t\t\t\treturn overrides[subId];\n\t\t\t\t}\n\t\t\t\telse if (l10nStrings.hasOwnProperty(subId)) {\n\t\t\t\t\treturn l10nStrings[subId];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn processed;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tAttempt to map legacy `strings` object properties to the `l10nStrings` object.\n\t*/\n\tfunction _mapStringsToL10nStrings() {\n\t\tif (strings && Object.keys(strings).length > 0) {\n\t\t\tObject.keys(l10nStrings).forEach(id => {\n\t\t\t\ttry {\n\t\t\t\t\tlet value;\n\n\t\t\t\t\tswitch (id) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tGeneral.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'identity': value = strings.identity; break;\n\t\t\t\t\tcase 'aborting': value = strings.aborting; break;\n\t\t\t\t\tcase 'cancel':   value = strings.cancel; break;\n\t\t\t\t\tcase 'close':    value = strings.close; break;\n\t\t\t\t\tcase 'ok':       value = strings.ok; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tErrors.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'errorTitle':              value = strings.errors.title; break;\n\t\t\t\t\tcase 'errorNonexistentPassage': value = strings.errors.nonexistentPassage; break;\n\t\t\t\t\tcase 'errorSaveMissingData':    value = strings.errors.saveMissingData; break;\n\t\t\t\t\tcase 'errorSaveIdMismatch':     value = strings.errors.saveIdMismatch; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tWarnings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'warningDegraded': value = strings.warnings.degraded; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tDebug View.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'debugViewTitle':  value = strings.debugView.title; break;\n\t\t\t\t\tcase 'debugViewToggle': value = strings.debugView.toggle; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tUI bar.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'uiBarToggle':   value = strings.uiBar.toggle; break;\n\t\t\t\t\tcase 'uiBarBackward': value = strings.uiBar.backward; break;\n\t\t\t\t\tcase 'uiBarForward':  value = strings.uiBar.forward; break;\n\t\t\t\t\tcase 'uiBarJumpto':   value = strings.uiBar.jumpto; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tJump To.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'jumptoTitle':       value = strings.jumpto.title; break;\n\t\t\t\t\tcase 'jumptoTurn':        value = strings.jumpto.turn; break;\n\t\t\t\t\tcase 'jumptoUnavailable': value = strings.jumpto.unavailable; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSaves.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'savesTitle':       value = strings.saves.title; break;\n\t\t\t\t\tcase 'savesDisallowed':  value = strings.saves.disallowed; break;\n\t\t\t\t\tcase 'savesIncapable':   value = strings.saves.incapable; break;\n\t\t\t\t\tcase 'savesLabelAuto':   value = strings.saves.labelAuto; break;\n\t\t\t\t\tcase 'savesLabelDelete': value = strings.saves.labelDelete; break;\n\t\t\t\t\tcase 'savesLabelExport': value = strings.saves.labelExport; break;\n\t\t\t\t\tcase 'savesLabelImport': value = strings.saves.labelImport; break;\n\t\t\t\t\tcase 'savesLabelLoad':   value = strings.saves.labelLoad; break;\n\t\t\t\t\tcase 'savesLabelClear':  value = strings.saves.labelClear; break;\n\t\t\t\t\tcase 'savesLabelSave':   value = strings.saves.labelSave; break;\n\t\t\t\t\tcase 'savesLabelSlot':   value = strings.saves.labelSlot; break;\n\t\t\t\t\tcase 'savesUnavailable': value = strings.saves.unavailable; break;\n\t\t\t\t\tcase 'savesUnknownDate': value = strings.saves.unknownDate; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSettings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'settingsTitle': value = strings.settings.title; break;\n\t\t\t\t\tcase 'settingsOff':   value = strings.settings.off; break;\n\t\t\t\t\tcase 'settingsOn':    value = strings.settings.on; break;\n\t\t\t\t\tcase 'settingsReset': value = strings.settings.reset; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tRestart.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'restartTitle':  value = strings.restart.title; break;\n\t\t\t\t\tcase 'restartPrompt': value = strings.restart.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tShare.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'shareTitle': value = strings.share.title; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAlert.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'alertTitle': /* none */ break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAutoload.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'autoloadTitle':  value = strings.autoload.title; break;\n\t\t\t\t\tcase 'autoloadCancel': value = strings.autoload.cancel; break;\n\t\t\t\t\tcase 'autoloadOk':     value = strings.autoload.ok; break;\n\t\t\t\t\tcase 'autoloadPrompt': value = strings.autoload.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tMacros.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'macroBackText':   value = strings.macros.back.text; break;\n\t\t\t\t\tcase 'macroReturnText': value = strings.macros.return.text; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tl10nStrings[id] = value.replace(/%\\w+%/g, pat => `{${pat.slice(1, -1)}}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tLocalization Functions.\n\t\t*/\n\t\tinit : { value : l10nInit },\n\n\t\t/*\n\t\t\tLocalized String Functions.\n\t\t*/\n\t\tget : { value : l10nGet }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/legacy.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\t[DEPRECATED] The `strings` object is deprecated and should no longer be used.\n\tAll new or updated translations should be based upon the `l10nStrings` object\n\t(see: `l10n/strings.js`).\n\n\tLegacy/existing uses of the `strings` object will be mapped to the `l10nStrings`\n\tobject after user script evaluation.\n*/\nvar strings = { // eslint-disable-line no-unused-vars, no-var\n\terrors    : {},\n\twarnings  : {},\n\tdebugView : {},\n\tuiBar     : {},\n\tjumpto    : {},\n\tsaves     : {},\n\tsettings  : {},\n\trestart   : {},\n\tshare     : {},\n\tautoload  : {},\n\tmacros    : {\n\t\tback   : {},\n\t\treturn : {}\n\t}\n};\n\n/***********************************************************************************************************************\n\n\tl10n/strings.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* eslint-disable max-len, prefer-template */\n\n/*\n\tATTENTION TRANSLATORS\n\n\tPlease use the `locale/l10n-template.js` file, from the root of the repository,\n\tas the template for your translation rather than this file.\n\n\tSEE: https://github.com/tmedwards/sugarcube-2/tree/develop/locale\n*/\nvar l10nStrings = { // eslint-disable-line no-unused-vars, no-var\n\t/*\n\t\tGeneral.\n\t*/\n\tidentity : 'game',\n\taborting : 'Aborting',\n\tcancel   : 'Cancel',\n\tclose    : 'Close',\n\tok       : 'OK',\n\n\t/*\n\t\tErrors.\n\t*/\n\terrorTitle              : 'Error',\n\terrorToggle             : 'Toggle the error view',\n\terrorNonexistentPassage : 'the passage \"{passage}\" does not exist', // NOTE: `passage` is supplied locally\n\terrorSaveMissingData    : 'save is missing required data. Either the loaded file is not a save or the save has become corrupted',\n\terrorSaveIdMismatch     : 'save is from the wrong {identity}',\n\n\t/*\n\t\tWarnings.\n\t*/\n\t_warningIntroLacking  : 'Your browser either lacks or has disabled',\n\t_warningOutroDegraded : ', so this {identity} is running in a degraded mode. You may be able to continue, however, some parts may not work properly.',\n\twarningNoWebStorage   : '{_warningIntroLacking} the Web Storage API{_warningOutroDegraded}',\n\twarningDegraded       : '{_warningIntroLacking} some of the capabilities required by this {identity}{_warningOutroDegraded}',\n\n\t/*\n\t\tDebug bar.\n\t*/\n\tdebugBarToggle      : 'Toggle the debug bar',\n\tdebugBarNoWatches   : '\\u2014 no watches set \\u2014',\n\tdebugBarAddWatch    : 'Add watch',\n\tdebugBarDeleteWatch : 'Delete watch',\n\tdebugBarWatchAll    : 'Watch all',\n\tdebugBarWatchNone   : 'Delete all',\n\tdebugBarLabelAdd    : 'Add',\n\tdebugBarLabelWatch  : 'Watch',\n\tdebugBarLabelTurn   : 'Turn', // (noun) chance to act (in a game), moment, period\n\tdebugBarLabelViews  : 'Views',\n\tdebugBarViewsToggle : 'Toggle the debug views',\n\tdebugBarWatchToggle : 'Toggle the watch panel',\n\n\t/*\n\t\tUI bar.\n\t*/\n\tuiBarToggle   : 'Toggle the UI bar',\n\tuiBarBackward : 'Go backward within the {identity} history',\n\tuiBarForward  : 'Go forward within the {identity} history',\n\tuiBarJumpto   : 'Jump to a specific point within the {identity} history',\n\n\t/*\n\t\tJump To.\n\t*/\n\tjumptoTitle       : 'Jump To',\n\tjumptoTurn        : 'Turn', // (noun) chance to act (in a game), moment, period\n\tjumptoUnavailable : 'No jump points currently available\\u2026',\n\n\t/*\n\t\tSaves.\n\t*/\n\tsavesTitle       : 'Saves',\n\tsavesDisallowed  : 'Saving has been disallowed on this passage.',\n\tsavesIncapable   : '{_warningIntroLacking} the capabilities required to support saves, so saves have been disabled for this session.',\n\tsavesLabelAuto   : 'Autosave',\n\tsavesLabelDelete : 'Delete',\n\tsavesLabelExport : 'Save to Disk\\u2026',\n\tsavesLabelImport : 'Load from Disk\\u2026',\n\tsavesLabelLoad   : 'Load',\n\tsavesLabelClear  : 'Delete All',\n\tsavesLabelSave   : 'Save',\n\tsavesLabelSlot   : 'Slot',\n\tsavesUnavailable : 'No save slots found\\u2026',\n\tsavesUnknownDate : 'unknown',\n\n\t/*\n\t\tSettings.\n\t*/\n\tsettingsTitle : 'Settings',\n\tsettingsOff   : 'Off',\n\tsettingsOn    : 'On',\n\tsettingsReset : 'Reset to Defaults',\n\n\t/*\n\t\tRestart.\n\t*/\n\trestartTitle  : 'Restart',\n\trestartPrompt : 'Are you sure that you want to restart? Unsaved progress will be lost.',\n\n\t/*\n\t\tShare.\n\t*/\n\tshareTitle : 'Share',\n\n\t/*\n\t\tAlert.\n\t*/\n\talertTitle : 'Alert',\n\n\t/*\n\t\tAutoload.\n\t*/\n\tautoloadTitle  : 'Autoload',\n\tautoloadCancel : 'Go to start',\n\tautoloadOk     : 'Load autosave',\n\tautoloadPrompt : 'An autosave exists. Load it now or go to the start?',\n\n\t/*\n\t\tMacros.\n\t*/\n\tmacroBackText   : 'Back',  // (verb) rewind, revert\n\tmacroReturnText : 'Return' // (verb) go/send back\n};\n\n/***********************************************************************************************************************\n\n\tconfig.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar Config = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// General settings.\n\tlet _debug                 = false;\n\tlet _addVisitedLinkClass   = false;\n\tlet _cleanupWikifierOutput = false;\n\tlet _loadDelay             = 0;\n\n\t// Audio settings.\n\tlet _audioPauseOnFadeToZero = true;\n\tlet _audioPreloadMetadata   = true;\n\n\t// State history settings.\n\tlet _historyControls  = true;\n\tlet _historyMaxStates = 100;\n\n\t// Macros settings.\n\tlet _macrosIfAssignmentError   = true;\n\tlet _macrosMaxLoopIterations   = 1000;\n\tlet _macrosTypeSkipKey         = '\\x20'; // Space\n\tlet _macrosTypeVisitedPassages = true;\n\n\t// Navigation settings.\n\tlet _navigationOverride;\n\n\t// Passages settings.\n\tlet _passagesDescriptions;\n\tlet _passagesDisplayTitles = false;\n\tlet _passagesNobr          = false;\n\tlet _passagesStart; // set by `Story.load()`\n\tlet _passagesOnProcess;\n\tlet _passagesTransitionOut;\n\n\t// Saves settings.\n\tlet _savesAutoload;\n\tlet _savesAutosave;\n\tlet _savesId        = 'untitled-story';\n\tlet _savesIsAllowed;\n\tlet _savesOnLoad;\n\tlet _savesOnSave;\n\tlet _savesSlots     = 8;\n\tlet _savesVersion;\n\n\t// UI settings.\n\tlet _uiStowBarInitially    = 800;\n\tlet _uiUpdateStoryElements = true;\n\n\n\t/*******************************************************************************\n\t\tError Constants.\n\t*******************************************************************************/\n\n\tconst _errHistoryModeDeprecated     = 'Config.history.mode has been deprecated and is no longer used by SugarCube, please remove it from your code';\n\tconst _errHistoryTrackingDeprecated = 'Config.history.tracking has been deprecated, use Config.history.maxStates instead';\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze({\n\t\t/*\n\t\t\tGeneral settings.\n\t\t*/\n\t\tget debug() { return _debug; },\n\t\tset debug(value) { _debug = Boolean(value); },\n\n\t\tget addVisitedLinkClass() { return _addVisitedLinkClass; },\n\t\tset addVisitedLinkClass(value) { _addVisitedLinkClass = Boolean(value); },\n\n\t\tget cleanupWikifierOutput() { return _cleanupWikifierOutput; },\n\t\tset cleanupWikifierOutput(value) { _cleanupWikifierOutput = Boolean(value); },\n\n\t\tget loadDelay() { return _loadDelay; },\n\t\tset loadDelay(value) {\n\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\tthrow new RangeError('Config.loadDelay must be a non-negative integer');\n\t\t\t}\n\n\t\t\t_loadDelay = value;\n\t\t},\n\n\t\t/*\n\t\t\tAudio settings.\n\t\t*/\n\t\taudio : Object.freeze({\n\t\t\tget pauseOnFadeToZero() { return _audioPauseOnFadeToZero; },\n\t\t\tset pauseOnFadeToZero(value) { _audioPauseOnFadeToZero = Boolean(value); },\n\n\t\t\tget preloadMetadata() { return _audioPreloadMetadata; },\n\t\t\tset preloadMetadata(value) { _audioPreloadMetadata = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tState history settings.\n\t\t*/\n\t\thistory : Object.freeze({\n\t\t\t// TODO: (v3) This should be under UI settings → `Config.ui.historyControls`.\n\t\t\tget controls() { return _historyControls; },\n\t\t\tset controls(value) {\n\t\t\t\tconst controls = Boolean(value);\n\n\t\t\t\tif (_historyMaxStates === 1 && controls) {\n\t\t\t\t\tthrow new Error('Config.history.controls must be false when Config.history.maxStates is 1');\n\t\t\t\t}\n\n\t\t\t\t_historyControls = controls;\n\t\t\t},\n\n\t\t\tget maxStates() { return _historyMaxStates; },\n\t\t\tset maxStates(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.history.maxStates must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_historyMaxStates = value;\n\n\t\t\t\t// Force `Config.history.controls` to `false`, when limited to `1` moment.\n\t\t\t\tif (_historyControls && value === 1) {\n\t\t\t\t\t_historyControls = false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// legacy\n\t\t\t// Die if deprecated state history settings are accessed.\n\t\t\tget mode()  { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tset mode(_) { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tget tracking()  { throw new Error(_errHistoryTrackingDeprecated); },\n\t\t\tset tracking(_) { throw new Error(_errHistoryTrackingDeprecated); }\n\t\t\t// /legacy\n\t\t}),\n\n\t\t/*\n\t\t\tMacros settings.\n\t\t*/\n\t\tmacros : Object.freeze({\n\t\t\tget ifAssignmentError() { return _macrosIfAssignmentError; },\n\t\t\tset ifAssignmentError(value) { _macrosIfAssignmentError = Boolean(value); },\n\n\t\t\tget maxLoopIterations() { return _macrosMaxLoopIterations; },\n\t\t\tset maxLoopIterations(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.macros.maxLoopIterations must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_macrosMaxLoopIterations = value;\n\t\t\t},\n\n\t\t\tget typeSkipKey() { return _macrosTypeSkipKey; },\n\t\t\tset typeSkipKey(value) { _macrosTypeSkipKey = String(value); },\n\n\t\t\tget typeVisitedPassages() { return _macrosTypeVisitedPassages; },\n\t\t\tset typeVisitedPassages(value) { _macrosTypeVisitedPassages = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tNavigation settings.\n\t\t*/\n\t\tnavigation : Object.freeze({\n\t\t\tget override() { return _navigationOverride; },\n\t\t\tset override(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.navigation.override must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_navigationOverride = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tPassages settings.\n\t\t*/\n\t\tpassages : Object.freeze({\n\t\t\tget descriptions() { return _passagesDescriptions; },\n\t\t\tset descriptions(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'Object' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.descriptions must be a boolean, object, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesDescriptions = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.updateTitle`.\n\t\t\tget displayTitles() { return _passagesDisplayTitles; },\n\t\t\tset displayTitles(value) { _passagesDisplayTitles = Boolean(value); },\n\n\t\t\tget nobr() { return _passagesNobr; },\n\t\t\tset nobr(value) { _passagesNobr = Boolean(value); },\n\n\t\t\tget onProcess() { return _passagesOnProcess; },\n\t\t\tset onProcess(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.onProcess must be a function or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesOnProcess = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.(start|startingPassage)`.\n\t\t\tget start() { return _passagesStart; },\n\t\t\tset start(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.start must be a string or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesStart = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.transitionOut`.\n\t\t\tget transitionOut() { return _passagesTransitionOut; },\n\t\t\tset transitionOut(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'string'\n\t\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.transitionOut must be a string, non-negative integer, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesTransitionOut = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tSaves settings.\n\t\t*/\n\t\tsaves : Object.freeze({\n\t\t\tget autoload() { return _savesAutoload; },\n\t\t\tset autoload(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'string' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autoload must be a boolean, string, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutoload = value;\n\t\t\t},\n\n\t\t\tget autosave() { return _savesAutosave; },\n\t\t\tset autosave(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\t// legacy\n\t\t\t\t\t// Convert a string value to an Array of string.\n\t\t\t\t\tif (valueType === 'string') {\n\t\t\t\t\t\t_savesAutosave = [value];\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// /legacy\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t\t&& (valueType !== 'Array' || !value.every(item => typeof item === 'string'))\n\t\t\t\t\t\t&& valueType !== 'function'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autosave must be a boolean, Array of strings, function, or null/undefined (received: ${valueType}${valueType === 'Array' ? ' of mixed' : ''})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutosave = value;\n\t\t\t},\n\n\t\t\tget id() { return _savesId; },\n\t\t\tset id(value) {\n\t\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\t\tthrow new TypeError(`Config.saves.id must be a non-empty string (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesId = value;\n\t\t\t},\n\n\t\t\tget isAllowed() { return _savesIsAllowed; },\n\t\t\tset isAllowed(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.isAllowed must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesIsAllowed = value;\n\t\t\t},\n\n\t\t\tget onLoad() { return _savesOnLoad; },\n\t\t\tset onLoad(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onLoad must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnLoad = value;\n\t\t\t},\n\n\t\t\tget onSave() { return _savesOnSave; },\n\t\t\tset onSave(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onSave must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnSave = value;\n\t\t\t},\n\n\t\t\tget slots() { return _savesSlots; },\n\t\t\tset slots(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new TypeError(`Config.saves.slots must be a non-negative integer (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesSlots = value;\n\t\t\t},\n\n\t\t\tget version() { return _savesVersion; },\n\t\t\tset version(value) { _savesVersion = value; }\n\t\t}),\n\n\t\t/*\n\t\t\tUI settings.\n\t\t*/\n\t\tui : Object.freeze({\n\t\t\tget stowBarInitially() { return _uiStowBarInitially; },\n\t\t\tset stowBarInitially(value) {\n\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\tif (\n\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError(`Config.ui.stowBarInitially must be a boolean or non-negative integer (received: ${valueType})`);\n\t\t\t\t}\n\n\t\t\t\t_uiStowBarInitially = value;\n\t\t\t},\n\n\t\t\tget updateStoryElements() { return _uiUpdateStoryElements; },\n\t\t\tset updateStoryElements(value) { _uiUpdateStoryElements = Boolean(value); }\n\t\t})\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tsimpleaudio.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser, Config, Has, LoadScreen, Story, Util, Visibility, clone */\n\nvar SimpleAudio = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tEvents that count as user activation—i.e. \"user gestures\", \"activation behavior\".\n\n\t\tNOTE (ca. Dec, 2018): This not an exhaustive list and varies significantly by browser.\n\t\tProposals for a specification/standard are still very much in flux at this point.\n\n\t\tTODO (ca. Dec, 2018): Revisit this topic.\n\n\t\tSEE: (too many to list)\n\t\t\thttps://github.com/whatwg/html/issues/3849\n\t\t\thttps://github.com/whatwg/html/issues/1903\n\t\t\thttps://html.spec.whatwg.org/#activation\n\t\t\thttps://docs.google.com/spreadsheets/d/1DGXjhQ6D3yZXIePOMo0dsd2agz0t5W7rYH1NwJ-QGJo/edit#gid=0\n\t*/\n\tconst _gestureEventNames = Object.freeze(['click', 'contextmenu', 'dblclick', 'keyup', 'mouseup', 'pointerup', 'touchend']);\n\n\t// Special group IDs.\n\tconst _specialIds = Object.freeze([':not', ':all', ':looped', ':muted', ':paused', ':playing']);\n\n\t// Format specifier regular expression.\n\tconst _formatSpecRe = /^([\\w-]+)\\s*\\|\\s*(\\S.*)$/; // e.g. 'mp3|https://audiohost.tld/id'\n\n\t// ID verification regular expressions.\n\tconst _badIdRe = /[:\\s]/;\n\n\t// Tracks collection.\n\tconst _tracks = new Map();\n\n\t// Groups collection.\n\tconst _groups = new Map();\n\n\t// Playlists collection.\n\tconst _lists = new Map();\n\n\t// Subscriber collection.\n\tconst _subscribers = new Map();\n\n\t// Master playback rate.\n\tlet _masterRate = 1;\n\n\t// Master playback volume.\n\tlet _masterVolume = 1;\n\n\t// Master mute state.\n\tlet _masterMute = false;\n\n\t// Master mute on tab/window visibility state.\n\tlet _masterMuteOnHidden = false;\n\n\n\t/*******************************************************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************************************************/\n\t// Return whether the `<HTMLAudioElement>.play()` method returns a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _playReturnsPromise = (function () {\n\t\t// Cache of whether `<HTMLAudioElement>.play()` returns a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _playReturnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (Has.audio) {\n\t\t\t\ttry {\n\t\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t\t// NOTE (ca. Jan 01, 2020): Firefox will still log an \"Autoplay is only allowed\n\t\t\t\t\t// when […] media is muted.\" message to the console when attempting the test\n\t\t\t\t\t// below, even though the audio has been muted.  Stay classy, Firefox.\n\t\t\t\t\t//\n\t\t\t\t\t// QUESTION (ca. Jan 01, 2020): Keep this?  It's only here to appease Firefox,\n\t\t\t\t\t// but doesn't seem to work as Firefox seems to ignore mute in violation of the\n\t\t\t\t\t// `HTMLAudioElement` specification—willfully or simply a bug, I can't say.\n\t\t\t\t\taudio.muted = true;\n\n\t\t\t\t\tconst value = audio.play();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since there's no source, and we don't actually\n\t\t\t\t\t// care about the error, since we just want the return value, so we consign it\n\t\t\t\t\t// to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _playReturnsPromise;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tAudioTrack Class.\n\t*******************************************************************************************************************/\n\tclass AudioTrack {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of sources or AudioTrack object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioTrack) {\n\t\t\t\tthis._copy(obj);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('sources parameter must be either an array, of URIs or source objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(sourceList) {\n\t\t\tconst dataUriRe   = /^data:\\s*audio\\/([^;,]+)\\s*[;,]/i;\n\t\t\tconst extRe       = /\\.([^./\\\\]+)$/;\n\t\t\tconst getType     = AudioTrack.getType;\n\t\t\tconst usedSources = [];\n\t\t\t/*\n\t\t\t\tHTMLAudioElement: DOM factory method vs. constructor\n\n\t\t\t\tUse of the DOM factory method, `document.createElement('audio')`, should be\n\t\t\t\tpreferred over use of the constructor, `new Audio()`.  The reason being that\n\t\t\t\tobjects created by the latter are, erroneously, treated differently, often\n\t\t\t\tunfavorably, by certain browser engines—e.g. within some versions of the iOS\n\t\t\t\tbrowser core.\n\n\t\t\t\tNotably, the only difference between the two, per the specification, is that\n\t\t\t\tobjects created via the constructor should have their `preload` property\n\t\t\t\tautomatically set to 'auto'.  Thus, there's no technical reason to prefer\n\t\t\t\tusage of the constructor, even discounting buggy browser implementations.\n\t\t\t*/\n\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t// Initially set the `preload` attribute to `'none'`.\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Process the array of sources, adding any valid sources to the `usedSources`\n\t\t\t// array and to the audio element as source elements.\n\t\t\tsourceList.forEach(src => {\n\t\t\t\tlet srcObj = null;\n\n\t\t\t\tswitch (typeof src) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t{\n\t\t\t\t\t\tlet match;\n\n\t\t\t\t\t\tif (src.slice(0, 5) === 'data:') {\n\t\t\t\t\t\t\tmatch = dataUriRe.exec(src);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source data URI missing media type');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tmatch = extRe.exec(Util.parseUrl(src).pathname);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source URL missing file extension');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(match[1]);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\t{\n\t\t\t\t\t\tif (src === null) {\n\t\t\t\t\t\t\tthrow new Error('source object cannot be null');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('src')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"src\" property');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('format')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"format\" property');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(src.format);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src : src.src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`invalid source value (type: ${typeof src})`);\n\t\t\t\t}\n\n\t\t\t\tif (srcObj !== null) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tOpera (Blink; ca. Jul 2017) fails to play audio from some sources\n\t\t\t\t\t\twith MIME-types containing a `codecs` parameter, despite the fact\n\t\t\t\t\t\tthat `canPlayType()` blessed the full MIME-type including `codecs`.\n\n\t\t\t\t\t\tBizarrely, this only affects some MIME-types—e.g. MP3s are affected,\n\t\t\t\t\t\twhile WAVEs are not.\n\t\t\t\t\t\t\tFails: 'audio/mpeg; codecs=\"mp3\"'\n\t\t\t\t\t\t\tPlays: 'audio/mpeg'\n\t\t\t\t\t\t\tPlays: 'audio/wav; codecs=\"1\"'\n\n\t\t\t\t\t\tTo workaround this we remove all parameters from the MIME-type in\n\t\t\t\t\t\tOpera.\n\t\t\t\t\t*/\n\t\t\t\t\tif (Browser.isOpera) {\n\t\t\t\t\t\tsrcObj.type =  srcObj.type.replace(/;.*$/, '');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\taudio.appendChild(source);\n\t\t\t\t\tusedSources.push(srcObj);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (audio.hasChildNodes()) {\n\t\t\t\t// Set the `preload` attribute to `'metadata'`, unless preloading has been disabled.\n\t\t\t\tif (Config.audio.preloadMetadata) {\n\t\t\t\t\taudio.preload = 'metadata';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._finalize(audio, usedSources, clone(sourceList));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(\n\t\t\t\tobj.audio.cloneNode(true), // deep clone of the audio element & its children\n\t\t\t\tclone(obj.sources),\n\t\t\t\tclone(obj.originals)\n\t\t\t);\n\t\t}\n\n\t\t_finalize(audio, sources, originals) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\taudio : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : audio\n\t\t\t\t},\n\n\t\t\t\tsources : {\n\t\t\t\t\tvalue : Object.freeze(sources)\n\t\t\t\t},\n\n\t\t\t\toriginals : {\n\t\t\t\t\tvalue : Object.freeze(originals)\n\t\t\t\t},\n\n\t\t\t\t_error : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_faderId : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up event handlers on the audio and source elements.\n\t\t\tjQuery(this.audio)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving a `loadstart` event on the audio element, set `_error` to\n\t\t\t\t\t`false`.\n\t\t\t\t*/\n\t\t\t\t.on('loadstart.AudioTrack', () => this._error = false)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the audio element, set `_error` to\n\t\t\t\t\t`true`.\n\n\t\t\t\t\tCaveats by browser:\n\t\t\t\t\t\tEdge violates the specification by triggering `error` events from source\n\t\t\t\t\t\telements on their parent media element, rather than the source element.\n\t\t\t\t\t\tTo enable error handling in all browsers, we set the error handler on the\n\t\t\t\t\t\taudio element and have the final source element forward its `error` event.\n\n\t\t\t\t\t\tIE does not trigger, at least some, `error` events from source elements at\n\t\t\t\t\t\tall, not on the source element or its parent media element.  AFAIK, nothing\n\t\t\t\t\t\tcan be done about this lossage.\n\t\t\t\t*/\n\t\t\t\t.on('error.AudioTrack', () => this._error = true)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the final source element (if any), trigger\n\t\t\t\t\tan `error` event on the audio element—that being necessary because the source\n\t\t\t\t\t`error` event does not bubble.\n\t\t\t\t*/\n\t\t\t\t.find('source:last-of-type')\n\t\t\t\t.on('error.AudioTrack', () => this._trigger('error'));\n\n\t\t\t// Subscribe to command messages.\n\t\t\tsubscribe(this, mesg => {\n\t\t\t\tif (!this.audio) {\n\t\t\t\t\tunsubscribe(this);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tswitch (mesg) {\n\t\t\t\tcase 'loadwithscreen':\n\t\t\t\t\tif (this.hasSource()) {\n\t\t\t\t\t\tconst lockId = LoadScreen.lock();\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t// NOTE: Do not use an arrow function here.\n\t\t\t\t\t\t\t.one(\n\t\t\t\t\t\t\t\t'canplaythrough.AudioTrack_loadwithscreen error.AudioTrack_loadwithscreen',\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\tjQuery(this).off('.AudioTrack_loadwithscreen');\n\t\t\t\t\t\t\t\t\tLoadScreen.unlock(lockId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.load();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'load':   this.load();               break;\n\t\t\t\tcase 'mute':   this._updateAudioMute();   break;\n\t\t\t\tcase 'rate':   this._updateAudioRate();   break;\n\t\t\t\tcase 'stop':   this.stop();               break;\n\t\t\t\tcase 'volume': this._updateAudioVolume(); break;\n\t\t\t\tcase 'unload': this.unload();             break;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Synchronize with the current master audio settings.\n\t\t\tthis._updateAudioMute();\n\t\t\tthis._updateAudioRate();\n\t\t\tthis._updateAudioVolume();\n\t\t}\n\n\t\t_trigger(eventName) {\n\t\t\t// Do not use `trigger()` here as we do not want these events to bubble.\n\t\t\tjQuery(this.audio).triggerHandler(eventName);\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.  That said, since the audio element contains\n\t\t\t\tdata buffers for the selected audio source, which may be quite large, manually\n\t\t\t\tpurging them as soon as we know that they're no longer needed is not a bad idea.\n\t\t\t*/\n\t\t\tunsubscribe(this);\n\n\t\t\tif (!this.audio) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(this.audio).off();\n\t\t\tthis.unload();\n\t\t\tthis._error = true;\n\n\t\t\t// Delete the audio element property.\n\t\t\tdelete this.audio;\n\t\t}\n\n\t\tclone() {\n\t\t\treturn new AudioTrack(this);\n\t\t}\n\n\t\tload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.audio.pause();\n\n\t\t\tif (!this.audio.hasChildNodes()) {\n\t\t\t\tif (this.sources.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.sources.forEach(srcObj => {\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\tthis.audio.appendChild(source);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tif (!this.isLoading()) {\n\t\t\t\tthis.audio.load();\n\t\t\t}\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.stop();\n\n\t\t\tconst audio = this.audio;\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Remove all source elements.\n\t\t\twhile (audio.hasChildNodes()) {\n\t\t\t\taudio.removeChild(audio.firstChild);\n\t\t\t}\n\n\t\t\t// Force the audio element to drop any existing data buffers.\n\t\t\taudio.load();\n\t\t}\n\n\t\tplay() {\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tconst namespace = '.AudioTrack_play';\n\n\t\t\treturn _playReturnsPromise()\n\t\t\t\t? this.audio.play()\n\t\t\t\t: new Promise((resolve, reject) => {\n\t\t\t\t\tif (this.isPlaying()) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t\t\t.off(namespace)\n\t\t\t\t\t\t\t.one(`error${namespace} playing${namespace} timeupdate${namespace}`, ev => {\n\t\t\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\t\t\tif (ev.type === 'error') {\n\t\t\t\t\t\t\t\t\treject(new Error('unknown audio play error'));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.audio.play();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioTrack_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioTrack_playWhenAllowed');\n\t\t\t\t\tthis.audio.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tthis.audio.pause();\n\t\t}\n\n\t\tstop() {\n\t\t\tthis.audio.pause();\n\t\t\tthis.time(0);\n\t\t\tthis._trigger(':stopped');\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tthis.fadeStop();\n\n\t\t\tconst from = Math.clamp(fromVol == null ? this.volume() : fromVol, 0, 1); // lazy equality for null\n\t\t\tconst to   = Math.clamp(toVol, 0, 1);\n\n\t\t\tif (from === to) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.volume(from);\n\n\t\t\t/*\n\t\t\t\tWe listen for the `timeupdate` event here, rather than `playing`, because\n\t\t\t\tvarious browsers (notably, mobile browsers) are poor at firing media events\n\t\t\t\tin a timely fashion, so we use `timeupdate` to ensure that we don't start\n\t\t\t\tthe fade until the track is actually progressing.\n\t\t\t*/\n\t\t\tjQuery(this.audio)\n\t\t\t\t.off('timeupdate.AudioTrack_fade')\n\t\t\t\t.one('timeupdate.AudioTrack_fade', () => {\n\t\t\t\t\tlet min;\n\t\t\t\t\tlet max;\n\n\t\t\t\t\t// Fade in.\n\t\t\t\t\tif (from < to) {\n\t\t\t\t\t\tmin = from;\n\t\t\t\t\t\tmax = to;\n\t\t\t\t\t}\n\t\t\t\t\t// Fade out.\n\t\t\t\t\telse {\n\t\t\t\t\t\tmin = to;\n\t\t\t\t\t\tmax = from;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst time     = Math.max(duration, 1);\n\t\t\t\t\tconst interval = 25; // in milliseconds\n\t\t\t\t\tconst delta    = (to - from) / (time / (interval / 1000));\n\n\t\t\t\t\tthis._trigger(':fading');\n\t\t\t\t\tthis._faderId = setInterval(() => {\n\t\t\t\t\t\tif (!this.isPlaying()) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWhile it may seem like a good idea to also set the track volume\n\t\t\t\t\t\t\t\tto the `to` value here, we should not do so.  We cannot know why\n\t\t\t\t\t\t\t\tthe track is no longer playing, nor if the volume has been modified\n\t\t\t\t\t\t\t\tin the interim, so doing so now may clobber an end-user set volume.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.volume(Math.clamp(this.volume() + delta, min, max));\n\n\t\t\t\t\t\tif (Config.audio.pauseOnFadeToZero && this.volume() === 0) {\n\t\t\t\t\t\t\tthis.pause();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.volume() === to) {\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\tthis._trigger(':faded');\n\t\t\t\t\t\t}\n\t\t\t\t\t}, interval);\n\t\t\t\t});\n\n\t\t\treturn this.play();\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this._faderId !== null) {\n\t\t\t\tclearInterval(this._faderId);\n\t\t\t\tthis._faderId = null;\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this.audio.loop;\n\t\t\t}\n\n\t\t\tthis.audio.loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\t\t\tthis._updateAudioMute();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioMute() {\n\t\t\tthis.audio.muted = this._mute || _masterMute;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tClamp the playback rate to sane values—some browsers also do this to varying degrees.\n\n\t\t\t\tNOTE (ca. Aug 2016): The specification allows negative values for reverse playback,\n\t\t\t\thowever, most browsers either completely ignore negative values or clamp them to\n\t\t\t\tsome positive value.  In some (notably, IE & Edge), setting a negative playback\n\t\t\t\trate breaks the associated controls, if displayed.\n\t\t\t*/\n\t\t\t/*\n\t\t\tthis._rate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\t\tthis._updateAudioRate();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioRate() {\n\t\t\t/*\n\t\t\tconst rate = this._rate * _masterRate;\n\t\t\tthis.audio.playbackRate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis.audio.playbackRate = Math.clamp(this._rate * _masterRate, 0.2, 5); // clamp to 5× slower & faster\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\treturn this.audio.currentTime;\n\t\t\t}\n\n\t\t\tif (typeof time !== 'number') {\n\t\t\t\tthrow new TypeError('time parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE (historic): If we try to modify the audio clip's `.currentTime` property\n\t\t\t\tbefore its metadata has been loaded, it will throw an `InvalidStateError`\n\t\t\t\t(since it doesn't know its duration, allowing `.currentTime` to be set would\n\t\t\t\tbe undefined behavior), so in case an exception is thrown we provide a fallback\n\t\t\t\tusing the `loadedmetadata` event.\n\n\t\t\t\tNOTE (ca. 2016): This workaround should no longer be necessary in most browsers.\n\t\t\t\tThat said, it will still be required for some time to service legacy browsers.\n\n\t\t\t\tNOTE (ca. Dec 09, 2018): Firefox will still log an `InvalidStateError` to the\n\t\t\t\tconsole when attempting to modify the clip's `.currentTime` property before its\n\t\t\t\tmetadata has been loaded, even though it handles the situation properly—by waiting\n\t\t\t\tfor the metadata, as all browsers do now.  To prevent this spurious logging, we\n\t\t\t\tmust now manually check for the existence of the metadata and always failover to\n\t\t\t\tan event regardless of if the browser needs it or not—because I don't want to\n\t\t\t\tintroduce a browser check here.  Stay classy, Firefox.\n\t\t\t*/\n\t\t\tif (this.hasMetadata()) {\n\t\t\t\tthis.audio.currentTime = time;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t.off('loadedmetadata.AudioTrack_time')\n\t\t\t\t\t.one('loadedmetadata.AudioTrack_time', () => this.audio.currentTime = time);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\t\tthis._updateAudioVolume();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioVolume() {\n\t\t\tthis.audio.volume = Math.clamp(this._volume * _masterVolume, 0, 1);\n\t\t}\n\n\t\tduration() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration;\n\t\t}\n\n\t\tremaining() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration - this.audio.currentTime;\n\t\t}\n\n\t\tisFailed() {\n\t\t\treturn this._error;\n\t\t}\n\n\t\tisLoading() {\n\t\t\treturn this.audio.networkState === HTMLMediaElement.NETWORK_LOADING;\n\t\t}\n\n\t\tisUnloaded() {\n\t\t\treturn !this.audio.hasChildNodes();\n\t\t}\n\n\t\tisUnavailable() {\n\t\t\treturn !this.hasSource() || this.isUnloaded() || this.isFailed();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\t// NOTE: The `this.hasSomeData()` check is probably no longer necessary.\n\t\t\treturn !this.audio.paused && this.hasSomeData();\n\t\t}\n\n\t\tisPaused() {\n\t\t\t/*\n\t\t\t\tIf the selected audio resource is a stream, `currentTime` may return a non-zero\n\t\t\t\tvalue even at the earliest available position within the stream as the browser\n\t\t\t\tmay have dropped the earliest chunks of buffered data or the stream may have a\n\t\t\t\ttimeline which does not start at zero.\n\n\t\t\t\tIn an attempt to guard against these possiblities, as best as we can, we test\n\t\t\t\t`duration` against `Infinity` first, which should yield true for actual streams.\n\t\t\t*/\n\t\t\treturn this.audio.paused\n\t\t\t\t&& (this.audio.duration === Infinity || this.audio.currentTime > 0)\n\t\t\t\t&& !this.audio.ended;\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.audio.paused && this.audio.currentTime === 0;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.audio.ended;\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this._faderId !== null;\n\t\t}\n\n\t\tisSeeking() {\n\t\t\treturn this.audio.seeking;\n\t\t}\n\n\t\thasSource() {\n\t\t\treturn this.sources.length > 0;\n\t\t}\n\n\t\thasNoData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_NOTHING;\n\t\t}\n\n\t\thasMetadata() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_METADATA;\n\t\t}\n\n\t\thasSomeData() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA;\n\t\t}\n\n\t\thasData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tjQuery.fn.on.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tjQuery.fn.one.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tjQuery.fn.off.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\t/*\n\t\t\tVerifies that the browser supports the given MIME-type and then retuns either\n\t\t\tthe MIME-type, if it is supported, or `null`, if it is not.\n\t\t*/\n\t\tstatic _verifyType(type) {\n\t\t\tif (!type || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cache = AudioTrack._types;\n\n\t\t\tif (!cache.hasOwnProperty(type)) {\n\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t// Some early implementations return 'no' instead of the empty string.\n\t\t\t\tcache[type] = audio.canPlayType(type).replace(/^no$/i, '') !== '';\n\t\t\t}\n\n\t\t\treturn cache[type] ? type : null;\n\t\t}\n\n\t\t/*\n\t\t\tRetuns the MIME-type associated with the given format-ID, if it is supported,\n\t\t\telsewise `null`.\n\t\t*/\n\t\tstatic getType(format) {\n\t\t\tif (!format || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst known = AudioTrack.formats;\n\t\t\tconst id    = format.toLowerCase();\n\t\t\tconst type  = known.hasOwnProperty(id) ? known[id] : `audio/${id}`;\n\n\t\t\treturn AudioTrack._verifyType(type);\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a format.\n\t\t*/\n\t\tstatic canPlayFormat(format) {\n\t\t\treturn AudioTrack.getType(format) !== null;\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a MIME-type.\n\t\t*/\n\t\tstatic canPlayType(type) {\n\t\t\treturn AudioTrack._verifyType(type) !== null;\n\t\t}\n\t}\n\n\t// Attach the static data members.\n\tObject.defineProperties(AudioTrack, {\n\t\t/*\n\t\t\tFormat-ID to MIME-type mappings for common audio types.\n\n\t\t\tIn most cases, the codecs property should not be included with the MIME-type,\n\t\t\tas we have no way of knowing which codec was used—and the browser will figure\n\t\t\tit out.  Conversely, in cases where the relationship relationship between a\n\t\t\tformat-ID and a specific codec is strong, we should include the codecs property.\n\n\t\t\tNOTE: Caveats by browser/engine:\n\t\t\t\tOpera ≤12 (Presto) will return a false-negative if the codecs value is quoted\n\t\t\t\twith single quotes, requiring the use of either double quotes or no quotes.\n\n\t\t\t\tBlink-based browsers (e.g. Chrome, Opera ≥15) will return a false-negative\n\t\t\t\tfor WAVE audio if the preferred MIME-type of 'audio/wave' is specified,\n\t\t\t\trequiring the use of 'audio/wav' instead.\n\t\t*/\n\t\tformats : {\n\t\t\tvalue : { // Leave this object extensible for users.\n\t\t\t\t// AAC — MPEG-2 AAC audio; specific profiles vary, but commonly \"AAC-LC\".\n\t\t\t\taac : 'audio/aac',\n\n\t\t\t\t// CAF — Codecs vary.\n\t\t\t\tcaf     : 'audio/x-caf',\n\t\t\t\t'x-caf' : 'audio/x-caf',\n\n\t\t\t\t// MP3 — MPEG-1/-2 Layer-III audio.\n\t\t\t\tmp3  : 'audio/mpeg; codecs=\"mp3\"',\n\t\t\t\tmpeg : 'audio/mpeg; codecs=\"mp3\"',\n\n\t\t\t\t// MP4 — Codecs vary, but commonly \"mp4a.40.2\" (a.k.a. \"AAC-LC\").\n\t\t\t\tm4a     : 'audio/mp4',\n\t\t\t\tmp4     : 'audio/mp4',\n\t\t\t\t'x-m4a' : 'audio/mp4',\n\t\t\t\t'x-mp4' : 'audio/mp4',\n\n\t\t\t\t// OGG — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\toga : 'audio/ogg',\n\t\t\t\togg : 'audio/ogg',\n\n\t\t\t\t// OPUS — Opus audio in an Ogg container.\n\t\t\t\topus : 'audio/ogg; codecs=\"opus\"',\n\n\t\t\t\t// WAVE — Codecs vary, but commonly \"1\" (1 is the FourCC for PCM/LPCM).\n\t\t\t\twav  : 'audio/wav',\n\t\t\t\twave : 'audio/wav',\n\n\t\t\t\t// WEBM — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\tweba : 'audio/webm',\n\t\t\t\twebm : 'audio/webm'\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tCache of supported MIME-types.\n\t\t*/\n\t\t_types : {\n\t\t\tvalue : {}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudioList Class.\n\t*******************************************************************************************************************/\n\tclass AudioList {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of track objects or AudioList object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioList) {\n\t\t\t\tthis._copy(obj);\n\t\t\t\t// this._create(obj.tracks);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('tracks parameter must be either an array, of track objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(trackList) {\n\t\t\t// Map the array of tracks to playlist track objects.\n\t\t\tthis._finalize(trackList.map(trackObj => {\n\t\t\t\tif (typeof trackObj !== 'object') { // lazy equality for null\n\t\t\t\t\tthrow new Error('tracks parameter array members must be objects');\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\tlet rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (trackObj instanceof AudioTrack) {\n\t\t\t\t\town    = true;\n\t\t\t\t\trate   = trackObj.rate();\n\t\t\t\t\ttrack  = trackObj.clone();\n\t\t\t\t\tvolume = trackObj.volume();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (!trackObj.hasOwnProperty('track')) {\n\t\t\t\t\t\tthrow new Error('track object missing required \"track\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (!(trackObj.track instanceof AudioTrack)) {\n\t\t\t\t\t\tthrow new Error('track object\\'s \"track\" property must be an AudioTrack object');\n\t\t\t\t\t}\n\t\t\t\t\t// else if (!trackObj.hasOwnProperty('volume')) {\n\t\t\t\t\t// \tthrow new Error('track object missing required \"volume\" property');\n\t\t\t\t\t// }\n\n\t\t\t\t\town    = trackObj.hasOwnProperty('own') && trackObj.own;\n\t\t\t\t\trate   = trackObj.hasOwnProperty('rate') ? trackObj.rate : trackObj.track.rate();\n\t\t\t\t\ttrack  = trackObj.track;\n\t\t\t\t\tvolume = trackObj.hasOwnProperty('volume') ? trackObj.volume : trackObj.track.volume();\n\t\t\t\t}\n\n\t\t\t\ttrack.stop();\n\t\t\t\ttrack.loop(false);\n\t\t\t\ttrack.mute(false);\n\t\t\t\ttrack.rate(rate);\n\t\t\t\ttrack.volume(volume);\n\t\t\t\ttrack.on('ended.AudioList', () => this._onEnd());\n\n\t\t\t\treturn { own, track, volume, rate };\n\t\t\t}));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(clone(obj.tracks));\n\t\t}\n\n\t\t_finalize(tracks) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttracks : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : Object.freeze(tracks)\n\t\t\t\t},\n\n\t\t\t\tqueue : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : []\n\t\t\t\t},\n\n\t\t\t\tcurrent : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_loop : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_shuffle : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.\n\t\t\t*/\n\t\t\t// Stop playback.\n\t\t\tthis.stop();\n\n\t\t\t// Destroy all owned tracks.\n\t\t\tthis.tracks\n\t\t\t\t.filter(trackObj => trackObj.own)\n\t\t\t\t.forEach(trackObj => trackObj.track._destroy());\n\n\t\t\t// Delete the reference-type properties.\n\t\t\tdelete this.tracks;\n\t\t\tdelete this.queue;\n\t\t}\n\n\t\tload() {\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.load());\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.stop();\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.unload());\n\t\t}\n\n\t\tplay() {\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (this.queue.length === 0) {\n\t\t\t\t\tthis._fillQueue();\n\t\t\t\t}\n\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn Promise.reject(new Error('no tracks were available'));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.current.track.play();\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioList_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioList_playWhenAllowed');\n\t\t\t\t\tthis.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.pause();\n\t\t\t}\n\t\t}\n\n\t\tstop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tthis._drainQueue();\n\t\t}\n\n\t\tskip() {\n\t\t\tif (this._next()) {\n\t\t\t\tthis.current.track.play();\n\t\t\t}\n\t\t\telse if (this._loop) {\n\t\t\t\tthis.play();\n\t\t\t}\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adjToVol = Math.clamp(toVol, 0, 1) * this.current.volume;\n\t\t\tlet adjFromVol;\n\n\t\t\tif (fromVol != null) { // lazy equality for null\n\t\t\t\tadjFromVol = Math.clamp(fromVol, 0, 1) * this.current.volume;\n\t\t\t}\n\n\t\t\tthis._volume = toVol; // NOTE: Kludgey, but necessary.\n\n\t\t\treturn this.current.track.fade(duration, adjToVol, adjFromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.fadeStop();\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this._loop;\n\t\t\t}\n\n\t\t\tthis._loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.mute(this._mute);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tshuffle(shuffle) {\n\t\t\tif (shuffle == null) { // lazy equality for null\n\t\t\t\treturn this._shuffle;\n\t\t\t}\n\n\t\t\tthis._shuffle = !!shuffle;\n\n\t\t\tif (this.queue.length > 0) {\n\t\t\t\tthis._fillQueue();\n\n\t\t\t\t// Try not to immediately replay the last track when not shuffling.\n\t\t\t\tif (!this._shuffle && this.current !== null && this.queue.length > 1) {\n\t\t\t\t\tconst firstIdx = this.queue.findIndex(trackObj => trackObj === this.current);\n\n\t\t\t\t\tif (firstIdx !== -1) {\n\t\t\t\t\t\tthis.queue.push(...this.queue.splice(0, firstIdx + 1));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tduration() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('duration takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.tracks\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\t\t}\n\n\t\tremaining() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('remaining takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\tlet remainingTime = this.queue\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tremainingTime += this.current.track.remaining();\n\t\t\t}\n\n\t\t\treturn remainingTime;\n\t\t}\n\n\t\ttime() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('time takes no parameters');\n\t\t\t}\n\n\t\t\treturn this.duration() - this.remaining();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\treturn this.current !== null && this.current.track.isPlaying();\n\t\t}\n\n\t\tisPaused() {\n\t\t\treturn this.current === null || this.current.track.isPaused();\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.queue.length === 0 && this.current === null;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.queue.length === 0 && (this.current === null || this.current.track.isEnded());\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this.current !== null && this.current.track.isFading();\n\t\t}\n\n\t\t_next() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tlet nextTrack;\n\n\t\t\twhile ((nextTrack = this.queue.shift())) {\n\t\t\t\tif (!nextTrack.track.isUnavailable()) {\n\t\t\t\t\tthis.current = nextTrack;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.current === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.current.track.mute(this._mute);\n\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\n\t\t\t// Attempt to protect against the `loop` state being reenabled\n\t\t\t// outside of the playlist.  Mostly for unowned tracks.\n\t\t\t//\n\t\t\t// TODO: Should we reapply the `ended` event handler too?\n\t\t\tthis.current.track.loop(false);\n\n\t\t\treturn true;\n\t\t}\n\n\t\t_onEnd() {\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tif (!this._loop) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (!this._next()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.current.track.play();\n\t\t}\n\n\t\t_drainQueue() {\n\t\t\tthis.queue.splice(0);\n\t\t}\n\n\t\t_fillQueue() {\n\t\t\tthis._drainQueue();\n\t\t\tthis.queue.push(...this.tracks.filter(trackObj => !trackObj.track.isUnavailable()));\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this._shuffle) {\n\t\t\t\tthis.queue.shuffle();\n\n\t\t\t\t// Try not to immediately replay the last track when shuffling.\n\t\t\t\tif (this.queue.length > 1 && this.queue[0] === this.current) {\n\t\t\t\t\tthis.queue.push(this.queue.shift());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAudioRunner Class.\n\t*******************************************************************************************************************/\n\tclass AudioRunner {\n\t\tconstructor(list) {\n\t\t\tif (!(list instanceof Set || list instanceof AudioRunner)) {\n\t\t\t\tthrow new TypeError('list parameter must be a Set or a AudioRunner instance');\n\t\t\t}\n\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttrackIds : {\n\t\t\t\t\tvalue : new Set(list instanceof AudioRunner ? list.trackIds : list)\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.load);\n\t\t}\n\n\t\tunload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.unload);\n\t\t}\n\n\t\tplay() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.play);\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.playWhenAllowed);\n\t\t}\n\n\t\tpause() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.pause);\n\t\t}\n\n\t\tstop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.stop);\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (duration == null || toVol == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fade requires parameters');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fade, duration, toVol, fromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeIn requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeIn, duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeOut requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeOut, duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeStop);\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\tthrow new Error('loop requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.loop, loop);\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\tthrow new Error('mute requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.mute, mute);\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\tthrow new Error('rate requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.rate, rate);\n\t\t\treturn this;\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\tthrow new Error('time requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.time, time);\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\tthrow new Error('volume requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.volume, volume);\n\t\t\treturn this;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.on, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.one, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.off, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tstatic _run(ids, fn, ...args) {\n\t\t\tids.forEach(id => {\n\t\t\t\tconst track = _tracks.get(id);\n\n\t\t\t\tif (track) {\n\t\t\t\t\tfn.apply(track, args);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTrack Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.tracks.add(trackId, sources…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.tracks.add(\n\t\t\t\t'over_the_top',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.mp3',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.ogg'\n\t\t\t);\n\t*/\n\tfunction trackAdd(/* trackId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('track ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('sources'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `track ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\tthrow new Error(`invalid ${what}: track IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst sources = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet track;\n\n\t\ttry {\n\t\t\ttrack = _newTrack(sources);\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during track initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If in Test Mode and no supported sources were specified, throw an error.\n\t\tif (Config.debug && !track.hasSource()) {\n\t\t\tthrow new Error(`${what}: no supported audio sources found`);\n\t\t}\n\n\t\t// If a track by the given ID already exists, destroy it.\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// Add the track to the cache.\n\t\t_tracks.set(id, track);\n\t}\n\n\tfunction trackDelete(id) {\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// TODO: Should this also remove references to the track from groups and playlists?\n\n\t\treturn _tracks.delete(id);\n\t}\n\n\tfunction trackClear() {\n\t\t_tracks.forEach(track => track._destroy());\n\t\t_tracks.clear();\n\t}\n\n\tfunction trackHas(id) {\n\t\treturn _tracks.has(id);\n\t}\n\n\tfunction trackGet(id) {\n\t\treturn _tracks.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tGroup Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.groups.add(groupId, trackIds…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.groups.add(':ui', 'beep', 'boop', 'boing');\n\t*/\n\tfunction groupAdd(/* groupId , trackIds… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('group ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `group ID \"${id}\"`;\n\n\t\tif (id[0] !== ':' || _badIdRe.test(id.slice(1))) {\n\t\t\tthrow new Error(`invalid ${what}: group IDs must start with a colon and must not contain colons or whitespace`);\n\t\t}\n\n\t\tif (_specialIds.includes(id)) {\n\t\t\tthrow new Error(`cannot clobber special ${what}`);\n\t\t}\n\n\t\tconst trackIds = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet group;\n\n\t\ttry {\n\t\t\tgroup = new Set(trackIds.map(trackId => {\n\t\t\t\tif (!_tracks.has(trackId)) {\n\t\t\t\t\tthrow new Error(`track \"${trackId}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\treturn trackId;\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during group initialization: ${ex.message}`);\n\t\t}\n\n\t\t// Add the group to the cache.\n\t\t_groups.set(id, Object.freeze(Array.from(group)));\n\t}\n\n\tfunction groupDelete(id) {\n\t\treturn _groups.delete(id);\n\t}\n\n\tfunction groupClear() {\n\t\t_groups.clear();\n\t}\n\n\tfunction groupHas(id) {\n\t\treturn _groups.has(id);\n\t}\n\n\tfunction groupGet(id) {\n\t\treturn _groups.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPlaylist Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.lists.add(listId, sources…);\n\t\t\tWhere `sources` may be either a track ID or descriptor (object).\n\t\t\tTrack descriptors are either { id, [own], [rate], [volume] } or { sources, [rate], [volume] }.\n\n\t\tNOTE: Rate properties are currently unsupported due to poor browser support.\n\n\t\tE.g.\n\t\t\tSimpleAudio.lists.add(\n\t\t\t\t'bgm',\n\t\t\t\t'over_the_top',\n\t\t\t\t{\n\t\t\t\t\tid     : 'heavens_a_lie',\n\t\t\t\t\tvolume : 0.5,\n\t\t\t\t\town    : true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tsources : [\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.mp3',\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.ogg'\n\t\t\t\t\t],\n\t\t\t\t\tvolume  : 0.75\n\t\t\t\t}\n\t\t\t);\n\t*/\n\tfunction listAdd(/* listId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('list ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `list ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\treturn this.error(`invalid ${what}: list IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst descriptors = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet list;\n\n\t\ttry {\n\t\t\tlist = new AudioList(descriptors.map(desc => {\n\t\t\t\tif (desc === null) {\n\t\t\t\t\tthrow new Error('track descriptor must be a string or object (type: null)');\n\t\t\t\t}\n\n\t\t\t\tswitch (typeof desc) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t// Simply a track ID, so convert it into an object.\n\t\t\t\t\tdesc = { id : desc }; // eslint-disable-line no-param-reassign\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (!desc.hasOwnProperty('id') && !desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain one of either an \"id\" or a \"sources\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (desc.hasOwnProperty('id') && desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain either an \"id\" or a \"sources\" property, not both');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`track descriptor must be a string or object (type: ${typeof desc})`);\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\t// let rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (desc.hasOwnProperty('id')) {\n\t\t\t\t\tif (typeof desc.id !== 'string') {\n\t\t\t\t\t\tthrow new Error('\"id\" property must be a string');\n\t\t\t\t\t}\n\t\t\t\t\tif (!_tracks.has(desc.id)) {\n\t\t\t\t\t\tthrow new Error(`track \"${desc.id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\ttrack = _tracks.get(desc.id);\n\t\t\t\t}\n\t\t\t\telse if (desc.hasOwnProperty('sources')) {\n\t\t\t\t\tif (!Array.isArray(desc.sources) || desc.sources.length === 0) {\n\t\t\t\t\t\tthrow new Error('\"sources\" property must be a non-empty array');\n\t\t\t\t\t}\n\t\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\t\tthrow new Error('\"own\" property is not allowed with the \"sources\" property');\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttrack = _newTrack(desc.sources);\n\t\t\t\t\t\town = true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`error during track initialization: ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\t\tif (Config.debug && !track.hasSource()) {\n\t\t\t\t\t\tthrow new Error('no supported audio sources found');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\tif (typeof desc.own !== 'boolean') {\n\t\t\t\t\t\tthrow new Error('\"own\" property must be a boolean');\n\t\t\t\t\t}\n\n\t\t\t\t\town = desc.own;\n\n\t\t\t\t\tif (own) {\n\t\t\t\t\t\ttrack = track.clone();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if (desc.hasOwnProperty('rate')) {\n\t\t\t\t// \tif (\n\t\t\t\t// \t\t   typeof desc.rate !== 'number'\n\t\t\t\t// \t\t|| Number.isNaN(desc.rate)\n\t\t\t\t// \t\t|| !Number.isFinite(desc.rate)\n\t\t\t\t// \t) {\n\t\t\t\t// \t\tthrow new Error('\"rate\" property must be a finite number');\n\t\t\t\t// \t}\n\t\t\t\t//\n\t\t\t\t// \trate = desc.rate;\n\t\t\t\t// }\n\n\t\t\t\tif (desc.hasOwnProperty('volume')) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t   typeof desc.volume !== 'number'\n\t\t\t\t\t\t|| Number.isNaN(desc.volume)\n\t\t\t\t\t\t|| !Number.isFinite(desc.volume)\n\t\t\t\t\t\t|| desc.volume < 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error('\"volume\" property must be a non-negative finite number');\n\t\t\t\t\t}\n\n\t\t\t\t\tvolume = desc.volume;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\town    : own != null ? own : false, // lazy equality for null,\n\t\t\t\t\t// rate   : rate != null ? rate : track.rate(), // lazy equality for null,\n\t\t\t\t\ttrack,\n\t\t\t\t\tvolume : volume != null ? volume : track.volume() // lazy equality for null\n\t\t\t\t};\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during playlist initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If a playlist by the given ID already exists, destroy it.\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\t// Add the playlist to the cache.\n\t\t_lists.set(id, list);\n\t}\n\n\tfunction listDelete(id) {\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\treturn _lists.delete(id);\n\t}\n\n\tfunction listClear() {\n\t\t_lists.forEach(list => list._destroy());\n\t\t_lists.clear();\n\t}\n\n\tfunction listHas(id) {\n\t\treturn _lists.has(id);\n\t}\n\n\tfunction listGet(id) {\n\t\treturn _lists.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tRunner Functions.\n\t*******************************************************************************************************************/\n\tconst _runnerParseSelector = (() => {\n\t\tconst notWsRe = /\\S/g;\n\t\tconst parenRe = /[()]/g;\n\n\t\tfunction processNegation(str, startPos) {\n\t\t\tlet match;\n\n\t\t\tnotWsRe.lastIndex = startPos;\n\t\t\tmatch = notWsRe.exec(str);\n\n\t\t\tif (match === null || match[0] !== '(') {\n\t\t\t\tthrow new Error('invalid \":not()\" syntax: missing parentheticals');\n\t\t\t}\n\n\t\t\tparenRe.lastIndex = notWsRe.lastIndex;\n\t\t\tconst start  = notWsRe.lastIndex;\n\t\t\tconst result = { str : '', nextMatch : -1 };\n\t\t\tlet depth = 1;\n\n\t\t\twhile ((match = parenRe.exec(str)) !== null) {\n\t\t\t\tif (match[0] === '(') {\n\t\t\t\t\t++depth;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t--depth;\n\t\t\t\t}\n\n\t\t\t\tif (depth < 1) {\n\t\t\t\t\tresult.nextMatch = parenRe.lastIndex;\n\t\t\t\t\tresult.str = str.slice(start, result.nextMatch - 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tfunction parseSelector(idArg) {\n\t\t\tconst ids  = [];\n\t\t\tconst idRe = /:?[^\\s:()]+/g;\n\t\t\tlet match;\n\n\t\t\twhile ((match = idRe.exec(idArg)) !== null) {\n\t\t\t\tconst id = match[0];\n\n\t\t\t\t// Group negation.\n\t\t\t\tif (id === ':not') {\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error('invalid negation: no group ID preceded \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst parent = ids[ids.length - 1];\n\n\t\t\t\t\tif (parent.id[0] !== ':') {\n\t\t\t\t\t\tthrow new Error(`invalid negation of track \"${parent.id}\": only groups may be negated with \":not()\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst negation = processNegation(idArg, idRe.lastIndex);\n\n\t\t\t\t\tif (negation.nextMatch === -1) {\n\t\t\t\t\t\tthrow new Error('unknown error parsing \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tidRe.lastIndex = negation.nextMatch;\n\t\t\t\t\tparent.not = parseSelector(negation.str);\n\t\t\t\t}\n\n\t\t\t\t// Group or track ID.\n\t\t\t\telse {\n\t\t\t\t\tids.push({ id });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ids;\n\t\t}\n\n\t\treturn parseSelector;\n\t})();\n\n\t/*\n\t\tSimpleAudio.select(selector).…;\n\n\t\tE.g.\n\t\t\tSimpleAudio.select(':ui').…\n\t\t\tSimpleAudio.select(':ui:not(boop)').…\n\t\t\tSimpleAudio.select('boop beep').…\n\t\t\tSimpleAudio.select(':ui :sfx').…\n\t\t\tSimpleAudio.select(':ui:not(boop) :sfx overthetop').…\n\t*/\n\tfunction runnerGet(/* selector */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('no track selector specified');\n\t\t}\n\n\t\tconst selector = String(arguments[0]).trim();\n\t\tconst trackIds = new Set();\n\n\t\ttry {\n\t\t\tconst allIds = Array.from(_tracks.keys());\n\n\t\t\tfunction renderIds(idObj) {\n\t\t\t\tconst id = idObj.id;\n\t\t\t\tlet ids;\n\n\t\t\t\tswitch (id) {\n\t\t\t\tcase ':all':     ids = allIds; break;\n\t\t\t\tcase ':looped':  ids = allIds.filter(id => _tracks.get(id).loop()); break;\n\t\t\t\tcase ':muted':   ids = allIds.filter(id => _tracks.get(id).mute()); break;\n\t\t\t\tcase ':paused':  ids = allIds.filter(id => _tracks.get(id).isPaused()); break;\n\t\t\t\tcase ':playing': ids = allIds.filter(id => _tracks.get(id).isPlaying()); break;\n\t\t\t\tdefault:         ids = id[0] === ':' ? _groups.get(id) : [id]; break;\n\t\t\t\t}\n\n\t\t\t\tif (idObj.hasOwnProperty('not')) {\n\t\t\t\t\tconst negated = idObj.not.map(idObj => renderIds(idObj)).flat(Infinity);\n\t\t\t\t\tids = ids.filter(id => !negated.includes(id));\n\t\t\t\t}\n\n\t\t\t\treturn ids;\n\t\t\t}\n\n\t\t\t_runnerParseSelector(selector).forEach(idObj => renderIds(idObj).forEach(id => {\n\t\t\t\tif (!_tracks.has(id)) {\n\t\t\t\t\tthrow new Error(`track \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\ttrackIds.add(id);\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`error during runner initialization: ${ex.message}`);\n\t\t}\n\n\t\treturn new AudioRunner(trackIds);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMaster Audio Functions.\n\t*******************************************************************************************************************/\n\tfunction masterLoad() {\n\t\tpublish('load');\n\t}\n\n\tfunction masterLoadWithScreen() {\n\t\tpublish('loadwithscreen');\n\t}\n\n\tfunction masterMute(mute) {\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMute;\n\t\t}\n\n\t\t_masterMute = !!mute;\n\t\tpublish('mute', _masterMute);\n\t}\n\n\tfunction masterMuteOnHidden(mute) {\n\t\t// NOTE: Some older browsers—notably: IE 9—do not support the Page Visibility API.\n\t\tif (!Visibility.isEnabled()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMuteOnHidden;\n\t\t}\n\n\t\t_masterMuteOnHidden = !!mute;\n\n\t\tconst namespace = '.SimpleAudio_masterMuteOnHidden';\n\n\t\tif (_masterMuteOnHidden) {\n\t\t\tconst visibilityChange = `${Visibility.changeEvent}${namespace}`;\n\t\t\tjQuery(document)\n\t\t\t\t.off(namespace)\n\t\t\t\t.on(visibilityChange, () => masterMute(Visibility.isHidden()));\n\n\t\t\t// Only change the mute state initially if hidden.\n\t\t\tif (Visibility.isHidden()) {\n\t\t\t\tmasterMute(true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tjQuery(document).off(namespace);\n\t\t}\n\t}\n\n\tfunction masterRate(rate) {\n\t\tif (rate == null) { // lazy equality for null\n\t\t\treturn _masterRate;\n\t\t}\n\n\t\tif (typeof rate !== 'number' || Number.isNaN(rate) || !Number.isFinite(rate)) {\n\t\t\tthrow new Error('rate must be a finite number');\n\t\t}\n\n\t\t_masterRate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\tpublish('rate', _masterRate);\n\t}\n\n\tfunction masterStop() {\n\t\tpublish('stop');\n\t}\n\n\tfunction masterUnload() {\n\t\tpublish('unload');\n\t}\n\n\tfunction masterVolume(volume) {\n\t\tif (volume == null) { // lazy equality for null\n\t\t\treturn _masterVolume;\n\t\t}\n\n\t\tif (typeof volume !== 'number' || Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\tthrow new Error('volume must be a finite number');\n\t\t}\n\n\t\t_masterVolume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\tpublish('volume', _masterVolume);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSubscription Functions.\n\t*******************************************************************************************************************/\n\tfunction subscribe(id, callback) {\n\t\tif (typeof callback !== 'function') {\n\t\t\tthrow new Error('callback parameter must be a function');\n\t\t}\n\n\t\t_subscribers.set(id, callback);\n\t}\n\n\tfunction unsubscribe(id) {\n\t\t_subscribers.delete(id);\n\t}\n\n\tfunction publish(mesg, data) {\n\t\t_subscribers.forEach(fn => fn(mesg, data));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _newTrack(sources) {\n\t\treturn new AudioTrack(sources.map(source => {\n\t\t\t// Handle audio passages.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.audio')) {\n\t\t\t\t\treturn passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle URIs—possibly prefixed with a format specifier.\n\t\t\tconst match = _formatSpecRe.exec(source);\n\t\t\treturn match === null ? source : {\n\t\t\t\tformat : match[1],\n\t\t\t\tsrc    : match[2]\n\t\t\t};\n\t\t}));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Track Functions.\n\t\ttracks : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : trackAdd },\n\t\t\t\tdelete : { value : trackDelete },\n\t\t\t\tclear  : { value : trackClear },\n\t\t\t\thas    : { value : trackHas },\n\t\t\t\tget    : { value : trackGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Group Functions.\n\t\tgroups : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : groupAdd },\n\t\t\t\tdelete : { value : groupDelete },\n\t\t\t\tclear  : { value : groupClear },\n\t\t\t\thas    : { value : groupHas },\n\t\t\t\tget    : { value : groupGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Playlist Functions.\n\t\tlists : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : listAdd },\n\t\t\t\tdelete : { value : listDelete },\n\t\t\t\tclear  : { value : listClear },\n\t\t\t\thas    : { value : listHas },\n\t\t\t\tget    : { value : listGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Runner Functions.\n\t\tselect : { value : runnerGet },\n\n\t\t// Master Audio Functions.\n\t\tload           : { value : masterLoad },\n\t\tloadWithScreen : { value : masterLoadWithScreen },\n\t\tmute           : { value : masterMute },\n\t\tmuteOnHidden   : { value : masterMuteOnHidden },\n\t\trate           : { value : masterRate },\n\t\tstop           : { value : masterStop },\n\t\tunload         : { value : masterUnload },\n\t\tvolume         : { value : masterVolume }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstate.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Diff, Engine, PRNGWrapper, Scripting, clone, session, storage */\n\nvar State = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// History moment stack.\n\tlet _history = [];\n\n\t// Currently active/played moment.\n\tlet _active = momentCreate();\n\n\t// Currently active/played moment index.\n\tlet _activeIndex = -1;\n\n\t// Titles of all moments which have expired (i.e. fallen off the bottom of the stack).\n\tlet _expired = [];\n\n\t// (optional) Seedable PRNG object.\n\tlet _prng = null;\n\n\t// Temporary variables object.\n\tlet _tempVariables = {};\n\n\n\t/*******************************************************************************************************************\n\t\tState Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tResets the story state.\n\t*/\n\tfunction stateReset() {\n\t\tif (DEBUG) { console.log('[State/stateReset()]'); }\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tsession.delete('state');\n\n\t\t/*\n\t\t\tReset the properties.\n\t\t*/\n\t\t_history     = [];\n\t\t_active      = momentCreate();\n\t\t_activeIndex = -1;\n\t\t_expired     = [];\n\t\t_prng        = _prng === null ? null : new PRNGWrapper(_prng.seed, false);\n\t}\n\n\t/*\n\t\tRestores the story state from the active session.\n\t*/\n\tfunction stateRestore() {\n\t\tif (DEBUG) { console.log('[State/stateRestore()]'); }\n\n\t\t/*\n\t\t\tAttempt to restore an active session.\n\t\t*/\n\t\tif (session.has('state')) {\n\t\t\t/*\n\t\t\t\tRetrieve the session.\n\t\t\t*/\n\t\t\tconst stateObj = session.get('state');\n\n\t\t\tif (DEBUG) { console.log('\\tsession state:', stateObj); }\n\n\t\t\tif (stateObj == null) { // lazy equality for null\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tRestore the session.\n\t\t\t*/\n\t\t\tstateUnmarshal(stateObj);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a serializable object.\n\t*/\n\tfunction stateMarshal(noDelta) {\n\t\t/*\n\t\t\tGather the properties.\n\t\t*/\n\t\tconst stateObj = {\n\t\t\tindex : _activeIndex\n\t\t};\n\n\t\tif (noDelta) {\n\t\t\tstateObj.history = clone(_history);\n\t\t}\n\t\telse {\n\t\t\tstateObj.delta = historyDeltaEncode(_history);\n\t\t}\n\n\t\tif (_expired.length > 0) {\n\t\t\tstateObj.expired = [];\n\t\t}\n\n\t\tif (_prng !== null) {\n\t\t\tstateObj.seed = _prng.seed;\n\t\t}\n\n\t\treturn stateObj;\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled story state serialization object.\n\t*/\n\tfunction stateUnmarshal(stateObj, noDelta) {\n\t\tif (stateObj == null) { // lazy equality for null\n\t\t\tthrow new Error('state object is null or undefined');\n\t\t}\n\n\t\tif (\n\t\t\t   !stateObj.hasOwnProperty(noDelta ? 'history' : 'delta')\n\t\t\t|| stateObj[noDelta ? 'history' : 'delta'].length === 0\n\t\t) {\n\t\t\tthrow new Error('state object has no history or history is empty');\n\t\t}\n\n\t\tif (!stateObj.hasOwnProperty('index')) {\n\t\t\tthrow new Error('state object has no index');\n\t\t}\n\n\t\tif (_prng !== null && !stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has no seed, but PRNG is enabled');\n\t\t}\n\n\t\tif (_prng === null && stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has seed, but PRNG is disabled');\n\t\t}\n\n\t\t/*\n\t\t\tRestore the properties.\n\t\t*/\n\t\t_history     = noDelta ? clone(stateObj.history) : historyDeltaDecode(stateObj.delta);\n\t\t_activeIndex = stateObj.index;\n\t\t_expired     = stateObj.hasOwnProperty('expired') ? [...stateObj.expired] : [];\n\n\t\tif (stateObj.hasOwnProperty('seed')) {\n\t\t\t/*\n\t\t\t\tWe only need to restore the PRNG's seed here as `momentActivate()` will handle\n\t\t\t\tfully restoring the PRNG to its proper state.\n\t\t\t*/\n\t\t\t_prng.seed = stateObj.seed;\n\t\t}\n\n\t\t/*\n\t\t\tActivate the current moment (do this only after all properties have been restored).\n\t\t*/\n\t\tmomentActivate(_activeIndex);\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a save-compatible serializable object.\n\t*/\n\tfunction stateMarshalForSave() {\n\t\treturn stateMarshal(true);\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled save-compatible story state serialization object.\n\t*/\n\tfunction stateUnmarshalForSave(stateObj) {\n\t\treturn stateUnmarshal(stateObj, true);\n\t}\n\n\t/*\n\t\tReturns the titles of expired moments.\n\t*/\n\tfunction stateExpired() {\n\t\treturn _expired;\n\t}\n\n\t/*\n\t\tReturns the total number of played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTurns() {\n\t\treturn _expired.length + historyLength();\n\t}\n\n\t/*\n\t\tReturns the passage titles of all played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTitles() {\n\t\treturn _expired.concat(_history.slice(0, historyLength()).map(moment => moment.title));\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title has been played (expired + in-play history moments).\n\t*/\n\tfunction stateHasPlayed(title) {\n\t\tif (title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tif (_expired.includes(title)) {\n\t\t\treturn true;\n\t\t}\n\t\telse if (_history.slice(0, historyLength()).some(moment => moment.title === title)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMoment Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a new moment object created from the given passage title and variables object.\n\t*/\n\tfunction momentCreate(title, variables) {\n\t\treturn {\n\t\t\ttitle     : title == null ? '' : String(title), // lazy equality for null\n\t\t\tvariables : variables == null ? {} : variables   // lazy equality for null\n\t\t};\n\t}\n\n\t/*\n\t\tReturns the active (present) moment.\n\t*/\n\tfunction momentActive() {\n\t\treturn _active;\n\t}\n\n\t/*\n\t\tReturns the index within the history of the active (present) moment.\n\t*/\n\tfunction momentActiveIndex() {\n\t\treturn _activeIndex;\n\t}\n\n\t/*\n\t\tReturns the title from the active (present) moment.\n\t*/\n\tfunction momentActiveTitle() {\n\t\treturn _active.title;\n\t}\n\n\t/*\n\t\tReturns the variables from the active (present) moment.\n\t*/\n\tfunction momentActiveVariables() {\n\t\treturn _active.variables;\n\t}\n\n\t/*\n\t\tReturns the active (present) moment after setting it to either the given moment object\n\t\tor the moment object at the given history index.  Additionally, updates the active session\n\t\tand triggers a history update event.\n\t*/\n\tfunction momentActivate(moment) {\n\t\tif (moment == null) { // lazy equality for null\n\t\t\tthrow new Error('moment activation attempted with null or undefined');\n\t\t}\n\n\t\t/*\n\t\t\tSet the active moment.\n\t\t*/\n\t\tswitch (typeof moment) {\n\t\tcase 'object':\n\t\t\t_active = clone(moment);\n\t\t\tbreak;\n\n\t\tcase 'number':\n\t\t\tif (historyIsEmpty()) {\n\t\t\t\tthrow new Error('moment activation attempted with index on empty history');\n\t\t\t}\n\n\t\t\tif (moment < 0 || moment >= historySize()) {\n\t\t\t\tthrow new RangeError(`moment activation attempted with out-of-bounds index; need [0, ${historySize() - 1}], got ${moment}`);\n\t\t\t}\n\n\t\t\t_active = clone(_history[moment]);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new TypeError(`moment activation attempted with a \"${typeof moment}\"; must be an object or valid history stack index`);\n\t\t}\n\n\t\t/*\n\t\t\tRestore the seedable PRNG.\n\n\t\t\tNOTE: We cannot simply set `_prng.pull` to `_active.pull` as that would\n\t\t\tnot properly mutate the PRNG's internal state.\n\t\t*/\n\t\tif (_prng !== null) {\n\t\t\t_prng = PRNGWrapper.unmarshal({\n\t\t\t\tseed : _prng.seed,\n\t\t\t\tpull : _active.pull\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tUpdate the active session.\n\t\t*/\n\t\tsession.set('state', stateMarshal());\n\n\t\t/*\n\t\t\tTrigger a global `:historyupdate` event.\n\n\t\t\tNOTE: We do this here because setting a new active moment is a core component\n\t\t\tof, virtually, all history updates.\n\t\t*/\n\t\tjQuery.event.trigger(':historyupdate');\n\n\t\treturn _active;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHistory Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the moment history.\n\t*/\n\tfunction historyGet() {\n\t\treturn _history;\n\t}\n\n\t/*\n\t\tReturns the number of active history moments (past only).\n\t*/\n\tfunction historyLength() {\n\t\treturn _activeIndex + 1;\n\t}\n\n\t/*\n\t\tReturns the total number of history moments (past + future).\n\t*/\n\tfunction historySize() {\n\t\treturn _history.length;\n\t}\n\n\t/*\n\t\tReturns whether the history is empty.\n\t*/\n\tfunction historyIsEmpty() {\n\t\treturn _history.length === 0;\n\t}\n\n\t/*\n\t\tReturns the current (pre-play version of the active) moment within the history.\n\t*/\n\tfunction historyCurrent() {\n\t\treturn _history.length > 0 ? _history[_activeIndex] : null;\n\t}\n\n\t/*\n\t\tReturns the topmost (most recent) moment within the history.\n\t*/\n\tfunction historyTop() {\n\t\treturn _history.length > 0 ? _history[_history.length - 1] : null;\n\t}\n\n\t/*\n\t\tReturns the bottommost (least recent) moment within the history.\n\t*/\n\tfunction historyBottom() {\n\t\treturn _history.length > 0 ? _history[0] : null;\n\t}\n\n\t/*\n\t\tReturns the moment at the given index within the history.\n\t*/\n\tfunction historyIndex(index) {\n\t\tif (historyIsEmpty() || index < 0 || index > _activeIndex) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[index];\n\t}\n\n\t/*\n\t\tReturns the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyPeek(offset) {\n\t\tif (historyIsEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst lengthOffset = 1 + (offset ? Math.abs(offset) : 0);\n\n\t\tif (lengthOffset > historyLength()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[historyLength() - lengthOffset];\n\t}\n\n\t/*\n\t\tReturns whether a moment with the given title exists within the history.\n\t*/\n\tfunction historyHas(title) {\n\t\tif (historyIsEmpty() || title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = _activeIndex; i >= 0; --i) {\n\t\t\tif (_history[i].title === title) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tCreates a new moment and pushes it onto the history, discarding future moments if necessary.\n\t*/\n\tfunction historyCreate(title) {\n\t\tif (DEBUG) { console.log(`[State/historyCreate(title: \"${title}\")]`); }\n\n\t\t/*\n\t\t\tTODO: It might be good to have some assertions about the passage title here.\n\t\t*/\n\n\t\t/*\n\t\t\tIf we're not at the top of the stack, discard the future moments.\n\t\t*/\n\t\tif (historyLength() < historySize()) {\n\t\t\tif (DEBUG) { console.log(`\\tnon-top push; discarding ${historySize() - historyLength()} future moments`); }\n\n\t\t\t_history.splice(historyLength(), historySize() - historyLength());\n\t\t}\n\n\t\t/*\n\t\t\tPush the new moment onto the history stack.\n\t\t*/\n\t\t_history.push(momentCreate(title, _active.variables));\n\n\t\tif (_prng) {\n\t\t\thistoryTop().pull = _prng.pull;\n\t\t}\n\n\t\t/*\n\t\t\tTruncate the history, if necessary, by discarding moments from the bottom.\n\t\t*/\n\t\tif (Config.history.maxStates > 0) {\n\t\t\twhile (historySize() > Config.history.maxStates) {\n\t\t\t\t_expired.push(_history.shift().title);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tActivate the new top moment.\n\t\t*/\n\t\t_activeIndex = historySize() - 1;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn historyLength();\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the history.\n\t*/\n\tfunction historyGoTo(index) {\n\t\tif (DEBUG) { console.log(`[State/historyGoTo(index: ${index})]`); }\n\n\t\tif (\n\t\t\t   index == null /* lazy equality for null */\n\t\t\t|| index < 0\n\t\t\t|| index >= historySize()\n\t\t\t|| index === _activeIndex\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t_activeIndex = index;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyGo(offset) {\n\t\tif (DEBUG) { console.log(`[State/historyGo(offset: ${offset})]`); }\n\n\t\tif (offset == null || offset === 0) { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\treturn historyGoTo(_activeIndex + offset);\n\t}\n\n\t/*\n\t\tReturns the delta encoded form of the given history array.\n\t*/\n\tfunction historyDeltaEncode(historyArr) {\n\t\tif (!Array.isArray(historyArr)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (historyArr.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst delta = [historyArr[0]];\n\n\t\tfor (let i = 1, iend = historyArr.length; i < iend; ++i) {\n\t\t\tdelta.push(Diff.diff(historyArr[i - 1], historyArr[i]));\n\t\t}\n\n\t\treturn delta;\n\t}\n\n\t/*\n\t\tReturns a history array from the given delta encoded history array.\n\t*/\n\tfunction historyDeltaDecode(delta) {\n\t\tif (!Array.isArray(delta)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (delta.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst historyArr = [clone(delta[0])];\n\n\t\tfor (let i = 1, iend = delta.length; i < iend; ++i) {\n\t\t\thistoryArr.push(Diff.patch(historyArr[i - 1], delta[i]));\n\t\t}\n\n\t\treturn historyArr;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPRNG Functions.\n\t*******************************************************************************************************************/\n\tfunction prngInit(seed, useEntropy) {\n\t\tif (DEBUG) { console.log(`[State/prngInit(seed: ${seed}, useEntropy: ${useEntropy})]`); }\n\n\t\tif (!historyIsEmpty()) {\n\t\t\tlet scriptSection;\n\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tscriptSection = 'a script-tagged passage';\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tscriptSection = 'the Story JavaScript';\n\t\t\t}\n\n\t\t\tthrow new Error(`State.initPRNG must be called during initialization, within either ${scriptSection} or the StoryInit special passage`);\n\t\t}\n\n\t\t_prng = new PRNGWrapper(seed, useEntropy);\n\t\t_active.pull = _prng.pull;\n\t}\n\n\tfunction prngIsEnabled() {\n\t\treturn _prng !== null;\n\t}\n\n\tfunction prngPull() {\n\t\treturn _prng ? _prng.pull : NaN;\n\t}\n\n\tfunction prngSeed() {\n\t\treturn _prng ? _prng.seed : null;\n\t}\n\n\tfunction prngRandom() {\n\t\tif (DEBUG) { console.log('[State/prngRandom()]'); }\n\n\t\treturn _prng ? _prng.random() : Math.random();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTemporary Variables Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tClear the temporary variables.\n\t*/\n\tfunction tempVariablesClear() {\n\t\tif (DEBUG) { console.log('[State/tempVariablesReset()]'); }\n\n\t\t_tempVariables = {};\n\n\t\t/* legacy */\n\t\tTempVariables = _tempVariables; // eslint-disable-line no-undef\n\t\t/* /legacy */\n\t}\n\n\t/*\n\t\tReturns the current temporary variables.\n\t*/\n\tfunction tempVariables() {\n\t\treturn _tempVariables;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tVariable Chain Parsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value of the given story/temporary variable.\n\t*/\n\tfunction variableGet(varExpression) {\n\t\ttry {\n\t\t\treturn Scripting.evalTwineScript(varExpression);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\t}\n\n\t/*\n\t\tSets the value of the given story/temporary variable.\n\t*/\n\tfunction variableSet(varExpression, value) {\n\t\ttry {\n\t\t\tScripting.evalTwineScript(`${varExpression} = evalTwineScript$Data$`, null, value);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tStory Metadata Functions.\n\t*******************************************************************************************************************/\n\tconst _METADATA_STORE = 'metadata';\n\n\tfunction metadataClear() {\n\t\tstorage.delete(_METADATA_STORE);\n\t}\n\n\tfunction metadataDelete(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.delete key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\n\t\tif (store && store.hasOwnProperty(key)) {\n\t\t\tif (Object.keys(store).length === 1) {\n\t\t\t\tstorage.delete(_METADATA_STORE);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelete store[key];\n\t\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction metadataGet(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.get key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key) ? store[key] : undefined;\n\t}\n\n\tfunction metadataHas(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.has key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key);\n\t}\n\n\tfunction metadataSet(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.set key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tif (typeof value === 'undefined') {\n\t\t\tmetadataDelete(key);\n\t\t}\n\t\telse {\n\t\t\tconst store = storage.get(_METADATA_STORE) || {};\n\t\t\tstore[key] = value;\n\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t}\n\t}\n\n\tfunction metadataSize() {\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store ? Object.keys(store).length : 0;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tState Functions.\n\t\t*/\n\t\treset            : { value : stateReset },\n\t\trestore          : { value : stateRestore },\n\t\tmarshalForSave   : { value : stateMarshalForSave },\n\t\tunmarshalForSave : { value : stateUnmarshalForSave },\n\t\texpired          : { get : stateExpired },\n\t\tturns            : { get : stateTurns },\n\t\tpassages         : { get : stateTitles },\n\t\thasPlayed        : { value : stateHasPlayed },\n\n\t\t/*\n\t\t\tMoment Functions.\n\t\t*/\n\t\tactive      : { get : momentActive },\n\t\tactiveIndex : { get : momentActiveIndex },\n\t\tpassage     : { get : momentActiveTitle },     // shortcut for `State.active.title`\n\t\tvariables   : { get : momentActiveVariables }, // shortcut for `State.active.variables`\n\n\t\t/*\n\t\t\tHistory Functions.\n\t\t*/\n\t\thistory     : { get : historyGet },\n\t\tlength      : { get : historyLength },\n\t\tsize        : { get : historySize },\n\t\tisEmpty     : { value : historyIsEmpty },\n\t\tcurrent     : { get : historyCurrent },\n\t\ttop         : { get : historyTop },\n\t\tbottom      : { get : historyBottom },\n\t\tindex       : { value : historyIndex },\n\t\tpeek        : { value : historyPeek },\n\t\thas         : { value : historyHas },\n\t\tcreate      : { value : historyCreate },\n\t\tgoTo        : { value : historyGoTo },\n\t\tgo          : { value : historyGo },\n\t\tdeltaEncode : { value : historyDeltaEncode },\n\t\tdeltaDecode : { value : historyDeltaDecode },\n\n\t\t/*\n\t\t\tPRNG Functions.\n\t\t*/\n\t\tprng : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tinit      : { value : prngInit },\n\t\t\t\tisEnabled : { value : prngIsEnabled },\n\t\t\t\tpull      : { get : prngPull },\n\t\t\t\tseed      : { get : prngSeed }\n\t\t\t}))\n\t\t},\n\t\trandom : { value : prngRandom },\n\n\t\t/*\n\t\t\tTemporary Variables Functions.\n\t\t*/\n\t\tclearTemporary : { value : tempVariablesClear },\n\t\ttemporary      : { get : tempVariables },\n\n\t\t/*\n\t\t\tVariable Chain Parsing Functions.\n\t\t*/\n\t\tgetVar : { value : variableGet },\n\t\tsetVar : { value : variableSet },\n\n\t\t/*\n\t\t\tStory Metadata Functions.\n\t\t*/\n\t\tmetadata : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tclear  : { value : metadataClear },\n\t\t\t\tdelete : { value : metadataDelete },\n\t\t\t\tget    : { value : metadataGet },\n\t\t\t\thas    : { value : metadataHas },\n\t\t\t\tset    : { value : metadataSet },\n\t\t\t\tsize   : { get : metadataSize }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tinitPRNG : { value : prngInit },\n\t\trestart  : { value : () => Engine.restart() },\n\t\tbackward : { value : () => Engine.backward() },\n\t\tforward  : { value : () => Engine.forward() },\n\t\tdisplay  : { value : (...args) => Engine.display(...args) },\n\t\tshow     : { value : (...args) => Engine.show(...args) },\n\t\tplay     : { value : (...args) => Engine.play(...args) }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/scripting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Engine, Patterns, State, Story, Util */\n\nvar Scripting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable no-unused-vars */\n\n\t/*******************************************************************************************************************\n\t\tDeprecated Legacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns the jQuery-wrapped target element(s) after making them accessible\n\t\tclickables (ARIA compatibility).\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction addAccessibleClickHandler(targets, selector, handler, one, namespace) {\n\t\tif (arguments.length < 2) {\n\t\t\tthrow new Error('addAccessibleClickHandler insufficient number of parameters');\n\t\t}\n\n\t\tlet fn;\n\t\tlet opts;\n\n\t\tif (typeof selector === 'function') {\n\t\t\tfn = selector;\n\t\t\topts = {\n\t\t\t\tnamespace : one,\n\t\t\t\tone       : !!handler\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tfn = handler;\n\t\t\topts = {\n\t\t\t\tnamespace,\n\t\t\t\tone : !!one,\n\t\t\t\tselector\n\t\t\t};\n\t\t}\n\n\t\tif (typeof fn !== 'function') {\n\t\t\tthrow new TypeError('addAccessibleClickHandler handler parameter must be a function');\n\t\t}\n\n\t\treturn jQuery(targets).ariaClick(opts, fn);\n\t}\n\n\t/*\n\t\t[DEPRECATED] Returns a new DOM element, optionally appending it to the passed DOM element, if any.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertElement(place, type, id, classNames, text, title) { // eslint-disable-line max-params\n\t\tconst $el = jQuery(document.createElement(type));\n\n\t\t// Add attributes/properties.\n\t\tif (id) {\n\t\t\t$el.attr('id', id);\n\t\t}\n\n\t\tif (classNames) {\n\t\t\t$el.addClass(classNames);\n\t\t}\n\n\t\tif (title) {\n\t\t\t$el.attr('title', title);\n\t\t}\n\n\t\t// Add text content.\n\t\tif (text) {\n\t\t\t$el.text(text);\n\t\t}\n\n\t\t// Append it to the given node.\n\t\tif (place) {\n\t\t\t$el.appendTo(place);\n\t\t}\n\n\t\treturn $el[0];\n\t}\n\n\t/*\n\t\t[DEPRECATED] Creates a new text node and appends it to the passed DOM element.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertText(place, text) {\n\t\tjQuery(place).append(document.createTextNode(text));\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes all children from the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeChildren(node) {\n\t\tjQuery(node).empty();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeElement(node) {\n\t\tjQuery(node).remove();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Fades a DOM element in or out.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction fade(el, options) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tconst direction = options.fade === 'in' ? 1 : -1;\n\t\tlet current;\n\t\tlet proxy      = el.cloneNode(true);\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tcurrent += 0.05 * direction;\n\t\t\tsetOpacity(proxy, Math.easeInOut(current));\n\n\t\t\tif (direction === 1 && current >= 1 || direction === -1 && current <= 0) {\n\t\t\t\tel.style.visibility = options.fade === 'in' ? 'visible' : 'hidden';\n\t\t\t\tproxy.parentNode.replaceChild(el, proxy);\n\t\t\t\tproxy = null;\n\t\t\t\twindow.clearInterval(intervalId);\n\n\t\t\t\tif (options.onComplete) {\n\t\t\t\t\toptions.onComplete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction setOpacity(el, opacity) {\n\t\t\t// Old IE.\n\t\t\tel.style.zoom = 1;\n\t\t\tel.style.filter = `alpha(opacity=${Math.floor(opacity * 100)})`;\n\n\t\t\t// CSS.\n\t\t\tel.style.opacity = opacity;\n\t\t}\n\n\t\tel.parentNode.replaceChild(proxy, el);\n\n\t\tif (options.fade === 'in') {\n\t\t\tcurrent = 0;\n\t\t\tproxy.style.visibility = 'visible';\n\t\t}\n\t\telse {\n\t\t\tcurrent = 1;\n\t\t}\n\n\t\tsetOpacity(proxy, current);\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\t/*\n\t\t[DEPRECATED] Scrolls the browser window to ensure that a DOM element is in view.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction scrollWindowTo(el, incrementBy) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tlet increment = incrementBy != null ? Number(incrementBy) : 0.1; // lazy equality for null\n\n\t\tif (Number.isNaN(increment) || !Number.isFinite(increment) || increment < 0) {\n\t\t\tincrement = 0.1;\n\t\t}\n\t\telse if (increment > 1) {\n\t\t\tincrement = 1;\n\t\t}\n\n\t\tconst start     = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\tconst end       = ensureVisible(el);\n\t\tconst distance  = Math.abs(start - end);\n\t\tconst direction = start > end ? -1 : 1;\n\t\tlet progress   = 0;\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tprogress += increment;\n\t\t\twindow.scroll(0, start + direction * (distance * Math.easeInOut(progress)));\n\n\t\t\tif (progress >= 1) {\n\t\t\t\twindow.clearInterval(intervalId);\n\t\t\t}\n\t\t}\n\n\t\tfunction findPosY(el) { // eslint-disable-line no-shadow\n\t\t\tlet curtop = 0;\n\n\t\t\twhile (el.offsetParent) {\n\t\t\t\tcurtop += el.offsetTop;\n\t\t\t\tel = el.offsetParent;\n\t\t\t}\n\n\t\t\treturn curtop;\n\t\t}\n\n\t\tfunction ensureVisible(el) { // eslint-disable-line no-shadow\n\t\t\tconst posTop    = findPosY(el);\n\t\t\tconst posBottom = posTop + el.offsetHeight;\n\t\t\tconst winTop    = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\t\tconst winHeight = window.innerHeight ? window.innerHeight : document.body.clientHeight;\n\t\t\tconst winBottom = winTop + winHeight;\n\n\t\t\treturn posTop >= winTop && posBottom > winBottom && el.offsetHeight < winHeight\n\t\t\t\t? posTop - (winHeight - el.offsetHeight) + 20\n\t\t\t\t: posTop;\n\t\t}\n\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUser Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a random value from its given arguments.\n\t*/\n\tfunction either(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn Array.prototype.concat.apply([], arguments).random();\n\t}\n\n\t/*\n\t\tRemoves the given key, and its value, from the story metadata store.\n\t*/\n\tfunction forget(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`forget key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.delete(key);\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the logical-AND\n\t\taggregate of the set.\n\t*/\n\tfunction hasVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('hasVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend; ++i) {\n\t\t\tif (!played.includes(needles[i])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tReturns the number of turns that have passed since the last instance of the given passage\n\t\toccurred within the story history or `-1` if it does not exist.  If multiple passages are\n\t\tgiven, returns the lowest count (which can be `-1`).\n\t*/\n\tfunction lastVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('lastVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\t\tconst uBound  = played.length - 1;\n\t\tlet turns = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && turns > -1; ++i) {\n\t\t\tconst lastIndex = played.lastIndexOf(needles[i]);\n\t\t\tturns = Math.min(turns, lastIndex === -1 ? -1 : uBound - lastIndex);\n\t\t}\n\n\t\treturn turns;\n\t}\n\n\t/*\n\t\tSets the given key/value pair within the story metadata store.\n\t*/\n\tfunction memorize(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`memorize key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.set(key, value);\n\t}\n\n\t/*\n\t\tReturns the title of the current passage.\n\t*/\n\tfunction passage() {\n\t\treturn State.passage;\n\t}\n\n\t/*\n\t\tReturns the title of a previous passage, either the most recent one whose title does not\n\t\tmatch that of the active passage or the one at the optional offset, or an empty string,\n\t\tif there is no such passage.\n\t*/\n\tfunction previous(/* legacy: offset */) {\n\t\tconst passages = State.passages;\n\n\t\t/* legacy: behavior with an offset */\n\t\tif (arguments.length > 0) {\n\t\t\tconst offset = Number(arguments[0]);\n\n\t\t\tif (!Number.isSafeInteger(offset) || offset < 1) {\n\t\t\t\tthrow new RangeError('previous offset parameter must be a positive integer greater than zero');\n\t\t\t}\n\n\t\t\treturn passages.length > offset ? passages[passages.length - 1 - offset] : '';\n\t\t}\n\t\t/* /legacy */\n\n\t\tfor (let i = passages.length - 2; i >= 0; --i) {\n\t\t\tif (passages[i] !== State.passage) {\n\t\t\t\treturn passages[i];\n\t\t\t}\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the range of the given bounds.\n\t*/\n\tfunction random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(arguments[0]);\n\t\t\tmax = Math.trunc(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!Number.isInteger(min)) {\n\t\t\tthrow new Error('random min parameter must be an integer');\n\t\t}\n\t\tif (!Number.isInteger(max)) {\n\t\t\tthrow new Error('random max parameter must be an integer');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(State.random() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a pseudo-random real number (floating-point) within the range of the given bounds.\n\n\t\tNOTE: Unlike with its sibling function `random()`, the `max` parameter\n\t\tis exclusive, not inclusive—i.e. the range goes to, but does not include,\n\t\tthe given value.\n\t*/\n\tfunction randomFloat(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('randomFloat called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0.0;\n\t\t\tmax = Number(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Number(arguments[0]);\n\t\t\tmax = Number(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min) || !Number.isFinite(min)) {\n\t\t\tthrow new Error('randomFloat min parameter must be a number');\n\t\t}\n\t\tif (Number.isNaN(max) || !Number.isFinite(max)) {\n\t\t\tthrow new Error('randomFloat max parameter must be a number');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn State.random() * (max - min) + min;\n\t}\n\n\t/*\n\t\tReturns the value of the given key from the story metadata store\n\t\tor the given default value if the key does not exist.\n\t*/\n\tfunction recall(key, defaultValue) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`recall key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\treturn State.metadata.has(key) ? State.metadata.get(key) : defaultValue;\n\t}\n\n\t/*\n\t\tReturns a new array consisting of all of the tags of the given passages.\n\t*/\n\tfunction tags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn Story.get(State.passage).tags.slice(0);\n\t\t}\n\n\t\tconst passages = Array.prototype.concat.apply([], arguments);\n\t\tlet tags = [];\n\n\t\tfor (let i = 0, iend = passages.length; i < iend; ++i) {\n\t\t\ttags = tags.concat(Story.get(passages[i]).tags);\n\t\t}\n\n\t\treturn tags;\n\t}\n\n\t/*\n\t\tReturns a reference to the current temporary _variables store.\n\t*/\n\tfunction temporary() {\n\t\treturn State.temporary;\n\t}\n\n\t/*\n\t\tReturns the number of milliseconds which have passed since the current passage was rendered.\n\t*/\n\tfunction time() {\n\t\treturn Engine.lastPlay === null ? 0 : Util.now() - Engine.lastPlay;\n\t}\n\n\t/*\n\t\tReturns the number of passages that the player has visited.\n\n\t\tNOTE: Passages which were visited but have been undone—e.g. via the backward\n\t\tbutton or the `<<back>>` macro—are no longer part of the in-play story\n\t\thistory and thus are not tallied.  Passages which were visited but have\n\t\texpired from the story history, on the other hand, are tallied.\n\t*/\n\tfunction turns() {\n\t\treturn State.turns;\n\t}\n\n\t/*\n\t\tReturns a reference to the current story $variables store.\n\t*/\n\tfunction variables() {\n\t\treturn State.variables;\n\t}\n\n\t/*\n\t\tReturns the number of times that the passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the lowest count.\n\t*/\n\tfunction visited(/* variadic */) {\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments.length === 0 ? [State.passage] : arguments);\n\t\tconst played  = State.passages;\n\t\tlet count = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && count > 0; ++i) {\n\t\t\tcount = Math.min(count, played.count(needles[i]));\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/*\n\t\tReturns the number of passages within the story history which are tagged with all of the given tags.\n\t*/\n\tfunction visitedTags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('visitedTags called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst nLength = needles.length;\n\t\tconst played  = State.passages;\n\t\tconst seen    = new Map();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0, iend = played.length; i < iend; ++i) {\n\t\t\tconst title = played[i];\n\n\t\t\tif (seen.has(title)) {\n\t\t\t\tif (seen.get(title)) {\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst tags = Story.get(title).tags;\n\n\t\t\t\tif (tags.length > 0) {\n\t\t\t\t\tlet found = 0;\n\n\t\t\t\t\tfor (let j = 0; j < nLength; ++j) {\n\t\t\t\t\t\tif (tags.includes(needles[j])) {\n\t\t\t\t\t\t\t++found;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (found === nLength) {\n\t\t\t\t\t\t++count;\n\t\t\t\t\t\tseen.set(title, true);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tseen.set(title, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/* eslint-enable no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tImport Functions.\n\t*******************************************************************************************************************/\n\tvar { // eslint-disable-line no-var\n\t\t/* eslint-disable no-unused-vars */\n\t\timportScripts,\n\t\timportStyles\n\t\t/* eslint-enable no-unused-vars */\n\t} = (() => {\n\t\t// Slugify the given URL.\n\t\tfunction slugifyUrl(url) {\n\t\t\treturn Util.parseUrl(url).path\n\t\t\t\t.replace(/^[^\\w]+|[^\\w]+$/g, '')\n\t\t\t\t.replace(/[^\\w]+/g, '-')\n\t\t\t\t.toLocaleLowerCase();\n\t\t}\n\n\t\t// Add a <script> element which will load the script from the given URL.\n\t\tfunction addScript(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('script'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importScripts failed to load the script \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `script-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\ttype : 'text/javascript',\n\t\t\t\t\t\tsrc  : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Add a <link> element which will load the stylesheet from the given URL.\n\t\tfunction addStyle(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('link'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importStyles failed to load the stylesheet \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `style-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\trel  : 'stylesheet',\n\t\t\t\t\t\thref : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Turn a list of callbacks into a sequential chain of `Promise` objects.\n\t\tfunction sequence(callbacks) {\n\t\t\treturn callbacks.reduce((seq, fn) => seq = seq.then(fn), Promise.resolve()); // eslint-disable-line no-param-reassign\n\t\t}\n\n\t\t/*\n\t\t\tImport scripts from a URL.\n\t\t*/\n\t\tfunction importScripts(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addScript(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addScript(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t/*\n\t\t\tImport stylesheets from a URL.\n\t\t*/\n\t\tfunction importStyles(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addStyle(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addStyle(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t// Exports.\n\t\treturn {\n\t\t\timportScripts,\n\t\t\timportStyles\n\t\t};\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tParsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the given string after converting all TwineScript syntactical sugars to\n\t\ttheir native JavaScript counterparts.\n\t*/\n\tconst parse = (() => {\n\t\tconst tokenTable = Util.toEnum({\n\t\t\t/* eslint-disable quote-props */\n\t\t\t// Story $variable sigil-prefix.\n\t\t\t'$'     : 'State.variables.',\n\t\t\t// Temporary _variable sigil-prefix.\n\t\t\t'_'     : 'State.temporary.',\n\t\t\t// Assignment operators.\n\t\t\t'to'    : '=',\n\t\t\t// Equality operators.\n\t\t\t'eq'    : '==',\n\t\t\t'neq'   : '!=',\n\t\t\t'is'    : '===',\n\t\t\t'isnot' : '!==',\n\t\t\t// Relational operators.\n\t\t\t'gt'    : '>',\n\t\t\t'gte'   : '>=',\n\t\t\t'lt'    : '<',\n\t\t\t'lte'   : '<=',\n\t\t\t// Logical operators.\n\t\t\t'and'   : '&&',\n\t\t\t'or'    : '||',\n\t\t\t// Unary operators.\n\t\t\t'not'   : '!',\n\t\t\t'def'   : '\"undefined\" !== typeof',\n\t\t\t'ndef'  : '\"undefined\" === typeof'\n\t\t\t/* eslint-enable quote-props */\n\t\t});\n\t\tconst parseRe = new RegExp([\n\t\t\t'(\"\"|\\'\\')',                                          // 1=Empty quotes\n\t\t\t'(\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\")',                            // 2=Double quoted, non-empty\n\t\t\t\"('(?:\\\\\\\\.|[^'\\\\\\\\])+')\",                            // 3=Single quoted, non-empty\n\t\t\t'([=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}]+)',       // 4=Operator delimiters\n\t\t\t'([^\"\\'=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}\\\\s]+)' // 5=Barewords\n\t\t].join('|'), 'g');\n\t\tconst notSpaceRe      = /\\S/;\n\t\tconst varTest         = new RegExp(`^${Patterns.variable}`);\n\t\tconst withColonTestRe = /^\\s*:/;\n\t\tconst withNotTestRe   = /^\\s+not\\b/;\n\n\t\tfunction parse(rawCodeString) {\n\t\t\tif (parseRe.lastIndex !== 0) {\n\t\t\t\tthrow new RangeError('Scripting.parse last index is non-zero at start');\n\t\t\t}\n\n\t\t\tlet code  = rawCodeString;\n\t\t\tlet match;\n\n\t\t\twhile ((match = parseRe.exec(code)) !== null) {\n\t\t\t\t// no-op: Empty quotes | Double quoted | Single quoted | Operator delimiters\n\n\t\t\t\t// Barewords.\n\t\t\t\tif (match[5]) {\n\t\t\t\t\tlet token = match[5];\n\n\t\t\t\t\t// If the token is simply a dollar-sign or underscore, then it's either\n\t\t\t\t\t// just the raw character or, probably, a function alias, so skip it.\n\t\t\t\t\tif (token === '$' || token === '_') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is a story $variable or temporary _variable, reset it\n\t\t\t\t\t// to just its sigil—for later mapping.\n\t\t\t\t\telse if (varTest.test(token)) {\n\t\t\t\t\t\ttoken = token[0];\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is `is`, check to see if it's followed by `not`, if so,\n\t\t\t\t\t// convert them into the `isnot` operator.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: This is a safety feature, since `$a is not $b` probably sounds\n\t\t\t\t\t// reasonable to most users.\n\t\t\t\t\telse if (token === 'is') {\n\t\t\t\t\t\tconst start = parseRe.lastIndex;\n\t\t\t\t\t\tconst ahead = code.slice(start);\n\n\t\t\t\t\t\tif (withNotTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcode = code.splice(start, ahead.search(notSpaceRe));\n\t\t\t\t\t\t\ttoken = 'isnot';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is followed by a colon, then it's likely to be an object\n\t\t\t\t\t// property, so skip it.\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst ahead = code.slice(parseRe.lastIndex);\n\n\t\t\t\t\t\tif (withColonTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the finalized token has a mapping, replace it within the code string\n\t\t\t\t\t// with its counterpart.\n\t\t\t\t\tif (tokenTable[token]) {\n\t\t\t\t\t\tcode = code.splice(\n\t\t\t\t\t\t\tmatch.index,      // starting index\n\t\t\t\t\t\t\ttoken.length,     // replace how many\n\t\t\t\t\t\t\ttokenTable[token] // replacement string\n\t\t\t\t\t\t);\n\t\t\t\t\t\tparseRe.lastIndex += tokenTable[token].length - token.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn code;\n\t\t}\n\n\t\treturn parse;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tEval Functions.\n\t*******************************************************************************************************************/\n\t/* eslint-disable no-eval, no-extra-parens, no-unused-vars */\n\t/*\n\t\tEvaluates the given JavaScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalJavaScript(code, output, data) {\n\t\treturn (function (code, output, evalJavaScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, String(code), output, data);\n\t}\n\n\t/*\n\t\tEvaluates the given TwineScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalTwineScript(code, output, data) {\n\t\t// NOTE: Do not move the dollar sign to the front of `evalTwineScript$Data$`,\n\t\t// as `parse()` will break references to it within the code string.\n\t\treturn (function (code, output, evalTwineScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, parse(String(code)), output, data);\n\t}\n\t/* eslint-enable no-eval, no-extra-parens, no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tparse           : { value : parse },\n\t\tevalJavaScript  : { value : evalJavaScript },\n\t\tevalTwineScript : { value : evalTwineScript }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/lexer.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tEOF,\n\tLexer\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\t// End of file (string, actually).\n\tconst EOF = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tLexer Class.\n\t*******************************************************************************************************************/\n\tclass Lexer {\n\t\tconstructor(source, initialState) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer constructor called with too few parameters (source:string , initialState:function)');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tthis.source  → the string to be scanned\n\t\t\t\tthis.initial → initial state\n\t\t\t\tthis.state   → current state\n\t\t\t\tthis.start   → start position of an item\n\t\t\t\tthis.pos     → current position in the source string\n\t\t\t\tthis.depth   → current brace/bracket/parenthesis nesting depth\n\t\t\t\tthis.items   → scanned item queue\n\t\t\t\tthis.data    → lexing data\n\t\t\t*/\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : source\n\t\t\t\t},\n\n\t\t\t\tinitial : {\n\t\t\t\t\tvalue : initialState\n\t\t\t\t},\n\n\t\t\t\tstate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : initialState\n\t\t\t\t},\n\n\t\t\t\tstart : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tpos : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tdepth : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\titems : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : []\n\t\t\t\t},\n\n\t\t\t\tdata : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : {}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treset() {\n\t\t\tthis.state  = this.initial;\n\t\t\tthis.start  = 0;\n\t\t\tthis.pos    = 0;\n\t\t\tthis.depth  = 0;\n\t\t\tthis.items  = [];\n\t\t\tthis.data   = {};\n\t\t}\n\n\t\trun() {\n\t\t\t// scan the source string until no states remain\n\t\t\twhile (this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the array of items\n\t\t\treturn this.items;\n\t\t}\n\n\t\tnextItem() {\n\t\t\t// scan the source string until we have an item or no states remain\n\t\t\twhile (this.items.length === 0 && this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the current item\n\t\t\treturn this.items.shift();\n\t\t}\n\n\t\tnext() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos++];\n\t\t}\n\n\t\tpeek() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos];\n\t\t}\n\n\t\tbackup(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos -= num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t--this.pos;\n\t\t\t// }\n\t\t\tthis.pos -= num || 1;\n\t\t}\n\n\t\tforward(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos += num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t++this.pos;\n\t\t\t// }\n\t\t\tthis.pos += num || 1;\n\t\t}\n\n\t\tignore() {\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\taccept(valid) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (valid.includes(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRe(validRe) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (validRe.test(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRun(valid) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!valid.includes(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\tacceptRunRe(validRe) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!validRe.test(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\temit(type) {\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\terror(type, message) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer.prototype.error called with too few parameters (type:number , message:string)');\n\t\t\t}\n\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\tmessage,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic enumFromNames(names) {\n\t\t\tconst obj = names.reduce((obj, name, i) => {\n\t\t\t\tobj[name] = i; // eslint-disable-line no-param-reassign\n\t\t\t\treturn obj;\n\t\t\t}, {});\n\t\t\treturn Object.freeze(Object.assign(Object.create(null), obj));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn {\n\t\tEOF,\n\t\tLexer\n\t};\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/wikifier.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, EOF, Engine, Lexer, Patterns, Scripting, State, Story, TempState, Util, convertBreaks,\n\t       errorPrologRegExp\n*/\n\n/*\n\tTODO: The Wikifier, and associated code, could stand to receive a serious refactoring.\n*/\n/* eslint-disable max-len */\nvar Wikifier = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Wikifier call depth.\n\tlet _callDepth = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tWikifier Class.\n\t*******************************************************************************************************************/\n\tclass Wikifier {\n\t\tconstructor(destination, source, options) {\n\t\t\tif (Wikifier.Parser.Profile.isEmpty()) {\n\t\t\t\tWikifier.Parser.Profile.compile();\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// General Wikifier properties.\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : String(source)\n\t\t\t\t},\n\n\t\t\t\toptions : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Object.assign({\n\t\t\t\t\t\tprofile : 'all'\n\t\t\t\t\t}, options)\n\t\t\t\t},\n\n\t\t\t\tnextMatch : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\toutput : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t// Macro parser ('macro') related properties.\n\t\t\t\t_rawArgs : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// No destination specified.  Create a fragment to act as the output buffer.\n\t\t\tif (destination == null) { // lazy equality for null\n\t\t\t\tthis.output = document.createDocumentFragment();\n\t\t\t}\n\n\t\t\t// jQuery-wrapped destination.  Grab the first element.\n\t\t\telse if (destination.jquery) { // cannot use `hasOwnProperty()` here as `jquery` is from jQuery's prototype\n\t\t\t\tthis.output = destination[0];\n\t\t\t}\n\n\t\t\t// Normal destination.\n\t\t\telse {\n\t\t\t\tthis.output = destination;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tWikify the source into the output buffer element, possibly converting line\n\t\t\t\tbreaks into paragraphs.\n\n\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\tto ensure that the call depth is properly restored in the event that an\n\t\t\t\tuncaught exception is thrown during the call to `subWikify()`.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\t++_callDepth;\n\n\t\t\t\tthis.subWikify(this.output);\n\n\t\t\t\t// Limit line break conversion to non-recursive calls.\n\t\t\t\tif (_callDepth === 1 && Config.cleanupWikifierOutput) {\n\t\t\t\t\tconvertBreaks(this.output);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t--_callDepth;\n\t\t\t}\n\t\t}\n\n\t\tsubWikify(output, terminator, options) {\n\t\t\t// Cache and temporarily replace the current output buffer.\n\t\t\tconst oldOutput = this.output;\n\t\t\tthis.output = output;\n\n\t\t\tlet newOptions;\n\t\t\tlet oldOptions;\n\n\t\t\t// Parser option overrides.\n\t\t\tif (Wikifier.Option.length > 0) {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, Wikifier.Option.options);\n\t\t\t}\n\t\t\t// Local parameter option overrides.\n\t\t\tif (options !== null && typeof options === 'object') {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, options);\n\t\t\t}\n\t\t\t// If new options exist, cache and temporarily replace the current options.\n\t\t\tif (newOptions) {\n\t\t\t\toldOptions = this.options;\n\t\t\t\tthis.options = Object.assign({}, this.options, newOptions);\n\t\t\t}\n\n\t\t\tconst parsersProfile   = Wikifier.Parser.Profile.get(this.options.profile);\n\t\t\tconst terminatorRegExp = terminator\n\t\t\t\t? new RegExp(`(?:${terminator})`, this.options.ignoreTerminatorCase ? 'gim' : 'gm')\n\t\t\t\t: null;\n\t\t\tlet terminatorMatch;\n\t\t\tlet parserMatch;\n\n\t\t\tdo {\n\t\t\t\t// Prepare the RegExp match positions.\n\t\t\t\tparsersProfile.parserRegExp.lastIndex = this.nextMatch;\n\n\t\t\t\tif (terminatorRegExp) {\n\t\t\t\t\tterminatorRegExp.lastIndex = this.nextMatch;\n\t\t\t\t}\n\n\t\t\t\t// Get the first matches.\n\t\t\t\tparserMatch     = parsersProfile.parserRegExp.exec(this.source);\n\t\t\t\tterminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;\n\n\t\t\t\t// Try for a terminator match, unless there's a closer parser match.\n\t\t\t\tif (terminatorMatch && (!parserMatch || terminatorMatch.index <= parserMatch.index)) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (terminatorMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, terminatorMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = terminatorMatch.index;\n\t\t\t\t\tthis.matchLength = terminatorMatch[0].length;\n\t\t\t\t\tthis.matchText   = terminatorMatch[0];\n\t\t\t\t\tthis.nextMatch   = terminatorRegExp.lastIndex;\n\n\t\t\t\t\t// Restore the original output buffer and options.\n\t\t\t\t\tthis.output = oldOutput;\n\n\t\t\t\t\tif (oldOptions) {\n\t\t\t\t\t\tthis.options = oldOptions;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Exit.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try for a parser match.\n\t\t\t\telse if (parserMatch) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (parserMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, parserMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = parserMatch.index;\n\t\t\t\t\tthis.matchLength = parserMatch[0].length;\n\t\t\t\t\tthis.matchText   = parserMatch[0];\n\t\t\t\t\tthis.nextMatch   = parsersProfile.parserRegExp.lastIndex;\n\n\t\t\t\t\t// Figure out which parser matched.\n\t\t\t\t\tlet matchingParser;\n\n\t\t\t\t\tfor (let i = 1, iend = parserMatch.length; i < iend; ++i) {\n\t\t\t\t\t\tif (parserMatch[i]) {\n\t\t\t\t\t\t\tmatchingParser = i - 1;\n\t\t\t\t\t\t\tbreak; // stop once we've found the matching parser\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call the parser.\n\t\t\t\t\tparsersProfile.parsers[matchingParser].handler(this);\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (terminatorMatch || parserMatch);\n\n\t\t\t// Output any text after the last match.\n\t\t\tif (TempState.break == null) { // lazy equality for null\n\t\t\t\tif (this.nextMatch < this.source.length) {\n\t\t\t\t\tthis.outputText(this.output, this.nextMatch, this.source.length);\n\t\t\t\t\tthis.nextMatch = this.source.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In case of <<break>>/<<continue>>, remove the last <br>.\n\t\t\telse if (\n\t\t\t\t   this.output.lastChild\n\t\t\t\t&& this.output.lastChild.nodeType === Node.ELEMENT_NODE\n\t\t\t\t&& this.output.lastChild.nodeName.toUpperCase() === 'BR'\n\t\t\t) {\n\t\t\t\tjQuery(this.output.lastChild).remove();\n\t\t\t}\n\n\t\t\t// Restore the original output buffer and options.\n\t\t\tthis.output = oldOutput;\n\n\t\t\tif (oldOptions) {\n\t\t\t\tthis.options = oldOptions;\n\t\t\t}\n\t\t}\n\n\t\toutputText(destination, startPos, endPos) {\n\t\t\tdestination.appendChild(document.createTextNode(this.source.substring(startPos, endPos)));\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the raw, unprocessed\n\t\t\ttext given to the currently executing macro.\n\t\t*/\n\t\trawArgs() {\n\t\t\treturn this._rawArgs;\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the text given to\n\t\t\tthe currently executing macro after doing TwineScript-to-JavaScript transformations.\n\t\t*/\n\t\tfullArgs() {\n\t\t\treturn Scripting.parse(this._rawArgs);\n\t\t}\n\n\t\t/*\n\t\t\tReturns the output generated by wikifying the given text, throwing if there were errors.\n\t\t*/\n\t\tstatic wikifyEval(text) {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\tnew Wikifier(output, text);\n\n\t\t\tconst errors = output.querySelector('.error');\n\n\t\t\tif (errors !== null) {\n\t\t\t\tthrow new Error(errors.textContent.replace(errorPrologRegExp, ''));\n\t\t\t}\n\n\t\t\treturn output;\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an internal link.\n\t\t*/\n\t\tstatic createInternalLink(destination, passage, text, callback) {\n\t\t\tconst $link = jQuery(document.createElement('a'));\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\n\t\t\t\t$link.ariaClick({ one : true }, () => {\n\t\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\n\t\t\t\t\tEngine.play(passage);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (text) {\n\t\t\t\t$link.append(document.createTextNode(text));\n\t\t\t}\n\n\t\t\tif (destination) {\n\t\t\t\t$link.appendTo(destination);\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an external link.\n\t\t*/\n\t\tstatic createExternalLink(destination, url, text) {\n\t\t\tconst $link = jQuery(document.createElement('a'))\n\t\t\t\t.attr('target', '_blank')\n\t\t\t\t.addClass('link-external')\n\t\t\t\t.text(text)\n\t\t\t\t.appendTo(destination);\n\n\t\t\tif (url != null) { // lazy equality for null\n\t\t\t\t$link.attr({\n\t\t\t\t\thref     : url,\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the given link source is external (probably).\n\t\t*/\n\t\tstatic isExternalLink(link) {\n\t\t\tif (Story.has(link)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst urlRegExp = new RegExp(`^${Patterns.url}`, 'gim');\n\t\t\treturn urlRegExp.test(link) || /[/.?#]/.test(link);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tOption Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Option', {\n\t\tvalue : (() => {\n\t\t\t// Options array (stack).\n\t\t\tlet _optionsStack = [];\n\n\n\t\t\t/*\n\t\t\t\tGlobalOption Functions.\n\t\t\t*/\n\t\t\tfunction optionLength() {\n\t\t\t\treturn _optionsStack.length;\n\t\t\t}\n\n\t\t\tfunction optionGetter() {\n\t\t\t\treturn Object.assign({}, ..._optionsStack);\n\t\t\t}\n\n\t\t\tfunction optionClear() {\n\t\t\t\t_optionsStack = [];\n\t\t\t}\n\n\t\t\tfunction optionGet(idx) {\n\t\t\t\treturn _optionsStack[idx];\n\t\t\t}\n\n\t\t\tfunction optionPop() {\n\t\t\t\treturn _optionsStack.pop();\n\t\t\t}\n\n\t\t\tfunction optionPush(options) {\n\t\t\t\tif (typeof options !== 'object' || options === null) {\n\t\t\t\t\tthrow new TypeError(`Wikifier.Option.push options parameter must be an object (received: ${Util.getType(options)})`);\n\t\t\t\t}\n\n\t\t\t\treturn _optionsStack.push(options);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\tlength  : { get : optionLength },\n\t\t\t\toptions : { get : optionGetter },\n\t\t\t\tclear   : { value : optionClear },\n\t\t\t\tget     : { value : optionGet },\n\t\t\t\tpop     : { value : optionPop },\n\t\t\t\tpush    : { value : optionPush }\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tParser Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Parser', {\n\t\tvalue : (() => {\n\t\t\t// Parser definition array.  Ordering matters, so this must be an ordered list.\n\t\t\tconst _parsers = [];\n\n\t\t\t// Parser profiles object.\n\t\t\tlet _profiles;\n\n\n\t\t\t/*\n\t\t\t\tParser Functions.\n\t\t\t*/\n\t\t\tfunction parsersGetter() {\n\t\t\t\treturn _parsers;\n\t\t\t}\n\n\t\t\tfunction parsersAdd(parser) {\n\t\t\t\t// Parser object sanity checks.\n\t\t\t\tif (typeof parser !== 'object') {\n\t\t\t\t\tthrow new Error('Wikifier.Parser.add parser parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('name')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"name\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.name !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"name\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('match')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"match\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.match !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"match\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('handler')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"handler\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.handler !== 'function') {\n\t\t\t\t\tthrow new Error('parser object \"handler\" property must be a function');\n\t\t\t\t}\n\n\t\t\t\tif (parser.hasOwnProperty('profiles') && !Array.isArray(parser.profiles)) {\n\t\t\t\t\tthrow new Error('parser object \"profiles\" property must be an array');\n\t\t\t\t}\n\n\t\t\t\t// Check for an existing parser with the same name.\n\t\t\t\tif (parsersHas(parser.name)) {\n\t\t\t\t\tthrow new Error(`cannot clobber existing parser \"${parser.name}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Add the parser to the end of the array.\n\t\t\t\t_parsers.push(parser);\n\t\t\t}\n\n\t\t\tfunction parsersDelete(name) {\n\t\t\t\tconst parser = _parsers.find(parser => parser.name === name);\n\n\t\t\t\tif (parser) {\n\t\t\t\t\t_parsers.delete(parser);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction parsersIsEmpty() {\n\t\t\t\treturn _parsers.length === 0;\n\t\t\t}\n\n\t\t\tfunction parsersHas(name) {\n\t\t\t\treturn !!_parsers.find(parser => parser.name === name);\n\t\t\t}\n\n\t\t\tfunction parsersGet(name) {\n\t\t\t\treturn _parsers.find(parser => parser.name === name) || null;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tParser Profile Functions.\n\t\t\t*/\n\t\t\tfunction profilesGetter() {\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesCompile() {\n\t\t\t\tif (DEBUG) { console.log('[Wikifier.Parser/profilesCompile()]'); }\n\n\t\t\t\tconst all  = _parsers;\n\t\t\t\tconst core = all.filter(parser => !Array.isArray(parser.profiles) || parser.profiles.includes('core'));\n\n\t\t\t\t_profiles = Object.freeze({\n\t\t\t\t\tall : {\n\t\t\t\t\t\tparsers      : all,\n\t\t\t\t\t\tparserRegExp : new RegExp(all.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t},\n\t\t\t\t\tcore : {\n\t\t\t\t\t\tparsers      : core,\n\t\t\t\t\t\tparserRegExp : new RegExp(core.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesIsEmpty() {\n\t\t\t\treturn typeof _profiles !== 'object' || Object.keys(_profiles).length === 0;\n\t\t\t}\n\n\t\t\tfunction profilesGet(profile) {\n\t\t\t\tif (typeof _profiles !== 'object' || !_profiles.hasOwnProperty(profile)) {\n\t\t\t\t\tthrow new Error(`nonexistent parser profile \"${profile}\"`);\n\t\t\t\t}\n\n\t\t\t\treturn _profiles[profile];\n\t\t\t}\n\n\t\t\tfunction profilesHas(profile) {\n\t\t\t\treturn typeof _profiles === 'object' && _profiles.hasOwnProperty(profile);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\t/*\n\t\t\t\t\tParser Containers.\n\t\t\t\t*/\n\t\t\t\tparsers : { get : parsersGetter },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Functions.\n\t\t\t\t*/\n\t\t\t\tadd     : { value : parsersAdd },\n\t\t\t\tdelete  : { value : parsersDelete },\n\t\t\t\tisEmpty : { value : parsersIsEmpty },\n\t\t\t\thas     : { value : parsersHas },\n\t\t\t\tget     : { value : parsersGet },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Profile.\n\t\t\t\t*/\n\t\t\t\tProfile : {\n\t\t\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Containers.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tprofiles : { get : profilesGetter },\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Functions.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tcompile : { value : profilesCompile },\n\t\t\t\t\t\tisEmpty : { value : profilesIsEmpty },\n\t\t\t\t\t\thas     : { value : profilesHas },\n\t\t\t\t\t\tget     : { value : profilesGet }\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAdditional Static Properties.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier, {\n\t\thelpers : { value : {} },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tgetValue       : { value : State.getVar },              // SEE: `state.js`.\n\t\tsetValue       : { value : State.setVar },              // SEE: `state.js`.\n\t\tparse          : { value : Scripting.parse },           // SEE: `markup/scripting.js`.\n\t\tevalExpression : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\ttextPrimitives : { value : Patterns }                   // SEE: `lib/patterns.js`.\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Static Methods.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier.helpers, {\n\t\tinlineCss : {\n\t\t\tvalue : (() => {\n\t\t\t\tconst lookaheadRe = new RegExp(Patterns.inlineCss, 'gm');\n\t\t\t\tconst idOrClassRe = new RegExp(`(${Patterns.cssIdOrClassSigil})(${Patterns.anyLetter}+)`, 'g');\n\n\t\t\t\tfunction helperInlineCss(w) {\n\t\t\t\t\tconst css = { classes : [], id : '', styles : {} };\n\t\t\t\t\tlet matched;\n\n\t\t\t\t\tdo {\n\t\t\t\t\t\tlookaheadRe.lastIndex = w.nextMatch;\n\n\t\t\t\t\t\tconst match = lookaheadRe.exec(w.source);\n\n\t\t\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\tif (match[1]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[1])] = match[2].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[3]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[3])] = match[4].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[5]) {\n\t\t\t\t\t\t\t\tlet subMatch;\n\n\t\t\t\t\t\t\t\tidOrClassRe.lastIndex = 0; // NOTE: Guard against buggy implementations.\n\n\t\t\t\t\t\t\t\twhile ((subMatch = idOrClassRe.exec(match[5])) !== null) {\n\t\t\t\t\t\t\t\t\tif (subMatch[1] === '.') {\n\t\t\t\t\t\t\t\t\t\tcss.classes.push(subMatch[2]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tcss.id = subMatch[2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tw.nextMatch = lookaheadRe.lastIndex; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (matched);\n\n\t\t\t\t\treturn css;\n\t\t\t\t}\n\n\t\t\t\treturn helperInlineCss;\n\t\t\t})()\n\t\t},\n\n\t\tevalText : {\n\t\t\tvalue(text) {\n\t\t\t\tlet result;\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = Scripting.evalTwineScript(text);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAttempt to prevent the leakage of auto-globals by enforcing that\n\t\t\t\t\t\tthe resultant value be either a string or a number.\n\n\t\t\t\t\t\tNOTE: This is not a foolproof solution to the problem of auto-global\n\t\t\t\t\t\tleakage.  Various auto-globals, which return strings or numbers, can\n\t\t\t\t\t\tstill leak through—e.g. `window.status` → string.\n\t\t\t\t\t*/\n\t\t\t\t\tswitch (typeof result) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tif (result.trim() === '') {\n\t\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tresult = String(result);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tresult = text;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t},\n\n\t\tevalPassageId : {\n\t\t\tvalue(passage) {\n\t\t\t\tif (passage == null || Story.has(passage)) { // lazy equality for null; `0` is a valid name, so we cannot simply evaluate `passage`\n\t\t\t\t\treturn passage;\n\t\t\t\t}\n\n\t\t\t\treturn Wikifier.helpers.evalText(passage);\n\t\t\t}\n\t\t},\n\n\t\thasBlockContext : {\n\t\t\tvalue(nodes) {\n\t\t\t\tconst hasGCS = typeof window.getComputedStyle === 'function';\n\n\t\t\t\tfor (let i = nodes.length - 1; i >= 0; --i) {\n\t\t\t\t\tconst node = nodes[i];\n\n\t\t\t\t\tswitch (node.nodeType) {\n\t\t\t\t\tcase Node.ELEMENT_NODE:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\t\t\t\tif (tagName === 'BR') {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst styles = hasGCS ? window.getComputedStyle(node, null) : node.currentStyle;\n\n\t\t\t\t\t\t\tif (styles && styles.display) {\n\t\t\t\t\t\t\t\tif (styles.display === 'none') {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn styles.display === 'block';\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWebKit/Blink-based browsers do not attach any computed style\n\t\t\t\t\t\t\t\tinformation to elements until they're inserted into the DOM\n\t\t\t\t\t\t\t\t(and probably visible), not even the default browser styles\n\t\t\t\t\t\t\t\tand any user styles.  So, we make an assumption based on the\n\t\t\t\t\t\t\t\telement.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\t\tcase 'ADDRESS':\n\t\t\t\t\t\t\tcase 'ARTICLE':\n\t\t\t\t\t\t\tcase 'ASIDE':\n\t\t\t\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\t\t\t\tcase 'CENTER':\n\t\t\t\t\t\t\tcase 'DIV':\n\t\t\t\t\t\t\tcase 'DL':\n\t\t\t\t\t\t\tcase 'FIGURE':\n\t\t\t\t\t\t\tcase 'FOOTER':\n\t\t\t\t\t\t\tcase 'FORM':\n\t\t\t\t\t\t\tcase 'H1':\n\t\t\t\t\t\t\tcase 'H2':\n\t\t\t\t\t\t\tcase 'H3':\n\t\t\t\t\t\t\tcase 'H4':\n\t\t\t\t\t\t\tcase 'H5':\n\t\t\t\t\t\t\tcase 'H6':\n\t\t\t\t\t\t\tcase 'HEADER':\n\t\t\t\t\t\t\tcase 'HR':\n\t\t\t\t\t\t\tcase 'MAIN':\n\t\t\t\t\t\t\tcase 'NAV':\n\t\t\t\t\t\t\tcase 'OL':\n\t\t\t\t\t\t\tcase 'P':\n\t\t\t\t\t\t\tcase 'PRE':\n\t\t\t\t\t\t\tcase 'SECTION':\n\t\t\t\t\t\t\tcase 'TABLE':\n\t\t\t\t\t\t\tcase 'UL':\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tcase Node.COMMENT_NODE:\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\n\t\tcreateShadowSetterCallback : {\n\t\t\tvalue : (() => {\n\t\t\t\tlet macroParser = null;\n\n\t\t\t\tfunction cacheMacroParser() {\n\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\tmacroParser = Wikifier.Parser.get('macro');\n\n\t\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\t\tthrow new Error('cannot find \"macro\" parser');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn macroParser;\n\t\t\t\t}\n\n\t\t\t\tfunction getMacroContextShadowView() {\n\t\t\t\t\tconst macro = macroParser || cacheMacroParser();\n\t\t\t\t\tconst view  = new Set();\n\n\t\t\t\t\tfor (let context = macro.context; context !== null; context = context.parent) {\n\t\t\t\t\t\tif (context._shadows) {\n\t\t\t\t\t\t\tcontext._shadows.forEach(name => view.add(name));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [...view];\n\t\t\t\t}\n\n\t\t\t\tfunction helperCreateShadowSetterCallback(code) {\n\t\t\t\t\tconst shadowStore = {};\n\n\t\t\t\t\tgetMacroContextShadowView().forEach(varName => {\n\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t\t});\n\n\t\t\t\t\treturn function () {\n\t\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\t\tevaluation.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Evaluate the JavaScript.\n\t\t\t\t\t\t\treturn Scripting.evalJavaScript(code);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn helperCreateShadowSetterCallback;\n\t\t\t})()\n\t\t},\n\n\t\tparseSquareBracketedMarkup : {\n\t\t\tvalue : (() => {\n\t\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t\t'Error',     // error\n\t\t\t\t\t'DelimLTR',  // '|' or '->'\n\t\t\t\t\t'DelimRTL',  // '<-'\n\t\t\t\t\t'InnerMeta', // ']['\n\t\t\t\t\t'ImageMeta', // '[img[', '[<img[', or '[>img['\n\t\t\t\t\t'LinkMeta',  // '[['\n\t\t\t\t\t'Link',      // link destination\n\t\t\t\t\t'RightMeta', // ']]'\n\t\t\t\t\t'Setter',    // setter expression\n\t\t\t\t\t'Source',    // image source\n\t\t\t\t\t'Text'       // link text or image alt text\n\t\t\t\t]);\n\t\t\t\tconst Delim = Lexer.enumFromNames([ // delimiter state object (pseudo-enumeration)\n\t\t\t\t\t'None', // no delimiter encountered\n\t\t\t\t\t'LTR',  // '|' or '->'\n\t\t\t\t\t'RTL'   // '<-'\n\t\t\t\t]);\n\n\t\t\t\t// Lexing functions.\n\t\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\t\tloop: for (;;) {\n\t\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* falls through */\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t\t}\n\n\t\t\t\t\treturn lexer.pos;\n\t\t\t\t}\n\n\t\t\t\tfunction lexLeftMeta(lexer) {\n\t\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t}\n\n\t\t\t\t\t// Is link markup.\n\t\t\t\t\tif (lexer.accept('[')) {\n\t\t\t\t\t\tlexer.data.isLink = true;\n\t\t\t\t\t\tlexer.emit(Item.LinkMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\t// May be image markup.\n\t\t\t\t\telse {\n\t\t\t\t\t\tlexer.accept('<>'); // aligner syntax\n\n\t\t\t\t\t\tif (!lexer.accept('Ii') || !lexer.accept('Mm') || !lexer.accept('Gg') || !lexer.accept('[')) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlexer.data.isLink = false;\n\t\t\t\t\t\tlexer.emit(Item.ImageMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\t\t\t\t\treturn lexCoreComponents;\n\t\t\t\t}\n\n\t\t\t\tfunction lexCoreComponents(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\t\t\t\t\tlet delim = Delim.None;\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '|': // possible pipe ('|') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None) {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward();\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '-': // possible right arrow ('->') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '>') {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '<': // possible left arrow ('<-') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '-') {\n\t\t\t\t\t\t\t\tdelim = Delim.RTL;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimRTL);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexer.data.isLink ? lexSetter : lexImageLink;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexImageLink(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup link component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexSetter;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexSetter(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated single quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tif (lexer.peek() !== ']') {\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Setter);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Parse function.\n\t\t\t\tfunction parseSquareBracketedMarkup(w) {\n\t\t\t\t\t// Initialize the lexer.\n\t\t\t\t\tconst lexer  = new Lexer(w.source, lexLeftMeta);\n\n\t\t\t\t\t// Set the initial positions within the source string.\n\t\t\t\t\tlexer.start = lexer.pos = w.matchStart;\n\n\t\t\t\t\t// Lex the raw argument string.\n\t\t\t\t\tconst markup = {};\n\t\t\t\t\tconst items  = lexer.run();\n\t\t\t\t\tconst last   = items.last();\n\n\t\t\t\t\tif (last && last.type === Item.Error) {\n\t\t\t\t\t\tmarkup.error = last.message;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\titems.forEach(item => {\n\t\t\t\t\t\t\tconst text = item.text.trim();\n\n\t\t\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\t\t\tcase Item.ImageMeta:\n\t\t\t\t\t\t\t\tmarkup.isImage = true;\n\n\t\t\t\t\t\t\t\tif (text[1] === '<') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'left';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (text[1] === '>') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'right';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.LinkMeta:\n\t\t\t\t\t\t\t\tmarkup.isLink = true;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Link:\n\t\t\t\t\t\t\t\tif (text[0] === '~') {\n\t\t\t\t\t\t\t\t\tmarkup.forceInternal = true;\n\t\t\t\t\t\t\t\t\tmarkup.link = text.slice(1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tmarkup.link = text;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Setter:\n\t\t\t\t\t\t\t\tmarkup.setter = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Source:\n\t\t\t\t\t\t\t\tmarkup.source = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Text:\n\t\t\t\t\t\t\t\tmarkup.text = text;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tmarkup.pos = lexer.pos;\n\t\t\t\t\treturn markup;\n\t\t\t\t}\n\n\t\t\t\treturn parseSquareBracketedMarkup;\n\t\t\t\t/* eslint-enable no-param-reassign */\n\t\t\t})()\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Wikifier;\n})();\n/* eslint-enable max-len */\n\n/***********************************************************************************************************************\n\n\tmarkup/parserlib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, EOF, Engine, Lexer, Macro, MacroContext, Patterns, Scripting, State, Story, Template,\n\t       Wikifier, toStringOrDefault, throwError\n*/\n/* eslint \"no-param-reassign\": [ 2, { \"props\" : false } ] */\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _verbatimTagHandler(w) {\n\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\tconst match = this.lookahead.exec(w.source);\n\n\t\tif (match && match.index === w.matchStart) {\n\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(match[1])\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tParsers.\n\t*******************************************************************************************************************/\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByBlock',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^<<<\\\\n',\n\t\tterminator : '^<<<\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t.appendTo(w.output)\n\t\t\t\t\t.get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByLine',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^>+',\n\t\tlookahead  : /^>+/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curLevel = 0;\n\t\t\tlet newLevel = w.matchLength;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcurLevel = newLevel;\n\t\t\t\tw.subWikify(destStack[destStack.length - 1], this.terminator);\n\t\t\t\tjQuery(document.createElement('br')).appendTo(destStack[destStack.length - 1]);\n\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tnewLevel = match[0].length;\n\t\t\t\t\tw.nextMatch += match[0].length;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'macro',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<<',\n\t\tlookahead : new RegExp(`<<(/?${Patterns.macroName})(?:\\\\s*)((?:(?:\\`(?:\\\\\\\\.|[^\\`\\\\\\\\])*\\`)|(?:\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")|(?:'(?:\\\\\\\\.|[^'\\\\\\\\])*')|(?:\\\\[(?:[<>]?[Ii][Mm][Gg])?\\\\[[^\\\\r\\\\n]*?\\\\]\\\\]+)|[^>]|(?:>(?!>)))*)>>`, 'gm'),\n\t\tworking   : { source : '', name : '', arguments : '', index : 0 }, // the working parse object\n\t\tcontext   : null, // last execution context object (top-level macros, hierarchically, have a null context)\n\n\t\thandler(w) {\n\t\t\tconst matchStart = this.lookahead.lastIndex = w.matchStart;\n\n\t\t\tif (this.parseTag(w)) {\n\t\t\t\t/*\n\t\t\t\t\tIf `parseBody()` is called below, it will modify the current working\n\t\t\t\t\tvalues, so we must cache them now.\n\t\t\t\t*/\n\t\t\t\tconst nextMatch = w.nextMatch;\n\t\t\t\tconst name      = this.working.name;\n\t\t\t\tconst rawArgs   = this.working.arguments;\n\t\t\t\tlet macro;\n\n\t\t\t\ttry {\n\t\t\t\t\tmacro = Macro.get(name);\n\n\t\t\t\t\tif (macro) {\n\t\t\t\t\t\tlet payload = null;\n\n\t\t\t\t\t\tif (typeof macro.tags !== 'undefined') {\n\t\t\t\t\t\t\tpayload = this.parseBody(w, macro);\n\n\t\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\t\tw.nextMatch = nextMatch; // we must reset `w.nextMatch` here, as `parseBody()` modifies it\n\t\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t\t`cannot find a closing tag for macro <<${name}>>`,\n\t\t\t\t\t\t\t\t\t`${w.source.slice(matchStart, w.nextMatch)}\\u2026`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (typeof macro.handler === 'function') {\n\t\t\t\t\t\t\tconst args = !payload\n\t\t\t\t\t\t\t\t? this.createArgs(rawArgs, this.skipArgs(macro, macro.name))\n\t\t\t\t\t\t\t\t: payload[0].args;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tNew-style macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (typeof macro._MACRO_API !== 'undefined') {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tAdd the macro's execution context to the context chain.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tthis.context = new MacroContext({\n\t\t\t\t\t\t\t\t\tmacro,\n\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\t\t\tsource : w.source.slice(matchStart, w.nextMatch),\n\t\t\t\t\t\t\t\t\tparent : this.context,\n\t\t\t\t\t\t\t\t\tparser : w\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the execution context is properly restored in the event\n\t\t\t\t\t\t\t\t\tthat an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler.call(this.context);\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\tQUESTION: Swap to the following, which passes macro arguments in\n\t\t\t\t\t\t\t\t\t\tas parameters to the handler function, in addition to them being\n\t\t\t\t\t\t\t\t\t\tavailable on its `this`?  If so, it might still be something to\n\t\t\t\t\t\t\t\t\t\thold off on until v3, when the legacy macro API is removed.\n\n\t\t\t\t\t\t\t\t\t\tmacro.handler.apply(this.context, this.context.args);\n\t\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tthis.context = this.context.parent;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t[DEPRECATED] Old-style/legacy macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tSet up the raw arguments string.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tconst prevRawArgs = w._rawArgs;\n\t\t\t\t\t\t\t\tw._rawArgs = rawArgs;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the previous raw arguments string is properly restored in\n\t\t\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler(w.output, name, args, w, payload);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tw._rawArgs = prevRawArgs;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`macro <<${name}>> handler function ${typeof macro.handler === 'undefined' ? 'does not exist' : 'is not a function'}`,\n\t\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (Macro.tags.has(name)) {\n\t\t\t\t\t\tconst tags = Macro.tags.get(name);\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`child tag <<${name}>> was found outside of a call to its parent macro${tags.length === 1 ? '' : 's'} <<${tags.join('>>, <<')}>>`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`macro <<${name}>> does not exist`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute ${macro && macro.isWidget ? 'widget' : 'macro'} <<${name}>>: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.working.source    = '';\n\t\t\t\t\tthis.working.name      = '';\n\t\t\t\t\tthis.working.arguments = '';\n\t\t\t\t\tthis.working.index     = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t}\n\t\t},\n\n\t\tparseTag(w) {\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart && match[1]) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tthis.working.source    = w.source.slice(match.index, this.lookahead.lastIndex);\n\t\t\t\tthis.working.name      = match[1];\n\t\t\t\tthis.working.arguments = match[2];\n\t\t\t\tthis.working.index     = match.index;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseBody(w, macro) {\n\t\t\tconst openTag  = this.working.name;\n\t\t\tconst closeTag = `/${openTag}`;\n\t\t\tconst closeAlt = `end${openTag}`;\n\t\t\tconst bodyTags = Array.isArray(macro.tags) ? macro.tags : false;\n\t\t\tconst payload  = [];\n\t\t\tlet end          = -1;\n\t\t\tlet opened       = 1;\n\t\t\tlet curSource    = this.working.source;\n\t\t\tlet curTag       = this.working.name;\n\t\t\tlet curArgument  = this.working.arguments;\n\t\t\tlet contentStart = w.nextMatch;\n\n\t\t\twhile ((w.matchStart = w.source.indexOf(this.match, w.nextMatch)) !== -1) {\n\t\t\t\tif (!this.parseTag(w)) {\n\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = w.matchStart + this.match.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst tagSource = this.working.source;\n\t\t\t\tconst tagName   = this.working.name;\n\t\t\t\tconst tagArgs   = this.working.arguments;\n\t\t\t\tconst tagBegin  = this.working.index;\n\t\t\t\tconst tagEnd    = w.nextMatch;\n\t\t\t\tconst hasArgs   = tagArgs.trim() !== '';\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase openTag:\n\t\t\t\t\t++opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase closeAlt:\n\t\t\t\tcase closeTag:\n\t\t\t\t\tif (hasArgs) {\n\t\t\t\t\t\t// Skip over malformed closing tags and throw.\n\t\t\t\t\t\tw.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tthrow new Error(`malformed closing tag: \"${tagSource}\"`);\n\t\t\t\t\t}\n\t\t\t\t\t--opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (hasArgs && (tagName.startsWith('/') || tagName.startsWith('end'))) {\n\t\t\t\t\t\t// Skip over malformed alien closing tags.\n\t\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (opened === 1 && bodyTags) {\n\t\t\t\t\t\tfor (let i = 0, iend = bodyTags.length; i < iend; ++i) {\n\t\t\t\t\t\t\tif (tagName === bodyTags[i]) {\n\t\t\t\t\t\t\t\tpayload.push({\n\t\t\t\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tcurSource    = tagSource;\n\t\t\t\t\t\t\t\tcurTag       = tagName;\n\t\t\t\t\t\t\t\tcurArgument  = tagArgs;\n\t\t\t\t\t\t\t\tcontentStart = tagEnd;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (opened === 0) {\n\t\t\t\t\tpayload.push({\n\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t});\n\t\t\t\t\tend = tagEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (end !== -1) {\n\t\t\t\tw.nextMatch = end;\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tcreateArgs(rawArgsString, skipArgs) {\n\t\t\tconst args = skipArgs ? [] : this.parseArgs(rawArgsString);\n\n\t\t\t// Extend the args array with the raw and full argument strings.\n\t\t\tObject.defineProperties(args, {\n\t\t\t\traw : {\n\t\t\t\t\tvalue : rawArgsString\n\t\t\t\t},\n\t\t\t\tfull : {\n\t\t\t\t\tvalue : Scripting.parse(rawArgsString)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn args;\n\t\t},\n\n\t\tskipArgs(macro, tagName) {\n\t\t\tif (typeof macro.skipArgs !== 'undefined') {\n\t\t\t\tconst sa = macro.skipArgs;\n\n\t\t\t\treturn typeof sa === 'boolean' && sa || Array.isArray(sa) && sa.includes(tagName);\n\t\t\t}\n\t\t\t/* legacy */\n\t\t\telse if (typeof macro.skipArg0 !== 'undefined') {\n\t\t\t\treturn macro.skipArg0 && macro.name === tagName;\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseArgs : (() => {\n\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t'Error',        // error\n\t\t\t\t'Bareword',     // bare identifier\n\t\t\t\t'Expression',   // expression (backquoted)\n\t\t\t\t'String',       // quoted string (single or double)\n\t\t\t\t'SquareBracket' // [[…]] or [img[…]]\n\t\t\t]);\n\t\t\tconst spaceRe    = new RegExp(Patterns.space);\n\t\t\tconst notSpaceRe = new RegExp(Patterns.notSpace);\n\t\t\tconst varTest    = new RegExp(`^${Patterns.variable}`);\n\n\t\t\t// Lexing functions.\n\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\treturn lexer.pos;\n\t\t\t}\n\n\t\t\tfunction lexSpace(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(notSpaceRe);\n\n\t\t\t\tif (offset === EOF) {\n\t\t\t\t\t// no non-whitespace characters, so bail\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\telse if (offset !== 0) {\n\t\t\t\t\tlexer.pos += offset;\n\t\t\t\t\tlexer.ignore();\n\t\t\t\t}\n\n\t\t\t\t// determine what the next state is\n\t\t\t\tswitch (lexer.next()) {\n\t\t\t\tcase '`':\n\t\t\t\t\treturn lexExpression;\n\t\t\t\tcase '\"':\n\t\t\t\t\treturn lexDoubleQuote;\n\t\t\t\tcase \"'\":\n\t\t\t\t\treturn lexSingleQuote;\n\t\t\t\tcase '[':\n\t\t\t\t\treturn lexSquareBracket;\n\t\t\t\tdefault:\n\t\t\t\t\treturn lexBareword;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction lexExpression(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '`') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated backquote expression');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.Expression);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexDoubleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated double quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSingleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated single quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSquareBracket(lexer) {\n\t\t\t\tconst imgMeta = '<>IiMmGg';\n\t\t\t\tlet what;\n\n\t\t\t\tif (lexer.accept(imgMeta)) {\n\t\t\t\t\twhat = 'image';\n\t\t\t\t\tlexer.acceptRun(imgMeta);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\twhat = 'link';\n\t\t\t\t}\n\n\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t}\n\n\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\tcase '[':\n\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ']':\n\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\tif (lexer.depth < 0) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, \"unexpected right square bracket ']'\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\tif (lexer.next() === ']') {\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.SquareBracket);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexBareword(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(spaceRe);\n\t\t\t\tlexer.pos = offset === EOF ? lexer.source.length : lexer.pos + offset;\n\t\t\t\tlexer.emit(Item.Bareword);\n\t\t\t\treturn offset === EOF ? null : lexSpace;\n\t\t\t}\n\n\t\t\t// Parse function.\n\t\t\tfunction parseMacroArgs(rawArgsString) {\n\t\t\t\t// Initialize the lexer.\n\t\t\t\tconst lexer = new Lexer(rawArgsString, lexSpace);\n\t\t\t\tconst args  = [];\n\n\t\t\t\t// Lex the raw argument string.\n\t\t\t\tlexer.run().forEach(item => {\n\t\t\t\t\tlet arg = item.text;\n\n\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\tcase Item.Error:\n\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${item.message}`);\n\n\t\t\t\t\tcase Item.Bareword:\n\t\t\t\t\t\t// A variable, so substitute its value.\n\t\t\t\t\t\tif (varTest.test(arg)) {\n\t\t\t\t\t\t\targ = State.getVar(arg);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Property access on the settings or setup objects, so try to evaluate it.\n\t\t\t\t\t\telse if (/^(?:settings|setup)[.[]/.test(arg)) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(arg);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Null literal, so convert it into null.\n\t\t\t\t\t\telse if (arg === 'null') {\n\t\t\t\t\t\t\targ = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Undefined literal, so convert it into undefined.\n\t\t\t\t\t\telse if (arg === 'undefined') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean true literal, so convert it into true.\n\t\t\t\t\t\telse if (arg === 'true') {\n\t\t\t\t\t\t\targ = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean false literal, so convert it into false.\n\t\t\t\t\t\telse if (arg === 'false') {\n\t\t\t\t\t\t\targ = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// NaN literal, so convert it into NaN.\n\t\t\t\t\t\telse if (arg === 'NaN') {\n\t\t\t\t\t\t\targ = NaN;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Attempt to convert it into a number, in case it's a numeric literal.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst argAsNum = Number(arg);\n\n\t\t\t\t\t\t\tif (!Number.isNaN(argAsNum)) {\n\t\t\t\t\t\t\t\targ = argAsNum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.Expression:\n\t\t\t\t\t\targ = arg.slice(1, -1).trim(); // remove the backquotes and trim the expression\n\n\t\t\t\t\t\t// Empty backquotes.\n\t\t\t\t\t\tif (arg === '') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Evaluate the expression.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tThe enclosing parenthesis here are necessary to force a code string\n\t\t\t\t\t\t\t\t\tconsisting solely of an object literal to be evaluated as such, rather\n\t\t\t\t\t\t\t\t\tthan as a code block.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(`(${arg})`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument expression \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.String:\n\t\t\t\t\t\t// Evaluate the string to handle escaped characters.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targ = Scripting.evalJavaScript(arg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument string \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.SquareBracket:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\t\tsource     : arg,\n\t\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${markup.error}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (markup.pos < arg.length) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": unexpected character(s) \"${arg.slice(markup.pos)}\" (pos: ${markup.pos})`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Convert to a link or image object.\n\t\t\t\t\t\t\tif (markup.isLink) {\n\t\t\t\t\t\t\t\t// .isLink, [.text], [.forceInternal], .link, [.setter]\n\t\t\t\t\t\t\t\targ = { isLink : true };\n\t\t\t\t\t\t\t\targ.count    = markup.hasOwnProperty('text') ? 2 : 1;\n\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\targ.text     = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : arg.link;\n\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\targ.setFn    = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (markup.isImage) {\n\t\t\t\t\t\t\t\t// .isImage, [.align], [.title], .source, [.forceInternal], [.link], [.setter]\n\t\t\t\t\t\t\t\targ = (source => {\n\t\t\t\t\t\t\t\t\tconst imgObj = {\n\t\t\t\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t\t\t\tisImage : true\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t// Check for Twine 1.4 Base64 image passage transclusion.\n\t\t\t\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\t\t\t\timgObj.source  = passage.text;\n\t\t\t\t\t\t\t\t\t\t\timgObj.passage = passage.title;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn imgObj;\n\t\t\t\t\t\t\t\t})(Wikifier.helpers.evalPassageId(markup.source));\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\t\t\t\t\t\targ.align = markup.align;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\t\t\t\t\t\targ.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\targ.setFn = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\targs.push(arg);\n\t\t\t\t});\n\n\t\t\t\treturn args;\n\t\t\t}\n\n\t\t\treturn parseMacroArgs;\n\t\t})()\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'link',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[\\\\[[^[]',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// text=(text), forceInternal=(~), link=link, setter=(setter)\n\t\t\tconst link  = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\tconst text  = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : link;\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\n\t\t\t// Debug view setup.\n\t\t\tconst output = (Config.debug\n\t\t\t\t? new DebugView(w.output, 'link-markup', '[[link]]', w.source.slice(w.matchStart, w.nextMatch))\n\t\t\t\t: w\n\t\t\t).output;\n\n\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\tWikifier.createInternalLink(output, link, text, setFn);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tWikifier.createExternalLink(output, link, text);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'urlLink',\n\t\tprofiles : ['core'],\n\t\tmatch    : Patterns.url,\n\n\t\thandler(w) {\n\t\t\tw.outputText(Wikifier.createExternalLink(w.output, w.matchText), w.matchStart, w.nextMatch);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'image',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[[<>]?[Ii][Mm][Gg]\\\\[',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// Debug view setup.\n\t\t\tlet debugView;\n\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tw.output,\n\t\t\t\t\t'image-markup',\n\t\t\t\t\tmarkup.hasOwnProperty('link') ? '[img[][link]]' : '[img[]]',\n\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// align=(left|right), title=(title), source=source, forceInternal=(~), link=(link), setter=(setter)\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\t\t\tlet el     = (Config.debug ? debugView : w).output;\n\t\t\tlet source;\n\n\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\tconst link = Wikifier.helpers.evalPassageId(markup.link);\n\n\t\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\t\tel = Wikifier.createInternalLink(el, link, null, setFn);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tel = Wikifier.createExternalLink(el, link);\n\t\t\t\t}\n\n\t\t\t\tel.classList.add('link-image');\n\t\t\t}\n\n\t\t\tel = jQuery(document.createElement('img'))\n\t\t\t\t.appendTo(el)\n\t\t\t\t.get(0);\n\t\t\tsource = Wikifier.helpers.evalPassageId(markup.source);\n\n\t\t\t// Check for image passage transclusion.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\tel.setAttribute('data-passage', passage.title);\n\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tel.src = source;\n\n\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\tel.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t}\n\n\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\tel.align = markup.align;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'monospacedByBlock',\n\t\tprofiles  : ['block'],\n\t\tmatch     : '^\\\\{\\\\{\\\\{\\\\n',\n\t\tlookahead : /^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tconst pre = jQuery(document.createElement('pre'));\n\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t.text(match[1])\n\t\t\t\t\t.appendTo(pre);\n\t\t\t\tpre.appendTo(w.output);\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'formatByChar',\n\t\tprofiles : ['core'],\n\t\tmatch    : \"''|//|__|\\\\^\\\\^|~~|==|\\\\{\\\\{\\\\{\",\n\n\t\thandler(w) {\n\t\t\tswitch (w.matchText) {\n\t\t\tcase \"''\":\n\t\t\t\tw.subWikify(jQuery(document.createElement('strong')).appendTo(w.output).get(0), \"''\");\n\t\t\t\tbreak;\n\n\t\t\tcase '//':\n\t\t\t\tw.subWikify(jQuery(document.createElement('em')).appendTo(w.output).get(0), '//');\n\t\t\t\tbreak;\n\n\t\t\tcase '__':\n\t\t\t\tw.subWikify(jQuery(document.createElement('u')).appendTo(w.output).get(0), '__');\n\t\t\t\tbreak;\n\n\t\t\tcase '^^':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sup')).appendTo(w.output).get(0), '\\\\^\\\\^');\n\t\t\t\tbreak;\n\n\t\t\tcase '~~':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sub')).appendTo(w.output).get(0), '~~');\n\t\t\t\tbreak;\n\n\t\t\tcase '==':\n\t\t\t\tw.subWikify(jQuery(document.createElement('s')).appendTo(w.output).get(0), '==');\n\t\t\t\tbreak;\n\n\t\t\tcase '{{{':\n\t\t\t\t{\n\t\t\t\t\tconst lookahead = /\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}/gm;\n\n\t\t\t\t\tlookahead.lastIndex = w.matchStart;\n\n\t\t\t\t\tconst match = lookahead.exec(w.source);\n\n\t\t\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t\t\t.text(match[1])\n\t\t\t\t\t\t\t.appendTo(w.output);\n\t\t\t\t\t\tw.nextMatch = lookahead.lastIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'customStyle',\n\t\tprofiles   : ['core'],\n\t\tmatch      : '@@',\n\t\tterminator : '@@',\n\t\tblockRe    : /\\s*\\n/gm,\n\n\t\thandler(w) {\n\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\n\t\t\tthis.blockRe.lastIndex = w.nextMatch; // must follow the call to `inlineCss()`\n\n\t\t\tconst blockMatch = this.blockRe.exec(w.source);\n\t\t\tconst blockLevel = blockMatch && blockMatch.index === w.nextMatch;\n\t\t\tconst $el        = jQuery(document.createElement(blockLevel ? 'div' : 'span'))\n\t\t\t\t.appendTo(w.output);\n\n\t\t\tif (css.classes.length === 0 && css.id === '' && Object.keys(css.styles).length === 0) {\n\t\t\t\t$el.addClass('marked');\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcss.classes.forEach(className => $el.addClass(className));\n\n\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t$el.attr('id', css.id);\n\t\t\t\t}\n\n\t\t\t\t$el.css(css.styles);\n\t\t\t}\n\n\t\t\tif (blockLevel) {\n\t\t\t\t// Skip the leading and, if it exists, trailing newlines.\n\t\t\t\tw.nextMatch += blockMatch[0].length;\n\t\t\t\tw.subWikify($el[0], `\\\\n?${this.terminator}`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.subWikify($el[0], this.terminator);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimText',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '\"{3}|<[Nn][Oo][Ww][Ii][Kk][Ii]>',\n\t\tlookahead : /(?:\"{3}((?:.|\\n)*?)\"{3})|(?:<[Nn][Oo][Ww][Ii][Kk][Ii]>((?:.|\\n)*?)<\\/[Nn][Oo][Ww][Ii][Kk][Ii]>)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('verbatim')\n\t\t\t\t\t.text(match[1] || match[2])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'horizontalRule',\n\t\tprofiles : ['core'],\n\t\tmatch    : '^----+$\\\\n?|<[Hh][Rr]\\\\s*/?>\\\\n?',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createElement('hr')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'emdash',\n\t\tprofiles : ['core'],\n\t\tmatch    : '--',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('\\u2014')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'doubleDollarSign',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\${2}', // eslint-disable-line no-template-curly-in-string\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('$')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tSupported syntax:\n\t\t\t\t$variable\n\t\t\t\t$variable.property\n\t\t\t\t$variable[numericIndex]\n\t\t\t\t$variable[\"property\"]\n\t\t\t\t$variable['property']\n\t\t\t\t$variable[$indexOrPropertyVariable]\n\t\t*/\n\t\tname     : 'nakedVariable',\n\t\tprofiles : ['core'],\n\t\tmatch    : `${Patterns.variable}(?:(?:\\\\.${Patterns.identifier})|(?:\\\\[\\\\d+\\\\])|(?:\\\\[\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\"\\\\])|(?:\\\\['(?:\\\\\\\\.|[^'\\\\\\\\])+'\\\\])|(?:\\\\[${Patterns.variable}\\\\]))*`,\n\n\t\thandler(w) {\n\t\t\tconst result = toStringOrDefault(State.getVar(w.matchText), null);\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'variable', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'template',\n\t\tprofiles : ['core'],\n\t\tmatch    : `\\\\?${Patterns.templateName}`,\n\n\t\thandler(w) {\n\t\t\tconst name = w.matchText.slice(1);\n\t\t\tlet template = Template.get(name);\n\t\t\tlet result   = null;\n\n\t\t\t// If we have an array of templates, randomly choose one.\n\t\t\tif (template instanceof Array) {\n\t\t\t\ttemplate = template.random();\n\t\t\t}\n\n\t\t\tswitch (typeof template) {\n\t\t\tcase 'function':\n\t\t\t\ttry {\n\t\t\t\t\tresult = toStringOrDefault(template.call({ name }), null);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute function template ?${name}: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tresult = template;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'template', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'heading',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^!{1,6}',\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement(`h${w.matchLength}`)).appendTo(w.output).get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'table',\n\t\tprofiles       : ['block'],\n\t\tmatch          : '^\\\\|(?:[^\\\\n]*)\\\\|(?:[fhck]?)$',\n\t\tlookahead      : /^\\|([^\\n]*)\\|([fhck]?)$/gm,\n\t\trowTerminator  : '\\\\|(?:[cfhk]?)$\\\\n?',\n\t\tcellPattern    : '(?:\\\\|([^\\\\n\\\\|]*)\\\\|)|(\\\\|[cfhk]?$\\\\n?)',\n\t\tcellTerminator : '(?:\\\\u0020*)\\\\|',\n\t\trowTypes       : { c : 'caption', f : 'tfoot', h : 'thead', '' : 'tbody' }, // eslint-disable-line id-length\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst table       = jQuery(document.createElement('table')).appendTo(w.output).get(0);\n\t\t\tconst prevColumns = [];\n\t\t\tlet curRowType    = null;\n\t\t\tlet $rowContainer = null;\n\t\t\tlet rowCount      = 0;\n\t\t\tlet matched;\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst nextRowType = match[2];\n\n\t\t\t\t\tif (nextRowType === 'k') {\n\t\t\t\t\t\ttable.className = match[1];\n\t\t\t\t\t\tw.nextMatch += match[0].length + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (nextRowType !== curRowType) {\n\t\t\t\t\t\t\tcurRowType = nextRowType;\n\t\t\t\t\t\t\t$rowContainer = jQuery(document.createElement(this.rowTypes[nextRowType]))\n\t\t\t\t\t\t\t\t.appendTo(table);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (curRowType === 'c') {\n\t\t\t\t\t\t\t$rowContainer.css('caption-side', rowCount === 0 ? 'top' : 'bottom');\n\t\t\t\t\t\t\tw.nextMatch += 1;\n\t\t\t\t\t\t\tw.subWikify($rowContainer[0], this.rowTerminator);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.rowHandler(\n\t\t\t\t\t\t\t\tw,\n\t\t\t\t\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t\t\t\t\t.appendTo($rowContainer)\n\t\t\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\t\t\tprevColumns\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t++rowCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t},\n\n\t\trowHandler(w, rowEl, prevColumns) {\n\t\t\tconst cellRe = new RegExp(this.cellPattern, 'gm');\n\t\t\tlet col         = 0;\n\t\t\tlet curColCount = 1;\n\t\t\tlet matched;\n\n\t\t\tdo {\n\t\t\t\tcellRe.lastIndex = w.nextMatch;\n\n\t\t\t\tconst cellMatch = cellRe.exec(w.source);\n\n\t\t\t\tmatched = cellMatch && cellMatch.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tif (cellMatch[1] === '~') {\n\t\t\t\t\t\tconst last = prevColumns[col];\n\n\t\t\t\t\t\tif (last) {\n\t\t\t\t\t\t\t++last.rowCount;\n\t\t\t\t\t\t\tlast.$element\n\t\t\t\t\t\t\t\t.attr('rowspan', last.rowCount)\n\t\t\t\t\t\t\t\t.css('vertical-align', 'middle');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[1] === '>') {\n\t\t\t\t\t\t++curColCount;\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[2]) {\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t++w.nextMatch;\n\n\t\t\t\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\t\t\t\t\t\tlet spaceLeft  = false;\n\t\t\t\t\t\tlet spaceRight = false;\n\t\t\t\t\t\tlet $cell;\n\n\t\t\t\t\t\twhile (w.source.substr(w.nextMatch, 1) === ' ') {\n\t\t\t\t\t\t\tspaceLeft = true;\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (w.source.substr(w.nextMatch, 1) === '!') {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('th')).appendTo(rowEl);\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('td')).appendTo(rowEl);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevColumns[col] = {\n\t\t\t\t\t\t\trowCount : 1,\n\t\t\t\t\t\t\t$element : $cell\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (curColCount > 1) {\n\t\t\t\t\t\t\t$cell.attr('colspan', curColCount);\n\t\t\t\t\t\t\tcurColCount = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.subWikify($cell[0], this.cellTerminator);\n\n\t\t\t\t\t\tif (w.matchText.substr(w.matchText.length - 2, 1) === ' ') {\n\t\t\t\t\t\t\tspaceRight = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcss.classes.forEach(className => $cell.addClass(className));\n\n\t\t\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t\t\t$cell.attr('id', css.id);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (spaceLeft && spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'center';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceLeft) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'right';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'left';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$cell.css(css.styles);\n\n\t\t\t\t\t\tw.nextMatch = w.nextMatch - 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'list',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^(?:(?:\\\\*+)|(?:#+))',\n\t\tlookahead  : /^(?:(\\*+)|(#+))/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curType  = null;\n\t\t\tlet curLevel = 0;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst newType  = match[2] ? 'ol' : 'ul';\n\t\t\t\t\tconst newLevel = match[0].length;\n\n\t\t\t\t\tw.nextMatch += match[0].length;\n\n\t\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel === curLevel && newType !== curType) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tcurLevel = newLevel;\n\t\t\t\t\tcurType = newType;\n\t\t\t\t\tw.subWikify(\n\t\t\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\tthis.terminator\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'commentByBlock',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '(?:/(?:%|\\\\*))|(?:<!--)',\n\t\tlookahead : /(?:\\/(%|\\*)(?:(?:.|\\n)*?)\\1\\/)|(?:<!--(?:(?:.|\\n)*?)-->)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineContinuation',\n\t\tprofiles : ['core'],\n\n\t\t// WARNING: The ordering here is important: end-of-line, start-of-line, end-of-string, start-of-string.\n\t\tmatch : `\\\\\\\\${Patterns.spaceNoTerminator}*\\\\n|\\\\n${Patterns.spaceNoTerminator}*\\\\\\\\|\\\\n?\\\\\\\\${Patterns.spaceNoTerminator}*$|^${Patterns.spaceNoTerminator}*\\\\\\\\\\\\n?`,\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineBreak',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\n|<[Bb][Rr]\\\\s*/?>',\n\n\t\thandler(w) {\n\t\t\tif (!w.options.nobr) {\n\t\t\t\tjQuery(document.createElement('br')).appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'htmlCharacterReference',\n\t\tprofiles : ['core'],\n\t\tmatch    : '(?:(?:&#?[0-9A-Za-z]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9A-Fa-f]|1D[C-Fc-f][0-9A-Fa-f]|20[D-Fd-f][0-9A-Fa-f]|FE2[0-9A-Fa-f])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[0-9A-Za-z]{2,8};)',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(w.matchText)\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'xmlProlog',\n\t\tprofiles : ['core'],\n\t\tmatch    : '<\\\\?[Xx][Mm][Ll][^>]*\\\\?>',\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimHtml',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Hh][Tt][Mm][Ll]>',\n\t\tlookahead : /<[Hh][Tt][Mm][Ll]>((?:.|\\n)*?)<\\/[Hh][Tt][Mm][Ll]>/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimScriptTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Cc][Rr][Ii][Pp][Tt][^>]*>',\n\t\tlookahead : /(<[Ss][Cc][Rr][Ii][Pp][Tt]*>(?:.|\\n)*?<\\/[Ss][Cc][Rr][Ii][Pp][Tt]>)/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'styleTag',\n\t\tprofiles       : ['core'],\n\t\tmatch          : '<[Ss][Tt][Yy][Ll][Ee][^>]*>',\n\t\tlookahead      : /(<[Ss][Tt][Yy][Ll][Ee]*>)((?:.|\\n)*?)(<\\/[Ss][Tt][Yy][Ll][Ee]>)/gm,\n\t\timageMarkup    : new RegExp(Patterns.cssImage, 'g'),\n\t\thasImageMarkup : new RegExp(Patterns.cssImage),\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tlet css = match[2];\n\n\t\t\t\t// Check for wiki image transclusion.\n\t\t\t\tif (this.hasImageMarkup.test(css)) {\n\t\t\t\t\tthis.imageMarkup.lastIndex = 0;\n\n\t\t\t\t\tcss = css.replace(this.imageMarkup, wikiImage => {\n\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\tsource = passage.text;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t\t.append(match[1] + css + match[3])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'svgTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Vv][Gg][^>]*>',\n\t\tlookahead : /(<[Ss][Vv][Gg][^>]*>(?:.|\\n)*?<\\/[Ss][Vv][Gg]>)/gm,\n\t\tnamespace : 'http://www.w3.org/2000/svg',\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tconst $frag = jQuery(document.createDocumentFragment()).append(match[1]);\n\n\t\t\t\t// Postprocess the relevant SVG element nodes.\n\t\t\t\t$frag.find('a[data-passage],image[data-passage]').each((_, el) => {\n\t\t\t\t\tconst tagName = el.tagName.toLowerCase();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`svg|<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$frag.appendTo(w.output);\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// '<image>' element, so attempt media passage transclusion.\n\t\t\t\tif (tagName === 'image') {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t// NOTE: SVG `.href` IDL attribute is read-only,\n\t\t\t\t\t\t\t// so set its `href` content attribute instead.\n\t\t\t\t\t\t\tel.setAttribute('href', passage.text.trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>'.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tNOTE: This parser MUST come after any parser which handles HTML tag-\n\t\t\tlike constructs—e.g. 'verbatimText', 'horizontalRule', 'lineBreak',\n\t\t\t'xmlProlog', 'verbatimHtml', 'verbatimSvgTag', 'verbatimScriptTag',\n\t\t\tand 'styleTag'.\n\t\t*/\n\t\tname      : 'htmlTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<\\\\w+(?:\\\\s+[^\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\s\"\\'>\\\\/=]+(?:\\\\s*=\\\\s*(?:\"[^\"]*?\"|\\'[^\\']*?\\'|[^\\\\s\"\\'=<>`]+))?)*\\\\s*\\\\/?>',\n\t\ttagRe     : /^<(\\w+)/,\n\t\tmediaTags : ['audio', 'img', 'source', 'track', 'video'], // NOTE: The `<picture>` element should not be in this list.\n\t\tnobrTags  : ['audio', 'colgroup', 'datalist', 'dl', 'figure', 'meter', 'ol', 'optgroup', 'picture', 'progress', 'ruby', 'select', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'ul', 'video'],\n\t\tvoidTags  : ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],\n\n\t\thandler(w) {\n\t\t\tconst tagMatch = this.tagRe.exec(w.matchText);\n\t\t\tconst tag      = tagMatch && tagMatch[1];\n\t\t\tconst tagName  = tag && tag.toLowerCase();\n\n\t\t\tif (tagName) {\n\t\t\t\tconst isVoid = this.voidTags.includes(tagName) || w.matchText.endsWith('/>');\n\t\t\t\tconst isNobr = this.nobrTags.includes(tagName);\n\t\t\t\tlet terminator;\n\t\t\t\tlet terminatorMatch;\n\n\t\t\t\tif (!isVoid) {\n\t\t\t\t\tterminator = `<\\\\/${tagName}\\\\s*>`;\n\n\t\t\t\t\tconst terminatorRe = new RegExp(terminator, 'gim'); // ignore case during match\n\n\t\t\t\t\tterminatorRe.lastIndex = w.matchStart;\n\t\t\t\t\tterminatorMatch = terminatorRe.exec(w.source);\n\t\t\t\t}\n\n\t\t\t\tif (isVoid || terminatorMatch) {\n\t\t\t\t\tlet output    = w.output;\n\t\t\t\t\tlet el        = document.createElement(w.output.tagName);\n\t\t\t\t\tlet debugView;\n\n\t\t\t\t\tel.innerHTML = w.matchText;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of a `while` statement here is curious, however,\n\t\t\t\t\t\tI'm hesitant to change it for fear of breaking some edge case.\n\t\t\t\t\t*/\n\t\t\t\t\twhile (el.firstChild) {\n\t\t\t\t\t\tel = el.firstChild;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\n\t\t\t\t\t\t// Debug view setup.\n\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`html-${tagName}`,\n\t\t\t\t\t\t\t\ttagName,\n\t\t\t\t\t\t\t\tw.matchText\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tdebugView.modes({\n\t\t\t\t\t\t\t\tblock   : tagName === 'img',\n\t\t\t\t\t\t\t\tnonvoid : terminatorMatch\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toutput = debugView.output;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (terminatorMatch) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists\n\t\t\t\t\t\t\tsolely to ensure that the options stack is properly restored in\n\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the call to\n\t\t\t\t\t\t\t`subWikify()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tWikifier.Option.push({ nobr : isNobr });\n\t\t\t\t\t\t\tw.subWikify(el, terminator, { ignoreTerminatorCase : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tWikifier.Option.pop();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tDebug view modification.  If the current element has any debug\n\t\t\t\t\t\t\tview descendants who have \"block\" mode set, then set its debug\n\t\t\t\t\t\t\tview to the same.  It just makes things look a bit nicer.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif (debugView && jQuery(el).find('.debug.block').length > 0) {\n\t\t\t\t\t\t\tdebugView.modes({ block : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of `cloneNode(true)` here for `<track>` elements\n\t\t\t\t\t\tis necessary to workaround a poorly understood rehoming issue.\n\t\t\t\t\t*/\n\t\t\t\t\toutput.appendChild(tagName === 'track' ? el.cloneNode(true) : el);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot find a closing tag for HTML <${tag}>`,\n\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// Media element, so attempt media passage transclusion.\n\t\t\t\tif (this.mediaTags.includes(tagName)) {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tlet parentName;\n\t\t\t\t\t\tlet twineTag;\n\n\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\tcase 'audio':\n\t\t\t\t\t\tcase 'video':\n\t\t\t\t\t\t\ttwineTag = `Twine.${tagName}`;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'img':\n\t\t\t\t\t\t\ttwineTag = 'Twine.image';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'track':\n\t\t\t\t\t\t\ttwineTag = 'Twine.vtt';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'source':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst $parent = $(el).closest('audio,picture,video');\n\n\t\t\t\t\t\t\t\tif ($parent.length) {\n\t\t\t\t\t\t\t\t\tparentName = $parent.get(0).tagName.toLowerCase();\n\t\t\t\t\t\t\t\t\ttwineTag = `Twine.${parentName === 'picture' ? 'image' : parentName}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (passage.tags.includes(twineTag)) {\n\t\t\t\t\t\t\tel[parentName === 'picture' ? 'srcset' : 'src'] = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>', '<area>', '<button>', etc.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/template.js\n\n\tCopyright © 2019–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\nvar Template = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Template definitions.\n\tconst _templates = new Map();\n\n\t// Valid template name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.templateName})$`);\n\n\t// Valid template type predicate.\n\tconst _validType = template => {\n\t\tconst templateType = typeof template;\n\t\treturn templateType === 'function' || templateType === 'string';\n\t};\n\n\n\t/*******************************************************************************\n\t\tTemplate Functions.\n\t*******************************************************************************/\n\n\tfunction templateAdd(name, template) {\n\t\tif (\n\t\t\t   !_validType(template)\n\t\t\t&& !(template instanceof Array && template.length > 0 && template.every(_validType))\n\t\t) {\n\t\t\tthrow new TypeError(`invalid template type (${name}); templates must be: functions, strings, or an array of either`);\n\t\t}\n\n\t\t(name instanceof Array ? name : [name]).forEach(name => {\n\t\t\tif (!_validNameRe.test(name)) {\n\t\t\t\tthrow new Error(`invalid template name \"${name}\"`);\n\t\t\t}\n\t\t\tif (_templates.has(name)) {\n\t\t\t\tthrow new Error(`cannot clobber existing template ?${name}`);\n\t\t\t}\n\n\t\t\t_templates.set(name, template);\n\t\t});\n\t}\n\n\tfunction templateDelete(name) {\n\t\t(name instanceof Array ? name : [name]).forEach(name => _templates.delete(name));\n\t}\n\n\tfunction templateGet(name) {\n\t\treturn _templates.has(name) ? _templates.get(name) : null;\n\t}\n\n\tfunction templateHas(name) {\n\t\treturn _templates.has(name);\n\t}\n\n\tfunction templateSize() {\n\t\treturn _templates.size;\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tadd    : { value : templateAdd },\n\t\tdelete : { value : templateDelete },\n\t\tget    : { value : templateGet },\n\t\thas    : { value : templateHas },\n\t\tsize   : { get : templateSize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macro.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Scripting, macros */\n\nvar Macro = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Macro definitions.\n\tconst _macros = {};\n\n\t// Map of all macro tags and their parents (key: 'tag name' => value: ['list of parent names']).\n\tconst _tags = {};\n\n\t// Valid macro name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.macroName})$`);\n\n\n\t/*******************************************************************************************************************\n\t\tMacros Functions.\n\t*******************************************************************************************************************/\n\tfunction macrosAdd(name, def) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosAdd(name, def));\n\t\t\treturn;\n\t\t}\n\n\t\tif (!_validNameRe.test(name)) {\n\t\t\tthrow new Error(`invalid macro name \"${name}\"`);\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing macro <<${name}>>`);\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber child tag <<${name}>> of parent macro${_tags[name].length === 1 ? '' : 's'} <<${_tags[name].join('>>, <<')}>>`);\n\t\t}\n\n\t\ttry {\n\t\t\tif (typeof def === 'object') {\n\t\t\t\t// Add the macro definition.\n\t\t\t\t//\n\t\t\t\t// NOTE: Since `macrosGet()` may return legacy macros, we add the `_MACRO_API`\n\t\t\t\t// flag to (modern) API macros, so that the macro formatter will know how to\n\t\t\t\t// call the macro.  This should be removed in v3.\n\t\t\t\t_macros[name] = Object.assign(Object.create(null), def, { _MACRO_API : true });\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Add the macro alias.\n\t\t\t\tif (macrosHas(def)) {\n\t\t\t\t\t_macros[name] = Object.create(_macros[def], {\n\t\t\t\t\t\t_ALIAS_OF : {\n\t\t\t\t\t\t\tenumerable : true,\n\t\t\t\t\t\t\tvalue      : def\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`cannot create alias of nonexistent macro <<${def}>>`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(_macros, name, { writable : false });\n\t\t}\n\t\tcatch (ex) {\n\t\t\tif (ex.name === 'TypeError') {\n\t\t\t\tthrow new Error(`cannot clobber protected macro <<${name}>>`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`unknown error when attempting to add macro <<${name}>>: [${ex.name}] ${ex.message}`);\n\t\t\t}\n\t\t}\n\n\t\t// Tags post-processing.\n\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\tif (_macros[name].tags == null) { // lazy equality for null\n\t\t\t\ttagsRegister(name);\n\t\t\t}\n\t\t\telse if (Array.isArray(_macros[name].tags)) {\n\t\t\t\ttagsRegister(name, _macros[name].tags);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`bad value for \"tags\" property of macro <<${name}>>`);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction macrosDelete(name) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosDelete(name));\n\t\t\treturn;\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\t// Tags pre-processing.\n\t\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\t\ttagsUnregister(name);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Remove the macro definition.\n\t\t\t\tObject.defineProperty(_macros, name, { writable : true });\n\t\t\t\tdelete _macros[name];\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tthrow new Error(`unknown error removing macro <<${name}>>: ${ex.message}`);\n\t\t\t}\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot remove child tag <<${name}>> of parent macro <<${_tags[name]}>>`);\n\t\t}\n\t}\n\n\tfunction macrosIsEmpty() {\n\t\treturn Object.keys(_macros).length === 0;\n\t}\n\n\tfunction macrosHas(name) {\n\t\treturn _macros.hasOwnProperty(name);\n\t}\n\n\tfunction macrosGet(name) {\n\t\tlet macro = null;\n\n\t\tif (macrosHas(name) && typeof _macros[name].handler === 'function') {\n\t\t\tmacro = _macros[name];\n\t\t}\n\t\t/* legacy macro support */\n\t\telse if (macros.hasOwnProperty(name) && typeof macros[name].handler === 'function') {\n\t\t\tmacro = macros[name];\n\t\t}\n\t\t/* /legacy macro support */\n\n\t\treturn macro;\n\t}\n\n\tfunction macrosInit(handler = 'init') { // eslint-disable-line no-unused-vars\n\t\tObject.keys(_macros).forEach(name => {\n\t\t\tif (typeof _macros[name][handler] === 'function') {\n\t\t\t\t_macros[name][handler](name);\n\t\t\t}\n\t\t});\n\n\t\t/* legacy macro support */\n\t\tObject.keys(macros).forEach(name => {\n\t\t\tif (typeof macros[name][handler] === 'function') {\n\t\t\t\tmacros[name][handler](name);\n\t\t\t}\n\t\t});\n\t\t/* /legacy macro support */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTags Functions.\n\t*******************************************************************************************************************/\n\tfunction tagsRegister(parent, bodyTags) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tconst endTags = [`/${parent}`, `end${parent}`]; // automatically create the closing tags\n\t\tconst allTags = [].concat(endTags, Array.isArray(bodyTags) ? bodyTags : []);\n\n\t\tfor (let i = 0; i < allTags.length; ++i) {\n\t\t\tconst tag = allTags[i];\n\n\t\t\tif (macrosHas(tag)) {\n\t\t\t\tthrow new Error('cannot register tag for an existing macro');\n\t\t\t}\n\n\t\t\tif (tagsHas(tag)) {\n\t\t\t\tif (!_tags[tag].includes(parent)) {\n\t\t\t\t\t_tags[tag].push(parent);\n\t\t\t\t\t_tags[tag].sort();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_tags[tag] = [parent];\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction tagsUnregister(parent) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tObject.keys(_tags).forEach(tag => {\n\t\t\tconst i = _tags[tag].indexOf(parent);\n\n\t\t\tif (i !== -1) {\n\t\t\t\tif (_tags[tag].length === 1) {\n\t\t\t\t\tdelete _tags[tag];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_tags[tag].splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction tagsHas(name) {\n\t\treturn _tags.hasOwnProperty(name);\n\t}\n\n\tfunction tagsGet(name) {\n\t\treturn tagsHas(name) ? _tags[name] : null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tMacro Functions.\n\t\t*/\n\t\tadd     : { value : macrosAdd },\n\t\tdelete  : { value : macrosDelete },\n\t\tisEmpty : { value : macrosIsEmpty },\n\t\thas     : { value : macrosHas },\n\t\tget     : { value : macrosGet },\n\t\tinit    : { value : macrosInit },\n\n\t\t/*\n\t\t\tTags Functions.\n\t\t*/\n\t\ttags : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tregister   : { value : tagsRegister },\n\t\t\t\tunregister : { value : tagsUnregister },\n\t\t\t\thas        : { value : tagsHas },\n\t\t\t\tget        : { value : tagsGet }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) } // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrocontext.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, DebugView, Patterns, State, Wikifier, throwError */\n\nvar MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tMacroContext Class.\n\t*******************************************************************************************************************/\n\tclass MacroContext {\n\t\tconstructor(contextData) {\n\t\t\tconst context = Object.assign({\n\t\t\t\tparent      : null,\n\t\t\t\tmacro       : null,\n\t\t\t\tname        : '',\n\t\t\t\tdisplayName : '',\n\t\t\t\targs        : null,\n\t\t\t\tpayload     : null,\n\t\t\t\tparser      : null,\n\t\t\t\tsource      : ''\n\t\t\t}, contextData);\n\n\t\t\tif (context.macro === null || context.name === '' || context.parser === null) {\n\t\t\t\tthrow new TypeError('context object missing required properties');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tself : {\n\t\t\t\t\tvalue : context.macro\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : typeof context.macro._ALIAS_OF === 'undefined' ? context.name : context.macro._ALIAS_OF\n\t\t\t\t},\n\n\t\t\t\tdisplayName : {\n\t\t\t\t\tvalue : context.name\n\t\t\t\t},\n\n\t\t\t\targs : {\n\t\t\t\t\tvalue : context.args\n\t\t\t\t},\n\n\t\t\t\tpayload : {\n\t\t\t\t\tvalue : context.payload\n\t\t\t\t},\n\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : context.source\n\t\t\t\t},\n\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : context.parent\n\t\t\t\t},\n\n\t\t\t\tparser : {\n\t\t\t\t\tvalue : context.parser\n\t\t\t\t},\n\n\t\t\t\t_output : {\n\t\t\t\t\tvalue : context.parser.output\n\t\t\t\t},\n\n\t\t\t\t_shadows : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugView : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugViewEnabled : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Config.debug\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this._debugViewEnabled ? this.debugView.output : this._output;\n\t\t}\n\n\t\tget shadows() {\n\t\t\treturn [...this._shadows];\n\t\t}\n\n\t\tget shadowView() {\n\t\t\tconst view = new Set();\n\t\t\tthis.contextSelectAll(ctx => ctx._shadows)\n\t\t\t\t.forEach(ctx => ctx._shadows.forEach(name => view.add(name)));\n\t\t\treturn [...view];\n\t\t}\n\n\t\tget debugView() {\n\t\t\tif (this._debugViewEnabled) {\n\t\t\t\treturn this._debugView !== null ? this._debugView : this.createDebugView();\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextHas(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tcontextSelect(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextSelectAll(filter) {\n\t\t\tconst result = [];\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\tresult.push(context);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\taddShadow(...names) {\n\t\t\tif (!this._shadows) {\n\t\t\t\tthis._shadows = new Set();\n\t\t\t}\n\n\t\t\tconst varRe = new RegExp(`^${Patterns.variable}$`);\n\n\t\t\tnames\n\t\t\t\t.flat(Infinity)\n\t\t\t\t.forEach(name => {\n\t\t\t\t\tif (typeof name !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`variable name must be a string; type: ${typeof name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!varRe.test(name)) {\n\t\t\t\t\t\tthrow new Error(`invalid variable name \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._shadows.add(name);\n\t\t\t\t});\n\t\t}\n\n\t\tcreateShadowWrapper(callback, doneCallback, startCallback) {\n\t\t\tconst shadowContext = this;\n\t\t\tlet shadowStore;\n\n\t\t\tif (typeof callback === 'function') {\n\t\t\t\tshadowStore = {};\n\t\t\t\tthis.shadowView.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn function (...args) {\n\t\t\t\tif (typeof startCallback === 'function') {\n\t\t\t\t\tstartCallback.apply(this, args);\n\t\t\t\t}\n\n\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\t\t\t\t\tconst macroParser = Wikifier.Parser.get('macro');\n\t\t\t\t\tlet contextCache;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\tcallback.\n\t\t\t\t\t*/\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Cache the existing macro execution context and assign the shadow context.\n\t\t\t\t\t\tcontextCache = macroParser.context;\n\t\t\t\t\t\tmacroParser.context = shadowContext;\n\n\t\t\t\t\t\t// Call the callback function.\n\t\t\t\t\t\tcallback.apply(this, args);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\t// Revert the macro execution context shadowing.\n\t\t\t\t\t\tif (contextCache !== undefined) {\n\t\t\t\t\t\t\tmacroParser.context = contextCache;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (typeof doneCallback === 'function') {\n\t\t\t\t\tdoneCallback.apply(this, args);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tcreateDebugView(name, title) {\n\t\t\tthis._debugView = new DebugView(\n\t\t\t\tthis._output,\n\t\t\t\t'macro',\n\t\t\t\tname ? name : this.displayName,\n\t\t\t\ttitle ? title : this.source\n\t\t\t);\n\n\t\t\tif (this.payload !== null && this.payload.length > 0) {\n\t\t\t\tthis._debugView.modes({ nonvoid : true });\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = true;\n\t\t\treturn this._debugView;\n\t\t}\n\n\t\tremoveDebugView() {\n\t\t\tif (this._debugView !== null) {\n\t\t\t\tthis._debugView.remove();\n\t\t\t\tthis._debugView = null;\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = false;\n\t\t}\n\n\t\terror(message, source, stack) {\n\t\t\treturn throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source, stack);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn MacroContext;\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrolib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, Engine, Has, L10n, Macro, NodeTyper, Patterns, Scripting, SimpleAudio, State,\n\t       Story, TempState, Util, Wikifier, postdisplay, prehistory, storage, toStringOrDefault\n*/\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tVariables Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<capture>>\n\t*/\n\tMacro.add('capture', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\t\ttsVarRe  : new RegExp(`(${Patterns.variable})`,'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.raw.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst valueCache = {};\n\n\t\t\t/*\n\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t`Wikifier` call.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\tconst tsVarRe = this.self.tsVarRe;\n\t\t\t\tlet match;\n\n\t\t\t\t/*\n\t\t\t\t\tCache the existing values of the variables and add a shadow.\n\t\t\t\t*/\n\t\t\t\twhile ((match = tsVarRe.exec(this.args.raw)) !== null) {\n\t\t\t\t\tconst varName = match[1];\n\t\t\t\t\tconst varKey  = varName.slice(1);\n\t\t\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.addShadow(varName);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier(this.output, this.payload[0].contents);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t// Revert the variable shadowing.\n\t\t\t\tthis.shadows.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<set>>\n\t*/\n\tMacro.add('set', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<unset>>\n\t*/\n\tMacro.add('unset', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(\n\t\t\t`State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})`,\n\t\t\t'g'\n\t\t),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst jsVarRe = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst store = State[match[1]];\n\t\t\t\tconst name  = match[2];\n\n\t\t\t\tif (store.hasOwnProperty(name)) {\n\t\t\t\t\tdelete store[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remember>>\n\t*/\n\tMacro.add('remember', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember') || {};\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\t\t\t\tremember[name] = State.variables[name];\n\t\t\t}\n\n\t\t\tif (!storage.set('remember', remember)) {\n\t\t\t\treturn this.error(`unknown error, cannot remember: ${this.args.raw}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t},\n\n\t\tinit() {\n\t\t\tconst remember = storage.get('remember');\n\n\t\t\tif (remember) {\n\t\t\t\tObject.keys(remember).forEach(name => State.variables[name] = remember[name]);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<forget>>\n\t*/\n\tMacro.add('forget', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story variable list specified');\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember');\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\t\t\tlet needStore = false;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\n\t\t\t\tif (State.variables.hasOwnProperty(name)) {\n\t\t\t\t\tdelete State.variables[name];\n\t\t\t\t}\n\n\t\t\t\tif (remember && remember.hasOwnProperty(name)) {\n\t\t\t\t\tneedStore = true;\n\t\t\t\t\tdelete remember[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needStore) {\n\t\t\t\tif (Object.keys(remember).length === 0) {\n\t\t\t\t\tif (!storage.delete('remember')) {\n\t\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!storage.set('remember', remember)) {\n\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tScripting Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<run>>\n\t*/\n\tMacro.add('run', 'set'); // add <<run>> as an alias of <<set>>\n\n\t/*\n\t\t<<script>>\n\t*/\n\tMacro.add('script', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.payload[0].contents, output);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.createDebugView();\n\t\t\t}\n\n\t\t\tif (output.hasChildNodes()) {\n\t\t\t\tthis.output.appendChild(output);\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDisplay Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<include>>\n\t*/\n\tMacro.add('include', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tpassage = Story.get(passage);\n\t\t\tlet $el;\n\n\t\t\tif (this.args[1]) {\n\t\t\t\t$el = jQuery(document.createElement(this.args[1]))\n\t\t\t\t\t.addClass(`${passage.domId} macro-${this.name}`)\n\t\t\t\t\t.attr('data-passage', passage.title)\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(this.output);\n\t\t\t}\n\n\t\t\t$el.wiki(passage.processText());\n\t\t}\n\t});\n\n\t/*\n\t\t<<nobr>>\n\t*/\n\tMacro.add('nobr', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\t/*\n\t\t\t\tWikify the contents, after removing all leading & trailing newlines and compacting\n\t\t\t\tall internal sequences of newlines into single spaces.\n\t\t\t*/\n\t\t\tnew Wikifier(this.output, this.payload[0].contents.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' '));\n\t\t}\n\t});\n\n\t/*\n\t\t<<print>>, <<=>>, & <<->>\n\t*/\n\tMacro.add(['print', '=', '-'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = toStringOrDefault(Scripting.evalJavaScript(this.args.full), null);\n\n\t\t\t\tif (result !== null) {\n\t\t\t\t\tnew Wikifier(this.output, this.name === '-' ? Util.escape(result) : result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<silently>>\n\t*/\n\tMacro.add('silently', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.payload[0].contents.trim());\n\n\t\t\tif (Config.debug) {\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tthis.debugView.modes({ block : true, hidden : true });\n\t\t\t\tthis.output.appendChild(frag);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Discard the output, unless there were errors.\n\t\t\t\tconst errList = [...frag.querySelectorAll('.error')].map(errEl => errEl.textContent);\n\n\t\t\t\tif (errList.length > 0) {\n\t\t\t\t\treturn this.error(`error${errList.length === 1 ? '' : 's'} within contents (${errList.join('; ')})`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<type speed [start delay] [class classes] [element tag] [id ID] [keep|none] [skipkey key]>>\n\t*/\n\tMacro.add('type', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttypeId  : 0,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no speed specified');\n\t\t\t}\n\n\t\t\tconst speed = Util.fromCssTime(this.args[0]); // in milliseconds\n\n\t\t\tif (speed < 0) {\n\t\t\t\treturn this.error(`speed time value must be non-negative (received: ${this.args[0]})`);\n\t\t\t}\n\n\t\t\tlet cursor;\n\t\t\tlet elClass = '';\n\t\t\tlet elId    = '';\n\t\t\tlet elTag   = 'div';\n\t\t\tlet skipKey = Config.macros.typeSkipKey;\n\t\t\tlet start   = 400; // in milliseconds\n\n\t\t\t// Process optional arguments.\n\t\t\tconst options = this.args.slice(1);\n\n\t\t\twhile (options.length > 0) {\n\t\t\t\tconst option = options.shift();\n\n\t\t\t\tswitch (option) {\n\t\t\t\tcase 'class': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('class option missing required class name(s)');\n\t\t\t\t\t}\n\n\t\t\t\t\telClass = options.shift();\n\n\t\t\t\t\tif (elClass === '') {\n\t\t\t\t\t\tthrow new Error('class option class name(s) must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'element': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('element option missing required element tag name');\n\t\t\t\t\t}\n\n\t\t\t\t\telTag = options.shift();\n\n\t\t\t\t\tif (elTag === '') {\n\t\t\t\t\t\tthrow new Error('element option tag name must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'id': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('id option missing required ID');\n\t\t\t\t\t}\n\n\t\t\t\t\telId = options.shift();\n\n\t\t\t\t\tif (elId === '') {\n\t\t\t\t\t\tthrow new Error('id option ID must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'keep':\n\t\t\t\t\tcursor = 'keep';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'none':\n\t\t\t\t\tcursor = 'none';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'skipkey': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('skipkey option missing required key value');\n\t\t\t\t\t}\n\n\t\t\t\t\tskipKey = options.shift();\n\n\t\t\t\t\tif (skipKey === '') {\n\t\t\t\t\t\tthrow new Error('skipkey option key value must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'start': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('start option missing required time value');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = options.shift();\n\t\t\t\t\tstart = Util.fromCssTime(value);\n\n\t\t\t\t\tif (start < 0) {\n\t\t\t\t\t\tthrow new Error(`start option time value must be non-negative (received: ${value})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.error(`unknown option: ${option}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contents = this.payload[0].contents;\n\n\t\t\t// Do nothing if there's no content to type out.\n\t\t\tif (contents.trim() === '') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// Set up our base class name and event namespace.\n\t\t\tconst className = `macro-${this.name}`;\n\t\t\tconst namespace = `.${className}`;\n\n\t\t\t// Create a target to be later replaced by the typing wrapper.\n\t\t\tconst $target = jQuery(document.createElement(elTag))\n\t\t\t\t.addClass(`${className} ${className}-target`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Initialize the queue and clean up handlers.\n\t\t\tif (!TempState.macroTypeQueue) {\n\t\t\t\t// Set up the typing handler queue for all invocations.\n\t\t\t\tTempState.macroTypeQueue = [];\n\n\t\t\t\t// Immediately clear any existing handlers from our namespace and set up a\n\t\t\t\t// `:passageinit` event handler to clean up after navigation.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`:passageinit${namespace}`, () => $(document).off(namespace));\n\t\t\t}\n\n\t\t\t// If the queue is empty at this point, set the start typing flag.\n\t\t\tconst startTyping = TempState.macroTypeQueue.length === 0;\n\n\t\t\t// Generate our unique ID.\n\t\t\tconst selfId = ++this.self.typeId;\n\n\t\t\t// Push our typing handler onto the queue.\n\t\t\tTempState.macroTypeQueue.push({\n\t\t\t\tid : selfId,\n\n\t\t\t\thandler() {\n\t\t\t\t\tconst $wrapper = jQuery(document.createElement(elTag))\n\t\t\t\t\t\t.addClass(className);\n\n\t\t\t\t\t// Add the user ID, if any.\n\t\t\t\t\tif (elId) {\n\t\t\t\t\t\t$wrapper.attr('id', elId);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add the user class(es), if any.\n\t\t\t\t\tif (elClass) {\n\t\t\t\t\t\t$wrapper.addClass(elClass);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wikify the contents into `$wrapper`.\n\t\t\t\t\tnew Wikifier($wrapper, contents);\n\n\t\t\t\t\t// Cache info about the current turn.\n\t\t\t\t\tconst passage = State.passage;\n\t\t\t\t\tconst turn    = State.turns;\n\n\t\t\t\t\t// Skip typing if….\n\t\t\t\t\tif (\n\t\t\t\t\t\t// …we've visited the passage before.\n\t\t\t\t\t\t!Config.macros.typeVisitedPassages\n\t\t\t\t\t\t&& State.passages.slice(0, -1).some(title => title === passage)\n\n\t\t\t\t\t\t// …there were any content errors.\n\t\t\t\t\t\t|| $wrapper.find('.error').length > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t\t// Run the next typing handler in the queue, if any.\n\t\t\t\t\t\tif (TempState.macroTypeQueue.length > 0) {\n\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Exit.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create a new `NodeTyper` instance for the wrapper's contents and\n\t\t\t\t\t// replace the target with the typing wrapper.\n\t\t\t\t\tconst typer = new NodeTyper({\n\t\t\t\t\t\ttargetNode : $wrapper.get(0),\n\t\t\t\t\t\tclassNames : cursor === 'none' ? null : `${className}-cursor`\n\t\t\t\t\t});\n\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t// Set up event IDs.\n\t\t\t\t\tconst typingCompleteId = ':typingcomplete';\n\t\t\t\t\tconst typingStartId    = ':typingstart';\n\t\t\t\t\tconst typingStopId     = ':typingstop';\n\t\t\t\t\tconst keydownAndNS     = `keydown${namespace}`;\n\t\t\t\t\tconst typingStopAndNS  = `${typingStopId}${namespace}`;\n\n\t\t\t\t\t// Set up handlers for spacebar aborting and continuations.\n\t\t\t\t\t$(document)\n\t\t\t\t\t\t.off(keydownAndNS)\n\t\t\t\t\t\t.on(keydownAndNS, ev => {\n\t\t\t\t\t\t\t// Finish typing if the player aborts via the skip key.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tUtil.scrubEventKey(ev.key) === skipKey\n\t\t\t\t\t\t\t\t&& (ev.target === document.body || ev.target === document.documentElement)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t\t$(document).off(keydownAndNS);\n\t\t\t\t\t\t\t\ttyper.finish();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.one(typingStopAndNS, () => {\n\t\t\t\t\t\t\tif (TempState.macroTypeQueue) {\n\t\t\t\t\t\t\t\t// If the queue is empty, fire the typing complete event.\n\t\t\t\t\t\t\t\tif (TempState.macroTypeQueue.length === 0) {\n\t\t\t\t\t\t\t\t\tjQuery.event.trigger(typingCompleteId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Elsewise, run the next typing handler in the queue.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Set up the typing interval and start/stop event firing.\n\t\t\t\t\tconst typeNode = function typeNode() {\n\t\t\t\t\t\t// Fire the typing start event.\n\t\t\t\t\t\t$wrapper.trigger(typingStartId);\n\n\t\t\t\t\t\tconst typeNodeId = setInterval(() => {\n\t\t\t\t\t\t\t// Stop typing if….\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t// …we've navigated away.\n\t\t\t\t\t\t\t\tState.passage !== passage\n\t\t\t\t\t\t\t\t|| State.turns !== turn\n\n\t\t\t\t\t\t\t\t// …we're done typing.\n\t\t\t\t\t\t\t\t|| !typer.type()\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Terminate the timer.\n\t\t\t\t\t\t\t\tclearInterval(typeNodeId);\n\n\t\t\t\t\t\t\t\t// Remove this handler from the queue, if the queue still exists and the\n\t\t\t\t\t\t\t\t// handler IDs match.\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.length > 0\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.first().id === selfId\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Fire the typing stop event.\n\t\t\t\t\t\t\t\t$wrapper.trigger(typingStopId);\n\n\t\t\t\t\t\t\t\t// Add the done class to the wrapper.\n\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-done`);\n\n\t\t\t\t\t\t\t\t// Add the cursor class to the wrapper, if we're keeping it.\n\t\t\t\t\t\t\t\tif (cursor === 'keep') {\n\t\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-cursor`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, speed);\n\t\t\t\t\t};\n\n\t\t\t\t\t// Kick off typing the node.\n\t\t\t\t\tif (start) {\n\t\t\t\t\t\tsetTimeout(typeNode, start);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\ttypeNode();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we're to start typing, then either set up a `:passageend` event handler\n\t\t\t// to do so or start it immediately, depending on the engine state.\n\t\t\tif (startTyping) {\n\t\t\t\tif (Engine.isPlaying()) {\n\t\t\t\t\t$(document).one(`:passageend${namespace}`, () => TempState.macroTypeQueue.first().handler());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<display>>\n\t*/\n\tMacro.add('display', 'include'); // add <<display>> as an alias of <<include>>\n\n\n\t/*******************************************************************************************************************\n\t\tControl Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<if>>, <<elseif>>, & <<else>>\n\t*/\n\tMacro.add('if', {\n\t\tskipArgs   : true,\n\t\ttags       : ['elseif', 'else'],\n\t\telseifWsRe : /^\\s*if\\b/i,\n\t\tifAssignRe : /[^!=&^|<>*/%+-]=[^=>]/,\n\n\t\thandler() {\n\t\t\tlet i;\n\n\t\t\ttry {\n\t\t\t\tconst len = this.payload.length;\n\n\t\t\t\t// Sanity checks.\n\t\t\t\tconst elseifWsRe = this.self.elseifWsRe;\n\t\t\t\tconst ifAssignRe = this.self.ifAssignRe;\n\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t/* eslint-disable prefer-template */\n\t\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\t\tcase 'else':\n\t\t\t\t\t\tif (this.payload[i].args.raw.length > 0) {\n\t\t\t\t\t\t\tif (elseifWsRe.test(this.payload[i].args.raw)) {\n\t\t\t\t\t\t\t\treturn this.error(`whitespace is not allowed between the \"else\" and \"if\" in <<elseif>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn this.error(`<<else>> does not accept a conditional expression (perhaps you meant to use <<elseif>>), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\t\treturn this.error('<<else>> must be the final clause');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (this.payload[i].args.full.length === 0) {\n\t\t\t\t\t\t\treturn this.error(`no conditional expression specified for <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t   Config.macros.ifAssignmentError\n\t\t\t\t\t\t\t&& ifAssignRe.test(this.payload[i].args.full)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn this.error(`assignment operator found within <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''} (perhaps you meant to use an equality operator: ==, ===, eq, is), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable prefer-template */\n\t\t\t\t}\n\n\t\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\t\tlet success = false;\n\n\t\t\t\t// Evaluate the clauses.\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t// Custom debug view setup for the current clause.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Conditional test.\n\t\t\t\t\tif (this.payload[i].name === 'else' || !!evalJavaScript(this.payload[i].args.full)) {\n\t\t\t\t\t\tsuccess = true;\n\t\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t\t// Custom debug view setup for a failed conditional.\n\t\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup for the remaining clauses.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tFake a debug view for `<</if>>`.  We do this to aid the checking of nesting\n\t\t\t\t\t\tand as a quick indicator of if any of the clauses matched.\n\t\t\t\t\t*/\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : !success,\n\t\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression in <<${i === 0 ? 'if' : 'elseif'}>> clause${i > 0 ? ' (#' + i + ')' : ''}: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack); // eslint-disable-line prefer-template\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<switch>>, <<case>>, & <<default>>\n\t*/\n\tMacro.add('switch', {\n\t\tskipArgs : ['switch'],\n\t\ttags     : ['case', 'default'],\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\tconst len = this.payload.length;\n\n\t\t\t// if (len === 1 || !this.payload.some(p => p.name === 'case')) {\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no cases specified');\n\t\t\t}\n\n\t\t\tlet i;\n\n\t\t\t// Sanity checks.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\tcase 'default':\n\t\t\t\t\tif (this.payload[i].args.length > 0) {\n\t\t\t\t\t\treturn this.error(`<<default>> does not accept values, invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\treturn this.error('<<default>> must be the final case');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no value(s) specified for <<${this.payload[i].name}>> (#${i})`);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet result;\n\n\t\t\ttry {\n\t\t\t\tresult = Scripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst debugView = this.debugView; // cache it now, to be modified later\n\t\t\tlet success = false;\n\n\t\t\t// Initial debug view setup for `<<switch>>`.\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Evaluate the clauses.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\t// Custom debug view setup for the current case.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t}\n\n\t\t\t\t// Case test(s).\n\t\t\t\tif (this.payload[i].name === 'default' || this.payload[i].args.some(val => val === result)) {\n\t\t\t\t\tsuccess = true;\n\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t// Custom debug view setup for a failed case.\n\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup for the remaining cases.\n\t\t\tif (Config.debug) {\n\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t\tFinalize the debug view for `<<switch>>` and fake a debug view for `<</switch>>`.\n\t\t\t\t\tWe do both as a quick indicator of if any of the cases matched and the latter\n\t\t\t\t\tto aid the checking of nesting.\n\t\t\t\t*/\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t\tthis\n\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<for>>, <<break>>, & <<continue>>\n\t*/\n\tMacro.add('for', {\n\t\t/* eslint-disable max-len */\n\t\tskipArgs    : true,\n\t\ttags        : null,\n\t\thasRangeRe  : new RegExp(`^\\\\S${Patterns.anyChar}*?\\\\s+range\\\\s+\\\\S${Patterns.anyChar}*?$`),\n\t\trangeRe     : new RegExp(`^(?:State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s*,\\\\s*)?State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s+range\\\\s+(\\\\S${Patterns.anyChar}*?)$`),\n\t\tthreePartRe : /^([^;]*?)\\s*;\\s*([^;]*?)\\s*;\\s*([^;]*?)$/,\n\t\tforInRe     : /^\\S+\\s+in\\s+\\S+/i,\n\t\tforOfRe     : /^\\S+\\s+of\\s+\\S+/i,\n\t\t/* eslint-enable max-len */\n\n\t\thandler() {\n\t\t\tconst argsStr = this.args.full.trim();\n\t\t\tconst payload = this.payload[0].contents.replace(/\\n$/, '');\n\n\t\t\t// Empty form.\n\t\t\tif (argsStr.length === 0) {\n\t\t\t\tthis.self.handleFor.call(this, payload, null, true, null);\n\t\t\t}\n\n\t\t\t// Range form.\n\t\t\telse if (this.self.hasRangeRe.test(argsStr)) {\n\t\t\t\tconst parts = argsStr.match(this.self.rangeRe);\n\n\t\t\t\tif (parts === null) {\n\t\t\t\t\treturn this.error('invalid range form syntax, format: [index ,] value range collection');\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleForRange.call(\n\t\t\t\t\tthis,\n\t\t\t\t\tpayload,\n\t\t\t\t\t{ type : parts[1], name : parts[2] },\n\t\t\t\t\t{ type : parts[3], name : parts[4] },\n\t\t\t\t\tparts[5]\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Conditional forms.\n\t\t\telse {\n\t\t\t\tlet init;\n\t\t\t\tlet condition;\n\t\t\t\tlet post;\n\n\t\t\t\t// Conditional-only form.\n\t\t\t\tif (argsStr.indexOf(';') === -1) {\n\t\t\t\t\t// Sanity checks.\n\t\t\t\t\tif (this.self.forInRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…in is not supported; see: for…range');\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.self.forOfRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…of is not supported; see: for…range');\n\t\t\t\t\t}\n\n\t\t\t\t\tcondition = argsStr;\n\t\t\t\t}\n\n\t\t\t\t// 3-part conditional form.\n\t\t\t\telse {\n\t\t\t\t\tconst parts = argsStr.match(this.self.threePartRe);\n\n\t\t\t\t\tif (parts === null) {\n\t\t\t\t\t\treturn this.error('invalid 3-part conditional form syntax, format: [init] ; [condition] ; [post]');\n\t\t\t\t\t}\n\n\t\t\t\t\tinit      = parts[1];\n\t\t\t\t\tcondition = parts[2].trim();\n\t\t\t\t\tpost      = parts[3];\n\n\t\t\t\t\tif (condition.length === 0) {\n\t\t\t\t\t\tcondition = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleFor.call(this, payload, init, condition, post);\n\t\t\t}\n\t\t},\n\n\t\thandleFor(payload, init, condition, post) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet first  = true;\n\t\t\tlet safety = Config.macros.maxLoopIterations;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tif (init) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tevalJavaScript(init);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad init expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile (evalJavaScript(condition)) {\n\t\t\t\t\tif (--safety < 0) {\n\t\t\t\t\t\treturn this.error(`exceeded configured maximum loop iterations (${Config.macros.maxLoopIterations})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (post) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevalJavaScript(post);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\treturn this.error(`bad post expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\thandleForRange(payload, indexVar, valueVar, rangeExp) {\n\t\t\tlet first     = true;\n\t\t\tlet rangeList;\n\n\t\t\ttry {\n\t\t\t\trangeList = this.self.toRangeList(rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tfor (let i = 0; i < rangeList.length; ++i) {\n\t\t\t\t\tif (indexVar.name) {\n\t\t\t\t\t\tState[indexVar.type][indexVar.name] = rangeList[i][0];\n\t\t\t\t\t}\n\n\t\t\t\t\tState[valueVar.type][valueVar.name] = rangeList[i][1];\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\ttoRangeList(rangeExp) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet value;\n\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t*/\n\t\t\t\tvalue = evalJavaScript(rangeExp[0] === '{' ? `(${rangeExp})` : rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tif (typeof ex !== 'object') {\n\t\t\t\t\tthrow new Error(`bad range expression: ${ex}`);\n\t\t\t\t}\n\n\t\t\t\tex.message = `bad range expression: ${ex.message}`;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tlet list;\n\n\t\t\tswitch (typeof value) {\n\t\t\tcase 'string':\n\t\t\t\tlist = [];\n\t\t\t\tfor (let i = 0; i < value.length; /* empty */) {\n\t\t\t\t\tconst obj = Util.charAndPosAt(value, i);\n\t\t\t\t\tlist.push([i, obj.char]);\n\t\t\t\t\ti = 1 + obj.end;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'object':\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tlist = value.map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Set) {\n\t\t\t\t\tlist = [...value].map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Map) {\n\t\t\t\t\tlist = [...value.entries()];\n\t\t\t\t}\n\t\t\t\telse if (Util.toStringTag(value) === 'Object') {\n\t\t\t\t\tlist = Object.keys(value).map(key => [key, value[key]]);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`unsupported range expression type: ${Util.toStringTag(value)}`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`unsupported range expression type: ${typeof value}`);\n\t\t\t}\n\n\t\t\treturn list;\n\t\t}\n\t});\n\tMacro.add(['break', 'continue'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.contextHas(ctx => ctx.name === 'for')) {\n\t\t\t\tTempState.break = this.name === 'continue' ? 1 : 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<for>>');\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tInteractive Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<button>> & <<link>>\n\t*/\n\tMacro.add(['button', 'link'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error(`no ${this.name === 'button' ? 'button' : 'link'} text specified`);\n\t\t\t}\n\n\t\t\tconst $link = jQuery(document.createElement(this.name === 'button' ? 'button' : 'a'));\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\tconst $image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t.attr('src', this.args[0].source)\n\t\t\t\t\t\t.appendTo($link);\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t}\n\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t$link.append(document.createTextNode(this.args[0].text));\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the link text.\n\t\t\t\t$link.wikiWithOptions({ profile : 'core' }, this.args[0]);\n\t\t\t\tpassage = this.args.length > 1 ? this.args[1] : undefined;\n\t\t\t}\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$link.addClass('link-internal');\n\t\t\t}\n\n\t\t\t$link\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : passage != null // lazy equality for null\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\tthis.payload[0].contents !== ''\n\t\t\t\t\t\t? () => Wikifier.wikifyEval(this.payload[0].contents.trim())\n\t\t\t\t\t\t: null,\n\t\t\t\t\tpassage != null // lazy equality for null\n\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t: null\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<checkbox>>\n\t*/\n\tMacro.add('checkbox', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 3) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('unchecked value'); }\n\t\t\t\tif (this.args.length < 3) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst uncheckValue = this.args[1];\n\t\t\tconst checkValue   = this.args[2];\n\t\t\tconst el           = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'checkbox',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.checked ? checkValue : uncheckValue);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and input element to the appropriate value and state, as requested.\n\t\t\t*/\n\t\t\tswitch (this.args[3]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<cycle>>, <<listbox>>, <<option>>, & <<optionsfrom>>\n\t*/\n\tMacro.add(['cycle', 'listbox'], {\n\t\tisAsync  : true,\n\t\tskipArgs : ['optionsfrom'],\n\t\ttags     : ['option', 'optionsfrom'],\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no variable name specified');\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst len   = this.payload.length;\n\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no options specified');\n\t\t\t}\n\n\t\t\tconst autoselect = this.args.length > 1 && this.args[1] === 'autoselect';\n\t\t\tconst options    = [];\n\t\t\tconst tagCount   = { option : 0, optionsfrom : 0 };\n\t\t\tlet selectedIdx = -1;\n\n\t\t\t// Get the options and selected index, if any.\n\t\t\tfor (let i = 1; i < len; ++i) {\n\t\t\t\tconst payload = this.payload[i];\n\n\t\t\t\t// <<option label value [selected]>>\n\t\t\t\tif (payload.name === 'option') {\n\t\t\t\t\t++tagCount.option;\n\n\t\t\t\t\tif (payload.args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no arguments specified for <<${payload.name}>> (#${tagCount.option})`);\n\t\t\t\t\t}\n\n\t\t\t\t\toptions.push({\n\t\t\t\t\t\tlabel : String(payload.args[0]),\n\t\t\t\t\t\tvalue : payload.args.length === 1 ? payload.args[0] : payload.args[1]\n\t\t\t\t\t});\n\n\t\t\t\t\tif (payload.args.length > 2 && payload.args[2] === 'selected') {\n\t\t\t\t\t\tif (autoselect) {\n\t\t\t\t\t\t\treturn this.error('cannot specify both the autoselect and selected keywords');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (selectedIdx !== -1) {\n\t\t\t\t\t\t\treturn this.error(`multiple selected keywords specified for <<${payload.name}>> (#${selectedIdx + 1} & #${tagCount.option})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tselectedIdx = options.length - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// <<optionsfrom expression>>\n\t\t\t\telse {\n\t\t\t\t\t++tagCount.optionsfrom;\n\n\t\t\t\t\tif (payload.args.full.length === 0) {\n\t\t\t\t\t\treturn this.error(`no expression specified for <<${payload.name}>> (#${tagCount.optionsfrom})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tconst exp = payload.args.full;\n\t\t\t\t\t\tresult = Scripting.evalJavaScript(exp[0] === '{' ? `(${exp})` : exp);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (typeof result !== 'object' || result === null) {\n\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (type: ${result === null ? 'null' : typeof result})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result instanceof Array || result instanceof Set) {\n\t\t\t\t\t\tresult.forEach(val => options.push({ label : String(val), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse if (result instanceof Map) {\n\t\t\t\t\t\tresult.forEach((val, key) => options.push({ label : String(key), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst oType = Util.toStringTag(result);\n\n\t\t\t\t\t\tif (oType !== 'Object') {\n\t\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (object type: ${oType})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tObject.keys(result).forEach(key => options.push({ label : key, value : result[key] }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No options were selected by the user, so we must select one.\n\t\t\tif (selectedIdx === -1) {\n\t\t\t\t// Attempt to automatically select an option by matching the variable's current value.\n\t\t\t\tif (autoselect) {\n\t\t\t\t\t// NOTE: This will usually fail for objects due to a variety of reasons.\n\t\t\t\t\tconst sameValueZero = Util.sameValueZero;\n\t\t\t\t\tconst curValue      = State.getVar(varName);\n\t\t\t\t\tconst curValueIdx   = options.findIndex(opt => sameValueZero(opt.value, curValue));\n\t\t\t\t\tselectedIdx = curValueIdx === -1 ? 0 : curValueIdx;\n\t\t\t\t}\n\n\t\t\t\t// Simply select the first option.\n\t\t\t\telse {\n\t\t\t\t\tselectedIdx = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set up and append the appropriate element to the output buffer.\n\t\t\tif (this.name === 'cycle') {\n\t\t\t\tlet cycleIdx = selectedIdx;\n\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t.wikiWithOptions({ profile : 'core' }, options[selectedIdx].label)\n\t\t\t\t\t.attr('id', `${this.name}-${varId}`)\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () {\n\t\t\t\t\t\tcycleIdx = (cycleIdx + 1) % options.length;\n\t\t\t\t\t\t$(this).empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label);\n\t\t\t\t\t\tState.setVar(varName, options[cycleIdx].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse { // this.name === 'listbox'\n\t\t\t\tconst $select = jQuery(document.createElement('select'));\n\n\t\t\t\toptions.forEach((opt, i) => {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(opt.label)\n\t\t\t\t\t\t.appendTo($select);\n\t\t\t\t});\n\n\t\t\t\t$select\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t\t})\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.val(selectedIdx)\n\t\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\t\tState.setVar(varName, options[Number(this.value)].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\n\t\t\t// Set the variable to the appropriate value, as requested.\n\t\t\tState.setVar(varName, options[selectedIdx].value);\n\t\t}\n\t});\n\n\t/*\n\t\t<<linkappend>>, <<linkprepend>>, & <<linkreplace>>\n\t*/\n\tMacro.add(['linkappend', 'linkprepend', 'linkreplace'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no link text specified');\n\t\t\t}\n\n\t\t\tconst $link      = jQuery(document.createElement('a'));\n\t\t\tconst $insert    = jQuery(document.createElement('span'));\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\n\t\t\t$link\n\t\t\t\t.wikiWithOptions({ profile : 'core' }, this.args[0])\n\t\t\t\t.addClass(`link-internal macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : true\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tif (this.name === 'linkreplace') {\n\t\t\t\t\t\t\t$link.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$link\n\t\t\t\t\t\t\t\t.wrap(`<span class=\"macro-${this.name}\"></span>`)\n\t\t\t\t\t\t\t\t.replaceWith(() => $link.html());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\t\t\t\t\t\t\t$insert.append(frag);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (transition) {\n\t\t\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t$insert.addClass(`macro-${this.name}-insert`);\n\n\t\t\tif (transition) {\n\t\t\t\t$insert.addClass(`macro-${this.name}-in`);\n\t\t\t}\n\n\t\t\tif (this.name === 'linkprepend') {\n\t\t\t\t$insert.insertBefore($link);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$insert.insertAfter($link);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<numberbox>> & <<textbox>>\n\t*/\n\tMacro.add(['numberbox', 'textbox'], {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst asNumber     = this.name === 'numberbox';\n\t\t\tconst defaultValue = asNumber ? Number(this.args[1]) : this.args[1];\n\n\t\t\tif (asNumber && Number.isNaN(defaultValue)) {\n\t\t\t\treturn this.error(`default value \"${this.args[1]}\" is neither a number nor can it be parsed into a number`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst el    = document.createElement('input');\n\t\t\tlet autofocus = false;\n\t\t\tlet passage;\n\n\t\t\tif (this.args.length > 3) {\n\t\t\t\tpassage   = this.args[2];\n\t\t\t\tautofocus = this.args[3] === 'autofocus';\n\t\t\t}\n\t\t\telse if (this.args.length > 2) {\n\t\t\t\tif (this.args[2] === 'autofocus') {\n\t\t\t\t\tautofocus = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpassage = this.args[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (typeof passage === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = passage.link;\n\t\t\t}\n\n\t\t\t// Set up and append the input element to the output buffer.\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : asNumber ? 'number' : 'text',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\t\t\t\t}))\n\t\t\t\t.on('keypress.macros', this.createShadowWrapper(function (ev) {\n\t\t\t\t\t// If Return/Enter is pressed, set the variable and, optionally, forward to another passage.\n\t\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\n\t\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Set the variable and input element to the default value.\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\tel.value = defaultValue;\n\n\t\t\t// Autofocus the input element, if requested.\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<radiobutton>>\n\t*/\n\tMacro.add('radiobutton', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId      = Util.slugify(varName);\n\t\t\tconst checkValue = this.args[1];\n\t\t\tconst el         = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and initialize the group counter.\n\t\t\t*/\n\t\t\tif (!TempState.hasOwnProperty(this.name)) {\n\t\t\t\tTempState[this.name] = {};\n\t\t\t}\n\n\t\t\tif (!TempState[this.name].hasOwnProperty(varId)) {\n\t\t\t\tTempState[this.name][varId] = 0;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}-${TempState[this.name][varId]++}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'radio',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tif (this.checked) {\n\t\t\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable to the checked value and the input element to checked, if requested.\n\t\t\t*/\n\t\t\tswitch (this.args[2]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<textarea>>\n\t*/\n\tMacro.add('textarea', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst defaultValue = this.args[1];\n\t\t\tconst autofocus    = this.args[2] === 'autofocus';\n\t\t\tconst el           = document.createElement('textarea');\n\n\t\t\t/*\n\t\t\t\tSet up and append the textarea element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\trows     : 4,\n\t\t\t\t\t// cols     : 68, // instead of setting \"cols\" we set the `min-width` in CSS\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.value);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and textarea element to the default value.\n\t\t\t*/\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\t// Ideally, we should be setting `.defaultValue` here, but IE doesn't support it,\n\t\t\t// so we have to use `.textContent`, which is equivalent.\n\t\t\tel.textContent = defaultValue;\n\n\t\t\t/*\n\t\t\t\tAutofocus the textarea element, if requested.\n\t\t\t*/\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<click>>\n\t*/\n\tMacro.add('click', 'link'); // add <<click>> as an alias of <<link>>\n\n\n\t/*******************************************************************************************************************\n\t\tLinks Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<actions>>\n\t*/\n\tMacro.add('actions', {\n\t\thandler() {\n\t\t\tconst $list = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass(this.name)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\tfor (let i = 0; i < this.args.length; ++i) {\n\t\t\t\tlet passage;\n\t\t\t\tlet text;\n\t\t\t\tlet $image;\n\t\t\t\tlet setFn;\n\n\t\t\t\tif (typeof this.args[i] === 'object') {\n\t\t\t\t\tif (this.args[i].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[i].source);\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[i].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[i].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[i].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[i].text;\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[i];\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t   State.variables.hasOwnProperty('#actions')\n\t\t\t\t\t&& State.variables['#actions'].hasOwnProperty(passage)\n\t\t\t\t\t&& State.variables['#actions'][passage]\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tjQuery(Wikifier.createInternalLink(\n\t\t\t\t\tjQuery(document.createElement('li')).appendTo($list),\n\t\t\t\t\tpassage,\n\t\t\t\t\tnull,\n\t\t\t\t\t((passage, fn) => () => {\n\t\t\t\t\t\tif (!State.variables.hasOwnProperty('#actions')) {\n\t\t\t\t\t\t\tState.variables['#actions'] = {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tState.variables['#actions'][passage] = true;\n\n\t\t\t\t\t\tif (typeof fn === 'function') {\n\t\t\t\t\t\t\tfn();\n\t\t\t\t\t\t}\n\t\t\t\t\t})(passage, setFn)\n\t\t\t\t))\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.append($image || document.createTextNode(text));\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<back>> & <<return>>\n\t*/\n\tMacro.add(['back', 'return'], {\n\t\thandler() {\n\t\t\t/* legacy */\n\t\t\tif (this.args.length > 1) {\n\t\t\t\treturn this.error('too many arguments specified, check the documentation for details');\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\tlet momentIndex = -1;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('link')) {\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\tif (this.args[0].count === 1) {\n\t\t\t\t\t\t\t// Simple link syntax: `[[...]]`.\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Pretty link syntax: `[[...|...]]`.\n\t\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (this.args.length === 1) {\n\t\t\t\t\t// Argument was simply the link text.\n\t\t\t\t\ttext = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\t/*\n\t\t\t\t\tFind the index and title of the most recent moment whose title does not match\n\t\t\t\t\tthat of the active (present) moment's.\n\t\t\t\t*/\n\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\tif (State.history[i].title !== State.passage) {\n\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\tpassage = State.history[i].title;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we failed to find a passage and we're `<<return>>`, fallback to `State.expired`.\n\t\t\t\tif (passage == null && this.name === 'return') { // lazy equality for null\n\t\t\t\t\tfor (let i = State.expired.length - 1; i >= 0; --i) {\n\t\t\t\t\t\tif (State.expired[i] !== State.passage) {\n\t\t\t\t\t\t\tpassage = State.expired[i];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tif (this.name === 'back') {\n\t\t\t\t\t/*\n\t\t\t\t\t\tFind the index of the most recent moment whose title matches that of the\n\t\t\t\t\t\tspecified passage.\n\t\t\t\t\t*/\n\t\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\t\tif (State.history[i].title === passage) {\n\t\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (momentIndex === -1) {\n\t\t\t\t\t\treturn this.error(`cannot find passage \"${passage}\" in the current story history`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn this.error('cannot find passage');\n\t\t\t}\n\n\t\t\t// if (this.name === \"back\" && momentIndex === -1) {\n\t\t\t// \t// no-op; we're already at the first passage in the current story history\n\t\t\t// \treturn;\n\t\t\t// }\n\n\t\t\tlet $el;\n\n\t\t\tif (this.name !== 'back' || momentIndex !== -1) {\n\t\t\t\t$el = jQuery(document.createElement('a'))\n\t\t\t\t\t.addClass('link-internal')\n\t\t\t\t\t.ariaClick(\n\t\t\t\t\t\t{ one : true },\n\t\t\t\t\t\tthis.name === 'return'\n\t\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t\t: () => Engine.goTo(momentIndex)\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('link-disabled');\n\t\t\t}\n\n\t\t\t$el\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text || L10n.get(`macro${this.name.toUpperFirst()}Text`)))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<choice>>\n\t*/\n\tMacro.add('choice', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tconst choiceId = State.passage;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\t\t\tlet setFn;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// NOTE: The arguments here are backwards.\n\t\t\t\tpassage = this.args[0];\n\t\t\t\ttext    = this.args[1];\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t   State.variables.hasOwnProperty('#choice')\n\t\t\t\t&& State.variables['#choice'].hasOwnProperty(choiceId)\n\t\t\t\t&& State.variables['#choice'][choiceId]\n\t\t\t) {\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass(`link-disabled macro-${this.name}`)\n\t\t\t\t\t.attr('tabindex', -1)\n\t\t\t\t\t.append($image || document.createTextNode(text))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(Wikifier.createInternalLink(this.output, passage, null, () => {\n\t\t\t\tif (!State.variables.hasOwnProperty('#choice')) {\n\t\t\t\t\tState.variables['#choice'] = {};\n\t\t\t\t}\n\n\t\t\t\tState.variables['#choice'][choiceId] = true;\n\n\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\tsetFn();\n\t\t\t\t}\n\t\t\t}))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text));\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDOM Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<addclass>> & <<toggleclass>>\n\t*/\n\tMacro.add(['addclass', 'toggleclass'], {\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('selector'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('class names'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tswitch (this.name) {\n\t\t\tcase 'addclass':\n\t\t\t\t$targets.addClass(this.args[1].trim());\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggleclass':\n\t\t\t\t$targets.toggleClass(this.args[1].trim());\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<removeclass>>\n\t*/\n\tMacro.add('removeclass', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.args.length > 1) {\n\t\t\t\t$targets.removeClass(this.args[1].trim());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$targets.removeClass();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<copy>>\n\t*/\n\tMacro.add('copy', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tjQuery(this.output).append($targets.html());\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<append>>, <<prepend>>, & <<replace>>\n\t*/\n\tMacro.add(['append', 'prepend', 'replace'], {\n\t\ttags  : null,\n\t\tt8nRe : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\t\tlet $insert;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$insert = jQuery(document.createElement('span'));\n\t\t\t\t\t$insert.addClass(`macro-${this.name}-insert macro-${this.name}-in`);\n\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$insert = jQuery(document.createDocumentFragment());\n\t\t\t\t}\n\n\t\t\t\t$insert.wiki(this.payload[0].contents);\n\n\t\t\t\tswitch (this.name) {\n\t\t\t\tcase 'replace':\n\t\t\t\t\t$targets.empty();\n\t\t\t\t\t/* falls through */\n\n\t\t\t\tcase 'append':\n\t\t\t\t\t$targets.append($insert);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'prepend':\n\t\t\t\t\t$targets.prepend($insert);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (this.name === 'replace') {\n\t\t\t\t$targets.empty();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remove>>\n\t*/\n\tMacro.add('remove', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\t$targets.remove();\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudio Macros.\n\t*******************************************************************************************************************/\n\tif (Has.audio) {\n\t\tconst errorOnePlaybackAction = (cur, prev) => `only one playback action allowed per invocation, \"${cur}\" cannot be combined with \"${prev}\"`;\n\n\t\t/*\n\t\t\t<<audio>>\n\t\t*/\n\t\tMacro.add('audio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track and/or group IDs'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tlet selected;\n\n\t\t\t\t// Process the track and/or group IDs.\n\t\t\t\ttry {\n\t\t\t\t\tselected = SimpleAudio.select(this.args[0]);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(1);\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet passage;\n\t\t\t\tlet time;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'time':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('time missing required seconds value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\ttime = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(time) || !Number.isFinite(time)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse time: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'goto':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('goto missing required passage title');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\n\t\t\t\t\t\tif (typeof raw === 'object') {\n\t\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\t\tpassage = raw.link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\t\t\tpassage = raw;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tselected.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (time != null) { // lazy equality for null\n\t\t\t\t\t\tselected.time(time);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tselected.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tselected.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\tconst nsEnded = `ended.macros.macro-${this.name}_goto`;\n\t\t\t\t\t\tselected\n\t\t\t\t\t\t\t.off(nsEnded)\n\t\t\t\t\t\t\t.one(nsEnded, () => {\n\t\t\t\t\t\t\t\tselected.off(nsEnded);\n\t\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tselected.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tselected.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tselected.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tselected.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tselected.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tselected.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<cacheaudio track_id source_list>>\n\t\t*/\n\t\tMacro.add('cacheaudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track ID'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('sources'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tconst id       = String(this.args[0]).trim();\n\t\t\t\tconst oldFmtRe = /^format:\\s*([\\w-]+)\\s*;\\s*/i;\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.tracks.add(id, this.args.slice(1).map(source => {\n\t\t\t\t\t\t/* legacy */\n\t\t\t\t\t\t// Transform an old format specifier into the new style.\n\t\t\t\t\t\tif (oldFmtRe.test(source)) {\n\t\t\t\t\t\t\t// If in Test Mode, return an error.\n\t\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\t\treturn this.error(`track ID \"${id}\": format specifier migration required, \"format:formatId;\" \\u2192 \"formatId|\"`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsource = source.replace(oldFmtRe, '$1|'); // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn source;\n\t\t\t\t\t\t/* /legacy */\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\tif (Config.debug && !SimpleAudio.tracks.get(id).hasSource()) {\n\t\t\t\t\treturn this.error(`track ID \"${id}\": no supported audio sources found`);\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createaudiogroup group_id>>\n\t\t\t\t<<track track_id>>\n\t\t\t\t…\n\t\t\t<</createaudiogroup>>\n\t\t*/\n\t\tMacro.add('createaudiogroup', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst groupId  = String(this.args[0]).trim();\n\t\t\t\tconst trackIds = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length < 1) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackIds.push(String(this.payload[i].args[0]).trim());\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.groups.add(groupId, trackIds);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createplaylist list_id>>\n\t\t\t\t<<track track_id action_list>>\n\t\t\t\t…\n\t\t\t<</createplaylist>>\n\t\t*/\n\t\tMacro.add('createplaylist', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'createplaylist') {\n\t\t\t\t\treturn this.error('a playlist has already been defined with <<setplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst listId    = String(this.args[0]).trim();\n\t\t\t\tconst trackObjs = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst trackObj = { id : String(this.payload[i].args[0]).trim() };\n\t\t\t\t\tconst args     = this.payload[i].args.slice(1);\n\n\t\t\t\t\t// Process arguments.\n\t\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\t\tlet raw;\n\t\t\t\t\t\tlet parsed;\n\n\t\t\t\t\t\tswitch (arg) {\n\t\t\t\t\t\tcase 'copy': // [DEPRECATED]\n\t\t\t\t\t\tcase 'own':\n\t\t\t\t\t\t\ttrackObj.own = true;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'rate':\n\t\t\t\t\t\t\t// if (args.length === 0) {\n\t\t\t\t\t\t\t// \treturn this.error('rate missing required speed value');\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// raw = args.shift();\n\t\t\t\t\t\t\t// parsed = Number.parseFloat(raw);\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// if (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t// \treturn this.error(`cannot parse rate: ${raw}`);\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// trackObj.rate = parsed;\n\t\t\t\t\t\t\tif (args.length > 0) {\n\t\t\t\t\t\t\t\targs.shift();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\t\tparsed = Number.parseFloat(raw);\n\n\t\t\t\t\t\t\tif (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttrackObj.volume = parsed;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackObjs.push(trackObj);\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add(listId, trackObjs);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'createplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<masteraudio action_list>>\n\t\t*/\n\t\tMacro.add('masteraudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(0);\n\t\t\t\tlet action;\n\t\t\t\tlet mute;\n\t\t\t\tlet muteOnHide;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'muteonhide':\n\t\t\t\t\tcase 'nomuteonhide':\n\t\t\t\t\t\tmuteOnHide = arg === 'muteonhide';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (muteOnHide != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.muteOnHidden(muteOnHide);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tSimpleAudio.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tSimpleAudio.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tSimpleAudio.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<playlist list_id action_list>>  ← <<createplaylist>> syntax\n\t\t\t<<playlist action_list>>          ← <<setplaylist>> syntax\n\t\t*/\n\t\tMacro.add('playlist', {\n\t\t\tfrom : null,\n\n\t\t\thandler() {\n\t\t\t\tconst from = this.self.from;\n\n\t\t\t\tif (from === null) {\n\t\t\t\t\treturn this.error('no playlists have been created');\n\t\t\t\t}\n\n\t\t\t\tlet list;\n\t\t\t\tlet args;\n\n\t\t\t\tif (from === 'createplaylist') {\n\t\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\tif (this.args.length < 1) { errors.push('list ID'); }\n\t\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get(id);\n\t\t\t\t\targs = this.args.slice(1);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get('setplaylist');\n\t\t\t\t\targs = this.args.slice(0);\n\t\t\t\t}\n\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet shuffle;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'shuffle':\n\t\t\t\t\tcase 'unshuffle':\n\t\t\t\t\t\tshuffle = arg === 'shuffle';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tlist.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tlist.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tlist.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (shuffle != null) { // lazy equality for null\n\t\t\t\t\t\tlist.shuffle(shuffle);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tlist.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tlist.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tlist.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tlist.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\t\tlist.skip();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tlist.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tlist.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeaudiogroup group_id>>\n\t\t*/\n\t\tMacro.add('removeaudiogroup', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.groups.has(id)) {\n\t\t\t\t\treturn this.error(`group \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.groups.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeplaylist list_id>>\n\t\t*/\n\t\tMacro.add('removeplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.lists.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<waitforaudio>>\n\t\t*/\n\t\tMacro.add('waitforaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.loadWithScreen();\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<setplaylist track_id_list>>\n\t\t*/\n\t\tMacro.add('setplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no track ID(s) specified');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'setplaylist') {\n\t\t\t\t\treturn this.error('playlists have already been defined with <<createplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Create the new playlist.\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add('setplaylist', this.args.slice(0));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'setplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<stopallaudio>>\n\t\t*/\n\t\tMacro.add('stopallaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.select(':all').stop();\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\telse {\n\t\t/* The HTML5 <audio> API appears to be missing or disabled, set up no-op macros. */\n\t\tMacro.add([\n\t\t\t'audio',\n\t\t\t'cacheaudio',\n\t\t\t'createaudiogroup',\n\t\t\t'createplaylist',\n\t\t\t'masteraudio',\n\t\t\t'playlist',\n\t\t\t'removeaudiogroup',\n\t\t\t'removeplaylist',\n\t\t\t'waitforaudio',\n\n\t\t\t// Deprecated.\n\t\t\t'setplaylist',\n\t\t\t'stopallaudio'\n\t\t], {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\t/* no-op */\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMiscellaneous Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<goto>>\n\t*/\n\tMacro.add('goto', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCall `Engine.play()` asynchronously.\n\n\t\t\t\tNOTE: This does not terminate the current Wikifier call chain,\n\t\t\t\tthough, ideally, it should.  Doing so would not be trivial, however,\n\t\t\t\tand there's also the question of whether that behavior would be\n\t\t\t\tunwanted by users, who are used to the current behavior from\n\t\t\t\tsimilar macros and constructs.\n\t\t\t*/\n\t\t\tsetTimeout(() => Engine.play(passage), Engine.minDomActionDelay);\n\t\t}\n\t});\n\n\t/*\n\t\t<<repeat>> & <<stop>>\n\t*/\n\tMacro.add('repeat', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified');\n\t\t\t}\n\n\t\t\tlet delay;\n\n\t\t\ttry {\n\t\t\t\tdelay = Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0]));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerInterval(this.createShadowWrapper(() => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-repeat-insert macro-repeat-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-repeat-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), delay);\n\t\t},\n\n\t\tregisterInterval(callback, delay) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId = null;\n\n\t\t\t// Set up the interval.\n\t\t\ttimerId = setInterval(() => {\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\tclearInterval(timerId);\n\t\t\t\t\ttimers.delete(timerId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet timerIdCache;\n\t\t\t\t/*\n\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t`Wikifier` call.\n\t\t\t\t*/\n\t\t\t\ttry {\n\t\t\t\t\tTempState.break = null;\n\n\t\t\t\t\t// Set up the `repeatTimerId` value, caching the existing value, if necessary.\n\t\t\t\t\tif (TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\t\t\ttimerIdCache = TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.repeatTimerId = timerId;\n\n\t\t\t\t\t// Execute the callback.\n\t\t\t\t\tcallback.call(this);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\t// Teardown the `repeatTimerId` property, restoring the cached value, if necessary.\n\t\t\t\t\tif (typeof timerIdCache !== 'undefined') {\n\t\t\t\t\t\tTempState.repeatTimerId = timerIdCache;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.break = null;\n\t\t\t\t}\n\t\t\t}, delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#repeat-timers-cleanup')) {\n\t\t\t\tprehistory['#repeat-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearInterval(timerId));\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\tMacro.add('stop', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (!TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<repeat>>');\n\t\t\t}\n\n\t\t\tconst timers  = Macro.get('repeat').timers;\n\t\t\tconst timerId = TempState.repeatTimerId;\n\t\t\tclearInterval(timerId);\n\t\t\ttimers.delete(timerId);\n\t\t\tTempState.break = 2;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<timed>> & <<next>>\n\t*/\n\tMacro.add('timed', {\n\t\tisAsync : true,\n\t\ttags    : ['next'],\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified in <<timed>>');\n\t\t\t}\n\n\t\t\tconst items = [];\n\n\t\t\ttry {\n\t\t\t\titems.push({\n\t\t\t\t\tname    : this.name,\n\t\t\t\t\tsource  : this.source,\n\t\t\t\t\tdelay   : Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0])),\n\t\t\t\t\tcontent : this.payload[0].contents\n\t\t\t\t});\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`${ex.message} in <<timed>>`);\n\t\t\t}\n\n\t\t\tif (this.payload.length > 1) {\n\t\t\t\tlet i;\n\n\t\t\t\ttry {\n\t\t\t\t\tlet len;\n\n\t\t\t\t\tfor (i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tname   : this.payload[i].name,\n\t\t\t\t\t\t\tsource : this.payload[i].source,\n\t\t\t\t\t\t\tdelay  : this.payload[i].args.length === 0\n\t\t\t\t\t\t\t\t? items[items.length - 1].delay\n\t\t\t\t\t\t\t\t: Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.payload[i].args[0])),\n\t\t\t\t\t\t\tcontent : this.payload[i].contents\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`${ex.message} in <<next>> (#${i})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerTimeout(this.createShadowWrapper(item => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, item.content);\n\n\t\t\t\t// Output.\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\t// Custom debug view setup for `<<next>>`.\n\t\t\t\tif (Config.debug && item.name === 'next') {\n\t\t\t\t\t$output = jQuery(new DebugView( // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t$output[0],\n\t\t\t\t\t\t'macro',\n\t\t\t\t\t\titem.name,\n\t\t\t\t\t\titem.source\n\t\t\t\t\t).output);\n\t\t\t\t}\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-timed-insert macro-timed-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-timed-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), items);\n\t\t},\n\n\t\tregisterTimeout(callback, items) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId  = null;\n\t\t\tlet nextItem = items.shift();\n\n\t\t\tconst worker = function () {\n\t\t\t\t// Bookkeeping.\n\t\t\t\ttimers.delete(timerId);\n\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the current item and set up the next worker, if any.\n\t\t\t\tconst curItem = nextItem;\n\n\t\t\t\tif ((nextItem = items.shift()) != null) { // lazy equality for null\n\t\t\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\t\t\ttimers.add(timerId);\n\t\t\t\t}\n\n\t\t\t\t// Execute the callback.\n\t\t\t\tcallback.call(this, curItem);\n\t\t\t};\n\n\t\t\t// Setup the timeout.\n\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#timed-timers-cleanup')) {\n\t\t\t\tprehistory['#timed-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearTimeout(timerId)); // eslint-disable-line no-shadow\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<widget>>\n\t*/\n\tMacro.add('widget', {\n\t\ttags : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no widget name specified');\n\t\t\t}\n\n\t\t\tconst widgetName = this.args[0];\n\n\t\t\tif (Macro.has(widgetName)) {\n\t\t\t\tif (!Macro.get(widgetName).isWidget) {\n\t\t\t\t\treturn this.error(`cannot clobber existing macro \"${widgetName}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Delete the existing widget.\n\t\t\t\tMacro.delete(widgetName);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMacro.add(widgetName, {\n\t\t\t\t\tisWidget : true,\n\t\t\t\t\thandler  : (function (contents) {\n\t\t\t\t\t\treturn function () {\n\t\t\t\t\t\t\tlet argsCache;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Cache the existing value of the `$args` variable, if necessary.\n\t\t\t\t\t\t\t\tif (State.variables.hasOwnProperty('args')) {\n\t\t\t\t\t\t\t\t\targsCache = State.variables.args;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set up the widget `$args` variable and add a shadow.\n\t\t\t\t\t\t\t\tState.variables.args = [...this.args];\n\t\t\t\t\t\t\t\tState.variables.args.raw = this.args.raw;\n\t\t\t\t\t\t\t\tState.variables.args.full = this.args.full;\n\t\t\t\t\t\t\t\tthis.addShadow('$args');\n\n\t\t\t\t\t\t\t\t// Set up the error trapping variables.\n\t\t\t\t\t\t\t\tconst resFrag = document.createDocumentFragment();\n\t\t\t\t\t\t\t\tconst errList = [];\n\n\t\t\t\t\t\t\t\t// Wikify the widget contents.\n\t\t\t\t\t\t\t\tnew Wikifier(resFrag, contents);\n\n\t\t\t\t\t\t\t\t// Carry over the output, unless there were errors.\n\t\t\t\t\t\t\t\tArray.from(resFrag.querySelectorAll('.error')).forEach(errEl => {\n\t\t\t\t\t\t\t\t\terrList.push(errEl.textContent);\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tif (errList.length === 0) {\n\t\t\t\t\t\t\t\t\tthis.output.appendChild(resFrag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\treturn this.error(`error${errList.length > 1 ? 's' : ''} within widget contents (${errList.join('; ')})`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot execute widget: ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t// Revert the `$args` variable shadowing.\n\t\t\t\t\t\t\t\tif (typeof argsCache !== 'undefined') {\n\t\t\t\t\t\t\t\t\tState.variables.args = argsCache;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete State.variables.args;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t})(this.payload[0].contents)\n\t\t\t\t});\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`cannot create widget macro \"${widgetName}\": ${ex.message}`);\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tdialog.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, L10n, safeActiveElement */\n\nvar Dialog = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Dialog element caches.\n\tlet _$overlay       = null;\n\tlet _$dialog        = null;\n\tlet _$dialogTitle   = null;\n\tlet _$dialogBody    = null;\n\n\t// The last active/focused non-dialog element.\n\tlet _lastActive     = null;\n\n\t// The width of the browser's scrollbars.\n\tlet _scrollbarWidth = 0;\n\n\t// Dialog mutation resize handler.\n\tlet _dialogObserver = null;\n\n\n\t/*******************************************************************************\n\t\tDialog Functions.\n\t*******************************************************************************/\n\n\t/*\n\t\t[DEPRECATED] Adds a click hander to the target element(s) which opens the dialog modal.\n\t*/\n\tfunction dialogAddClickHandler(targets, options, startFn, doneFn, closeFn) {\n\t\treturn jQuery(targets).ariaClick(ev => {\n\t\t\tev.preventDefault();\n\n\t\t\t// Call the start function.\n\t\t\tif (typeof startFn === 'function') {\n\t\t\t\tstartFn(ev);\n\t\t\t}\n\n\t\t\t// Open the dialog.\n\t\t\tdialogOpen(options, closeFn);\n\n\t\t\t// Call the done function.\n\t\t\tif (typeof doneFn === 'function') {\n\t\t\t\tdoneFn(ev);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction dialogBodyAppend(...args) {\n\t\t_$dialogBody.append(...args);\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogBody() {\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogClose(ev) {\n\t\t// Trigger a `:dialogclosing` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogclosing');\n\n\t\t// Largely reverse the actions taken in `dialogOpen()`.\n\t\tjQuery(document)\n\t\t\t.off('.dialog-close');\n\t\tif (_dialogObserver) {\n\t\t\t_dialogObserver.disconnect();\n\t\t\t_dialogObserver = null;\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.off('.dialog-resize');\n\t\t}\n\t\tjQuery(window)\n\t\t\t.off('.dialog-resize');\n\t\t_$dialog\n\t\t\t.removeClass('open')\n\t\t\t.css({ left : '', right : '', top : '', bottom : '' });\n\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex=-2]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.attr('tabindex', 0);\n\t\tjQuery('body>[tabindex=-3]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.removeAttr('tabindex');\n\n\t\t_$overlay\n\t\t\t.removeClass('open');\n\t\tjQuery(document.documentElement)\n\t\t\t.removeAttr('data-dialog');\n\n\t\t// Clear the dialog's content.\n\t\t_$dialogTitle\n\t\t\t.empty();\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\t// Attempt to restore focus to whichever element had it prior to opening the dialog.\n\t\tif (_lastActive !== null) {\n\t\t\tjQuery(_lastActive).focus();\n\t\t\t_lastActive = null;\n\t\t}\n\n\t\t// Call the given \"on close\" callback function, if any.\n\t\tif (ev && ev.data && typeof ev.data.closeFn === 'function') {\n\t\t\tev.data.closeFn(ev);\n\t\t}\n\n\t\t// Trigger a `:dialogclosed` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogclose');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogclosed');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogInit() {\n\t\tif (DEBUG) { console.log('[Dialog/dialogInit()]'); }\n\n\t\tif (document.getElementById('ui-dialog')) {\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t\tCalculate and cache the width of scrollbars.\n\t\t*/\n\t\t_scrollbarWidth = (() => {\n\t\t\tlet scrollbarWidth;\n\n\t\t\ttry {\n\t\t\t\tconst inner = document.createElement('p');\n\t\t\t\tconst outer = document.createElement('div');\n\n\t\t\t\tinner.style.width      = '100%';\n\t\t\t\tinner.style.height     = '200px';\n\t\t\t\touter.style.position   = 'absolute';\n\t\t\t\touter.style.left       = '0px';\n\t\t\t\touter.style.top        = '0px';\n\t\t\t\touter.style.width      = '100px';\n\t\t\t\touter.style.height     = '100px';\n\t\t\t\touter.style.visibility = 'hidden';\n\t\t\t\touter.style.overflow   = 'hidden';\n\n\t\t\t\touter.appendChild(inner);\n\t\t\t\tdocument.body.appendChild(outer);\n\n\t\t\t\tconst w1 = inner.offsetWidth;\n\t\t\t\t/*\n\t\t\t\t\tThe `overflow: scroll` style property value does not work consistently\n\t\t\t\t\twith scrollbars which are styled with `::-webkit-scrollbar`, so we use\n\t\t\t\t\t`overflow: auto` with dimensions guaranteed to force a scrollbar.\n\t\t\t\t*/\n\t\t\t\touter.style.overflow = 'auto';\n\t\t\t\tlet w2 = inner.offsetWidth;\n\n\t\t\t\tif (w1 === w2) {\n\t\t\t\t\tw2 = outer.clientWidth;\n\t\t\t\t}\n\n\t\t\t\tdocument.body.removeChild(outer);\n\n\t\t\t\tscrollbarWidth = w1 - w2;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn scrollbarWidth || 17; // 17px is a reasonable failover\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate the dialog elements.\n\t\t*/\n\t\tconst $elems = jQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"ui-overlay\" class=\"ui-close\"></div>'\n\t\t\t\t+ '<div id=\"ui-dialog\" tabindex=\"0\" role=\"dialog\" aria-labelledby=\"ui-dialog-title\">'\n\t\t\t\t+     '<div id=\"ui-dialog-titlebar\">'\n\t\t\t\t+         '<h1 id=\"ui-dialog-title\"></h1>'\n\t\t\t\t+         `<button id=\"ui-dialog-close\" class=\"ui-close\" tabindex=\"0\" aria-label=\"${L10n.get('close')}\">\\uE804</button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div id=\"ui-dialog-body\"></div>'\n\t\t\t\t+ '</div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t/*\n\t\t\tCache the dialog elements, since they're going to be used often.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$overlay     = jQuery($elems.find('#ui-overlay').get(0));\n\t\t_$dialog      = jQuery($elems.find('#ui-dialog').get(0));\n\t\t_$dialogTitle = jQuery($elems.find('#ui-dialog-title').get(0));\n\t\t_$dialogBody  = jQuery($elems.find('#ui-dialog-body').get(0));\n\n\t\t/*\n\t\t\tInsert the dialog elements into the page before the main script.\n\t\t*/\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t}\n\n\tfunction dialogIsOpen(classNames) {\n\t\treturn _$dialog.hasClass('open')\n\t\t\t&& (classNames ? classNames.splitOrEmpty(/\\s+/).every(cn => _$dialogBody.hasClass(cn)) : true);\n\t}\n\n\tfunction dialogOpen(options, closeFn) {\n\t\t// Trigger a `:dialogopening` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogopening');\n\n\t\t// Grab the options we care about.\n\t\tconst { top } = jQuery.extend({ top : 50 }, options);\n\n\t\t// Record the last active/focused non-dialog element.\n\t\tif (!dialogIsOpen()) {\n\t\t\t_lastActive = safeActiveElement();\n\t\t}\n\n\t\t// Add the `data-dialog` attribute to <html> (mostly used to style <body>).\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-dialog', 'open');\n\n\t\t// Display the overlay.\n\t\t_$overlay\n\t\t\t.addClass('open');\n\n\t\t/*\n\t\t\tAdd the imagesLoaded handler to the dialog body, if necessary.\n\n\t\t\tNOTE: We use `querySelector()` here as jQuery has no simple way to\n\t\t\tcheck if, and only if, at least one element of the specified type\n\t\t\texists.  The best that jQuery offers is analogous to `querySelectorAll()`,\n\t\t\twhich enumerates all elements of the specified type.\n\t\t*/\n\t\tif (_$dialogBody[0].querySelector('img') !== null) {\n\t\t\t_$dialogBody\n\t\t\t\t.imagesLoaded()\n\t\t\t\t.always(() => _resizeHandler({ data : { top } }));\n\t\t}\n\n\t\t// Add `aria-hidden=true` to all direct non-dialog-children of <body> to\n\t\t// hide the underlying page from screen readers while the dialog is open.\n\t\tjQuery('body>:not(script,#store-area,tw-storydata,#ui-bar,#ui-overlay,#ui-dialog)')\n\t\t\t.attr('tabindex', -3)\n\t\t\t.attr('aria-hidden', true);\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex]:not([tabindex^=-])')\n\t\t\t.attr('tabindex', -2)\n\t\t\t.attr('aria-hidden', true);\n\n\t\t// Display the dialog.\n\t\t_$dialog\n\t\t\t.css(_calcPosition(top))\n\t\t\t.addClass('open')\n\t\t\t.focus();\n\n\t\t// Add the UI resize handler.\n\t\tjQuery(window)\n\t\t\t.on('resize.dialog-resize', null, { top }, jQuery.throttle(40, _resizeHandler));\n\n\t\t// Add the dialog mutation resize handler.\n\t\tif (Has.mutationObserver) {\n\t\t\t_dialogObserver = new MutationObserver(mutations => {\n\t\t\t\tfor (let i = 0; i < mutations.length; ++i) {\n\t\t\t\t\tif (mutations[i].type === 'childList') {\n\t\t\t\t\t\t_resizeHandler({ data : { top } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t_dialogObserver.observe(_$dialogBody[0], {\n\t\t\t\tchildList : true,\n\t\t\t\tsubtree   : true\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.on(\n\t\t\t\t\t'DOMNodeInserted.dialog-resize DOMNodeRemoved.dialog-resize',\n\t\t\t\t\tnull,\n\t\t\t\t\t{ top },\n\t\t\t\t\tjQuery.throttle(40, _resizeHandler)\n\t\t\t\t);\n\t\t}\n\n\t\t// Set up the delegated UI close handler.\n\t\tjQuery(document)\n\t\t\t.one('click.dialog-close', '.ui-close', { closeFn }, ev => {\n\t\t\t\t// NOTE: Do not allow this event handler to return the `Dialog` static object,\n\t\t\t\t// as doing so causes Edge (ca. 18) to throw a \"Number expected\" exception due\n\t\t\t\t// to `Dialog` not having a prototype.\n\t\t\t\tdialogClose(ev);\n\t\t\t\t/* implicit `return undefined;` */\n\t\t\t})\n\t\t\t.one('keypress.dialog-close', '.ui-close', function (ev) {\n\t\t\t\t// 13 is Enter/Return, 32 is Space.\n\t\t\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\t\t\tjQuery(this).trigger('click');\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Trigger a `:dialogopened` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogopen');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogopened');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogResize(data) {\n\t\treturn _resizeHandler(typeof data === 'object' ? { data } : undefined);\n\t}\n\n\tfunction dialogSetup(title, classNames) {\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\tif (classNames != null) { // lazy equality for null\n\t\t\t_$dialogBody.addClass(classNames);\n\t\t}\n\n\t\t_$dialogTitle\n\t\t\t.empty()\n\t\t\t.append((title != null ? String(title) : '') || '\\u00A0'); // lazy equality for null\n\n\t\t// TODO: In v3 this should return `Dialog` for chaining.\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogBodyWiki(...args) {\n\t\t_$dialogBody.wiki(...args);\n\t\treturn Dialog;\n\t}\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _calcPosition(topPos) {\n\t\tconst top       = topPos != null ? topPos : 50; // lazy equality for null\n\t\tconst $parent   = jQuery(window);\n\t\tconst dialogPos = { left : '', right : '', top : '', bottom : '' };\n\n\t\t// Unset the dialog's positional properties before checking its dimensions.\n\t\t_$dialog.css(dialogPos);\n\n\t\tlet horzSpace = $parent.width() - _$dialog.outerWidth(true) - 1;   // -1 to address a Firefox issue\n\t\tlet vertSpace = $parent.height() - _$dialog.outerHeight(true) - 1; // -1 to address a Firefox issue\n\n\t\tif (horzSpace <= 32 + _scrollbarWidth) {\n\t\t\tvertSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (vertSpace <= 32 + _scrollbarWidth) {\n\t\t\thorzSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (horzSpace <= 32) {\n\t\t\tdialogPos.left = dialogPos.right = 16;\n\t\t}\n\t\telse {\n\t\t\tdialogPos.left = dialogPos.right = horzSpace / 2 >> 0;\n\t\t}\n\n\t\tif (vertSpace <= 32) {\n\t\t\tdialogPos.top = dialogPos.bottom = 16;\n\t\t}\n\t\telse {\n\t\t\tif (vertSpace / 2 > top) {\n\t\t\t\tdialogPos.top = top;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdialogPos.top = dialogPos.bottom = vertSpace / 2 >> 0;\n\t\t\t}\n\t\t}\n\n\t\tObject.keys(dialogPos).forEach(key => {\n\t\t\tif (dialogPos[key] !== '') {\n\t\t\t\tdialogPos[key] += 'px';\n\t\t\t}\n\t\t});\n\n\t\treturn dialogPos;\n\t}\n\n\tfunction _resizeHandler(ev) {\n\t\tconst top = ev && ev.data && typeof ev.data.top !== 'undefined' ? ev.data.top : 50;\n\n\t\tif (_$dialog.css('display') === 'block') {\n\t\t\t// Stow the dialog.\n\t\t\t_$dialog.css({ display : 'none' });\n\n\t\t\t// Restore the dialog with its new positional properties.\n\t\t\t_$dialog.css(jQuery.extend({ display : '' }, _calcPosition(top)));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tappend : { value : dialogBodyAppend },\n\t\tbody   : { value : dialogBody },\n\t\tclose  : { value : dialogClose },\n\t\tinit   : { value : dialogInit },\n\t\tisOpen : { value : dialogIsOpen },\n\t\topen   : { value : dialogOpen },\n\t\tresize : { value : dialogResize },\n\t\tsetup  : { value : dialogSetup },\n\t\twiki   : { value : dialogBodyWiki },\n\n\t\t// Legacy Functions.\n\t\taddClickHandler : { value : dialogAddClickHandler }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tengine.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Config, DebugView, Dialog, Has, LoadScreen, Save, State, Story, StyleWrapper, UI, UIBar, Util,\n\t       Wikifier, postdisplay, postrender, predisplay, prehistory, prerender, setDisplayTitle\n*/\n\nvar Engine = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Engine state types object (pseudo-enumeration).\n\tconst States = Util.toEnum({\n\t\tIdle      : 'idle',\n\t\tPlaying   : 'playing',\n\t\tRendering : 'rendering'\n\t});\n\n\t// Minimum delay for DOM actions (in milliseconds).\n\tconst minDomActionDelay = 40;\n\n\t// Current state of the engine (default: `Engine.States.Idle`).\n\tlet _state = States.Idle;\n\n\t// Last time `enginePlay()` was called (in milliseconds).\n\tlet _lastPlay = null;\n\n\t// Cache of the debug view for the StoryInit special passage.\n\tlet _storyInitDebugView = null;\n\n\t// Cache of the outline patching <style> element (`StyleWrapper`-wrapped).\n\tlet _outlinePatch = null;\n\n\t// List of objects describing `StoryInterface` elements to update via passages during navigation.\n\tlet _updating = null;\n\n\n\t/*******************************************************************************************************************\n\t\tEngine Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize the core story elements and perform some bookkeeping.\n\t*/\n\tfunction engineInit() {\n\t\tif (DEBUG) { console.log('[Engine/engineInit()]'); }\n\n\t\t/*\n\t\t\tRemove #init-no-js & #init-lacking from #init-screen.\n\t\t*/\n\t\tjQuery('#init-no-js,#init-lacking').remove();\n\n\t\t/*\n\t\t\tGenerate the core story elements and insert them into the page before the store area.\n\t\t*/\n\t\t(() => {\n\t\t\tconst $elems = jQuery(document.createDocumentFragment());\n\t\t\tconst markup = Story.has('StoryInterface') && Story.get('StoryInterface').text.trim();\n\n\t\t\tif (markup) {\n\t\t\t\t// Remove the UI bar, its styles, and events.\n\t\t\t\tUIBar.destroy();\n\n\t\t\t\t// Remove the core display area styles.\n\t\t\t\tjQuery(document.head).find('#style-core-display').remove();\n\n\t\t\t\t$elems.append(markup);\n\n\t\t\t\tif ($elems.find('#passages').length === 0) {\n\t\t\t\t\tthrow new Error('no element with ID \"passages\" found within \"StoryInterface\" special passage');\n\t\t\t\t}\n\n\t\t\t\tconst updating = [];\n\n\t\t\t\t$elems.find('[data-passage]').each((i, el) => {\n\t\t\t\t\tif (el.id === 'passages') {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} id=\"passages\"> must not contain a \"data-passage\" content attribute`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst passage = el.getAttribute('data-passage').trim();\n\n\t\t\t\t\tif (el.firstElementChild !== null) {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} data-passage=\"${passage}\"> contains child elements`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tupdating.push({\n\t\t\t\t\t\t\tpassage,\n\t\t\t\t\t\t\telement : el\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (updating.length > 0) {\n\t\t\t\t\t_updating = updating;\n\t\t\t\t}\n\n\t\t\t\tConfig.ui.updateStoryElements = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$elems.append('<div id=\"story\" role=\"main\"><div id=\"passages\"></div></div>');\n\t\t\t}\n\n\t\t\t// Insert the core UI elements into the page before the main script.\n\t\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate and cache the ARIA outlines <style> element (`StyleWrapper`-wrapped)\n\t\t\tand set up the handler to manipulate the outlines.\n\n\t\t\tIDEA: http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/\n\t\t*/\n\t\t_outlinePatch = new StyleWrapper((\n\t\t\t() => jQuery(document.createElement('style'))\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-aria-outlines',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t})\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.get(0) // return the <style> element itself\n\t\t)());\n\t\t_hideOutlines(); // initially hide outlines\n\t\tlet _lastOutlineEvent;\n\t\tjQuery(document).on(\n\t\t\t'mousedown.aria-outlines keydown.aria-outlines',\n\t\t\tev => {\n\t\t\t\tif (ev.type !== _lastOutlineEvent) {\n\t\t\t\t\t_lastOutlineEvent = ev.type;\n\n\t\t\t\t\tif (ev.type === 'keydown') {\n\t\t\t\t\t\t_showOutlines();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t_hideOutlines();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\n\t/*\n\t\tStarts the story.\n\t*/\n\tfunction engineStart() {\n\t\tif (DEBUG) { console.log('[Engine/engineStart()]'); }\n\n\t\t/*\n\t\t\tExecute the StoryInit special passage.\n\t\t*/\n\t\tif (Story.has('StoryInit')) {\n\t\t\ttry {\n\t\t\t\tconst debugBuffer = Wikifier.wikifyEval(Story.get('StoryInit').text);\n\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tconst debugView = new DebugView(\n\t\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t\t'special',\n\t\t\t\t\t\t'StoryInit',\n\t\t\t\t\t\t'StoryInit'\n\t\t\t\t\t);\n\t\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\t\tdebugView.append(debugBuffer);\n\t\t\t\t\t_storyInitDebugView = debugView.output;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('StoryInit', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Sanity checks.\n\t\tif (Config.passages.start == null) { // lazy equality for null\n\t\t\tthrow new Error('starting passage not selected');\n\t\t}\n\t\tif (!Story.has(Config.passages.start)) {\n\t\t\tthrow new Error(`starting passage (\"${Config.passages.start}\") not found`);\n\t\t}\n\n\t\t// Focus the document element initially.\n\t\tjQuery(document.documentElement).focus();\n\n\t\t/*\n\t\t\tAttempt to restore an active session.  Failing that, attempt to autoload the autosave,\n\t\t\tif requested.  Failing that, display the starting passage.\n\t\t*/\n\t\tif (State.restore()) {\n\t\t\tengineShow();\n\t\t}\n\t\telse {\n\t\t\tlet loadStart = true;\n\n\t\t\tswitch (typeof Config.saves.autoload) {\n\t\t\tcase 'boolean':\n\t\t\t\tif (Config.saves.autoload && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tif (Config.saves.autoload === 'prompt' && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tloadStart = false;\n\t\t\t\t\tUI.buildAutoload();\n\t\t\t\t\tDialog.open();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'function':\n\t\t\t\tif (Save.autosave.ok() && Save.autosave.has() && !!Config.saves.autoload()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (loadStart) {\n\t\t\t\tif (DEBUG) { console.log(`\\tstarting passage: \"${Config.passages.start}\"`); }\n\n\t\t\t\tenginePlay(Config.passages.start);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t\tRestarts the story.\n\t*/\n\tfunction engineRestart() {\n\t\tif (DEBUG) { console.log('[Engine/engineRestart()]'); }\n\n\t\t/*\n\t\t\tShow the loading screen to hide any unsightly rendering shenanigans during the\n\t\t\tpage reload.\n\t\t*/\n\t\tLoadScreen.show();\n\n\t\t/*\n\t\t\tScroll the window to the top.\n\n\t\t\tThis is required by most browsers for the starting passage or it will remain at\n\t\t\twhatever its current scroll position is after the page reload.  We do it generally,\n\t\t\trather than only for the currently set starting passage, since the starting passage\n\t\t\tmay be dynamically manipulated.\n\t\t*/\n\t\twindow.scroll(0, 0);\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tState.reset();\n\n\t\t/*\n\t\t\tTrigger an ':enginerestart' event.\n\t\t*/\n\t\tjQuery.event.trigger(':enginerestart');\n\n\t\t/*\n\t\t\tReload the page.\n\t\t*/\n\t\twindow.location.reload();\n\t}\n\n\t/*\n\t\tReturns the current state of the engine.\n\t*/\n\tfunction engineState() {\n\t\treturn _state;\n\t}\n\n\t/*\n\t\tReturns whether the engine is idle.\n\t*/\n\tfunction engineIsIdle() {\n\t\treturn _state === States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is playing.\n\t*/\n\tfunction engineIsPlaying() {\n\t\treturn _state !== States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is rendering.\n\t*/\n\tfunction engineIsRendering() {\n\t\treturn _state === States.Rendering;\n\t}\n\n\t/*\n\t\tReturns a timestamp representing the last time `Engine.play()` was called.\n\t*/\n\tfunction engineLastPlay() {\n\t\treturn _lastPlay;\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the state history and show it.\n\t*/\n\tfunction engineGoTo(idx) {\n\t\tconst succeded = State.goTo(idx);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the state history\n\t\tand show it.\n\t*/\n\tfunction engineGo(offset) {\n\t\tconst succeded = State.go(offset);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tGo to the moment which directly precedes the active moment and show it.\n\t*/\n\tfunction engineBackward() {\n\t\treturn engineGo(-1);\n\t}\n\n\t/*\n\t\tGo to the moment which directly follows the active moment and show it.\n\t*/\n\tfunction engineForward() {\n\t\treturn engineGo(1);\n\t}\n\n\t/*\n\t\tRenders and displays the active (present) moment's associated passage without adding\n\t\ta new moment to the history.\n\t*/\n\tfunction engineShow() {\n\t\treturn enginePlay(State.passage, true);\n\t}\n\n\t/*\n\t\tRenders and displays the passage referenced by the given title, optionally without\n\t\tadding a new moment to the history.\n\t*/\n\tfunction enginePlay(title, noHistory) {\n\t\tif (DEBUG) { console.log(`[Engine/enginePlay(title: \"${title}\", noHistory: ${noHistory})]`); }\n\n\t\tlet passageTitle = title;\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Reset the temporary state and variables objects.\n\t\tTempState = {}; // eslint-disable-line no-undef\n\t\tState.clearTemporary();\n\n\t\t// Debug view setup.\n\t\tlet passageReadyOutput;\n\t\tlet passageDoneOutput;\n\n\t\t// Execute the navigation override callback.\n\t\tif (typeof Config.navigation.override === 'function') {\n\t\t\ttry {\n\t\t\t\tconst overrideTitle = Config.navigation.override(passageTitle);\n\n\t\t\t\tif (overrideTitle) {\n\t\t\t\t\tpassageTitle = overrideTitle;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\t\t}\n\n\t\t// Retrieve the passage by the given title.\n\t\t//\n\t\t// NOTE: The values of the `title` parameter and `passageTitle` variable\n\t\t// may be empty, strings, or numbers (though using a number as reference\n\t\t// to a numeric title should be discouraged), so after loading the passage,\n\t\t// always refer to `passage.title` and never to the others.\n\t\tconst passage = Story.get(passageTitle);\n\n\t\t// Execute the pre-history events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype : ':passageinit',\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prehistory).forEach(task => {\n\t\t\tif (typeof prehistory[task] === 'function') {\n\t\t\t\tprehistory[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Create a new entry in the history.\n\t\tif (!noHistory) {\n\t\t\tState.create(passage.title);\n\t\t}\n\n\t\t// Clear the document body's classes.\n\t\tif (document.body.className) {\n\t\t\tdocument.body.className = '';\n\t\t}\n\n\t\t// Update the last play time.\n\t\t//\n\t\t// NOTE: This is mostly for event, task, and special passage code,\n\t\t// though the likelihood of it being needed this early is low.  This\n\t\t// will be updated again later at the end.\n\t\t_lastPlay = Util.now();\n\n\t\t// Execute pre-display tasks and the `PassageReady` special passage.\n\t\tObject.keys(predisplay).forEach(task => {\n\t\t\tif (typeof predisplay[task] === 'function') {\n\t\t\t\tpredisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\tif (Story.has('PassageReady')) {\n\t\t\ttry {\n\t\t\t\tpassageReadyOutput = Wikifier.wikifyEval(Story.get('PassageReady').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageReady', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Update the engine state.\n\t\t_state = States.Rendering;\n\n\t\t// Get the passage's tags as a string, or `null` if there aren't any.\n\t\tconst dataTags = passage.tags.length > 0 ? passage.tags.join(' ') : null;\n\n\t\t// Create and set up the incoming passage element.\n\t\tconst passageEl = document.createElement('div');\n\t\tjQuery(passageEl)\n\t\t\t.attr({\n\t\t\t\tid             : passage.domId,\n\t\t\t\t'data-passage' : passage.title,\n\t\t\t\t'data-tags'    : dataTags\n\t\t\t})\n\t\t\t.addClass(`passage ${passage.className}`);\n\n\t\t// Add the passage's classes and tags to the document body.\n\t\tjQuery(document.body)\n\t\t\t.attr('data-tags', dataTags)\n\t\t\t.addClass(passage.className);\n\n\t\t// Add the passage's tags to the document element.\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-tags', dataTags);\n\n\t\t// Execute pre-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagestart',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prerender).forEach(task => {\n\t\t\tif (typeof prerender[task] === 'function') {\n\t\t\t\tprerender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Render the `PassageHeader` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageHeader')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageHeader').processText());\n\t\t}\n\n\t\t// Render the passage into its element.\n\t\tpassageEl.appendChild(passage.render());\n\n\t\t// Render the `PassageFooter` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageFooter')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageFooter').processText());\n\t\t}\n\n\t\t// Execute post-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagerender',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postrender).forEach(task => {\n\t\t\tif (typeof postrender[task] === 'function') {\n\t\t\t\tpostrender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Cache the passage container.\n\t\tconst containerEl = document.getElementById('passages');\n\n\t\t// Empty the passage container.\n\t\tif (containerEl.hasChildNodes()) {\n\t\t\tif (\n\t\t\t\t   typeof Config.passages.transitionOut === 'number'\n\t\t\t\t|| typeof Config.passages.transitionOut === 'string'\n\t\t\t\t&& Config.passages.transitionOut !== ''\n\t\t\t\t&& Has.transitionEndEvent\n\t\t\t) {\n\t\t\t\t[...containerEl.childNodes].forEach(outgoing => {\n\t\t\t\t\tconst $outgoing = jQuery(outgoing);\n\n\t\t\t\t\tif (outgoing.nodeType === Node.ELEMENT_NODE && $outgoing.hasClass('passage')) {\n\t\t\t\t\t\tif ($outgoing.hasClass('passage-out')) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$outgoing\n\t\t\t\t\t\t\t.attr('id', `out-${$outgoing.attr('id')}`)\n\t\t\t\t\t\t\t.addClass('passage-out');\n\n\t\t\t\t\t\tif (typeof Config.passages.transitionOut === 'string') {\n\t\t\t\t\t\t\t$outgoing.on(Has.transitionEndEvent, ev => {\n\t\t\t\t\t\t\t\tif (ev.originalEvent.propertyName === Config.passages.transitionOut) {\n\t\t\t\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t\t() => $outgoing.remove(),\n\t\t\t\t\t\t\t\tMath.max(minDomActionDelay, Config.passages.transitionOut)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(containerEl).empty();\n\t\t\t}\n\t\t}\n\n\t\t// Append the passage element to the passage container and set up its transition.\n\t\tjQuery(passageEl)\n\t\t\t.addClass('passage-in')\n\t\t\t.appendTo(containerEl);\n\t\tsetTimeout(() => jQuery(passageEl).removeClass('passage-in'), minDomActionDelay);\n\n\t\t// Update the story display title, if necessary.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\t// NOTE: We don't have an `else` here because that case will be handled later (below).\n\t\t\tif (_updating !== null || !Config.ui.updateStoryElements) {\n\t\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t\t}\n\t\t}\n\t\telse if (Config.passages.displayTitles && passage.title !== Config.passages.start) {\n\t\t\tdocument.title = `${passage.title} | ${Story.title}`;\n\t\t}\n\n\t\t// Scroll the window to the top.\n\t\twindow.scroll(0, 0);\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Execute post-display events, tasks, and the `PassageDone` special passage.\n\t\tif (Story.has('PassageDone')) {\n\t\t\ttry {\n\t\t\t\tpassageDoneOutput = Wikifier.wikifyEval(Story.get('PassageDone').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageDone', ex.message);\n\t\t\t}\n\t\t}\n\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagedisplay',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postdisplay).forEach(task => {\n\t\t\tif (typeof postdisplay[task] === 'function') {\n\t\t\t\tpostdisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Update the other interface elements, if necessary.\n\t\tif (_updating !== null) {\n\t\t\t_updating.forEach(pair => {\n\t\t\t\tjQuery(pair.element).empty();\n\t\t\t\tnew Wikifier(pair.element, Story.get(pair.passage).processText().trim());\n\t\t\t});\n\t\t}\n\t\telse if (Config.ui.updateStoryElements) {\n\t\t\tUIBar.update();\n\t\t}\n\n\t\t// Add the completed debug views for `StoryInit`, `PassageReady`, and `PassageDone`\n\t\t// to the incoming passage element.\n\t\tif (Config.debug) {\n\t\t\tlet debugView;\n\n\t\t\t// Prepend the `PassageReady` debug view.\n\t\t\tif (passageReadyOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageReady',\n\t\t\t\t\t'PassageReady'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageReadyOutput);\n\t\t\t\tjQuery(passageEl).prepend(debugView.output);\n\t\t\t}\n\n\t\t\t// Append the `PassageDone` debug view.\n\t\t\tif (passageDoneOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageDone',\n\t\t\t\t\t'PassageDone'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageDoneOutput);\n\t\t\t\tjQuery(passageEl).append(debugView.output);\n\t\t\t}\n\n\t\t\t// Prepend the cached `StoryInit` debug view, if we're showing the first moment/turn.\n\t\t\tif (State.turns === 1 && _storyInitDebugView != null) { // lazy equality for null\n\t\t\t\tjQuery(passageEl).prepend(_storyInitDebugView);\n\t\t\t}\n\t\t}\n\n\t\t// Last second post-processing for accessibility and other things.\n\t\tjQuery('#story')\n\t\t\t// Add `link-external` to all `href` bearing `<a>` elements which don't have it.\n\t\t\t.find('a[href]:not(.link-external)')\n\t\t\t.addClass('link-external')\n\t\t\t.end()\n\t\t\t// Add `tabindex=0` to all interactive elements which don't have it.\n\t\t\t.find('a,link,button,input,select,textarea')\n\t\t\t.not('[tabindex]')\n\t\t\t.attr('tabindex', 0);\n\n\t\t// Handle autosaves.\n\t\tswitch (typeof Config.saves.autosave) {\n\t\tcase 'boolean':\n\t\t\tif (Config.saves.autosave) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'object':\n\t\t\tif (passage.tags.some(tag => Config.saves.autosave.includes(tag))) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'function':\n\t\t\tif (Config.saves.autosave()) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// Execute post-play events.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passageend',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\n\t\t// Reset the engine state.\n\t\t_state = States.Idle;\n\n\t\t// Update the last play time.\n\t\t_lastPlay = Util.now();\n\n\t\treturn passageEl;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Play the given passage, optionally without altering the history.\n\t*/\n\tfunction engineDisplay(title, link, option) {\n\t\tif (DEBUG) { console.log('[Engine/engineDisplay()]'); }\n\n\t\tlet noHistory = false;\n\n\t\t// Process the option parameter.\n\t\tswitch (option) {\n\t\tcase undefined:\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'replace':\n\t\tcase 'back':\n\t\t\tnoHistory = true;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`Engine.display option parameter called with obsolete value \"${option}\"; please notify the developer`);\n\t\t}\n\n\t\tenginePlay(title, noHistory);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _hideOutlines() {\n\t\t_outlinePatch.set('*:focus{outline:none;}');\n\t}\n\n\tfunction _showOutlines() {\n\t\t_outlinePatch.clear();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tConstants.\n\t\t*/\n\t\tStates            : { value : States },\n\t\tminDomActionDelay : { value : minDomActionDelay },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tinit        : { value : engineInit },\n\t\tstart       : { value : engineStart },\n\t\trestart     : { value : engineRestart },\n\t\tstate       : { get : engineState },\n\t\tisIdle      : { value : engineIsIdle },\n\t\tisPlaying   : { value : engineIsPlaying },\n\t\tisRendering : { value : engineIsRendering },\n\t\tlastPlay    : { get : engineLastPlay },\n\t\tgoTo        : { value : engineGoTo },\n\t\tgo          : { value : engineGo },\n\t\tbackward    : { value : engineBackward },\n\t\tforward     : { value : engineForward },\n\t\tshow        : { value : engineShow },\n\t\tplay        : { value : enginePlay },\n\n\t\t/*\n\t\t\tLegacy Functions.\n\t\t*/\n\t\tdisplay : { value : engineDisplay }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tpassage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, Util, Wikifier */\n\nvar Passage = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tlet _tagsToSkip;\n\tlet _twine1Unescape;\n\n\t/*\n\t\tTags which should not be transformed into classes:\n\t\t\tdebug      → special tag\n\t\t\tnobr       → special tag\n\t\t\tpassage    → the default class\n\t\t\tscript     → special tag (only in Twine 1)\n\t\t\tstylesheet → special tag (only in Twine 1)\n\t\t\ttwine.*    → special tag\n\t\t\twidget     → special tag\n\t*/\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|script|stylesheet|widget|twine\\..*)$/i;\n\t}\n\t// For Twine 2\n\telse {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|widget|twine\\..*)$/i;\n\t}\n\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t/*\n\t\t\tReturns a decoded version of the passed Twine 1 passage store encoded string.\n\t\t*/\n\t\tconst _twine1EscapesRe    = /(?:\\\\n|\\\\t|\\\\s|\\\\|\\r)/g;\n\t\tconst _hasTwine1EscapesRe = new RegExp(_twine1EscapesRe.source); // to drop the global flag\n\t\tconst _twine1EscapesMap   = Object.freeze({\n\t\t\t'\\\\n' : '\\n',\n\t\t\t'\\\\t' : '\\t',\n\t\t\t'\\\\s' : '\\\\',\n\t\t\t'\\\\'  : '\\\\',\n\t\t\t'\\r'  : ''\n\t\t});\n\n\t\t_twine1Unescape = function (str) {\n\t\t\tif (str == null) { // lazy equality for null\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst val = String(str);\n\t\t\treturn val && _hasTwine1EscapesRe.test(val)\n\t\t\t\t? val.replace(_twine1EscapesRe, esc => _twine1EscapesMap[esc])\n\t\t\t\t: val;\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Class.\n\t*******************************************************************************************************************/\n\tclass Passage {\n\t\tconstructor(title, el) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage title/ID.\n\t\t\t\ttitle : {\n\t\t\t\t\tvalue : Util.unescape(title)\n\t\t\t\t},\n\n\t\t\t\t// Passage data element (within the story data element; i.e. T1: '[tiddler]', T2: 'tw-passagedata').\n\t\t\t\telement : {\n\t\t\t\t\tvalue : el || null\n\t\t\t\t},\n\n\t\t\t\t// Passage tags array (sorted and unique).\n\t\t\t\ttags : {\n\t\t\t\t\tvalue : Object.freeze(el && el.hasAttribute('tags')\n\t\t\t\t\t\t? el.getAttribute('tags')\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.splitOrEmpty(/\\s+/)\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.filter((tag, i, aref) => i === 0 || aref[i - 1] !== tag)\n\t\t\t\t\t\t: [])\n\t\t\t\t},\n\n\t\t\t\t// Passage excerpt.  Used by the `description()` method.\n\t\t\t\t_excerpt : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Properties dependant upon the above set.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage DOM-compatible ID.\n\t\t\t\tdomId : {\n\t\t\t\t\tvalue : `passage-${Util.slugify(this.title)}`\n\t\t\t\t},\n\n\t\t\t\t// Passage classes array (sorted and unique).\n\t\t\t\tclasses : {\n\t\t\t\t\tvalue : Object.freeze(this.tags.length === 0 ? [] : (() =>\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tReturn the sorted list of unique classes.\n\n\t\t\t\t\t\t\tNOTE: The `this.tags` array is already sorted and unique,\n\t\t\t\t\t\t\tso we only need to filter and map here.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tthis.tags\n\t\t\t\t\t\t\t.filter(tag => !_tagsToSkip.test(tag))\n\t\t\t\t\t\t\t.map(tag => Util.slugify(tag))\n\t\t\t\t\t)())\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Getters.\n\t\tget className() {\n\t\t\treturn this.classes.join(' ');\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get source`.\n\t\tget text() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\tconst passage = Util.escapeMarkup(this.title);\n\t\t\t\tconst mesg    = `${L10n.get('errorTitle')}: ${L10n.get('errorNonexistentPassage', { passage })}`;\n\t\t\t\treturn `<div class=\"error-view\"><span class=\"error\">${mesg}</span></div>`;\n\t\t\t}\n\n\t\t\t// For Twine 1\n\t\t\tif (TWINE1) {\n\t\t\t\treturn _twine1Unescape(this.element.textContent);\n\t\t\t}\n\t\t\t// For Twine 2\n\t\t\telse { // eslint-disable-line no-else-return\n\t\t\t\treturn this.element.textContent.replace(/\\r/g, '');\n\t\t\t}\n\t\t}\n\n\t\tdescription() {\n\t\t\tconst descriptions = Config.passages.descriptions;\n\n\t\t\tif (descriptions != null) { // lazy equality for null\n\t\t\t\tswitch (typeof descriptions) {\n\t\t\t\tcase 'boolean':\n\t\t\t\t\tif (descriptions) {\n\t\t\t\t\t\treturn this.title;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (descriptions instanceof Map && descriptions.has(this.title)) {\n\t\t\t\t\t\treturn descriptions.get(this.title);\n\t\t\t\t\t}\n\t\t\t\t\telse if (descriptions.hasOwnProperty(this.title)) {\n\t\t\t\t\t\treturn descriptions[this.title];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'function':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst result = descriptions.call(this);\n\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new TypeError('Config.passages.descriptions must be a boolean, object, or function');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Initialize the excerpt cache from the raw passage text, if necessary.\n\t\t\tif (this._excerpt === null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromText(this.text);\n\t\t\t}\n\n\t\t\treturn this._excerpt;\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get text`.\n\t\tprocessText() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\treturn this.text;\n\t\t\t}\n\n\t\t\t// Handle image passage transclusion.\n\t\t\tif (this.tags.includes('Twine.image')) {\n\t\t\t\treturn `[img[${this.text}]]`;\n\t\t\t}\n\n\t\t\tlet processed = this.text;\n\n\t\t\t// Handle `Config.passages.onProcess`.\n\t\t\tif (Config.passages.onProcess) {\n\t\t\t\tprocessed = Config.passages.onProcess.call(null, {\n\t\t\t\t\ttitle : this.title,\n\t\t\t\t\ttags  : this.tags,\n\t\t\t\t\ttext  : processed\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle `Config.passages.nobr` and the `nobr` tag.\n\t\t\tif (Config.passages.nobr || this.tags.includes('nobr')) {\n\t\t\t\t// Remove all leading & trailing newlines and compact all internal sequences\n\t\t\t\t// of newlines into single spaces.\n\t\t\t\tprocessed = processed.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' ');\n\t\t\t}\n\n\t\t\treturn processed;\n\t\t}\n\n\t\trender(options) {\n\t\t\t// Wikify the passage into a document fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.processText(), options);\n\n\t\t\t// Update the excerpt cache to reflect the rendered text, if we need it for the passage description\n\t\t\tif (Config.passages.descriptions == null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromNode(frag);\n\t\t\t}\n\n\t\t\treturn frag;\n\t\t}\n\n\t\tstatic getExcerptFromNode(node, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromNode(node=…, count=${count})]`, node); }\n\n\t\t\tif (!node.hasChildNodes()) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t// WARNING: es5-shim's `<String>.trim()` can cause \"too much recursion\" errors\n\t\t\t// here on very large strings (e.g., ≥40 KiB), at least in Firefox, for unknown\n\t\t\t// reasons.\n\t\t\t//\n\t\t\t// To fix the issue, we're removed `\\u180E` from es5-shim's whitespace pattern\n\t\t\t// to prevent it from erroneously shimming `<String>.trim()` in the first place.\n\t\t\tlet excerpt = node.textContent.trim();\n\n\t\t\tif (excerpt !== '') {\n\t\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\t\texcerpt = excerpt\n\t\t\t\t\t// Compact whitespace.\n\t\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t\t.match(excerptRe);\n\t\t\t}\n\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\n\t\tstatic getExcerptFromText(text, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromText(text=…, count=${count})]`, text); }\n\n\t\t\tif (text === '') {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\tconst excerpt   = text\n\t\t\t\t// Strip macro tags (replace with a space).\n\t\t\t\t.replace(/<<.*?>>/g, ' ')\n\t\t\t\t// Strip html tags (replace with a space).\n\t\t\t\t.replace(/<.*?>/g, ' ')\n\t\t\t\t// The above might have left problematic whitespace, so trim.\n\t\t\t\t.trim()\n\t\t\t\t// Strip table markup.\n\t\t\t\t.replace(/^\\s*\\|.*\\|.*?$/gm, '')\n\t\t\t\t// Strip image markup.\n\t\t\t\t.replace(/\\[[<>]?img\\[[^\\]]*\\]\\]/g, '')\n\t\t\t\t// Clean link markup (remove all but the link text).\n\t\t\t\t.replace(/\\[\\[([^|\\]]*?)(?:(?:\\||->|<-)[^\\]]*)?\\]\\]/g, '$1')\n\t\t\t\t// Clean heading markup.\n\t\t\t\t.replace(/^\\s*!+(.*?)$/gm, '$1')\n\t\t\t\t// Clean bold/italic/underline/highlight styles.\n\t\t\t\t.replace(/'{2}|\\/{2}|_{2}|@{2}/g, '')\n\t\t\t\t// A final trim.\n\t\t\t\t.trim()\n\t\t\t\t// Compact whitespace.\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t.match(excerptRe);\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Passage;\n})();\n\n/***********************************************************************************************************************\n\n\tsave.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Dialog, Engine, L10n, State, Story, UI, Util, storage */\n\nvar Save = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Save operation type pseudo-enumeration.\n\tconst Type = Util.toEnum({\n\t\tAutosave  : 'autosave',\n\t\tDisk      : 'disk',\n\t\tSerialize : 'serialize',\n\t\tSlot      : 'slot'\n\t});\n\n\t// The upper bound of the saves slots.\n\tlet _slotsUBound = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tSaves Functions.\n\t*******************************************************************************************************************/\n\tfunction savesInit() {\n\t\tif (DEBUG) { console.log('[Save/savesInit()]'); }\n\n\t\t// Disable save slots and the autosave when Web Storage is unavailable.\n\t\tif (storage.name === 'cookie') {\n\t\t\tsavesObjClear();\n\t\t\tConfig.saves.autoload = undefined;\n\t\t\tConfig.saves.autosave = undefined;\n\t\t\tConfig.saves.slots = 0;\n\t\t\treturn false;\n\t\t}\n\n\n\t\t// convert the single big saves object into the new smaller objects\n\t\tconst saves = storage.get('saves');\n\t\tif (saves !== null) {\n\t\t\tstorage.delete('saves'); // Make sure we have enough space to store the saves in the new format\n\t\t\tconst container = Dialog.setup('Backup');\n\t\t\tconst message = document.createElement('span');\n\t\t\tmessage.className = 'red';\n\t\t\tmessage.append('Due to changes to the saves system your existing saves were converted. Backup all saves' +\n\t\t\t\t' that are important to you as they may have been lost during conversion. Once you close this dialog' +\n\t\t\t\t' it will be IMPOSSIBLE to get your old saves back.');\n\t\t\tcontainer.append(message);\n\t\t\tconst index = indexGet();\n\t\t\t// we store the index every time we store a save, so in case we exceed local storage only the\n\t\t\t// last save is lost\n\t\t\tif (saves.autosave !== null) {\n\t\t\t\tcontainer.append(savesLink('autosave', saves.autosave));\n\t\t\t\tindex.autosave = { title : saves.autosave.title, date : saves.autosave.date };\n\t\t\t\ttry {\n\t\t\t\t\tstorage.set('autosave', saves.autosave);\n\t\t\t\t\tindexSave(index);\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tstorage.delete('autosave');\n\t\t\t\t\tindex.autosave = null;\n\t\t\t\t\tindexSave(index);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (let i = 0; i < saves.slots.length; i++) {\n\t\t\t\tif (saves.slots[i] !== null) {\n\t\t\t\t\tcontainer.append(savesLink(`slot${i}`, saves.slots[i]));\n\t\t\t\t\tindex.slots[i] = { title : saves.slots[i].title, date : saves.slots[i].date };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstorage.set(`slot${i}`, saves.slots[i]);\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tstorage.delete(`slot${i}`);\n\t\t\t\t\t\tindex.slots[i] = null;\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tDialog.open();\n\t\t}\n\n\t\tfunction savesLink(name, save) {\n\t\t\tconst div = document.createElement('div');\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.append(`Backup ${name}`);\n\t\t\ta.onclick = () => {\n\t\t\t\texportToDisk(`backup-${name}`, {}, save);\n\t\t\t};\n\t\t\tdiv.append(a);\n\t\t\treturn div;\n\t\t}\n\n\t\t/* All SC2 compatibility converters are disabled, too much work */\n\n\t\t/* legacy */\n\t\t// Convert an ancient saves array into a new saves object.\n\t\t/* if (Array.isArray(saves)) {\n\t\t\tsaves = {\n\t\t\t\tautosave : null,\n\t\t\t\tslots    : saves\n\t\t\t};\n\t\t\tupdated = true;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Handle the author changing the number of save slots.\n\t\t/*\n\t\tif (Config.saves.slots !== saves.slots.length) {\n\t\t\tif (Config.saves.slots < saves.slots.length) {\n\t\t\t\t// Attempt to decrease the number of slots; this will only compact\n\t\t\t\t// the slots array, by removing empty slots, no saves will be deleted.\n\t\t\t\tsaves.slots.reverse();\n\n\t\t\t\tsaves.slots = saves.slots.filter(function (val) {\n\t\t\t\t\tif (val === null && this.count > 0) {\n\t\t\t\t\t\t--this.count;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}, { count : saves.slots.length - Config.saves.slots });\n\n\t\t\t\tsaves.slots.reverse();\n\t\t\t}\n\t\t\telse if (Config.saves.slots > saves.slots.length) {\n\t\t\t\t// Attempt to increase the number of slots.\n\t\t\t\t_appendSlots(saves.slots, Config.saves.slots - saves.slots.length);\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t */\n\n\t\t/* legacy */\n\t\t// Update saves with old/obsolete properties.\n\t\t/* if (_savesObjUpdate(saves.autosave)) {\n\t\t\tupdated = true;\n\t\t}\n\n\t\tfor (let i = 0; i < saves.slots.length; ++i) {\n\t\t\tif (_savesObjUpdate(saves.slots[i])) {\n\t\t\t\tupdated = true;\n\t\t\t}\n\t\t}\n\n\t\t// Remove save stores which are empty.\n\t\tif (_savesObjIsEmpty(saves)) {\n\t\t\tstorage.delete('saves');\n\t\t\tupdated = false;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// If the saves object was updated, then update the store.\n\t\t/* if (updated) {\n\t\t\t_savesObjSave(saves);\n\t\t}\n\t\t*/\n\n\t\t_slotsUBound = indexGet().slots.length - 1;\n\n\t\treturn true;\n\t}\n\n\tfunction indexCreate() {\n\t\treturn {\n\t\t\tautosave : null,\n\t\t\tslots    : _appendSlots([], Config.saves.slots)\n\t\t};\n\t}\n\n\tfunction indexGet() {\n\t\tconst index = storage.get('index');\n\t\treturn index === null ? indexCreate() : index;\n\t}\n\n\tfunction indexSave(index) {\n\t\treturn storage.set('index', index);\n\t}\n\n\tfunction savesObjClear() {\n\t\tstorage.delete('autosave');\n\t\tconst index = indexGet();\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tstorage.delete(`slot${i}`);\n\t\t}\n\t\tstorage.delete('index');\n\t\treturn true;\n\t}\n\n\tfunction savesOk() {\n\t\treturn autosaveOk() || slotsOk();\n\t}\n\n\t/*******************************************************************************************************************\n\t\tAutosave Functions.\n\t*******************************************************************************************************************/\n\tfunction autosaveOk() {\n\t\treturn storage.name !== 'cookie' && typeof Config.saves.autosave !== 'undefined';\n\t}\n\n\tfunction autosaveHas() {\n\t\treturn indexGet().autosave !== null;\n\t}\n\n\tfunction autosaveGet() {\n\t\treturn storage.get('autosave');\n\t}\n\n\tfunction autosaveLoad() {\n\t\tconst autosave = autosaveGet();\n\n\t\tif (autosave === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn _unmarshal(autosave);\n\t}\n\n\tfunction autosaveSave(title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle : title || Story.get(State.passage).description(),\n\t\t\tdate  : Date.now()\n\t\t};\n\t\tconst index = indexGet();\n\t\tindex.autosave = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set('autosave', _marshal(supplemental, { type : Type.Autosave }), false);\n\t\t} catch (ex) {\n\t\t\tindex.autosave = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction autosaveDelete() {\n\t\tconst index = indexGet();\n\t\tindex.autosave = null;\n\t\tindexSave(index);\n\t\treturn storage.delete('autosave');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSlots Functions.\n\t*******************************************************************************************************************/\n\tfunction slotsOk() {\n\t\treturn storage.name !== 'cookie' && _slotsUBound !== -1;\n\t}\n\n\tfunction slotsLength() {\n\t\treturn _slotsUBound + 1;\n\t}\n\n\tfunction slotsCount() {\n\t\tif (!slotsOk()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst index = indexGet();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tif (index.slots[i] !== null) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\tfunction slotsIsEmpty() {\n\t\treturn slotsCount() === 0;\n\t}\n\n\tfunction slotsHas(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length || index.slots[slot] === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction slotsGet(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn storage.get(`slot${slot}`);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tfunction slotsLoad(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn _unmarshal(storage.get(`slot${slot}`));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction slotsSave(slot, title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle: title || Story.get(State.passage).description(),\n\t\t\tdate: Date.now()\n\t\t};\n\n\t\tindex.slots[slot] = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set(`slot${slot}`, _marshal(supplemental, { type : Type.Slot }));\n\t\t} catch (ex) {\n\t\t\tindex.slots[slot] = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction slotsDelete(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tindex.slots[slot] = null;\n\t\tindexSave(index);\n\n\t\treturn storage.delete(`slot${slot}`);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDisk Import/Export Functions.\n\t*******************************************************************************************************************/\n\tfunction exportToDisk(filename, metadata, marshaledSave) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tfunction datestamp() {\n\t\t\tconst now = new Date();\n\t\t\tlet MM = now.getMonth() + 1;\n\t\t\tlet DD = now.getDate();\n\t\t\tlet hh = now.getHours();\n\t\t\tlet mm = now.getMinutes();\n\t\t\tlet ss = now.getSeconds();\n\n\t\t\tif (MM < 10) { MM = `0${MM}`; }\n\t\t\tif (DD < 10) { DD = `0${DD}`; }\n\t\t\tif (hh < 10) { hh = `0${hh}`; }\n\t\t\tif (mm < 10) { mm = `0${mm}`; }\n\t\t\tif (ss < 10) { ss = `0${ss}`; }\n\n\t\t\treturn `${now.getFullYear()}${MM}${DD}-${hh}${mm}${ss}`;\n\t\t}\n\n\t\tfunction legalizeName(str) {\n\t\t\t/*\n\t\t\t\tNOTE: The range of illegal characters consists of: C0 controls, double quote,\n\t\t\t\tnumber, dollar, percent, ampersand, single quote, asterisk, plus, comma,\n\t\t\t\tforward slash, colon, semi-colon, less-than, equals, greater-than, question,\n\t\t\t\tbackslash, caret, backquote/grave, pipe/vertical-bar, delete, C1 controls.\n\t\t\t*/\n\t\t\treturn String(str).trim()\n\t\t\t\t.replace(/[\\x00-\\x1f\"#$%&'*+,/:;<=>?\\\\^`|\\x7f-\\x9f]+/g, '') // eslint-disable-line no-control-regex\n\t\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-'); // legacy\n\t\t}\n\n\t\tconst baseName     = filename == null ? Story.domId : legalizeName(filename); // lazy equality for null\n\t\tconst saveName     = `${baseName}-${datestamp()}.save`;\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\tconst saveObj      = LZString.compressToBase64(JSON.stringify(\n\t\t\tmarshaledSave == null ? _marshal(supplemental, { type : Type.Disk }) : marshaledSave\n\t\t));\n\t\tsaveAs(new Blob([saveObj], { type : 'text/plain;charset=UTF-8' }), saveName);\n\t}\n\n\tfunction importFromDisk(event) {\n\t\tconst file   = event.target.files[0];\n\t\tconst reader = new FileReader();\n\n\t\t// Add the handler that will capture the file information once the load is finished.\n\t\tjQuery(reader).on('load', ev => {\n\t\t\tconst target = ev.currentTarget;\n\n\t\t\tif (!target.result) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet saveObj;\n\n\t\t\ttry {\n\t\t\t\tsaveObj = JSON.parse(\n\t\t\t\t\t/* legacy */ /\\.json$/i.test(file.name) || /^\\{/.test(target.result)\n\t\t\t\t\t\t? target.result\n\t\t\t\t\t\t: /* /legacy */ LZString.decompressFromBase64(target.result)\n\t\t\t\t);\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\t\t_unmarshal(saveObj);\n\t\t});\n\n\t\t// Initiate the file load.\n\t\treader.readAsText(file);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSerialization Functions.\n\t*******************************************************************************************************************/\n\tfunction serialize(metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\treturn LZString.compressToBase64(JSON.stringify(_marshal(supplemental, { type : Type.Serialize })));\n\t}\n\n\tfunction deserialize(base64Str) {\n\t\t/*\n\t\t\tNOTE: We purposefully do not attempt to catch parameter shenanigans\n\t\t\there, instead relying on `_unmarshal()` to do the heavy lifting.\n\t\t*/\n\n\t\tlet saveObj;\n\n\t\ttry {\n\t\t\tsaveObj = JSON.parse(LZString.decompressFromBase64(base64Str));\n\t\t}\n\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\tif (!_unmarshal(saveObj)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn saveObj.metadata;\n\t}\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _storageAlert() {\n\t\tif (Dialog.isOpen()) {\n\t\t\t$(document).one(':dialogclosed', () => UI.alert('Local storage full, delete saves or increase local storage'));\n\t\t} else {\n\t\t\tUI.alert('Local storage full, delete saves or increase local storage');\n\t\t}\n\t}\n\n\tfunction _appendSlots(array, num) {\n\t\tfor (let i = 0; i < num; ++i) {\n\t\t\tarray.push(null);\n\t\t}\n\n\t\treturn array;\n\t}\n\n\tfunction _savesObjUpdate(saveObj) {\n\t\tif (saveObj == null || typeof saveObj !== \"object\") { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tlet updated = false;\n\n\t\t/* eslint-disable no-param-reassign */\n\t\tif (\n\t\t\t   !saveObj.hasOwnProperty('state')\n\t\t\t|| !saveObj.state.hasOwnProperty('delta')\n\t\t\t|| !saveObj.state.hasOwnProperty('index')\n\t\t) {\n\t\t\tif (saveObj.hasOwnProperty('data')) {\n\t\t\t\tdelete saveObj.mode;\n\t\t\t\tsaveObj.state = {\n\t\t\t\t\tdelta : State.deltaEncode(saveObj.data)\n\t\t\t\t};\n\t\t\t\tdelete saveObj.data;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('delta')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\t\t\tdelete saveObj.state.history;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('index')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t}\n\n\t\t\tsaveObj.state.index = saveObj.state.delta.length - 1;\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (saveObj.state.hasOwnProperty('rseed')) {\n\t\t\tsaveObj.state.seed = saveObj.state.rseed;\n\t\t\tdelete saveObj.state.rseed;\n\n\t\t\tsaveObj.state.delta.forEach((_, i, delta) => {\n\t\t\t\tif (delta[i].hasOwnProperty('rcount')) {\n\t\t\t\t\tdelta[i].pull = delta[i].rcount;\n\t\t\t\t\tdelete delta[i].rcount;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (\n\t\t\t   saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number'\n\t\t\t||  saveObj.state.hasOwnProperty('unique')\n\t\t\t||  saveObj.state.hasOwnProperty('last')\n\t\t) {\n\t\t\tif (saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number') {\n\t\t\t\tdelete saveObj.state.expired;\n\t\t\t}\n\n\t\t\tif (saveObj.state.hasOwnProperty('unique') || saveObj.state.hasOwnProperty('last')) {\n\t\t\t\tsaveObj.state.expired = [];\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('unique')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.unique);\n\t\t\t\t\tdelete saveObj.state.unique;\n\t\t\t\t}\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('last')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.last);\n\t\t\t\t\tdelete saveObj.state.last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t/* eslint-enable no-param-reassign */\n\n\t\treturn updated;\n\t}\n\n\tfunction _marshal(supplemental, details) {\n\t\tif (DEBUG) { console.log(`[Save/_marshal(…, { type : '${details.type}' })]`); }\n\n\t\tif (supplemental != null && typeof supplemental !== 'object') { // lazy equality for null\n\t\t\tthrow new Error('supplemental parameter must be an object');\n\t\t}\n\n\t\tconst saveObj = Object.assign({}, supplemental, {\n\t\t\tid    : Config.saves.id,\n\t\t\tstate : State.marshalForSave()\n\t\t});\n\n\t\tif (Config.saves.version) {\n\t\t\tsaveObj.version = Config.saves.version;\n\t\t}\n\n\t\tif (typeof Config.saves.onSave === 'function') {\n\t\t\tConfig.saves.onSave(saveObj, details);\n\t\t}\n\n\t\t// Delta encode the state history and delete the non-encoded property.\n\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\tdelete saveObj.state.history;\n\n\t\treturn saveObj;\n\t}\n\n\tfunction _unmarshal(saveObj) {\n\t\tif (DEBUG) { console.log('[Save/_unmarshal()]'); }\n\n\t\ttry {\n\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t/* legacy */\n\t\t\t// Update saves with old/obsolete properties.\n\t\t\t_savesObjUpdate(saveObj);\n\t\t\t/* /legacy */\n\n\t\t\tif (!saveObj || !saveObj.hasOwnProperty('id') || !saveObj.hasOwnProperty('state')) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveMissingData'));\n\t\t\t}\n\n\t\t\t// Delta decode the state history and delete the encoded property.\n\t\t\tsaveObj.state.history = State.deltaDecode(saveObj.state.delta);\n\t\t\tdelete saveObj.state.delta;\n\n\t\t\tif (typeof Config.saves.onLoad === 'function') {\n\t\t\t\tConfig.saves.onLoad(saveObj);\n\t\t\t}\n\n\t\t\tif (saveObj.id !== Config.saves.id) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveIdMismatch'));\n\t\t\t}\n\n\t\t\t// Restore the state.\n\t\t\tState.unmarshalForSave(saveObj.state); // may also throw exceptions\n\n\t\t\t// Show the active moment.\n\t\t\tEngine.show();\n\t\t\t/* eslint-enable no-param-reassign */\n\t\t}\n\t\tcatch (ex) {\n\t\t\tUI.alert(`${ex.message.toUpperFirst()}.</p><p>${L10n.get('aborting')}.`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tSave Functions.\n\t\t*/\n\t\tinit  : { value : savesInit },\n\t\tclear : { value : savesObjClear },\n\t\tok    : { value : savesOk },\n\t\tindex : { value : indexGet },\n\n\t\t/*\n\t\t\tAutosave Functions.\n\t\t*/\n\t\tautosave : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok     : { value : autosaveOk },\n\t\t\t\thas    : { value : autosaveHas },\n\t\t\t\tget    : { value : autosaveGet },\n\t\t\t\tload   : { value : autosaveLoad },\n\t\t\t\tsave   : { value : autosaveSave },\n\t\t\t\tdelete : { value : autosaveDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tSlots Functions.\n\t\t*/\n\t\tslots : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok      : { value : slotsOk },\n\t\t\t\tlength  : { get : slotsLength },\n\t\t\t\tisEmpty : { value : slotsIsEmpty },\n\t\t\t\tcount   : { value : slotsCount },\n\t\t\t\thas     : { value : slotsHas },\n\t\t\t\tget     : { value : slotsGet },\n\t\t\t\tload    : { value : slotsLoad },\n\t\t\t\tsave    : { value : slotsSave },\n\t\t\t\tdelete  : { value : slotsDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tDisk Import/Export Functions.\n\t\t*/\n\t\texport : { value : exportToDisk },\n\t\timport : { value : importFromDisk },\n\n\t\t/*\n\t\t\tSerialization Functions.\n\t\t*/\n\t\tserialize   : { value : serialize },\n\t\tdeserialize : { value : deserialize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsetting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, settings:true, storage */\n\nvar Setting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Setting control types object (pseudo-enumeration).\n\tconst Types = Util.toEnum({\n\t\tHeader : 0,\n\t\tToggle : 1,\n\t\tList   : 2,\n\t\tRange  : 3\n\t});\n\n\t// Setting definition array.\n\tconst _definitions = [];\n\n\n\t/*******************************************************************************************************************\n\t\tSettings Functions.\n\t*******************************************************************************************************************/\n\tfunction settingsInit() {\n\t\tif (DEBUG) { console.log('[Setting/settingsInit()]'); }\n\n\t\t/* legacy */\n\t\t// Attempt to migrate an existing `options` store to `settings`.\n\t\tif (storage.has('options')) {\n\t\t\tconst old = storage.get('options');\n\n\t\t\tif (old !== null) {\n\t\t\t\twindow.SugarCube.settings = settings = Object.assign(settingsCreate(), old);\n\t\t\t}\n\n\t\t\tsettingsSave();\n\t\t\tstorage.delete('options');\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Load existing settings.\n\t\tsettingsLoad();\n\n\t\t// Execute `onInit` callbacks.\n\t\t_definitions.forEach(def => {\n\t\t\tif (def.hasOwnProperty('onInit')) {\n\t\t\t\tconst thisArg = {\n\t\t\t\t\tname    : def.name,\n\t\t\t\t\tvalue   : settings[def.name],\n\t\t\t\t\tdefault : def.default\n\t\t\t\t};\n\n\t\t\t\tif (def.hasOwnProperty('list')) {\n\t\t\t\t\tthisArg.list = def.list;\n\t\t\t\t}\n\n\t\t\t\tdef.onInit.call(thisArg);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction settingsCreate() {\n\t\treturn Object.create(null);\n\t}\n\n\tfunction settingsSave() {\n\t\tconst savedSettings = settingsCreate();\n\n\t\tif (Object.keys(settings).length > 0) {\n\t\t\t_definitions\n\t\t\t\t.filter(def => def.type !== Types.Header && settings[def.name] !== def.default)\n\t\t\t\t.forEach(def => savedSettings[def.name] = settings[def.name]);\n\t\t}\n\n\t\tif (Object.keys(savedSettings).length === 0) {\n\t\t\tstorage.delete('settings');\n\t\t\treturn true;\n\t\t}\n\n\t\treturn storage.set('settings', savedSettings);\n\t}\n\n\tfunction settingsLoad() {\n\t\tconst defaultSettings = settingsCreate();\n\t\tconst loadedSettings  = storage.get('settings') || settingsCreate();\n\n\t\t// Load the defaults.\n\t\t_definitions\n\t\t\t.filter(def => def.type !== Types.Header)\n\t\t\t.forEach(def => defaultSettings[def.name] = def.default);\n\n\t\t// Assign to the `settings` object while overwriting the defaults with the loaded settings.\n\t\twindow.SugarCube.settings = settings = Object.assign(defaultSettings, loadedSettings);\n\t}\n\n\tfunction settingsClear() {\n\t\twindow.SugarCube.settings = settings = settingsCreate();\n\t\tstorage.delete('settings');\n\t\treturn true;\n\t}\n\n\tfunction settingsReset(name) {\n\t\tif (arguments.length === 0) {\n\t\t\tsettingsClear();\n\t\t\tsettingsLoad();\n\t\t}\n\t\telse {\n\t\t\tif (name == null || !definitionsHas(name)) { // lazy equality for null\n\t\t\t\tthrow new Error(`nonexistent setting \"${name}\"`);\n\t\t\t}\n\n\t\t\tconst def = definitionsGet(name);\n\n\t\t\tif (def.type !== Types.Header) {\n\t\t\t\tsettings[name] = def.default;\n\t\t\t}\n\t\t}\n\n\t\treturn settingsSave();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDefinitions Functions.\n\t*******************************************************************************************************************/\n\tfunction definitionsForEach(callback, thisArg) {\n\t\t_definitions.forEach(callback, thisArg);\n\t}\n\n\tfunction definitionsAdd(type, name, def) {\n\t\tif (arguments.length < 3) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('type'); }\n\t\t\tif (arguments.length < 2) { errors.push('name'); }\n\t\t\tif (arguments.length < 3) { errors.push('definition'); }\n\t\t\tthrow new Error(`missing parameters, no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tif (typeof def !== 'object') {\n\t\t\tthrow new TypeError('definition parameter must be an object');\n\t\t}\n\n\t\tif (definitionsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing setting \"${name}\"`);\n\t\t}\n\n\t\t/*\n\t\t\tDefinition object properties and types:\n\t\t\t\ttype      →  (all)   → Setting.Types\n\t\t\t\tname      →  (all)   → string\n\t\t\t\tlabel     →  (all)   → string\n\t\t\t\tdesc      →  (all)   → string\n\t\t\t\tdefault\n\t\t\t\t\t(if defined)\n \t\t\t\t\t\t  →  Toggle  → boolean\n\t\t\t\t\t\t  →  List    → Array\n\t\t\t\t\t\t  →  Range   → number\n\t\t\t\t\t(if undefined)\n\t\t\t\t\t\t  →  Toggle  → false\n\t\t\t\t\t\t  →  List    → list[0]\n\t\t\t\t\t\t  →  Range   → max\n\t\t\t\tlist      →  List    → Array\n\t\t\t\tmin       →  Range   → number\n\t\t\t\tmax       →  Range   → number\n\t\t\t\tstep      →  Range   → number\n\t\t\t\tonInit    →  (all)   → function\n\t\t\t\tonChange  →  (all)   → function\n\t\t*/\n\t\tconst definition = {\n\t\t\ttype,\n\t\t\tname,\n\t\t\tlabel : typeof def.label === 'string' ? def.label.trim() : ''\n\t\t};\n\n\t\tif (typeof def.desc === 'string') {\n\t\t\tconst desc = def.desc.trim();\n\n\t\t\tif (desc !== '') {\n\t\t\t\tdefinition.desc = desc;\n\t\t\t}\n\t\t}\n\n\t\tswitch (type) {\n\t\tcase Types.Header:\n\t\t\tbreak;\n\n\t\tcase Types.Toggle:\n\t\t\tdefinition.default = !!def.default;\n\t\t\tbreak;\n\n\t\tcase Types.List:\n\t\t\tif (!def.hasOwnProperty('list')) {\n\t\t\t\tthrow new Error('no list specified');\n\t\t\t}\n\t\t\telse if (!Array.isArray(def.list)) {\n\t\t\t\tthrow new TypeError('list must be an array');\n\t\t\t}\n\t\t\telse if (def.list.length === 0) {\n\t\t\t\tthrow new Error('list must not be empty');\n\t\t\t}\n\n\t\t\tdefinition.list = Object.freeze(def.list);\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.list[0];\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst defaultIndex = def.list.indexOf(def.default);\n\n\t\t\t\tif (defaultIndex === -1) {\n\t\t\t\t\tthrow new Error('list does not contain default');\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.list[defaultIndex];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase Types.Range:\n\t\t\tif (!def.hasOwnProperty('min')) {\n\t\t\t\tthrow new Error('no min specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.min !== 'number'\n\t\t\t\t|| Number.isNaN(def.min)\n\t\t\t\t|| !Number.isFinite(def.min)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('min must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('max')) {\n\t\t\t\tthrow new Error('no max specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.max !== 'number'\n\t\t\t\t|| Number.isNaN(def.max)\n\t\t\t\t|| !Number.isFinite(def.max)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('max must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('step')) {\n\t\t\t\tthrow new Error('no step specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.step !== 'number'\n\t\t\t\t|| Number.isNaN(def.step)\n\t\t\t\t|| !Number.isFinite(def.step)\n\t\t\t\t|| def.step <= 0\n\t\t\t) {\n\t\t\t\tthrow new TypeError('step must be a finite number greater than zero');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Determine how many fractional digits we need to be concerned with based on the step value.\n\t\t\t\tconst fracDigits = (() => {\n\t\t\t\t\tconst str = String(def.step);\n\t\t\t\t\tconst pos = str.lastIndexOf('.');\n\t\t\t\t\treturn pos === -1 ? 0 : str.length - pos - 1;\n\t\t\t\t})();\n\n\t\t\t\t// Set up a function to validate a given value against the step value.\n\t\t\t\tfunction stepValidate(value) {\n\t\t\t\t\tif (fracDigits > 0) {\n\t\t\t\t\t\tconst ma = Number(`${def.min}e${fracDigits}`);\n\t\t\t\t\t\tconst sa = Number(`${def.step}e${fracDigits}`);\n\t\t\t\t\t\tconst va = Number(`${value}e${fracDigits}`) - ma;\n\t\t\t\t\t\treturn Number(`${va - va % sa + ma}e-${fracDigits}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst va = value - def.min;\n\t\t\t\t\treturn va - va % def.step + def.min;\n\t\t\t\t}\n\n\t\t\t\t// Sanity check the max value against the step value.\n\t\t\t\tif (stepValidate(def.max) !== def.max) {\n\t\t\t\t\tthrow new RangeError(`max (${def.max}) is not a multiple of the step (${def.step}) plus the min (${def.min})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefinition.max = def.max;\n\t\t\tdefinition.min = def.min;\n\t\t\tdefinition.step = def.step;\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.max;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (\n\t\t\t\t\t   typeof def.default !== 'number'\n\t\t\t\t\t|| Number.isNaN(def.default)\n\t\t\t\t\t|| !Number.isFinite(def.default)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError('default must be a finite number');\n\t\t\t\t}\n\t\t\t\telse if (def.default < def.min) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is less than min (${def.min})`);\n\t\t\t\t}\n\t\t\t\telse if (def.default > def.max) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is greater than max (${def.max})`);\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.default;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`unknown Setting type: ${type}`);\n\t\t}\n\n\t\tif (typeof def.onInit === 'function') {\n\t\t\tdefinition.onInit = Object.freeze(def.onInit);\n\t\t}\n\n\t\tif (typeof def.onChange === 'function') {\n\t\t\tdefinition.onChange = Object.freeze(def.onChange);\n\t\t}\n\n\t\t_definitions.push(Object.freeze(definition));\n\t}\n\n\tfunction definitionsAddHeader(name, desc) {\n\t\tdefinitionsAdd(Types.Header, name, { desc });\n\t}\n\n\tfunction definitionsAddToggle(...args) {\n\t\tdefinitionsAdd(Types.Toggle, ...args);\n\t}\n\n\tfunction definitionsAddList(...args) {\n\t\tdefinitionsAdd(Types.List, ...args);\n\t}\n\n\tfunction definitionsAddRange(...args) {\n\t\tdefinitionsAdd(Types.Range, ...args);\n\t}\n\n\tfunction definitionsIsEmpty() {\n\t\treturn _definitions.length === 0;\n\t}\n\n\tfunction definitionsHas(name) {\n\t\treturn _definitions.some(definition => definition.name === name);\n\t}\n\n\tfunction definitionsGet(name) {\n\t\treturn _definitions.find(definition => definition.name === name);\n\t}\n\n\tfunction definitionsDelete(name) {\n\t\tif (definitionsHas(name)) {\n\t\t\tdelete settings[name];\n\t\t}\n\n\t\tfor (let i = 0; i < _definitions.length; ++i) {\n\t\t\tif (_definitions[i].name === name) {\n\t\t\t\t_definitions.splice(i, 1);\n\t\t\t\tdefinitionsDelete(name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tEnumerations.\n\t\t*/\n\t\tTypes : { value : Types },\n\n\t\t/*\n\t\t\tSettings Functions.\n\t\t*/\n\t\tinit   : { value : settingsInit },\n\t\tcreate : { value : settingsCreate },\n\t\tsave   : { value : settingsSave },\n\t\tload   : { value : settingsLoad },\n\t\tclear  : { value : settingsClear },\n\t\treset  : { value : settingsReset },\n\n\t\t/*\n\t\t\tDefinitions Functions.\n\t\t*/\n\t\tforEach   : { value : definitionsForEach },\n\t\tadd       : { value : definitionsAdd },\n\t\taddHeader : { value : definitionsAddHeader },\n\t\taddToggle : { value : definitionsAddToggle },\n\t\taddList   : { value : definitionsAddList },\n\t\taddRange  : { value : definitionsAddRange },\n\t\tisEmpty   : { value : definitionsIsEmpty },\n\t\thas       : { value : definitionsHas },\n\t\tget       : { value : definitionsGet },\n\t\tdelete    : { value : definitionsDelete }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstory.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Alert, Config, Passage, Scripting, StyleWrapper, Util, Wikifier */\n\nvar Story = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Map of normal passages.\n\tconst _passages = {};\n\n\t// List of script passages.\n\tconst _scripts = [];\n\n\t// List of style passages.\n\tconst _styles = [];\n\n\t// List of widget passages.\n\tconst _widgets = [];\n\n\t// Story title.\n\tlet _title = '';\n\n\t// Story IFID.\n\tlet _ifId = '';\n\n\t// DOM-compatible ID.\n\tlet _domId = '';\n\n\n\t/*******************************************************************************************************************\n\t\tStory Functions.\n\t*******************************************************************************************************************/\n\tfunction storyLoad() {\n\t\tif (DEBUG) { console.log('[Story/storyLoad()]'); }\n\n\t\tconst validationCodeTags = [\n\t\t\t'widget'\n\t\t];\n\t\tconst validationNoCodeTagPassages = [\n\t\t\t'PassageDone',\n\t\t\t'PassageFooter',\n\t\t\t'PassageHeader',\n\t\t\t'PassageReady',\n\t\t\t'StoryAuthor',\n\t\t\t'StoryBanner',\n\t\t\t'StoryCaption',\n\t\t\t'StoryInit',\n\t\t\t'StoryMenu',\n\t\t\t'StoryShare',\n\t\t\t'StorySubtitle'\n\t\t];\n\n\t\tfunction validateStartingPassage(passage) {\n\t\t\tif (passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`starting passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\tfunction validateSpecialPassages(passage) {\n\t\t\tif (validationNoCodeTagPassages.includes(passage.title) && passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`special passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\t// For Twine 1.\n\t\tif (TWINE1) {\n\t\t\t/*\n\t\t\t\tAdditional Twine 1 validation setup.\n\t\t\t*/\n\t\t\tvalidationCodeTags.unshift('script', 'stylesheet');\n\t\t\tvalidationNoCodeTagPassages.push('StoryTitle');\n\n\t\t\tfunction validateTwine1CodePassages(passage) {\n\t\t\t\tconst codeTags  = [...validationCodeTags];\n\t\t\t\tconst foundTags = [];\n\n\t\t\t\tpassage.tags.forEach(tag => {\n\t\t\t\t\tif (codeTags.includes(tag)) {\n\t\t\t\t\t\tfoundTags.push(...codeTags.delete(tag));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (foundTags.length > 1) {\n\t\t\t\t\tthrow new Error(`code passage \"${passage.title}\" contains multiple code tags; invalid: \"${foundTags.sort().join('\", \"')}\"`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = (() => {\n\t\t\t\t/*\n\t\t\t\t\tHandle the Twine 1.4+ Test Play From Here feature.\n\n\t\t\t\t\tWARNING: Do not remove the `String()` wrapper from or change the quote\n\t\t\t\t\tstyle of the `\"START_AT\"` replacement target.  The former is there to\n\t\t\t\t\tkeep UglifyJS from pruning the code into oblivion—i.e. minifying the\n\t\t\t\t\tcode into something broken.  The latter is there because the Twine 1\n\t\t\t\t\tpattern that matches it depends upon the double quotes.\n\n\t\t\t\t*/\n\t\t\t\tconst testPlay = String(\"START_AT\"); // eslint-disable-line quotes\n\n\t\t\t\tif (testPlay !== '') {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tTest play; starting passage: \"${testPlay}\"`); }\n\n\t\t\t\t\tConfig.debug = true;\n\t\t\t\t\treturn testPlay;\n\t\t\t\t}\n\n\t\t\t\t// In the absence of a `testPlay` value, return 'Start'.\n\t\t\t\treturn 'Start';\n\t\t\t})();\n\n\t\t\t/*\n\t\t\t\tProcess the passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\tjQuery('#store-area')\n\t\t\t\t.children(':not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst passage = new Passage($this.attr('tiddler'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (passage.title === Config.passages.start) {\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('stylesheet')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_styles.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('script')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_scripts.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tSet the story title or throw an exception.\n\t\t\t*/\n\t\t\tif (_passages.hasOwnProperty('StoryTitle')) {\n\t\t\t\tconst buf = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(buf, _passages.StoryTitle.processText().trim());\n\t\t\t\t_storySetTitle(buf.textContent);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('cannot find the \"StoryTitle\" special passage');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\n\t\t// For Twine 2.\n\t\telse {\n\t\t\tconst $storydata = jQuery('tw-storydata');\n\t\t\tconst startNode  = $storydata.attr('startnode') || '';\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = null; // no default in Twine 2\n\n\t\t\t/*\n\t\t\t\tProcess story options.\n\n\t\t\t\tNOTE: Currently, the only option of interest is 'debug', so we\n\t\t\t\tsimply use a regular expression to check for it.\n\t\t\t*/\n\t\t\tConfig.debug = /\\bdebug\\b/.test($storydata.attr('options'));\n\n\t\t\t/*\n\t\t\t\tProcess stylesheet passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('style') // alternatively: '[type=\"text/twine-css\"]' or '#twine-user-stylesheet'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_styles.push(new Passage(`tw-user-style-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess script passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('script') // alternatively: '[type=\"text/twine-javascript\"]' or '#twine-user-script'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_scripts.push(new Passage(`tw-user-script-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess normal passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('tw-passagedata:not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst pid     = $this.attr('pid') || '';\n\t\t\t\t\tconst passage = new Passage($this.attr('name'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (pid === startNode && startNode !== '') {\n\t\t\t\t\t\tConfig.passages.start = passage.title;\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tGet the story IFID.\n\t\t\t*/\n\t\t\t_ifId = $storydata.attr('ifid');\n\n\t\t\t/*\n\t\t\t\tSet the story title.\n\n\t\t\t\tFIXME: Maybe `$storydata.attr('name')` should be used instead of `'{{STORY_NAME}}'`?\n\t\t\t*/\n\t\t\t// _storySetTitle($storydata.attr('name'));\n\t\t\t_storySetTitle('{{STORY_NAME}}');\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\t}\n\n\tfunction storyInit() {\n\t\tif (DEBUG) { console.log('[Story/storyInit()]'); }\n\n\t\t/*\n\t\t\tAdd the story styles.\n\t\t*/\n\t\t(() => {\n\t\t\tconst storyStyle = document.createElement('style');\n\n\t\t\tnew StyleWrapper(storyStyle)\n\t\t\t\t.add(_styles.map(style => style.text.trim()).join('\\n'));\n\n\t\t\tjQuery(storyStyle)\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-story',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t});\n\t\t})();\n\n\t\t/*\n\t\t\tEvaluate the story scripts.\n\t\t*/\n\t\tfor (let i = 0; i < _scripts.length; ++i) {\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(_scripts[i].text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_scripts[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tProcess the story widgets.\n\t\t*/\n\t\tfor (let i = 0; i < _widgets.length; ++i) {\n\t\t\ttry {\n\t\t\t\tWikifier.wikifyEval(_widgets[i].processText());\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_widgets[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction _storySetTitle(rawTitle) {\n\t\tif (rawTitle == null) { // lazy equality for null\n\t\t\tthrow new Error('story title must not be null or undefined');\n\t\t}\n\n\t\tconst title = Util.unescape(String(rawTitle)).trim();\n\n\t\tif (title === '') { // lazy equality for null\n\t\t\tthrow new Error('story title must not be empty or consist solely of whitespace');\n\t\t}\n\n\t\tdocument.title = _title = title;\n\n\t\t// TODO: In v3 the `_domId` should be created from a combination of the\n\t\t// `_title` slug and the IFID, if available, to avoid collisions between\n\t\t// stories whose titles generate identical slugs.\n\t\t_domId = Util.slugify(_title);\n\n\t\t// [v2] Protect the `_domId` against being an empty string.\n\t\t//\n\t\t// If `_domId` is empty, attempt a failover.\n\t\tif (_domId === '') {\n\t\t\t// If `_ifId` is not empty, then use it.\n\t\t\tif (_ifId !== '') {\n\t\t\t\t_domId = _ifId;\n\t\t\t}\n\n\t\t\t// Elsewise generate a string from the `_title`'s code points (in hexadecimal).\n\t\t\telse {\n\t\t\t\tfor (let i = 0, len = _title.length; i < len; ++i) {\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(_title, i);\n\t\t\t\t\t_domId += char.codePointAt(0).toString(16);\n\t\t\t\t\ti += end - start;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction storyTitle() {\n\t\treturn _title;\n\t}\n\n\tfunction storyDomId() {\n\t\treturn _domId;\n\t}\n\n\tfunction storyIfId() {\n\t\treturn _ifId;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Functions.\n\t*******************************************************************************************************************/\n\tfunction passagesAdd(passage) {\n\t\tif (!(passage instanceof Passage)) {\n\t\t\tthrow new TypeError('Story.add passage parameter must be an instance of Passage');\n\t\t}\n\n\t\tconst title = passage.title;\n\n\t\tif (!_passages.hasOwnProperty(title)) {\n\t\t\t_passages[title] = passage;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction passagesHas(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t\treturn _passages.hasOwnProperty(String(title));\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.has title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGet(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t/* eslint-disable indent */\n\t\t\t{\n\t\t\t\tconst id = String(title);\n\t\t\t\treturn _passages.hasOwnProperty(id) ? _passages[id] : new Passage(id || '(unknown)');\n\t\t\t}\n\t\t/* eslint-enable indent */\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.get title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGetAllRegular() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Object.assign({}, _passages));\n\t}\n\n\tfunction passagesGetAllScript() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_scripts));\n\t}\n\n\tfunction passagesGetAllStylesheet() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_styles));\n\t}\n\n\tfunction passagesGetAllWidget() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_widgets));\n\t}\n\n\tfunction passagesLookup(key, value  /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\t// Objects (sans `null`).\n\t\t\tif (typeof passage[key] === 'object' && passage[key] !== null) {\n\t\t\t\t// The only object type currently supported is `Array`, since the\n\t\t\t\t// non-method `Passage` object properties currently yield only either\n\t\t\t\t// primitives or arrays.\n\t\t\t\tif (passage[key] instanceof Array && passage[key].some(m => Util.sameValueZero(m, value))) {\n\t\t\t\t\tresults.push(passage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// All other types (incl. `null`).\n\t\t\telse if (Util.sameValueZero(passage[key], value)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\tfunction passagesLookupWith(predicate /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tif (typeof predicate !== 'function') {\n\t\t\tthrow new TypeError('Story.lookupWith predicate parameter must be a function');\n\t\t}\n\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\tif (predicate(passage)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Story Functions.\n\t\tload  : { value : storyLoad },\n\t\tinit  : { value : storyInit },\n\t\ttitle : { get : storyTitle },\n\t\tdomId : { get : storyDomId },\n\t\tifId  : { get : storyIfId },\n\n\t\t// Passage Functions.\n\t\tadd              : { value : passagesAdd },\n\t\thas              : { value : passagesHas },\n\t\tget              : { value : passagesGet },\n\t\tgetAllRegular    : { value : passagesGetAllRegular },\n\t\tgetAllScript     : { value : passagesGetAllScript },\n\t\tgetAllStylesheet : { value : passagesGetAllStylesheet },\n\t\tgetAllWidget     : { value : passagesGetAllWidget },\n\t\tlookup           : { value : passagesLookup },\n\t\tlookupWith       : { value : passagesLookupWith }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tui.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, Has, L10n, Save, Setting, State, Story, Util, Wikifier, Config, errorPrologRegExp,\n\t       settings\n*/\n\nvar UI = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Core.\n\t*******************************************************************************************************************/\n\tfunction uiAssembleLinkList(passage, listEl) {\n\t\tlet list = listEl;\n\n\t\t// Cache the values of `Config.debug` and `Config.cleanupWikifierOutput`,\n\t\t// then disable them during this method's run.\n\t\tconst debugState = Config.debug;\n\t\tconst cleanState = Config.cleanupWikifierOutput;\n\t\tConfig.debug = false;\n\t\tConfig.cleanupWikifierOutput = false;\n\n\t\ttry {\n\t\t\tif (list == null) { // lazy equality for null\n\t\t\t\tlist = document.createElement('ul');\n\t\t\t}\n\n\t\t\t// Wikify the content of the given source passage into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, Story.get(passage).processText().trim());\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\n\t\t\twhile (frag.hasChildNodes()) {\n\t\t\t\tconst node = frag.firstChild;\n\n\t\t\t\t// Create list items for <a>-element nodes.\n\t\t\t\tif (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toUpperCase() === 'A') {\n\t\t\t\t\tconst li = document.createElement('li');\n\t\t\t\t\tlist.appendChild(li);\n\t\t\t\t\tli.appendChild(node);\n\t\t\t\t}\n\n\t\t\t\t// Discard non-<a>-element nodes.\n\t\t\t\telse {\n\t\t\t\t\tfrag.removeChild(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t// Restore the `Config` settings to their original values.\n\t\t\tConfig.cleanupWikifierOutput = cleanState;\n\t\t\tConfig.debug = debugState;\n\t\t}\n\n\t\treturn list;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Built-ins.\n\t*******************************************************************************************************************/\n\tfunction uiOpenAlert(message, /* options, closeFn */ ...args) {\n\t\tjQuery(Dialog.setup(L10n.get('alertTitle'), 'alert'))\n\t\t\t.append(\n\t\t\t\t  `<p>${message}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"alert-ok\" class=\"ui-close\">${L10n.get(['alertOk', 'ok'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t);\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenJumpto(/* options, closeFn */ ...args) {\n\t\tuiBuildJumpto();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenRestart(/* options, closeFn */ ...args) {\n\t\tuiBuildRestart();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSaves(/* options, closeFn */ ...args) {\n\t\tuiBuildSaves();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSettings(/* options, closeFn */ ...args) {\n\t\tuiBuildSettings();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenShare(/* options, closeFn */ ...args) {\n\t\tuiBuildShare();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiBuildAutoload() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildAutoload()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('autoloadTitle'), 'autoload'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('autoloadPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"autoload-ok\" class=\"ui-close\">${L10n.get(['autoloadOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"autoload-cancel\" class=\"ui-close\">${L10n.get(['autoloadCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t// Add an additional delegated click handler for the `.ui-close` elements to handle autoloading.\n\t\tjQuery(document).one('click.autoload', '.ui-close', ev => {\n\t\t\tconst isAutoloadOk = ev.target.id === 'autoload-ok';\n\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\tif (!isAutoloadOk || !Save.autosave.load()) {\n\t\t\t\t\tEngine.play(Config.passages.start);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildJumpto() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildJumpto()]'); }\n\n\t\tconst list = document.createElement('ul');\n\n\t\tjQuery(Dialog.setup(L10n.get('jumptoTitle'), 'jumpto list'))\n\t\t\t.append(list);\n\n\t\tconst expired = State.expired.length;\n\n\t\tfor (let i = State.size - 1; i >= 0; --i) {\n\t\t\tif (i === State.activeIndex) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst passage = Story.get(State.history[i].title);\n\n\t\t\tif (passage && passage.tags.includes('bookmark')) {\n\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t.append(\n\t\t\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t\t\t.ariaClick({ one : true }, (function (idx) {\n\t\t\t\t\t\t\t\treturn () => jQuery(document).one(':dialogclosed', () => Engine.goTo(idx));\n\t\t\t\t\t\t\t})(i))\n\t\t\t\t\t\t\t.addClass('ui-close')\n\t\t\t\t\t\t\t.text(`${L10n.get('jumptoTurn')} ${expired + i + 1}: ${passage.description()}`)\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo(list);\n\t\t\t}\n\t\t}\n\n\t\tif (!list.hasChildNodes()) {\n\t\t\tjQuery(list).append(`<li><a><em>${L10n.get('jumptoUnavailable')}</em></a></li>`);\n\t\t}\n\t}\n\n\tfunction uiBuildRestart() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildRestart()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('restartTitle'), 'restart'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('restartPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"restart-ok\">${L10n.get(['restartOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"restart-cancel\" class=\"ui-close\">${L10n.get(['restartCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.find('#restart-ok')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#restart-ok' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `Engine.restart()` happens after the dialog\n\t\t\t\thas fully closed.  If we did not, then a race condition could occur, causing display\n\t\t\t\tshenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => Engine.restart());\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildSaves() {\n\t\tconst savesAllowed = typeof Config.saves.isAllowed !== 'function' || Config.saves.isAllowed();\n\n\t\tfunction createActionItem(bId, bClass, bText, bAction) {\n\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t.attr('id', `saves-${bId}`)\n\t\t\t\t.html(bText);\n\n\t\t\tif (bClass) {\n\t\t\t\t$btn.addClass(bClass);\n\t\t\t}\n\n\t\t\tif (bAction) {\n\t\t\t\t$btn.ariaClick(bAction);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('li'))\n\t\t\t\t.append($btn);\n\t\t}\n\n\t\tfunction createSaveList() {\n\t\t\tfunction createButton(bId, bClass, bText, bSlot, bAction) {\n\t\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t\t.attr('id', `saves-${bId}-${bSlot}`)\n\t\t\t\t\t.addClass(bId)\n\t\t\t\t\t.html(bText);\n\n\t\t\t\tif (bClass) {\n\t\t\t\t\t$btn.addClass(bClass);\n\t\t\t\t}\n\n\t\t\t\tif (bAction) {\n\t\t\t\t\tif (bSlot === 'auto') {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelAuto')}`\n\t\t\t\t\t\t}, () => bAction());\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelSlot')} ${bSlot + 1}`\n\t\t\t\t\t\t}, () => bAction(bSlot));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t\t}\n\n\t\t\t\treturn $btn;\n\t\t\t}\n\n\t\t\tconst index  = Save.index();\n\t\t\tconst $tbody = jQuery(document.createElement('tbody'));\n\n\t\t\tif (Save.autosave.ok()) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\tjQuery(document.createElement('b'))\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttitle        : L10n.get('savesLabelAuto'),\n\t\t\t\t\t\t'aria-label' : L10n.get('savesLabelAuto')\n\t\t\t\t\t})\n\t\t\t\t\t.text('A') // '\\u25C6' Black Diamond\n\t\t\t\t\t.appendTo($tdSlot);\n\n\t\t\t\tif (index.autosave) {\n\t\t\t\t\t// Add the load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), 'auto', () => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.autosave.load());\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.autosave.title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.autosave.date\n\t\t\t\t\t\t\t\t? `${new Date(index.autosave.date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto', () => {\n\t\t\t\t\t\t\tSave.autosave.delete();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the disabled load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', null, L10n.get('savesLabelLoad'), 'auto')\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto')\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = index.slots.length; i < iend; ++i) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\t$tdSlot.append(document.createTextNode(i + 1));\n\n\t\t\t\tif (index.slots[i]) {\n\t\t\t\t\t// Add the save & load buttons.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, Save.slots.save),\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), i, slot => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.slots.load(slot));\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.slots[i].title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.slots[i].date\n\t\t\t\t\t\t\t\t? `${new Date(index.slots[i].date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i, slot => {\n\t\t\t\t\t\t\tSave.slots.delete(slot);\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the save button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, savesAllowed ? Save.slots.save : null)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i)\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('table'))\n\t\t\t\t.attr('id', 'saves-list')\n\t\t\t\t.append($tbody);\n\t\t}\n\n\t\tif (DEBUG) { console.log('[UI/uiBuildSaves()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('savesTitle'), 'saves'));\n\t\tconst savesOk     = Save.ok();\n\n\t\t// Add saves list.\n\t\tif (savesOk) {\n\t\t\t$dialogBody.append(createSaveList());\n\t\t}\n\n\t\t// Add button bar items (export, import, and clear).\n\t\tif (savesOk || Has.fileAPI) {\n\t\t\tconst $btnBar = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass('buttons')\n\t\t\t\t.appendTo($dialogBody);\n\n\t\t\tif (Has.fileAPI) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'export',\n\t\t\t\t\t'ui-close',\n\t\t\t\t\tL10n.get('savesLabelExport'),\n\t\t\t\t\tsavesAllowed ? () => Save.export() : null\n\t\t\t\t));\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'import',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelImport'),\n\t\t\t\t\t() => $dialogBody.find('#saves-import-file').trigger('click')\n\t\t\t\t));\n\n\t\t\t\t// Add the hidden `input[type=file]` element which will be triggered by the `#saves-import` button.\n\t\t\t\tjQuery(document.createElement('input'))\n\t\t\t\t\t.css({\n\t\t\t\t\t\tdisplay    : 'block',\n\t\t\t\t\t\tvisibility : 'hidden',\n\t\t\t\t\t\tposition   : 'fixed',\n\t\t\t\t\t\tleft       : '-9999px',\n\t\t\t\t\t\ttop        : '-9999px',\n\t\t\t\t\t\twidth      : '1px',\n\t\t\t\t\t\theight     : '1px'\n\t\t\t\t\t})\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype          : 'file',\n\t\t\t\t\t\tid            : 'saves-import-file',\n\t\t\t\t\t\ttabindex      : -1,\n\t\t\t\t\t\t'aria-hidden' : true\n\t\t\t\t\t})\n\t\t\t\t\t.on('change', ev => {\n\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.import(ev));\n\t\t\t\t\t\tDialog.close();\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t}\n\n\t\t\tif (savesOk) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'clear',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelClear'),\n\t\t\t\t\tSave.autosave.has() || !Save.slots.isEmpty()\n\t\t\t\t\t\t? () => {\n\t\t\t\t\t\t\tSave.clear();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null\n\t\t\t\t));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tuiOpenAlert(L10n.get('savesIncapable'));\n\t\treturn false;\n\t}\n\n\tfunction uiBuildSettings() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildSettings()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('settingsTitle'), 'settings'));\n\n\t\tSetting.forEach(control => {\n\t\t\tif (control.type === Setting.Types.Header) {\n\t\t\t\tconst name     = control.name;\n\t\t\t\tconst id       = Util.slugify(name);\n\t\t\t\tconst $header  = jQuery(document.createElement('div'));\n\t\t\t\tconst $heading = jQuery(document.createElement('h2'));\n\n\t\t\t\t$header\n\t\t\t\t\t.attr('id', `header-body-${id}`)\n\t\t\t\t\t.append($heading)\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t\t$heading\n\t\t\t\t\t.attr('id', `header-heading-${id}`)\n\t\t\t\t\t.wiki(name);\n\n\t\t\t\t// Set up the description, if any.\n\t\t\t\tif (control.desc) {\n\t\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t\t.attr('id', `header-desc-${id}`)\n\t\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t\t.appendTo($header);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst name        = control.name;\n\t\t\tconst id          = Util.slugify(name);\n\t\t\tconst $setting    = jQuery(document.createElement('div'));\n\t\t\tconst $label      = jQuery(document.createElement('label'));\n\t\t\tconst $controlBox = jQuery(document.createElement('div'));\n\t\t\tlet $control;\n\n\t\t\t// Set up the label+control wrapper.\n\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t.append($label)\n\t\t\t\t.append($controlBox)\n\t\t\t\t.appendTo($setting);\n\n\t\t\t// Set up the description, if any.\n\t\t\tif (control.desc) {\n\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t.attr('id', `setting-desc-${id}`)\n\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t.appendTo($setting);\n\t\t\t}\n\n\t\t\t// Set up the label.\n\t\t\t$label\n\t\t\t\t.attr({\n\t\t\t\t\tid  : `setting-label-${id}`,\n\t\t\t\t\tfor : `setting-control-${id}` // must be in sync with $control's ID (see below)\n\t\t\t\t})\n\t\t\t\t.wiki(control.label);\n\n\t\t\t// Set up the control.\n\t\t\tif (settings[name] == null) { // lazy equality for null\n\t\t\t\tsettings[name] = control.default;\n\t\t\t}\n\n\t\t\tswitch (control.type) {\n\t\t\tcase Setting.Types.Toggle:\n\t\t\t\t$control = jQuery(document.createElement('button'));\n\n\t\t\t\tif (settings[name]) {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t}\n\n\t\t\t\t$control.ariaClick(function () {\n\t\t\t\t\tif (settings[name]) {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.removeClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t\t\tsettings[name] = false;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t\t\tsettings[name] = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tSetting.save();\n\n\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\tdefault : control.default\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.List:\n\t\t\t\t$control = jQuery(document.createElement('select'));\n\n\t\t\t\tfor (let i = 0, iend = control.list.length; i < iend; ++i) {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(control.list[i])\n\t\t\t\t\t\t.appendTo($control);\n\t\t\t\t}\n\n\t\t\t\t$control\n\t\t\t\t\t.val(control.list.indexOf(settings[name]))\n\t\t\t\t\t.attr('tabindex', 0)\n\t\t\t\t\t.on('change', function () {\n\t\t\t\t\t\tsettings[name] = control.list[Number(this.value)];\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tlist    : control.list\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.Range:\n\t\t\t\t$control = jQuery(document.createElement('input'));\n\n\t\t\t\t// NOTE: Setting the value with `<jQuery>.val()` can cause odd behavior\n\t\t\t\t// in Edge if it's called before the type is set, so we use the `value`\n\t\t\t\t// content attribute here to dodge the entire issue.\n\t\t\t\t$control\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype     : 'range',\n\t\t\t\t\t\tmin      : control.min,\n\t\t\t\t\t\tmax      : control.max,\n\t\t\t\t\t\tstep     : control.step,\n\t\t\t\t\t\tvalue    : settings[name],\n\t\t\t\t\t\ttabindex : 0\n\t\t\t\t\t})\n\t\t\t\t\t.on('change input', function () {\n\t\t\t\t\t\tsettings[name] = Number(this.value);\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tmin     : control.min,\n\t\t\t\t\t\t\t\tmax     : control.max,\n\t\t\t\t\t\t\t\tstep    : control.step\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.on('keypress', ev => {\n\t\t\t\t\t\tif (ev.which === 13) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$control.trigger('change');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$control\n\t\t\t\t.attr('id', `setting-control-${id}`)\n\t\t\t\t.appendTo($controlBox);\n\n\t\t\t$setting\n\t\t\t\t.attr('id', `setting-body-${id}`)\n\t\t\t\t.appendTo($dialogBody);\n\t\t});\n\n\t\t// Add the button bar.\n\t\t$dialogBody\n\t\t\t.append(\n\t\t\t\t  '<ul class=\"buttons\">'\n\t\t\t\t+     `<li><button id=\"settings-ok\" class=\"ui-close\">${L10n.get(['settingsOk', 'ok'])}</button></li>`\n\t\t\t\t+     `<li><button id=\"settings-reset\">${L10n.get('settingsReset')}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t)\n\t\t\t.find('#settings-reset')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#settings-reset' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `window.location.reload()` happens after the\n\t\t\t\tdialog has fully closed.  If we did not, then a race condition could occur, causing\n\t\t\t\tdisplay shenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\t\tSetting.reset();\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t});\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildShare() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildShare()]'); }\n\n\t\ttry {\n\t\t\tjQuery(Dialog.setup(L10n.get('shareTitle'), 'share list'))\n\t\t\t\t.append(uiAssembleLinkList('StoryShare'));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tconsole.error(ex);\n\t\t\tAlert.error('StoryShare', ex.message);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tUI Functions, Core.\n\t\t*/\n\t\tassembleLinkList : { value : uiAssembleLinkList },\n\n\t\t/*\n\t\t\tUI Functions, Built-ins.\n\t\t*/\n\t\talert         : { value : uiOpenAlert },\n\t\tjumpto        : { value : uiOpenJumpto },\n\t\trestart       : { value : uiOpenRestart },\n\t\tsaves         : { value : uiOpenSaves },\n\t\tsettings      : { value : uiOpenSettings },\n\t\tshare         : { value : uiOpenShare },\n\t\tbuildAutoload : { value : uiBuildAutoload },\n\t\tbuildJumpto   : { value : uiBuildJumpto },\n\t\tbuildRestart  : { value : uiBuildRestart },\n\t\tbuildSaves    : { value : uiBuildSaves },\n\t\tbuildSettings : { value : uiBuildSettings },\n\t\tbuildShare    : { value : uiBuildShare },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\t// `UIBar` methods.\n\t\t/* global UIBar */\n\t\tstow                     : { value : () => UIBar.stow() },\n\t\tunstow                   : { value : () => UIBar.unstow() },\n\t\tsetStoryElements         : { value : () => UIBar.update() },\n\t\t// `Dialog` methods.\n\t\tisOpen                   : { value : (...args) => Dialog.isOpen(...args) },\n\t\tbody                     : { value : () => Dialog.body() },\n\t\tsetup                    : { value : (...args) => Dialog.setup(...args) },\n\t\taddClickHandler          : { value : (...args) => Dialog.addClickHandler(...args) },\n\t\topen                     : { value : (...args) => Dialog.open(...args) },\n\t\tclose                    : { value : (...args) => Dialog.close(...args) },\n\t\tresize                   : { value : () => Dialog.resize() },\n\t\t// Deprecated method names.\n\t\tbuildDialogAutoload      : { value : uiBuildAutoload },\n\t\tbuildDialogJumpto        : { value : uiBuildJumpto },\n\t\tbuildDialogRestart       : { value : uiBuildRestart },\n\t\tbuildDialogSaves         : { value : uiBuildSaves },\n\t\tbuildDialogSettings      : { value : uiBuildSettings },\n\t\tbuildDialogShare         : { value : uiBuildShare },\n\t\tbuildLinkListFromPassage : { value : uiAssembleLinkList }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tuibar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, L10n, Setting, State, Story, UI, Config, setDisplayTitle, setPageElement\n*/\n\nvar UIBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// UI bar element cache.\n\tlet _$uiBar = null;\n\n\n\t/*******************************************************************************\n\t\tUI Bar Functions.\n\t*******************************************************************************/\n\n\tfunction uiBarDestroy() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarDestroy()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Hide the UI bar.\n\t\t_$uiBar.hide();\n\n\t\t// Remove its namespaced events.\n\t\tjQuery(document).off('.ui-bar');\n\n\t\t// Remove its styles.\n\t\tjQuery(document.head).find('#style-ui-bar').remove();\n\n\t\t// Remove it from the DOM.\n\t\t_$uiBar.remove();\n\n\t\t// Drop the reference to the element.\n\t\t_$uiBar = null;\n\t}\n\n\tfunction uiBarHide() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.hide();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarInit() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarInit()]'); }\n\n\t\tif (document.getElementById('ui-bar')) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Generate the UI bar elements.\n\t\tconst $elems = (() => {\n\t\t\tconst toggleLabel   = L10n.get('uiBarToggle');\n\t\t\tconst backwardLabel = L10n.get('uiBarBackward');\n\t\t\tconst jumptoLabel   = L10n.get('uiBarJumpto');\n\t\t\tconst forwardLabel  = L10n.get('uiBarForward');\n\n\t\t\treturn jQuery(document.createDocumentFragment())\n\t\t\t\t.append(\n\t\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t\t  '<div id=\"ui-bar\">'\n\t\t\t\t\t+     '<div id=\"ui-bar-tray\">'\n\t\t\t\t\t+         `<button id=\"ui-bar-toggle\" tabindex=\"0\" title=\"${toggleLabel}\" aria-label=\"${toggleLabel}\"></button>`\n\t\t\t\t\t+         '<div id=\"ui-bar-history\">'\n\t\t\t\t\t+             `<button id=\"history-backward\" tabindex=\"0\" title=\"${backwardLabel}\" aria-label=\"${backwardLabel}\">\\uE821</button>`\n\t\t\t\t\t+             `<button id=\"history-jumpto\" tabindex=\"0\" title=\"${jumptoLabel}\" aria-label=\"${jumptoLabel}\">\\uE839</button>`\n\t\t\t\t\t+             `<button id=\"history-forward\" tabindex=\"0\" title=\"${forwardLabel}\" aria-label=\"${forwardLabel}\">\\uE822</button>`\n\t\t\t\t\t+         '</div>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+     '<div id=\"ui-bar-body\">'\n\t\t\t\t\t+         '<header id=\"title\" role=\"banner\">'\n\t\t\t\t\t+             '<div id=\"story-banner\"></div>'\n\t\t\t\t\t+             '<h1 id=\"story-title\"></h1>'\n\t\t\t\t\t+             '<div id=\"story-subtitle\"></div>'\n\t\t\t\t\t+             '<div id=\"story-title-separator\"></div>'\n\t\t\t\t\t+             '<p id=\"story-author\"></p>'\n\t\t\t\t\t+         '</header>'\n\t\t\t\t\t+         '<div id=\"story-caption\"></div>'\n\t\t\t\t\t+         '<nav id=\"menu\" role=\"navigation\">'\n\t\t\t\t\t+             '<ul id=\"menu-story\"></ul>'\n\t\t\t\t\t+             '<ul id=\"menu-core\">'\n\t\t\t\t\t+                 `<li id=\"menu-item-saves\"><a tabindex=\"0\">${L10n.get('savesTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-settings\"><a tabindex=\"0\">${L10n.get('settingsTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-restart\"><a tabindex=\"0\">${L10n.get('restartTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-share\"><a tabindex=\"0\">${L10n.get('shareTitle')}</a></li>`\n\t\t\t\t\t+             '</ul>'\n\t\t\t\t\t+         '</nav>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+ '</div>'\n\t\t\t\t\t/* eslint-enable max-len */\n\t\t\t\t);\n\t\t})();\n\n\t\t/*\n\t\t\tCache the UI bar element, since its going to be used often.\n\n\t\t\tNOTE: We rewrap the element itself, rather than simply using the result\n\t\t\tof `find()`, so that we cache an uncluttered jQuery-wrapper (i.e. `context`\n\t\t\trefers to the element and there is no `prevObject`).\n\t\t*/\n\t\t_$uiBar = jQuery($elems.find('#ui-bar').get(0));\n\n\t\t// Insert the UI bar elements into the page before the main script.\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\n\t\t// Set up the UI bar's global event handlers.\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history-backward/-forward buttons.\n\t\t\t.on(':historyupdate.ui-bar', (($backward, $forward) => () => {\n\t\t\t\t$backward.ariaDisabled(State.length < 2);\n\t\t\t\t$forward.ariaDisabled(State.length === State.size);\n\t\t\t})(jQuery('#history-backward'), jQuery('#history-forward')));\n\t}\n\n\tfunction uiBarIsHidden() {\n\t\treturn _$uiBar && _$uiBar.css('display') === 'none';\n\t}\n\n\tfunction uiBarIsStowed() {\n\t\treturn _$uiBar && _$uiBar.hasClass('stowed');\n\t}\n\n\tfunction uiBarShow() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.show();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarStart() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarStart()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the #ui-bar's initial state.\n\t\tif (\n\t\t\ttypeof Config.ui.stowBarInitially === 'boolean'\n\t\t\t\t? Config.ui.stowBarInitially\n\t\t\t\t: jQuery(window).width() <= Config.ui.stowBarInitially\n\t\t) {\n\t\t\tuiBarStow(true);\n\t\t}\n\n\t\t// Set up the #ui-bar-toggle and #ui-bar-history widgets.\n\t\tjQuery('#ui-bar-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('uiBarToggle')\n\t\t\t}, () => _$uiBar.toggleClass('stowed'));\n\n\t\tif (Config.history.controls) {\n\t\t\tjQuery('#history-backward')\n\t\t\t\t.ariaDisabled(State.length < 2)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarBackward')\n\t\t\t\t}, () => Engine.backward());\n\n\t\t\tif (Story.lookup('tags', 'bookmark').length > 0) {\n\t\t\t\tjQuery('#history-jumpto')\n\t\t\t\t\t.ariaClick({\n\t\t\t\t\t\tlabel : L10n.get('uiBarJumpto')\n\t\t\t\t\t}, () => UI.jumpto());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery('#history-jumpto').remove();\n\t\t\t}\n\n\t\t\tjQuery('#history-forward')\n\t\t\t\t.ariaDisabled(State.length === State.size)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarForward')\n\t\t\t\t}, () => Engine.forward());\n\t\t}\n\t\telse {\n\t\t\tjQuery('#ui-bar-history').remove();\n\t\t}\n\n\t\t// Set up the story display title.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\telse {\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tsetPageElement('story-title', 'StoryTitle', Story.title);\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tjQuery('#story-title').text(Story.title);\n\t\t\t}\n\t\t}\n\n\t\t// Set up the dynamic page elements.\n\t\tif (!Story.has('StoryCaption')) {\n\t\t\tjQuery('#story-caption').remove();\n\t\t}\n\n\t\tif (!Story.has('StoryMenu')) {\n\t\t\tjQuery('#menu-story').remove();\n\t\t}\n\n\t\tif (!Config.ui.updateStoryElements) {\n\t\t\t// We only need to set the story elements here if `Config.ui.updateStoryElements`\n\t\t\t// is falsy, since otherwise they will be set by `Engine.play()`.\n\t\t\tuiBarUpdate();\n\t\t}\n\n\t\t// Set up the Saves menu item.\n\t\tjQuery('#menu-item-saves a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildSaves();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('savesTitle'));\n\n\t\t// Set up the Settings menu item.\n\t\tif (!Setting.isEmpty()) {\n\t\t\tjQuery('#menu-item-settings a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildSettings();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('settingsTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-settings').remove();\n\t\t}\n\n\t\t// Set up the Restart menu item.\n\t\tjQuery('#menu-item-restart a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildRestart();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('restartTitle'));\n\n\t\t// Set up the Share menu item.\n\t\tif (Story.has('StoryShare')) {\n\t\t\tjQuery('#menu-item-share a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildShare();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('shareTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-share').remove();\n\t\t}\n\t}\n\n\tfunction uiBarStow(noAnimation) {\n\t\tif (_$uiBar && !_$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.addClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUnstow(noAnimation) {\n\t\tif (_$uiBar && _$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.removeClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUpdate() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarUpdate()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the (non-navigation) dynamic page elements.\n\t\tsetPageElement('story-banner', 'StoryBanner');\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\tsetPageElement('story-subtitle', 'StorySubtitle');\n\t\tsetPageElement('story-author', 'StoryAuthor');\n\t\tsetPageElement('story-caption', 'StoryCaption');\n\n\t\t// Set up the #menu-story items.\n\t\tconst menuStory = document.getElementById('menu-story');\n\n\t\tif (menuStory !== null) {\n\t\t\tjQuery(menuStory).empty();\n\n\t\t\tif (Story.has('StoryMenu')) {\n\t\t\t\ttry {\n\t\t\t\t\tUI.assembleLinkList('StoryMenu', menuStory);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tconsole.error(ex);\n\t\t\t\t\tAlert.error('StoryMenu', ex.message);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tdestroy  : { value : uiBarDestroy },\n\t\thide     : { value : uiBarHide },\n\t\tinit     : { value : uiBarInit },\n\t\tisHidden : { value : uiBarIsHidden },\n\t\tisStowed : { value : uiBarIsStowed },\n\t\tshow     : { value : uiBarShow },\n\t\tstart    : { value : uiBarStart },\n\t\tstow     : { value : uiBarStow },\n\t\tunstow   : { value : uiBarUnstow },\n\t\tupdate   : { value : uiBarUpdate },\n\n\t\t// Legacy Functions.\n\t\tsetStoryElements : { value : uiBarUpdate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tdebugbar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal DebugView, Engine, L10n, Patterns, State, Util, session\n*/\n\nvar DebugBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _variableRe   = new RegExp(`^${Patterns.variable}$`);\n\tconst _numericKeyRe = /^\\d+$/;\n\tconst _watchList    = [];\n\tlet _$debugBar   = null;\n\tlet _$watchBody  = null;\n\tlet _$watchList  = null;\n\tlet _$turnSelect = null;\n\tlet _stowed      = true;\n\n\n\t/*******************************************************************************************************************\n\t\tDebug Bar Functions.\n\t*******************************************************************************************************************/\n\tfunction debugBarInit() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarInit()]'); }\n\n\t\t/*\n\t\t\tGenerate the debug bar elements and append them to the `<body>`.\n\t\t*/\n\t\tconst barToggleLabel   = L10n.get('debugBarToggle');\n\t\tconst watchAddLabel    = L10n.get('debugBarAddWatch');\n\t\tconst watchAllLabel    = L10n.get('debugBarWatchAll');\n\t\tconst watchNoneLabel   = L10n.get('debugBarWatchNone');\n\t\tconst watchToggleLabel = L10n.get('debugBarWatchToggle');\n\t\tconst viewsToggleLabel = L10n.get('debugBarViewsToggle');\n\n\t\tjQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"debug-bar\">'\n\t\t\t\t+     '<div id=\"debug-bar-watch\">'\n\t\t\t\t+         `<div>${L10n.get('debugBarNoWatches')}</div>>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-toggle\" tabindex=\"0\" title=\"${watchToggleLabel}\" aria-label=\"${watchToggleLabel}\">${L10n.get('debugBarLabelWatch')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-watch-label\" for=\"debug-bar-watch-input\">${L10n.get('debugBarLabelAdd')}</label>`\n\t\t\t\t+         '<input id=\"debug-bar-watch-input\" name=\"debug-bar-watch-input\" type=\"text\" list=\"debug-bar-watch-list\" tabindex=\"0\">'\n\t\t\t\t+         '<datalist id=\"debug-bar-watch-list\" aria-hidden=\"true\" hidden=\"hidden\"></datalist>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-add\" tabindex=\"0\" title=\"${watchAddLabel}\" aria-label=\"${watchAddLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-all\" tabindex=\"0\" title=\"${watchAllLabel}\" aria-label=\"${watchAllLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-none\" tabindex=\"0\" title=\"${watchNoneLabel}\" aria-label=\"${watchNoneLabel}\"></button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-views-toggle\" tabindex=\"0\" title=\"${viewsToggleLabel}\" aria-label=\"${viewsToggleLabel}\">${L10n.get('debugBarLabelViews')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-turn-label\" for=\"debug-bar-turn-select\">${L10n.get('debugBarLabelTurn')}</label>`\n\t\t\t\t+         '<select id=\"debug-bar-turn-select\" tabindex=\"0\"></select>'\n\t\t\t\t+     '</div>'\n\t\t\t\t+     `<button id=\"debug-bar-toggle\" tabindex=\"0\" title=\"${barToggleLabel}\" aria-label=\"${barToggleLabel}\"></button>`\n\t\t\t\t+ '</div>'\n\t\t\t\t+ '<div id=\"debug-bar-hint\"></div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.appendTo('body');\n\n\t\t/*\n\t\t\tCache various oft used elements.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$debugBar   = jQuery('#debug-bar');\n\t\t_$watchBody  = jQuery(_$debugBar.find('#debug-bar-watch').get(0));\n\t\t_$watchList  = jQuery(_$debugBar.find('#debug-bar-watch-list').get(0));\n\t\t_$turnSelect = jQuery(_$debugBar.find('#debug-bar-turn-select').get(0));\n\n\t\tconst $barToggle   = jQuery(_$debugBar.find('#debug-bar-toggle').get(0));\n\t\tconst $watchToggle = jQuery(_$debugBar.find('#debug-bar-watch-toggle').get(0));\n\t\tconst $watchInput  = jQuery(_$debugBar.find('#debug-bar-watch-input').get(0));\n\t\tconst $watchAdd    = jQuery(_$debugBar.find('#debug-bar-watch-add').get(0));\n\t\tconst $watchAll    = jQuery(_$debugBar.find('#debug-bar-watch-all').get(0));\n\t\tconst $watchNone   = jQuery(_$debugBar.find('#debug-bar-watch-none').get(0));\n\t\tconst $viewsToggle = jQuery(_$debugBar.find('#debug-bar-views-toggle').get(0));\n\n\t\t/*\n\t\t\tSet up the debug bar's local event handlers.\n\t\t*/\n\t\t$barToggle\n\t\t\t.ariaClick(debugBarToggle);\n\t\t$watchToggle\n\t\t\t.ariaClick(debugBarWatchToggle);\n\t\t$watchInput\n\t\t\t.on(':addwatch', function () {\n\t\t\t\tdebugBarWatchAdd(this.value.trim());\n\t\t\t\tthis.value = '';\n\t\t\t})\n\t\t\t.on('keypress', ev => {\n\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t$watchInput.trigger(':addwatch');\n\t\t\t\t}\n\t\t\t});\n\t\t$watchAdd\n\t\t\t.ariaClick(() => $watchInput.trigger(':addwatch'));\n\t\t$watchAll\n\t\t\t.ariaClick(debugBarWatchAddAll);\n\t\t$watchNone\n\t\t\t.ariaClick(debugBarWatchClear);\n\t\t_$turnSelect\n\t\t\t.on('change', function () {\n\t\t\t\tEngine.goTo(Number(this.value));\n\t\t\t});\n\t\t$viewsToggle\n\t\t\t.ariaClick(() => {\n\t\t\t\tDebugView.toggle();\n\t\t\t\t_updateSession();\n\t\t\t});\n\n\t\t/*\n\t\t\tSet up the debug bar's global event handlers.\n\t\t*/\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history select.\n\t\t\t.on(':historyupdate.debug-bar', _updateTurnSelect)\n\t\t\t// Set up a handler for the variables watch.\n\t\t\t.on(':passageend.debug-bar', () => {\n\t\t\t\t_updateWatchBody();\n\t\t\t\t_updateWatchList();\n\t\t\t})\n\t\t\t// Set up a handler for engine resets to clear the active debug session.\n\t\t\t.on(':enginerestart.debug-bar', _clearSession);\n\n\t\t/*\n\t\t\tInitially enable debug views if there's no active debug session.\n\t\t*/\n\t\tif (!_hasSession()) {\n\t\t\tDebugView.enable();\n\t\t}\n\t}\n\n\tfunction debugBarStart() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarStart()]'); }\n\n\t\t// Attempt to restore an existing session.\n\t\t_restoreSession();\n\n\t\t// Update the UI.\n\t\t_updateBar();\n\t\t_updateTurnSelect();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t}\n\n\tfunction debugBarIsStowed() {\n\t\treturn _stowed;\n\t}\n\n\tfunction debugBarStow() {\n\t\t_debugBarStowNoUpdate();\n\t\t_stowed = true;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarUnstow() {\n\t\t_debugBarUnstowNoUpdate();\n\t\t_stowed = false;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarToggle() {\n\t\tif (_stowed) {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarStow();\n\t\t}\n\t}\n\n\tfunction debugBarWatchAdd(varName) {\n\t\tif (!_variableRe.test(varName)) {\n\t\t\treturn;\n\t\t}\n\n\t\t_watchList.pushUnique(varName);\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchAddAll() {\n\t\tObject.keys(State.variables).map(name => _watchList.pushUnique(`$${name}`));\n\t\tObject.keys(State.temporary).map(name => _watchList.pushUnique(`_${name}`));\n\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchClear() {\n\t\tfor (let i = _watchList.length - 1; i >= 0; --i) {\n\t\t\t_watchList.pop();\n\t\t}\n\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDelete(varName) {\n\t\t_watchList.delete(varName);\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDisable() {\n\t\t_debugBarWatchDisableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchEnable() {\n\t\t_debugBarWatchEnableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchIsEnabled() {\n\t\treturn !_$watchBody.attr('hidden');\n\t}\n\n\tfunction debugBarWatchToggle() {\n\t\tif (_$watchBody.attr('hidden')) {\n\t\t\tdebugBarWatchEnable();\n\t\t}\n\t\telse {\n\t\t\tdebugBarWatchDisable();\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _debugBarStowNoUpdate() {\n\t\t_$debugBar.css('right', `-${_$debugBar.outerWidth()}px`);\n\t}\n\n\tfunction _debugBarUnstowNoUpdate() {\n\t\t_$debugBar.css('right', 0);\n\t}\n\n\tfunction _debugBarWatchDisableNoUpdate() {\n\t\t_$watchBody.attr({\n\t\t\t'aria-hidden' : true,\n\t\t\thidden        : 'hidden'\n\t\t});\n\t}\n\n\tfunction _debugBarWatchEnableNoUpdate() {\n\t\t_$watchBody.removeAttr('aria-hidden hidden');\n\t}\n\n\tfunction _clearSession() {\n\t\tsession.delete('debugState');\n\t}\n\n\tfunction _hasSession() {\n\t\treturn session.has('debugState');\n\t}\n\n\tfunction _restoreSession() {\n\t\tif (!_hasSession()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst debugState = session.get('debugState');\n\n\t\t_stowed = debugState.stowed;\n\n\t\t_watchList.push(...debugState.watchList);\n\n\t\tif (debugState.watchEnabled) {\n\t\t\t_debugBarWatchEnableNoUpdate();\n\t\t}\n\t\telse {\n\t\t\t_debugBarWatchDisableNoUpdate();\n\t\t}\n\n\t\tif (debugState.viewsEnabled) {\n\t\t\tDebugView.enable();\n\t\t}\n\t\telse {\n\t\t\tDebugView.disable();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction _updateSession() {\n\t\tsession.set('debugState', {\n\t\t\tstowed       : _stowed,\n\t\t\twatchList    : _watchList,\n\t\t\twatchEnabled : debugBarWatchIsEnabled(),\n\t\t\tviewsEnabled : DebugView.isEnabled()\n\t\t});\n\t}\n\n\tfunction _updateBar() {\n\t\tif (_stowed) {\n\t\t\tdebugBarStow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t}\n\n\tfunction _updateWatchBody() {\n\t\tif (_watchList.length === 0) {\n\t\t\t_$watchBody\n\t\t\t\t.empty()\n\t\t\t\t.append(`<div>${L10n.get('debugBarNoWatches')}</div>`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst delLabel = L10n.get('debugBarDeleteWatch');\n\t\tconst $table   = jQuery(document.createElement('table'));\n\t\tconst $tbody   = jQuery(document.createElement('tbody'));\n\n\t\tfor (let i = 0, len = _watchList.length; i < len; ++i) {\n\t\t\tconst varName = _watchList[i];\n\t\t\tconst varKey  = varName.slice(1);\n\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\tconst $row    = jQuery(document.createElement('tr'));\n\t\t\tconst $delBtn = jQuery(document.createElement('button'));\n\t\t\tconst $code   = jQuery(document.createElement('code'));\n\n\t\t\t$delBtn\n\t\t\t\t.addClass('watch-delete')\n\t\t\t\t.attr('data-name', varName)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tone   : true,\n\t\t\t\t\tlabel : delLabel\n\t\t\t\t}, () => debugBarWatchDelete(varName));\n\t\t\t$code\n\t\t\t\t.text(_toWatchString(store[varKey]));\n\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($delBtn)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.text(varName)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($code)\n\t\t\t\t.appendTo($row);\n\t\t\t$row\n\t\t\t\t.appendTo($tbody);\n\t\t}\n\n\t\t$table\n\t\t\t.append($tbody);\n\t\t_$watchBody\n\t\t\t.empty()\n\t\t\t.append($table);\n\t}\n\n\tfunction _updateWatchList() {\n\t\tconst svn = Object.keys(State.variables);\n\t\tconst tvn = Object.keys(State.temporary);\n\n\t\tif (svn.length === 0 && tvn.length === 0) {\n\t\t\t_$watchList.empty();\n\t\t\treturn;\n\t\t}\n\n\t\tconst names   = [...svn.map(name => `$${name}`), ...tvn.map(name => `_${name}`)].sort();\n\t\tconst options = document.createDocumentFragment();\n\n\t\tnames.delete(_watchList);\n\n\t\tfor (let i = 0, len = names.length; i < len; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(names[i])\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$watchList\n\t\t\t.empty()\n\t\t\t.append(options);\n\t}\n\n\tfunction _updateTurnSelect() {\n\t\tconst histLen = State.size;\n\t\tconst expLen  = State.expired.length;\n\t\tconst options = document.createDocumentFragment();\n\n\t\tfor (let i = 0; i < histLen; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(i)\n\t\t\t\t.text(`${expLen + i + 1}. ${Util.escape(State.history[i].title)}`)\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$turnSelect\n\t\t\t.empty()\n\t\t\t.ariaDisabled(histLen < 2)\n\t\t\t.append(options)\n\t\t\t.val(State.activeIndex);\n\t}\n\n\tfunction _toWatchString(value) {\n\t\t/*\n\t\t\tHandle the `null` primitive.\n\t\t*/\n\t\tif (value === null) {\n\t\t\treturn 'null';\n\t\t}\n\n\t\t/*\n\t\t\tHandle the rest of the primitives and functions.\n\t\t*/\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn 'NaN';\n\t\t\t}\n\t\t\telse if (!Number.isFinite(value)) {\n\t\t\t\treturn 'Infinity';\n\t\t\t}\n\t\t\t/* falls through */\n\t\tcase 'boolean':\n\t\tcase 'symbol':\n\t\tcase 'undefined':\n\t\t\treturn String(value);\n\n\t\tcase 'string':\n\t\t\treturn JSON.stringify(value);\n\n\t\tcase 'function':\n\t\t\treturn 'Function';\n\t\t}\n\n\t\tconst objType = Util.toStringTag(value);\n\n\t\t// /*\n\t\t// \tHandle instances of the primitive exemplar objects (`Boolean`, `Number`, `String`).\n\t\t// */\n\t\t// if (objType === 'Boolean') {\n\t\t// \treturn `Boolean\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'Number') {\n\t\t// \treturn `Number\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'String') {\n\t\t// \treturn `String\\u202F{\"${String(value)}\"}`;\n\t\t// }\n\n\t\t/*\n\t\t\tHandle `Date` objects.\n\t\t*/\n\t\tif (objType === 'Date') {\n\t\t\t// return `Date\\u202F${value.toISOString()}`;\n\t\t\treturn `Date\\u202F{${value.toLocaleString()}}`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `RegExp` objects.\n\t\t*/\n\t\tif (objType === 'RegExp') {\n\t\t\treturn `RegExp\\u202F${value.toString()}`;\n\t\t}\n\n\t\tconst result = [];\n\n\t\t/*\n\t\t\tHandle `Array` & `Set` objects.\n\t\t*/\n\t\tif (value instanceof Array || value instanceof Set) {\n\t\t\tconst list = value instanceof Array ? value : Array.from(value);\n\n\t\t\t// own numeric properties\n\t\t\t// NOTE: Do not use `<Array>.forEach()` here as it skips undefined members.\n\t\t\tfor (let i = 0, len = list.length; i < len; ++i) {\n\t\t\t\tresult.push(list.hasOwnProperty(i) ? _toWatchString(list[i]) : '<empty>');\n\t\t\t}\n\n\t\t\t// own enumerable non-numeric expando properties\n\t\t\tObject.keys(list)\n\t\t\t\t.filter(key => !_numericKeyRe.test(key))\n\t\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(list[key])}`));\n\n\t\t\treturn `${objType}(${list.length})\\u202F[${result.join(', ')}]`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `Map` objects.\n\t\t*/\n\t\tif (value instanceof Map) {\n\t\t\tvalue.forEach((val, key) => result.push(`${_toWatchString(key)} \\u2192 ${_toWatchString(val)}`));\n\n\t\t\treturn `${objType}(${value.size})\\u202F{${result.join(', ')}}`;\n\t\t}\n\n\t\t/*\n\t\t\tGeneral object handling.\n\t\t*/\n\t\t// own enumerable properties\n\t\tObject.keys(value)\n\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(value[key])}`));\n\n\t\treturn `${objType}\\u202F{${result.join(', ')}}`;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tDebug Bar Functions.\n\t\t*/\n\t\tinit     : { value : debugBarInit },\n\t\tisStowed : { value : debugBarIsStowed },\n\t\tstart    : { value : debugBarStart },\n\t\tstow     : { value : debugBarStow },\n\t\ttoggle   : { value : debugBarToggle },\n\t\tunstow   : { value : debugBarUnstow },\n\n\t\t/*\n\t\t\tWatch Functions.\n\t\t*/\n\t\twatch : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd       : { value : debugBarWatchAdd },\n\t\t\t\tall       : { value : debugBarWatchAddAll },\n\t\t\t\tclear     : { value : debugBarWatchClear },\n\t\t\t\tdelete    : { value : debugBarWatchDelete },\n\t\t\t\tdisable   : { value : debugBarWatchDisable },\n\t\t\t\tenable    : { value : debugBarWatchEnable },\n\t\t\t\tisEnabled : { value : debugBarWatchIsEnabled },\n\t\t\t\ttoggle    : { value : debugBarWatchToggle }\n\t\t\t}))\n\t\t}\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tloadscreen.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Engine */\n\nvar LoadScreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Locks collection.\n\tconst _locks = new Set();\n\n\t// Auto-incrementing lock ID.\n\tlet _autoId = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tLoadScreen Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize management of the loading screen.\n\t*/\n\tfunction loadScreenInit() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenInit()]'); }\n\n\t\t// Add a `readystatechange` listener for hiding/showing the loading screen.\n\t\tjQuery(document).on('readystatechange.SugarCube', () => {\n\t\t\tif (DEBUG) { console.log(`[LoadScreen/<readystatechange>] document.readyState: \"${document.readyState}\"; locks(${_locks.size}):`, _locks); }\n\n\t\t\tif (_locks.size > 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The value of `document.readyState` may be: 'loading' -> 'interactive' -> 'complete'.\n\t\t\t// Though, to reach this point, it must already be in, at least, the 'interactive' state.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\tif (jQuery(document.documentElement).attr('data-init') === 'loading') {\n\t\t\t\t\tif (Config.loadDelay > 0) {\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (_locks.size === 0) {\n\t\t\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, Math.max(Engine.minDomActionDelay, Config.loadDelay));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadScreenShow();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\tClear the loading screen.\n\t*/\n\tfunction loadScreenClear() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenClear()]'); }\n\n\t\t// Remove the event listener.\n\t\tjQuery(document).off('readystatechange.SugarCube');\n\n\t\t// Clear all locks.\n\t\t_locks.clear();\n\n\t\t// Hide the loading screen.\n\t\tloadScreenHide();\n\t}\n\n\t/*\n\t\tHide the loading screen.\n\t*/\n\tfunction loadScreenHide() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenHide()]'); }\n\n\t\tjQuery(document.documentElement).removeAttr('data-init');\n\t}\n\n\t/*\n\t\tShow the loading screen.\n\t*/\n\tfunction loadScreenShow() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenShow()]'); }\n\n\t\tjQuery(document.documentElement).attr('data-init', 'loading');\n\t}\n\n\t/*\n\t\tReturns a new lock ID after locking and showing the loading screen.\n\t*/\n\tfunction loadScreenLock() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenLock()]'); }\n\n\t\t++_autoId;\n\t\t_locks.add(_autoId);\n\n\t\tif (DEBUG) { console.log(`\\tacquired loading screen lock; id: ${_autoId}`); }\n\n\t\tloadScreenShow();\n\t\treturn _autoId;\n\t}\n\n\t/*\n\t\tRemove the lock associated with the given lock ID and, if no locks remain,\n\t\ttrigger a `readystatechange` event.\n\t*/\n\tfunction loadScreenUnlock(id) {\n\t\tif (DEBUG) { console.log(`[LoadScreen/loadScreenUnlock(id: ${id})]`); }\n\n\t\tif (id == null) { // lazy equality for null\n\t\t\tthrow new Error('LoadScreen.unlock called with a null or undefined ID');\n\t\t}\n\n\t\tif (_locks.has(id)) {\n\t\t\t_locks.delete(id);\n\n\t\t\tif (DEBUG) { console.log(`\\treleased loading screen lock; id: ${id}`); }\n\t\t}\n\n\t\tif (_locks.size === 0) {\n\t\t\tjQuery(document).trigger('readystatechange');\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : loadScreenInit },\n\t\tclear  : { value : loadScreenClear },\n\t\thide   : { value : loadScreenHide },\n\t\tshow   : { value : loadScreenShow },\n\t\tlock   : { value : loadScreenLock },\n\t\tunlock : { value : loadScreenUnlock }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsugarcube.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Browser, Config, Dialog, Engine, Fullscreen, Has, LoadScreen, SimpleStore, L10n, Macro, Passage,\n\t       Save, Scripting, Setting, SimpleAudio, State, Story, UI, UIBar, DebugBar, Util, Visibility, Wikifier\n*/\n/* eslint-disable no-var */\n\n/*\n\tVersion object.\n*/\nvar version = Object.freeze({\n\ttitle      : 'SugarCube',\n\tmajor      : 2,\n\tminor      : 33,\n\tpatch      : 2,\n\tprerelease : null,\n\tbuild      : 56,\n\tdate       : new Date(\"2020-08-29T12:37:07.022Z\"),\n\t/* legacy */\n\textensions : {},\n\t/* /legacy */\n\n\ttoString() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.major}.${this.minor}.${this.patch}${prerelease}+${this.build}`;\n\t},\n\n\tshort() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.title} (v${this.major}.${this.minor}.${this.patch}${prerelease})`;\n\t},\n\n\tlong() {\n\t\t'use strict';\n\n\t\treturn `${this.title} v${this.toString()} (${this.date.toUTCString()})`;\n\t}\n});\n\n/* eslint-disable no-unused-vars */\n/*\n\tInternal variables.\n*/\n// Temporary state object.\nvar TempState = {};\n\n// Legacy macros object.\nvar macros = {};\n\n// Post-display task callbacks object.\nvar postdisplay = {};\n\n// Post-render task callbacks object.\nvar postrender = {};\n\n// Pre-display task callbacks object.\nvar predisplay = {};\n\n// Pre-history task callbacks object.\nvar prehistory = {};\n\n// Pre-render task callbacks object.\nvar prerender = {};\n\n// Session storage manager object.\nvar session = null;\n\n// Settings object.\nvar settings = {};\n\n// Setup object.\nvar setup = {};\n\n// Persistant storage manager object.\nvar storage = null;\n\n/*\n\tLegacy aliases.\n*/\nvar browser       = Browser;\nvar config        = Config;\nvar has           = Has;\nvar History       = State;\nvar state         = State;\nvar tale          = Story;\nvar TempVariables = State.temporary;\n/* eslint-enable no-unused-vars */\n\n/*\n\tGlobal `SugarCube` object.  Allows scripts to detect if they're running in SugarCube by\n\ttesting for the object (e.g. `\"SugarCube\" in window`) and contains exported identifiers\n\tfor debugging purposes.\n*/\nwindow.SugarCube = {};\n\n/*\n\tMain function, entry point for the story.\n*/\njQuery(() => {\n\t'use strict';\n\n\tif (DEBUG) { console.log('[SugarCube/main()] Document loaded; beginning startup.'); }\n\n\t/*\n\t\tWARNING!\n\n\t\tThe ordering of the code within this function is critically important,\n\t\tso be careful when mucking around with it.\n\t*/\n\ttry {\n\t\t// Acquire an initial lock for and initialize the loading screen.\n\t\tconst lockId = LoadScreen.lock();\n\t\tLoadScreen.init();\n\n\t\t// Normalize the document.\n\t\tif (document.normalize) {\n\t\t\tdocument.normalize();\n\t\t}\n\n\t\t// Load the story data (must be done before most anything else).\n\t\tStory.load();\n\n\t\t// Instantiate the storage and session objects.\n\t\t// NOTE: `SimpleStore.create(storageId, persistent)`\n\t\tstorage = SimpleStore.create(Story.domId, true);\n\t\tsession = SimpleStore.create(Story.domId, false);\n\n\t\t// Initialize the user interface (must be done before story initialization, specifically before scripts).\n\t\tDialog.init();\n\t\tUIBar.init();\n\t\tEngine.init();\n\n\t\t// Initialize the story (largely load the user styles, scripts, and widgets).\n\t\tStory.init();\n\n\t\t// Initialize the localization (must be done after story initialization).\n\t\tL10n.init();\n\n\t\t// Alert when the browser is degrading required capabilities (must be done after localization initialization).\n\t\tif (!session.has('rcWarn') && storage.name === 'cookie') {\n\t\t\t/* eslint-disable no-alert */\n\t\t\tsession.set('rcWarn', 1);\n\t\t\twindow.alert(L10n.get('warningNoWebStorage'));\n\t\t\t/* eslint-enable no-alert */\n\t\t}\n\n\t\t// Initialize the saves (must be done after story initialization, but before engine start).\n\t\tSave.init();\n\n\t\t// Initialize the settings.\n\t\tSetting.init();\n\n\t\t// Initialize the macros.\n\t\tMacro.init();\n\n\t\t// Start the engine (should be done as late as possible, but before interface startup).\n\t\tEngine.start();\n\n\t\t// Initialize the debug bar interface (should be done as late as possible, but before interface startup).\n\t\tif (Config.debug) {\n\t\t\tDebugBar.init();\n\t\t}\n\n\t\t// Set a recurring timer to start the interfaces (necessary due to DOM readiness issues in some browsers).\n\t\tconst $window    = $(window);\n\t\tconst vprCheckId = setInterval(() => {\n\t\t\t// If `$window.width()` returns a zero value, bail out and wait.\n\t\t\tif (!$window.width()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear the recurring timer.\n\t\t\tclearInterval(vprCheckId);\n\n\t\t\t// Start the UI bar interface.\n\t\t\tUIBar.start();\n\n\t\t\t// Start the debug bar interface.\n\t\t\tif (Config.debug) {\n\t\t\t\tDebugBar.start();\n\t\t\t}\n\n\t\t\t// Trigger the `:storyready` global synthetic event.\n\t\t\tjQuery.event.trigger({ type : ':storyready' });\n\n\t\t\t// Release the loading screen lock after a short delay.\n\t\t\tsetTimeout(() => LoadScreen.unlock(lockId), Engine.minDomActionDelay * 2);\n\t\t}, Engine.minDomActionDelay);\n\n\t\t// Finally, export identifiers for debugging purposes.\n\t\tObject.defineProperty(window, 'SugarCube', {\n\t\t\t// WARNING: We need to assign new values at points, so seal it, do not freeze it.\n\t\t\tvalue : Object.seal(Object.assign(Object.create(null), {\n\t\t\t\tBrowser,\n\t\t\t\tConfig,\n\t\t\t\tDialog,\n\t\t\t\tEngine,\n\t\t\t\tFullscreen,\n\t\t\t\tHas,\n\t\t\t\tL10n,\n\t\t\t\tMacro,\n\t\t\t\tPassage,\n\t\t\t\tSave,\n\t\t\t\tScripting,\n\t\t\t\tSetting,\n\t\t\t\tSimpleAudio,\n\t\t\t\tState,\n\t\t\t\tStory,\n\t\t\t\tUI,\n\t\t\t\tUIBar,\n\t\t\t\tDebugBar,\n\t\t\t\tUtil,\n\t\t\t\tVisibility,\n\t\t\t\tWikifier,\n\t\t\t\tsession,\n\t\t\t\tsettings,\n\t\t\t\tsetup,\n\t\t\t\tstorage,\n\t\t\t\tversion\n\t\t\t}))\n\t\t});\n\n\t\tif (DEBUG) { console.log('[SugarCube/main()] Startup complete; story ready.'); }\n\t}\n\tcatch (ex) {\n\t\tconsole.error(ex);\n\t\tLoadScreen.clear();\n\t\treturn Alert.fatal(null, ex.message, ex);\n\t}\n});\n\n})(window, window.document, jQuery);\n}\n\t</script>\n</body>\n</html>\n"});
\ No newline at end of file
diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index c5fb85c24387576cbec1e634b52cb1b368f93dec..e3432c04859968490ce39296b3944508fa482708 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -596,6 +596,7 @@ App.Data.resetOnNGPlus = {
 	dairySlimMaintain: 0,
 	dairyHyperPregRemodel: 0,
 	dairyWeightSetting: 0,
+	dairyHormonesSetting: 0,
 	dairyImplantsSetting: 1,
 	dairyUpgradeMenials: 0,
 	createBioreactors: 0,
diff --git a/js/utils.js b/js/utils.js
index 112b768fdcabcad130b4d56b8a24d0e7d4209881..dfa2aea268b98a485ee04d106b990a5478691051 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -298,7 +298,7 @@ function mapIdList(list) {
  * Added keys parameter since it better suits our needs and updated to project code style.
  *
  * @param {[]} keys
- * @param {[[]]} edges list of edges. each edge forms Array<ID,ID> e.g. [12 , 3]
+ * @param {[[]]} edges list of edges. each edge forms Array<ID,ID> e.g. [12, 3]
  * @returns Array: topological sorted list of IDs
  * @throws Error: in case there is a closed chain.
  **/
diff --git a/src/001-lib/04-ScopeMacro/ScopeMacro.js b/src/001-lib/04-ScopeMacro/ScopeMacro.js
deleted file mode 100644
index e350fbdcde4389f09dda44035ff3eecca43d3978..0000000000000000000000000000000000000000
--- a/src/001-lib/04-ScopeMacro/ScopeMacro.js
+++ /dev/null
@@ -1,57 +0,0 @@
-Macro.add('scope', {
-	skipArgs : true,
-	tags     : null,
-	handler() {
-		const oldTemporary = State.temporary;
-		const restoreOldTemporary = function() {
-			let keys = Object.keys(oldTemporary);
-			for(let i=0; i<keys.length; ++i) {
-				let key = keys[i];
-				let obj = oldTemporary[key];
-				State.temporary[key] = obj;
-			}
-		};
-		let keepOverwrites = false;
-		try {
-			State.clearTemporary();
-			if(/^\s*as\s+child\s*$/.test(this.args.raw)) {
-				restoreOldTemporary();
-				keepOverwrites = true;
-			} else {
-				const varRe = new RegExp(`(${Patterns.variable})`, 'g');
-				let match;
-				/*
-					Cache the existing values of the variables and add a shadow.
-				*/
-				while ((match = varRe.exec(this.args.raw)) !== null) {
-					const varName = match[1];
-					const varKey = varName.slice(1);
-					if(varName[0] === '$') {
-						return this.error("Global variable '" + varName + "'cannot be scoped.");
-					}
-
-					if (!oldTemporary.hasOwnProperty(varKey)) {
-						continue;
-					}
-
-					State.temporary[varKey] = oldTemporary[varKey];
-				}
-			}
-			new Wikifier(this.output, this.payload[0].contents.trim());
-		} finally {
-			if(!keepOverwrites) {
-				State.clearTemporary();
-				restoreOldTemporary();
-			} else {
-				const overwrites = State.temporary;
-				State.clearTemporary();
-				let keys = Object.keys(oldTemporary);
-				for(let i=0; i<keys.length; ++i) {
-					let key = keys[i];
-					let obj = overwrites[key];
-					State.temporary[key] = obj;
-				}
-			}
-		}
-	}
-});
diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js
index 9f9136992a93b16297a903663f1049fa3a8a930c..06195761b0b034389a9722ead3c5560a5b2716bf 100644
--- a/src/002-config/fc-version.js
+++ b/src/002-config/fc-version.js
@@ -1,6 +1,6 @@
 App.Version = {
 	base: "0.10.7.1", // The vanilla version the mod is based off of, this should never be changed.
-	pmod: "3.7.0",
+	pmod: "3.7.1",
 	commitHash: null,
-	release: 1090
+	release: 1091
 };
diff --git a/src/Corporation/corporate-divisionAcquiring.js b/src/Corporation/corporate-divisionAcquiring.js
index 145c9a3bc16bcf6f611a03f27999bc8126e83fe1..3b38d926bf039933479e2d358448e6b0c22dc706 100644
--- a/src/Corporation/corporate-divisionAcquiring.js
+++ b/src/Corporation/corporate-divisionAcquiring.js
@@ -34,11 +34,11 @@ App.Corporate.Init_DivisionAcquiring = function(shared) {
 		message_endWeek_Slaves(divLedger) {
 			let newSlaves = divLedger.slaves.value;
 			if(newSlaves > 0) {
-				return `<span class="green">acquired ${numberWithPlural(newSlaves, "slave")}</span>`
-					 + (this.hadMercenaryHelp ? " with the help of your mercenaries" : "");
+				return `<span class="green">acquired ${numberWithPlural(newSlaves, "slave")}`
+					 + (this.hadMercenaryHelp ? "</span> with the help of your mercenaries." : ".</span>");
 			} else {
-				return `<span class="red">failed to acquire any slaves</span>`
-					 + (this.hadMercenaryHelp ? " even with the help of your mercenaries" : "");
+				return `<span class="red">failed to acquire any slaves`
+					 + (this.hadMercenaryHelp ? "</span> even with the help of your mercenaries." : ".</span>");
 			}
 		}
 		endWeek_Slaves(divLedger) {
diff --git a/src/Corporation/corporate-divisionProcessing.js b/src/Corporation/corporate-divisionProcessing.js
index 719dac292fc015dec1dc4064a25490a2060a7f31..d1425fdd164faaf29d54cddc8f8099bd0a78bb08 100644
--- a/src/Corporation/corporate-divisionProcessing.js
+++ b/src/Corporation/corporate-divisionProcessing.js
@@ -43,9 +43,9 @@ App.Corporate.Init_DivisionProcessing = function(shared) {
 			}
 			retval += " The division ";
 			if(this.activeSlaves) {
-				retval += `is still ${this._const.slaveProcessDescription.present} ${numberWithPlural(this.activeSlaves, "slave")}`;
+				retval += `is still ${this._const.slaveProcessDescription.present} ${numberWithPlural(this.activeSlaves, "slave")}.`;
 			} else {
-				retval += `doesn't have any slaves to ${this._const.slaveProcessDescription.future}`;
+				retval += `doesn't have any slaves to ${this._const.slaveProcessDescription.future}.`;
 			}
 			return retval;
 		}
@@ -56,9 +56,8 @@ App.Corporate.Init_DivisionProcessing = function(shared) {
 			return divLedger.slaves.apply(slaves);
 		}
 		get initialSlaveValue() {
-			const values = this.relatedDivisions.from
-												.map(fromDiv => fromDiv.initialSlaveValue);
-			if(values.length == 0) { throw "No route to acquisition found."; }
+			const values = this.relatedDivisions.from.map(fromDiv => fromDiv.initialSlaveValue);
+			if(values.length === 0) { throw "No route to acquisition found."; }
 			return Math.min(...values) + this._const.addedValue;
 		}
 		// virtual override
diff --git a/src/Corporation/corporate-divisionWorking.js b/src/Corporation/corporate-divisionWorking.js
index 2cfbd382f0ce6d1d2a97c3a1561aa0ecbae9ceed..8f4a6dbf1b13f769abca14e09e28f7c913b111f9 100644
--- a/src/Corporation/corporate-divisionWorking.js
+++ b/src/Corporation/corporate-divisionWorking.js
@@ -38,12 +38,12 @@ App.Corporate.Init_DivisionWorking = function(shared) {
 			let lostSlaves = divLedger.slaves.value;
 			let retval = '';// The division
 			if(this.activeSlaves <= 0) {
-				retval += `has <span class="red">no slaves</span> to ${this._const.slaveWorkDescription.future}`;
+				retval += `has <span class="red">no slaves</span> to ${this._const.slaveWorkDescription.future}.`;
 			} else {
 				retval += `is ${this._const.slaveWorkDescription.present} <span class="green">${numberWithPlural(this.activeSlaves, 'slave')}.</span> `;
 			}
 			if(lostSlaves > 0) {
-				retval += `During operations <span class="red">${numberWithPlural(lostSlaves, 'slave')}</span> ${this._const.slaveWorkDescription.past}`;
+				retval += `During operations <span class="red">${numberWithPlural(lostSlaves, 'slave')}</span> ${this._const.slaveWorkDescription.past}.`;
 			}
 			return retval;
 		}
diff --git a/src/Corporation/corporate.js b/src/Corporation/corporate.js
index 0f0d0dbbc0c8029a32f73a271dc1ff1d4de6cdd0..204edfd1f1c9d4375ac465250d008aa7edfe6344 100644
--- a/src/Corporation/corporate.js
+++ b/src/Corporation/corporate.js
@@ -60,31 +60,30 @@ App.Corporate.Init = function() {
 		constructor(corp, suffix="") {
 			this.corp = corp;
 			this.suffix = suffix;
-			this.clear();
 		}
-		get operations(	 )	 { return this.getStored('OpCost'			  ); }
-		set operations(value)	 { return this.setStored('OpCost', value); }
+		get operations(	 )	 { return this.getStored('OpCost'); }
+		set operations(value)	 { this.setStored('OpCost', value); }
 
-		get localRevenue(	 )   { return this.getStored('Rev'				 ); }
-		set localRevenue(value)   { return this.setStored('Rev', value); }
+		get localRevenue(	 )   { return this.getStored('Rev'); }
+		set localRevenue(value)   { this.setStored('Rev', value); }
 
-		get development(	 )	{ return this.getStored('AssetsDev'		   ); }
-		set development(value)	{ return this.setStored('AssetsDev', value); }
+		get development(	 )	{ return this.getStored('AssetsDev'); }
+		set development(value)	{ this.setStored('AssetsDev', value); }
 
-		get slaves(	 )		 { return this.getStored('AssetsSlave'		 ); }
-		set slaves(value)		 { return this.setStored('AssetsSlave', value); }
+		get slaves(	 )		 { return this.getStored('AssetsSlave'); }
+		set slaves(value)		 { this.setStored('AssetsSlave', value); }
 
-		get overhead(	 )	   { return this.getStored('Overhead'			); }
-		set overhead(value)	   { return this.setStored('Overhead', value); }
+		get overhead(	 )	   { return this.getStored('Overhead'); }
+		set overhead(value)	   { this.setStored('Overhead', value); }
 
-		get economicBoost(	 )  { return this.getStored('EconBonus'		   ); }
-		set economicBoost(value)  { return this.setStored('EconBonus', value); }
+		get economicBoost(	 )  { return this.getStored('EconBonus'); }
+		set economicBoost(value)  { this.setStored('EconBonus', value); }
 
-		get economy(	 )		{ return this.getStored('Econ'		   ); }
-		set economy(value)		{ return this.setStored('Econ', value); }
+		get economy(	 )		{ return this.getStored('Econ'); }
+		set economy(value)		{ this.setStored('Econ', value); }
 
-		get foreignRevenue(	 ) { return this.getStored('NeighborBonus'	   ); }
-		set foreignRevenue(value) { return this.setStored('NeighborBonus', value); }
+		get foreignRevenue(	 ) { return this.getStored('NeighborBonus'); }
+		set foreignRevenue(value) { this.setStored('NeighborBonus', value); }
 
 		copy(ledger) {
 			this.operations	 = ledger.operations;
@@ -422,7 +421,7 @@ App.Corporate.Init = function() {
 		},
 		FoundingSetupAutoBuy: function(division) {
 			let foundedFrom = division.relatedDivisions.from.filter(div=>div.founded);
-			if(foundedFrom.length == 0) {
+			if(foundedFrom.length === 0) {
 				division.setAutoBuyFromMarket(true);
 			} else {
 				for(let otherDiv of foundedFrom) {
@@ -434,7 +433,7 @@ App.Corporate.Init = function() {
 		},
 		FoundingSetupAutoSell: function(division) {
 			let foundedTo = division.relatedDivisions.to.filter(div=>div.founded);
-			if(foundedTo.length == 0) {
+			if(foundedTo.length === 0) {
 				division.setAutoSendToMarket(true);
 			} else {
 				for(let otherDiv of foundedTo) {
@@ -451,7 +450,6 @@ App.Corporate.Init = function() {
 	App.Corporate.Init_DivisionWorking(shared);
 
 	App.Corporate.InitConstants();
-	if (!V.corp) { V.corp = {}; }
 	let divisions = App.Corporate.divisions = mapIdList(App.Corporate.divisionList);
 	App.Corporate.maintenance.divisionCategories = mapIdList(App.Corporate.maintenance.divisionCategoriesList);
 
@@ -484,11 +482,11 @@ App.Corporate.Init = function() {
 	for(const property in propertyToStoryInt) {
 		const key = propertyToStoryInt[property];
 		Object.defineProperty(App.Corporate, property, {
-		get: function(	 ) { return this.getStored(key); },
-		set: function(value) {
-			if(!Number.isFinite(value)) { throw "Unreal number " + key; }
-			this.setStored(key, Math.trunc(value));
-		}
+			get: function(	 ) { return this.getStored(key); },
+			set: function(value) {
+				if(!Number.isFinite(value)) { throw "Unreal number " + key; }
+				this.setStored(key, Math.trunc(value));
+			}
 		});
 	}
 
@@ -503,7 +501,7 @@ App.Corporate.Init = function() {
 		const key = propertyToStoryBool[property];
 		Object.defineProperty(App.Corporate, property, {
 			get: function(	 ) { return this.getStored(key) === 1; },
-			set: function(value) { this.setStored(key, value == true ? 1 : 0); }
+			set: function(value) { this.setStored(key, value ? 1 : 0); }
 		});
 	}
 
@@ -647,7 +645,7 @@ App.Corporate.Init = function() {
 		if(!Number.isFinite(cost)) { throw "The cost provided was not a real number"; }
 		cost = Math.trunc(cost);
 		if(!(type in this.ledger.current)) { throw `Ledger doesn't record '${type}' category.`; }
-		if(cost == 0) { return; }
+		if(cost === 0) { return; }
 
 		this.ledger.current[type] += cost;
 		this.cash -= cost;
@@ -679,7 +677,7 @@ App.Corporate.Init = function() {
 	App.Corporate.ownsIntermediaryDivision = function(fromDivision, toDivision) {
 		for(let intermediateDiv of toDivision.relatedDivisions
 											 .from
-											 .filter(iDep => iDep.id != fromDivision.id
+											 .filter(iDep => iDep.id !== fromDivision.id
 														  && fromDivision.relatedDivisions.to.includes(iDep))) {
 			if(intermediateDiv.founded) { return true; }
 		}
@@ -901,13 +899,6 @@ App.Corporate.Init = function() {
 			c.economicBoost = 0;
 		}
 	};
-
-	if(V.corp.DivTrainSurgeryTimer !== undefined) {
-		let timer = V.corp.DivTrainSurgeryTimer;
-		// Note: originally the timer was capped at 20, so the founding week isn't guaranteed to be correct.
-		V.corp.DivSurgeryFounded = V.week - timer;
-		delete V.corp.DivTrainSurgeryTimer;
-	}
 };
 
 
diff --git a/src/Corporation/corporateLedger.js b/src/Corporation/corporateLedger.js
new file mode 100644
index 0000000000000000000000000000000000000000..a01a12557afbb72e42f5568641040a4bd814c6c4
--- /dev/null
+++ b/src/Corporation/corporateLedger.js
@@ -0,0 +1,102 @@
+/** Create the DOM ledger table
+ * @param {object} ledger - One of the members of App.Corporate.ledger, such as .current or .old
+ * @param {number} week - Week number (usually V.week, for the current ledger, or V.week-1, for the old one)
+ * @returns {HTMLTableElement}
+ */
+App.Corporate.writeLedger = function(ledger, week) {
+	/** Add a row to the ledger
+	 * @param {HTMLElement} table
+	 * @param {string} cellType
+	 * @param {string} caption
+	 * @param {number} [value]
+	 * @param {string|Node} [note]
+	 */
+	function createRow(table, cellType, caption, value, note) {
+		const row = App.UI.DOM.appendNewElement("tr", table);
+		/** @type {HTMLTableCellElement} */
+		const leftCell = App.UI.DOM.appendNewElement(cellType, row, caption);
+		if (jsDef(value)) {
+			const rightCell = App.UI.DOM.appendNewElement(cellType, row, App.UI.DOM.makeElement("div", formatCash(value)));
+			if (note) {
+				App.UI.DOM.appendNewElement("div", rightCell, note, "minor-note");
+			}
+		} else {
+			leftCell.colSpan = 2;
+		}
+	}
+
+	/** Format cash with color, DOM style (probably should be a shared function)
+	 * @param {number} cash
+	 * @returns {HTMLSpanElement}
+	 */
+	function formatCash(cash) {
+		let span = App.UI.DOM.makeElement('span', cashFormat(cash));
+		if (cash === 0) {
+			span.classList.add("gray");
+		} else {
+			span.classList.add("cash");
+			if (cash < 0) {
+				span.classList.add("dec");
+			} else if (cash > 0) {
+				span.classList.add("inc");
+			}
+		}
+		return span;
+	}
+
+	/** Build the cheat textbox for liquidity
+	 * @param {number} cash - initial value
+	 * @returns {HTMLInputElement}
+	 */
+	function makeCheatTextbox(cash) {
+		return App.UI.DOM.makeTextBox(cash, (v) => {
+			App.Corporate.cheatCash(v);
+			updateLedgerTable();
+		}, true);
+	}
+
+	function buildLedgerTable() {
+		const table = document.createElement('table');
+		table.className = "corporate";
+		table.id = "corporate-ledger";
+
+		const thead = App.UI.DOM.appendNewElement('thead', table);
+		createRow(thead, "th", `Ledger for ${asDateString(week)} - ${asDateString(week + 1)}`);
+		const tbody = App.UI.DOM.appendNewElement('tbody', table);
+		createRow(tbody, "td", "Revenue", ledger.revenue);
+		if (V.cheatMode && ledger.foreignRevenue > 0) {
+			createRow(tbody, "td", "Including Neighbor Bonus", ledger.foreignRevenue);
+		}
+		createRow(tbody, "td", "Operating Expenses", forceNeg(ledger.operations));
+		createRow(tbody, "td", "Slave Expenses", forceNeg(ledger.slaves));
+		createRow(tbody, "td", "Asset Expenses", forceNeg(ledger.development));
+		if (V.cheatMode) {
+			createRow(tbody, "td", `Economic ${ledger.economicBoost < 0 ? "Expenses" : "Windfall"}`, ledger.economicBoost);
+		}
+		createRow(tbody, "td", "Overhead", forceNeg(ledger.overhead));
+		let econNote = '';
+		if (ledger.economy > 100) {
+			econNote = "* Profits benefited from a strong economy.";
+		} else if (ledger.economy > 60) {
+			econNote = "* Profits were lowered by the weak economy.";
+		} else {
+			econNote = "* Profits were severely depressed by the failing economy.";
+		}
+		createRow(tbody, "td", "Profit", ledger.profit, econNote);
+
+		createRow(tbody, "th", "Totals");
+		createRow(tbody, "td", "Liquidity", App.Corporate.cash, V.cheatMode ? makeCheatTextbox(App.Corporate.cash) : undefined);
+		createRow(tbody, "td", "Corporate Value", App.Corporate.value);
+		createRow(tbody, "td", "Dividend for Payout", App.Corporate.dividend, `Pays out on ${asDateString(V.week + App.Corporate.dividendTimer, -1)},
+			${App.Corporate.dividendTimer === 1	? `the end of this week` : `in ${App.Corporate.dividendTimer} weeks`}
+		`);
+
+		return table;
+	}
+
+	function updateLedgerTable() {
+		$('#corporate-ledger').replaceWith(buildLedgerTable());
+	}
+
+	return buildLedgerTable();
+};
diff --git a/src/Corporation/corporateWidgets.tw b/src/Corporation/corporateWidgets.tw
deleted file mode 100644
index 14e57d4a7545356cce98f6d8efc0e23e4b3eb1ea..0000000000000000000000000000000000000000
--- a/src/Corporation/corporateWidgets.tw
+++ /dev/null
@@ -1,87 +0,0 @@
-:: corporate widgets [widget nobr]
-
-/*
-Usage:
-<<CorporateLedger _ledger $week>>
-
-	_ledger: One of the members of App.Corporate.ledger, such as .current or .old
-	$week: The current week or the previous week, depending on whether you used current or old.
-*/
-<<widget "CorporateLedger">>
-<<scope>>
-<<set _ledger = $args[0]>>
-<<set _week = $args[1]>>
-<table class="corporate">
-<thead>
-<tr><th colspan="2">Ledger for <<= asDateString(_week) >> - <<= asDateString(_week + 1, -1) >></th></tr>
-</thead>
-<tbody>
-/*Returns last week's revenue, gets calculated in corporationDevelopments, but slaves sold here also added to it for next week*/
-<tr><td>Revenue</td><td><<= cashFormatColor(_ledger.revenue)>></td></tr>
-<<if ($cheatMode) && ($cheatModeM) && App.Corporate.foreignRevenue > 0>>
-	<tr><td>Including Neighbor Bonus</td><td><<= cashFormatColor(_ledger.foreignRevenue)>></td></tr>
-<</if>>
-/*Just like revenue, except for operating expenses (all calculated in corporationDevelopments)*/
-<tr><td>Operating Expenses</td><td><<= cashFormatColor(_ledger.operations, true)>></td></tr>
-/*buying slaves to work on adds to this expense, works just like revenue*/
-<tr><td>Slave Expenses</td><td><<= cashFormatColor(_ledger.slaves, true)>></td></tr>
-/*costs associated with expanding divisions end up here, reports costs from last week, not current*/
-<tr><td>Asset Expenses</td><td><<= cashFormatColor(_ledger.development, true)>></td></tr>
-<<if ($cheatMode) && ($cheatModeM)>>
-	<tr>
-		<td>Economic <<if _ledger.economicBoost < 0>>Expenses<<else>>Windfall<</if>></td>
-		<td><<= cashFormatColor(_ledger.economicBoost)>></td>
-	</tr>
-<</if>>
-<tr><td>Overhead</td><td><<= cashFormatColor(_ledger.overhead, true)>></td></tr>
-<tr><td>Profit</td><td>
-<div><<= cashFormatColor(_ledger.profit)>></div>
-<<if _ledger.economicBoost > 0>>
-<div class="minor-note">
-	<<if _ledger.economy > 100>>
-		* Profits benefited from a strong economy.
-	<<elseif _ledger.economy > 60>>
-		* Profits were lowered by the weak economy.
-	<<else>>
-		* Profits were severely depressed by the failing economy.
-	<</if>>
-</div>
-<</if>>
-</td></tr>
-</tbody>
-<thead>
-<tr><th colspan="2">Totals</th></tr>
-</thead>
-<tbody>
-<tr>
-	<td>Liquidity</td>
-	<td>
-	<<if ($cheatMode) && ($cheatModeM)>>
-		<span id="corp-cash"><<= cashFormatColor(App.Corporate.cash)>></span>
-		<<set _tCorpCash = App.Corporate.cash>>
-		<<textbox "_tCorpCash" _tCorpCash>>
-		<<link "Apply">>
-		<<set App.Corporate.cheatCash(_tCorpCash)>>
-		<<replace "#corp-cash">><<= cashFormatColor(App.Corporate.cash)>><</replace>>
-		<</link>>
-	<<else>>
-		<<= cashFormatColor(App.Corporate.cash)>>
-	<</if>>
-	</td>
-</tr>
-<tr><td>Corporate Value</td><td><<= cashFormatColor(App.Corporate.value)>></td></tr>
-<tr>
-<td>Dividend for Payout</td>
-<td>
-<div><<= cashFormatColor(App.Corporate.dividend)>></div>
-<div class="minor-note">Pays out on <<=asDateString($args[1] + App.Corporate.dividendTimer, -1)>>, <<if App.Corporate.dividendTimer == 1>>
-	the end of this week
-<<else>>
-	in <<= App.Corporate.dividendTimer >> weeks
-<</if>>
-</div>
-</td></tr>
-</tbody>
-</table>
-<</scope>>
-<</widget>>
diff --git a/src/Corporation/corporationDevelopments.tw b/src/Corporation/corporationDevelopments.tw
index 0403f009bc6769bb1194d467642e5bc9085caa02..a664bf48726b515a19e1c114be50c8b0f7e3d254 100644
--- a/src/Corporation/corporationDevelopments.tw
+++ b/src/Corporation/corporationDevelopments.tw
@@ -10,11 +10,10 @@
 <h2>Operational Results</h2>
 /*Divisions doing their thing*/
 <<set _weekLedger = App.Corporate.endWeek()>>
-/* TODO: I would like to move some of the following loop into a new loop. It should go Process -> Transfer -> Sell -> Buy (most expensive to least) to give the best chance of auto-buy having enough money to make purchases. */
 <<for _d range _weekLedger.divisionLedgers>>
 	<<set _div = _d.division>>
 	/* Reporting on number of slaves being processed or completed processing */
-	<br><<= _div.name>>: The division <<= _div.message_endWeek_Slaves(_d) >>.
+	<br><<= _div.name>>: The division <<= _div.message_endWeek_Slaves(_d) >>
 	<<if _d.market.originalBuy != null>>
 		<br>
 		<<if _d.market.buy == 0>>
@@ -53,8 +52,7 @@
 <</for>>
 
 /*Aggregate Corporation Results*/
-<<set _ledger = App.Corporate.ledger.current>>/*Note: You can't just pass the object directly into a widget. */
-<<CorporateLedger _ledger $week>>
+<<includeDOM App.Corporate.writeLedger(App.Corporate.ledger.current, $week)>>
 
 /*Division Expansion Tokens*/
 <<if _weekLedger.canExpandNow>>
diff --git a/src/Corporation/manageCorporation.tw b/src/Corporation/manageCorporation.tw
index 3ac259e971c36fcd0530ab2d7ca04967bb2da7d2..454175169ada074b595ffcf15c28e6da08488943 100644
--- a/src/Corporation/manageCorporation.tw
+++ b/src/Corporation/manageCorporation.tw
@@ -61,9 +61,7 @@
 <<if App.Corporate.foundedDate>>
 <div class="founding">Founded on <<= asDateString(App.Corporate.foundedDate)>></div>
 <</if>>
-<<set _ledger = App.Corporate.ledger.old>>/*You can't pass the object directly into the widget. */
-<<set _lastWeek = $week - 1>>
-<<CorporateLedger _ledger _lastWeek>>
+<<includeDOM App.Corporate.writeLedger(App.Corporate.ledger.old, $week-1)>>
 
 <h1>Division Management</h1>
 <<for _div range App.Corporate.divisionList.filter(x => x.founded)>>
diff --git a/src/Mods/SecExp/potentialToDo.txt b/src/Mods/SecExp/potentialToDo.txt
index 3025ed4fbc39e241ac45bf595177c62d5424a94d..9167e7358e8e81de5bfcd38cced8b0adebe09575 100644
--- a/src/Mods/SecExp/potentialToDo.txt
+++ b/src/Mods/SecExp/potentialToDo.txt
@@ -5,7 +5,7 @@ Hexall90's last merged commit: 52dde0b3
 - Enable oceanic battles
 	Fine, I'll Do It Myself
 	Pirates? Enemy navies? - 328279
-	Pirates could raid trade routs and such ,even could capture a high class cruise liner and you have to move retake it with an army similar to the ground battles. you could have an event where you gain a old world port to ship your goods to , and you have to defend it .		-328413
+	Pirates could raid trade routs and such ,even could capture a high class cruise liner and you have to move retake it with an army similar to the ground battles. you could have an event where you gain a old world port to ship your goods to, and you have to defend it .		-328413
 - How to make threads - 355452
 	A suggestion for prisoners of war after winning the battle: maybe add an option to execute them on the spot for authority gains?
 	And how about giving a chance (depending on the amount of captives) to actually make one or two of them that aren't as hateful or wounded as your sex slaves instead of just menials? Perhaps you may even turn (hypothetical) female captives into sex dolls or bioreactors too. It can be an optional button.
diff --git a/src/Mods/SecExp/secExpSmilingMan.tw b/src/Mods/SecExp/secExpSmilingMan.tw
index 4b85421b85c8663a775c4e89d69fa40dd5dadc14..a97eacf2e3158fa8a2d1336c5fce957361097097 100644
--- a/src/Mods/SecExp/secExpSmilingMan.tw
+++ b/src/Mods/SecExp/secExpSmilingMan.tw
@@ -429,7 +429,8 @@
 			<</if>>
 		</p>
 		<<includeDOM App.Desc.longSlave(_activeSlave)>>
-		<p> <<include "New Slave Intro">> </p>
+		<p> <<includeDOM App.UI.newSlaveIntro(_activeSlave)>> </p>
+		<<= newSlave(_activeSlave)>>
 	<</if>>
 	<<run delete $SecExp.smilingMan.relationship>>
 <</if>>
\ No newline at end of file
diff --git a/src/Mods/SpecialForce/TrickShotNight.tw b/src/Mods/SpecialForce/TrickShotNight.tw
index 39c1758f0a91f3a3e8b52fdb6feb30c0a711cfef..ad6d9ca743cc3b3007b6bffc9a4efdcf753b0a64 100644
--- a/src/Mods/SpecialForce/TrickShotNight.tw
+++ b/src/Mods/SpecialForce/TrickShotNight.tw
@@ -67,20 +67,17 @@ Despite your direct elevator, interaction with the majority of your security for
 			<<if random(1,100) < _shootChance>>
 				<br> <<link "Up the ante">>
 					<<replace "#bounty-result">>
-						<<set $activeSlaveOneTimeMinAge = 25>>
-						<<set $activeSlaveOneTimeMaxAge = 35>>
-						<<set $one_time_age_overrides_pedo_mode = 1>>
-						<<set $oneTimeDisableDisability = 1>>
 						<<if $arcologies[0].FSSupremacistLawME == 1>>
-							<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+							<<set _race = $arcologies[0].FSSupremacistRace>>
 						<<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 							<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-							<<set $fixedRace = _races.random()>>
+							<<set _race = _races.random()>>
 						<</if>>
+						<<set _genParam = {minAge: 25, maxAge: 35, ageOverridesPedoMode: 1, race: _race, disableDisability: 1}>>
 						<<if $seeDicks != 100>>
-							<<set $activeSlave = GenerateNewSlave("XX")>>
+							<<set $activeSlave = GenerateNewSlave("XX", _genParam)>>
 						<<else>>
-							<<set $activeSlave = GenerateNewSlave("XY")>>
+							<<set $activeSlave = GenerateNewSlave("XY", _genParam)>>
 						<</if>>
 						<<set $activeSlave.origin = "$He put $himself up as collateral at a trick shot game, and lost.">>
 						<<set $activeSlave.career = "a soldier">>
diff --git a/src/cheats/mod_EditSlaveCheat.tw b/src/cheats/mod_EditSlaveCheat.tw
index 0b39e745e74003940fcdae98070a18c0ff4e0a59..84c5ab79eb540bab18c2c7cd1bc2bfda60d88ee9 100644
--- a/src/cheats/mod_EditSlaveCheat.tw
+++ b/src/cheats/mod_EditSlaveCheat.tw
@@ -335,7 +335,7 @@ Custom sclera color: <<textbox "$tempSlave.sclerae" $tempSlave.sclerae>>
 ''$His eyes are (0: normal, 1: artificial): $tempSlave.eyesImplant''
 <br>Eyes: <<textbox "$tempSlave.eyesImplant" $tempSlave.eyesImplant>>
 <<radiobutton "$tempSlave.eyesImplant" 0>> Normal
-<<radiobutton "$tempSlave.eyesImplant" 0>> Artificial
+<<radiobutton "$tempSlave.eyesImplant" 1>> Artificial
 
 <br><br>
 
diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
index 015bbf3689f12cfe7aa5def560e7729c99d46c73..45bfc5980186153f629a7363608a17571a75adf6 100644
--- a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
+++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
@@ -59,6 +59,16 @@
 		<</if>>
 	<</if>>
 <</if>>
+<<if $tempSlave.eyes === 2>>
+	<<set $tempSlave.eye.left.vision = 2, $tempSlave.eye.right.vision = 2>>
+<<elseif $tempSlave.eyes === 1>>
+	<<set $tempSlave.eye.left.vision = 1, $tempSlave.eye.right.vision = 1>>
+<<elseif $tempSlave.eyes === -1>>
+	<<set $tempSlave.eye.left.vision = 0, $tempSlave.eye.right.vision = 0>>
+<</if>>
+<<if $tempSlave.eyesImplant === 1>>
+	<<set $tempSlave.eye.left.type = 3, $tempSlave.eye.right.type = 3>>
+<</if>>
 <<run SlaveDatatypeCleanup($tempSlave)>> /* will break cheated pregnancies if run before the above block of code */
 
 You perform the dark rituals, pray to the dark gods, and sell your soul for the power to change and mold slaves to your will.
diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js
index a4fa8df25aed2d720d45edab93104bfd8e5a106f..8c304a9c01e0adee05584c12b2fed5442e257b95 100644
--- a/src/data/backwardsCompatibility/datatypeCleanup.js
+++ b/src/data/backwardsCompatibility/datatypeCleanup.js
@@ -1631,6 +1631,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		V.dairyHyperPregRemodel = Math.clamp(+V.dairyHyperPregRemodel, 0, 1) || 0;
 		V.dairyImplantsSetting = Math.clamp(+V.dairyImplantsSetting, 0, 3) || 0;
 		V.dairyWeightSetting = Math.clamp(+V.dairyWeightSetting, -1, 4) || 0;
+		V.dairyHormonesSetting = Math.clamp(+V.dairyHormonesSetting, -1, 2) || 0;
 		/* bioreactors */
 		V.bioreactorsAnnounced = Math.clamp(+V.bioreactorsAnnounced, 0, 1) || 0;
 		V.createBioreactors = Math.clamp(+V.createBioreactors, 0, 1) || 0;
diff --git a/src/endWeek/saDevotion.js b/src/endWeek/saDevotion.js
index 3e2e42f4f9e82825ffa9b146b940e79f844e159f..d19c310bc4488f4b6366523ff61d585e996ad67f 100644
--- a/src/endWeek/saDevotion.js
+++ b/src/endWeek/saDevotion.js
@@ -947,12 +947,12 @@ App.SlaveAssignment.devotion = (function() {
 		if (slave.devotion > 100 && slave.trust > 100) {
 			r.push(`${His} perfect devotion and trust <span class="reputation inc">improve your reputation.</span>`);
 			repX(10 * (slave.devotion - 100), "slavesViewOfPC", slave);
-			slave.devotion = 100;
 			repX(10 * (slave.trust - 100), "slavesViewOfPC", slave);
-			slave.trust = 100;
 			if (slave.energy <= 50 && App.Utils.sexAllowed(slave, PC)) {
 				energyPlus += Math.trunc((slave.trust + slave.devotion) - 200);
 			}
+			slave.devotion = 100;
+			slave.trust = 100;
 		} else {
 			if (slave.devotion > 100) {
 				if (slave.trust < 100 && slave.trust > 20) {
diff --git a/src/endWeek/saServeThePublic.js b/src/endWeek/saServeThePublic.js
index 08dcfb7f848dc8e210cefb8766435e9d346fdbce..84bfd46b4d1a8eee0c59836d13152a75015222c8 100644
--- a/src/endWeek/saServeThePublic.js
+++ b/src/endWeek/saServeThePublic.js
@@ -296,7 +296,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 			} else if (slave.health.illness === 5) {
 				r += ` terribly ill`;
 			}
-			if (slave.health.illness > 0 && slave.health.tired > 90) {
+			if (slave.health.illness > 0 && slave.health.tired > 60) {
 				r += ` and`;
 			}
 			if (slave.health.tired > 90) {
diff --git a/src/endWeek/saWhore.js b/src/endWeek/saWhore.js
index 2bc8ff9edfb2f1562f721137536d0330deea7903..1fb242d40dd7f636a015f3b097757549d2843057 100644
--- a/src/endWeek/saWhore.js
+++ b/src/endWeek/saWhore.js
@@ -334,7 +334,7 @@ App.SlaveAssignment.whore = (function() {
 			} else if (slave.health.illness === 5) {
 				r += ` terribly ill`;
 			}
-			if (slave.health.illness > 0 && slave.health.tired > 90) {
+			if (slave.health.illness > 0 && slave.health.tired > 60) {
 				r += ` and`;
 			}
 			if (slave.health.tired > 90) {
diff --git a/src/facilities/pit/pitFramework.js b/src/facilities/pit/pitFramework.js
index df3b2e7fda084849d5bab94a29d0fc922dfb0e42..4d2b99f992cb79d0b912554d9e8bad9e4be00dd5 100644
--- a/src/facilities/pit/pitFramework.js
+++ b/src/facilities/pit/pitFramework.js
@@ -32,6 +32,21 @@ App.Entity.Facilities.PitFighterJob = class extends App.Entity.Facilities.Facili
 		}
 		return r;
 	}
+
+	/**
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string[]}
+	 */
+	canEmploy(slave) {
+		if (this.isEmployed(slave)) {
+			return [`${slave.slaveName} is already assigned to fight in ${this.facility.name}.`];
+		}
+		if (!this._facilityHasFreeSpace) {
+			return [`Capacity of ${this.facility.name} exceeded.`];
+		}
+
+		return this.checkRequirements(slave);
+	}
 };
 
 App.Entity.Facilities.Pit = class extends App.Entity.Facilities.SingleJobFacility {
diff --git a/src/gui/css/mainStyleSheet.css b/src/gui/css/mainStyleSheet.css
index 8c5fe85aa2dc48c0163f7f298a378f68423e2663..37d1b869d0b7d0a87ddcaf78e6982284e498065c 100644
--- a/src/gui/css/mainStyleSheet.css
+++ b/src/gui/css/mainStyleSheet.css
@@ -255,6 +255,10 @@ table.corporate tbody tr:nth-child(odd) {
 table.corporate td {
 	vertical-align: top;
 }
+table.corporate tbody th, table.corporate thead th {
+	background-color: black;
+	font-weight: bold;
+}
 .minor-note {
 	font-size: 0.75em;
 	font-style: italic;
diff --git a/src/interaction/main/walkPast.js b/src/interaction/main/walkPast.js
index 3530725741f8a5765677dc1ff51345e16efd2a4e..b06dd69419db7e63ea64cfc5b1c51e5e7ca4239d 100644
--- a/src/interaction/main/walkPast.js
+++ b/src/interaction/main/walkPast.js
@@ -1,62 +1,48 @@
 globalThis.walkPast = (function() {
 	// core string variables
 	let t;
-	let r;
-	let output;
 
 	// selection variables
 	let target;
-
-	// rng variables
-	let seed;
-	let fuckSeed;
-
-	// activeSlave variables
-	let name;
-	let race;
-
-	// partnerSlave variables
+	let partner;
 	let partnerSlave;
-	let partnerName;
 
 	/** generate a walkPast vignette
 	 * @param {App.Entity.SlaveState} activeSlave
 	 * @param {string} [fixedTarget] - if set, force target to this value
-	 * @returns {string} - vignette text (SC markup)
+	 * @returns {DocumentFragment} - vignette
 	 */
 	function walkPast(activeSlave, fixedTarget) {
-		output = "";
-		seed = jsRandom(1, 100);
-		name = activeSlave.slaveName;
+		let output = "";
 
 		let flag;
 		if (totalRelatives(activeSlave) > 0 && ((fixedTarget === "FRelation") || (!fixedTarget && jsRandom(1, 100) > 80))) {
 			flag = 110;
-			V.partner = "relation";
+			partner = "relation";
 		} else if (activeSlave.relationship > 0 && ((fixedTarget === "FRelation") || (!fixedTarget && jsRandom(1, 100) > 70))) {
 			flag = 120;
-			V.partner = "relationship";
+			partner = "relationship";
 		} else if (activeSlave.rivalry !== 0 && hasAllLimbs(activeSlave) && ((fixedTarget === "FRival") || (!fixedTarget && jsRandom(1, 100) > 70))) {
 			flag = 130;
-			V.partner = "rivalry";
+			partner = "rivalry";
 		} else {
 			flag = 99;
-			V.partner = "";
+			partner = "";
 		}
 
 		// return if we insist on a relation or rival but we didn't find one above
-		if (["FRelation", "FRival"].includes(fixedTarget) && !(V.partner)) {
+		if (["FRelation", "FRival"].includes(fixedTarget) && !partner) {
 			return;
 		}
 
-		App.Utils.setLocalPronouns(activeSlave); // Needed for the few places that still call widgets instead of functions, such as <<faceDescription>>
+		const seed = jsRandom(1, 100);
 		output += primeSlave(activeSlave, seed);
 
-		if (V.partner === "rivalry") {
+		if (partner === "rivalry") {
 			output += rivalSlave(activeSlave, seed);
-		} else if (V.partner === "relationship" && App.Utils.hasPartnerSex(activeSlave)) {
+		} else if (partner === "relationship" && App.Utils.hasPartnerSex(activeSlave)) {
 			output += loverSlave(activeSlave);
-		} else if (V.partner === "relationship" || V.partner === "relation") {
+		} else if (partner === "relationship" || partner === "relation") {
 			output += relatedSlave(activeSlave);
 		} else {
 			switch (fixedTarget) {
@@ -121,46 +107,68 @@ globalThis.walkPast = (function() {
 			him, his, He
 		} = getPronouns(activeSlave);
 
+		let links = [];
+		const executeWalkpast = (passage) => {
+			V.activeSlave = activeSlave; // TODO: remove me once all the F* passages don't need activeSlave anymore
+			$(`#walkpast-${activeSlave.ID}`).empty().append(App.UI.DOM.renderPassage(passage));
+		};
 		if (activeSlave.fuckdoll === 0) {
 			if (target === "FButt" && activeSlave.chastityAnus) {
 				output += ` If you wish to have anal intercourse with ${him} you must order ${him} to remove ${his} chastity belt.`;
-				output += ` <span id="walkpast"><<link "Grope ${his} rear instead">><<replace "#walk">><<include "FondleButt">><</replace>><</link>></span>`;
+				links.push(App.UI.DOM.link(`Grope ${his} rear instead`, executeWalkpast, "FondleButt"));
 			} else if (target === "FVagina" && activeSlave.chastityVagina) {
 				output += ` If you wish to have vanilla intercourse with ${him} you must order ${him} to remove ${his} chastity belt.`;
 			} else if (flag > 100) {
-				const setter = (flag === 110) ? `<<set $partner="${V.partner}", $relation=${V.relation}>>` : `<<set $partner="${V.partner}">>`;
-				output += ` <span id="walkpast"><<link "Summon them both">>${setter}<<replace "#walk">><<include "${target}">><</replace>><</link>></span>`;
+				links.push(App.UI.DOM.link(`Summon them both`, (partner, relation, target) => {
+					V.partner = partner;
+					V.relation = relation; // TODO: remove these once FRelation/FRival don't need them anymore
+					executeWalkpast(target);
+				}, [partner, flag === 110 ? partnerSlave.ID : null, target]));
 			} else if (activeSlave.assignment === Job.CONFINEMENT) {
-				output += ` <span id="walkpast"><<link "Have ${him} brought out of ${his} cell">><<replace "#walk">><<include "${target}">><</replace>><</link>></span>`;
+				links.push(App.UI.DOM.link(`Have ${him} brought out of ${his} cell`, executeWalkpast, target));
 			} else if (activeSlave.assignment === Job.DAIRY && V.dairyRestraintsSetting > 1) {
 				output += ` ${He} is strapped into a milking machine and cannot leave ${V.dairyName}.`;
 			} else {
 				if ([Job.ARCADE, Job.CELLBLOCK, Job.ATTENDANT, Job.DJ, Job.MADAM, Job.MILKMAID, Job.TEACHER, Job.STEWARD, Job.WARDEN, Job.AGENT, Job.CONCUBINE, Job.AGENTPARTNER, Job.HEADGIRLSUITE, Job.CLUB, Job.MASTERSUITE, Job.CONFINEMENT, Job.GLORYHOLE, Job.QUARTER, Job.BROTHEL, Job.DAIRY].includes(activeSlave.assignment)) {
-					output += ` <span id="walkpast"><<link "Have ${him} take a break and come up">><<replace "#walk">><<include "${target}">><</replace>><</link>></span>`;
+					links.push(App.UI.DOM.link(`Have ${him} take a break and come up`, executeWalkpast, target));
 				} else {
-					output += ` <span id="walkpast"><<link "Call ${him} over">><<replace "#walk">><<include "${target}">><</replace>><</link>></span>`;
+					links.push(App.UI.DOM.link(`Call ${him} over`, executeWalkpast, target));
 				}
 			}
 		} else {
 			switch (target) {
 				case "FVagina":
-					output += ` <span id="walkpast"><<link "Fuck ${him}">><<replace "#walk">><<include "FFuckdollVaginal">><</replace>><</link>></span>`;
+					links.push(App.UI.DOM.link(`Fuck ${him}`, executeWalkpast, "FFuckdollVaginal"));
 					break;
 				case "FButt":
 				case "FAnus":
-					output += ` <span id="walkpast"><<link "Fuck ${him}">><<replace "#walk">><<include "FFuckdollAnal">><</replace>><</link>></span>`;
+					links.push(App.UI.DOM.link(`Fuck ${him}`, executeWalkpast, "FFuckdollAnal"));
 					break;
 				default:
-					output += ` <span id="walkpast"><<link "Fuck ${him}">><<replace "#walk">><<include "FFuckdollOral">><</replace>><</link>></span>`;
+					links.push(App.UI.DOM.link(`Fuck ${him}`, executeWalkpast, "FFuckdollOral"));
 			}
 		}
-		return output;
+
+		if (V.debugMode && passage() !== "Walk Past List") {
+			links.push(App.UI.DOM.passageLink("Show all walk past scenes", "Walk Past List"));
+		}
+
+		const frag = document.createDocumentFragment();
+		$(frag).append(output);
+		const span = App.UI.DOM.appendNewElement("span", frag, App.UI.DOM.generateLinksStrip(links));
+		span.id = `walkpast-${activeSlave.ID}`;
+		return frag;
 	}
 
+	/** Generate walkpast for the main slave we're looking at
+	 * @param {App.Entity.SlaveState} activeSlave
+	 * @param {number} seed
+	 * @returns {string}
+	 */
 	function primeSlave(activeSlave, seed) {
-		r = "";
+		let r = "";
 
-		if (V.partner !== "relationship" || !App.Utils.hasPartnerSex(activeSlave)) {
+		if (partner !== "relationship" || !App.Utils.hasPartnerSex(activeSlave)) {
 			r += `${activeSlave.slaveName} `;
 			r += walkPasts(activeSlave, seed);
 			t = "";
@@ -171,9 +179,14 @@ globalThis.walkPast = (function() {
 		return r;
 	}
 
+	/** Generate walkpast for the rival of the slave we're looking at
+	 * @param {App.Entity.SlaveState} activeSlave
+	 * @param {number} seed
+	 * @returns {string}
+	 */
 	function rivalSlave(activeSlave, seed) {
 		partnerSlave = getSlave(activeSlave.rivalryTarget);
-		r = "";
+		let r = "";
 
 		const {
 			he, his
@@ -196,12 +209,17 @@ globalThis.walkPast = (function() {
 		return r + ` `;
 	}
 
+	/** Generate walkpast for the lover of the slave we're looking at
+	 * @param {App.Entity.SlaveState} activeSlave
+	 * @returns {string}
+	 */
 	function loverSlave(activeSlave) {
 		t = "";
 		partnerSlave = getSlave(activeSlave.relationshipTarget);
-		fuckSeed = jsRandom(1, 100);
-		seed = jsRandom(1, 100);
-		race = (V.seeRace ? activeSlave.race : "");
+		const fuckSeed = jsRandom(1, 100);
+		const seed = jsRandom(1, 100);
+		const name = activeSlave.slaveName;
+		const race = (V.seeRace ? activeSlave.race : "");
 
 		const {
 			he, him, his, himself, boy,
@@ -214,7 +232,7 @@ globalThis.walkPast = (function() {
 				he2, him2, his2, hers2, himself2, boy2, wife2, He2, His2
 			} = getPronouns(partnerSlave).appendSuffix("2");
 				/* /* eslint-enable no-unused-vars */
-			partnerName = partnerSlave.slaveName;
+			const partnerName = partnerSlave.slaveName;
 			const race2 = (V.seeRace ? partnerSlave.race : "");
 			const activeSlaveRel = relationshipTerm(activeSlave);
 			const partnerSlaveRel = relationshipTerm(partnerSlave);
@@ -1387,25 +1405,28 @@ globalThis.walkPast = (function() {
 		return t + ` `;
 	}
 
+	/** Generate walkpast for a relative of the slave we're looking at
+	 * @param {App.Entity.SlaveState} activeSlave
+	 * @returns {string}
+	 */
 	function relatedSlave(activeSlave) {
-		r = "";
-		fuckSeed = jsRandom(1, 100);
+		let r = "";
+		const fuckSeed = jsRandom(1, 100);
 
 		const {
 			he, him, his, hers, himself, boy,
 			He, His
 		} = getPronouns(activeSlave);
 
-		if (V.partner === "relation") {
+		if (partner === "relation") {
 			partnerSlave = randomRelatedSlave(activeSlave);
-			V.relation = partnerSlave.ID;
 		} else {
 			partnerSlave = getSlave(activeSlave.relationshipTarget);
 		}
 
 		if (partnerSlave !== undefined) { /* potential problem point */
 			r += ` ${His} `;
-			if (V.partner === "relation") {
+			if (partner === "relation") {
 				r += `${relativeTerm(activeSlave, partnerSlave)} `;
 			} else {
 				r += `${relationshipTerm(activeSlave)} `;
@@ -1421,13 +1442,18 @@ globalThis.walkPast = (function() {
 		return r + ` `;
 	}
 
+	/** Generate walkpast for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @param {number} seed
+	 * @returns {string}
+	 */
 	function walkPasts(slave, seed) {
 		const {
 			he, him, his, hers, himself, boy,
 			He, His
 		} = getPronouns(slave);
 		t = "";
-		race = (V.seeRace ? slave.race : "");
+		const race = (V.seeRace ? slave.race : "");
 
 		switch (slave.assignment) {
 			case Job.AGENT:
@@ -2028,6 +2054,10 @@ globalThis.walkPast = (function() {
 		return t + ` `;
 	}
 
+	/** Generate boob text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function boobWatch(slave) {
 		const pronouns = getPronouns(slave);
 		const {
@@ -2460,6 +2490,10 @@ globalThis.walkPast = (function() {
 		return t;
 	}
 
+	/** Generate butt text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function buttWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -2803,6 +2837,10 @@ globalThis.walkPast = (function() {
 		return t;
 	}
 
+	/** Generate belly text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function bellyWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -7636,6 +7674,10 @@ globalThis.walkPast = (function() {
 		return t;
 	}
 
+	/** Generate vagina text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function vaginaWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -8197,16 +8239,20 @@ globalThis.walkPast = (function() {
 		}
 		if ((slave.vaginalAccessory === "long dildo") || (slave.vaginalAccessory === "long, large dildo") || (slave.vaginalAccessory === "long, huge dildo")) {
 			t += `With every motion ${he} makes, ${his} dildo shifts, bulging out ${his} stomach.`;
-			if ((slave.buttPlug === "long plug") || (slave.buttPlug === "long, large plug") || (slave.buttPlug === "long, huge plug")) {
+			if ((slave.buttplug === "long plug") || (slave.buttplug === "long, large plug") || (slave.buttplug === "long, huge plug")) {
 				t += `Beside it, a second bulge caused by ${his} extra long buttplug.`;
 			}
-		} else if ((slave.buttPlug === "long plug") || (slave.buttPlug === "long, large plug") || (slave.buttPlug === "long, huge plug")) {
+		} else if ((slave.buttplug === "long plug") || (slave.buttplug === "long, large plug") || (slave.buttplug === "long, huge plug")) {
 			t += `With every motion ${he} makes, ${his} buttplug shifts, bulging out ${his} stomach.`;
 		}
 
 		return t;
 	}
 
+	/** Generate dick text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function dickWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -8768,16 +8814,20 @@ globalThis.walkPast = (function() {
 		}
 		if ((slave.vaginalAccessory === "long dildo") || (slave.vaginalAccessory === "long, large dildo") || (slave.vaginalAccessory === "long, huge dildo")) {
 			t += `With every motion ${he} makes, ${his} dildo shifts, bulging out ${his} stomach.`;
-			if ((slave.buttPlug === "long plug") || (slave.buttPlug === "long, large plug") || (slave.buttPlug === "long, huge plug")) {
+			if ((slave.buttplug === "long plug") || (slave.buttplug === "long, large plug") || (slave.buttplug === "long, huge plug")) {
 				t += `Beside it, a second bulge caused by ${his} extra long buttplug.`;
 			}
-		} else if ((slave.buttPlug === "long plug") || (slave.buttPlug === "long, large plug") || (slave.buttPlug === "long, huge plug")) {
+		} else if ((slave.buttplug === "long plug") || (slave.buttplug === "long, large plug") || (slave.buttplug === "long, huge plug")) {
 			t += `With every motion ${he} makes, ${his} buttplug shifts, bulging out ${his} stomach.`;
 		}
 
 		return t;
 	}
 
+	/** Generate anus text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function anusWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -9093,6 +9143,10 @@ globalThis.walkPast = (function() {
 		return t;
 	}
 
+	/** Generate lip/mouth text for a given slave
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
 	function lipWatch(slave) {
 		const {
 			he, him, his, hers, himself, boy,
@@ -9201,11 +9255,10 @@ globalThis.walkPast = (function() {
 
 /** generate the specified walkPast vignette for all slaves (for debugging purposes)
  * @param {string} fixedTarget
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 globalThis.walkPastAll = function(fixedTarget) {
-	return V.slaves.map((s) => {
-		const wp = walkPast(s, fixedTarget);
-		return wp ? `<p>${wp}</p>` : ``;
-	}).join('');
+	return App.UI.DOM.combineNodes(...V.slaves.map(
+		(s) => App.UI.DOM.makeElement("p", walkPast(s, fixedTarget))
+	));
 };
diff --git a/src/interaction/prostheticConfig.tw b/src/interaction/prostheticConfig.tw
index 8a68dbe21a99b923a830f662d4882e026c8c74ad..dc84c4ea06d63de195ca06c57a653ff27a52fd64 100644
--- a/src/interaction/prostheticConfig.tw
+++ b/src/interaction/prostheticConfig.tw
@@ -1,7 +1,5 @@
 :: Prosthetics Configuration [nobr]
 
-<<set $AS = $activeSlave.ID>>
-
 <<switch $prostheticsConfig>>
 
 <<case "main">>
diff --git a/src/interaction/slaveInteract.js b/src/interaction/slaveInteract.js
index acd8e50842d97def5ddd7057c54bacd4fb77b3fa..48e7b162f7d11f79f31e4a7f31032cf0b24ae186 100644
--- a/src/interaction/slaveInteract.js
+++ b/src/interaction/slaveInteract.js
@@ -48,9 +48,9 @@ App.UI.SlaveInteract.modify = function(slave) {
 	 * @param {string} caption
 	 * @param {string} passage
 	 * @param {string} note
-	 * @param {function ():void} handler
+	 * @param {function ():void} [handler]
 	 */
-	function makeRoomLink(c, caption, passage, note, handler) {
+	function makeRoomLink(c, caption, passage, note, handler = () => {}) {
 		const res = document.createElement('div');
 		c.appendChild(res);
 		res.appendChild(App.UI.DOM.link(caption, handler, [], passage));
@@ -58,31 +58,21 @@ App.UI.SlaveInteract.modify = function(slave) {
 		return res;
 	}
 
-	makeRoomLink(el, "Auto salon", "Salon", ' Modify hair (color, length, style), nails, and even skin color.',
-		() => {
-			V.activeSlave = slave;
-		}
-	);
+	makeRoomLink(el, "Auto salon", "Salon", ' Modify hair (color, length, style), nails, and even skin color.');
 
 	makeRoomLink(el, "Body mod studio", "Body Modification", ' Mark your slave with piercings, tattoos, brands or even scars.',
 		() => {
-			V.activeSlave = slave;
 			V.degradation = 0;
 			V.tattooChoice = undefined;
 		},
 	);
 
-	makeRoomLink(el, "Remote surgery", "Remote Surgery", ` Surgically modify your slave with state of the art plastic surgery and more. Alter ${his} senses, skeletal structure, organs, and even more.`,
-		() => {
-			V.activeSlave = slave;
-		}
-	);
+	makeRoomLink(el, "Remote surgery", "Remote Surgery", ` Surgically modify your slave with state of the art plastic surgery and more. Alter ${his} senses, skeletal structure, organs, and even more.`);
 
 	// Prosthetics
 	if (V.prostheticsUpgrade > 0) {
 		makeRoomLink(el, "Configure cybernetics", "Prosthetics Configuration", ` Configure prosthetics, if ${he} has been surgically implanted with interfaces that support it.`,
 			() => {
-				V.activeSlave = slave;
 				V.prostheticsConfig = "main";
 			}
 		);
@@ -90,11 +80,7 @@ App.UI.SlaveInteract.modify = function(slave) {
 
 	// Analyze Pregnancy
 	if (V.pregnancyMonitoringUpgrade > 0) {
-		makeRoomLink(el, "Internal scan", "Analyze Pregnancy", ` Full scan of abdomen and reproductive organs.`,
-			() => {
-				V.activeSlave = slave;
-			}
-		);
+		makeRoomLink(el, "Internal scan", "Analyze Pregnancy", ` Full scan of abdomen and reproductive organs.`);
 	}
 
 	return el;
@@ -399,7 +385,6 @@ App.UI.SlaveInteract.assignmentBlock = function(slave) {
 			slave.ID, passage(),
 			(slave, assignment) => {
 				assignJob(slave, assignment);
-				V.activeSlave = slave;
 			}
 		)
 	);
diff --git a/src/js/assignJS.js b/src/js/assignJS.js
index 252e586a627b7eb44d4838050fb734d70b056be3..6914b5dae7165565286473d39591a22c8f839f72 100644
--- a/src/js/assignJS.js
+++ b/src/js/assignJS.js
@@ -564,6 +564,12 @@ globalThis.removeJob = function(slave, assignment, saveRecord = false) {
 				if (V.dairyRestraintsSetting > 1) {
 					slave.health.tired = 100;
 				}
+				if (V.dairyFeedersSetting > 0) {
+					slave.diet = "healthy";
+				}
+				if (V.dairyHormonesSetting > 0) {
+					slave.hormones = 0;
+				}
 				break;
 
 			case Job.FARMYARD.toLowerCase():
@@ -914,7 +920,7 @@ globalThis.assignmentTransition = function(slave, assignTo, passage) {
 	V.activeSlave = slave; // might be needed by the passage we're transitioning to (TODO: get rid of this)
 	assignJob(slave, assignTo);
 	if (V.showAssignToScenes === 1 && slave.fetish !== "mindbroken") {
-		if (assignTo === Job.DAIRY && ((V.dairyStimulatorsSetting >= 2) || (V.dairyFeedersSetting >= 2) || (V.dairyPregSetting >= 2))) {
+		if (assignTo === Job.DAIRY && V.dairyRestraintsSetting >= 2 && ((V.dairyStimulatorsSetting >= 2) || (V.dairyFeedersSetting >= 2) || (V.dairyPregSetting >= 2))) {
 			detourThroughScene("Industrial Dairy Assignment Scene");
 		} else if (assignTo === Job.DAIRY && (V.dairyRestraintsSetting === 0 && V.activeSlave.devotion > 0)) {
 			detourThroughScene("Free Range Dairy Assignment Scene");
diff --git a/src/js/economyJS.js b/src/js/economyJS.js
index 2f791dedcd56a3ffb668bc158eec356c0e475233..6c7a2f5b4e7543c54923d55b48f133d94914af1b 100644
--- a/src/js/economyJS.js
+++ b/src/js/economyJS.js
@@ -544,7 +544,8 @@ globalThis.calculateCosts = (function() {
 	}
 
 	function getArcadeCosts() {
-		return (0.05 + 0.02 * V.arcadeUpgradeInjectors + 0.05 * V.arcadeUpgradeCollectors + 0.02 * V.arcadeUpgradeHealth) * (V.arcade * V.facilityCost);
+		const healthUpgrade = V.arcadeUpgradeHealth >= 0 ? V.arcadeUpgradeHealth : 0;
+		return (0.05 + (0.02 * V.arcadeUpgradeInjectors) + (0.05 * V.arcadeUpgradeCollectors) + (0.02 * healthUpgrade)) * (V.arcade * V.facilityCost);
 	}
 
 	function getClubCosts() {
diff --git a/src/js/hTagMacroJS.js b/src/js/hTagMacroJS.js
deleted file mode 100644
index 0862ab250863651d47801df38e295ed8f0f70969..0000000000000000000000000000000000000000
--- a/src/js/hTagMacroJS.js
+++ /dev/null
@@ -1,44 +0,0 @@
-
-/*
-* <<htag>> macro
-* A simple macro which allows to create wrapping html elements with dynamic IDs.
-*	idea blatantly robbed from the spanMacroJS.tw but expanded to a more generic
-*	case, allowing <div>, <button> or whatever you want. elements, default is for
-*	the div though. In addition, you can pass an object in as the first argument
-*	instead of an id, and each of the object's attributes will become attributes
-*	of the generate tag.
-*
-* Usage: <<htag id>>...<</htag>>
-* Usage: <<htag id tag>>...<</htag>>
-* Usage: <<htag attributes>>...<</htag>>
-* Usage: <<htag attributes tag>>...<</htag>>
-*/
-Macro.add('htag', {
-	tags: null,
-	handler() {
-		const payload = this.payload[0].contents.replace(/(^\n+|\n+$)/, '');
-		let htag = 'div';
-		let attributes;
-
-		function munge(val, key) {
-			return `${key}="${val}"`;
-		}
-
-		if (this.args.length === 0) { return this.error('invalid syntax, format: <<htag [id [ tag ] | attributes [ tag ] >>'); }
-		if (this.args.length > 1) { htag = String(this.args[1]).trim(); }
-		if (typeof this.args[0] === "object") {
-			attributes = $.map(this.args[0], munge).join(" ");
-		} else {
-			attributes = `id="${String(this.args[0]).trim()}"`;
-		}
-		if (Config.debug) {
-			this.debugView.modes({
-				block: true
-			});
-		}
-
-		jQuery(`<${htag} ${attributes} />`)
-			.wiki(payload)
-			.appendTo(this.output);
-	}
-});
diff --git a/src/js/main.js b/src/js/main.js
index 0a96871e591df887d8f751d5387f8df04d550723..af842e9b7c07bad7b3ac4bb266e7e42695f8f51b 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -212,6 +212,17 @@ App.MainView.useGuard = function() {
 	return outerDiv;
 };
 
+App.MainView.walkPast = function() {
+	const outerDiv = document.createElement("div");
+
+	const slave = V.slaves.filter(s => ![Job.BODYGUARD, Job.FUCKTOY].includes(s.assignment)).random();
+	if (slave) {
+		App.UI.DOM.appendNewElement("span", outerDiv, walkPast(slave), "scene-intro");
+	}
+
+	return outerDiv;
+};
+
 App.MainView.full = function() {
 	function mainMenu() {
 		const div = document.createElement("div");
@@ -283,6 +294,7 @@ App.MainView.full = function() {
 	if (V.useSlaveSummaryOverviewTab === 0) {
 		fragment.append(App.MainView.useGuard());
 	}
+	fragment.append(App.MainView.walkPast());
 
 	return fragment;
 };
diff --git a/src/js/modification.js b/src/js/modification.js
index 2a9358b08cdf07248ec5b33b88270aa6a3782fee..bfc134987e926095a0ec115fe2bc7da489b3d51a 100644
--- a/src/js/modification.js
+++ b/src/js/modification.js
@@ -65,7 +65,6 @@ App.Medicine.Modification.removeScar = function(slave, scar, design) {
  */
 App.Medicine.Modification.addScourged = function(slave, weight) {
 	let scarArray = ["left breast", "right breast", "back", "lower back", "left buttock", "right buttock"];
-	let i = 0;
 	// Whip
 	if (getLeftArmID(slave) === 1) {
 		scarArray.push("left upper arm");
@@ -80,7 +79,7 @@ App.Medicine.Modification.addScourged = function(slave, weight) {
 		scarArray.push("right thigh");
 	}
 
-	for (i = 0; i < scarArray.length; i++) {
+	for (let i = 0; i < scarArray.length; i++) {
 		App.Medicine.Modification.addScar(slave, scarArray[i], "whip", weight);
 	}
 	// Manacles
@@ -97,7 +96,7 @@ App.Medicine.Modification.addScourged = function(slave, weight) {
 	if (getRightLegID(slave) === 1) {
 		scarArray.push("right ankle");
 	}
-	for (i = 0; i < scarArray.length; i++) {
+	for (let i = 0; i < scarArray.length; i++) {
 		App.Medicine.Modification.addScar(slave, scarArray[i], "chain", weight);
 	}
 };
@@ -151,8 +150,7 @@ App.Medicine.Modification.addBulkScars = function(slave, location, type, weight)
 		scarArray.push("back", "lower back");
 	}
 
-	let i = 0;
-	for (i = 0; i < scarArray.length; i++) {
+	for (let i = 0; i < scarArray.length; i++) {
 		App.Medicine.Modification.addScar(slave, scarArray[i], type, weight);
 	}
 };
@@ -294,7 +292,7 @@ App.Medicine.Modification.setPiercing = function(slave, location, weight) {
 		}
 	}
 
-	if (slave.genes === "XY" && slave.attrXY <= 35 && ["ear", "eyebrow", "lips", "navel", "nose"].contains(location)) {
+	if (slave.genes === "XY" && slave.attrXY <= 35 && ["ear", "eyebrow", "lips", "navel", "nose"].includes(location)) {
 		r += `${His} girly new `;
 		switch (location) {
 			case "ear":
diff --git a/src/js/pregJS.js b/src/js/pregJS.js
index addda73e7fb64548d9d50c716e82124ae0e0b148..6aea68b3cac8c1ed0e5ae2394e3a14021e12b425 100644
--- a/src/js/pregJS.js
+++ b/src/js/pregJS.js
@@ -13,8 +13,7 @@ globalThis.getPregBellySize = function(s) {
 		targetLen = ((-0.00003266 * Math.pow(gestationWeek, 2)) + (0.076 * gestationWeek) + 43.843);
 	}
 
-	let bellySize = ((4 / 3) * (Math.PI) * (phi / 2) * (Math.pow((targetLen / 2), 3)) * fetuses);
-	return bellySize;
+	return ((4 / 3) * (Math.PI) * (phi / 2) * (Math.pow((targetLen / 2), 3)) * fetuses);
 };
 
 /**
diff --git a/src/js/sexActsJS.js b/src/js/sexActsJS.js
index 5342a4f4806319a91ac81dce73412f06e9c77bb7..0141ff1c5e34ca98cdfe291eef3502c094f84623 100644
--- a/src/js/sexActsJS.js
+++ b/src/js/sexActsJS.js
@@ -495,6 +495,9 @@ globalThis.actX = function(slave, act, count = 1) {
 		case "vaginal":
 			V.vaginalTotal += count;
 			break;
+		case "abortions":
+			V.abortionsTotal += count;
+			break;
 		default:
 			// Act was likely entered incorrectly.
 			return;
diff --git a/src/js/slaveListing.js b/src/js/slaveListing.js
index 0dcd37aab3c517a757c6660eb2f78bb356601322..1a1deadb415dd22e46f29dbd39f40e09f30f83c5 100644
--- a/src/js/slaveListing.js
+++ b/src/js/slaveListing.js
@@ -537,14 +537,6 @@ App.UI.SlaveList.SlaveInteract.penthouseInteract = function(slave) {
 	return stdLink;
 };
 
-/**
- * @param {App.Entity.SlaveState} slave
- * @returns {HTMLElement}
- */
-App.UI.SlaveList.SlaveInteract.personalAttention = (slave) =>
-	App.UI.DOM.passageLink(SlaveFullName(slave), undefined,
-		() => { App.UI.selectSlaveForPersonalAttention(slave.ID); });
-
 /**
  * Adds/removes a slave with the given id to/from the personal attention array
  * @param {number} id slave id
@@ -582,7 +574,6 @@ App.UI.selectSlaveForPersonalAttention = function(id) {
  */
 App.UI.SlaveList.sortingLinks = function(passage) {
 	const outerDiv = document.createElement("div");
-	outerDiv.classList.add("flex-container");
 	const textify = string => capFirstChar(string.replace(/([A-Z])/g, " $1"));
 
 	let innerDiv = App.UI.DOM.makeElement("div", "Sort by: ", "indent");
@@ -592,7 +583,7 @@ App.UI.SlaveList.sortingLinks = function(passage) {
 	innerDiv.append(App.UI.DOM.arrayToList(order, " | ", " | "));
 	outerDiv.append(innerDiv);
 
-	innerDiv = App.UI.DOM.makeElement("div", "Sort: ", "indent");
+	innerDiv = App.UI.DOM.makeElement("div", "Sort direction: ", "indent");
 	order = ["descending", "ascending"].map(so => V.sortSlavesOrder !== so ?
 		App.UI.DOM.passageLink(textify(so), passage, () => { V.sortSlavesOrder = so; }) : textify(so));
 	innerDiv.append(App.UI.DOM.arrayToList(order, " | ", " | "));
diff --git a/src/js/statsChecker/statsChecker.js b/src/js/statsChecker/statsChecker.js
index afb63c489bc68e8724150118ee675ace188fd813..a0143692a453ef7452b88604fd24caed328f7bfe 100644
--- a/src/js/statsChecker/statsChecker.js
+++ b/src/js/statsChecker/statsChecker.js
@@ -619,7 +619,7 @@ globalThis.isEliteBreeder = function(slave) {
 };
 
 /**
- * @param {App.Entity.SlaveState} slave
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave
  * @returns {boolean}
  */
 globalThis.canGetPregnant = function(slave) {
@@ -639,7 +639,7 @@ globalThis.canGetPregnant = function(slave) {
 };
 
 /** contraceptives (.preg === -1) do not negate this function
- * @param {App.Entity.SlaveState} slave
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave
  * @returns {boolean}
  */
 globalThis.isFertile = function(slave) {
diff --git a/src/js/storyJS.js b/src/js/storyJS.js
index f33c613061c574afb6fdf180e3d7621c678cab37..f975ebf56ebd9e45fb883528ea363b12411b7c77 100644
--- a/src/js/storyJS.js
+++ b/src/js/storyJS.js
@@ -69,8 +69,8 @@ globalThis.filterInPlace = function(arr, callback, thisArg) {
 };
 
 /** pregmod: are slave2's sperm compatible with slave1's eggs?
- * @param {App.Entity.SlaveState} slave1
- * @param {App.Entity.SlaveState} slave2
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave1
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave2
  * @returns {boolean}
  */
 globalThis.canBreed = function(slave1, slave2) {
@@ -83,8 +83,8 @@ globalThis.canBreed = function(slave1, slave2) {
 /** assuming slave1 is fertile, could slave2 impregnate slave1?
  * slave2 must have dick and balls with compatible sperm;
  * both slaves must not be in chastity; slave2 need not achieve erection
- * @param {App.Entity.SlaveState} slave1
- * @param {App.Entity.SlaveState} slave2
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave1
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave2
  * @returns {boolean}
  */
 globalThis.canImpreg = function(slave1, slave2) {
@@ -114,8 +114,8 @@ globalThis.canImpreg = function(slave1, slave2) {
 /** assuming slave1 is fertile, could slave2 impregnate slave1?
  * slave2 must have balls with compatible sperm;
  * a special function intended for tribbing scenes;
- * @param {App.Entity.SlaveState} slave1
- * @param {App.Entity.SlaveState} slave2
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave1
+ * @param {App.Entity.SlaveState|App.Entity.PlayerState} slave2
  * @returns {boolean}
  */
 globalThis.canFemImpreg = function(slave1, slave2) {
diff --git a/src/js/utilsFC.js b/src/js/utilsFC.js
index e33d9c8e2e7e349085711420d007cbd300c8869e..0690e3f524b37f3453325f81935f471b3cbfea28 100644
--- a/src/js/utilsFC.js
+++ b/src/js/utilsFC.js
@@ -2208,15 +2208,15 @@ globalThis.convertCareer = function(slave) {
 };
 
 /**
- * @param {string} targetSkill. Skill to be checked.
- * @param {Object} slave. Slave to be checked.
+ * @param {string} targetSkill - Skill to be checked.
+ * @param {Object} slave - Slave to be checked.
  * @param {number} [skillIncrease=1]
  * @returns {string}
  */
 globalThis.slaveSkillIncrease = function(targetSkill, slave, skillIncrease = 1) {
 	let r = "", skillDec;
-	const {He, his} = getPronouns(slave),
-	isleadershipRole = function() {
+	const {He, his, him} = getPronouns(slave);
+	const isleadershipRole = function() {
 		if (['headGirl', 'recruiter', 'bodyguard', 'madam', 'DJ', 'nurse', 'teacher', 'attendant', 'matron', 'stewardess', 'milkmaid', 'farmer', 'wardeness'].includes(targetSkill)) {
 			return true;
 		}
@@ -2244,9 +2244,9 @@ globalThis.slaveSkillIncrease = function(targetSkill, slave, skillIncrease = 1)
 				case 'oral':
 					r += ` and at least suck a dick without constant gagging.`; break;
 				case 'vaginal':
-					r += ` and avoid some of the common pitfalls and turnoffs`; break;
+					r += ` and can avoid some of the common pitfalls and turnoffs.`; break;
 				case 'anal':
-					r += ` and can accept penetration of ${his} anus without danger`; break;
+					r += ` and can accept penetration of ${his} anus without danger.`; break;
 				case 'whoring':
 					r += ` and can avoid some potentially dangerous situations.`; break;
 				case 'entertainment':
@@ -2304,7 +2304,7 @@ globalThis.slaveSkillIncrease = function(targetSkill, slave, skillIncrease = 1)
 				case 'oral':
 					r += ` and has a delightfully experienced tongue.`; break;
 				case 'vaginal':
-					r += ` and has the muscular control to massage anything that's inside $him`; break;
+					r += ` and has the muscular control to massage anything that's inside ${him}.`; break;
 				case 'anal':
 					r += ` and knows how to use ${his} sphincter to please.`; break;
 				case 'whoring':
diff --git a/src/js/utilsSC.js b/src/js/utilsSC.js
index 50d0a12b4442d3d8c0de0775568a82e7224393de..b796c63e8d24b639e726aa4d28dee707f6908a72 100644
--- a/src/js/utilsSC.js
+++ b/src/js/utilsSC.js
@@ -114,35 +114,6 @@ App.UI.replace = function(selector, newContent) {
 	target.append(ins);
 };
 
-/**
- * A simple macro which allows to create wrapping html elements with dynamic IDs.
- *
- * idea blatantly robbed from the spanMacroJS.tw but expanded to a more generic case, allowing <div>,
- * <button> or whatever you want elements, default is for the div though.
- * In addition, you can pass an object in as the first argument instead of an id, and each of the
- * object's attributes will become attributes of the generate tag.
- *
- * @example
- * htag('test', "red") // <div id="red">test</div>
- * htag('test', {class: red}); // <div class="red">test</div>
- * htag('test', {class: red, id: green}); // <div class="red" id="green">test</div>
- * @param {string} text
- * @param {string|object} attributes
- * @param {string} [tag='div'] (optional)
- * @returns {string}
- */
-App.UI.htag = function(text, attributes, tag = 'div') {
-	const payload = text.replace(/(^\n+|\n+$)/, "");
-
-	if (typeof attributes === "object") {
-		attributes = $.map(attributes, (val, key) => `${key}="${val}"`).join(" ");
-	} else {
-		attributes = `id="${attributes.trim()}"`;
-	}
-
-	return `<${tag} ${attributes}>${payload}</${tag}>`;
-};
-
 App.UI.tabBar = function() {
 	return {
 		openTab: openTab,
diff --git a/src/markets/criminalMarkets.tw b/src/markets/criminalMarkets.tw
index 3c935d09c25e87a034ff7b86cc2ec14baaee1185..81fc15dab97ba29aea74f753f60f380b051f4ebb 100644
--- a/src/markets/criminalMarkets.tw
+++ b/src/markets/criminalMarkets.tw
@@ -1,6 +1,6 @@
 :: Criminal Market [nobr]
 
-<<set $nextButton = "Back", $nextLink = "Buy Slaves", $returnTo = "Buy Slaves", $encyclopedia = "Slave Schools", $slaveMarket = $weeklyMarket>>
+<<set $nextButton = "Back", $nextLink = "Buy Slaves", $returnTo = "Buy Slaves", $encyclopedia = "Slave Schools", $slaveMarket = $prisonCircuit[$prisonCircuitIndex]>>
 
 <<if ndef $newSlaves>><<set $newSlaves = []>><</if>>
 <<if $newSlaves.length > 0>>
@@ -8,7 +8,7 @@
 <</if>>
 
 You board the transport to
-<<switch $weeklyMarket>>
+<<switch $slaveMarket>>
 <<case "low tier criminals">> /* mixed prisoners */
 
 	a small prison on the edge of the city spanning from the foot of your arcology with the appearance of an old world police station. You arrive inside and are met by a cute receptionist, asking why one such as yourself would visit such a place. With formalities out of the way, the head of the station is called and arrives shortly. "Well now, didn't expect to see you here. Came to pick up a criminal and give them a chance at not being a menial?" You are then led further into the station and into the prisoners' quarters; most of them appear downcast and dismal, but some look up to see what the commotion is about. "You! Come here! This fine <<if $PC.title == 1>>gentleman<<else>>lady<</if>> wants to take a look, so be on your best behavior!" the head calls out, bringing one of the criminals close enough to be inspected.
@@ -41,7 +41,7 @@ You board the transport to
 
 <</switch>>
 
-<<set _marketResult = generateMarketSlave($weeklyMarket)>>
+<<set _marketResult = generateMarketSlave($slaveMarket)>>
 <<set $activeSlave = _marketResult.slave>>
 <<print _marketResult.text>>
 
diff --git a/src/markets/eliteSlave.tw b/src/markets/eliteSlave.tw
index c2814c58e847d4bbf2a76bbd70153399003522e7..7bba87b8776bf0c110b2e70f3739d808943be7a5 100644
--- a/src/markets/eliteSlave.tw
+++ b/src/markets/eliteSlave.tw
@@ -8,25 +8,23 @@ You check to see if any potential breeding slaves are on auction. <<if $eliteAuc
 <<set $eliteAuctioned = 1>>
 
 <<if $arcologies[0].FSMaturityPreferentialist > 20>>
-	<<set $activeSlaveOneTimeMinAge = 25>>
+	<<set _minAge = 25>>
 <<else>>
-	<<set $activeSlaveOneTimeMinAge = $fertilityAge>>
+	<<set _minAge = $fertilityAge>>
 <</if>>
 <<if $arcologies[0].FSYouthPreferentialist > 20>>
-	<<set $activeSlaveOneTimeMaxAge = 23>>
+	<<set _maxAge = 23>>
 <<else>>
-	<<set $activeSlaveOneTimeMaxAge = 40>>
+	<<set _maxAge = 40>>
 <</if>>
-<<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $oneTimeDisableDisability = 1>>
 <<if $arcologies[0].FSSupremacist != "unset">>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationist != "unset">>
 	<<set _races = ["amerindian", "asian", "asian", "asian", "asian", "asian", "asian", "black", "black", "indo-aryan", "indo-aryan", "latina", "latina", "latina", "malay", "malay", "middle eastern", "middle eastern", "mixed race", "pacific islander", "semitic", "semitic", "southern european", "southern european", "white", "white", "white", "white", "white", "white", "white", "white", "white"]>>
 	<<set _races = _races.delete($arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
-<<set $activeSlave = GenerateNewSlave("XX")>>
+<<set $activeSlave = GenerateNewSlave("XX", {minAge: _minAge, maxAge: _maxAge, ageOverridesPedoMode: 1, race: _race, disableDisability: 1})>>
 <<set $activeSlave.origin = "$He was purchased from a member of the Elite.">>
 <<set $activeSlave.career = "a slave">>
 <<set $activeSlave.prestige = 1>>
diff --git a/src/markets/householdLiquidator.tw b/src/markets/householdLiquidator.tw
index af760dd4da3af999d4813e4fc230741ca917a9f2..502ae6acbc4d833a556719f1b5495fc5af9e002c 100644
--- a/src/markets/householdLiquidator.tw
+++ b/src/markets/householdLiquidator.tw
@@ -6,8 +6,7 @@
 
 <<if random(1,100) > 50>>
 
-<<set $oneTimeDisableDisability = 1>>
-<<set $activeSlave = GenerateNewSlave()>>
+<<set $activeSlave = GenerateNewSlave(null, {disableDisability: 1})>>
 <<set $activeSlave.origin = "You bought $him from the household liquidator.">>
 <<set $activeSlave.devotion = random(-75,-25)>>
 <<set $activeSlave.trust = random(-45,-25)>>
@@ -35,13 +34,7 @@ The household liquidator is offering a set of siblings for sale. You are permitt
 
 <<elseif random(1,100) > 20>>
 
-/% Begin younger parents submod. %/
-<<set $activeSlaveOneTimeMinAge = $fertilityAge + $minimumSlaveAge>>
-<<set $activeSlaveOneTimeMaxAge = 42>>
-<<set $one_time_age_overrides_pedo_mode = 1>> /% Old enough to have a child who can be a slave. %/
-/% End younger parents submod. %/
-<<set $oneTimeDisableDisability = 1>>
-<<set $activeSlave = GenerateNewSlave()>>
+<<set $activeSlave = GenerateNewSlave(null, {minAge: ($fertilityAge + $minimumSlaveAge), maxAge: 42, ageOverridesPedoMode: 1, disableDisability: 1})>>
 <<set $activeSlave.origin = "You bought $him from the household liquidator.">>
 <<set $activeSlave.devotion = random(-75,-25)>>
 <<set $activeSlave.trust = random(-45,-25)>>
@@ -73,8 +66,7 @@ The household liquidator is offering a $mother and $his <<if $activeSlave.genes
 
 <<else>>
 
-<<set $oneTimeDisableDisability = 1>>
-<<set $activeSlave = GenerateNewSlave()>>
+<<set $activeSlave = GenerateNewSlave({disableDisability: 1})>>
 <<set $activeSlave.origin = "You bought $him from the household liquidator.">>
 <<set $activeSlave.devotion = random(-75,-25)>>
 <<set $activeSlave.trust = random(-45,-25)>>
diff --git a/src/markets/marketUI.js b/src/markets/marketUI.js
index 734ef4abc827c5c0a4ed003c25af860a8f42c5a6..437b79880181cd52f557f33445905def6c5f2ded 100644
--- a/src/markets/marketUI.js
+++ b/src/markets/marketUI.js
@@ -77,6 +77,6 @@ App.UI.buyingFromMarketControls = function(slave, slaveCost) {
 		);
 	}
 
-	el.append(App.Desc.longSlave(V.activeSlave, {market: V.slaveMarket}));
+	el.append(App.Desc.longSlave(slave, {market: V.slaveMarket}));
 	return el;
 };
diff --git a/src/markets/specialSlave.js b/src/markets/specialSlave.js
index f3bc54bda6b4ad1a6357c2fbe8e4cdd23c8a6f7c..4fe18fdbe4315918ef6a3ad7f92e3f9d0004f290 100644
--- a/src/markets/specialSlave.js
+++ b/src/markets/specialSlave.js
@@ -5,7 +5,7 @@
 App.Markets.specialSlave = function() {
 	const el = document.createElement("p");
 	let p;
-	const heroSlaves = App.Utils.buildHeroArray();
+	let heroSlaves = App.Utils.buildHeroArray();
 	let slave;
 	let selectedID;
 
@@ -75,11 +75,55 @@ App.Markets.specialSlave = function() {
 		}
 		App.UI.DOM.appendNewElement("p", el, App.UI.DOM.generateLinksStrip(linkArray));
 
+		if (V.cheatMode) {
+			let oneSlaveCost = (s) => { let cost = slaveCost(s); return cost + (10 * Math.trunc((cost / 10) * 2)); };
+			let allHeroSlaves = App.Utils.buildHeroArray().map(
+				(hs) => {
+					const slave = App.Utils.getHeroSlave(hs);
+					return {slave: slave, cost: oneSlaveCost(slave), ID: hs.ID};
+				}
+			);
+			let totalCost = allHeroSlaves.reduce((acc, s) => acc += s.cost, 0);
+
+			if (V.cash > totalCost) {
+				App.UI.DOM.appendNewElement(
+					"div",
+					el,
+					App.UI.DOM.link(
+						`Buy all of them for ${cashFormat(totalCost)}`,
+						() => {
+							for (const hero of allHeroSlaves) {
+								cashX(forceNeg(hero.cost), "slaveTransfer", hero.slave);
+								newSlave(hero.slave);
+								V.heroSlavesPurchased.push(hero.ID);
+							}
+							refresh();
+						}
+					)
+				);
+			} else {
+				App.UI.DOM.appendNewElement(
+					"div",
+					el,
+					App.UI.DOM.disabledLink(
+						`Buy all of them for ${cashFormat(totalCost)}`,
+						[`Cannot afford`]
+					)
+				);
+			}
+		}
+
 		return el;
 
 		function refresh() {
-			jQuery("#complete-catalog").empty().append(catalog());
-			jQuery("#show-slave").empty().append(showSlave());
+			heroSlaves = App.Utils.buildHeroArray();
+			if (heroSlaves.length === 0) {
+				jQuery("#complete-catalog").empty().append(`There are no longer any special slaves available.`);
+				jQuery("#show-slave").empty();
+			} else {
+				jQuery("#complete-catalog").empty().append(catalog());
+				jQuery("#show-slave").empty().append(showSlave());
+			}
 		}
 	}
 
diff --git a/src/markets/theMarket/marketData.js b/src/markets/theMarket/marketData.js
index d98467b6ccd9dc339c766f9e871a2efdaacb43b6..b136d888a318f838e5d0e2efbd81945e2683a080 100644
--- a/src/markets/theMarket/marketData.js
+++ b/src/markets/theMarket/marketData.js
@@ -150,9 +150,9 @@ App.Data.Markets = {
 			get requirements() { return (V.seeDicks !== 100); }
 		},
 		{
-			title: "St.Claver Preparatory",
+			title: "St. Claver Preparatory",
 			marketType: "SCP",
-			passage: "St.Claver Preparatory",
+			passage: "St. Claver Preparatory",
 			note: "Slaves with basic training and solid implants.",
 			get requirements() { return (V.seeDicks !== 100); }
 		},
@@ -178,9 +178,9 @@ App.Data.Markets = {
 			get requirements() { return (V.seeDicks !== 0); }
 		},
 		{
-			title: "The Gymnasium - Academy",
+			title: "The Gymnasium-Academy",
 			marketType: "TGA",
-			passage: "The Gymnasium - Academy",
+			passage: "The Gymnasium-Academy",
 			note: "Well trained slaves with dicks.",
 			get requirements() { return (V.seeDicks !== 0); }
 		},
diff --git a/src/markets/theMarket/tradeMenials.js b/src/markets/theMarket/tradeMenials.js
index 57b16edcae8e947f5087c26fb7c751d229b1c148..be7ab60a304f6f231b29581c5b9cc36c0462e79d 100644
--- a/src/markets/theMarket/tradeMenials.js
+++ b/src/markets/theMarket/tradeMenials.js
@@ -392,7 +392,7 @@ App.UI.tradeMenials = function() {
 				linkArray = [];
 				const sellSomeFuckdolls = function(number = 1) {
 					const value = menialSlaveCost(number) * number;
-					V.menials -= number;
+					V.fuckdolls -= number;
 					V.menialDemandFactor -= number;
 					cashX(value, "fuckdollsTransfer");
 					jQuery("#menial-transaction-result").empty().append(App.UI.DOM.cashFormat(value));
@@ -492,7 +492,7 @@ App.UI.tradeMenials = function() {
 				linkArray = [];
 				const sellSomeBioreactors = function(number = 1) {
 					const value = menialSlaveCost(number - 100) * number;
-					V.menials -= number;
+					V.menialBioreactors -= number;
 					V.menialDemandFactor -= number;
 					jQuery("#menial-transaction-result").empty().append(App.UI.DOM.cashFormat(value));
 					cashX(value, "bioreactorsTransfer");
diff --git a/src/npc/acquisition.tw b/src/npc/acquisition.tw
index d1e22197f4c7d785b5f10d884f2c90c9f402a22a..52fcc6c06f02f917891997851c46b98ac065dbce 100644
--- a/src/npc/acquisition.tw
+++ b/src/npc/acquisition.tw
@@ -476,8 +476,7 @@ The previous owner seems to have left in something of a hurry.
 		<<case "AztecRevivalist">>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $fixedRace = "latina">>
-			<<set $fixedNationality = "Mexican">>
-			<<set $activeSlave = GenerateNewSlave()>>
+			<<set $activeSlave = GenerateNewSlave(null, {nationality: "Mexican"})>>
 			<<set $activeSlave.accent = 0>>
 			<<set $activeSlave.devotion = 75, $activeSlave.trust = 75>>
 			<<run setHealth($activeSlave, jsRandom(-20, 20), 0, 0, 0, 0)>>
@@ -504,8 +503,7 @@ The previous owner seems to have left in something of a hurry.
 		<<case "EdoRevivalist">>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $fixedRace = "asian">>
-			<<set $fixedNationality = "Japanese">>
-			<<set $activeSlave = GenerateNewSlave()>>
+			<<set $activeSlave = GenerateNewSlave(null, {nationality: "Japanese"})>>
 			<<set $activeSlave.accent = 0>>
 			<<set $activeSlave.devotion = random(25,45), $activeSlave.trust = random(25,45)>>
 			<<run setHealth($activeSlave, jsRandom(25, 45), 0, 0, 0, 0)>>
@@ -529,8 +527,7 @@ The previous owner seems to have left in something of a hurry.
 			<<set $one_time_age_overrides_pedo_mode = 1>>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $fixedRace = "asian">>
-			<<set $fixedNationality = "Chinese">>
-			<<set $activeSlave = GenerateNewSlave()>>
+			<<set $activeSlave = GenerateNewSlave(null, {nationality: "Chinese"})>>
 			<<set $activeSlave.devotion = random(55,65), $activeSlave.trust = random(25,45)>>
 			<<run setHealth($activeSlave, jsRandom(25, 45), 0, 0, 0, 0)>>
 			<<set $activeSlave.face = random(0,55)>>
diff --git a/src/npc/bodyguard/bgDescription.js b/src/npc/bodyguard/bgDescription.js
index 3526467c4ab8433e8bc6c3bb3cc54083b9a187e2..c308fc6421dca887451208047582ba71dfb4b519 100644
--- a/src/npc/bodyguard/bgDescription.js
+++ b/src/npc/bodyguard/bgDescription.js
@@ -8,7 +8,7 @@ App.Desc.bodyguard = function(slave) {
 		his, He, he
 	} = getPronouns(slave);
 	if (slave.fuckdoll !== 0 || slave.ID !== V.BodyguardID) {
-		return;
+		return "";
 	}
 	r.push(`${He} is your bodyguard.`);
 	r.push(bgWeapon());
diff --git a/src/npc/children/childInteract.tw b/src/npc/children/childInteract.tw
index 644d87be929ac274d419e75c4d656b3a4dca80bf..898b15b2a104d27e3c23c26bd2a6703f40067a7f 100644
--- a/src/npc/children/childInteract.tw
+++ b/src/npc/children/childInteract.tw
@@ -63,7 +63,7 @@ FIXME:
 | [[Auto salon|Salon][$degradation = 0, $tattooChoice = "", $piercingLevel = ""]]
 | [[Body mod studio|Body Modification][$degradation = 0, $tattooChoice = undefined]]
 | [[Remote surgery|Remote Surgery][$degradation = 0]]
-| [[Configure cybernetics|Prosthetics Configuration][$prostheticsConfig = "main"]]
+| [[Configure cybernetics|Prosthetics Configuration][$AS = $activeChild.ID, $prostheticsConfig = "main"]]
 */
 
 /*TODO: These will most likely need to be reworked*/
diff --git a/src/npc/descriptions/limbs.js b/src/npc/descriptions/limbs.js
index 2421844d9ece4bdc4c791d55214db41198ebabbf..1d18a1c071bab589ad77869a311de0591ab3552e 100644
--- a/src/npc/descriptions/limbs.js
+++ b/src/npc/descriptions/limbs.js
@@ -175,7 +175,7 @@ App.Desc.limbChange = function() {
 			if (slave.PLimb > 1) {
 				r += row("Cybernetic limb", 6);
 			} else {
-				r += "<div class=full'><span class='detail'>Install an advanced interface to attach cybernetic limbs.</span></div>";
+				r += "<div class='full'><span class='detail'>Install an advanced interface to attach cybernetic limbs.</span></div>";
 			}
 		}
 
@@ -183,6 +183,7 @@ App.Desc.limbChange = function() {
 
 		function apply() {
 			let s;
+			V.AS = slave.ID;
 			if (!returnTo) {
 				s = `<<set $prostheticsConfig = "limbs", $oldLimbs = ${JSON.stringify(oldLimbs)}>>` +
 					'<<goto "Prosthetics Configuration">>';
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index 85b3c6419e7f0ab5e77c1c3906ee411089f584ac..80853ba899583c8218aa95ed3c686ecd2427e30b 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -28,6 +28,7 @@ globalThis.generateGenetics = (function() {
 			motherName: "none",
 			father: 0,
 			fatherName: "none",
+			inbreedingCoeff: 0,
 			nationality: "Stateless",
 			race: "white",
 			intelligence: 0,
diff --git a/src/npc/generate/generateNewSlaveJS.js b/src/npc/generate/generateNewSlaveJS.js
index 5f5b28b5063e9a033fbc4c5754f761ec22e4607f..1d6e3ef428e5a91b35f2455015679fcac9f99c95 100644
--- a/src/npc/generate/generateNewSlaveJS.js
+++ b/src/npc/generate/generateNewSlaveJS.js
@@ -44,7 +44,7 @@ globalThis.GenerateNewSlave = (function() {
 		x.maxAge = maxAge || V.activeSlaveOneTimeMaxAge || 999;
 		x.ageOverridesPedoMode = ageOverridesPedoMode || V.one_time_age_overrides_pedo_mode || 0;
 		x.mature = mature || 1;
-		x.nationality = nationality || V.fixedNationality || 0;
+		x.nationality = nationality || 0;
 		x.race = race || V.fixedRace || 0;
 		if (x.race !== 0 && !(setup.filterRacesLowercase.includes(x.race))) {
 			console.log("Error, cannot find race: ", x.race);
@@ -65,7 +65,6 @@ globalThis.GenerateNewSlave = (function() {
 		delete V.activeSlaveOneTimeMinAge;
 		delete V.activeSlaveOneTimeMaxAge;
 		delete V.one_time_age_overrides_pedo_mode;
-		delete V.fixedNationality;
 		delete V.fixedRace;
 		delete V.oneTimeDisableDisability;
 
diff --git a/src/npc/generate/newSlaveIntro.js b/src/npc/generate/newSlaveIntro.js
new file mode 100644
index 0000000000000000000000000000000000000000..a9eaff982656532f5b0a71a1169ddefc2c41ecd9
--- /dev/null
+++ b/src/npc/generate/newSlaveIntro.js
@@ -0,0 +1,4112 @@
+/**
+ *
+ * @param {App.Entity.SlaveState} slave
+ * @param {App.Entity.SlaveState} [slave2]
+ */
+App.UI.newSlaveIntro = function(slave, slave2 = V.eventSlave) {
+	const desc = SlaveTitle(slave);
+	const {title: Master} = getEnunciation(slave);
+	const {
+		His, He, his, him, he, girl, hers, himself
+	} = getPronouns(slave);
+	const {woman: womanP} = getPronouns(V.PC);
+
+	const el = new DocumentFragment();
+	let r;
+	let p;
+	let span;
+	let brandTarget = V.brandTarget.primary;
+	let scarTarget = V.scarTarget.primary;
+
+	modSetup();
+
+	el.append(inspect());
+
+	el.append(choices());
+
+	return el;
+
+	/**
+	 * 	use the secondary location if the primary mark target won't work for this slave
+	 */
+	function modSetup() {
+		if (slave.dick === 0) {
+			if (V.brandTarget.primary === "penis") {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (V.scarTarget.primary === "penis") {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (slave.balls === 0 && slave.scrotum === 0) {
+			if (["left testicle", "right testicle"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["left testicle", "right testicle"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (slave.earShape === "none") {
+			if (["left ear", "right ear"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["left ear", "right ear"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (getLeftArmID(slave) !== 1) {
+			if (["left upper arm", "left lower arm", "left wrist", "left hand"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["left upper arm", "left lower arm", "left wrist", "left hand"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (getRightArmID(slave) !== 1) {
+			if (["right upper arm", "right lower arm", "right wrist", "right hand"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["right upper arm", "right lower arm", "right wrist", "right hand"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (getLeftLegID(slave) !== 1) {
+			if (["left thigh", "left calve", "left ankle", "left foot"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["left thigh", "left calve", "left ankle", "left foot"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+		if (getRightLegID(slave) !== 1) {
+			if (["right thigh", "right calve", "right ankle", "right foot"].includes(V.brandTarget.primary)) {
+				brandTarget = V.brandTarget.secondary;
+			}
+			if (["right thigh", "right calve", "right ankle", "right foot"].includes(V.scarTarget.primary)) {
+				scarTarget = V.scarTarget.secondary;
+			}
+		}
+	}
+
+	function inspect() {
+		const el = new DocumentFragment();
+		r = [];
+		p = document.createElement("p");
+
+		p.append(`The legalities completed, `);
+		span = document.createElement("span");
+		span.classList.add('slave-name');
+		span.textContent = SlaveFullName(slave);
+		p.append(span);
+		p.append(" ");
+
+		if (!hasAnyLegs(slave)) {
+			if (isAmputee(slave)) {
+				r.push(`lies`);
+			} else {
+				r.push(`rests`);
+			}
+			r.push(`helplessly on your desk,`);
+		} else if (!canStand(slave)) {
+			r.push(`kneels before your desk,`);
+		} else {
+			r.push(`stands before your desk,`);
+		}
+		if (slave.devotion < -50) {
+			if (canSee(slave)) {
+				r.push(`looking you in the eyes`);
+			} else {
+				r.push(`directly facing you`);
+			}
+			r.push(`with defiance.`);
+		} else if (slave.devotion < -20) {
+			if (canSee(slave)) {
+				r.push(`looking at`);
+			} else {
+				r.push(`facing`);
+			}
+			r.push(`you sullenly.`);
+		} else if (slave.trust <= 50) {
+			r.push(`staring`);
+			if (canSee(slave)) {
+				r.push(`at the ground`);
+			} else {
+				r.push(`blankly downwards`);
+			}
+			r.push(`in fear.`);
+		} else if (slave.devotion <= 50) {
+			r.push(`waiting meekly for orders.`);
+		} else if (canSmell(slave) && slave.career === "a breeding bull" && canGetPregnant(V.PC)) {
+			r.push(`sniffing the air and`);
+			if (canSee(slave)) {
+				r.push(`staring`);
+			} else {
+				r.push(`pointing ${his} nose`);
+			}
+			r.push(`intently at your crotch.`);
+		} else if (slave.origin === "$He submitted to enslavement to escape the hard life of an old world whore.") {
+			r.push(`wondering what one of your working ${girl}s can expect.`);
+		} else if ((slave.origin === "$He submitted to enslavement to get access to modern prenatal care.") || (slave.origin === "$He sold $himself into slavery out of fear that life on the streets was endangering $his pregnancy.")) {
+			r.push(`hoping you'll give ${him} modern medicine right away.`);
+		} else if (slave.origin === "$He submitted to enslavement as $his only way to obtain surgery to transform $him into a woman.") {
+			r.push(`hoping you'll schedule ${his} reassignment surgery immediately.`);
+		} else if (slave.origin === "$He submitted to enslavement out of a misguided desire to join a sexually libertine society.") {
+			r.push(`hoping ${he}'s made the right decision, and fantasizing a little, to go by ${his} half-hard dick.`);
+		} else if (slave.origin === "$He submitted to enslavement for a better chance at survival than $he had as a migrant.") {
+			r.push(`wondering what life as a sex slave will be like.`);
+		} else if (slave.origin === "$He came to you to escape being sold to a cruel master after $his producer informed $him of $his debt.") {
+			r.push(`wondering if ${he} will get to continue to perform for your people even as a slave.`);
+		} else {
+			r.push(`looking shyly at you and blushing.`);
+		}
+
+		if (V.gingering !== 0 && V.beforeGingering !== 0 && V.beforeGingering.ID === slave.ID) {
+			// extra checks to ensure gingering state is not left over from a different slave that was inspected but not purchased
+			let seed = "sale";
+			if (V.gingeringDetected === 1) {
+				if (V.gingeringDetection === "slaver") {
+					seed = "sale, as your slaving experience revealed";
+				} else if (V.gingeringDetection === "mercenary") {
+					seed = "sale, as the seller admitted in the face of your intimidating reputation";
+				} else if (V.gingeringDetection === "force") {
+					seed = "sale, as the seller admitted in the face of your deadly reputation";
+				} else {
+					seed = "sale, as you suspected";
+				}
+			}
+
+			if (V.gingering === "antidepressant") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given antidepressants to make ${him} seem less fearful for ${seed}. ${He} is much less trusting than ${he} appeared in the market.`);
+			} else if (V.gingering === "depressant") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given a depressant to make ${him} seem less hateful for ${seed}. ${He} is much less obedient than ${he} appeared in the market.`);
+			} else if (V.gingering === "stimulant") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given a stimulant to make ${him} seem healthier for ${seed}. ${He} is much less vital than ${he} appeared in the market.`);
+			} else if (V.gingering === "vasoconstrictor") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given a vasoconstrictor to make ${his} cock seem more feminine for ${seed}. It's larger and more apt to become hard than it appeared in the market.`);
+			} else if (V.gingering === "vasodilator") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given a vasodilator to give ${him} an excessive erection for ${seed}. ${His} dick is somewhat smaller than it appeared in the market, and ${he}'s not really hard all the time.`);
+			} else if (V.gingering === "aphrodisiac") {
+				r.push(`${His} intake toxicology reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`given aphrodisiacs to make ${him} horny and attracted to everyone for ${seed}. ${His} true sex drive and sexuality remain to be discovered.`);
+			} else {
+				r.push(`A close inspection of ${his} anus reveals that ${he} was`);
+				if (V.gingeringDetected === 1) {
+					r.push(`indeed`);
+				}
+				r.push(`doctored with an irritant to make ${him} present ${his} butt when shown for ${seed}. ${He} is not an actual anal sex enthusiast.`);
+			}
+		}
+
+		removeGingering();
+		// retrieve original slave without gingering effects
+
+
+		if (V.seeRace === 1) {
+			if (slave.override_Race !== 1) {
+				slave.origRace = slave.race;
+			}
+			if (slave.race !== slave.origRace) {
+				r.push(`A blood test reveals that ${he} was originally ${slave.origRace}, not slave.race ${(V.PC.skill.medicine >= 50 || V.PC.skill.slaving >= 50) ? `, just as you suspected` : ``}.`);
+			}
+		}
+		if (slave.skin !== slave.origSkin) {
+			if (slave.skin !== "sun tanned" && slave.skin !== "spray tanned") {
+				r.push(`An epidermis scan reveals that ${his} skin was originally ${slave.origSkin}, not slave.skin${(V.PC.skill.medicine >= 75 || V.PC.skill.slaving >= 75) ? `, just as you suspected` : ``}.`);
+			}
+		}
+
+
+		slave.oldDevotion = slave.devotion;
+		slave.oldTrust = slave.trust;
+
+		if (slave.vagina < 0) {
+			slave.skill.vaginal = 0;
+		}
+
+		if (slave.nipplesPiercing > 0) {
+			if (slave.nipples === "partially inverted") {
+				slave.nipples = "cute";
+			} else if (slave.nipples === "inverted") {
+				slave.nipples = "puffy";
+			}
+		}
+
+		if (slave.vagina > -1 && slave.dick === 0) {
+			if (V.PC.dick === 0 && V.PC.boobs >= 300) {
+				r.push(`${He} looks to you and sees a fellow woman, and is <span class="mediumaquamarine">a little less afraid</span> that you will rape and abuse ${him}.`);
+				slave.trust += 4;
+			}
+		} else if (slave.career === "a Futanari Sister") {
+			if (V.PC.dick !== 0 && V.PC.boobs >= 300 && V.PC.vagina !== -1) {
+				if (V.TFS.schoolPresent === 1) {
+					r.push(`${He}'s heard of you, of course, as both a futanari yourself and a generous patron of the Sisters. ${He} feels <span class="mediumaquamarine">very lucky</span> that ${he}'s going to be your slave, not to mention <span class="hotpink">almost desperate</span> to have sex with you.`);
+					slave.devotion += 10;
+					slave.trust += 10;
+				} else {
+					if (canSee(slave)) {
+						r.push(`Seeing`);
+					} else {
+						r.push(`Learning`);
+					}
+					r.push(`that you're a fellow futanari, ${he}'s <span class="mediumaquamarine">relieved</span> that ${he}'s going to be your slave, not to mention <span class="hotpink">quite eager</span> to have sex with you.`);
+					slave.devotion += 5;
+					slave.trust += 5;
+				}
+			}
+		} else if ((slave.boobs > 400) && (slave.dick > 0)) {
+			if (V.PC.dick !== 0 && (V.PC.boobs >= 400) || (V.PC.vagina !== -1)) {
+				r.push(`${He} looks to you and sees a fellow intersex person, and is <span class="mediumaquamarine">rather hopeful</span> that ${he} will find you a sympathetic owner.`);
+				slave.trust += 4;
+			}
+		}
+
+		if ((slave.attrXX > 50) || (slave.energy > 95) && slave.behavioralFlaw !== "hates women" && slave.trust >= -20) {
+			if (V.PC.boobs >= 400) {
+				r.push(`${He} seems to think you're pretty, and is more willing to <span class="hotpink">try for your approval</span> than ${he} would otherwise be. ${He} glances at your rack when ${he} thinks you're not looking.`);
+				slave.devotion += 4;
+			}
+		}
+
+		if (slave.behavioralFlaw === "hates women" && slave.devotion <= 50) {
+			if (V.PC.dick === 0 && V.PC.boobs >= 300) {
+				r.push(`${He} obviously does not find you immediately attractive, and is <span class="mediumorchid">less eager to conciliate you</span> than ${he} would otherwise be.`);
+				slave.devotion -= 5;
+			}
+		}
+
+		if ((slave.attrXY > 50 || slave.energy > 95) && slave.behavioralFlaw !== "hates men" && slave.trust >= -20) {
+			if (V.PC.dick === 0 && V.PC.boobs < 300) {
+				r.push(`${He} seems to think you're handsome, and is more willing to <span class="hotpink">try for your approval</span> than ${he} would otherwise be. ${He} glances at your crotch when ${he} thinks you're not looking.`);
+				slave.devotion += 4;
+			}
+		}
+
+		if (slave.devotion >= -20) {
+			if (slave.actualAge > 35) {
+				if (V.PC.visualAge < 35) {
+					if ((slave.behavioralFlaw !== "hates men" && V.PC.title === 1) || (slave.behavioralFlaw !== "hates women" && V.PC.title === 0)) {
+						r.push(`${He} tries to conceal ${his} surprise at your age, and keeps glancing at ${his} own naked body and then back at you, obviously wondering at such a <span class="hotpink">gorgeous young ${womanP}'s</span> interest in an old slave like ${him}.`);
+						slave.devotion += 4;
+					}
+				}
+			} else if ((slave.actualAge < 25)) {
+				if (V.PC.visualAge >= 50) {
+					if ((slave.behavioralFlaw !== "hates men" && V.PC.title === 1) || (slave.behavioralFlaw !== "hates women" && V.PC.title === 0)) {
+						r.push(`${He} keeps stealing glances at your face, probably wrestling with ${his} feelings about your age. Eventually ${he} seems to relax and accept it, with a certain <span class="hotpink">obvious thrill</span> at ${his} own willingness towards an older`);
+						if (V.PC.title === 1) {
+							r.push(`man.`);
+						} else {
+							r.push(`lady.`);
+						}
+						slave.devotion += 4;
+					}
+				}
+			}
+		}
+
+		if (V.PC.pregKnown === 1) {
+			if (V.PC.career === "escort") {
+				if (V.PC.belly >= 1000) {
+					if (slave.fetish === "pregnancy") {
+						if (slave.fetishKnown === 0) {
+							r.push(`${He} keeps stealing glances at your`);
+							if (V.PC.belly >= 120000) {
+								r.push(`massive`);
+							} else if (V.PC.belly >= 60000) {
+								r.push(`giant`);
+							} else if (V.PC.belly >= 15000) {
+								r.push(`huge`);
+							} else if (V.PC.belly >= 5000) {
+								r.push(`big`);
+							}
+							r.push(`baby bump.`);
+							if (canAchieveErection(slave)) {
+								r.push(`${His} dick rapidly hardening.`);
+							}
+							r.push(`You teasingly ask if ${he}'d like to feel it sometime, eliciting a delighted squeal from the ${girl}. <span class="green">${He}'s a pregnancy fetishist!</span>`);
+							slave.fetishKnown = 1;
+						} else {
+							r.push(`${He} keeps stealing glances at your`);
+							if (V.PC.belly >= 120000) {
+								r.push(`massive`);
+							} else if (V.PC.belly >= 60000) {
+								r.push(`giant`);
+							} else if (V.PC.belly >= 15000) {
+								r.push(`huge`);
+							} else if (V.PC.belly >= 5000) {
+								r.push(`big`);
+							}
+							r.push(`baby bump.`);
+							if (canAchieveErection(slave)) {
+								r.push(`${His} dick rapidly hardening.`);
+							}
+							r.push(`As you inspect ${him}, you take care to gently brush your pregnancy across ${him} as you move. ${He} is practically bursting with lust by the end and <span class="hotpink">eager to please you</span> so ${he} can be close to that belly.`);
+							slave.devotion += 5;
+						}
+					} else {
+						r.push(`Your pregnancy is obvious to ${him}, which means two things: you won't be as capable of disciplining ${him}, leading ${him} to be <span class="mediumorchid">more willing to disobey,</span> and you going to be demanding and needy, possibly even <span class="gold">using force to get ${him} to do what you want.</span>`);
+						slave.trust -= 10;
+						slave.devotion -= 10;
+					}
+				}
+			} else {
+				if (V.PC.belly >= 1500) {
+					if (slave.fetish === "pregnancy") {
+						if (slave.fetishKnown === 0) {
+							r.push(`${He} keeps stealing glances at your`);
+							if (V.PC.belly >= 120000) {
+								r.push(`massive`);
+							} else if (V.PC.belly >= 60000) {
+								r.push(`giant`);
+							} else if (V.PC.belly >= 15000) {
+								r.push(`huge`);
+							} else if (V.PC.belly >= 5000) {
+								r.push(`big`);
+							}
+							r.push(`baby bump.`);
+							if (canAchieveErection(slave)) {
+								r.push(`${His} dick rapidly hardening.`);
+							}
+							r.push(`You teasingly ask if ${he}'d like to feel it sometime, eliciting a delighted squeal from the ${girl}. <span class="green">${He}'s an impregnation fetishist!</span>`);
+							slave.fetishKnown = 1;
+						} else {
+							r.push(`${He} keeps stealing glances at your`);
+							if (V.PC.belly >= 120000) {
+								r.push(`massive`);
+							} else if (V.PC.belly >= 60000) {
+								r.push(`giant`);
+							} else if (V.PC.belly >= 15000) {
+								r.push(`huge`);
+							} else if (V.PC.belly >= 5000) {
+								r.push(`big`);
+							}
+							r.push(`baby bump.`);
+							if (canAchieveErection(slave)) {
+								r.push(`${His} dick rapidly hardening.`);
+							}
+							r.push(`As you inspect ${him}, you take care to gently brush your pregnancy across ${him} as you move. ${He} is practically bursting with lust by the end and <span class="hotpink">eager to please you</span> so ${he} can be close to that belly.`);
+							slave.devotion += 5;
+						}
+					} else {
+						r.push(`Your pregnancy is obvious to ${him}, which means two things: you won't be as capable of disciplining ${him}, leading ${him} to be <span class="mediumorchid">more willing to disobey,</span> and you going to be demanding and needy, possibly even <span class="gold">using force to get ${him} to do what you want.</span>`);
+						slave.trust -= 10;
+						slave.devotion -= 10;
+					}
+				}
+			}
+		}
+
+		if (V.PC.boobs >= 1400) {
+			if (slave.fetish === "boobs") {
+				if (slave.fetishKnown === 0) {
+					r.push(`${He} keeps stealing glances at your huge rack${canAchieveErection(slave) ? `, ${his} dick rapidly hardening` : ``}. You stretch ${(canSee(slave)) ? `, giving ${him} a fantastic view of your breasts,` : ``} before circling around behind ${him} and cupping ${his} chest. Between your tits against ${his} back, and how teasingly big you are pretending ${his} breasts will be, ${he} can't contain ${himself}; ${he} cums from overstimulation, completely pinning ${him} <span class="green">as a breast fetishist!</span>`);
+					slave.fetishKnown = 1;
+				} else {
+					r.push(`${He} keeps stealing glances at your huge rack${(canAchieveErection(slave)) ? `, ${his} dick rapidly hardening` : ``}. As you inspect ${him}, you take care to keep "accidentally" bumping ${him} with your tits, before "tripping" and landing with ${his} face buried in your chest. ${He} is practically bursting with lust by the end and <span class="hotpink">eager to please you</span> so ${he} can be close to those boobs.`);
+					slave.devotion += 5;
+				}
+			}
+		}
+
+		if (V.enduringTrust > 20) {
+			if (slave.trust >= -20) {
+				r.push(`${He} notices that most of the slaves ${he} sees around your penthouse seem to trust you, and hopes that ${he} can <span class="mediumaquamarine">trust you,</span> too.`);
+				slave.trust += 1;
+			}
+		} else {
+			if (slave.trust < 50) {
+				r.push(`${He} notices that most of the slaves ${he} sees around your penthouse seem to fear you, and starts to <span class="gold">fear you,</span> too.`);
+				slave.trust -= 2;
+			}
+		}
+
+		if (V.enduringDevotion > 20) {
+			if (slave.devotion >= -20) {
+				r.push(`${He} sees that most of the slaves ${he} sees around your penthouse like you, and hopes that ${he} can <span class="hotpink">like you</span> a little more ${himself}.`);
+				slave.devotion += 1;
+			}
+		} else {
+			if (slave.devotion <= 50) {
+				r.push(`${He} sees that most of the slaves ${he} sees around your penthouse dislike you, and starts to <span class="mediumorchid">dislike you</span> a little more ${himself}.`);
+				slave.devotion -= 2;
+			}
+		}
+
+		if (V.PC.career === "escort") {
+			r.push(`${He} recognizes you as a whore, leading ${him} to <span class="mediumaquamarine">fear you less;</span> however, ${his} views of you also leave ${him} less willing to <span class="mediumorchid">listen to you.</span>`);
+			slave.devotion -= 10;
+			slave.trust += 10;
+		} else if ((V.PC.career === "servant")) {
+			if (canSee(slave)) {
+				r.push(`Seeing`);
+			} else {
+				r.push(`Having`);
+			}
+			r.push(`you in your`);
+			if (V.PC.title === 0) {
+				r.push(`maid's dress`);
+			} else {
+				r.push(`maid's outfit`);
+			}
+			r.push(`<span class="mediumaquamarine">calms ${his} fears;</span> however, ${he} is unwilling to <span class="mediumorchid">listen to a servant.</span>`);
+			slave.devotion -= 3;
+			slave.trust += 10;
+		} else if ((V.PC.career === "gang")) {
+			r.push(`${He} recognizes you from various crime reports, <span class="gold">filling ${him} with fear</span> over what life under you will be like.`);
+			slave.trust -= 10;
+		}
+
+		if (V.PC.rumor === "force") {
+			if (slave.devotion <= 20) {
+				if (slave.trust > 0) {
+					r.push(`${He} seems to have picked up rumors about your ruthlessness, and is <span class="gold">horrified into obedience.</span>`);
+					slave.trust -= 25;
+				} else if ((slave.trust > -10)) {
+					r.push(`${He} seems to have picked up rumors about your ruthlessness, and is <span class="gold">terrified into obedience.</span>`);
+					slave.trust -= 20;
+				} else if ((slave.trust >= -20)) {
+					r.push(`${He} seems to have picked up rumors about your ruthlessness, and is <span class="gold">frightened into obedience.</span>`);
+					slave.trust -= 15;
+				}
+			}
+		} else if (V.PC.rumor === "diligence") {
+			r.push(`${He} seems to have picked up rumors about your probity, and is <span class="mediumaquamarine">less afraid of you</span> and more <span class="hotpink">willing to like you.</span>`);
+			slave.devotion += 5;
+			slave.trust += 5;
+		}
+
+		if (slave.accent >= 3) {
+			if (slave.trust > 20) {
+				r.push(`During the enslavement process so far, it became obvious to ${him} that ${he} can barely understand enough ${V.language} to understand orders. ${He}'s <span class="gold">frightened</span> by the sudden danger that ${he} won't be able to do what ${he}'s told, even if ${he} wants to.`);
+				slave.trust -= 5;
+			} else if ((slave.devotion <= 20)) {
+				if (slave.intelligence + slave.intelligenceImplant > 50) {
+					r.push(`${He} can barely understand the ${V.language} language, but ${he}'s smart enough to understand everything that's happening to ${him}. Even so, <span class="mediumaquamarine">some fear is deferred,</span> since the harsh realities of being a sex slave may not be clear to ${him} yet.`);
+					slave.trust += 10;
+				} else if (slave.intelligence + slave.intelligenceImplant >= -15) {
+					r.push(`${He} can barely understand enough ${V.language} to understand what's happening to ${him}. This incapacity <span class="mediumaquamarine">defers a great deal of fear,</span> since ${his} exact future hasn't become clear to ${him} yet.`);
+					slave.trust += 10;
+				} else {
+					r.push(`${He} can barely understand ${V.language} at all, and scarcely understands what's happening to ${him}. This leaves ${him} <span class="mediumaquamarine">unaware of how afraid ${he} should be,</span> since ${he}'s too stupid to figure it out without being told.`);
+					slave.trust += 25;
+				}
+			}
+		}
+
+		if (V.arcologies[0].FSSupremacist >= 50) {
+			if (slave.race === V.arcologies[0].FSSupremacistRace) {
+				r.push(`${He} realizes that ${he}'s a member of the favored race in your arcology, and <span class="mediumaquamarine">hopes</span> this will earn ${him} good treatment.`);
+				slave.trust += 2;
+			} else {
+				r.push(`${He} realizes that ${he}'s not a member of the favored race in your arcology, and <span class="gold">fears</span> this will earn ${him} extra abuse.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSSubjugationist >= 50) {
+			if (slave.race !== V.arcologies[0].FSSubjugationistRace) {
+				r.push(`${He} realizes that ${he}'s not one of the people considered subhuman here, and <span class="mediumaquamarine">hopes</span> this will allow ${him} to avoid some abuse.`);
+				slave.trust += 2;
+			} else {
+				r.push(`${He} realizes that ${he}'s one of the people considered subhuman here, and <span class="gold">fears</span> ${he} will be unable to avoid degradation.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSIntellectualDependency >= 50) {
+			if (slave.pregKnown === 1) {
+				r.push(`${He} realizes that your arcology values bearing children and <span class="mediumaquamarine">hopes</span> ${he}'ll be treated gently for being pregnant.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSSlaveProfessionalism >= 50) {
+			if (slave.intelligence + slave.intelligenceImplant >= 75) {
+				r.push(`${He} realizes that your arcology values intelligence and <span class="mediumaquamarine">expects</span> ${his} intellect will prove ${his} worth.`);
+				slave.trust += 2;
+			} else if (slave.intelligence + slave.intelligenceImplant < 10) {
+				r.push(`${He} realizes that your arcology values intelligence and <span class="gold">worries</span> ${he} isn't smart enough to keep up with society's expectations.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSRepopulationFocus >= 50) {
+			if (slave.pregKnown === 1) {
+				r.push(`${He} realizes that your arcology values bearing children and <span class="mediumaquamarine">hopes</span> ${he}'ll be treated gently for being pregnant.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSRestart >= 50) {
+			if (slave.pregKnown === 0) {
+				r.push(`${He} recognizes ${his} place in society and <span class="mediumaquamarine">is glad</span> ${he} isn't pregnant.`);
+				slave.trust++;
+			}
+		}
+
+		if (V.arcologies[0].FSGenderRadicalist >= 50) {
+			if (slave.dick > 0) {
+				if (slave.anus > 0) {
+					r.push(`${He} realizes that your arcology accepts girls with dicks who take it up the ass, and since ${he} qualifies, ${he} <span class="mediumaquamarine">hopes</span> to be well treated here.`);
+					slave.trust += 2;
+				} else {
+					r.push(`${He} realizes that girls with dicks take it up the butt here, and <span class="gold">fears</span> for ${his} virgin anus.`);
+					slave.trust -= 4;
+				}
+			}
+		}
+
+		if (V.arcologies[0].FSGenderFundamentalist >= 50) {
+			if (slave.dick === 0) {
+				r.push(`${He} realizes that ${he} has a chance of conforming to gender roles in your arcology, and <span class="mediumaquamarine">hopes</span> to avoid abuse this way.`);
+				slave.trust += 2;
+			} else {
+				r.push(`${He} realizes that your arcology's culture looks down on ${girl}s like ${him}, and <span class="gold">fears</span> ${he}'ll be abused as a result.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSPaternalist >= 50) {
+			r.push(`${He} realizes that your arcology's citizens look after their slaves, you more than anyone, and <span class="mediumaquamarine">hopes</span> ${he}'ll do well here.`);
+			slave.trust += 2;
+		}
+
+		if (V.arcologies[0].FSDegradationist >= 50) {
+			r.push(`${He} realizes that for ${him}, your arcology is likely to be a hell unending, and is <span class="gold">terrified.</span>`);
+			slave.trust -= 5;
+		}
+
+		if (V.arcologies[0].FSBodyPurist >= 50) {
+			if (slave.boobsImplant > 0 || slave.buttImplant > 0 || slave.waist < -95 || slave.lipsImplant > 0 || slave.faceImplant >= 30) {
+				r.push(`${He} realizes that your arcology disapproves of body modifications like ${hers}, and <span class="gold">fears</span> that ${he} will be subjected to further surgery to purify ${him}.`);
+				slave.trust -= 4;
+			} else {
+				r.push(`${He} realizes that your arcology approves of natural bodies like ${hers}, and <span class="mediumaquamarine">trusts</span> that ${he} will be allowed to keep it that way.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSTransformationFetishist >= 50) {
+			if (slave.boobsImplant > 0 || slave.buttImplant > 0 || slave.waist < -95 || slave.lipsImplant > 0 || slave.faceImplant >= 30) {
+				r.push(`${He} realizes that your arcology approves of body modifications like ${hers}, and <span class="mediumaquamarine">trusts</span> that ${he} will be valued for them.`);
+				slave.trust += 2;
+			} else {
+				r.push(`${He} realizes that your arcology disapproves of natural bodies like ${hers}, and <span class="gold">fears</span> that ${he} will soon be a plastic surgeon's plaything.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSMaturityPreferentialist >= 50) {
+			if (slave.actualAge > 30) {
+				r.push(`${He} realizes that your arcology appreciates older ${girl}s, and <span class="mediumaquamarine">hopes</span> it'll be nicer here.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSYouthPreferentialist >= 50) {
+			if (slave.actualAge > 30) {
+				r.push(`${He} realizes that your arcology has little use for older ${girl}s, and <span class="gold">fears</span> ${he}'ll be abused here.`);
+				slave.trust -= 4;
+			} else if ((slave.actualAge < 18)) {
+				/* begin is a child block */
+				/* Set understands to 0 or 1 to reflect whether ${he} understands. */
+				let understands = 0;
+				if ((slave.origin === "$He was raised in a radical slave school that treated $him from a very young age, up to the point that $he never experienced male puberty.") || (slave.origin === "$He was raised in a radical slave school that treated $him with drugs and surgery from a very young age.") || (slave.origin === "$He was brought up in a radical slave school to match $his twin.")) {
+					understands = 1;
+				} else if (isSexuallyPure(slave)) {
+					if (slave.intelligence > jsRandom(0, 50)) {
+						understands = 1;
+					}
+				} else {
+					if (slave.intelligence > jsRandom(-50, 50)) {
+						understands = 1;
+					}
+				}
+				if (understands < 1) {
+					r.push(`${He} has heard that you like little ${girl}s and is <span class="mediumaquamarine">reassured</span> because ${he} misunderstands what this means.`);
+					slave.trust += 2;
+				} else if ((slave.sexualQuirk === "perverted")) {
+					r.push(`${He} has heard that you like little ${girl}s and <span class="mediumaquamarine">hopes</span> that matching your taste means ${he} will be treated well.`);
+					slave.trust += 2;
+				} else if ((slave.sexualQuirk === "size queen") && V.PC.dick !== 0) {
+					r.push(`${He} has heard that you like little ${girl}s and <span class="hotpink">bites ${his} lip</span> at the thought of how big your dick will be inside ${him}.`);
+					slave.devotion += 2;
+				} else if ((slave.sexualFlaw === "repressed") || (slave.sexualFlaw === "shamefast") || (slave.behavioralFlaw === "devout")) {
+					r.push(`${He} has heard that you like little ${girl}s and is <span class="gold">terrified</span> because ${he} understands exactly what that means.`);
+					slave.trust -= 5;
+				} else if ((slave.sexualFlaw === "hates oral") || (slave.sexualFlaw === "hates anal") || (slave.sexualFlaw === "hates penetration") || (isSexuallyPure(slave) && (50 >= jsRandom(1, 100)))) {
+					r.push(`${He} has heard that you like little ${girl}s and <span class="gold">fears</span> what you might do to ${him}.`);
+					slave.trust -= 4;
+				} else {
+					r.push(`${He} has heard that you like little ${girl}s and <span class="mediumaquamarine">hopes</span> that matching your taste means ${he} will be treated well.`);
+					slave.trust += 2;
+				}
+				/* end is a child block */
+			}
+		}
+
+		if (V.arcologies[0].FSPetiteAdmiration >= 50) {
+			if (heightPass(slave)) {
+				r.push(`${He} realizes that your arcology favors short slaves, and <span class="mediumaquamarine">hopes</span> life will be good here.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSStatuesqueGlorification >= 50) {
+			if (heightPass(slave)) {
+				r.push(`${He} realizes that your arcology favors the tall, and <span class="mediumaquamarine">hopes</span> ${he}'ll retain some dignity.`);
+				slave.trust += 2;
+			} else {
+				r.push(`${He} realizes that your arcology favors the tall, and <span class="gold">anticipates</span> ${his} lacking height to bring ${him} nothing but torment.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSSlimnessEnthusiast >= 50) {
+			if (slave.boobs < 500 && slave.butt < 3 && slave.weight <= 10 && slave.muscles <= 30) {
+				r.push(`${He} realizes that ${he} has a fashionable body for your arcology, and <span class="mediumaquamarine">hopes</span> it'll earn ${him} some kindness.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSAssetExpansionist >= 50) {
+			if (slave.butt > 4 && slave.boobs > 800) {
+				r.push(`${He} realizes that ${he} has a fashionable body for your arcology, and <span class="mediumaquamarine">hopes</span> it'll earn ${him} some kindness.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSPastoralist >= 50) {
+			if (slave.pregKnown === 1 || slave.lactation > 0) {
+				r.push(`${He} realizes that your arcology values slaves with motherly bodies, and <span class="mediumaquamarine">hopes</span> ${he} won't be a low value slave for having one.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSPhysicalIdealist >= 50) {
+			if (slave.muscles > 5) {
+				r.push(`${He} realizes that muscles are respected around here, and though ${he} isn't truly swole, ${he}'s partway there; ${he} <span class="mediumaquamarine">hopes</span> ${he}'ll be kept healthy and strong.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSHedonisticDecadence >= 50) {
+			if (slave.behavioralFlaw === "gluttonous") {
+				r.push(`${He} realizes that fat slaves are preferred around here, and as a glutton, can't wait to <span class="mediumaquamarine">eat ${himself} sick.</span>`);
+				slave.trust += 2;
+			} else if ((slave.weight > 130) && slave.behavioralFlaw !== "anorexic") {
+				r.push(`${He} realizes that fat slaves are preferred around here, and since ${he} is quite rotund already, ${he} <span class="mediumaquamarine">hopes</span> ${he}'ll fit right in. That and if the cafeteria is open 24/7.`);
+				slave.trust += 2;
+			} else if ((slave.weight > 10) && slave.behavioralFlaw !== "anorexic") {
+				r.push(`${He} realizes that chubby slaves are preferred around here, and though ${he} isn't truly fat, ${he}'s partway there; ${he} <span class="mediumaquamarine">hopes</span> ${he}'ll be kept well fed and happy.`);
+				slave.trust += 2;
+			} else if (slave.behavioralFlaw === "anorexic") {
+				r.push(`${He} realizes that fat slaves are preferred around here, and that means ${he}'ll <span class="gold">be made fat too.</span>`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSChattelReligionist >= 50) {
+			if (slave.intelligence + slave.intelligenceImplant < -50) {
+				r.push(`${His} dimwitted mind naturally takes to being told ${his} role as a slave is righteous, and ${he} naïvely <span class="mediumaquamarine">hopes</span> your arcology's religion will protect ${him} from harm.`);
+				slave.trust += 2;
+			} else if (slave.intelligence + slave.intelligenceImplant > 50) {
+				r.push(`${His} intelligent mind <span class="gold">fears</span> the consequences of living in an arcology in which slavery has taken on religious significance.`);
+				slave.trust -= 4;
+			}
+		}
+
+		if (V.arcologies[0].FSRomanRevivalist >= 50) {
+			if (slave.intelligence + slave.intelligenceImplant > 50) {
+				r.push(`Though ${he} knows it's not a truly authentic Roman restoration, ${his} intelligent mind grasps the potential benefits of Roman mores for slaves, and ${he} <span class="mediumaquamarine">hopes</span> life in your arcology will be managed with virtue.`);
+				slave.trust += 2;
+			}
+		} else if (V.arcologies[0].FSAztecRevivalist >= 50) {
+			if (slave.intelligenceImplant >= 15) {
+				r.push(`Though ${he} knows it's not a truly authentic ancient Aztec restoration, ${his} educated mind grasps the potential benefits of ancient Aztec mores for slaves, and ${he} <span class="mediumaquamarine">hopes</span> your arcology will make respectful use of ${his} devotion.`);
+				slave.trust += 2;
+			}
+		} else if (V.arcologies[0].FSEgyptianRevivalist >= 50) {
+			if (slave.intelligenceImplant >= 15) {
+				r.push(`Though ${he} knows it's not a truly authentic ancient Egyptian restoration, ${his} educated mind grasps the potential benefits of ancient Egyptian mores for slaves, and ${he} <span class="mediumaquamarine">hopes</span> your arcology will make good and respectful use of ${his} learning.`);
+				slave.trust += 2;
+			}
+		} else if (V.arcologies[0].FSEdoRevivalist >= 50) {
+			if (slave.intelligenceImplant >= 15) {
+				r.push(`Though ${he} knows it's not a truly authentic feudal Japanese restoration, ${his} educated mind grasps the potential benefits of traditional Japanese mores for slaves, and ${he} <span class="mediumaquamarine">hopes</span> your arcology will treat those who behave well with a modicum of honor.`);
+				slave.trust += 2;
+			}
+		} else if (V.arcologies[0].FSArabianRevivalist >= 50) {
+			if (slave.intelligenceImplant >= 15) {
+				r.push(`Though ${he} knows it's not a truly authentic restoration of the old Caliphate, ${his} educated mind grasps the potential benefits of old Arabian law and culture for slaves, and ${he} <span class="mediumaquamarine">hopes</span> your arcology has absorbed enough of the old wisdom to respect slaves.`);
+				slave.trust += 2;
+			}
+		} else if (V.arcologies[0].FSChineseRevivalist >= 50) {
+			if (slave.intelligenceImplant >= 15) {
+				r.push(`Though ${he} knows it's not a truly authentic ancient Chinese restoration, ${his} educated mind grasps the potential benefits of ancient Confucian philosophy for slaves, and ${he} <span class="mediumaquamarine">hopes</span> your arcology at least maintains some pretense of order and conservatism.`);
+				slave.trust += 2;
+			}
+		}
+
+		if (V.arcologies[0].FSPaternalistLaw === 1) {
+			r.push(`${He}`);
+			if (canSee(slave)) {
+				r.push(`saw`);
+			} else if (canHear(slave)) {
+				r.push(`overheard`);
+			} else {
+				r.push(`passed through`);
+			}
+			r.push(`a good deal of your arcology and its society on ${his} way to your penthouse, and was amazed by all the happy, healthy slaves. ${He} <span class="mediumaquamarine">begins to trust</span> that ${he}'ll be one of them, and <span class="hotpink">anticipates meeting</span> the person who built this place.`);
+			slave.trust += 4;
+			slave.devotion += 4;
+		}
+
+		if (slave.devotion < -50 && slave.rudeTitle !== 1) {
+			if (jsRandom(-100, 0) >= slave.devotion) {
+				slave.rudeTitle = 1;
+				slave.custom.title = setup.badNames.random();
+				slave.custom.titleLisp = lispReplace(slave.custom.title);
+			}
+		}
+		$(p).append(r.join(" "));
+		$(el).append(p);
+		return el;
+	}
+
+	function choices() {
+		const el = new DocumentFragment();
+		let p;
+		let div;
+		let linkArray = [];
+		const hands = hasBothArms(slave) ? "arms" : "arm";
+		const wrists = hasBothArms(slave) ? "wrists" : "wrist";
+		const knees = hasBothLegs(slave) ? "knees" : "knee";
+		const ankles = hasBothLegs(slave) ? "ankles" : "ankle";
+		const feet = hasBothLegs(slave) ? "feet" : "foot";
+
+		function introText(text) {
+			if (text) {
+				App.UI.DOM.appendNewElement("span", p, text, "note");
+			}
+		}
+
+		/**
+		 *
+		 * @param {object} param0
+		 * @param {string} [param0.linkName]
+		 * @param {function(App.Entity.SlaveState):string|DocumentFragment} [param0.result]
+		 * @param {boolean} [param0.requirements]
+		 * @param {string} [param0.note]
+		 */
+		function choice({linkName, result, requirements, note} = {}) {
+			if (requirements === false) {
+				return;
+			}
+			let span = document.createElement("span");
+			span.append(
+				App.UI.DOM.link(
+					linkName,
+					() => {
+						jQuery("#introResult").empty().append(result(slave));
+					}
+				)
+			);
+
+			if (note) {
+				App.UI.DOM.appendNewElement("span", span, ` ${note}`, "note");
+			}
+
+			linkArray.push(span);
+		}
+
+		if (slave.breedingMark === 1) {
+			r.push(`${He} knows ${his} duty in life and playfully splays ${himself} across your desk with ${his} legs spread wide. Unfortunately, ${he} must first be unwrapped before any breeding can begin, so for the time being, ${he} rights ${himself} and makes ${his} way to your crotch to show you just how much ${he} was worth the ¤.`);
+			actX(slave, "oral");
+		} else {
+			if (V.rulesAssistantAuto === 0) {
+				div = document.createElement("div");
+				div.id = "rules";
+				div.append(
+					App.UI.DOM.link(
+						"Apply default rules",
+						() => {
+							jQuery("#rules").empty().append(DefaultRules(slave));
+						}
+					)
+				);
+				if (slave.devotion <= 20) {
+					div.append(`Will not apply many cosmetic and surgical options since ${he} is not obedient`);
+				}
+				el.append(div);
+			}
+			p = document.createElement("p");
+			p.id = "introResult";
+			el.append(p);
+			App.UI.DOM.appendNewElement("span", p, `Now might be a good time to introduce ${him} to life in your stable of sex slaves`, "note");
+
+			switch (slave.origin) {
+				case "$He submitted to enslavement to escape the hard life of an old world whore.":
+					introText(`, and since ${he} wanted to leave the old world...`);
+					choice({
+						linkName: `Brand ${him} on the ${brandTarget} to introduce ${him} to life as a slave whore`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You tell ${him} you'll be marking ${him} as one of your working ${girl}s. ${He} looks resigned as ${he} follows you to the body modification studio, and lets you strap ${him} down with ${his} ${brandTarget} bare. ${He} understands what's coming. You've got ${him} positioned just right${canDoAnal(slave) ? `, so your cock slides up ${his} experienced asshole easily` : ``}. You bring the brand in close so ${he} can feel the radiated heat, which breaks through even ${his} jaded exterior and makes ${him} tighten with fear. When you're close, you apply the brand${canDoAnal(slave) ? `, making the poor whore cinch ${his} sphincter down hard in agony, bringing you to climax` : ``}. ${He} knows you know how to <span class="gold">apply pain,</span> now, and ${he} <span class="mediumorchid">learns to dislike you</span> even as ${his} <span class="red">wound</span> heals.`);
+							if (canDoAnal(slave)) {
+								r.push(VCheck.Anal());
+							}
+							slave.brand[brandTarget] = V.brandDesign.primary;
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							healthDamage(slave, 10);
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He submitted to enslavement to get access to modern prenatal care.":
+					introText(`and since ${he}'s worried about ${his} child...`);
+					choice({
+						linkName: `Manipulate ${his} fear for ${his} pregnancy`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You place a curative injector on your desk, and describe its remarkable medical powers in detail, before mentioning its extreme cost. ${His} face rises at the first part and falls at the second. You tell ${him} that if ${he}'s a perfect sex slave, ${he}'ll get as much as ${he} needs, and that ${he} can start by sucking you off. ${He} grunts a little as ${he} hurries to get ${his} pregnant body down to ${his} knees, but`);
+							if (V.PC.dick > 0) {
+								r.push(`works your cock`);
+							} else if (V.PC.vagina > 0) {
+								r.push(`explores your vagina`);
+							} else {
+								r.push(`licks you to orgasm`);
+							}
+							r.push(`with almost desperate enthusiasm. You stroke ${his} hair comfortingly as ${he} does, and inject the healing dose into ${his} shoulder. ${He} murmurs ${his} <span class="hotpink">gratitude</span> into your dick, but <span class="gold">fears</span> for ${his} pregnancy.`);
+							slave.devotion += 4;
+							slave.trust -= 10;
+							actX(slave, "oral");
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He offered $himself as a slave to escape the horrors a blind $girl faces on the streets.":
+					introText(`and since ${he} loathes ${his} pregnancy...`);
+					choice({
+						linkName: `Abort ${his} child`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You loudly place an innocuous-looking drug injector on your desk, and ${him} think about it for a long moment. Then, you declare exactly what made that sound: abortifacients. After a moment of comprehension, ${his} mood instantly improves. ${He} pledges to <span class="hotpink">submit to you,</span>`);
+							if (V.PC.dick !== 0 && V.PC.vagina !== -1) {
+								r.push(`suck your cock, take it in ${his} pussy, take it up ${his} ass, eat you out,`);
+							} else if (V.PC.dick !== 0) {
+								r.push(`suck your cock, take it in ${his} pussy, take it up ${his} ass,`);
+							} else {
+								r.push(`eat you out, worship you with ${his} pussy, serve you with ${his} ass,`);
+							}
+							r.push(`anything, as long as you <span class="mediumaquamarine">remove ${his} rape baby.</span> You observe that ${he}'ll do all of those things, regardless of what you decide to do about ${his} pregnancy, but for now, you'll ${him} terminate it; ${he} needs all the nutrients for ${himself} right now. ${He} thanks you through ${his} tears.`);
+							slave.devotion += 4;
+							slave.trust += 3;
+							TerminatePregnancy(slave);
+							actX(slave, "abortions");
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He submitted to enslavement for a better chance at survival than $he had as a migrant.":
+					introText(`and since ${he}'s trying to do better than life as a migrant...`);
+					choice({
+						linkName: `Introduce ${him} to anal service`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You carefully and patiently explain to the fearful anal virgin how skilled Free Cities slaves take dick up the butt. ${He} is allowed to go at ${his} own pace, hesitantly progressing from enemata to a single well-lubricated finger to a little plug. After a long while ${he}'s beginning to get aroused, and you bring ${him} over to the couch to spoon. ${He} stiffens with fear but you take your time and just cuddle for a while before gently sodomizing ${him}. ${He} <span class="hotpink">thanks</span> you for being a <span class="mediumaquamarine">kind master,</span> and has <span class="green">learned</span> the basics of taking it up the ass.`);
+							r.push(VCheck.Anal());
+							slave.devotion += 4;
+							slave.trust += 4;
+							slave.anus += 1;
+							slave.skill.anal += 10;
+							return r.join(" ");
+						},
+					});
+
+					choice({
+						linkName: `Initiate ${him} with anal pain`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You haul ${him} wordlessly into the bathroom. In a few minutes ${he} finds ${himself} standing obediently behind you, waiting for your next command with a clean colon and an uncomfortably large buttplug stretching ${his} virgin ass. Once it's been in long enough to prevent any damage, you push ${his} fearful form over the desk and pull it free. ${He} squeals with more embarrassment than pain at the feeling of ${his} first sodomy, but before long you're pounding ${him} hard enough that all ${he} can do is moan and desperately wonder when you'll be done with ${his} poor little butt. You keep ${him} around all day; by night, ${his} asshole is well broken in and <span class="mediumorchid">so is ${his} spirit.</span> ${He} <span class="gold">fears</span> you now, knowing this is only the beginning.`);
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							slave.anus += 1;
+							r.push(VCheck.Anal());
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He begged to be enslaved to avoid starvation.":
+					introText(`and since ${he} enslaved ${himself} for a bite to eat...`);
+					choice({
+						linkName: `Allow ${him} to stuff ${himself}`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You point ${him} toward the nearest slave food dispenser and tell ${him} to drink as much as ${he} likes. ${He} <span class="hotpink">eagerly complies,</span> quickly bloating ${his} long-deprived belly; between the psychoactive effects of slave food, the sudden end of ${his} long fast, and the look of <span class="mediumaquamarine">slightly spaced-out contentment</span> on ${his} face as ${he} rubs ${his} sated belly, you have a feeling that you're witnessing the beginning of an <span class="red">overeating habit</span>.`);
+							() => {
+								slave.devotion += 10;
+								slave.trust += 10;
+								if (slave.behavioralQuirk === "fitness") {
+									slave.behavioralQuirk = "none";
+								}
+								slave.behavioralFlaw = "gluttonous";
+								slave.inflation = 1;
+								slave.inflationType = "food";
+								slave.inflationMethod = 1;
+							};
+							return r.join(" ");
+						},
+
+					});
+					break;
+				case "$He submitted to enslavement as $his only way to obtain surgery to transform $him into a woman.":
+					introText(`and since ${he} came here for surgery...`);
+					choice({
+						linkName: `Give ${him} ${his} surgery`,
+						result: function(slave) {
+							const r = [];
+							r.push(`When ${he}`);
+							if (canHear(slave)) {
+								r.push(`hears`);
+							} else {
+								r.push(`learns`);
+							}
+							r.push(`${he}'ll be heading to surgery immediately, ${he} bursts into <span class="hotpink">tears of gratitude</span> and makes to run around your desk to hug you before checking ${himself}. ${He} clearly doesn't want to put a foot wrong and isn't sure it would be appropriate. You solve ${his} dilemma by meeting ${him} with an embrace. ${He} <span class="mediumaquamarine">cries into your chest</span> and promises to be your best slave. The surgery does affect ${his} <span class="red">health</span> a little.`);
+							slave.devotion += 15;
+							surgeryDamage(slave, 10);
+							slave.trust += 10;
+							slave.vagina = 0;
+							slave.dick = 0;
+							slave.balls = 0;
+							return r.join(" ");
+						},
+					});
+					choice({
+						linkName: `Use ${him} as ${he} is`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You announce that ${he}'ll have to earn ${his} surgery — if ${he} ever gets it at all. ${He} only has time for a moment of shock and betrayal before ${he} finds ${his} face jammed into the cushions of your office couch. ${He} struggles, outraged, but only manages to avoid sodomy for a moment. ${His} resistance only makes it harder on ${himself} as you take ${his} anus without mercy. By the third time you buttfuck ${him} ${he}'s learned to <span class="hotpink">relax and let it happen.</span>`);
+							r.push(VCheck.Anal());
+							slave.devotion += 4;
+							slave.anus += 1;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He submitted to enslavement out of a misguided desire to join a sexually libertine society.":
+					introText(`and since ${he}'s looking for a sexually libertine society...`);
+					choice({
+						linkName: `Disabuse ${him} of ${his} silly ideas`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You rise from your desk and move in close. ${He} turns to face you but you roughly spin ${him} around. You haul ${him} up on tiptoe so you can use ${him} standing. As you get your`);
+							if (V.PC.dick !== 0) {
+								r.push(`cock`);
+							} else {
+								r.push(`strap-on`);
+							}
+							r.push(`lined up with ${his} sissy ass ${he} starts to struggle and protest, asking you to fuck in a more comfortable position. You tell ${him} to keep ${his} whore mouth shut, and administer an awful slap when ${he} tries to keep talking. ${His} stupid illusions about life as a sex slave <span class="mediumorchid">melt away</span> with the burning sensation of a dick forcing its way up ${his} ass at a harsh angle. ${He} wilts a little as you abuse ${his} butt, and afterward, ${he}'s in tears as ${he} stumbles to the bathroom to wash ${his} fuckhole. ${He}'s regained ${his} composure by the time ${he} gets out but <span class="gold">breaks down</span> when you tell ${him} to present ${his} asshole again.`);
+							r.push(VCheck.Anal());
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							return r.join(" ");
+						},
+						get requirements() { return (canDoAnal(slave)); }
+					});
+					choice({
+						linkName: `Cruelly castrate ${him}`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You rise from your desk and move in close, wordlessly dominating ${him} without touch, tempting and overawing ${him} until ${he}'s desperate with desire, ${his} prick stiff as iron. ${He} follows you willingly into the autosurgery and even allows you to strap ${him} in, face-down, without comment. ${His} fuckhole welcomes your`);
+							if (V.PC.dick !== 0) {
+								r.push(`cock`);
+							} else {
+								r.push(`strap-on`);
+							}
+							r.push(`and ${he} gasps with pleasure. ${He} climaxes with indecent speed, dripping ${his} cum onto the surgery floor. You keep fucking ${him}, but lean forward to whisper to ${him} that that was ${his} last hard-on. ${He}'s completely confused and says nothing, but gradually realizes what the numb feeling around ${his} ballsack means. ${He} <span class="gold">screams with horror</span> and <span class="mediumorchid">sobs disconsolately</span> as the autosurgery disengages from ${his} clipped genitals and you disengage from ${his}`);
+							if (V.PC.dick !== 0) {
+								r.push(`cum-filled`);
+							} else {
+								r.push(`wilting`);
+							}
+							r.push(`butthole. ${He} gingerly stumbles back to your office with you and, without even being ordered to, arranges ${himself} on the couch with ${his} fuckhole ready. The gelding does affect ${his} <span class="red">health</span> somewhat.`);
+							r.push(VCheck.Anal());
+							slave.devotion -= 10;
+							slave.trust -= -10;
+							surgeryDamage(slave, 10);
+							slave.balls = 0;
+							return r.join(" ");
+						},
+						get requirements() { return ((slave.indentureRestrictions <= 0) && (V.seeExtreme === 1)); }
+					});
+					break;
+				case "$He asked to be enslaved out of naïve infatuation with you.":
+					introText(`and since ${he} is already infatuated with you...`);
+					choice({
+						linkName: `Let ${him} show you what ${he} can do`,
+						result: function(slave) {
+							const r = [];
+							let temp;
+							if (slave.vagina === 1) {
+								temp = jsRandom(1, 100);
+							} else {
+								temp = 0;
+							}
+							if (temp > 50) {
+								r.push(VCheck.Vaginal());
+							} else {
+								r.push(VCheck.Anal());
+							}
+							r.push(`You tell ${him} that slaves working in your penthouse are all expected to please you, and ${he}'ll have to impress you. ${He} smiles and even giggles a little, standing to strip off ${his} clothes, taking ${his} time and showing off ${his} fresh body. ${He} gets lewder and lewder, displaying youthful libido basking in the gaze of ${his} crush. ${He} slowly becomes more and more desperate to entice you, so you ${him} keep going out of curiosity, to see how far ${he}'ll go. ${He} goes to the point of reclining on the couch with ${his} legs back, spreading ${himself} and masturbating, and when that fails to get you out of your chair, ${he} begins to wink ${his}`);
+							if (temp > 50) {
+								r.push(`pussy`);
+							} else {
+								r.push(`anus`);
+							}
+							r.push(`meaningfully. ${He}'s clearly got a good idea of what many men like, and you head over and take ${his} tight`);
+							if (temp > 50) {
+								r.push(`pussy.`);
+							} else {
+								r.push(`asshole.`);
+							}
+							r.push(`You do it gently, making sure ${he} enjoys ${himself}, and ${he} becomes <span class="mediumaquamarine">very hopeful</span> that ${he} was wrong to doubt you, and that ${he} really can <span class="hotpink">be close to</span> the object of ${his} infatuation. ${He} bounces up off the couch afterward to wiggle ${his} butt at you, and even blows you a kiss as you get back to work.`);
+							slave.devotion += 4;
+							slave.trust += 4;
+
+							return r.join(" ");
+						},
+
+						get requirements() { return (canDoAnal(slave) || canDoVaginal(slave)); }
+					});
+					choice({
+						linkName: `Make sure ${he} knows ${he} made a mistake`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You tell ${him} that slaves working in your penthouse are all expected to please you, and ${he} nods eagerly. ${He} strips, only starting to look doubtful again when you brusquely order ${him} to hurry up. You order ${him} to get down on the ground with ${his} face up, ${his} shoulders against the floor; ${he} does, and then you order ${him} to put ${his} legs over ${his} head. ${He} looks puzzled but does, flipping ${himself} flexibly up until ${he}'s doing a shoulder stand, bent double with ${his} crotch over ${his} face. You stand over ${him} and shove`);
+							if (V.PC.dick !== 0) {
+								r.push(`your cock`);
+							} else {
+								r.push(`a strap-on`);
+							}
+							r.push(`down and into ${his} asshole. This is an advanced anal position and`);
+							if (slave.anus > 0) {
+								r.push(`although ${he}'s clearly no`);
+							} else {
+								r.push(`even for an`);
+							}
+							r.push(`anal virgin it's well beyond ${him}. ${He} does ${his} best but tears begin to streak ${his} cheeks. After a while you tire of the position and flip ${him} over onto ${his} face to assrape ${him} doggy style. This allows you to whisper into ${his} ear that ${his} holes are your property now, to use, to abuse, or to sell. ${He} <span class="mediumorchid">sobs in despair</span> at the latter, knowing ${he}'s <span class="gold">thrown ${his} life away.</span>`);
+							r.push(VCheck.Anal());
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							return r.join(" ");
+						},
+						get requirements() { return (canDoAnal(slave)); }
+					});
+					break;
+				case "$He asked to be enslaved in the hope you'd treat a fellow woman well.":
+					introText(`and since ${he}'s hoping to protect someone...`);
+					choice({
+						linkName: `Build a connection with ${him}`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You ask conversationally what experience ${he} has with women. ${He} takes a while to understand your point, but once ${he} does, ${he} blushes and says,`);
+							r.push(Spoken(slave, `"Uh, I messed around in school once or twice, but since then, I've never —"`));
+							r.push(`and is then quiet, because you're making out with ${him}. ${He}'s surprised and stiffens for a moment but perceptibly thinks things through and realizes ${he}'s yours now, and then ${he} relaxes to let you have your way. You push ${his} boundaries pretty far, and before long you've got ${him} down`);
+							if (hasBothLegs(slave)) {
+								r.push(`on ${his} knees`);
+							} else {
+								r.push(`under your desk`);
+							}
+							r.push(`eating you out. ${He}'s badly discomfited but does ${his} best; ${he}'s <span class="mediumaquamarine">hopeful</span> that ${he} can do this, and slave life won't be so bad.`);
+							slave.trust += 4;
+							actX(slave, "oral");
+							return r.join(" ");
+						},
+					});
+					choice({
+						linkName: `Put ${him} in ${his} place`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You stand up and tell ${him} to strip. ${He} hesitates, staring at you in dawning apprehension, until ${he}`);
+							if (canSee(slave)) {
+								r.push(`sees`);
+							} else {
+								r.push(`realizes`);
+							}
+							r.push(`that you're stepping into a strap-on. ${He} unconsciously takes a step backward, but that's as far as ${he} gets before you cover the ground between you, grab ${him} by the throat, and push ${his} back until ${he} falls onto the couch. You drag the head of the fake phallus down over ${his} pussylips, and ${he} shivers, but you keep going, telling ${him} that you'll only be fucking ${him} there when ${he}'s good. When ${he}'s bad, you'll assfuck ${him}.`);
+							if (hasBothEyes(slave)) {
+								r.push(`${His} eyes fly open and ${he}`);
+							} else {
+								r.push(`${He}`);
+							}
+							r.push(`tries to struggle, but you give ${him} a warning slap and then push yourself home. ${He} starts to cry, more from hopelessness than anal pain, knowing that <span class="mediumorchid">you're a slaveowner like any other,</span> more likely to <span class="gold">abuse ${him}</span> than be sympathetic.`);
+							r.push(VCheck.Anal());
+							slave.devotion -= 5;
+							slave.trust -= 5;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He asked to be enslaved since $he felt you were $his only hope of becoming a prettier woman.":
+					introText(`and since ${he} desperately wants to be a prettier woman`);
+					choice({
+						linkName: `Start by fixing that voice of ${hers}`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You schedule ${him} for some vocal surgery. You have ${him} brought in to be inspected the next day, and though ${he}'s healing well, ${he}'s not to speak yet. ${He}'s sore enough to obey the stricture, but when ${he} stands naked before your desk ${he} clearly wants to communicate something. You approach ${him} and place a finger on ${his} fake lips, shushing ${him}, and you tell ${him} you understand. You kiss ${him} and push ${him} backward until ${he} falls onto the couch; ${he} smiles <span class="mediumaquamarine">trustingly</span> and pulls ${his} legs apart and back, offering you ${his} asshole. ${He} has a stiff hard-on, a clear indication that ${he}'s honestly <span class="hotpink">attracted to you,</span> and though ${he} still doesn't speak, ${he} makes a sore little whinny of pleasure when you enter ${his} welcoming butt.`);
+							r.push(VCheck.Anal());
+							slave.devotion += 4;
+							slave.trust += 4;
+							slave.voice += 1;
+							surgeryDamage(slave, 10);
+							return r.join(" ");
+						},
+					});
+
+					choice({
+						linkName: `Let ${him} know ${he}'ll have to earn ${his} rewards`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You complete the induction and then perform a thorough inspection, noting down each area in which surgery or drugs could improve ${his} body. ${He} understands what you're doing and can barely contain ${his} excitement, but you tell ${him} that ${he}'ll have to earn such improvements. ${His} face falls. You reassure ${him} that it won't take the years ${he} would have had to work to afford such things ${himself}, but that ${he} needs to be a good slave, and soon, for you to spend resources on ${him}. You ${him} start by taking a rough buttfuck, bent over the desk. ${His} ass is tight and you are not merciful. ${He} gasps and moans but takes it all the same, <span class="mediumaquamarine">trusting</span> that ${he}'ll eventually be rewarded with transformation.`);
+							r.push(VCheck.Anal());
+							slave.trust += 10;
+							return r.join(" ");
+						},
+						get requirements() { return canDoAnal(slave); }
+					});
+					break;
+				case "$He was transformed and enslaved after $he fell into debt to you.":
+					introText(`and since ${he}'s so deeply in debt to you...`);
+					choice({
+						linkName: `Introduce ${him} to getting fucked`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You stand up, your`);
+							if (V.PC.dick !== 0) {
+								r.push(`erection`);
+								if (V.PC.vagina !== -1) {
+									r.push(`and the pussy beneath it`);
+								}
+							} else {
+								r.push(`strap-on`);
+							}
+							r.push(`suddenly becoming visible to ${him}, and order ${him} in a conversational tone of voice to`);
+							if (hasAnyLegs(slave)) {
+								r.push(`kneel`);
+							} else {
+								r.push(`get`);
+							}
+							r.push(`on the couch and spread ${his} buttocks. The true nature of ${his} new life crashes down on ${him} in a moment, and ${he} cannot decide whether to run, to rage, or to cry. You solve ${his} dilemma for ${him} by seizing ${him} and flinging ${him} toward the couch, administering practiced cracks of your palm to ${his} ass until ${he} complies. ${He} sobs as ${he} feels the burning sensation of ${his} <span class="lime">anal virginity being taken,</span> knowing that ${he} can now be <span class="gold">used at will</span> and is now subject to the whims of someone who has shown themselves <span class="mediumorchid">willing to rape ${his} anus.</span>`);
+							r.push(VCheck.Anal());
+							slave.devotion -= 5;
+							slave.trust -= 5;
+							slave.anus = 1;
+							return r.join(" ");
+						},
+						get requirements() { return (canDoAnal(slave)); }
+					});
+					break;
+				case "$He offered $himself to you as a slave to escape a life of boredom.":
+					introText(`and since ${he} was so bored...`);
+					choice({
+						linkName: `Make sure ${he} enjoys ${his} first butt stuff`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You make no direct answer to ${his} nervous question about ${his} virgin butt, though you do wonder how ${he} was so selfish as to be a Free Cities party ${girl} without giving up ${his} asshole nightly. Instead, you ask ${him} about ${himself}, ensuring that ${he} learns the proper way to answer questions from ${his} ${WrittenMaster(slave)}. ${He} answers readily, even when you turn the subject to ${his} sexual experiences, and ${he} starts to get visibly aroused. Before long, ${he} finds ${himself} sitting on your lap on the couch, making out with you while ${he} gives you a handjob. ${He} stiffens a little when you begin to play with ${his} butthole, but you take your time. Soon ${he}'s taking a finger, and then two, and before long, ${he}'s riding`);
+							if (V.PC.dick !== 0) {
+								r.push(`your cock.`);
+							} else {
+								r.push(`a strap-on.`);
+							}
+							r.push(`${He} doesn't climax to ${his} <span class="lime">first buttsex,</span> but ${he} is learning to <span class="mediumaquamarine">trust</span> that you won't hurt ${him}.`);
+							r.push(VCheck.Anal());
+							slave.trust += 10;
+							slave.anus = 1;
+							return r.join(" ");
+						},
+						get requirements() { return (canDoAnal(slave)); }
+					});
+					break;
+				case "$He sold $himself into slavery out of fear that life on the streets was endangering $his pregnancy.":
+					introText(`and since ${he} is here to protect ${his} pregnancy...`);
+					choice({
+						linkName: `Make sure ${he} enjoys ${his} first anal sex`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You introduce ${him} to obedience and proper manners regarding ${his} ${WrittenMaster(slave)} before sending ${him} off for a physical. That night, ${he}'s returned to your room, and finds you doing business on a tablet in bed. ${He} looks doubtful, but obeys when you direct ${him} to get into bed${(V.PC.dick === 0) ? `, even after ${he} realizes you're wearing a strap-on` : ``}. You turn out the light and spoon ${him} from behind, kissing ${his} neck and ears, cupping ${his} swollen breasts, and running your hands across ${his} pregnant belly with its taut ${slave.skin} skin. ${He}'s awkward at first but ${his} body responds to the tenderness. Before long ${he}'s humping ${his} pussy back and forth against`);
+							if (V.PC.dick !== 0) {
+								r.push(`your cock.`);
+							} else {
+								r.push(`the strap-on.`);
+							}
+							r.push(`You tell ${him} no, not there, and begin to gently work`);
+							if (V.PC.dick !== 0) {
+								r.push(`your dickhead`);
+							} else {
+								r.push(`its tip`);
+							}
+							r.push(`up ${his} tight but relaxed ass. ${He}'s unsure of ${himself}, but you keep ${him} nice and relaxed. ${He} doesn't climax to ${his} <span class="lime">first buttsex,</span> but ${he} is learning to <span class="mediumaquamarine">trust</span> that you won't hurt ${him}.`);
+							r.push(VCheck.Anal());
+							slave.trust += 10;
+							slave.anus = 1;
+							return r.join(" ");
+						},
+						get requirements() { return (canDoAnal(slave)); }
+					});
+					break;
+				case "$He offered $himself to you as a slave to escape the hard life of a free whore.":
+					introText(`and since ${he} is trying to escape the hard life of a free whore...`);
+					choice({
+						linkName: `Clean up ${his} whorish appearance`,
+						result: function(slave) {
+							const r = [];
+							r.push(`${He}'s totally unsurprised when you send ${him} to the salon for a makeover. It takes several days of work before ${he}'s brought back in for another inspection; when ${he} arrives, you wordlessly point ${him} to a full length mirror. ${He}`);
+							if (canSee(slave)) {
+								r.push(`sees ${himself}`);
+							} else {
+								if (hasAnyArms(slave)) {
+									r.push(`tenderly uses ${his} hand`);
+									if (hasBothArms(slave)) {
+										r.push(`s`);
+									}
+									r.push(`and finds ${himself}`);
+								} else {
+									r.push(`stoically waits while you vividly describe ${his} new appearance. One`);
+								}
+							}
+							r.push(`without tattoos, a hooker's haircut and piercings, a conventionally pretty ${girl} with subtle implants and a clean appearance. ${He} gasps`);
+							if (hasAnyArms(slave)) {
+								r.push(`and covers ${his} mouth with a hand,`);
+							} else {
+								r.push(`but quickly closes ${his} mouth,`);
+							}
+							r.push(`and then suddenly bursts into tears. "Thank you, ${Master}," ${he} sobs. "I never would have thought."`);
+							if (hasAnyArms(slave)) {
+								r.push(`${He} reaches out to touch ${his} reflection.`);
+							} else {
+								r.push(`${He} pauses for just a moment.`);
+							}
+
+							if (canSee(slave)) {
+								r.push(`"I look`);
+							} else {
+								r.push(`"I feel`);
+							}
+							r.push(`like a nice ${girl}." ${He} is <span class="hotpink">grateful to you</span> for`);
+							if (canSee(slave)) {
+								r.push(`showing ${him}`);
+							} else {
+								if (hasAnyArms(slave)) {
+									r.push(`letting ${him} feel`);
+								} else {
+									r.push(`detailing`);
+								}
+							}
+							r.push(`this new side of ${himself}, and has <span class="mediumaquamarine">begun to trust</span> that being your slave will be less degrading than being a free prostitute.`);
+							slave.trust += 4;
+							slave.devotion += 4;
+							slave.boobs -= slave.boobsImplant;
+							slave.boobsImplant = 0;
+							slave.boobsImplantType = "none";
+							slave.lips -= slave.lipsImplant;
+							slave.lipsImplant = 0;
+							slave.butt -= slave.buttImplant;
+							slave.buttImplant = 0;
+							slave.buttImplantType = "none";
+							slave.lipsPiercing = 0;
+							slave.tonguePiercing = 0;
+							slave.nosePiercing = 0;
+							slave.eyebrowPiercing = 0;
+							slave.navelPiercing = 0;
+							slave.nipplesPiercing = 0;
+							slave.clitPiercing = 0;
+							slave.hStyle = "neat";
+							slave.custom.tattoo = " ";
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He sold $himself into slavery to escape life on the streets.":
+					introText(`and since ${he} is trying to escape life on the streets...`);
+					choice({
+						linkName: `Show off ${his} tits`,
+						result: function(slave) {
+							const r = [];
+							r.push(`You send ${him} out to be cleaned up and inducted, but have ${him} brought back afterward. You point out a clothes box on the couch and tell ${him} to get dressed, since you're about to make a tour of the club, and ${he}'ll be accompanying you. ${He} obeys without comment, but gasps with shock to find that ${he}'s been given a string sling bikini. ${He} climbs into it hesitantly, and finds that not only does its string bottom ride up between ${his} pussylips, it does not cover ${his} nipples at all: the strings part to either side of the nipples to let them stick through, bare. This can only generously be called clothing. ${He} accepts this with an obvious internal sigh, however, and walks dutifully behind you, ${his} bare feet slapping along as ${his} big butt bounces and ${his} tits constantly fall out of ${his} strings.`);
+							if (slave.behavioralFlaw !== "shamefast") {
+								r.push(`After a while, though, ${he} notices that ${he}'s getting a lot of very positive attention, and even starts to strut it a little. When you get back to the penthouse, you ask ${him} how ${he} felt. ${He} blushes. "Kinda hot, ${Master}" ${he}`);
+								r.push(`says, embarrassed.`);
+								r.push(Spoken(slave, `"I didn't think an older ${girl} like me would get so many stares."`));
+
+								r.push(`You reach out to grope ${his} tits,   and tell ${him} that with breasts like ${hers}, it's not surprising. ${He} <span class="hotpink">likes you</span> for liking ${his} body, and has <span class="mediumaquamarine">begun to trust</span> that even though ${he}'s old, ${he} can find a place under you.`);
+								slave.trust += 4;
+								slave.devotion += 4;
+							} else {
+								r.push(`After a while, though, ${he} notices that ${he}'s getting a lot of very positive attention, but ${he} remains embarrassed. ${His} gait is clumsy and hesitant, and ${he} almost trips over ${himself} with mortification. When you get back to the penthouse, you ask ${him} how ${he} felt. ${He} blushes furiously. "T-terrible, ${Master}," ${he} says with feeling.`);
+								r.push(Spoken(slave, `"P-please, I'll`));
+								r.push(`span class="hotpink">d-do anything you want</span> <span class="gold">`);
+								r.push(Spoken(slave, `as long as you don't m-make me do that again.`));
+								r.push(r.pop() + `</span>)`);
+								r.push(Spoken(slave, `Anything."`));
+								r.push(`${He} thinks frantically, obviously trying to come up with the most abject idea ${he} can.`);
+								r.push(Spoken(slave, `"I'll b-be a whore! J-just as long as I can do it, you know, in a room."`));
+								slave.trust -= 4;
+								slave.devotion += 4;
+							}
+
+							return r.join(" ");
+						},
+					});
+					break;
+
+				// Written by DrPill, coded by Boney M
+				case "$He was recruited into your service by $his older sibling.":
+					introText(`and since ${he} was recruited by ${his} older ${getPronouns(slave2).sister}...`);
+					choice({
+						get linkName() {
+							const {sister2} = getPronouns(slave2).appendSuffix('2');
+							return `Use ${his} big ${sister2} as an example`;
+						},
+						result: function(slave) {
+							const r = [];
+							const {He2, he2, his2, him2} = getPronouns(slave2).appendSuffix('2');
+							r.push(`Your new slave appears `);
+							if (slave.devotion < -10) {
+								r.push(`reluctant to assume ${his} new duties.`);
+							} else {
+								r.push(`unsure what ${his} new duties are.`);
+							}
+							r.push(`You gesture towards ${slave2.slaveName}. ${He2} is `);
+							if (slave2.intelligence + slave2.intelligenceImplant > 15) {
+								r.push(`bright enough`);
+							} else if ((slave2.skill.vaginal + slave2.skill.anal + slave2.skill.oral) > 100) {
+								r.push(`skilled enough`);
+							} else {
+								r.push(`obedient enough`);
+							}
+							r.push(` to understand you mean a demonstration is in order. ${slave2.slaveName} starts things off with a `);
+							if (slave2.skill.entertainment >= 100) {
+								r.push(`masterful`);
+							} else if (slave2.skill.entertainment > 10) {
+								r.push(`skillful`);
+							} else {
+								r.push(`passable`);
+							}
+							r.push(` striptease, culminating in ${him2} `);
+							if (slave2.anus > 0 && slave2.fetish !== "cumslut") {
+								r.push(`bending over`);
+							} else {
+								r.push(`kneeling`);
+							}
+							r.push(` in front of you. ${He2} eagerly moans as you enter ${him2}, begging for your seed`);
+							if (slave2.energy > 95) {
+								r.push(` like the slut ${he2} is`);
+							} else if (slave2.skill.whoring > 30) {
+								r.push(` like the whore ${he2} is`);
+							} else if ((slave2.assignment === "serve in the master suite") || (slave2.assignment === "please you")) {
+								r.push(` like the fucktoy ${he2} is`);
+							}
+							r.push(`. As you finish, ${he2} `);
+							if (slave2.fetish === "cumslut") {
+								r.push(`opens ${his2} mouth and savors your gift, thanking you once ${he2}'s swallowed enough to be able to talk again`);
+							} else if ((slave2.fetish === "buttslut") || (slave2.fetish === "submissive")) {
+								r.push(`collapses on the floor with ${his2} ass high in the air, thanking you for painting ${his2} hole white`);
+							} else {
+								r.push(`thanks you`);
+							}
+							r.push(`. Witnessing this display of servitude from ${his} big _sister2 <span class="hotpink">eases ${slave.slaveName} into ${his} new life,</span> and <span class="mediumaquamarine">gives ${him} hope</span> ${he} can find a place here.`);
+							if (slave.anus > 0 && slave.fetish !== "cumslut") {
+								actX(slave2, "anal");
+							} else {
+								actX(slave2, "oral");
+							}
+							slave.devotion += 4;
+							slave.trust += 4;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He was recruited into your service by $his younger sibling.":
+					introText(`and since ${he} was recruited by ${his} younger ${getPronouns(slave2).sister}...`);
+					choice({
+						get linkName() {
+							const {sister2} = getPronouns(slave2).appendSuffix('2');
+							return `Use ${his} little ${sister2} as an example`;
+						},
+						result: function(slave) {
+							const r = [];
+							const {He2, he2, his2, him2} = getPronouns(slave2).appendSuffix('2');
+							r.push(`Your new slave appears `);
+							if (slave.devotion < -10) {
+								r.push(`reluctant to assume ${his} new duties`);
+							} else {
+								r.push(`unsure what ${his} new duties are`);
+							}
+							r.push(`. You gesture towards ${slave2.slaveName}. ${He2} is `);
+							if (slave2.intelligence + slave2.intelligenceImplant > 15) {
+								r.push(`bright enough`);
+							} else if (((slave2.skill.vaginal + slave2.skill.anal + slave2.skill.oral) > 100)) {
+								r.push(`skilled enough`);
+							} else {
+								r.push(`obedient enough`);
+							}
+							r.push(` to understand you mean a demonstration is in order. ${slave2.slaveName} starts things off with a `);
+							if (slave2.skill.entertainment >= 100) {
+								r.push(`masterful`);
+							} else if (slave2.skill.entertainment > 10) {
+								r.push(`skillful`);
+							} else {
+								r.push(`passable`);
+							}
+							r.push(` striptease, culminating in ${him2} `);
+							if (slave2.anus > 0 && slave2.fetish !== "cumslut") {
+								r.push(`bending over`);
+							} else {
+								r.push(`kneeling`);
+							}
+							r.push(` in front of you. ${He2} eagerly moans as you enter ${him2}, begging for your seed`);
+							if (slave2.energy > 95) {
+								r.push(` like the slut ${he2} is`);
+							} else if (slave2.skill.whoring > 30) {
+								r.push(` like the whore ${he2} is`);
+							} else if ((slave2.assignment === "serve in the master suite") || (slave2.assignment === "please you")) {
+								r.push(` like the fucktoy ${he2} is`);
+							}
+							r.push(`. As you finish, ${he2} `);
+							if (slave2.fetish === "cumslut") {
+								r.push(`opens ${his2} mouth and savors your gift, thanking you once ${he2}'s swallowed enough to be able to talk again`);
+							} else if ((slave2.fetish === "buttslut") || (slave2.fetish === "submissive")) {
+								r.push(`collapses on the floor with ${his2} ass high in the air, thanking you for painting ${his2} hole white`);
+							} else {
+								r.push(`thanks you`);
+							}
+							r.push(`. Witnessing this display of servitude from ${his} little _sister2 <span class="hotpink">eases ${slave.slaveName} into ${his} new life,</span> and <span class="mediumaquamarine">gives ${him} hope</span> ${he} can find a place here.`); if (slave.anus > 0 && slave.fetish !== "cumslut") {
+								actX(slave2, "anal");
+							} else {
+								actX(slave2, "oral");
+							}
+							slave.devotion += 4;
+							slave.trust += 4;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He was recruited into your service by $his twin.":
+					introText(`and since ${he} was recruited by ${his} twin...`);
+					choice({
+						get linkName() {
+							const {sister2} = getPronouns(slave2).appendSuffix('2');
+							return `Use ${his} ${sister2} as an example`;
+						},
+						result: function(slave) {
+							const r = [];
+							const {He2, he2, his2, him2} = getPronouns(slave2).appendSuffix('2');
+							r.push(`Your new slave appears `);
+							if (slave.devotion < -10) {
+								r.push(`reluctant to assume ${his} new duties.`);
+							} else {
+								r.push(`unsure what ${his} new duties are.`);
+							}
+							r.push(`You gesture towards ${slave2.slaveName}. ${He2} is `);
+							if (slave2.intelligence + slave2.intelligenceImplant > 15) {
+								r.push(`bright enough`);
+							} else if (((slave2.skill.vaginal + slave2.skill.anal + slave2.skill.oral) > 100)) {
+								r.push(`skilled enough`);
+							} else {
+								r.push(`obedient enough`);
+							}
+							r.push(` to understand you mean a demonstration is in order. ${slave2.slaveName} starts things off with a `);
+							if (slave2.skill.entertainment >= 100) {
+								r.push(`masterful`);
+							} else if (slave2.skill.entertainment > 10) {
+								r.push(`skillful`);
+							} else {
+								r.push(`passable`);
+							}
+							r.push(` striptease, culminating in ${him2} `);
+							if (slave2.anus > 0 && slave2.fetish !== "cumslut") {
+								r.push(`bending over`);
+							} else {
+								r.push(`kneeling`);
+							}
+							r.push(` in front of you. ${He2} eagerly moans as you enter ${him2}, begging for your seed`);
+							if (slave2.energy > 95) {
+								r.push(` like the slut ${he2} is`);
+							} else if (slave2.skill.whoring > 30) {
+								r.push(` like the whore ${he2} is`);
+							} else if ((slave2.assignment === "serve in the master suite") || (slave2.assignment === "please you")) {
+								r.push(` like the fucktoy ${he2} is`);
+							}
+							r.push(`. As you finish, ${he2} `);
+							if (slave2.fetish === "cumslut") {
+								r.push(`opens ${his2} mouth and savors your gift, thanking you once ${he2}'s swallowed enough to be able to talk again`);
+							} else if ((slave2.fetish === "buttslut") || (slave2.fetish === "submissive")) {
+								r.push(`collapses on the floor with ${his2} ass high in the air, thanking you for painting ${his2} hole white`);
+							} else {
+								r.push(`thanks you`);
+							}
+							r.push(`. Witnessing this display of servitude from ${his} twin _sister2 <span class="hotpink">eases ${slave.slaveName} into ${his} new life,</span> and <span class="mediumaquamarine">gives ${him} hope</span> ${he} can find a place here.`);
+							if (slave.anus > 0 && slave.fetish !== "cumslut") {
+								actX(slave2, "anal");
+							} else {
+								actX(slave2, "oral");
+							}
+							slave.devotion += 4;
+							slave.trust += 4;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He was recruited into your service by $his parent.":
+					introText(`and since ${he} was recruited by ${his} parent...`);
+					choice({
+						linkName: `Use ${his} parent as an example`,
+						result: function(slave) {
+							const r = [];
+							const {He2, he2, his2, him2} = getPronouns(slave2).appendSuffix('2');
+							r.push(`You gesture towards ${slave2.slaveName}. ${He2} is `);
+							if (slave2.intelligence + slave2.intelligenceImplant > 15) {
+								r.push(`bright enough`);
+							} else if ((slave2.skill.vaginal + slave2.skill.anal + slave2.skill.oral) > 100) {
+								r.push(`skilled enough`);
+							} else {
+								r.push(`obedient enough`);
+							}
+							r.push(` to understand you mean a demonstration is in order. ${slave2.slaveName} starts things off with a `);
+							if (slave2.skill.entertainment >= 100) {
+								r.push(`masterful`);
+							} else if (slave2.skill.entertainment > 10) {
+								r.push(`skillful`);
+							} else {
+								r.push(`passable`);
+							}
+							r.push(` striptease, culminating in ${him2} `);
+							if (slave2.anus > 0 && slave2.fetish !== "cumslut") {
+								r.push(`bending over`);
+							} else {
+								r.push(`kneeling`);
+							}
+							r.push(` in front of you. ${He2} eagerly moans as you enter ${him2}, begging for your seed`);
+							if (slave2.energy > 95) {
+								r.push(` like the slut ${he2} is`);
+							} else if (slave2.skill.whoring > 30) {
+								r.push(` like the whore ${he2} is`);
+							} else if ((slave2.assignment === "serve in the master suite") || (slave2.assignment === "please you")) {
+								r.push(` like the fucktoy ${he2} is`);
+							}
+							r.push(`. As you finish, ${he2} `);
+							if (slave2.fetish === "cumslut") {
+								r.push(`opens ${his2} mouth and savors your gift, thanking you once ${he2}'s swallowed enough to be able to talk again`);
+							} else if ((slave2.fetish === "buttslut") || (slave2.fetish === "submissive")) {
+								r.push(`collapses on the floor with ${his2} ass high in the air, thanking you for painting ${his2} hole white`);
+							} else {
+								r.push(`thanks you`);
+							}
+							r.push(`. Witnessing this display of servitude from ${his} `);
+							if (slave.mother === slave2.ID) {
+								r.push(`mother`);
+							} else {
+								r.push(`father`);
+							}
+							r.push(` <span class="hotpink">eases ${slave.slaveName} into ${his} new life,</span> and <span class="mediumaquamarine">gives ${him} hope</span> ${he} can find a place here.`);
+							if (slave.anus > 0 && slave.fetish !== "cumslut") {
+								actX(slave2, "anal");
+							} else {
+								actX(slave2, "oral");
+							}
+							slave.devotion += 4;
+							slave.trust += 4;
+							return r.join(" ");
+						},
+					});
+					break;
+				case "$He was recruited into your service by $his child.":
+					introText(`and since ${he} was recruited by ${his} ${getPronouns(slave2).daughter}...`);
+					choice({
+						get linkName() {
+							const {daughter2} = getPronouns(slave2).appendSuffix('2');
+							return `Use ${his} ${daughter2} as an example`;
+						},
+						result: function(slave) {
+							const r = [];
+							const {He2, he2, his2, him2} = getPronouns(slave2).appendSuffix('2');
+							r.push(`You gesture towards ${slave2.slaveName}. ${He2} is `);
+							if (slave2.intelligence + slave2.intelligenceImplant > 15) {
+								r.push(`bright enough`);
+							} else if (((slave2.skill.vaginal + slave2.skill.anal + slave2.skill.oral) > 100)) {
+								r.push(`skilled enough`);
+							} else {
+								r.push(`obedient enough`);
+							}
+							r.push(` to understand you mean a demonstration is in order. ${slave2.slaveName} starts things off with a `);
+							if (slave2.skill.entertainment >= 100) {
+								r.push(`masterful`);
+							} else if (slave2.skill.entertainment > 10) {
+								r.push(`skillful`);
+							} else {
+								r.push(`passable`);
+							}
+							r.push(` striptease, culminating in ${him2} `);
+							if (slave2.anus > 0 && slave2.fetish !== "cumslut") {
+								r.push(`bending over`);
+							} else {
+								r.push(`kneeling`);
+							}
+							r.push(` in front of you. ${He2} eagerly moans as you enter ${him2}, begging for your seed`);
+							if (slave2.energy > 95) {
+								r.push(` like the slut ${he2} is`);
+							} else if (slave2.skill.whoring > 30) {
+								r.push(` like the whore ${he2} is`);
+							} else if ((slave2.assignment === "serve in the master suite") || (slave2.assignment === "please you")) {
+								r.push(` like the fucktoy ${he2} is`);
+							}
+							r.push(`. As you finish, ${he2} `);
+							if (slave2.fetish === "cumslut") {
+								r.push(`opens ${his2} mouth and savors your gift, thanking you once ${he2}'s swallowed enough to be able to talk again`);
+							} else if ((slave2.fetish === "buttslut") || (slave2.fetish === "submissive")) {
+								r.push(`collapses on the floor with ${his2} ass high in the air, thanking you for painting ${his2} hole white`);
+							} else {
+								r.push(`thanks you`);
+							}
+							r.push(`. Witnessing this display of servitude from ${his} _daughter2 <span class="hotpink">eases ${slave.slaveName} into ${his} new life,</span> and <span class="mediumaquamarine">gives ${him} hope</span> ${he} can find a place here.`);
+							if (slave.anus > 0 && slave.fetish !== "cumslut") {
+								actX(slave, "anal");
+							} else {
+								actX(slave, "oral");
+							}
+							slave.devotion += 4;
+							slave.trust += 4;
+							return r.join(" ");
+						},
+					});
+					break;
+				default:
+					if (typeof slave.origin === "string") {
+						if (slave.origin.includes("enslavement for the attempted rape of a free")) {
+							introText(`and since ${he} tried to rape you...`);
+							choice({
+								linkName: `Show ${him} how a professional does it`,
+								result: function(slave) {
+									const r = [];
+									r.push(`Judging from ${his} earlier, amateur attempt, it's clear ${slave.slaveName} is unfamiliar with the subtle nuances of a high-quality rape. You decide to clear your schedule for the rest of the day and teach ${him} yourself${V.PC.dick === 0 ? `, with the help of your trusty strap-on, of course` : ``}. Once ${he}'s bent over across your desk and properly restrained, ${his} lesson begins with you thrusting your`);
+									if (V.PC.dick === 0) {
+										r.push(`fake`);
+									}
+									r.push(`cock down ${his} throat as far as it will go. Over the course of the next several hours, you ensure that ${he} understands the fine points of nonconsensual oral${slave.vagina > -1 ? `, vaginal,` : ``} and anal intercourse as intimately as possible. When you're finally too tired to continue,you unshackle ${his} <span class="red">bruised and bloody body</span> and ask ${him} what ${he} learned. ${His} voice hoarse from the same brutal fucking that has gaped ${his} <span class="lime">asshole</span> ${(slave.vagina > -1) ? `and <span class="lime">pussy</span>` : ``}, ${he} hesitantly replies that ${he} has <span class="hotpink">learned a great deal about true dominance,</span> before fainting on the spot from a mixture of total exhaustion and pure terror. You've taught your student well.`);
+									actX(slave, "oral", 15);
+									slave.anus = 2;
+									actX(slave, "anal", 15);
+									if (slave.vagina > -1) {
+										slave.vagina = 2;
+										actX(slave, "vaginal", 15);
+									}
+									if (isFertile(slave) && V.PC.dick > 0) {
+										knockMeUp(slave, 100, 0, -1, true);
+									}
+									slave.devotion += 100;
+									return r.join(" ");
+								},
+
+							});
+							choice({
+								linkName: `Show ${him} that ${he} could have just asked`,
+								result: function(slave) {
+									const r = [];
+									r.push(`You tell ${slave.slaveName} that while ${his} previous attempt to fuck you was very flattering, ${he} should know that one can catch more flies with honey. Ordering the confused slave to lean back over and onto your desk, you proceed to gently`);
+									if (slave.dick > 0) {
+										r.push(`stroke`);
+									} else {
+										r.push(`finger`);
+									}
+									r.push(`${him} until ${his} arousal overwhelms ${his} wariness of you. Once ${he}'s finally relaxed, you climb on top of ${him}, and gently ease`);
+									if (V.PC.vagina > 0 && canPenetrate(slave)) {
+										r.push(`your pussy onto ${his} cock. ${He} shudders and moan softly as you slide yourself up and down ${his} shaft with steadily increasing speed. You keep your eyes locked on ${hers} all the while, as ${his} expression shifts from bewilderment to acceptance to ecstasy, as ${he} soon shoots ${his} seed up into you. Afterwards, you slip ${his} softening cock out of you, climb off of ${him}, and leave the exhausted and overwhelmed slave${girl} on your desk as you attend to business elsewhere. You think ${he}'s <span class="orangered">going to like it here.</span>`);
+										actX(slave, "penetrative");
+										if (canImpreg(V.PC, slave)) {
+											knockMeUp(V.PC, 100, 0, slave.ID, 1);
+										}
+									} else {
+										r.push(`yourself into ${his}`);
+										if (slave.vagina > -1) {
+											r.push(`pussy.`);
+										} else {
+											r.push(`asshole.`);
+										}
+										r.push(`${He} shudders and moans softly as you piston your`);
+										if (V.PC.dick > 0) {
+											r.push(`dick`);
+										} else {
+											r.push(`strap-on`);
+										}
+										r.push(`in and out of ${his} hole with steadily increasing intensity. You keep your eyes locked on ${hers} all the while, as ${his} expression shifts from bewilderment to acceptance to ecstasy, as you soon shoot your seed down into ${him}. Afterwards, you slip your`);
+										if (V.PC.dick > 0) {
+											r.push(`softening`);
+										} else {
+											r.push(`plastic`);
+										}
+										r.push(`cock out of ${him}, climb off of ${him}, and leave the exhausted and overwhelmed slave${girl} on your desk as you attend to business elsewhere. You think ${he}'s <span class="orangered">going to like it here.</span>`);
+										if (slave.vagina > -1) {
+											r.push(VCheck.Vaginal());
+											actX(slave, "vaignal");
+											if (isFertile(slave) && V.PC.dick > 0) {
+												knockMeUp(slave, 100, 0, -1, true);
+											}
+										} else {
+											r.push(VCheck.Anal());
+											actX(slave, "anal");
+										}
+									}
+									slave.trust += 100;
+									return r.join(" ");
+								},
+							});
+						}
+					}
+			}
+
+
+			lineBreak();
+
+			App.UI.DOM.appendNewElement("div", p, `Have ${him} changed...`, "note");
+
+			if (slave.health.condition < -20) {
+				choice({
+					linkName: `Address ${his} medical issues`,
+					result: function(slave) {
+						const r = [];
+						r.push(`Since ${he}'s in rough shape, you give ${him} a comprehensive medical exam with the help of the remote surgery. You apply care to <span class="green">address</span> some of the most outstanding concerns. Whatever gratitude ${he} might have felt for prompt medical attention is balanced by fear of the remote surgery and the nagging (and accurate) feeling that ${he}'s being treated like livestock, but ${he} does <span class="mediumaquamarine">begin to hope</span> ${he}'ll be well treated.`);
+						if (V.arcologies[0].FSPaternalist !== "unset") {
+							r.push(`Society <span class="green">approves</span> of your promptly seeing to your stock's health; this advances the idea that all slaveowners should look after their slaves.`);
+							FutureSocieties.Change("Paternalist", 2);
+						}
+						improveCondition(slave, 10);
+						slave.trust += 4;
+						return r.join(" ");
+					},
+				});
+			}
+
+			lineBreak();
+
+			if (slave.hStyle.indexOf("shaved") === -1) {
+				choice({
+					linkName: `Shave ${his} hair`,
+					result: function(slave) {
+						const r = [];
+						r.push(`You seize ${him} by the hair and drag ${him} to the auto salon. ${He} struggles and thrashes around but soon scrambles to keep up with you to relieve the pain in ${his} scalp as ${he}'s dragged. You strap ${him} in and set the auto salon to shave. ${He}`);
+						if (canSee(slave)) {
+							r.push(`looks around frantically`);
+						} else {
+							r.push(`trembles with fear`);
+						}
+						r.push(`as the manipulators with their buzzing shears descend menacingly. As they run quickly back and forth across ${his} head, ${he} begins to sob, obviously <span class="gold">fearing</span> that ${his} new master is <span class="mediumorchid">one to enjoy degradation and abuse.</span> ${He} has a strong indication that obedience is ${his} only hope.`);
+						slave.devotion -= 5;
+						slave.hStyle = "shaved";
+						slave.hLength = 0;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+				});
+			}
+			lineBreak();
+
+			if (slave.stampTat === 0) {
+				choice({
+					linkName: `Give ${him} a lewd tramp stamp`,
+					result: function(slave) {
+						const r = [];
+						slave.stampTat = either("advertisements", "degradation", "rude words");
+						r.push(`You`);
+						if (slave.devotion > 20) {
+							r.push(`bring ${him} down to the studio, lay the compliant`);
+						} else {
+							r.push(`drag ${him} down to the studio, strap the reluctant`);
+						}
+						r.push(`slave in the chair face-down, and select a sufficiently lewd pattern to decorate ${his} lower back. When the automated tattooing is done, a topical application of analgesics leaves ${him} with nothing more than a slight ache there.`);
+						if (canSee(slave)) {
+							r.push(`When allowed to stand, ${he} immediately turns ${his} back to one of the studio's full length mirrors and cranes around to read the writing just over ${his} butt. It's difficult, since, of course, it's reversed, and ${he} mouths the words letter by letter.`);
+							if (slave.stampTat === "advertisements") {
+								r.push(`${He} mouths, 'Fuck my ass!'`);
+							} else if (slave.stampTat === "rude words") {
+								r.push(`'Rear Entrance,' ${he} mouths, and then understands the arrow pointing down between ${his} buttocks.`);
+							} else if (slave.stampTat === "degradation") {
+								r.push(`Struggling to read the ornate gothic lettering, ${he} slowly mouths, 'Anal Whore.'`);
+							}
+						} else if (canHear(slave)) {
+							r.push(`When ${he} sits back up, you tell ${him} calmly and bluntly what ${his} new tattoo says:`);
+							if (slave.stampTat === "advertisements") {
+								"Fuck My Ass.";
+							} else if (slave.stampTat === "rude words") {
+								"Rear Entrance.";
+							} else if (slave.stampTat === "degradation") {
+								"Anal Whore.";
+							}
+						} else {
+							r.push(`Judging by the expression on ${his} face, ${he}'s either accurately judged the movement of the needle on ${his} skin or simply guessed correctly that ${his} new tattoo consists of`);
+							if (slave.stampTat === "advertisements") {
+								r.push(`blunt advertisements for anal sex.`);
+							} else if (slave.stampTat === "rude words") {
+								r.push(`mocking references to anal sex.`);
+							} else if (slave.stampTat === "degradation") {
+								r.push(`insulting invitations for anal sex.`);
+							}
+						}
+
+						if (slave.devotion > 20) {
+							r.push(`${He} gasps at the realization that taking it up the ass is about to become a dominant part of ${his} life. ${He} isn't surprised by that, but it's a little stark, having ${his} status as an anal slave permanently written on ${his} lower back for anyone to see. Nevertheless, ${he} <span class="hotpink">resolves to do ${his} best.</span>`);
+							slave.devotion += 5;
+						} else {
+							r.push(`${He} begins to cry softly. It's not likely that ${he} didn't know, on some level, that ${he}'s a sex slave, and that most sex slaves are expected to take it up the ass. ${He}'s <span class="gold">frightened</span> to find, though, that it's apparently so essential to ${his} existence that it's worth tattooing right over ${his} bottom. ${He} continues to weep as you lead ${him} out, but <span class="hotpink">offers no real resistance.</span> ${He}'s just mourning ${his} poor anal sphincter in an anticipatory sort of way.`);
+							slave.devotion += 5;
+							slave.trust -= 5;
+						}
+
+						return r.join(" ");
+					},
+				});
+			}
+
+			choice({
+				linkName: `Brand ${him} on the ${brandTarget}`,
+				result: function(slave) {
+					const r = [];
+					r.push(`You drag ${him} to the body modification studio and strap ${him} down with ${his} ${brandTarget} clear and defenseless. ${He} doesn't understand what's coming for a while, even as disinfectant is applied to ${his} ${brandTarget}. ${He} can't see the approaching brand,`);
+					if (!canSee(slave)) {
+						r.push(`of course,`);
+					}
+					r.push(`but eventually ${he} feels the radiated heat on ${his} skin and manages to get one inarticulate, wordless noise of terror out before the dreadful sizzling noise and the sweet smell of burning flesh. If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it.`);
+					if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
+						r.push(`Society <span class="green">approves</span> of your purchase and branding of an inferior ${slave.race} person; this advances the idea that slave.race people ought to be enslaved.`);
+						FutureSocieties.Change("Subjugationist", 2);
+					} else if ((V.arcologies[0].FSSupremacistRace === slave.race) && (V.arcologies[0].FSSupremacist > 0)) {
+						r.push(`Society <span class="red">disapproves</span> of your purchase and branding of ${addA(slave.race)} person; this reduces support for the idea that slave.race people are superior.`);
+						FutureSocieties.Change("Supremacist", -2);
+					}
+					slave.brand[brandTarget] = V.brandDesign.primary;
+					slave.devotion -= 5;
+					slave.trust -= 10;
+					healthDamage(slave, 10);
+					return r.join(" ");
+				},
+			});
+
+			if (slave.dick === 0 && slave.vagina > -1) {
+				choice({
+					linkName: `Give ${him} a smart clitoral piercing`,
+					result: function(slave) {
+						const r = [];
+						cashX(forceNeg(V.SPcost), "slaveMod", slave);
+						r.push(`You take ${him} to the body modification studio, strap ${him} down, pierce ${his} clit, and insert what appears to be a large stainless steel barbell piercing. It actually contains a device that can vibrate to give ${him} sexual stimulation or create subtle pain to suppress pleasure. It is connected wirelessly to the arcology, which can be given instructions to encourage or discourage sexual pleasure in specific situations. You run a quick test. On the oral setting, as soon as ${he} kneels in front of your crotch, ${he} gasps as the stimulation starts. On the anal setting, ${he} whimpers when you run a finger across ${his} asshole.`);
+						return r.join(" ");
+					},
+					note: `This option costs ${cashFormat(V.SPcost)}`
+				});
+			} else {
+				choice({
+					linkName: `Give ${him} a smart frenulum piercing`,
+					result: function(slave) {
+						const r = [];
+						cashX(forceNeg(V.SPcost), "slaveMod", slave);
+						r.push(`You take ${him} to the body modification studio, strap ${him} down, pierce ${his} frenulum, and insert what appears to be a large stainless steel barbell piercing. It actually contains a device that can vibrate to give ${him} sexual stimulation or create subtle pain to suppress pleasure. It is connected wirelessly to the arcology, which can be given instructions to encourage or discourage sexual pleasure in specific situations. You run a quick test. On the oral setting, as soon as ${he} kneels in front of your crotch, ${he} gasps as the stimulation starts. On the anal setting, ${he} whimpers and gets rock hard when you run a finger across ${his} asshole.`);
+						slave.clitPiercing = 3;
+						return r.join(" ");
+					},
+					note: `This option costs ${cashFormat(V.SPcost)}`
+				});
+			}
+
+			lineBreak();
+			if (slave.indentureRestrictions <= 0) {
+				choice({
+					linkName: `Whip ${him} until ${he} scars`,
+					result: function(slave) {
+						const r = [];
+						App.Medicine.Modification.addScourged(slave);
+						cashX(forceNeg(V.modCost), "slaveMod", slave); // Let's just bill once, for the whip and disinfectant.
+						r.push(`You drag ${him} to the`);
+						if (V.cellblock) {
+							r.push(V.cellblockName);
+						} else {
+							r.push(`body modification studio`);
+						}
+						r.push(`and`);
+						if (!isAmputee(slave)) {
+							r.push(`chain ${him} spread-eagled`);
+						} else {
+							r.push(`secure ${him}`);
+						}
+						r.push(`with ${his} face to the wall and ${his} naked back defenseless. ${He} doesn't understand what's coming for a while, even as disinfectant is applied from ${his} neck`);
+						if (hasAnyLegs(slave)) {
+							r.push(`to ${his} ${knees}.`);
+						} else {
+							r.push(`down.`);
+						}
+						r.push(`${He} can't see you prepare the whip,`);
+						if (!canSee(slave)) {
+							r.push(`of course,`);
+						}
+						if (canHear(slave)) {
+							r.push(`but almost jumps out of ${his} ${slave.skin} skin when you crack it behind ${him}.`);
+						} else {
+							r.push(`and in ${his} deaf state every blow is a devastating surprise.`);
+						}
+						r.push(`${He} frantically`);
+						if (!isAmputee(slave)) {
+							r.push(`wrenches ${his}`);
+							if (hasAnyArms(slave)) {
+								r.push(hands);
+								if (hasAnyLegs(slave)) {
+									r.push(`and`);
+								}
+							}
+							if (hasAnyLegs(slave)) {
+								r.push(feet);
+							}
+							r.push(`as you work up and down ${his} exposed back and`);
+							if (getLimbCount(slave)) {
+								r.push(`limbs,`);
+							} else {
+								r.push(`limb,`);
+							}
+							r.push(`but is completely unable to get free.`);
+						} else {
+							r.push(`twists and turns, but without limbs is powerless to escape the curling whip.`);
+						}
+						r.push(`If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it. What ${he} doesn't yet know is just how permanent this lashing's effects will be. The level of violence and the coating you used will leave ${him} scarred with the marks of slavery forever.`);
+						if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
+							r.push(`Society <span class="green">approves</span> of your purchase and whipping of an inferior ${slave.race} person; this advances the idea that slave.race people ought to be enslaved.`);
+							FutureSocieties.Change("Subjugationist", 2);
+						} else if ((V.arcologies[0].FSSupremacistRace === slave.race) && (V.arcologies[0].FSSupremacist > 0)) {
+							r.push(`Society <span class="red">disapproves</span> of your purchase and whipping of ${addA(slave.race)} person; this reduces support for the idea that slave.race people are superior.`);
+							FutureSocieties.Change("Supremacist", -2);
+						}
+						slave.devotion -= 5;
+						slave.trust -= 10;
+						healthDamage(slave, 10);
+						return r.join(" ");
+					},
+				});
+				choice({
+					linkName: `Scar ${him} on the ${scarTarget}`,
+					result: function(slave) {
+						const r = [];
+						r.push(`You drag ${him} to the body modification studio and strap ${him} down with ${his} ${scarTarget} clear and defenseless. ${He} doesn't understand what's coming for a while, even as disinfectant is applied to ${his} ${scarTarget}. You have a wide selection of tools to create scars, the trick is to keep the wound from healing correctly afterwards. Of course, ${he} has no way of knowing that the pain you are inflicting as you cut into ${his} flesh will leave such a permanent mark, but the basic message is clear: if ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it.`);
+						if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
+							r.push(`Society <span class="green">approves</span> of your purchase and scarring of an inferior ${slave.race} person; this advances the idea that slave.race people ought to be enslaved.`);
+							FutureSocieties.Change("Subjugationist", 2);
+						} else if ((V.arcologies[0].FSSupremacistRace === slave.race) && (V.arcologies[0].FSSupremacist > 0)) {
+							r.push(`Society <span class="red">disapproves</span> of your purchase and scarring of ${addA(slave.race)} person; this reduces support for the idea that slave.race people are superior.`);
+							FutureSocieties.Change("Supremacist", -2);
+						}
+						App.Medicine.Modification.addScar(slave, scarTarget, V.scarDesign.primary);
+						slave.devotion -= 5;
+						slave.trust -= 10;
+						healthDamage(slave, 10);
+						return r.join(" ");
+					},
+				});
+			}
+			lineBreak();
+			if (V.arcologies[0].FSHedonisticDecadence >= 50 && slave.behavioralFlaw === "anorexic" && slave.weight < 10) {
+				choice({
+					linkName: `Force-feed ${him}`,
+					result(slave) {
+						const r = [];
+						r.push(`${He} is much too thin and seems to shirk any offered food, but there is an easy solution to that. If ${he} refuses to eat, ${he} can be made to. Pulling the reluctant slave to the feeders, binding ${him} tightly to a chair, and attaching a hose to the slave food nozzle, you give ${him} a choice; suck the hose willingly or have it forced down ${his} throat. ${He} glares defiantly and keeps ${his} mouth firmly shut. You clamp down on ${his} nose, inevitably forcing ${him} to open ${his} mouth to breath. In that moment, you shove the hose in and down ${his} throat, carefully directing it down into ${his} stomach. ${He} gags as you turn on the flow, ${his} eyes filling with tears as ${he} feels the warm food travel down the tube and into ${his} stomach. ${He} sobs as ${his} belly steadily swells with unwelcome sustenance, ${his} eyes pleading with you, desperate to let you know ${he}'ll be good. You ignore ${him}, letting ${him} fill until ${his} belly is noticeably distended compared to ${his} thin frame. Once you feel ${he} has had enough, you pull the hose from ${his} gut, spraying ${his} face with food in the process, and tell ${him} it will go right back in if ${he} doesn't keep it all down. ${He} <span class="gold">nods fearfully,</span> anything to not go through that again. ${He} hobbles away once freed, one hand covering ${his} retching mouth and the other clasping ${his} <span class="mediumorchid">hated, food bloated middle.</span>`);
+						slave.devotion -= 10;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.pregKnown === 1 && V.seeExtreme === 1) {
+				choice({
+					linkName: `Threaten ${his} pregnancy`,
+					result(slave) {
+						const r = [];
+						r.push(`You place an innocuous-looking drug injector on your desk, and ${him}`);
+						if (!canSee(slave)) {
+							r.push(`blindly`);
+						}
+						r.push(`stare at it for a long moment. Then, you roll the cylinder slowly across the smooth surface, rotating it until its label comes into`);
+						if (canSee(slave)) {
+							r.push(`${his}`);
+						} else {
+							r.push(`your`);
+						}
+						r.push(`view. It depicts the silhouette of a pregnant woman, with a red "X" over it. Abortifacients. After an`);
+						if (!canSee(slave)) {
+							r.push(`short period`);
+						} else {
+							r.push(`instant`);
+						}
+						r.push(`of horrified comprehension, ${he} flings ${himself} at your feet, crying, begging, promising. ${He} pledges to <span class="hotpink">submit to you,</span>`);
+						if (V.PC.dick !== 0) {
+							if (V.PC.vagina !== -1) {
+								r.push(`eat you out,`);
+							}
+							r.push(`suck your cock, take it in ${his} pussy, take it up ${his} ass,`);
+						} else {
+							r.push(`eat you out, worship you with ${his} pussy, serve you with ${his} ass,`);
+						}
+						r.push(`anything, as long as <span class="gold">you don't hurt ${his} baby.</span> You observe that ${he}'ll do all of those things, regardless of what you decide to do about ${his} pregnancy, but for now, you'll keep ${him} as a pregnant slut. ${He} thanks you through ${his} tears.`);
+						slave.devotion += 10;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if ((V.arcologies[0].FSRestart >= 10) && (slave.preg !== -3) && (slave.ovaries > 0) && (slave.indentureRestrictions <= 0) && (V.seeExtreme === 1)) {
+				choice({
+					linkName: `Sterilize ${him}`,
+					result(slave) {
+						const r = [];
+						r.push(`You drag ${him} to the remote surgery and strap ${him} face-up with ${his}`);
+						if (slave.bellyPreg >= 1500) {
+							r.push(`pregnant`);
+						}
+						r.push(`stomach bare. ${He} doesn't understand what's coming for a while, even as ${his} belly is carefully cleaned and disinfected. ${He} begins to panic at the`);
+						if (canSee(slave)) {
+							r.push(`sight`);
+						} else if (canHear(slave)) {
+							r.push(`sound`);
+						} else {
+							r.push(`touch`);
+						}
+						r.push(`of the approaching surgical manipulators, cringing as they cut into ${his}`);
+						if (slave.bellyPreg >= 1500) {
+							r.push(`rounded`);
+						}
+						r.push(`abdomen. ${He} squeals in horror as ${his}`);
+						if (slave.preg > slave.pregData.normalBirth / 1.33) {
+							r.push(`swollen womb, still moving with life,`);
+						} else if (slave.bellyPreg >= 1500) {
+							r.push(`pregnant womb`);
+						} else {
+							r.push(`womb`);
+						}
+						r.push(`and ovaries are extracted${canSee(slave) ? `before ${his} eyes` : ``}.`);
+
+						if (slave.fetish === "pregnancy" && slave.pregKnown === 1) {
+							if ((slave.preg > slave.pregData.normalBirth / 1.6) && (canSee(slave))) {
+								r.push(`${He} watches ${his} exposed womb with absolute terror in ${his} eyes. A small handprint bulges against the organ; ${he} sobs once before ${his} mind <span class="red">shatters at the sight.</span>`);
+								slave.fetish = "mindbroken";
+								slave.fetishKnown = 1;
+								slave.behavioralQuirk === "none";
+								slave.behavioralFlaw === "none";
+								slave.sexualQuirk === "none";
+								slave.sexualFlaw === "none";
+							} else {
+								r.push(`${He} will never carry another child, and <span class="mediumorchid">deeply hates you</span> for snuffing out its life. Your absolute power over ${his} body is rightfully <span class="gold">terrifying.</span>`);
+								slave.trust = -100;
+								slave.devotion = -100;
+							}
+						} else {
+							r.push(`${He} will never`);
+							if (slave.counter.birthsTotal > 0) {
+								r.push(`again`);
+							}
+							r.push(`bear children, and will <span class="mediumorchid">remember ${his} status</span> with every fucking ${he} receives. Your absolute power over ${his} body is rightfully <span class="gold">terrifying.</span>`);
+							slave.trust -= 20;
+							slave.devotion -= 50;
+						}
+						r.push(`Society <span class="green">approves</span> of your promptly rendering ${him} sterile; this advances the idea that only the elite should breed.`);
+						FutureSocieties.Change("Eugenics", 1);
+						if (slave.pregKnown === 1) {
+							r.push(`Since you snuffed out an unworthy life in the process, the sterilization has an even <span class="green">bigger impact.</span>`);
+							FutureSocieties.Change("Eugenics", 2);
+						}
+						TerminatePregnancy(slave);
+						slave.preg = -3;
+						slave.ovaries = 0;
+						actX(slave, "abortions");
+						surgeryDamage(slave, 10);
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.balls > 0 && slave.vasectomy === 0 && slave.indentureRestrictions <= 1) {
+				choice({
+					linkName: `Snip ${his} vas deferens`,
+					result(slave) {
+						const r = [];
+						r.push(`You drag ${him} to the remote surgery and strap ${him}`);
+						if (slave.scrotum > 0) {
+							r.push(`face-down with ${his}`);
+							if (hasAnyLegs(slave)) {
+								r.push(`${hasBothLegs(slave) ? `legs` : `leg`} spread.`);
+							} else {
+								r.push(`testicles exposed.`);
+							}
+							r.push(`${He} doesn't understand what's coming for a while, even as ${his}`);
+							if (slave.balls === 1) {
+								r.push(`tiny balls`);
+							} else if (slave.balls === 2) {
+								r.push(`small balls`);
+							} else if (slave.balls === 3) {
+								r.push(`balls`);
+							} else if (slave.balls === 4) {
+								r.push(`big balls`);
+							} else if (slave.balls === 5) {
+								r.push(`lemon-sized balls`);
+							} else if (slave.balls < 10) {
+								r.push(`fist-sized balls`);
+							} else {
+								r.push(`hypertrophied balls`);
+							}
+							r.push(`are carefully cleaned and disinfected. ${He} can't see the approaching surgical manipulators,`);
+							if (!canSee(slave)) {
+								r.push(`of course,`);
+							}
+							r.push(`but eventually ${he} feels a small tugging on the sides of ${his} scrotum even through the local anesthetic.`);
+						} else {
+							r.push(`You drag ${him} to the remote surgery and strap ${him} face-up on the operating table. ${He} doesn't understand what's coming for a while, even as ${his} crotch and lower stomach are carefully cleaned and disinfected.`);
+							if (!canSee(slave)) {
+								r.push(`${He} can't see the approaching surgical manipulators, of course, but eventually ${he} feels a small pinch to either side of ${his} pubic mound even through the local anesthetic.`);
+							} else {
+								r.push(`${He} freezes at the sight of the surgical manipulators and squirms slightly as they slip into the sides of ${his} pubic mound.`);
+							}
+						}
+						r.push(`${He} gets one squeal of protest out before the surgery begins to apply healing agents. ${He} knows something was done to ${his} testicles and will <span class="mediumorchid">remember this act</span> with every load ${he} shoots. ${He} <span class="gold">can only imagine</span> what other modifications you may have planned for ${him}.`);
+						slave.vasectomy = 1;
+						slave.devotion -= 5;
+						surgeryDamage(slave, 10);
+						slave.trust -= 5;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if ((slave.balls > 0) && (slave.attrXY < 65) && (V.seeExtreme === 1)) {
+				choice({
+					linkName: `Threaten ${his} balls`,
+					result(slave) {
+						const r = [];
+						r.push(`You ask ${him} how ${he} feels about taking cock up ${his} ass. A look of revulsion and fear crosses ${his} face, and ${he} backs away from you slightly, unconsciously protecting ${his} bottom. Before ${he} can muster any kind of response, you tell ${him} that you suspected as much, and that ${he} has balls. ${He} looks confused at the apparently unrelated remarks until you explain that in your arcology, balls are a privilege. If ${he} won't be a good little bitch, you'll take them away and turn ${him} into a bitch anyway. Only good ${girl}s get to keep their hard-ons, and if ${he} isn't a good ${girl}, you'll make ${him} a good little ${girl} with a bouncing little bitchclit. ${He} begins to cry, <span class="gold">sobbing and sobbing</span> as ${he} promises to <span class="hotpink">be a good ${girl}.</span>`);
+						slave.devotion += 10;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.indentureRestrictions <= 0 && V.seeExtreme === 1) {
+				if (slave.balls > 0) {
+					choice({
+						linkName: `Geld ${him}`,
+						result(slave) {
+							const r = [];
+							r.push(`You drag ${him} to the remote surgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, since the anesthetics totally deprive ${him} of any sensation. ${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened. ${He} numbly carries on, terrified.`);
+							if (V.arcologies[0].FSGenderRadicalist !== "unset") {
+								r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that all societal inferiors can be made female.`);
+								FutureSocieties.Change("GenderRadicalist", 2);
+							}
+							if (V.arcologies[0].FSRestart !== "unset" && slave.pubertyXY === 1) {
+								r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that all only the elite should breed.`);
+								FutureSocieties.Change("Eugenics", 2);
+							}
+							slave.balls = 0;
+							slave.scrotum = 0;
+							surgeryDamage(slave, 10);
+							slave.trust -= 50;
+							return r.join(" ");
+						},
+
+					});
+				}
+				choice({
+					linkName: `Remove ${his} genitalia`,
+					result(slave) {
+						const r = [];
+						r.push(`You drag ${him} to the remote surgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, since the anesthetics totally deprive ${him} of any sensation. ${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. Eventually, though, ${he} realizes that ${he}'s been reduced to the status of a genital null: the only remaining feature of ${his} newly smooth groin is a tiny soft hole, ${his} urethra.`);
+						if (slave.scrotum > 0) {
+							r.push(`${He} retains ${his} ballsack beneath this, though of course you can always remove that later if you decide to geld ${him}, too.`);
+						}
+						r.push(`${He} almost passes out from <span class="gold">sheer horror.</span> Instead, ${he} collapses and tries desperately to vomit. Fortunately, ${he} doesn't have anything to bring up. ${He}'s reduced to impotent weeping and retching as ${he} begins to process the stress of having had ${his} parts cut off.`);
+						if (V.arcologies[0].FSRestart !== "unset") {
+							r.push(`Society <span class="green">approves</span> of you stripping away everything from society's inferiors.`);
+							FutureSocieties.Change("Eugenics", 3);
+						}
+						slave.chastityPenis = 0;
+						slave.chastityVagina = 0;
+						slave.dick = 0;
+						slave.foreskin = 0;
+						slave.ovaries = 0;
+						slave.preg = -2;
+						TerminatePregnancy(slave);
+						actX(slave, "abortions");
+						slave.vagina = -1;
+						slave.skill.vaginal = 0;
+						surgeryDamage(slave, 10);
+						slave.trust = Math.clamp(slave.trust - 100, -100, 100);
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (hasAnyNaturalLegs(slave) && (slave.indentureRestrictions <= 0) && (V.seeExtreme === 1)) {
+				choice({
+					linkName: `Clip ${his} Achilles tendons`,
+					result(slave) {
+						const r = [];
+						r.push(`You drag ${him} to the remote surgery and strap ${him} face-down with ${his} legs bare. ${He} doesn't understand what's coming for a while, even as ${his} lower legs are carefully cleaned and disinfected. ${He} can't see the approaching surgical manipulators,`);
+						if (!canSee(slave)) {
+							r.push(`of course,`);
+						}
+						r.push(`but eventually ${he} feels a tugging at ${his} lower legs even through the local anesthetic. ${He} gets one squeal of protest out before the surgery begins to apply healing agents. ${He} now requires special heels to walk, and will <span class="mediumorchid">remember ${his} status</span> with every <span class="red">painful</span> step ${he} takes. ${He}'s barefoot, crawling, and <span class="gold">frightened</span> for now, until you decide to give ${him} heels — if you ever do.`);
+						slave.heels = 1;
+						slave.devotion -= 5;
+						surgeryDamage(slave, 10);
+						slave.trust -= 20;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.lactation !== 2 && slave.indentureRestrictions <= 0) {
+				choice({
+					linkName: `Implant ${him} with slow release lactation drugs`,
+					result(slave) {
+						const r = [];
+						r.push(`You`);
+						if (slave.devotion > 20) {
+							r.push(`guide the compliant ${desc}`);
+						} else {
+							r.push(`have the ${desc} restrained and brought`);
+						}
+						r.push(`to the remote surgery. The procedure is quick and <span class="red">minimally invasive.</span>`);
+						surgeryDamage(slave, 10);
+						r.push(`Once the process is complete and the anesthesia subsides ${he} begins to feel a rising pressure within ${his}`);
+						if (slave.boobs > 2000) {
+							r.push(`gigantic udders.`);
+						} else if (slave.boobs > 900) {
+							r.push(`plentiful bosom.`);
+						} else if (slave.boobs > 400) {
+							r.push(`healthy breasts.`);
+						} else {
+							r.push(`small breasts.`);
+						}
+						r.push(`You then`);
+						if (slave.devotion > 20) {
+							r.push(`instruct ${him} to rest`);
+						} else {
+							r.push(`have ${him} restrained`);
+						}
+						r.push(`beside your desk until further notice. After a few hours ${his} increasing discomfort becomes obvious, and white droplets begin to appear across ${his} nipples and areola.`);
+						if (slave.devotion > 20) {
+							if (slave.fetish === "boobs") {
+								if (slave.fetishKnown === 0) {
+									if (V.servantMilkers === 1) {
+										r.push(`You escort ${him} to one of the many milkers installed in the penthouse and instruct ${him} in its use. The combined sensations of nipple stimulation and relief visibly overwhelm ${him}, and the resulting series of orgasms leaves ${him} exhausted.`);
+										r.push(`<span class="green">${He}'s a breast fetishist!</span>`);
+										slave.fetishKnown = 1;
+									} else {
+										r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple, and the intense sensation of relief and stimulation overwhelms ${him}. It doesn't take long for ${him} to reach the first of a series of orgasms, leaving ${him} exhausted.`);
+										r.push(`<span class="green">${He}'s a breast fetishist!</span>`);
+										slave.fetishKnown = 1;
+									}
+								} else {
+									if (V.servantMilkers === 1) {
+										r.push(`You escort ${him} to one of the many milkers installed in the penthouse and instruct ${him} in its use. ${His} breast fetish makes the experience comparable to sex for &him, and the resulting series of orgasms leaves ${him} exhausted.`);
+									} else {
+										r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple. ${His} breast fetish makes the experience comparable to sex for ${him}, and the resulting series of orgasms leaves ${him} exhausted.`);
+									}
+								}
+								r.push(`After allowing ${him} to rest for a bit you instruct ${him} to report for ${his} first assignment. As ${he} leaves your office ${he} can already feel the pressure returning to ${his} chest. <span class="hotpink">As far as ${he}'s concerned ${his} next milking can't come soon enough.</span>`);
+								slave.devotion += 5;
+							} else {
+								if (V.servantMilkers === 1) {
+									r.push(`You escort ${him} to one of the many milkers installed in the penthouse and instruct ${him} in its use. The sensation of relief is pleasant for ${him}, and ${he} moans softly as ${his} breasts drain into the apparatus.`);
+								} else {
+									r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple, and the sensation of relief is pleasant for ${him}. ${He} moans softly as you proceed to drain ${his} swollen breasts.`);
+								}
+								r.push(`Once drained, you instruct ${him} to report for ${his} first assignment. As ${he} leaves your office ${he} can already feel the pressure returning to ${his} chest. ${He} appreciates that ${his} next milking will be a nice break from ${his} regular duties. <span class="hotpink">It doesn't hurt that these breaks are both profitable for you, and pleasurable for ${him}.</span>`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "boobs") {
+								if (slave.fetishKnown === 0) {
+									if (V.servantMilkers === 1) {
+										r.push(`You escort ${him} to one of the many milkers installed in the penthouse and attach ${him} to the device, leaving ${his} restraints in place. ${He} is still struggling when you activate the device, and the sudden sensation of relief catches ${him} off guard. A primal moan erupts from the bottom of ${his} throat, and it's obvious to both of you that ${he}'s enjoying this in spite of ${himself}. ${He} spends the rest of ${his} milking trying and failing to hide the obvious pleasure ${he}'s deriving from ${his} predicament.`);
+										r.push(`<span class="green">${He}'s a breast fetishist!</span>`);
+										slave.fetishKnown = 1;
+									} else {
+										r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple. The sensation overwhelms ${him} almost immediately, and ${his} arms struggle against ${his} restraints as ${he} fights an impending orgasm. It's an amusing sight that goes on until ${he} is fully drained.`);
+										r.push(`<span class="green">${He}'s a breast fetishist!</span>`);
+										slave.fetishKnown = 1;
+									}
+								} else {
+									if (V.servantMilkers === 1) {
+										r.push(`You escort ${him} to one of the many milkers installed in the penthouse and attach ${him} to the device, leaving ${his} restraints in place. ${His} breast fetish soon overcomes ${his} obvious disdain for ${his} situation, and ${he} spends the milking red in the face, fighting an obviously impending orgasm.`);
+									} else {
+										r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple. With a sharp intake of breath ${he} begins to squirm, and ${his} breast fetish soon has ${him} moaning and writhing as ${he} fights an intense orgasm with tears of shame rolling down ${his} flushed cheeks.`);
+									}
+								}
+								r.push(`As you summon`);
+								if (V.HeadGirlID !== 0) {
+									r.push(`your Head Girl`);
+								} else {
+									r.push(`another slave`);
+								}
+								r.push(`to escort the ${girl} to ${his} first assignment, ${he} slumps dejectedly in ${his} restraints. ${He} is torn between the knowledge that ${he} is little more than livestock to you, and the disturbing realization that <span class="hotpink">${he} might enjoy ${his} new life as a human cow.</span>`);
+								slave.devotion += 4;
+							} else {
+								if (V.servantMilkers === 1) {
+									r.push(`You escort ${him} to one of the many milkers installed in the penthouse and attach ${him} to the device. ${He} struggles against ${his} restraints as you activate the device, drawing a brief pause and a look of confusion as ${his} breasts begin to drain. This only lasts a moment before ${he} returns to ${his} futile struggle, but ${his} milking continues regardless.`);
+								} else {
+									r.push(`You kneel behind ${him} and begin to gently massage ${his} swollen breasts. A steady stream of milk begins to erupt from each nipple as a look of confusion washes over ${his} features. ${He} then closes ${his} eyes tight and does ${his} best to ignore what's happening in a futile attempt to deny ${his} new reality, but you continue ${his} milking until ${he} is drained.`);
+								}
+								r.push(`As you summon`);
+								if (V.HeadGirlID !== 0) {
+									r.push(`your Head Girl`);
+								} else {
+									r.push(`another slave`);
+								}
+								r.push(`to escort the ${girl} to ${his} first assignment ${he} glares at you with disgust. ${His} resentment at the new burden you've saddled ${him} with is evident, and the regular milkings ${he}'ll require will serve to remind ${him} of the <span class="gold">disturbing fact that ${he} is little more than livestock to you.</span>`);
+								slave.devotion -= 4;
+							}
+						}
+						slave.lactation = 2;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+
+			App.UI.DOM.appendNewElement("div", p, `...or show ${him} what it's like here for a slave`, "note");
+
+			if (slave.fetish !== "mindbroken") {
+				/*
+				choice({
+					linkName: `Teach ${him} how to use ${his} throat`,
+					result(slave) {
+						const r = [];
+						r.push(`TODO: write this`);
+						return r.join(" ")
+					},
+
+				})
+				*/
+
+				choice({
+					linkName: `Inseminate ${him}`,
+					result(slave) {
+						const r = [];
+						r.push(`You`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`lay the obedient ${desc} across your desk`);
+						} else {
+							r.push(`force the resistant ${desc} to lie atop your desk`);
+						}
+						r.push(`and gently`);
+						if (V.PC.dick !== 0) {
+							r.push(`slide your hard cock`);
+						} else {
+							r.push(`push a special strap-on`);
+						}
+						r.push(``);
+						if (slave.vagina >= 0) {
+							r.push(`inside ${his} womanhood.`);
+						} else {
+							r.push(`up ${his} butt.`);
+						}
+						r.push(`You take ${him} with care, enjoying ${his} body`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`and doing your best to give ${him} pleasure, too.`);
+						} else {
+							r.push(`without hurting ${him}.`);
+						}
+						if (V.PC.dick !== 0) {
+							r.push(`You empty your balls inside ${him}, thrusting in as far as you can go`);
+						} else {
+							r.push(`The strap-on contains a reservoir of faux ejaculate, and you give it to ${him}`);
+						}
+						r.push(`as you climax. When ${he} feels the hot liquid jet into ${him},`);
+						if (slave.devotion > 20) {
+							if (slave.fetish === "pregnancy") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${he} gasps with unaccustomed pleasure, and climaxes so strongly that ${he} cries a little from the pain in ${his} flexing abs. <span class="green">${He}'s an impregnation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${he} climaxes, ${his} impregnation fetish displayed cutely on ${his} face.`);
+								}
+								r.push(`As you ${him} go, ${he} feels`);
+								if (V.PC.dick !== 0) {
+									r.push(`your`);
+								} else {
+									r.push(`the fake`);
+								}
+								r.push(`cum dripping out of ${him}, and ${he} <span class="hotpink">feels like your property.</span>`);
+								slave.devotion += 5;
+							} else {
+								r.push(`${he} gasps and does ${his} best to relax, accepting the flow.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't an impregnation fetishist, but ${he} is <span class="hotpink">willing to submit</span> to have ${his} body used as your receptacle.`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "pregnancy") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${he} shudders with a sensation of perverted pleasure, and climaxes despite all ${his} feelings about the situation. <span class="green">${He}'s an impregnation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${he} climaxes, ${his} impregnation fetish forcing ${him} to feel pleasure ${his} mind would prefer to reject.`);
+								}
+								r.push(`As you ${him} go, ${he} cries openly,`);
+								if (V.PC.dick !== 0) {
+									r.push(`your`);
+								} else {
+									r.push(`the fake`);
+								}
+								r.push(`cum dripping out of ${him}, and ${he} <span class="hotpink">feels like your property.</span>`);
+								slave.devotion += 4;
+							} else {
+								r.push(`${he} groans and struggles a little, disgusted to be filled by your fluids so immediately.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't an impregnation fetishist, and is <span class="gold">afraid</span> that ${he}'ll be treated as nothing more than your receptacle.`);
+								slave.trust -= 4;
+							}
+						}
+						r.push(VCheck.Simple());
+						return r.join(" ");
+					},
+
+				});
+			}
+
+			if (V.seePreg !== 0 && canImpreg(slave, V.PC)) {
+				choice({
+					linkName: `Impregnate ${him}`,
+					result(slave) {
+						const el = new DocumentFragment();
+						let r = [];
+						r.push(`You perform a careful medical examination to verify fertility, and then`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`lay the obedient ${desc} across your desk`);
+						} else {
+							r.push(`restrain the resistant ${desc} in your office with ${his} ass in the air`);
+						}
+						r.push(`and gently slide your hard cock`);
+						if (slave.mpreg === 1) {
+							r.push(`up ${his} butt.`);
+						} else {
+							r.push(`inside ${his} womanhood.`);
+						}
+						r.push(`You take ${him} with care, enjoying ${his} body`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`and doing your best to give ${him} pleasure, too.`);
+						} else {
+							r.push(`without hurting ${him}.`);
+						}
+						r.push(`You empty your balls inside ${him}, thrusting in as far as you can go as you climax. When ${he} feels the hot liquid jet into ${him},`);
+						if (slave.devotion > 20) {
+							if (slave.fetish === "pregnancy") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${he} gasps with unaccustomed pleasure, and climaxes so strongly that ${he} cries a little from the pain in ${his} flexing abs. <span class="green">${He}'s an impregnation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${he} climaxes, ${his} impregnation fetish displayed cutely on ${his} face.`);
+								}
+								r.push(`As you ${him} go, ${he} feels your cum dripping out of ${him}, and ${he} <span class="hotpink">feels like your property.</span>`);
+								slave.devotion += 5;
+							} else {
+								r.push(`${he} gasps and does ${his} best to relax, accepting the flow.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't an impregnation fetishist, but ${he} is <span class="hotpink">willing to submit</span> to have ${his} body used as your receptacle.`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "pregnancy") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${he} shudders with a sensation of perverted pleasure, and climaxes despite all ${his} feelings about the situation. <span class="green">${He}'s an impregnation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${he} climaxes, ${his} impregnation fetish forcing ${him} to feel pleasure ${his} mind would prefer to reject.`);
+								}
+								r.push(`As you ${him} go, ${he} cries openly, your cum dripping out of ${him}, and ${he} <span class="hotpink">feels like your property.</span>`);
+								slave.devotion += 4;
+							} else {
+								r.push(`${he} groans and struggles a little, disgusted to be filled by your fluids so immediately.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't an impregnation fetishist, and is <span class="gold">afraid</span> that ${he}'ll be treated as nothing more than your receptacle.`);
+								slave.trust -= 4;
+							}
+						}
+
+						App.UI.DOM.appendNewElement("p", el, r.join(" "));
+						r = [];
+						r.push(`Whenever you feel able, you drain your balls into ${his}`);
+						if (slave.mpreg === 1) {
+							r.push(`ass,`);
+						} else {
+							r.push(`cunt,`);
+						}
+						r.push(`only releasing ${him} for other duties when scans verify a fertilized ovum.`);
+						if (slave.intelligence + slave.intelligenceImplant < -50) {
+							r.push(`${He} didn't properly understand the scans, so ${he} just thought it was bound, forced sex; ${he} won't realize what happened for some weeks at least.`);
+						} else if (slave.fetish === "pregnancy") {
+							r.push(`${He} is thrilled to imagine ${he} might be pregnant.`);
+						} else {
+							r.push(`${He} wasn't sure what this was all about, but ${he} has ${his} suspicions.`);
+						}
+						if (slave.mpreg === 1) {
+							r.push(VCheck.Anal());
+						} else {
+							r.push(VCheck.Vaginal());
+						}
+						if (V.arcologies[0].FSRestart !== "unset" && V.eugenicsFullControl !== 1) {
+							r.push(`The Societal Elite <span class="green">disapprove</span> of this breach of eugenics.`);
+							V.failedElite += 5;
+						} else if (V.arcologies[0].FSGenderFundamentalist !== "unset" && slave.mpreg === 0) {
+							r.push(`Society <span class="green">approves</span> of your promptly putting a new slave in ${him}; this advances the idea that all slaves should bear their masters' babies.`);
+							FutureSocieties.Change("GenderFundamentalist", 2);
+						} else if (V.arcologies[0].FSGenderFundamentalist !== "unset") {
+							r.push(`Society <span class="red">is disgusted</span> by you promptly knocking up ${his} ass; babies come from women, not men.`);
+							FutureSocieties.Change("GenderFundamentalist", -2);
+						}
+						App.UI.DOM.appendNewElement("p", el, r.join(" "));
+						slave.preg = 1;
+						slave.pregType = setPregType(slave);
+						slave.pregWeek = 1;
+						slave.pregKnown = 1;
+						slave.pregSource = -1;
+						WombImpregnate(slave, slave.pregType, -1, 1);
+						return el;
+					},
+				});
+			}
+			lineBreak();
+			if (slave.vagina === 0) {
+				choice({
+					linkName: `Tie ${him} up and take ${his} virginity`,
+					result(slave) {
+						const r = [];
+						if (slave.devotion < -10) {
+							if (isAmputee(slave)) {
+								r.push(`You secure ${his} struggling, screeching body to the couch next to your desk with ${his} vagina exposed.`);
+							} else {
+								r.push(`You cuff ${his}`);
+								if (hasAnyArms(slave)) {
+									r.push(wrists);
+									if (hasAnyLegs(slave)) {
+										r.push(`and`);
+									}
+								}
+								if (hasAnyLegs(slave)) {
+									r.push(ankles);
+								}
+								r.push(`and secure ${his} struggling, screeching body to the couch next to your desk with ${his} legs spread.`);
+							}
+							r.push(`${He} wriggles and protests as you take ${his} virginity. ${He}'s certainly entered your service in a way that colors ${his} impression of you <span class="mediumorchid">with hatred</span> and <span class="gold">fear.</span> <span class="lime">${His} tight little pussy has been broken in.</span>`);
+							slave.devotion -= 5;
+						} else {
+							if (isAmputee(slave)) {
+								r.push(`You secure ${his} unresisting body to the couch next to your desk with ${his} vagina exposed.`);
+							} else {
+								r.push(`You cuff ${his}`);
+								if (hasAnyArms(slave)) {
+									r.push(wrists);
+									if (hasAnyLegs(slave)) {
+										r.push(`and`);
+									}
+								}
+								if (hasAnyLegs(slave)) {
+									r.push(ankles);
+								}
+								r.push(`and secure ${his} unresisting body to the couch next to your desk with ${his} legs spread.`);
+							}
+							r.push(`${He} writhes and moans as you enter ${his} virgin pussy. You might not have even had to restrain ${him} for this, but being tied up and deflowered sends ${him} a message. ${He}'s certainly entered your service in a way that colors ${his} impression of you <span class="hotpink">with pain</span> and <span class="gold">fear.</span> <span class="lime">${His} tight little pussy has been broken in.</span>`);
+							slave.devotion += 5;
+						}
+						r.push(VCheck.Vaginal());
+
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.devotion < -10 && slave.anus === 0) {
+				choice({
+					linkName: `Tie ${him} up and rape ${his} virgin ass`,
+					result(slave) {
+						const r = [];
+						if (isAmputee(slave)) {
+							r.push(`You secure ${his} struggling, screeching body to the couch next to your desk with ${his} butt sticking out.`);
+						} else {
+							r.push(`You cuff ${his}`);
+							if (hasAnyArms(slave)) {
+								r.push(wrists);
+								if (hasAnyLegs(slave)) {
+									r.push(`and`);
+								}
+							}
+							if (hasAnyLegs(slave)) {
+								r.push(ankles);
+							}
+							r.push(`and secure ${his} struggling, screeching body to the couch next to your desk so that ${he}'s straddling the back of the couch with ${his} butt sticking out.`);
+						}
+						r.push(`${He} wriggles and protests until ${he} feels the lubricated head of`);
+						if (V.PC.dick !== 0) {
+							r.push(`your dick`);
+						} else {
+							r.push(`a strap-on`);
+						}
+						r.push(`pushing against ${his} virgin butthole, at which point ${he} desperately tries to break free. ${He} fails, but ${he} never stops writhing and begging you to take`);
+						if (V.PC.dick !== 0) {
+							r.push(`your cock`);
+						} else {
+							r.push(`the phallus`);
+						}
+						r.push(`out of ${his} asshole. ${He}'s certainly entered your service in a way that colors ${his} impression of you <span class="mediumorchid">with hatred</span> and <span class="gold">fear.</span> <span class="lime">${His} tight little ass has been broken in.</span>`);
+						slave.devotion -= 5;
+						slave.trust -= 10;
+						slave.anus = 1;
+						r.push(VCheck.Anal());
+						return r.join(" ");
+					},
+
+				});
+			} else if ((slave.anus === 0)) {
+				choice({
+					linkName: `Tie ${him} up and break ${his} ass in`,
+					result(slave) {
+						const r = [];
+						if (isAmputee(slave)) {
+							r.push(`You secure ${his} unresisting body to the couch next to your desk with ${his} butt sticking out.`);
+						} else {
+							r.push(`You cuff ${his}`);
+							if (hasAnyArms(slave)) {
+								r.push(wrists);
+								if (hasAnyLegs(slave)) {
+									r.push(`and`);
+								}
+							}
+							if (hasAnyLegs(slave)) {
+								r.push(ankles);
+							}
+							r.push(`and secure ${his} unresisting body to the couch next to your desk so that ${he}'s straddling the back of the couch with ${his} butt sticking out.`);
+						}
+						r.push(`${He} lies obediently still until ${he} feels the lubricated head of your cock pushing against ${his} virgin butthole, at which point ${he} starts in surprise. When the pain of the buttfuck gets to be too much ${he} begs you to use ${his}`);
+						if (slave.dick === 0) {
+							r.push(`pussy`);
+						} else {
+							r.push(`mouth`);
+						}
+						r.push(`instead. ${He} gasps and cries as you slowly push your cock all the way up ${his} tight little butt, and in no time you fill ${his} rectum with cum. ${He}'s certainly entered your service in a way that colors ${his} impression of you <span class="mediumorchid">with pain</span> and <span class="gold">fear.</span> <span class="lime">${His} tight little ass has been broken in.</span>`);
+						slave.devotion -= 5;
+						slave.trust -= 10;
+						slave.anus = 1;
+						r.push(VCheck.Anal());
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.fetish !== "mindbroken") {
+				choice({
+					linkName: `Break ${him} in publicly`,
+					result(slave) {
+						const r = [];
+						r.push(`You`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`order the obedient slave to follow you`);
+						} else {
+							r.push(`bring the resistant slave`);
+						}
+						r.push(`out into the public areas below your penthouse, shove the nude ${desc} over a convenient handrail, and stick`);
+						if (V.PC.dick !== 0) {
+							r.push(`your dick`);
+						} else {
+							r.push(`a strap-on`);
+						}
+						if (slave.vagina >= 0) {
+							if (slave.vagina >= 3) {
+								r.push(`inside ${his} gaping cunt, which is loose enough that the rough treatment doesn't bother ${him} physically.`);
+							} else if (slave.vagina >= 2) {
+								r.push(`inside ${his} accommodating pussy, which is welcoming enough that the rough insertion doesn't hurt ${him} physically.`);
+							} else {
+								r.push(`inside ${his} tight little pussy, making the poor ${girl} writhe with discomfort.`);
+							}
+						} else {
+							if (slave.anus >= 3) {
+								r.push(`inside ${his} lewd anal slit, which is so used to being fucked that the rough treatment doesn't bother ${him} physically.`);
+							} else if (slave.anus >= 2) {
+								r.push(`up ${his} welcoming butt, which is loose enough that the rough insertion doesn't hurt ${him} physically.`);
+							} else {
+								r.push(`up ${his} poor little butthole, making the poor ${girl} struggle desperately with anal pain.`);
+							}
+						}
+						r.push(`You take ${him}, hard, while passersby stare at ${him} as ${he} gets pounded over a rail.`);
+						if (slave.devotion > 20) {
+							if (slave.fetish === "humiliation") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${He} struggles under the stares, blushing furiously as ${he} takes a fuck in the open. ${He} orgasms despite ${himself}; <span class="green">${he}'s a humiliation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`As a humiliation fetishist, ${he} gets off on the stares, blushing cutely as ${he} takes a fuck in public.`);
+								}
+								r.push(`${He} follows you back inside your penthouse, leaking`);
+								if (V.PC.dick !== 0) {
+									r.push(`your cum,`);
+								} else {
+									r.push(`lube,`);
+								}
+								r.push(`<span class="hotpink">feeling like a slut.</span>`);
+								slave.devotion += 5;
+							} else {
+								r.push(`${He} takes the fuck obediently, and does ${his} very best to ignore the stares.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't a humiliation fetishist, but ${he} is <span class="hotpink">willing to submit</span> to being used as a sex slave in public.`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "humiliation") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${His} struggles slowly diminish as the stares have a horribly embarrassing effect on ${him}. ${He} orgasms and then starts to cry, realizing what just happened; <span class="green">${he}'s a humiliation fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`As a humiliation fetishist, ${he} can't stop ${himself} from getting off on being fucked in public.`);
+								}
+								r.push(`As you pull ${him} back towards your penthouse, ${he} leaks`);
+								if (V.PC.dick !== 0) {
+									r.push(`your cum`);
+								} else {
+									r.push(`lube`);
+								}
+								r.push(`and <span class="hotpink">feels like a slut.</span>`);
+								slave.devotion += 4;
+							} else {
+								r.push(`${He} takes the fuck unwillingly, crying and doing ${his} best to ignore the stares.`);
+								if (slave.fetishKnown === 0) {
+									r.push(`It seems ${he}`);
+								} else {
+									r.push(`${He}`);
+								}
+								r.push(`isn't a humiliation fetishist, and is <span class="gold">rapidly learning what it means</span> to be your sex ${slave}`);
+								slave.trust -= 4;
+							}
+						}
+						r.push(VCheck.Simple());
+
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.devotion > 20) {
+				choice({
+					linkName: `Give ${him} a chance to impress`,
+					result(slave) {
+						const r = [];
+						const num = jsRandom(1, 100);
+						r.push(`${He} seems reasonably obedient, so you give ${him} a chance to impress.You remain seated and offer ${him} your`);
+						if (V.PC.dick !== 0) {
+							r.push(`half - erect cock`);
+							if (V.PC.vagina !== -1) {
+								r.push(`and ready pussy`);
+							}
+						} else {
+							r.push(`pussy, wet with arousal at the prospect of breaking in a new slut`);
+						}
+						r.push(`.${He} comes over obediently and gets`);
+						if (hasBothLegs(slave)) {
+							r.push(`on ${his} knees`);
+						} else {
+							r.push(`into position`);
+						}
+						r.push(`.${He} works hard and is clearly doing ${his} absolute best to please, so you ${him} finish you with ${his} mouth.`);
+						if (V.PC.dick !== 0) {
+							if (num > 20) {
+								r.push(`${He}`);
+								if (!canSee(slave)) {
+									r.push(`looks up at`);
+								} else {
+									r.push(`angles ${his} head towards`);
+								}
+								r.push(`you as ${he} swallows`);
+							} else if (num > 40) {
+								r.push(`${He} even swallows your cum without being told`);
+							} else if (num > 60) {
+								r.push(`${He} smiles sloppily as your seed slowly dribbles from ${his} mouth and down ${his} chin`);
+							} else if (num > 80) {
+								r.push(`${He} struggles to contain your seed and makes a mess all over ${his} face as a result`);
+							} else {
+								r.push(`${He} struggles to contain your seed with ${his} mouth and it splatters over the floor as a result`);
+							}
+						} else {
+							r.push(`${He} worked ${his} tongue hard and did ${his} best`);
+						}
+						r.push(`.You spend the rest of the day with ${him}`);
+						if (hasAnyLegs(slave)) {
+							r.push(`kneeling`);
+						} else {
+							r.push(`resting`);
+						}
+						r.push(`on a cushion next to your chair.${He} learns obedience, but also learns that you are < span class="mediumaquamarine" > fair</span > and < span class="hotpink" > reasonable.</span >`);
+						slave.devotion += 4;
+						slave.trust += 4;
+						actX(slave, "oral");
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.fetish !== "mindbroken") {
+				if (slave.bellyPreg >= 5000) {
+					choice({
+						linkName: `Make sure ${he} knows pregnant women can still enjoy sex`,
+						result(slave) {
+							const r = [];
+							r.push(`You introduce ${him} to obedience and proper manners regarding ${his} ${WrittenMaster(slave)} before sending ${him} off for a physical.That night, ${he} 's returned to your room, and finds you doing business on a tablet in bed. ${He} looks doubtful, but obeys when you direct ${him} to get into bed${V.PC.dick === 0 ? `, even after ${he} realizes you're wearing a strap-on` : ``}. You turn out the light and spoon ${him} from behind, kissing ${his} neck and ears, cupping ${his} swollen breasts, and running your hands across ${his} pregnant belly with its taut ${slave.skin} skin. ${He}'s awkward at first but ${his} body responds to the tenderness. Before long ${he}'s humping ${his} pussy back and forth against`);
+							if (V.PC.dick !== 0) {
+								r.push(`your cock.`);
+							} else {
+								r.push(`the strap-on.`);
+							}
+							r.push(`You begin to gently work`);
+							if (V.PC.dick !== 0) {
+								r.push(`your dickhead`);
+							} else {
+								r.push(`its tip`);
+							}
+							r.push(`up ${his} used pussy. ${He}'s unsure of ${himself}, but you keep ${him} nice and relaxed. After several minutes of gentle loving, ${he}'s nothing but a satisfied puddle in your arms. ${He} believes that ${he} can <span class="mediumaquamarine">trust</span> you won't harm ${him} or ${his} ${(slave.pregType === 1) ? `child` : `children`}.`);
+
+							slave.trust += 5;
+							r.push(VCheck.Vaginal());
+							if (slave.fetish === "none" && jsRandom(1, 100) > 60) {
+								slave.fetish = "pregnancy";
+								slave.fetishStrength = 10;
+							}
+							return r.join(" ");
+						},
+
+					});
+				}
+
+				choice({
+					linkName: `Tease ${his} nipples`,
+					result(slave) {
+						const r = [];
+						r.push(`You`);
+						if (slave.devotion > 20 || slave.trust < -20) {
+							r.push(`instruct the obedient ${desc}`);
+						} else {
+							r.push(`force the reluctant ${desc}`);
+						}
+						r.push(`to lie atop your desk in front of you and go back to business.After a few minutes, you extend an idle hand and begin to fondle ${his}`);
+						if (slave.boobs > 2000) {
+							r.push(`massive tits,`);
+						} else if (slave.boobs > 400) {
+							r.push(`healthy breasts,`);
+						} else {
+							r.push(`cute little boobs,`);
+						}
+						r.push(`continuing your work with your other hand.You quickly focus your fiddling on ${his} ${slave.nipples} nipples, the stimulation`);
+						if (slave.nipples === "fuckable") {
+							if (slave.devotion > 20) {
+								r.push(`quickly engorging them around your fingers.`);
+							} else {
+								r.push(`slowly engorging them around your fingers despite ${his} feelings.`);
+							}
+						} else {
+							r.push(`bringing them`);
+							if (slave.devotion > 20) {
+								r.push(`quickly erect.`);
+							} else {
+								r.push(`slowly erect despite ${his} feelings.`);
+							}
+						}
+						if (slave.devotion > 20) {
+							if (slave.fetish === "boobs") {
+								if (slave.fetishKnown === 0) {
+									r.push(`The slave quickly becomes aroused.Before long, an orgasm convulses ${his} entire body, jiggling the feminine flesh under your hand delightfully. < span class="green" > ${He} 's a breast fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${His} breast fixation makes this teasing quite pleasurable for ${him}, almost as good as a handjob. Before long, an orgasm convulses ${his} entire body, jiggling the feminine flesh under your hand delightfully.`);
+								}
+								r.push(`When you tell ${him} to go, ${he} carefully rolls off your desk in such a way that <span class="hotpink">${his} nipples graze your cheek.</span>`);
+								slave.devotion += 5;
+							} else {
+								r.push(`${He} accepts being treated as a desktop stress relief toy. ${He}'s no breast fetishist, but ${he}'s <span class="hotpink">willing to be used</span> as a sex object.`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "boobs") {
+								if (slave.fetishKnown === 0) {
+									r.push(`The slave slowly becomes aroused despite ${his} reluctance. Soon, ${he} can no longer hide ${his} horniness and begins to pant, openly enjoying the sensations. <span class="green">${He}'s a breast fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`The slave slowly becomes aroused despite ${his} reluctance. As a breast fetishist, playing with ${his} nipples is an excellent way to overcome ${his} petty resistance to being a sex object.`);
+								}
+								r.push(`When you tell ${him} to go, ${he} gets off your desk gingerly, since ${he}'s almost painfully aroused. <span class="hotpink">${He} feels like quite a slut</span> for wanting a fuck this badly after a little nipple play.`);
+								slave.devotion += 4;
+							} else {
+								r.push(`${He} expects something worse to happen immediately, and slowly relaxes when ${he} realizes that ${he}'s nothing more than your desktop stress relief toy, at least for now. ${He} experiences the usual effects of nipple play, but seems <span class="gold">somewhat frightened</span> to be dehumanized so thoroughly.`);
+								slave.devotion -= 4;
+							}
+						}
+
+						return r.join(" ");
+					},
+
+				});
+
+				if (V.PC.vagina !== -1 && slave.dick > 0 && canAchieveErection(slave)) {
+					choice({
+						linkName: `Dominate ${his} penis and demonstrate ${his} place`,
+						result(slave) {
+							const r = [];
+							r.push(`As you inspect ${him}, you take note of ${his} fully`);
+							if (slave.dick === 1) {
+								r.push(`functional tiny dick.`);
+							} else if (slave.dick === 2) {
+								r.push(`functional cute dick.`);
+							} else if (slave.dick === 3) {
+								r.push(`functional dick.`);
+							} else if (slave.dick === 4) {
+								r.push(`functional big dick.`);
+							} else if (slave.dick === 5) {
+								r.push(`functional impressive dick.`);
+							} else if (slave.dick === 6) {
+								r.push(`functional huge dick.`);
+							} else {
+								r.push(`functional.`); // Old SC code only went to 6.  I'm adding this to fix the period, but this needs writing assuming we can go beyond 6.
+							}
+							r.push(`You roughly push ${him} up against a wall and begin fondling ${his} penis, grinning at the look of panic growing on ${his} face.`);
+							if (slave.dick === 1) {
+								r.push(`You scoff at ${him} as ${his} micropenis barely fills your palm.`);
+							} else if (slave.dick === 2) {
+								r.push(`You laugh at ${him} as ${his} small penis fills your palm.`);
+							} else if (slave.dick === 3) {
+								r.push(`You nod at ${him} as ${his} penis fills your hand.`);
+							} else if (slave.dick === 4) {
+								r.push(`You smirk at ${him} as ${his} big penis fills your hand.`);
+							} else if (slave.dick === 5) {
+								r.push(`You smile widely at ${him}, a dangerous look in your eyes, as you bring another hand to ${his} impressive dick.`);
+							} else if (slave.dick === 6) {
+								r.push(`You grin sadistically at ${him} as ${his} massive dick fills both of your hands.`);
+							}
+							r.push(`As ${he} begins to moan with lust, you grip down tightly and force ${him} to the floor. You straddle ${him} and lower your dripping pussy onto ${his} face${V.PC.dick !== 0 ? `, your erect cock coming to rest on ${his} forehead` : ``}. You continue stroking your toy's rod as ${he} eagerly begins eating you out. As ${his} cock begins to throb, anticipating ${his} upcoming orgasm, you quickly bind the base of ${his} penis, denying ${his} release. You grind your cunt into ${his} face, telling ${him} that YOU are the one who'll be orgasming here, not ${him}. Only once you have initiated the new slave by soaking ${his} face in your cum do you release ${his} dick and lean back to avoid the coming blast. A few strokes later and your hand is coated in ${his} cum. You turn around and order the exhausted ${girl} to clean ${his} cum off your hand`);
+							if (V.PC.dick !== 0) {
+								r.push(`and to finish off ${his} twitching dick`);
+							}
+							r.push(`${he} <span class="hotpink">complies meekly,</span> knowing you are the < span class="gold" > dominant force</span > in ${his} life now.`);
+							slave.devotion += 5;
+							slave.trust -= 3;
+							return r.join(" ");
+						},
+
+					});
+				}
+
+				if (V.PC.belly >= 3000 && hasAnyArms(slave)) {
+					choice({
+						linkName: `Make ${him} worship your pregnancy`,
+						result(slave) {
+							const r = [];
+							r.push(`You aggressively approach ${slave.slaveName}, forcing your pregnancy directly into ${his}`);
+							if (slave.height > 175) {
+								r.push(`stomach`);
+							} else if (slave.height < 155) {
+								r.push(`face`);
+							} else {
+								r.push(`chest`);
+							}
+							r.push(`until ${he} has no choice but to be pushed to the ground. Standing over ${him}, staring ${him} down as ${he} peeks around your firm globe of a middle, you order ${him} to worship your pregnancy.`);
+							if (slave.fetish === "pregnancy") {
+								r.push(`${He} complies eagerly. ${He} begins with sucking your popped navel before running ${his} tongue across the taut, smooth surface of your pregnancy. Once ${he} has finished with your belly, ${he} lowers ${himself} under it to begin work on your needy pussy. Before long, ${his} overzealous efforts have you quaking in pleasure, rousing your ${(V.PC.pregType === 1) ? `child` : `children`}. Once ${he} finishes you off, ${he} returns to rubbing your belly, soothing your rowdy child`);
+								if (V.PC.pregType > 1) {
+									r.push(`ren`);
+								}
+								r.push(`and <span class="hotpink">solidifying ${his} place</span> beneath you.`);
+								slave.devotion += 15;
+							} else {
+								r.push(`${He} shifts ${his} gaze between your middle and your face, not sure what to do. Losing patience, you toss a tube of cream at ${him}. ${He} shakily massages it onto your stretched skin, missing spots, much to your pleasure. With reason, you force ${him} onto ${his} back, turn around, and plant your needy cunt directly onto ${his} face. Struggling to breath under your weight, ${he} begins eating you out in desperation. After coaxing ${him} to massage your belly as ${he} does, you quickly climax across ${his} face and gently lift yourself off the coughing ${girl}. ${He} now <span class="hotpink">understands ${his} place in life</span> and is <span class="gold">terrified</span> about what ${he} will have to do if ${he} wants to survive.`);
+								slave.devotion += 5;
+								slave.trust -= 5;
+							}
+
+							return r.join(" ");
+						},
+
+					});
+					if (V.PC.preg >= 28) {
+						if (V.PC.pregMood === 2 && V.PC.vagina > 0 && canPenetrate(slave)) {
+							choice({
+								linkName: `${He} has a dick and you need it`,
+								result(slave) {
+									const r = [];
+									r.push(`You aggressively approach ${slave.slaveName}, forcing your pregnancy directly into ${his}`);
+									if (slave.height > 175) {
+										r.push(`stomach`);
+									} else if (slave.height < 155) {
+										r.push(`face`);
+									} else {
+										r.push(`chest`);
+									}
+									r.push(`until ${he} has no choice but to be pushed to the ground. You quickly straddle ${his} face, forcing your oozing cunt over ${his} mouth as you eagerly stroke ${his} cock to full length.`);
+									if (slave.fetish === "pregnancy") {
+										r.push(`${He} groans with disappointment as your pregnant pussy leaves ${his} reach, though ${his} displeasure is short lived as you greedily take ${his} entire dick into your aching snatch. You ride ${him} mercilessly, frequently smacking ${him} with your heavy belly. ${He} loves every minute of it, especially when ${he} feels your body tense up as ${he} lets loose ${his} load deep into you. Where most slaves would be begging for mercy, ${he} <span class="hotpink">eagerly complies</span> as you adjust yourself and begin round two. You don't know what came over you, but when you wake up, you find ${he}'s resting peacefully under your gravid mass.`);
+										if (slave.fetishKnown === 0) {
+											r.push(`It seems ${he} likes <span class="green">being a pregnant ${womanP}'s plaything.</span>`);
+											slave.fetishKnown = 1;
+										} else {
+											r.push(`You knew ${he} had a pregnancy fetish and the look on ${his} face confirms it.`);
+										}
+										r.push(`A kick from within startles you from your thoughts; it would appear your child`);
+										if (V.PC.pregType > 1) {
+											r.push(`ren`);
+										}
+										r.push(`agree`);
+										if (V.PC.pregType === 1) {
+											r.push(`s`);
+										}
+										r.push(`that you'll have to have another ride sometime.`);
+										slave.devotion += 15;
+									} else {
+										r.push(`${He} coughs as your pregnant pussy vacates ${his} face, though ${his} relief is short lived as you greedily slam yourself down onto ${his} waiting dick. You ride ${him} mercilessly, frequently smacking ${him} with your heavy belly. ${He} hates every minute of it, choosing to alternate between begging you to stop and just openly weeping. You cum hard as you watch the look on ${his} face as ${he} unwillingly cums deep inside you. ${He} cries out in protest as you continue raping ${him}, but you don't care. All that matters is your satisfaction. This continues until you pass out from orgasmic exhaustion with ${him} still inside you. You are eventually awoken by ${his} desperate struggle to escape from beneath your gravid mass; ${he} quickly regrets ${his} choices as you remount ${him} for one last go. ${He} now <span class="hotpink">better understands ${his} place as a toy</span> and is <span class="gold">terrified</span> of your insatiable lust.`);
+										slave.devotion += 5;
+										slave.trust -= 15;
+									}
+									actX(slave, "penetrative", 5);
+									actX(slave, "oral");
+
+									return r.join(" ");
+								},
+
+							});
+						} else if (V.PC.pregMood === 1 && V.PC.lactation > 0) {
+							choice({
+								linkName: `Take ${him} to your breast`,
+								result(slave) {
+									const r = [];
+									r.push(`You calmly approach ${slave.slaveName}, pulling ${him} into a soothing embrace against your`);
+									if (V.PC.boobs >= 1400) {
+										r.push(`enormous,`);
+										if (V.PC.boobsImplant > 0) {
+											r.push(`fake,`);
+										}
+										r.push(`milk laden breasts.`);
+									} else if (V.PC.boobs >= 1200) {
+										r.push(`huge, `);
+										if (V.PC.boobsImplant > 0) {
+											r.push(`fake, `);
+										}
+										r.push(`milk laden breasts.`);
+									} else if (V.PC.boobs >= 1000) {
+										r.push(`large,`);
+										if (V.PC.boobsImplant > 0) {
+											r.push(`fake,`);
+										}
+										r.push(`milk laden breasts.`);
+									} else if (V.PC.boobs >= 800) {
+										r.push(`big, milk laden breasts.`);
+									} else if (V.PC.boobs >= 650) {
+										r.push(`milk laden breasts.`);
+									} else if (V.PC.boobs >= 500) {
+										r.push(`small, milk laden breasts.`);
+									} else if (V.PC.boobs >= 300) {
+										r.push(`tiny, milk laden breasts.`);
+									} else {
+										r.push(`milk swollen chest.`);
+									}
+									r.push(`${He} tries to squirm away from the moist spot growing under ${his} cheek, but you reveal your nipple and carefully direct ${his} mouth over it.`);
+									if (slave.fetish === "pregnancy") {
+										r.push(`Slowly ${he} begins to suckle from your swollen breast. You gently brush ${his} head as you try to hold back your pleasure — a wasted effort, as a hand sneaks its way to your`);
+										if (V.PC.dick !== 0) {
+											r.push(`growing erection and enthusiastically begins pumping away.`);
+										} else {
+											r.push(`wet pussy and enthusiastically begins rubbing your clit.`);
+										}
+										r.push(`You clutch your pervy ${girl} closer to you as ${he} caresses your pregnancy with one hand and gets you off with the other. Before long you find yourself bucking your hips with lust, a queue for you to release ${him} from your nipple so ${he} may slide down your gravid dome of a belly to finish you off. Happy to serve ${his} pregnant ${WrittenMaster(slave)}, ${he} returns to your chest, happy to relieve you of the pressure building in your neglected breast.`);
+										if (slave.fetishKnown === 0) {
+											r.push(`Judging by that show, <span class="green">${he} savors getting to be with a pregnant ${womanP}.</span>`);
+											slave.fetishKnown = 1;
+										} else {
+											r.push(`You knew ${he} had a pregnancy fetish and ${his} eagerness to serve a pregnant ${womanP} proves that.`);
+										}
+										r.push(`A kick from within startles you from your thoughts; as you reach to soothe your ${V.PC.pregType === 1 ? `child` : `children`}, you find your new slave <span class="mediumaquamarine">already doting on them.</span> ${He}'s already starting to <span class="hotpink">show understanding of ${his} place.</span>`);
+										slave.devotion += 15;
+										slave.trust += 15;
+									} else if (slave.fetish === "boobs") {
+										r.push(`Eagerly ${he} begins to suckle from your swollen breast. You gently brush ${his} head as you try to hold back your pleasure — a wasted effort, as a hand sneaks its way to your neglected breast. ${He} massages it, careful not to encourage your lactation too much, as ${he} greedily sucks you dry. ${He} wastes no time in swapping to your other nipple, shifting ${his} ministrations to the one the just left. By the time your reserves are tapped out, both you and ${he} are quite content. You permit ${him} to rest against your chest for a little before you send ${him} on ${his} way.`);
+										if (slave.fetishKnown === 0) {
+											r.push(`Judging by ${his} enthusiasm, <span class="green">${he} savors getting ${his} mouth close to a pair of boobs.</span>`);
+											slave.fetishKnown = 1;
+										} else {
+											r.push(`You knew ${he} had a breast fetishist and ${his} eagerness to lighten a lactating ${womanP} proves that.`);
+										}
+										r.push(`A kick from within startles you from your thoughts; you pat your gravid middle, reassuring your child`);
+										if (V.PC.pregType > 1) {
+											r.push(`ren`);
+										}
+										r.push(`that you'll make sure to save some milk for them. ${He}'s already starting to <span class="hotpink">show understanding of ${his} place</span> and even <span class="mediumaquamarine">beginning to build trust</span> with you.`);
+										slave.devotion += 15;
+										slave.trust += 15;
+									} else {
+										r.push(`Reluctantly ${he} begins to suckle from your swollen breast. You gently brush ${his} head as you try to hold back your pleasure, but it is too much. As ${he} drinks deeper, you begin moaning with relief. At first ${he} tenses at`);
+										if (canHear(slave)) {
+											r.push(`the sound,`);
+										} else {
+											r.push(`your body's shuddering,`);
+										}
+										r.push(`fearing punishment, but soon realizes you have no intent on <span class="mediumaquamarine">harming ${him}.</span> ${He} allows you to dote over ${him} as if ${he} were`);
+										if (slave.father === -1 || slave.mother === -1) {
+											r.push(`a`);
+										} else {
+											r.push(`your`);
+										}
+										r.push(`child, carefully moving to your other breast once the first runs dry. As ${he} drinks, ${he} begins to massage your taut middle, ${his} touch soft and gentle. When you both finish, you push ${him}`);
+										if (hasBothLegs(slave)) {
+											r.push(`to ${his} feet`);
+										} else {
+											r.push(`into an upright position`);
+										}
+										r.push(`and send ${him} on ${his} way. <span class="hotpink">${He} stays and offers a hand to help you to your feet.</span> You are surprised by this display; it might be obedience, but ${he} also may view you in your gravid state as someone weak. As ${he} helps you back to your desk, ${he} shoulders all of your weight. It would appear ${he} is putting you first, for now.`);
+										slave.devotion += 5;
+										slave.trust += 5;
+									}
+									V.PC.lactationDuration = 2;
+
+									return r.join(" ");
+								},
+
+							});
+						}
+					}
+				}
+
+				if (slave.belly + V.PC.belly <= 2000) {
+					// got to be able to get close without bumps getting in the way
+					if (V.PC.boobs >= 800 && slave.boobs <= V.PC.boobs - 200 && slave.boobs >= 300) {
+						choice({
+							linkName: `Let your ample bust dominate ${his} pathetic one`,
+							result(slave) {
+								const r = [];
+								r.push(`You quickly strip off your shirt and draw close to ${slave.slaveName}. You grab ${him} as ${he} attempts to back away and pull ${his} tits directly into your own. Both sets of`);
+								if (canSee(slave)) {
+									r.push(`eyes lock onto their pair of breasts,`);
+								} else {
+									r.push(`tits push out against each other,`);
+								}
+								r.push(`or they would have, had yours not utterly eclipsed their rival. Scoffing, you release the confused ${girl} and walk away; leaving ${him} cupping ${his} own breasts and feeling uncertain about ${himself}. ${He} gets caught up in how small they really are, and how much <span class="hotpink">lesser</span> than ${his} ${WrittenMaster(slave)} ${he} is.`);
+								if (slave.fetish === "boobs") {
+									if (slave.fetishKnown === 0) {
+										r.push(`You did note one thing when your breasts touched: ${his} nipples`);
+										if (slave.nipples === "fuckable") {
+											r.push(`tightened around yours.`);
+										} else {
+											r.push(`got very hard.`);
+										}
+										r.push(`<span class="green">Looks like ${he}'s a breast fetishist!</span>`);
+										slave.fetishKnown = 1;
+									}
+								}
+								slave.devotion += 5;
+								return r.join(" ");
+							},
+
+						});
+					}
+
+					if (canAchieveErection(slave) && canSee(slave) && V.PC.dick >= 4 && V.PC.dick > slave.dick && slave.trust >= -20) {
+						if (slave.energy > 95 || (slave.attrXX > 50 && slave.behavioralFlaw !== "hates women" && V.PC.boobs >= 400) || (slave.attrXY > 50 && slave.behavioralFlaw !== "hates men" && V.PC.boobs < 300) || (V.PC.belly >= 1500 && slave.fetish === "pregnancy") || (V.PC.boobs >= 1400 && slave.fetish === "pregnancy")) {
+							choice({
+								linkName: `Show ${him} what a real erection looks like`,
+								result(slave) {
+									const el = new DocumentFragment();
+									let p;
+									const r = [];
+
+									el.append(`${His} arousal is obvious with the hard-on ${he} is sporting, but ${he} still has much to learn about size. You pop your own dick out and draw close to ${slave.slaveName}, grabbing and pulling ${him} close enough for your erections to touch. Both sets of eyes lock onto the crossed swords, or more appropriately, your sword and ${his} dagger. You give the confused ${girl} plenty of time to internalize what a real dick looks like as you <span class="hotpink">thoroughly emasculate</span> ${him}.`);
+									slave.devotion += 5;
+
+									p = document.createElement("p");
+									p.id = "introResult2";
+									p.append(App.UI.DOM.link(
+										`Push ${his} head down`,
+										() => {
+											r.push(`You place a hand on ${his} head and guide ${him}`);
+											if (slave.fetish === "cumslut") {
+												r.push(`downwards, right onto your penis. As you slip deeper into your throat, you find ${him} hungrily sucking your cock like an addict looking for their next hit. You ${him} drain every last drop from your balls before pulling out. ${He} licks the last bit off the tip before opening wide again, <span class="hotpink">begging you for more.</span> <span class="green">You have quite the cum fiend on your hands.</span>`);
+												slave.fetishKnown = 1;
+											} else {
+												r.push(`downwards. ${He} stares at your penis before hesitantly slipping it into ${his} mouth`);
+												if (slave.skill.oral >= 100) {
+													r.push(`and practically sucking the life out of you; it stands to reason that ${he} may have known exactly what ${he} was doing this whole time.`);
+												} else if (slave.skill.oral > 60) {
+													r.push(`and giving you a rather surprising blowjob; ${he}'s well on ${his} way already, it seems.`);
+												} else if (slave.skill.oral > 30) {
+													r.push(`and giving you a forgettable blowjob; ${he}'ll learn how to properly suck dick under your ownership.`);
+												} else {
+													r.push(`and giving you an awful blowjob; ${he}'ll get plenty of practice under your ownership.`);
+												}
+												r.push(`Only once ${he} has swallowed the last of your cum do you allow ${him} to clean ${himself} up and <span class="hotpink">reflect on what ${he} just did.</span>`);
+											}
+											slave.devotion += 5;
+											actX(slave, "oral");
+											jQuery("#introResult2").empty().append(r.join(" "));
+										}
+									));
+									el.append(p);
+									return el;
+								},
+
+							});
+						}
+					}
+				}
+
+				if ((slave.accent >= 3) && (slave.anus < 2) && (slave.intelligence + slave.intelligenceImplant <= 50) && (slave.devotion < 10) && (canSee(slave))) {
+					choice({
+						linkName: `Force understanding of ${his} situation past the language barrier`,
+						result(slave) {
+							const r = [];
+							r.push(`With a rudimentary understanding of ${V.language}, at best, it's doubtful that ${he} understands exactly what ${his} immediate future is likely to entail. You resolve to ${him} in on the secret, and stand up, drawing ${his} attention to your hands with gestures. You point first at ${him}, and then at your left hand, which you form into a hole shape. Then you point at yourself with your left hand, and then at your right hand, whose index and middle fingers you extend, straight and together. Then, you take your left hand hole and your right hand phallus, and fuck the former with the latter, hard enough that the sex act depicted is obviously fun for your right hand only. ${He} watches raptly, and when comprehension dawns across ${his} face, <span class="gold">${he} starts to sob.</span>`);
+							if (slave.vagina > -1) {
+								r.push(`${He} indicates ${his} vagina tentatively, almost hopefully, but you shake your head, and emphasize the circular, anus-like shape of your left hand. ${He} cries harder.`);
+							} else {
+								r.push(`${He} wilts, pressing ${his} thighs together in an unconscious attempt to shield ${his} anus from its future as a target for ${his} ${WrittenMaster(slave)}'s attentions.`);
+							}
+							slave.trust -= 10;
+
+							return r.join(" ");
+						},
+
+					});
+				}
+
+				if (slave.inflation === 0 && hasAnyArms(slave) && canTaste(slave) && V.arcologies[0].FSHedonisticDecadenceResearch === 1) {
+					choice({
+						linkName: `Offer ${him} a slave-food cookie`,
+						result(slave) {
+							const r = [];
+							r.push(`You pull a big plate of specially formulated cookies from your desk, place it before ${him} and invite ${him} to have some. ${He} looks between the plate and you, unsure of what to make of this development, but eventually grabs one and`);
+							if (slave.behavioralFlaw === "gluttonous") {
+								r.push(`shoves it into ${his} mouth. ${He} nearly chokes in surprise at the taste before greedily reaching for another.`);
+							} else if (slave.behavioralFlaw === "anorexic") {
+								r.push(`nibbles the edge of it. ${He} gasps in surprise at the taste before greedily shoving the entire thing into ${his} mouth and reaching for another.`);
+							} else {
+								r.push(`takes a bite. ${He} gasps in surprise at the taste before greedily shoving the entire thing into ${his} mouth and reaching for another.`);
+							}
+							r.push(`In minutes, ${he}'s managed to devour every last cookie on the plate. Before ${he} can even pout that they're all gone, you place another pair of plates`);
+							if (canSmell(slave)) {
+								r.push(`under ${his} nose.`);
+							} else {
+								r.push(`in front of ${him}.`);
+							}
+							r.push(`${He} promptly rushes for them,`);
+							if (slave.behavioralFlaw === "gluttonous") {
+								r.push(`paying no mind to ${his} bloated belly bumping into your desk, and resumes stuffing ${himself}.`);
+							} else if (slave.behavioralFlaw === "anorexic") {
+								r.push(`only to recoil in shock when ${his} bloated belly`);
+								if (slave.belly >= 1000) {
+									r.push(`reaches your desk earlier than expected.`);
+								} else {
+									r.push(`pushes into your desk.`);
+								}
+								r.push(`${He} gropes it uncomfortably before being overwhelmed by the addictive substance and going back to stuffing ${himself}.`);
+							} else {
+								r.push(`barely noticing ${his} bloated belly`);
+								if (slave.belly >= 1000) {
+									r.push(`hanging heavier than ever,`);
+								} else {
+									r.push(`bumping into your desk,`);
+								}
+								r.push(`and resumes stuffing ${himself}.`);
+							}
+							r.push(`By the time ${he}'s done, ${his} belly`);
+							if (slave.belly >= 1000) {
+								r.push(`is positively heaving. You can clearly see the defined mass of ${his} stomach resting atop ${his} swollen abdomen.`);
+							} else {
+								r.push(`is positively huge. You can clearly see ${his} swollen stomach at the top of the mass of food inside ${him}.`);
+							}
+							r.push(`${He} collapses onto ${his} rear and belches loudly.`);
+							if (V.arcologies[0].FSDegradationist !== "unset") {
+								r.push(`It doesn't take long for the tainted food to do its thing. ${He} groans loudly and clutches ${his} bulging midriff, ${his} body in severe distress thanks to the modified slave food. ${He} rolls onto ${his} back and does everything ${he} can to soothe the turmoil growing inside ${him}. You know it won't do any good, but ${he} doesn't. ${He}'ll spend however long it takes ${his} body to digest the food in anguish; ${he}'ll regret ever touching the stuff. But you know better, ${his} growing addiction will have ${him} scarfing it down at every chance, but for now, all ${he} can do is <span class="mediumorchid">blame you</span> and <span class="gold">curse your trickery.</span>`);
+								slave.devotion -= 20;
+								slave.trust -= 20;
+							} else {
+								if (slave.behavioralFlaw === "gluttonous") {
+									r.push(`${He} <span class="mediumaquamarine">happily pats ${his} full belly;</span> ${he}'s going to <span class="hotpink">like it here.</span>`);
+									slave.devotion += 10;
+									slave.trust += 10;
+								} else if (slave.behavioralFlaw === "anorexic") {
+									r.push(`Only then does ${he} regain composure enough to realize what ${he}'s done. ${He} <span class="mediumorchid"> bursts into tears</span> as ${he} rubs ${his} disgustingly full belly. ${He} glares at you, only to catch you toying with even more food. ${He} begins to <span class="gold">tremble</span> when ${he} realizes how easily you did this to ${him}. You're sure ${he}'ll change ${his} tune in time, since you know ${he}'ll be unable to resist sneaking treats.`);
+									slave.devotion -= 10;
+									slave.trust -= 10;
+								} else {
+									r.push(`${He} <span class="hotpink">happily pats ${his} full belly,</span> before coming to ${his} senses and realizing what ${he} did. ${He} begins to <span class="gold">tremble</span> as it dawns on ${him} just how easily you manipulated ${him}.`);
+									slave.devotion += 5;
+									slave.trust -= 10;
+								}
+							}
+							r.push(`Given the availability of the slave treats and ${his} burgeoning addiction to them, ${he}'ll likely keep ${himself} stuffed unless you force ${him} to stop.`);
+							if (slave.preg > 0 || slave.bellyImplant > 0) {
+								slave.inflation = 1;
+							} else {
+								slave.inflation = 2;
+							}
+							slave.inflationType = "food";
+							slave.inflationMethod = 1;
+							SetBellySize(slave);
+
+							return r.join(" ");
+						},
+
+					});
+				}
+			}
+			// CLOSES MINDBROKEN CHECK
+			lineBreak();
+			choice({
+				linkName: `Spank ${him}`,
+				result(slave) {
+					const r = [];
+					r.push(`You`);
+					if (slave.devotion > 20 || slave.trust < -20) {
+						r.push(`lay the obedient ${desc} across your knees`);
+					} else {
+						r.push(`force the resistant ${desc} to lie across your knees`);
+					}
+					r.push(`and run a voluptuary hand across ${his}`);
+					if (slave.butt > 6) {
+						r.push(`jiggling`);
+					} else if (slave.butt > 3) {
+						r.push(`delightfully big`);
+					} else if (slave.butt > 1) {
+						r.push(`cute`);
+					} else {
+						r.push(`skinny`);
+					}
+					r.push(`buttocks, enjoying the way ${he} tenses up reflexively at the extreme vulnerability of ${his} position. ${He} feels the change in your posture as you bring your hand back, and`);
+					if (slave.devotion > 20 || slave.trust < -20) {
+						r.push(`can't stop ${himself} from wriggling`);
+					} else {
+						r.push(`struggles desperately`);
+					}
+					r.push(`for just a moment before your palm smacks against ${his} ass.`);
+					if (slave.devotion > 20) {
+						if (slave.fetish === "masochist") {
+							if (slave.fetishKnown === 0) {
+								r.push(`${He} gasps with profound shock, and you feel ${his} whole body stiffen atop your thighs as ${he} tries to work out what just happened. With each spank, ${he} grows more and more aroused until ${he} finally reaches orgasm. <span class="green">${He}'s a pain fetishist!</span>`);
+								slave.fetishKnown = 1;
+							} else {
+								r.push(`${He} groans with guilty pleasure, feeling the pain radiate out from ${his} buttocks with each swat, feeling it seem to circle around ${his} hips to pool between ${his} legs. Before long, ${he} reaches a quivering orgasm.`);
+							}
+							r.push(`${He} gets up very gingerly, but bites ${his} lip cutely, <span class="hotpink">feeling a strange satisfaction.</span>`);
+							slave.devotion += 5;
+						} else {
+							r.push(`${He} gasps with pain, but does not object or try to escape. ${He} accepts the spanking dutifully. You're not particularly harsh, and ${he} gets up after you tire of swatting ${his} bottom <span class="hotpink">without resentment</span> of this treatment.`);
+							slave.devotion += 4;
+						}
+					} else {
+						if (slave.fetish === "masochist") {
+							if (slave.fetishKnown === 0) {
+								r.push(`${He} gasps with profound shock, and you feel ${his} whole body stiffen atop your thighs as ${he} tries to work out what just happened. ${He} begins to cry as ${he} realizes that having ${his} bottom beaten is getting ${him} horny. <span class="green">${He}'s a pain fetishist!</span>`);
+								slave.fetishKnown = 1;
+							} else {
+								r.push(`${He} groans with unwilling pleasure, feeling the pain radiate out from ${his} buttocks with each swat, feeling it seem to circle around ${his} hips to pool between ${his} legs. ${He} does not orgasm, but feels much more pleasure than ${he}'s willing to admit.`);
+							}
+							r.push(`When you ${him} up, ${he}'s sobbing, more from humiliation than pain. However, ${he} <span class="hotpink">submits</span> to an uncomfortable groping of ${his} buttocks, which are pleasingly warm from the spanking.`);
+							slave.devotion += 4;
+						} else {
+							r.push(`${He} gasps with pain, and starts to wriggle off you until you pin ${him} with your other hand. ${He} accepts the rest of the spanking unhappily. You're not particularly harsh, and ${he} gets up after you tire of swatting ${his} bottom, <span class="gold">fearful</span> due to the humiliation rather than the pain.`);
+							slave.trust -= 4;
+						}
+					}
+
+					return r.join(" ");
+				},
+
+			});
+			lineBreak();
+
+			if (hasAnyArms(slave)) {
+				choice({
+					linkName: `Tie ${him} up and give ${him} a good whipping`,
+					result(slave) {
+						const r = [];
+						r.push(`You cuff ${his} wrist`);
+						if (hasBothArms(slave)) {
+							r.push(`s`);
+						}
+						r.push(`and tie the cuffs to a hook in the ceiling so ${he}'s forced up on tiptoe. Reflecting that sometimes the old ways are best, you take a whip to ${him}. It's soft leather and you have some skill, so ${his} skin isn't broken, but you lash ${his} buttocks and every stroke draws a scream. After a while, the pain grows dull for ${him} and ${he} slumps in ${his} bindings, moaning. You switch to ${his} nipples, bringing ${him} back to howling life as ${he} dances on tiptoe and tries to dodge.`);
+						if (slave.dick !== 0) {
+							r.push(`You finish by taking the whip to ${his} penis, leaving ${him} sobbing and begging.`);
+						}
+						r.push(`If ${he} was wondering what kind of master ${his} new master is, now ${he} <span class="gold">knows.</span> ${He} will remember <span class="mediumorchid">what you can do to ${him}.</span>`);
+						slave.devotion -= 5;
+						slave.trust -= 10;
+
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (slave.anus !== 0) {
+				if (slave.vagina > 0) {
+					choice({
+						linkName: `Use a machine on ${his} holes`,
+						result(slave) {
+							const r = [];
+							r.push(`${He}'s not a virgin anywhere, so you'll have to go more extreme to provide a properly introductory level of sexual torture equivalent to defloration. You tie your new slave down on`);
+							if (hasAnyArms(slave)) {
+								r.push(`hand`);
+								if (hasBothArms(slave)) {
+									r.push(`s`);
+								}
+								if (hasAnyLegs(slave)) {
+									r.push(`and`);
+								}
+							}
+							if (hasAnyLegs(slave)) {
+								r.push(`knee`);
+								if (hasBothLegs(slave)) {
+									r.push(`s`);
+								}
+							}
+							r.push(`and bring a fuckmachine up from storage. It fits over ${him} as ${he} whimpers down there on`);
+							if (hasAllLimbs(slave)) {
+								r.push(`all fours,`);
+							} else {
+								r.push(`the floor,`);
+							}
+							r.push(`and you adjust the pistons so that the tip of a dildo rests inside each of ${his} holes. You add lube when necessary,`);
+							if (V.assistant.personality === 0) {
+								r.push(`but the rest of the day is monotonous machine rape for ${him}.`);
+							} else {
+								r.push(`but for ${him} the rest of the day is machine rape controlled by your sultry-voiced personal assistant program. Your assistant is an exquisite sexual torturer, ensuring that the experience is never damaging and always overwhelming. ${He} keeps up a steady stream of degrading verbal abuse while ${he} works.`);
+							}
+							r.push(`Any resistance to you is <span class="mediumorchid">worn down</span> and replaced with a germ of <span class="gold">fear.</span> <span class="lime">The slave's holes are nicely stretched.</span>`);
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							actX(slave, "vaginal");
+							actX(slave, "anal");
+							slave.anus += 1;
+							slave.vagina += 1;
+
+							return r.join(" ");
+						},
+
+					});
+				} else if ((slave.vagina === -1)) {
+					choice({
+						linkName: `Use a machine on ${his} asshole`,
+						result(slave) {
+							const r = [];
+							r.push(`${He}'s not an anal virgin, so you'll have to go more extreme to provide an introductory level of sexual torture equivalent to defloration. You tie your new slave down on`);
+							if (hasAnyArms(slave)) {
+								r.push(`hand`);
+								if (hasBothArms(slave)) {
+									r.push(`s`);
+								}
+								if (hasAnyLegs(slave)) {
+									r.push(`and`);
+								}
+							}
+							if (hasAnyLegs(slave)) {
+								r.push(`knee`);
+								if (hasBothLegs(slave)) {
+									r.push(`s`);
+								}
+							}
+							r.push(`and bring a fuckmachine up from storage. It fits over ${him} as ${he} whimpers down there on`);
+							if (hasAllLimbs(slave)) {
+								r.push(`all fours,`);
+							} else {
+								r.push(`the floor,`);
+							}
+							r.push(`and you adjust the pistons so that the tip of a dildo rests inside ${his} asshole. You add lube when necessary,`);
+							if (V.assistant.personality === 0) {
+								r.push(`but the rest of the day is monotonous machine rape for ${him}.`);
+							} else {
+								r.push(`but for ${him} the rest of the day is machine rape controlled by your sultry-voiced personal assistant program. Your assistant is an exquisite sexual torturer, ensuring that the experience is never damaging and always overwhelming. ${He} keeps up a steady stream of degrading verbal abuse while ${he} works.`);
+							}
+							r.push(`Any resistance to you is <span class="mediumorchid">worn down</span> and replaced with a germ of <span class="gold">fear.</span> <span class="lime">The slave's asshole is nicely stretched.</span>`);
+							// Written and coded by Boney M
+							slave.devotion -= 5;
+							slave.trust -= 10;
+							actX(slave, "anal");
+							slave.anus += 1;
+
+							return r.join(" ");
+						},
+
+					});
+				}
+			}
+			lineBreak();
+			if (canTalk(slave) && !isAmputee(slave)) {
+				choice({
+					linkName: `Make ${him} an office ornament for the day`,
+					result(slave) {
+						const r = [];
+						r.push(`You cuff ${his}`);
+						if (hasAnyArms(slave)) {
+							r.push(wrists);
+							if (hasAnyLegs(slave)) {
+								r.push(`and`);
+							}
+						}
+						if (hasAnyLegs(slave)) {
+							r.push(`${ankles}, bend`);
+						} else {
+							r.push(r.pop() + `, bend`);
+						}
+						if (getLimbCount(slave) === 4) {
+							r.push(`all four limbs`);
+						} else if (getLimbCount(slave) === 3) {
+							r.push(`all three limbs`);
+						} else if (getLimbCount(slave) === 2) {
+							r.push(`both limbs`);
+						} else {
+							r.push(`${his} limb`);
+						}
+						r.push(`behind ${him}, secure them together, and hoist ${him} up to the ceiling on a rope. ${He} hangs up there like a sex slave chandelier${(slave.boobs >= 650) ? `, breasts swaying in a lovely way whenever ${he} struggles` : ``}. Periodically you ${him} down to stretch ${his} aching`);
+						if ((!hasAnyArms(slave) && !hasBothLegs(slave)) || (!hasBothArms(slave) && !hasAnyLegs(slave))) {
+							r.push(`limb,`);
+						} else {
+							r.push(`limbs,`);
+						}
+						r.push(`but whenever you put your decoration back up you add some clips with little bells on them: first the nose, then the nipples, and finally the`);
+						if (slave.dick !== 0) {
+							r.push(`dickhead.`);
+						} else {
+							r.push(`pussylips.`);
+						}
+						r.push(`By the end of the day your work is accompanied by the sweet sounds of ringing bells and <span class="gold">agonized pleas</span> for mercy in ${his}`);
+						if (slave.voice === 3) {
+							r.push(`high, girlish`);
+						} else if (slave.voice === 2) {
+							r.push(`feminine`);
+						} else {
+							r.push(`deep`);
+						}
+						r.push(`voice. ${He} will remember <span class="mediumorchid">what you can do to ${him}.</span>`);
+						slave.devotion -= 5;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (V.cumSlaves > 3) {
+				choice({
+					linkName: `Fill ${him} with cum`,
+					result(slave) {
+						const r = [];
+						r.push(`You securely restrain your new slave${(V.dairyRestraintsSetting > 1) ? `in a milking stall` : ``}. A quick finger check of ${his} holes allows you to choose wisely from a selection of hollow phalluses, which push into your helpless new slave's mouth,`);
+						actX(slave, "oral");
+						if (slave.vagina > 0) {
+							r.push(`vagina,`);
+							actX(slave, "vaginal");
+						}
+						if (slave.anus > 0) {
+							r.push(`and squeeze into ${his} ass,`);
+							actX(slave, "anal");
+						}
+						r.push(`with no room to spare.`);
+						r.push(`Your choices attach to long tubes, and with a fiendish smile you turn a valve. The lines run white with a mixture of fluids from your other slaves, and your newest slave begins to fill.`);
+						if (slave.devotion > 20) {
+							if (slave.fetish === "cumslut") {
+								if (slave.fetishKnown === 0) {
+									r.push(`${He} seems genuinely eager to hold as much of the fluid as ${he} can. <span class="green">${He}'s a cum fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${He} groans with guilty pleasure, feeling ${his} muscles ache and ${his} torso swell. Before long, ${he} reaches a quivering orgasm.`);
+								}
+								r.push(`As you release ${him} ${he} swings up with difficulty, but <span class="hotpink">${he} seems very pleased</span> to stay plugged for now.`);
+								slave.devotion += 5;
+							} else {
+								if (!hasBothEyes(slave)) {
+									r.push(`${His} face swells`);
+								} else {
+									r.push(`${His} eyes swell`);
+								}
+								r.push(`with shock, but ${he} does not flinch or make a sound. When ${he} seems full enough, you shut off the valve, deciding to leave ${him} plugged for now. ${He} waddles off <span class="hotpink">without apparent resentment</span> of this treatment.`);
+								slave.devotion += 4;
+							}
+						} else {
+							if (slave.fetish === "cumslut") {
+								if (slave.fetishKnown === 0) {
+									if (!hasBothEyes(slave)) {
+										r.push(`${His} face swells`);
+									} else {
+										r.push(`${His} eyes swell`);
+									}
+									r.push(`with profound horror at the realization of what is happening, and begin to leak tears as ${he} realizes that having ${his} body swell with semen is getting ${him} horny. <span class="green">${He}'s a cum fetishist!</span>`);
+									slave.fetishKnown = 1;
+								} else {
+									r.push(`${He} groans with guilty pleasure, feeling ${his} muscles ache and ${his} torso swell. ${He} does not orgasm from ${his} "meal," but feels much more pleasure than ${he}'s willing to admit.`);
+								}
+								r.push(`When you ${him} up, ${he}'s sobbing, more from humiliation than discomfort. However, ${he} <span class="hotpink">submits</span> to an uncomfortable groping of ${his} stomach, which is pleasingly taut from the inflation. You decide to leave ${him} plugged for a few hours, impressing on ${him} still more that ${his} fate is out of ${his} control.`);
+								slave.devotion += 4;
+							} else {
+								r.push(`${He} gasps in horror, and starts to struggle frantically against ${his} bonds. However, as the fluid reaches ${him} ${he} seems to resign ${himself} to ${his} fate. Once ${he} reaches a fullness you like you ${him} up, <span class="gold">fearful</span> due to the humiliation rather than the swelling. ${He} limps off to ${his} new duties as quickly as ${he} can, before you can unplug ${him}. Oh well!`);
+								slave.trust -= 4;
+							}
+						}
+						r.push(`${His} first day promises to be a full one!`);
+
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if (hasAnyArms(slave)) {
+				choice({
+					linkName: `Put a shock collar on ${him} and force ${him} to rape ${himself}`,
+					result(slave) {
+						const r = [];
+						slave.collar = "shock punishment";
+						r.push(`You put a shock collar on ${him}. Its function isn't immediately obvious, at least until you test it on the lowest power setting, making ${him} jump and look at you fearfully. This concern is compounded when you throw a big dildo at ${him} and tell ${him} to rape ${himself}. ${He} gapes at you incredulously until you give ${him} a stronger jolt and peremptorily tell ${him} to`);
+						if (slave.vagina > 0) {
+							r.push(`pick it up and pound ${his} own pussy. <span class="mediumorchid">Reluctantly,</span> ${he} reaches down shakily, seats ${himself}, and slowly pushes the uncomfortably big phallus inside ${his} womanhood. ${He} starts to fuck ${himself}. Harder, you command. Wincing, ${he} works ${his} cunt faster. Harder, you repeat, giving ${him} another shock. <span class="gold">${He} begins to cry,</span> but obeys, sawing the big fake cock in and out, really raping ${himself}.`);
+							actX(slave, "vaginal");
+						} else if (slave.anus > 0) {
+							r.push(`pick it up and pound ${his} own ass. <span class="mediumorchid">Reluctantly,</span> ${he} reaches down shakily, seats ${himself}, and gradually shoves the already-lubricated phallus into ${his} ass. It's uncomfortably big for ${his} butt, but ${he} can manage it, and slowly starts to sodomize ${himself}. Harder, you command. Wincing, ${he} fucks ${his} ass faster. Harder, you repeat, giving ${him} another shock. <span class="gold">${He} begins to cry,</span> but obeys, sawing the big fake cock in and out, really assraping ${himself}.`);
+							actX(slave, "anal");
+						} else {
+							r.push(`pick it up and facefuck ${himself}. <span class="mediumorchid">Reluctantly,</span> ${he} reaches down shakily, seats ${himself}, and slowly swallows the uncomfortably big phallus. ${He} has to start over several times as ${his} gag reflex kicks in, but ${he} finally manages to hilt it. Harder, you command. Eyes rolling fearfully, ${he} withdraws it a few`);
+							if (V.showInches === 2) {
+								r.push(`inches`);
+							} else {
+								r.push(`centimeters`);
+							}
+							r.push(`and shoves it down ${his} throat again. Harder, you repeat, giving ${him} another shock. <span class="gold">${He} begins to weep and gag,</span> but obeys, sawing the big fake cock in and out, really molesting ${himself}.`);
+							actX(slave, "oral");
+						}
+						slave.trust -= 10;
+						slave.devotion -= 5;
+						return r.join(" ");
+					},
+
+				});
+			}
+			lineBreak();
+			if ((V.dairy > 0) && (V.dairyRestraintsSetting > 1) && (V.seeExtreme === 1)) {
+				App.UI.DOM.appendNewElement("div", p, `...in the Industrial Dairy`, "note");
+				choice({
+					linkName: `Threaten ${him} with the Industrial Dairy`,
+					result(slave) {
+						const r = [];
+						const {hisU} = getNonlocalPronouns(V.seeDicks).appendSuffix('U');
+						r.push(`You tell ${him} that it's in ${his} interests to be a good ${girl}. ${He} does not react immediately, perhaps wondering if you think such a trite statement will have a real impact, but then you`);
+						if (canSee(slave)) {
+							r.push(`point at`);
+						} else {
+							r.push(`direct ${him} towards`);
+						}
+						r.push(`a wallscreen behind ${him}. ${He} turns, and beholds a live feed from the Industrial Dairy.`);
+						if (canSee(slave)) {
+							r.push(`${He} gazes spellbound at the interleaved row of bodies intertwined with machines in embraces far more intimate than any lover could hope to match. The gently heaving masses of breastflesh hold ${his} fascinated and horrified attention until one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.`);
+						} else if (canHear(slave)) {
+							r.push(`${He} listens entranced by the steady rhythm of the machines collecting their occupants' bodily fluids. The muffled moans and groans of the restrained slaves hold ${his} fascinated and horrified attention until one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.`);
+						} else {
+							r.push(`Such a broadcast would normally be pointless for a senseless slave like ${slave.slaveName}, but you've taken precautions to accommodate such slaves. The wallscreen is connected to the heating, air conditioning, and sprinkler systems in order to accurately replicate the aura of an industrial slave dairy, so ${he} is fully immersed in the scene when one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.`);
+						}
+						r.push(`It withdraws its horse-sized phallus from the slave's anus, leaving ${hisU} gape pulsing gently as it awaits the resumption of the endless assrape. ${slave.slaveName} lets out a huge sob and turns to you, <span class="gold">fear suffusing ${him}</span> as ${he} promises to <span class="hotpink">be a good ${girl}.</span>`);
+						slave.devotion += 10;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+
+				if ((slave.lactation > 0 || ((V.dairySlimMaintainUpgrade === 0 || V.dairySlimMaintain === 0) && (slave.boobs > 300 || slave.dick === 0 || V.dairyImplantsSetting === 1) && V.dairyImplantsSetting !== 2)) || (slave.balls > 0)) {
+					if (V.dairyPrepUpgrade === 1 && App.Entity.facilities.dairy.hasFreeSpace) {
+						choice({
+							linkName: `Send ${him} straight to the Industrial Dairy`,
+							result(slave) {
+								const r = [];
+								slave.choosesOwnAssignment = 0;
+								slave.anus = Math.clamp(slave.anus, 3, 4);
+								if (slave.vagina > -1) {
+									slave.vagina = Math.clamp(slave.vagina, 3, 4);
+								}
+								actX(slave, "anal", 10);
+								assignJob(slave, "dairy");
+
+								r.push(`You order`);
+								if (V.HeadGirlID === 0) {
+									r.push(`another slave`);
+								} else {
+									r.push(S.HeadGirl.slaveName);
+								}
+								r.push(`to get ${slave.slaveName} set up in ${V.dairyName}. The new slave does not know what V.dairyName is, not really, and ${he} doesn't know what being set up there means, either. If ${he} knew that you are able to send ${him} there so blithely only because it is equipped with a special preparatory raper that will seize ${him},`);
+								if (slave.vagina > -1) {
+									r.push(`mercilessly fuck ${his} pussy and ass until both are gaped,`);
+								} else {
+									r.push(`ream ${his} anus until it's cavernously gaped,`);
+								}
+								r.push(`and then consign ${him} to constant fucking by gargantuan machine phalli, ${he} might resist. But ${he} doesn't, so ${he} does not.`);
+
+								return r.join(" ");
+							},
+
+						});
+					}
+					if (App.Entity.facilities.dairy.hasFreeSpace) {
+						choice({
+							linkName: `Break ${him} in for the Industrial Dairy`,
+							result(slave) {
+								const r = [];
+								slave.choosesOwnAssignment = 0;
+								slave.anus = Math.clamp(slave.anus, 3, 4);
+								if (slave.vagina > -1) {
+									slave.vagina = Math.clamp(slave.vagina, 3, 4);
+									actX(slave, "vaginal", 10);
+								}
+								actX(slave, "anal", 10);
+								assignJob(slave, "dairy");
+
+								r.push(`Making use of`);
+								if (slave.trust < -20 || slave.devotion > 20) {
+									r.push(`${his} obedience,`);
+								} else {
+									r.push(`the compliance systems,`);
+								}
+								r.push(`you restrain ${him} on one of the chairs in your office in an approximation of the position ${he}'ll occupy in ${V.dairyName}. Then you put a mask on ${him}, like the ones the machines there feature, and turn it on, watching the slave squirm against ${his} restraints under the sudden bombardment of garish hardcore porn. Finally, you add a dildo gag, both to mimic the dildo that will feed ${him}, and to keep your office reasonably quiet. Then, for the rest of the day, you use ${his} vulnerable`);
+								if (slave.vagina > -1) {
+									r.push(`holes`);
+								} else {
+									r.push(`asshole`);
+								}
+								r.push(`as an outlet for your sexual energy. You are not gentle; in fact, the point of the whole exercise is to gape ${him}. By the evening ${he}'s been fucked so hard that ${he}'s stopped jerking against the chair when you pound`);
+								if (V.PC.dick !== 0) {
+									r.push(`your huge cock`);
+								} else {
+									r.push(`a huge strap-on`);
+								}
+								r.push(`in and out of ${him}, so you're obliged to get creative, sliding fingers in alongside`);
+								if (V.PC.dick !== 0) {
+									r.push(`yourself`);
+								} else {
+									r.push(`it`);
+								}
+								r.push(`to really blow ${him} out. Once that gets too easy, you start adding dildos for double`);
+								if (slave.vagina > -1) {
+									r.push(`and triple`);
+								}
+								r.push(`penetration. By nighttime ${he}'s properly prepared to take ${V.dairyName}'s giant phalli, and you're bored, so you consign ${him} to ${his} fate. ${He} might have some opinion on how ${he}'s spent ${his} day, but it's unlikely ${he}'ll remember it by tomorrow, what with the forearm-sized dildos sliding in and out of ${his} throat${(slave.vagina > -1) ? `, cunt,` : ``} and asshole.`);
+
+								return r.join(" ");
+							},
+
+						});
+					}
+				}
+			}
+			lineBreak();
+			if (V.dairyPiping === 1) {
+				if (((V.cumPipeline > 88 && V.cumPipeline !== 0) || V.arcologies[0].FSPastoralistLaw === 1) && slave.preg < 1 && slave.inflation === 0 && slave.bellyImplant <= 0) {
+					choice({
+						linkName: `Give ${him} all the cum ${he} can drink`,
+						result(slave) {
+							const el = new DocumentFragment();
+							let r = [];
+							r.push(`You securely restrain your new slave; for both ${his} own safety and so ${he} can't object to ${his} meal. You reassure ${him}`);
+							if (hasAnyEyes(slave)) {
+								r.push(`and order ${him} to close ${his} eyes`);
+							}
+							r.push(`and open wide for a treat.`);
+							if (canSee(slave)) {
+								r.push(`Since ${his} eyes are contentedly closed,`);
+							} else {
+								r.push(`Blind as ${he} is,`);
+							}
+							r.push(`${he} doesn't see you reach for one of the phallus-tipped feeding tubes located throughout your penthouse. Before ${he} knows what's happening, you've forced the cocktube firmly into ${his} gaping maw and anchored it to ${his} head, causing ${his} entire body to tense up`);
+							if (slave.devotion <= 20) {
+								r.push(`in panic`);
+							}
+							r.push(`once more.`);
+
+							App.UI.DOM.appendNewElement("p", el, r.join(" "));
+							r = [];
+							r.push(`You pause to examine the tap, making sure it is set to cum, before releasing the valve and unleashing a steady flow. The feeder bucks against ${his} face as thick, white liquid rushes downward towards the helpless slave.`);
+							if (slave.devotion > 20) {
+								if (slave.fetish === "cumslut") {
+									if (slave.fetishKnown === 0) {
+										r.push(`Stunningly, once ${he} gets over the impact of the wave of fresh semen, ${he} seems genuinely eager to hold as much of the fluid as ${he} can, stretching contentedly to ${his} reasonable limit. <span class="green">${He}'s a cum fetishist!</span>`);
+										slave.fetishKnown = 1;
+									} else {
+										r.push(`${His} belly steadily swells from a few months of apparent pregnancy, to "spent too much time at the buffet", until it finally stops wobbling, grows taut and forces ${his} belly button into an outie. Your cow groans not only with the weight and mounting pressure, but with guilt as well. Before long, ${he} reaches a quivering orgasm.`);
+									}
+									r.push(`You stroke ${his} gurgling stomach slowly, before turning off the valve, unfastening ${his} binds and leaving your <span class="hotpink">very pleased</span> cum balloon to savor ${his} meal. You'll make sure to set aside enough cum from your cumslaves for ${him} to drink ${himself} stupid with, and, glancing over your shoulder, find ${him} eagerly masturbating to ${his} cum filled gut. ${He}'ll probably intend to keep ${himself} filled to the brim, which is fine by you.`);
+									slave.devotion += 5;
+								} else {
+									if (!hasBothEyes(slave)) {
+										r.push(`${His} face swells`);
+									} else {
+										r.push(`${His} eyes swell`);
+									}
+									r.push(`with horror, but ${he} does not flinch or make a sound. ${His} belly also swells, from a few months of apparent pregnancy, to 'spent too much time at the buffet', till it finally stops wobbling, grows taut and forces ${his} belly button into an outie. Satisfied, you shut off the valve, deciding to leave ${him} to consider ${his} cum filled middle. Time passes. ${His}`);
+									if (canSee(slave)) {
+										r.push(`eyes are glazed over,`);
+									} else {
+										r.push(`blind face is distant,`);
+									}
+									r.push(`but ${he} tolerates you slowly stroking ${his} stomach <span class="hotpink">without apparent resentment.</span> You'll make sure to keep ${him} like this for the time being.`);
+									slave.devotion += 4;
+								}
+							} else {
+								if (slave.fetish === "cumslut") {
+									if (slave.fetishKnown === 0) {
+										if (!hasBothEyes(slave)) {
+											r.push(`${His} face tenses`);
+										} else {
+											r.push(`${His} eyes swell`);
+										}
+										r.push(`with profound horror at the realization of what is filling ${him} with a constant irresistible pressure, and ${he} begins to leak tears as ${he} realizes that ${his} rapidly swelling body is getting ${him} horny. <span class="green">${He}'s a cum fetishist!</span>`);
+										slave.fetishKnown = 1;
+									} else {
+										r.push(`${His} belly steadily swells from a few months of apparent pregnancy, to "spent too much time at the buffet", till it finally stops wobbling, grows taut and forces ${his} belly button into an outie. Your cow groans not only with the weight and quickly mounting pressure, but with guilt as well. ${He} does not orgasm from ${his} gluttonous "meal," but feels much more pleasure than ${he}'s willing to admit.`);
+									}
+									r.push(`Time passes, marked only by the slowing growth of ${his} waistline and the gurgling deep within ${him}. However, ${he} <span class="hotpink">submits</span> to an uncomfortable groping of ${his} fluid distended stomach, which is pleasingly taut from ${his} massive cum meal. You decide to make it abundantly clear that if a single drop of cum leaves ${his} gullet, ${he}'s getting the hose again, impressing on ${him} still more that ${his} fate is out of ${his} control.`);
+									slave.devotion += 4;
+								} else {
+									r.push(`${He} gasps in horror, and starts to struggle frantically against ${his} bonds,`);
+									if (slave.voice !== 0) {
+										r.push(`struggling to scream in panic.`);
+									} else {
+										r.push(`${his} whole body begging to scream.`);
+									}
+									r.push(`However, as the fluid reaches ${him}, ${he} seems to despair and resigns ${himself} to ${his} fate. As ${his} belly swells, you find yourself comparing ${him} first to a binge eater, then some of the chubbier citizens you've seen, a large pregnancy, and finally a water balloon ready to burst. You stop the flow, and rest a hand on ${his} heaving stomach. ${His} <span class="gold">terrified</span> breathing and distant expression are clearly due to the pain, not lust, but it's also now quite clear to ${him} who is in charge here. You have little intent on letting ${his} stomach empty and will be setting aside enough cum to keep ${him} bloated into submission.`);
+									slave.trust -= 4;
+								}
+							}
+							slave.inflation = 3;
+							slave.inflationType = "cum";
+							slave.inflationMethod = 1;
+							SetBellySize(slave);
+
+							return r.join(" ");
+						},
+
+					});
+				}
+				lineBreak();
+				if (((V.milkPipeline > 88 && V.milkPipeline !== 0) || V.arcologies[0].FSPastoralistLaw === 1) && slave.preg < 1 && slave.inflation === 0 && slave.bellyImplant <= 0) {
+					choice({
+						linkName: `Give ${him} all the milk ${he} can drink`,
+						result(slave) {
+							const el = new DocumentFragment();
+							let r = [];
+							r.push(`You securely restrain your new slave; for both ${his} own safety and so ${he} can't object to ${his} meal. You reassure ${him}`);
+							if (hasAnyEyes(slave)) {
+								r.push(`and order ${him} to close ${his} eyes`);
+							}
+							r.push(`and open wide for a treat.`);
+							if (canSee(slave)) {
+								r.push(`Since ${his} eyes are contentedly closed,`);
+							} else {
+								r.push(`Blind as ${he} is,`);
+							}
+							r.push(`${he} doesn't see you reach for one of the phallus-tipped feeding tubes located throughout your penthouse. Before ${he} knows what's happening, you've forced the cocktube firmly into ${his} gaping maw and anchored it to ${his} head, causing ${his} entire body to tense up`);
+							if (slave.devotion <= 20) {
+								r.push(`in panic`);
+							}
+							r.push(`once more.`);
+							App.UI.DOM.appendNewElement("p", el, r.join(" "));
+							r = [];
+
+							r.push(`You pause to examine the tap, making sure it is set to milk, before releasing the valve and unleashing a steady flow. The feeder bucks against ${his} face as creamy, white liquid rushes downward towards the helpless slave.`);
+							if (slave.behavioralFlaw === "gluttonous") {
+								if (!hasBothEyes(slave)) {
+									r.push(`${His} face swells`);
+								} else {
+									r.push(`${His} eyes swell`);
+								}
+								r.push(`with horror, but ${he} does not flinch or make a sound. ${His} belly also swells, from a few months of apparent pregnancy, to "spent too much time at the buffet", till it finally stops wobbling, grows taut and forces ${his} belly button into an outie. Satisfied, you shut off the valve, deciding to leave ${him} to consider ${his} milk filled middle. Time passes. ${His}`);
+								if (canSee(slave)) {
+									r.push(`eyes are glazed over,`);
+								} else {
+									r.push(`blind face is distant,`);
+								}
+								r.push(`but ${he} tolerates you slowly stroking ${his} stomach <span class="hotpink">without apparent resentment.</span> You'll make sure to keep ${him} like this for the time being.`);
+								slave.devotion += 5;
+							} else {
+								r.push(`${He} gasps in horror, and starts to struggle frantically against ${his} bonds,`);
+								if (slave.voice !== 0) {
+									r.push(`struggling to scream in panic.`);
+								} else {
+									r.push(`${his} whole body begging to scream.`);
+								}
+								r.push(`However, as the fluid reaches ${him}, ${he} seems to despair and resigns ${himself} to ${his} fate. As ${his} belly swells, you find yourself comparing ${him} first to a binge eater, then some of the chubbier citizens you've seen, a large pregnancy, and finally a water balloon ready to burst. You stop the flow, and rest a hand on ${his} heaving stomach. ${His} <span class="gold">terrified</span> breathing and distant expression are clearly due to the pain, not lust, but it's also now quite clear to ${him} who is in charge here. You have little intent on letting ${his} stomach empty and will be setting aside enough milk to keep ${him} bloated into submission.`);
+								slave.trust -= 3;
+							}
+							slave.inflation = 3;
+							slave.inflationType = "milk";
+							slave.inflationMethod = 1;
+							SetBellySize(slave);
+
+							return r.join(" ");
+						},
+
+					});
+				}
+			}
+			lineBreak();
+			if (V.arcade > 0 && V.seeExtreme === 1) {
+				App.UI.DOM.appendNewElement("div", p, "...in the Arcade", "note");
+				choice({
+					linkName: `Threaten ${him} with the Arcade`,
+					result(slave) {
+						const r = [];
+						const {girlU, himU} = getNonlocalPronouns(V.seeDicks).appendSuffix('U');
+						r.push(`You tell ${him} that it's in ${his} best interests to be a good ${girl}. ${He} does not react immediately, perhaps wondering if you think such a trite statement will have a real impact, but then you`);
+						if (canSee(slave)) {
+							r.push(`point at`);
+						} else {
+							r.push(`direct ${him} towards`);
+						}
+						r.push(`a wallscreen behind ${him}. ${He} turns, and beholds a live feed from ${V.arcadeName}.`);
+						if (canSee(slave)) {
+							r.push(`${He} gazes at the row of butts sticking out of the wall at dick height, not quite realizing what ${he}'s seeing. Then ${he} notices that there are citizens pumping away in front of two of the butts, and ${he} understands.`);
+						} else if (canHear(slave)) {
+							r.push(`${He} listens to the wet sounds of flesh on flesh, not quite realizing what ${he}'s hearing. Then ${he} picks out the sound of your citizens grunting and moaning as they pound away, and ${he} understands.`);
+						} else {
+							r.push(`Such a broadcast would normally be pointless for a senseless slave like ${slave.slaveName}, but you've taken precautions to accommodate such slaves. The wallscreen is connected to the heating, air conditioning, and sprinkler systems in order to accurately replicate the feeling of standing in front of a row of restrained public sex slaves, so the meaning of the scene in front of ${him} is not lost.`);
+						}
+						r.push(`As ${he}`);
+						if (canSee(slave)) {
+							r.push(`watches,`);
+						} else if (canHear(slave)) {
+							r.push(`listens,`);
+						} else {
+							r.push(`stands there,`);
+						}
+						r.push(`terrified, first one citizen and then the other finishes and steps away. The first arcade inmate's`);
+						if (V.seeDicks !== 100) {
+							r.push(`pussy`);
+						} else {
+							r.push(`butt`);
+						}
+						r.push(`is left looking sore until ${V.arcadeName}'s systems cover ${himU} for a quick cleaning, and the second inmate's asshole`);
+						if (V.seeDicks !== 0) {
+							r.push(`has obviously seen severe use, since the poor ${girlU} doesn't have a pussy to spread the load.`);
+						} else {
+							r.push(`is loose enough that the machines have to clean up the cum it drools onto the floor.`);
+						}
+						r.push(`${slave.slaveName} lets out a huge sob and turns to you, <span class="gold">fear suffusing ${him}</span> as ${he} promises to <span class="hotpink">be a good ${girl}.</span>`);
+						slave.devotion += 10;
+						slave.trust -= 10;
+						return r.join(" ");
+					},
+
+				});
+
+				if (App.Entity.facilities.arcade.hasFreeSpace || V.arcadeUpgradeFuckdolls === 2) {
+					choice({
+						linkName: `Send ${him} straight to the Arcade`,
+						result(slave) {
+							const r = [];
+							slave.assignment = "be confined in the arcade";
+							slave.choosesOwnAssignment = 0;
+							r.push(`You order`);
+							if (V.HeadGirlID === 0) {
+								r.push(`another slave`);
+							} else {
+								r.push(S.HeadGirl.slaveName);
+							}
+							r.push(`to get ${slave.slaveName} set up in ${V.arcadeName}. The new slave does not know what V.arcadeName is, not really, and ${he} doesn't know what being set up there means, either. ${He}'ll be confined inside a small space, not too different from the indignities ${he}'s suffered already. It's only when the restraints lock into place that ${he}'ll understand ${his} doom. ${His} mouth will be forced open and presented at one wall of V.arcadeName, and ${his} ass will protrude from its other side, ${his} holes available for public relief at both ends. ${He}'ll probably refuse to believe the truth, until the first cockhead enters ${his} mouth${(slave.vagina > -1) ? `, parts ${his} pussylips,` : ``} or presses against ${his} poor anus.`);
+							if (V.arcade <= App.Entity.facilities.arcade.employeesIDs().size) {
+								r.push(`Mere`);
+								if (V.showInches === 2) {
+									r.push(`yards`);
+								} else {
+									r.push(`meters`);
+								}
+								r.push(`away, preparations to convert the least appealing Arcade slave into a Fuckdoll begin. As ${slave.slaveName} is broken in by ${his} first customers, ${he}'s blissfully unaware that ${he}'s ${V.arcade} new slaves away from the same fate.`);
+							}
+
+							return r.join(" ");
+						},
+
+					});
+				}
+			}
+			lineBreak();
+		}
+
+		IncreasePCSkills('trading', 0.1);
+		if (V.PC.skill.slaving < 100 && jsRandom(V.PC.skill.slaving, 100) > 50) {
+			IncreasePCSkills('slaving', 0.5);
+		} else {
+			IncreasePCSkills('slaving', 0.1);
+		}
+
+		return el;
+
+		function lineBreak() {
+			if (linkArray.length > 0) {
+				App.UI.DOM.appendNewElement("div", p, App.UI.DOM.generateLinksStrip(linkArray));
+			}
+			linkArray = [];
+		}
+	}
+};
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index 88a608b32a409571951358b8b5836ce823c513f2..ef9db415c6ebe10d02281e8f709b26ff6b7010ec 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -29,7 +29,7 @@ App.Entity.PlayerSkillsState = class {
 };
 
 /**
- * Encasulates your sexual preferences. Used inside of the
+ * Encapsulates your sexual preferences. Used inside of the
  * App.Entity.PlayerState class.
  * @see App.Entity.PlayerState
  */
diff --git a/src/pregmod/FCTV/seFCTVremote.tw b/src/pregmod/FCTV/seFCTVremote.tw
index a321adba2bbfb6d01cac02c17914b868ba35b838..42cec0b944499df81129ce8c956a1e42ce3ab861 100644
--- a/src/pregmod/FCTV/seFCTVremote.tw
+++ b/src/pregmod/FCTV/seFCTVremote.tw
@@ -2,17 +2,14 @@
 
 <<set $nextButton = "Continue", $nextLink = "Scheduled Event", $returnTo = "Scheduled Event", $encyclopedia = "FCTV", $FCTV.remote = 2>>
 
-<<set $activeSlaveOneTimeMinAge = 25>>
-<<set $activeSlaveOneTimeMaxAge = 35>>
-<<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $oneTimeDisableDisability = 1>>
+
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
-<<set _slave = GenerateNewSlave()>>
+<<set _slave = GenerateNewSlave(null, {minAge: 25, maxAge: 35, ageOverridesPedoMode: 1, race: _race, disableDisability: 1})>>
 
 <<set _slave.devotion = 0>>
 <<set _slave.trust = 0>>
@@ -88,20 +85,17 @@
 		<<replace "#scene">>
 
 			/* set up customer */
-			<<set $activeSlaveOneTimeMinAge = 25>>
-			<<set $activeSlaveOneTimeMaxAge = 35>>
-			<<set $one_time_age_overrides_pedo_mode = 1>>
-			<<set $oneTimeDisableDisability = 1>>
 			<<if $arcologies[0].FSSupremacistLawME == 1>>
-				<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+				<<set _race = $arcologies[0].FSSupremacistRace>>
 			<<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 				<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-				<<set $fixedRace = _races.random()>>
+				<<set _race = _races.random()>>
 			<</if>>
+			<<set _genParam = {minAge: 25, maxAge: 35, ageOverridesPedoMode: 1, race: _race, disableDisability: 1}>>
 			<<if $seeDicks > random(1,99)>>
-				<<set _customer = GenerateNewSlave("XY")>>
+				<<set _customer = GenerateNewSlave("XY", _genParam)>>
 			<<else>>
-				<<set _customer = GenerateNewSlave("XX")>>
+				<<set _customer = GenerateNewSlave("XX", _genParam)>>
 			<</if>>
 			<<set _customer = GenerateNewSlave()>>
 			<<set _customer.devotion = 0>>
diff --git a/src/pregmod/analyzePregnancy.tw b/src/pregmod/analyzePregnancy.tw
index 16a1a15edee1e4e20ca6505440258e16a6da6984..693fe9e435f673376325e797916a9e897c32b2bc 100644
--- a/src/pregmod/analyzePregnancy.tw
+++ b/src/pregmod/analyzePregnancy.tw
@@ -6,28 +6,28 @@
 	<<set $storedLink = "Slave Interact">>
 <</if>>
 <<set $nextButton = "Continue", $nextLink = $storedLink>>
-<<run App.Utils.setLocalPronouns(getSlave($activeSlave.ID))>>
+<<run App.Utils.setLocalPronouns(getSlave($AS))>>
 
-<<set _WL = getSlave($activeSlave.ID).womb.length, _incubatorReservations = WombReserveCount(getSlave($activeSlave.ID), "incubator"), _nurseryReservations = WombReserveCount(getSlave($activeSlave.ID), "nursery"), _freeTanks = $incubator - $incubatorSlaves, _freeCribs = $nursery - $cribs.length>>
+<<set _WL = getSlave($AS).womb.length, _incubatorReservations = WombReserveCount(getSlave($AS), "incubator"), _nurseryReservations = WombReserveCount(getSlave($AS), "nursery"), _freeTanks = $incubator - $incubatorSlaves, _freeCribs = $nursery - $cribs.length>>
 
 <p class="scene-intro">
-	<<= getSlave($activeSlave.ID).slaveName>> is <<if getSlave($activeSlave.ID).devotion < 20>>restrained<<else>>comfortably reclined<</if>> with $his stomach prepped for examination. $He shudders slightly at the cold touch of the sensor against $his skin.
+	<<= getSlave($AS).slaveName>> is <<if getSlave($AS).devotion < 20>>restrained<<else>>comfortably reclined<</if>> with $his stomach prepped for examination. $He shudders slightly at the cold touch of the sensor against $his skin.
 </p>
 
-<<= App.Desc.pregnancy($activeSlave)>>
+<<= App.Desc.pregnancy(getSlave($AS))>>
 
 <p>
 	<h2>Overall statistics</h2>
 	<div class="indent">
-		<<set _cc = Math.round(getSlave($activeSlave.ID).bellyPreg)>>
-		<<set _safeCC = Math.round(getSlave($activeSlave.ID).pregAdaptation*2000)>>
+		<<set _cc = Math.round(getSlave($AS).bellyPreg)>>
+		<<set _safeCC = Math.round(getSlave($AS).pregAdaptation*2000)>>
 		<<if $geneticMappingUpgrade > 0>>
 			<div>
-				Estimated physical degree of pregnancy adaptation: <<print num(Math.round(getSlave($activeSlave.ID).pregAdaptation))>>
+				Estimated physical degree of pregnancy adaptation: <<print num(Math.round(getSlave($AS).pregAdaptation))>>
 			</div>
 			<div>
 				Estimated safe
-				<<if getSlave($activeSlave.ID).ovaries == 1 || getSlave($activeSlave.ID).mpreg == 1>>
+				<<if getSlave($AS).ovaries == 1 || getSlave($AS).mpreg == 1>>
 					womb
 				<<else>>
 					abdominal
@@ -36,11 +36,11 @@
 			</div>
 		<</if>>
 		<div>
-			Current fetal count: <<= getSlave($activeSlave.ID).womb.length>>
+			Current fetal count: <<= getSlave($AS).womb.length>>
 		</div>
 		<div>
 			Estimated
-			<<if getSlave($activeSlave.ID).ovaries == 1 || getSlave($activeSlave.ID).mpreg == 1>>
+			<<if getSlave($AS).ovaries == 1 || getSlave($AS).mpreg == 1>>
 				womb
 			<<else>>
 				abdominal
@@ -50,11 +50,11 @@
 	</div>
 </p>
 
-<<if getSlave($activeSlave.ID).womb.length > 0>>
+<<if getSlave($AS).womb.length > 0>>
 	<p>
 		<h2>Furthest developed pregnancy</h2>
 		<div class="indent">
-			Fetal development week: <<= Math.trunc(getSlave($activeSlave.ID).preg * 1000) / 1000>>
+			Fetal development week: <<= Math.trunc(getSlave($AS).preg * 1000) / 1000>>
 		</div>
 	</p>
 
@@ -64,14 +64,14 @@
 			<<if $incubator > 0>>
 				<<if _incubatorReservations > 0>>
 					<<link "Remove all of $his children from $incubatorName" "Analyze Pregnancy">>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "incubator", "")>>
+						<<run WombChangeReserveType(getSlave($AS), "incubator", "")>>
 						<<set $reservedChildren -= _incubatorReservations>>
 					<</link>> |
 				<</if>>
 				<<if _incubatorReservations < _WL && ($reservedChildren + _WL - _incubatorReservations <= _freeTanks)>>
 					<<link "Keep all of $his children in $incubatorName" "Analyze Pregnancy">>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "nursery", "incubator")>>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "", "incubator")>>
+						<<run WombChangeReserveType(getSlave($AS), "nursery", "incubator")>>
+						<<run WombChangeReserveType(getSlave($AS), "", "incubator")>>
 						<<set $reservedChildrenNursery -= _nurseryReservations>>
 						<<set $reservedChildren += _WL - _incubatorReservations>>
 					<</link>>
@@ -85,14 +85,14 @@
 			<<if $nursery > 0>>
 				<<if _nurseryReservations > 0>>
 					<<link "Remove all of $his children from $nurseryName" "Analyze Pregnancy">>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "nursery", "")>>
+						<<run WombChangeReserveType(getSlave($AS), "nursery", "")>>
 						<<set $reservedChildrenNursery -= _nurseryReservations>>
 					<</link>> |
 				<</if>>
 				<<if _nurseryReservations < _WL && ($reservedChildrenNursery + _WL - _nurseryReservations <= _freeCribs)>>
 					<<link "Keep all of $his children in $nurseryName" "Analyze Pregnancy">>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "incubator", "nursery")>>
-						<<run WombChangeReserveType(getSlave($activeSlave.ID), "", "nursery")>>
+						<<run WombChangeReserveType(getSlave($AS), "incubator", "nursery")>>
+						<<run WombChangeReserveType(getSlave($AS), "", "nursery")>>
 						<<set $reservedChildren -= _incubatorReservations>>
 						<<set $reservedChildrenNursery += _WL - _nurseryReservations>>
 					<</link>>
@@ -110,223 +110,223 @@
 			<hr style="margin:0">
 			<p>
 				<<capture _ap>>
-					<<if getSlave($activeSlave.ID).womb[_ap].age >=2 >>
+					<<if getSlave($AS).womb[_ap].age >=2 >>
 						<div class="indent">
-							Ova: <<= getSlave($activeSlave.ID).womb[_ap].genetics.name>>
+							Ova: <<= getSlave($AS).womb[_ap].genetics.name>>
 						</div>
 						<div class="indent">
-							Age: <<= Math.trunc(getSlave($activeSlave.ID).womb[_ap].age * 1000) / 1000>>
+							Age: <<= Math.trunc(getSlave($AS).womb[_ap].age * 1000) / 1000>>
 						</div>
 						<<if $geneticMappingUpgrade >= 1>>
 							<div class="indent">
-								Gender: <<= getSlave($activeSlave.ID).womb[_ap].genetics.gender>>
+								Gender: <<= getSlave($AS).womb[_ap].genetics.gender>>
 							</div>
 							<div class="indent">
-								Father ID: <<= getSlave($activeSlave.ID).womb[_ap].genetics.father>>
+								Father ID: <<= getSlave($AS).womb[_ap].genetics.father>>
 							</div>
 							<div class="indent">
 								Father Name:
-								<<if ndef getSlave($activeSlave.ID).womb[_ap].genetics.fatherName>>
+								<<if ndef getSlave($AS).womb[_ap].genetics.fatherName>>
 									name not registered
 								<<else>>
-									<<= getSlave($activeSlave.ID).womb[_ap].genetics.fatherName>>
+									<<= getSlave($AS).womb[_ap].genetics.fatherName>>
 								<</if>>
 							</div>
 							<div class="indent">
-								Mother ID: <<= getSlave($activeSlave.ID).womb[_ap].genetics.mother>>
+								Mother ID: <<= getSlave($AS).womb[_ap].genetics.mother>>
 							</div>
 							<div class="indent">
 								Mother Name:
-								<<if ndef getSlave($activeSlave.ID).womb[_ap].genetics.motherName>>
+								<<if ndef getSlave($AS).womb[_ap].genetics.motherName>>
 									name not registered
 								<<else>>
-									<<= getSlave($activeSlave.ID).womb[_ap].genetics.motherName>>
+									<<= getSlave($AS).womb[_ap].genetics.motherName>>
 								<</if>>
 							</div>
 							<div class="indent">
-								Nationality: <<= getSlave($activeSlave.ID).womb[_ap].genetics.nationality>>
+								Nationality: <<= getSlave($AS).womb[_ap].genetics.nationality>>
 							</div>
 							<div class="indent">
-								Race: <<= capFirstChar(getSlave($activeSlave.ID).womb[_ap].genetics.race)>>
+								Race: <<= capFirstChar(getSlave($AS).womb[_ap].genetics.race)>>
 							</div>
 							<div class="indent">
-								Skin tone: <<= capFirstChar(getSlave($activeSlave.ID).womb[_ap].genetics.skin)>>
+								Skin tone: <<= capFirstChar(getSlave($AS).womb[_ap].genetics.skin)>>
 							</div>
 							<div class="indent">
-								Intelligence index: <<= getSlave($activeSlave.ID).womb[_ap].genetics.intelligence>> out of 100
+								Intelligence index: <<= getSlave($AS).womb[_ap].genetics.intelligence>> out of 100
 							</div>
 							<div class="indent">
-								Face index: <<= getSlave($activeSlave.ID).womb[_ap].genetics.face>> out of 100
+								Face index: <<= getSlave($AS).womb[_ap].genetics.face>> out of 100
 							</div>
 							<div class="indent">
-								Eye Color: <<= capFirstChar(getSlave($activeSlave.ID).womb[_ap].genetics.eyeColor)>>
+								Eye Color: <<= capFirstChar(getSlave($AS).womb[_ap].genetics.eyeColor)>>
 							</div>
 							<div class="indent">
-								Hair Color: <<= capFirstChar(getSlave($activeSlave.ID).womb[_ap].genetics.hColor)>>
+								Hair Color: <<= capFirstChar(getSlave($AS).womb[_ap].genetics.hColor)>>
 							</div>
 							<div class="indent">
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.underArmHStyle == "hairless" && getSlave($activeSlave.ID).womb[_ap].genetics.pubicHStyle == "hairless">>
+								<<if getSlave($AS).womb[_ap].genetics.underArmHStyle == "hairless" && getSlave($AS).womb[_ap].genetics.pubicHStyle == "hairless">>
 									Body hair: None
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.underArmHStyle == "hairless">>
+								<<elseif getSlave($AS).womb[_ap].genetics.underArmHStyle == "hairless">>
 									Body hair: Lacks underarm hair
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.pubicHStyle == "hairless">>
+								<<elseif getSlave($AS).womb[_ap].genetics.pubicHStyle == "hairless">>
 									Body hair: Lacks pubic hair
 								<</if>>
 							</div>
-							<<if getSlave($activeSlave.ID).womb[_ap].genetics.markings == "freckles" || getSlave($activeSlave.ID).womb[_ap].genetics.markings == "heavily freckled">>
+							<<if getSlave($AS).womb[_ap].genetics.markings == "freckles" || getSlave($AS).womb[_ap].genetics.markings == "heavily freckled">>
 								<div class="indent">
-									Markings: <<= capFirstChar(getSlave($activeSlave.ID).womb[_ap].genetics.markings)>>
+									Markings: <<= capFirstChar(getSlave($AS).womb[_ap].genetics.markings)>>
 								</div>
 							<</if>>
 							<div class="indent">
 								Detected abnormalities:
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.albinism == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.albinism == 2>>
 									@@.green;alb@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.albinism == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.albinism == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;alb@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.gigantism == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.gigantism == 2>>
 									@@.red;gnt@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.gigantism == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.gigantism == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;gnt@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.dwarfism == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.dwarfism == 2>>
 									@@.red;dwrf@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.dwarfism == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.dwarfism == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;dwrf@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.heterochromia == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.heterochromia == 2>>
 									@@.green;hetchrom@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.heterochromia == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.heterochromia == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;hetchrom@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.androgyny == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.androgyny == 2>>
 									@@.red;andr@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.androgyny == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.androgyny == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;andr@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.pFace == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.pFace == 2>>
 									@@.green;pfce@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.pFace == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.pFace == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;pfce@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.uFace == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.uFace == 2>>
 									@@.red;ufce@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.uFace == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.uFace == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;ufce@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.fertility == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.fertility == 2>>
 									@@.green;fert@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.fertility == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.fertility == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;fert@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.hyperFertility == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.hyperFertility == 2>>
 									@@.green;hfert@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.hyperFertility == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.hyperFertility == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;hfert@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.superfetation == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.superfetation == 2>>
 									@@.green;supfet@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.superfetation == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.superfetation == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;supfet@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.uterineHypersensitivity == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.uterineHypersensitivity == 2>>
 									@@.green;uthyp@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.uterineHypersensitivity == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.uterineHypersensitivity == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;uthyp@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.macromastia >= 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.macromastia >= 2>>
 									@@.red;mmast@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.macromastia == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.macromastia == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;mast@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.gigantomastia >= 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.gigantomastia >= 2>>
 									@@.red;gmast@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.gigantomastia == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.gigantomastia == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;gmast@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wellHung == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.wellHung == 2>>
 									@@.green;bigd@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wellHung == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.wellHung == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;bigd@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.rearLipedema == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.rearLipedema == 2>>
 									@@.red;lipe@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.rearLipedema == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.rearLipedema == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;lipe@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wGain == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.wGain == 2>>
 									@@.red;lepti+@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wGain == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.wGain == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;lepti+@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wLoss == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.wLoss == 2>>
 									@@.red;lepti-@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.wLoss == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.wLoss == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;lepti-@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.mGain == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.mGain == 2>>
 									@@.red;myot+@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.mGain == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.mGain == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;myot+@@
 								<</if>>
-								<<if getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.mLoss == 2>>
+								<<if getSlave($AS).womb[_ap].genetics.geneticQuirks.mLoss == 2>>
 									@@.red;myot-@@
-								<<elseif getSlave($activeSlave.ID).womb[_ap].genetics.geneticQuirks.mLoss == 1 && $geneticMappingUpgrade >= 2>>
+								<<elseif getSlave($AS).womb[_ap].genetics.geneticQuirks.mLoss == 1 && $geneticMappingUpgrade >= 2>>
 									@@.yellow;myot-@@
 								<</if>>
 							</div>
 						<<else>>
-							<<if getSlave($activeSlave.ID).womb[_ap].age > 13>>
+							<<if getSlave($AS).womb[_ap].age > 13>>
 								<div class="indent">
-									Gender: <<= getSlave($activeSlave.ID).womb[_ap].genetics.gender>>
+									Gender: <<= getSlave($AS).womb[_ap].genetics.gender>>
 								</div>
 							<</if>>
-							<<if getSlave($activeSlave.ID).womb[_ap].age > 5>>
+							<<if getSlave($AS).womb[_ap].age > 5>>
 								<div class="indent">
-									Father ID: <<= getSlave($activeSlave.ID).womb[_ap].genetics.father>>
+									Father ID: <<= getSlave($AS).womb[_ap].genetics.father>>
 								</div>
 								<div class="indent">
-									Father Name: <<= getSlave($activeSlave.ID).womb[_ap].genetics.fatherName>>
+									Father Name: <<= getSlave($AS).womb[_ap].genetics.fatherName>>
 								</div>
 								<div class="indent">
-									Mother ID: <<= getSlave($activeSlave.ID).womb[_ap].genetics.mother>>
+									Mother ID: <<= getSlave($AS).womb[_ap].genetics.mother>>
 								</div>
 								<div class="indent">
-									Mother Name: <<= getSlave($activeSlave.ID).womb[_ap].genetics.motherName>>
+									Mother Name: <<= getSlave($AS).womb[_ap].genetics.motherName>>
 								</div>
 							<</if>>
 						<</if>>
 						<<if $incubator > 0 || $nursery > 0>>
 							<div class="indent">
-								Reserved: <<= getSlave($activeSlave.ID).womb[_ap].reserve>>
+								Reserved: <<= getSlave($AS).womb[_ap].reserve>>
 							</div>
 						<</if>>
 
 						<div class="indent">
-							Rename: <<textbox "_tempName" _tempName >> [[Apply | Analyze Pregnancy][getSlave($activeSlave.ID).womb[_ap].genetics.name = _tempName]]
+							Rename: <<textbox "_tempName" _tempName >> [[Apply | Analyze Pregnancy][getSlave($AS).womb[_ap].genetics.name = _tempName]]
 						</div>
-						<<if getSlave($activeSlave.ID).womb[_ap].age < 4 && ($arcologies[0].FSRestart == "unset" || $eugenicsFullControl == 1 || getSlave($activeSlave.ID).breedingMark == 0 || $propOutcome == 0 || (getSlave($activeSlave.ID).womb[_ap].fatherID != -1 && getSlave($activeSlave.ID).womb[_ap].fatherID != -6))>>
+						<<if getSlave($AS).womb[_ap].age < 4 && ($arcologies[0].FSRestart == "unset" || $eugenicsFullControl == 1 || getSlave($AS).breedingMark == 0 || $propOutcome == 0 || (getSlave($AS).womb[_ap].fatherID != -1 && getSlave($AS).womb[_ap].fatherID != -6))>>
 							<div class="indent">
-								<<link "Terminate ovum" "Analyze Pregnancy">><<run WombRemoveFetus(getSlave($activeSlave.ID), _ap)>><<if getSlave($activeSlave.ID).preg == 0>><<set getSlave($activeSlave.ID).pregWeek = -1>><</if>><</link>>
+								<<link "Terminate ovum" "Analyze Pregnancy">><<run WombRemoveFetus(getSlave($AS), _ap)>><<if getSlave($AS).preg == 0>><<set getSlave($AS).pregWeek = -1>><</if>><</link>>
 								<<if $surgeryUpgrade == 1>>
-									| [[Transplant ovum|Ova Transplant Workaround][$donatrix = getSlave($activeSlave.ID), $wombIndex = _ap, $nextLink = "Analyze Pregnancy"]]
+									| [[Transplant ovum|Ova Transplant Workaround][$donatrix = getSlave($AS), $wombIndex = _ap, $nextLink = "Analyze Pregnancy"]]
 								<</if>>
 							</div>
 						<</if>>
 						<<if $incubator > 0>>
 							<div class="indent">
-								<<if getSlave($activeSlave.ID).womb[_ap].reserve === "incubator">>
+								<<if getSlave($AS).womb[_ap].reserve === "incubator">>
 									<<link "Don't keep this child in $incubatorName" "Analyze Pregnancy">>
-										<<set getSlave($activeSlave.ID).womb[_ap].reserve = "">>
+										<<set getSlave($AS).womb[_ap].reserve = "">>
 										<<set $reservedChildren-->>
 									<</link>>
 								<<elseif _freeTanks - $reservedChildren > 0>>
 									<<link "Keep this child in $incubatorName" "Analyze Pregnancy">>
-										<<if getSlave($activeSlave.ID).womb[_ap].reserve === "nursery">>
+										<<if getSlave($AS).womb[_ap].reserve === "nursery">>
 											<<set $reservedChildrenNursery-->>
 										<</if>>
-										<<set getSlave($activeSlave.ID).womb[_ap].reserve = "incubator">>
+										<<set getSlave($AS).womb[_ap].reserve = "incubator">>
 										<<set $reservedChildren++>>
 									<</link>>
 								<<else>>
@@ -336,17 +336,17 @@
 						<</if>>
 						<<if $nursery > 0>>
 							<div class="indent">
-								<<if getSlave($activeSlave.ID).womb[_ap].reserve === "nursery">>
+								<<if getSlave($AS).womb[_ap].reserve === "nursery">>
 									<<link "Don't keep this child in $nurseryName" "Analyze Pregnancy">>
-										<<set getSlave($activeSlave.ID).womb[_ap].reserve = "">>
+										<<set getSlave($AS).womb[_ap].reserve = "">>
 										<<set $reservedChildrenNursery-->>
 									<</link>>
 								<<elseif _freeCribs - $reservedChildrenNursery > 0>>
 									<<link "Keep this child in $nurseryName" "Analyze Pregnancy">>
-										<<if getSlave($activeSlave.ID).womb[_ap].reserve === "incubator">>
+										<<if getSlave($AS).womb[_ap].reserve === "incubator">>
 											<<set $reservedChildren-->>
 										<</if>>
-										<<set getSlave($activeSlave.ID).womb[_ap].reserve = "nursery">>
+										<<set getSlave($AS).womb[_ap].reserve = "nursery">>
 										<<set $reservedChildrenNursery++>>
 									<</link>>
 								<<else>>
@@ -362,9 +362,9 @@
 							Age: too early for scan.
 						</div>
 						<div class="indent">
-							<<link "Terminate ovum" "Analyze Pregnancy">><<run WombRemoveFetus(getSlave($activeSlave.ID), _ap)>><<if getSlave($activeSlave.ID).preg == 0>><<set getSlave($activeSlave.ID).pregWeek = -1>><</if>><</link>>
+							<<link "Terminate ovum" "Analyze Pregnancy">><<run WombRemoveFetus(getSlave($AS), _ap)>><<if getSlave($AS).preg == 0>><<set getSlave($AS).pregWeek = -1>><</if>><</link>>
 							<<if $surgeryUpgrade == 1>>
-								| [[Transplant ovum|Ova Transplant Workaround][$donatrix = getSlave($activeSlave.ID), $wombIndex = _ap, $nextLink = "Analyze Pregnancy"]]
+								| [[Transplant ovum|Ova Transplant Workaround][$donatrix = getSlave($AS), $wombIndex = _ap, $nextLink = "Analyze Pregnancy"]]
 							<</if>>
 						</div>
 					<</if>>
@@ -372,16 +372,16 @@
 			</p>
 		<</for>>
 	</p>
-<<elseif getSlave($activeSlave.ID).preg == -3>> /*special states*/
+<<elseif getSlave($AS).preg == -3>> /*special states*/
 	Failure to locate any ova. Subject is infertile.
-<<elseif getSlave($activeSlave.ID).pregWeek < 0>> /*special states*/
+<<elseif getSlave($AS).pregWeek < 0>> /*special states*/
 	Subject is in the postpartem period.
-<<elseif getSlave($activeSlave.ID).ovaryAge >= 47 >>
+<<elseif getSlave($AS).ovaryAge >= 47 >>
 	$His infertility is due to menopausal ovaries.
-<<elseif getSlave($activeSlave.ID).ovaries === 0 && getSlave($activeSlave.ID).mpreg === 0>>
+<<elseif getSlave($AS).ovaries === 0 && getSlave($AS).mpreg === 0>>
 	$His infertility is due to missing ovaries.
-<<elseif getSlave($activeSlave.ID).preg == -2>> /*special states*/
+<<elseif getSlave($AS).preg == -2>> /*special states*/
 	$His infertility is due to sterilization; $his ovaries could still work.
-<<elseif getSlave($activeSlave.ID).preg == -1>> /*special states*/
+<<elseif getSlave($AS).preg == -1>> /*special states*/
 	Contraceptive agents detected in subject.
 <</if>>
diff --git a/src/pregmod/incubator.tw b/src/pregmod/incubator.tw
index 0f1681e67e4c2357b43fd3f5e41de8d70e5446e9..019d2157436ec4e0f592c60c7e6e2923d5e69b9f 100644
--- a/src/pregmod/incubator.tw
+++ b/src/pregmod/incubator.tw
@@ -116,7 +116,7 @@ Reserve an eligible mother-to-be's child to be placed in a tank upon birth. Of $
 				<<if (_reservedIncubator + _reservedNursery < _WL) && ($reservedChildren < _freeTanks)>>
 					<br>&nbsp;&nbsp;&nbsp;&nbsp;
 					<<if $pregnancyMonitoringUpgrade == 1>>
-						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $activeSlave = $slaves[$i]]] | ">>
+						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $AS = $slaves[$i].ID]] | ">>
 					<</if>>
 					<<print "[[Keep another child|Incubator][WombAddToGenericReserve($slaves[" + _u + "], 'incubator', 1)]]">>
 					<<if _reservedIncubator > 0>>
@@ -131,7 +131,7 @@ Reserve an eligible mother-to-be's child to be placed in a tank upon birth. Of $
 				<<elseif (_reservedIncubator == _WL) || ($reservedChildren == _freeTanks) || (_reservedIncubator + _reservedNursery == _WL)>>
 					<br>&nbsp;&nbsp;&nbsp;&nbsp;
 					<<if $pregnancyMonitoringUpgrade == 1>>
-						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $activeSlave = $slaves[$i]]] | ">>
+						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $AS = $slaves[$i].ID]] | ">>
 					<</if>>
 					<<print "[[Keep one less child|Incubator][WombCleanGenericReserve($slaves[" + _u + "], 'incubator', 1)]]">>
 					<<if _reservedIncubator > 1>>
@@ -143,14 +143,14 @@ Reserve an eligible mother-to-be's child to be placed in a tank upon birth. Of $
 					//$His children are already reserved for $nurseryName//
 					<br>&nbsp;&nbsp;&nbsp;&nbsp;
 					<<if $pregnancyMonitoringUpgrade == 1>>
-						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $activeSlave = $slaves[$i]]] | ">>
+						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $AS = $slaves[$i].ID]] | ">>
 					<</if>>
 					<<print "[[Keep " + $his + " " + ((_WL > 1) ? "children" : "child") + " here instead|Incubator][WombChangeReserveType($slaves[" + _u + "], 'nursery', 'incubator')]]">>
 				<<else>>
 					You have <<if _freeTanks == 1>>an<</if>> @@.lime;available aging tank<<if _freeTanks > 1>>s<</if>>.@@
 					<br>&nbsp;&nbsp;&nbsp;&nbsp;
 					<<if $pregnancyMonitoringUpgrade == 1>>
-						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $activeSlave = $slaves[$i]]] | ">>
+						<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $AS = $slaves[$i].ID]] | ">>
 					<</if>>
 					<<print "[[Keep "+ ((_WL > 1) ? "a" : "the") +" child|Incubator][WombAddToGenericReserve($slaves[" + _u + "], 'incubator', 1)]]">>
 					<<if (_WL > 1) && ($reservedChildren + _WL - _reservedIncubator) <= _freeTanks>>
@@ -160,7 +160,7 @@ Reserve an eligible mother-to-be's child to be placed in a tank upon birth. Of $
 			<<elseif $reservedChildren == _freeTanks>>
 				<br>&nbsp;&nbsp;&nbsp;&nbsp;
 				<<if $pregnancyMonitoringUpgrade == 1>>
-					<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $activeSlave = $slaves[$i]]] | ">>
+					<<print "[[Inspect pregnancy|Analyze Pregnancy][$i = " + _u + "; $AS = $slaves[$i].ID]] | ">>
 				<</if>>
 				You have @@.red;no room for $his offspring.@@
 			<</if>>
@@ -181,7 +181,7 @@ Reserve an eligible mother-to-be's child to be placed in a tank upon birth. Of $
 	<<set _WL = $PC.womb.length>>
 	<<set _reservedIncubator = WombReserveCount($PC, "incubator")>>
 	<<set _reservedNursery = WombReserveCount($PC, "nursery")>>
-	<br>''@@.pink;You're pregnant@@'' and going to have
+	<br>''@@.pink;You're $PC.pregWeek week<<if $PC.pregWeek > 1>>s<</if>> pregnant@@'' and going to have
 		<<if _WL == 1>>
 			a baby.
 		<<else>>
diff --git a/src/pregmod/pRaped.tw b/src/pregmod/pRaped.tw
index 3b3f74bc797350cc5dcb90d0804e52129a835007..861b3e0ab0f56cd04ba945a753a8da999e47b4cf 100644
--- a/src/pregmod/pRaped.tw
+++ b/src/pregmod/pRaped.tw
@@ -4,16 +4,15 @@
 
 <<setPlayerPronouns>>
 
-<<set $activeSlaveOneTimeMinAge = 16>>
-<<set $oneTimeDisableDisability = 1>>
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
+<<set _genParam = {minAge: 16, race: _race, disableDisability: 1}>>
 <<if $PC.vagina >= 0>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", _genParam)>>
 	<<set $activeSlave.dick = 6>>
 	<<set $activeSlave.balls = 5>>
 	<<set $activeSlave.boobs = 0>>
@@ -21,7 +20,7 @@
 	<<set $activeSlave.skill.oral = 0>>
 	<<set $activeSlave.skill.anal = 0>>
 <<else>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", _genParam)>>
 	<<set $activeSlave.vagina = 1>>
 	<<set $activeSlave.preg = 0>>
 	<<set $activeSlave.weight = random(-20,30)>>
diff --git a/src/pregmod/reTheSirenStrikesBack.tw b/src/pregmod/reTheSirenStrikesBack.tw
index c24337547c7ba457a6f9479a49d7f5ee5ab7ccb0..45c94ea84ff53752cd6dec3113f3c1f1f34f9fef 100644
--- a/src/pregmod/reTheSirenStrikesBack.tw
+++ b/src/pregmod/reTheSirenStrikesBack.tw
@@ -6,14 +6,11 @@
 <<set _ssb = $slaves.find(function(s) { return s.origin == "$He came to you to escape being sold to a cruel master after $his producer informed $him of $his debt." && s.newGamePlus == 0 && s.fetish != "mindbroken" && s.fuckdoll == 0; })>>
 
 <<if $pedo_mode == 1>>
-	<<set $activeSlaveOneTimeMinAge = 16>>
+	<<set _minAge = 16>>
 <<else>>
-	<<set $activeSlaveOneTimeMinAge = 30>>
+	<<set _minAge = 30>>
 <</if>>
-<<set $activeSlaveOneTimeMaxAge = 40>>
-<<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $oneTimeDisableDisability = 1>>
-<<set $activeSlave = GenerateNewSlave()>>
+<<set $activeSlave = GenerateNewSlave(null, {minAge: _minAge, maxAge: 40, ageOverridesPedoMode: 1, disableDisability: 1})>>
 <<set $activeSlave.origin = "$He was brought to you to pay for what $he did to one of your slaves.">>
 <<set $activeSlave.career = "a producer">>
 <<set $activeSlave.devotion = random(-70,30)>>
diff --git a/src/pregmod/seHuskSlaveDelivery.tw b/src/pregmod/seHuskSlaveDelivery.tw
index 318855e4c148b70b70e67e389137a476054a6260..c2079cb81bdd72a153cef01aa320b4bb7e2271f9 100644
--- a/src/pregmod/seHuskSlaveDelivery.tw
+++ b/src/pregmod/seHuskSlaveDelivery.tw
@@ -5,17 +5,16 @@
 <<set $activeSlaveOneTimeMinAge = parseInt($huskSlave.age)>>
 <<set $activeSlaveOneTimeMaxAge = parseInt($huskSlave.age)>>
 <<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $fixedNationality = $huskSlave.nationality>>
 <<if $huskSlave.race != "not important">>
 	<<set $fixedRace = $huskSlave.race>>
 <</if>>
 <<if $huskSlave.sex == 2>>
-	<<set $activeSlave = GenerateNewSlave("XY", {mature: 0})>>
+	<<set $activeSlave = GenerateNewSlave("XY", {mature: 0, nationality: $huskSlave.nationality})>>
 	<<set $activeSlave.boobs = 50>>
 	<<set $activeSlave.boobsImplant = 0>>
 	<<set $activeSlave.boobsImplantType = "none">>
 <<else>>
-	<<set $activeSlave = GenerateNewSlave("XX", {mature: 0})>>
+	<<set $activeSlave = GenerateNewSlave("XX", {mature: 0, nationality: $huskSlave.nationality})>>
 <</if>>
 
 <<set $activeSlave.slaveName = "Doll">>
diff --git a/src/uncategorized/RESS.tw b/src/uncategorized/RESS.tw
index 7633fcf64f0eb84fd406aa770b5756d8583ff078..9e6128a4681ac946212a102aa1d834bbabf0891b 100644
--- a/src/uncategorized/RESS.tw
+++ b/src/uncategorized/RESS.tw
@@ -65,18 +65,13 @@
 <<if $RESSevent == "mean girls">>
 	<<set _newSlaves = []>>
 	<<for _ress = 0; _ress < 3; _ress++>>
-		<<set $oneTimeDisableDisability = 1>>
 		<<if $arcologies[0].FSSupremacistLawME == 1>>
-			<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+			<<set _race = $arcologies[0].FSSupremacistRace>>
 		<<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 			<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-			<<set $fixedRace = _races.random()>>
+			<<set _race = _races.random()>>
 		<</if>>
-		<<set $fixedNationality = "American">>
-		<<set $one_time_age_overrides_pedo_mode = 1>>
-		<<set $activeSlaveOneTimeMinAge = 16>>
-		<<set $activeSlaveOneTimeMaxAge = 19>>
-		<<set _meanGirl = GenerateNewSlave("XX")>>
+		<<set _meanGirl = GenerateNewSlave("XX", {minAge: 16, maxAge: 19, ageOverridesPedoMode: 1, nationality: "American", race: _race, disableDisability: 1})>>
 		<<set _meanGirl.origin = "You enslaved $him for abusing " + $eventSlave.slaveName + " while " + getPronouns($eventSlave).pronoun + " was serving the public.">>
 		<<set _meanGirl.career = "a student">>
 		<<set _meanGirl.markings = "none", _meanGirl.weight = -20>>
diff --git a/src/uncategorized/REroyalblood.tw b/src/uncategorized/REroyalblood.tw
index 87f444edb073ad7badfa77c137a09ffa2bdbe0f7..4328209a088c5770895dfbaeb69ffa4b3b6939a1 100644
--- a/src/uncategorized/REroyalblood.tw
+++ b/src/uncategorized/REroyalblood.tw
@@ -394,8 +394,7 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 			<<set $activeSlaveOneTimeMinAge = 21>>
 			<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
 			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedNationality = _princess.nationality>>
-			<<set $activeSlave = GenerateNewSlave("XX")>>
+			<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 			<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was sold to you in exchange for credits.">>
 			<<set $activeSlave.origin = _origin>>
 			<<set $activeSlave.career = "a lady courtier">>
@@ -445,8 +444,7 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 			<<set $activeSlaveOneTimeMinAge = 21>>
 			<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
 			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedNationality = _princess.nationality>>
-			<<set $activeSlave = GenerateNewSlave("XX")>>
+			<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 			<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was acquired by "+$SF.Lower+".">>
 			<<set $activeSlave.origin = _origin>>
 			<<set $activeSlave.career = "a lady courtier">>
@@ -619,8 +617,7 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 			<<set $activeSlaveOneTimeMinAge = 21>>
 			<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
 			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedNationality = _princess.nationality>>
-			<<set $activeSlave = GenerateNewSlave("XX")>>
+			<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 			<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was sold for credits.">>
 			<<set $activeSlave.origin = _origin>>
 			<<set $activeSlave.career = "a lady courtier">>
@@ -680,8 +677,7 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 			<<set $activeSlaveOneTimeMinAge = 21>>
 			<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
 			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedNationality = _princess.nationality>>
-			<<set $activeSlave = GenerateNewSlave("XX")>>
+			<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 			<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was acquired by "+$SF.Lower+".">>
 			<<set $activeSlave.origin = _origin>>
 			<<set $activeSlave.career = "a lady courtier">>
@@ -764,8 +760,7 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 				<<set $activeSlaveOneTimeMinAge = 21>>
 				<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
 				<<set $oneTimeDisableDisability = 1>>
-				<<set $fixedNationality = _princess.nationality>>
-				<<set $activeSlave = GenerateNewSlave("XX")>>
+				<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 				<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was sold for credits.">>
 				<<set $activeSlave.origin = _origin>>
 				<<set $activeSlave.career = "a lady courtier">>
@@ -866,9 +861,8 @@ Time is short, but you are well placed to acquire some choice slaves. With an ad
 				<<for $i = 0; $i < 3; $i++>>
 					<<set $activeSlaveOneTimeMinAge = 21>>
 					<<set $activeSlaveOneTimeMaxAge = ($retirementAge-2)>>
-					<<set $fixedNationality = _princess.nationality>>
 					<<set $oneTimeDisableDisability = 1>>
-					<<set $activeSlave = GenerateNewSlave("XX")>>
+					<<set $activeSlave = GenerateNewSlave("XX", {nationality: _princess.nationality})>>
 					<<set _origin = "$He was a member of the court in an ancient kingdom, till it was overthrown and $he was acquired by "+$SF.Lower+".">>
 					<<set $activeSlave.origin = _origin>>
 					<<set $activeSlave.career = "a lady courtier">>
diff --git a/src/uncategorized/arcade.tw b/src/uncategorized/arcade.tw
index d64d67cf085dd86e50bf947e4a5a8c016aada927..4ec0d543d468d0bee18c71b9f2c2b66faeb2f86a 100644
--- a/src/uncategorized/arcade.tw
+++ b/src/uncategorized/arcade.tw
@@ -146,7 +146,10 @@
 	<<else>>
 		The arcade can be upgraded to include curative injectors in order to keep inmates from succumbing under the harsh treatment. You are assured the inmates won't like their time in the arcade any better, it is purely intended to keep them functional and ready for use around the clock. It comes equipped with two settings.
 		<div class="choices">
-			[[Install curative injectors|Arcade][$arcadeUpgradeHealth = 1]]
+			[[Install curative injectors|Arcade][cashX(forceNeg(_Tmult1), "capEx"), $arcadeUpgradeHealth = 1]]
+		</div>
+		<div class="note">
+			Costs <<print cashFormat(_Tmult1)>> and will increase upkeep costs
 		</div>
 	<</if>>
 </div>
@@ -188,3 +191,5 @@
 		Use a noun or similar short phrase
 	</span>
 </p>
+
+<<run App.UI.SlaveList.ScrollPosition.restore()>>
diff --git a/src/uncategorized/bodyModification.tw b/src/uncategorized/bodyModification.tw
index 4635f08762285bbf4fd2df7fc8a7c4675af87b91..5cfb1adedfca915ff626ee7dccf7c079b47bddbf 100644
--- a/src/uncategorized/bodyModification.tw
+++ b/src/uncategorized/bodyModification.tw
@@ -1,7 +1,6 @@
 :: Body Modification [nobr jump-from-safe]
 
 <<set $nextButton = "Confirm changes", $nextLink = "Slave Interact">>
-<<set $AS = $activeSlave.ID>>
 <<run App.Utils.setLocalPronouns(getSlave($AS))>>
 <<run Enunciate(getSlave($AS))>>
 
diff --git a/src/uncategorized/dairy.tw b/src/uncategorized/dairy.tw
index 5631c32cfa76f235a4b156b71b177f56a7f5578a..f6926859cd7c3d67eff9b3c49367c8e51c2cc66b 100644
--- a/src/uncategorized/dairy.tw
+++ b/src/uncategorized/dairy.tw
@@ -684,6 +684,43 @@
 	<</if>>
 </div>
 
+<div>
+	<<set _note = "<span class='note'>NOTE: Favors milk production if available</span>">>
+	<<if $dairyHormonesSetting == 0>>
+		Cows will not be given hormones.
+		<div class="choices">
+			[[Apply Hormones|Dairy][$dairyHormonesSetting = 1]] |
+			[[Apply Intense Hormones|Dairy][$dairyHormonesSetting = 2]] |
+			[[Disable Hormone Control|Dairy][$dairyHormonesSetting = -1]]
+			<<print _note>>
+		</div>
+	<<elseif $dairyHormonesSetting == 1>>
+		Cows will be given hormones.
+		<div class="choices">
+			[[Increase Hormone Dosage|Dairy][$dairyHormonesSetting = 2]] |
+			[[Hormone Free|Dairy][$dairyHormonesSetting = 0]] |
+			[[Disable Hormone Control|Dairy][$dairyHormonesSetting = -1]]
+			<<print _note>>
+		</div>
+	<<elseif $dairyHormonesSetting == 2>>
+		Cows will undergo intense hormone treatment.
+		<div class="choices">
+			[[Decrease Hormone Dosage|Dairy][$dairyHormonesSetting = 1]] |
+			[[Hormone Free|Dairy][$dairyHormonesSetting = 0]] |
+			[[Disable Hormone Control|Dairy][$dairyHormonesSetting = -1]]
+			<<print _note>>
+		</div>
+	<<else>>
+		Hormone application is left to your discretion.
+		<div class="choices">
+			[[Apply Hormones|Dairy][$dairyHormonesSetting = 1]] |
+			[[Apply Intense Hormones|Dairy][$dairyHormonesSetting = 2]] |
+			[[Hormone Free|Dairy][$dairyHormonesSetting = 0]]
+			<<print _note>>
+		</div>
+	<</if>>
+</div>
+
 <div>
 	<<if $arcologies[0].FSPaternalist == "unset">>
 		<<if $dairyUpgradeMenials == 1>>
@@ -710,7 +747,7 @@
 
 <<if ($dairyPregSetting > 1) || ($dairyFeedersSetting > 1) || ($dairyStimulatorsSetting > 1)>>
 	<p>
-		@@.yellow;WARNING:@@ current milking machine settings may have irreversible effects on cows.
+		@@.yellow;WARNING:@@ current milking machine settings will have dramatic and possibly irreversible effects on cow bodies and minds.
 	</p>
 <</if>>
 
@@ -771,3 +808,5 @@
 		Use a noun or similar short phrase
 	</span>
 </p>
+
+<<run App.UI.SlaveList.ScrollPosition.restore()>>
diff --git a/src/uncategorized/dairyReport.tw b/src/uncategorized/dairyReport.tw
index 8a7bbfbae6c5343fabb6190e7e48bb449617bcdf..79cd8a60eb7d7d2e9a42d4d634ecc4a8463830d1 100644
--- a/src/uncategorized/dairyReport.tw
+++ b/src/uncategorized/dairyReport.tw
@@ -485,12 +485,17 @@
 			<</if>>
 		<</if>>
 	<</if>>
+	<<if $dairyHormonesSetting >= 0>>
+		<<if _slave.lactation > 0>>
+			<<set _slave.hormones = $dairyHormonesSetting>>
+		<<elseif _slave.balls > 0>>
+			<<set _slave.hormones = -1 * $dairyHormonesSetting>>
+		<<else>>
+			<<set _slave.hormones = $dairyHormonesSetting>>
+		<</if>>
+	<</if>>
 	<<if ($dairyFeedersUpgrade == 1) && ($dairyFeedersSetting > 0)>>
-		<<if ($dairySlimMaintain == 0)>>
-			<<if (_slave.balls == 0)>>
-				<<set _slave.hormones = 2>>
-			<</if>>
-		<<elseif _slave.diet == "fattening">>
+		<<if $dairySlimMaintain == 0 && _slave.diet == "fattening">>
 			<<set _slave.weight += 2>>
 			<<if _slave.weightDirection == 1>>
 				<<set _slave.weight += 2>>
diff --git a/src/uncategorized/genericPlotEvents.tw b/src/uncategorized/genericPlotEvents.tw
index 212b554dfb3c82760f2f93c7338c3100432b1645..24cbaf6bb6e62b293bcc9c9a4ba33699adac8aef 100644
--- a/src/uncategorized/genericPlotEvents.tw
+++ b/src/uncategorized/genericPlotEvents.tw
@@ -1155,7 +1155,7 @@ A screen opposite your desk springs to life, <<if $assistant.personality == 0>>s
 	<<set $activeSlave.preg = -1>>
 	<<set $activeSlave.muscles = 50>>
 	<<set $activeSlave.weight = 0>>
-	<<run setHealth($activeSlave, 80, undefined, undefined undefined, 0)>>
+	<<run setHealth($activeSlave, 80, undefined, undefined, undefined, 0)>>
 	<<set $activeSlave.boobs = 400>>
 	<<set $activeSlave.butt = 2>>
 	<<set $activeSlave.waist = 0>>
@@ -1205,7 +1205,7 @@ A screen opposite your desk springs to life, <<if $assistant.personality == 0>>s
 		<<set $activeSlave.muscles = 0>>
 		<<set $activeSlave.weight = -10>>
 		<<set $activeSlave.height = 135>>
-		<<run setHealth($activeSlave, 80, undefined, undefined undefined, 0)>>
+		<<run setHealth($activeSlave, 80, undefined, undefined, undefined, 0)>>
 		<<set $activeSlave.boobs = 200>>
 		<<set $activeSlave.butt = 1>>
 		<<set $activeSlave.waist = -50>>
@@ -1263,7 +1263,7 @@ A screen opposite your desk springs to life, <<if $assistant.personality == 0>>s
 	<<set $activeSlave.skill.anal = 0>>
 	<<set $activeSlave.muscles = 20>>
 	<<set $activeSlave.weight = 20>>
-	<<run setHealth($activeSlave, 30, undefined, undefined undefined, 0)>>
+	<<run setHealth($activeSlave, 30, undefined, undefined, undefined, 0)>>
 	<<if $activeSlave.lips < 10>><<set $activeSlave.lips += 10>><</if>>
 	<<set $activeSlave.boobs = 1000>>
 	<<set $activeSlave.butt = 6>>
@@ -1321,7 +1321,7 @@ A screen opposite your desk springs to life, <<if $assistant.personality == 0>>s
 		<<set $activeSlave.skill.anal = 35>>
 		<<set $activeSlave.muscles = 0>>
 		<<set $activeSlave.weight = 0>>
-		<<run setHealth($activeSlave, 30, undefined, undefined undefined, 0)>>
+		<<run setHealth($activeSlave, 30, undefined, undefined, undefined, 0)>>
 		<<if $activeSlave.lips < 10>><<set $activeSlave.lips += 10>><</if>>
 		<<set $activeSlave.boobs = 800>>
 		<<set $activeSlave.butt = 4>>
diff --git a/src/uncategorized/main.tw b/src/uncategorized/main.tw
index 85c54fa7b0d4ee869efc1040417f4fdc8be97e52..c8f7fb34b21dab882b08c299c00c52a5eae39ca1 100644
--- a/src/uncategorized/main.tw
+++ b/src/uncategorized/main.tw
@@ -1,13 +1,8 @@
 :: Main [nobr jump-to-safe jump-from-safe]
 
-/* make sure the first thing that happens is the error check */
-<<set _errors = App.MainView.errors()>>
-<span id="errors"></span>
-<<script>>
-	$(document).one(':passagerender', event => {
-		event.content.prepend(State.temporary.errors);
-	});
-<</script>>
+<<includeDOM App.MainView.errors()>>
+
+<<set $nextButton = "END WEEK", $nextLink = "End Week", $encyclopedia = "How to Play">>
 
 <<if $tabChoice.SlaveInteract != 'Description'>>
 	<<set $tabChoice.SlaveInteract = 'Description'>>
@@ -15,7 +10,6 @@
 
 <<run penthouseCensus()>>
 
-<<set $nextButton = "END WEEK", $nextLink = "End Week", $encyclopedia = "How to Play">>
 <<set $costs = Math.trunc(calculateCosts.predict())>>
 
 <<set $currentRule = $defaultRules[0]>>
@@ -23,26 +17,11 @@
 <<run SlaveSort.slaves($slaves)>>
 
 <<if $newModelUI == 1>>
-	<span id="building"></span>
-	<<script>>
-		$(document).one(':passageend', () => {
-			$('#building').append(
-				V.building.render(),
-			);
-		});
-	<</script>>
+    <<includeDOM V.building.render()>>
 <</if>>
 
 <<if $seeArcology == 1>>
-	<div id="arc-desc"></div>
-	<<script>>
-		$(document).one(':passageend', () => {
-			$('#arc-desc').append(
-				App.Desc.playerArcology(App.UI.DOM.combineNodes("| ",
-					App.UI.DOM.passageLink("Hide", "Main", () => {V.seeArcology = 0})))
-			);
-		});
-	<</script>>
+    <<includeDOM App.Desc.playerArcology(App.UI.DOM.combineNodes("| ", App.UI.DOM.passageLink("Hide", "Main", () => {V.seeArcology = 0})))>>
 <</if>>
 
 <<if $seeDesk == 1>>
@@ -52,12 +31,4 @@
 
 <<includeDOM App.MainView.full()>>
 
-<<set $activeSlave = $slaves.random()>>
-<<if $activeSlave && ($activeSlave.assignment != "please you") && ($activeSlave.assignment != "guard you")>>
-	<br>//<span id="walk"><<= walkPast($activeSlave)>></span>//
-<</if>>
-<<if ($debugMode == 1)>>
-	//| [[Show all walk past scenes|Walk Past List]]//
-<</if>>
-
 <<run App.UI.SlaveList.ScrollPosition.restore()>>
diff --git a/src/uncategorized/multiImplant.tw b/src/uncategorized/multiImplant.tw
index 09e661aec1177c6ea24c58aa7994f95c0258f07a..adb5c7250e14f6ad6cd5ac5f78904289b7684565 100644
--- a/src/uncategorized/multiImplant.tw
+++ b/src/uncategorized/multiImplant.tw
@@ -17,12 +17,12 @@ that are ready be sent down.
 <h1>Implant Organs</h1>
 
 <<set _F = App.Medicine.OrganFarm>>
-<<for _i = 0; _i < $slaves.length; _i++>>
-	<<set $activeSlave = $slaves[_i]>>
-	<<set _sortedOrgans = _F.getSortedOrgans($activeSlave)>>
+<<for _i = 0; _i < $slaves.length; _i++>>	
+	<<set $AS = $slaves[_i].ID>>
+	<<set _sortedOrgans = _F.getSortedOrgans(getSlave($AS))>>
 	<<if _sortedOrgans.length === 0>><<continue>><</if>>
 
-	<h2>$activeSlave.slaveName</h2>
+	<h2>getSlave($AS).slaveName</h2>
 
 	<<for _k = 0; _k < _sortedOrgans.length; _k++>>
 		<h3><<print _F.Organs[_sortedOrgans[_k]].name>></h3>
@@ -30,22 +30,22 @@ that are ready be sent down.
 		<<set _manual = false, _success = false, _break = false>>
 		<<for _l = 0; _l < _actions.length; _l++>>
 			<<if !_actions[_l].autoImplant>>
-				<<if _actions[_l].canImplant($activeSlave)>>
+				<<if _actions[_l].canImplant(getSlave($AS))>>
 					<<set _manual = true>>
 				<</if>>
 				<<continue>>
 			<</if>>
-			<<if !_actions[_l].canImplant($activeSlave)>>
-				<<set _error = _actions[_l].implantError($activeSlave)>>
+			<<if !_actions[_l].canImplant(getSlave($AS))>>
+				<<set _error = _actions[_l].implantError(getSlave($AS))>>
 				<<if _error !== "">>
 					@@.red;ERROR: <<print _error>>@@<br>
 				<</if>>
-			<<elseif $activeSlave.health.health - _actions[_l].healthImpact < -75>>
+			<<elseif getSlave($AS).health.health - _actions[_l].healthImpact < -75>>
 				Estimated health impact too high, skipping further surgeries.
 				<<set _break = true>>
 				<<break>>
 			<<else>>
-				<<run App.Medicine.OrganFarm.implant($activeSlave, _sortedOrgans[_k], _l)>>
+				<<run App.Medicine.OrganFarm.implant(getSlave($AS), _sortedOrgans[_k], _l)>>
 				<<include "Surgery Degradation">><br><br>
 				<<set _success = true>>
 			<</if>>
@@ -55,8 +55,6 @@ that are ready be sent down.
 			//Cannot implant <<print _F.Organs[_sortedOrgans[_k]].name.toLowerCase()>> automatically, try implanting manually in the remote surgery.//
 		<</if>>
 	<</for>>
-
-	<<set $slaves[_i] = $activeSlave>>
 <</for>>
 
 
@@ -64,10 +62,10 @@ that are ready be sent down.
 
 /* prosthetics */
 <<for _i = 0; _i < $slaves.length; _i++>>
-	<<set $activeSlave = $slaves[_i]>>
+	<<set $AS = $slaves[_i].ID>>
 
 	/* count for estimating health impact */
-	<<set _prostheticCount = $adjustProsthetics.filter(p => p.slaveID == $activeSlave.ID && p.workLeft <= 0).length>>
+	<<set _prostheticCount = $adjustProsthetics.filter(p => p.slaveID == getSlave($AS).ID && p.workLeft <= 0).length>>
 
 	/* since we already have count, skip slaves that don't need work */
 	<<if _prostheticCount < 1>>
@@ -77,102 +75,102 @@ that are ready be sent down.
 	Ensures that a slave can never die during the execution of this passage.
 	Calculation based on worst case, so when changing worst case change it here too.
 	*/
-	<<if $activeSlave.health.health - (_prostheticCount * 20) < -75>>
+	<<if getSlave($AS).health.health - (_prostheticCount * 20) < -75>>
 		<br><hr>
-		@@.red;Estimated health impact too great; $activeSlave.slaveName skipped.@@
+		@@.red;Estimated health impact too great; getSlave($AS).slaveName skipped.@@
 		<<continue>>
 	<</if>>
 
 	<<for _k = 0; _k < $adjustProsthetics.length; _k++>>
 		<<set _p = $adjustProsthetics[_k]>>
-		<<if _p.slaveID == $activeSlave.ID && _p.workLeft <= 0>>
+		<<if _p.slaveID == getSlave($AS).ID && _p.workLeft <= 0>>
 			<<set $adjustProsthetics.splice(_k, 1), _k-->>
 			<<set $adjustProstheticsCompleted-->>
-			<<set addProsthetic($activeSlave, _p.id)>>
+			<<set addProsthetic(getSlave($AS), _p.id)>>
 			<br><hr>
 			<<switch _p.id>>
 			<<case "ocular">>
-				<<if getBestVision($activeSlave) === 0>>
-					<<run eyeSurgery($activeSlave, "both", "cybernetic"), cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave)>>
-					<<set surgeryDamage($activeSlave, 20), $surgeryType = "ocular implant">>
+				<<if getBestVision(getSlave($AS)) === 0>>
+					<<run eyeSurgery(getSlave($AS), "both", "cybernetic"), cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS))>>
+					<<set surgeryDamage(getSlave($AS), 20), $surgeryType = "ocular implant">>
 					<<include "Surgery Degradation">>
 				<<else>>
 					//Since $he has working eyes the <<= setup.prosthetics.ocular.name>> will be put into storage.//
 				<</if>>
 			<<case "cochlear">>
-				<<if $activeSlave.hears != 0>>
-					<<set $activeSlave.earImplant = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20), $surgeryType = "cochlear implant">>
+				<<if getSlave($AS).hears != 0>>
+					<<set getSlave($AS).earImplant = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "cochlear implant">>
 					<<include "Surgery Degradation">>
 				<<else>>
 					//Since $he has working ears the <<= setup.prosthetics.cochlear.name>> will be put into storage.//
 				<</if>>
 			<<case "electrolarynx">>
-				<<if $activeSlave.voice <= 0>>
-					<<set $activeSlave.electrolarynx = 1, $activeSlave.voice = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20), $surgeryType = "electrolarynx">>
+				<<if getSlave($AS).voice <= 0>>
+					<<set getSlave($AS).electrolarynx = 1, getSlave($AS).voice = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "electrolarynx">>
 					<<include "Surgery Degradation">>
 				<<else>>
 					//Since $he has a voice the <<= setup.prosthetics.electrolarynx.name>> will be put into storage.//
 				<</if>>
 			<<case "interfaceP1">>
-				<<if hasAnyNaturalLimbs($activeSlave)>>
+				<<if hasAnyNaturalLimbs(getSlave($AS))>>
 					//Since $he has at least one healthy limb the <<= setup.prosthetics.interfaceP1.name>> will be put into storage.//
-				<<elseif $activeSlave.PLimb == 2>>
+				<<elseif getSlave($AS).PLimb == 2>>
 					//Since $he already has <<= addA(setup.prosthetics.interfaceP2.name)>> installed the <<= setup.prosthetics.interfaceP1.name>> will be put into storage.//
 				<<else>>
-					<<set $activeSlave.PLimb = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20), $surgeryType = "PLimb interface1">>
+					<<set getSlave($AS).PLimb = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "PLimb interface1">>
 					<<include "Surgery Degradation">>
 				<</if>>
 			<<case "interfaceP2">>
-				<<if hasAllNaturalLimbs($activeSlave)>>
+				<<if hasAllNaturalLimbs(getSlave($AS))>>
 					//Since $he has no amputated limbs the <<= setup.prosthetics.interfaceP2.name>> will be put into storage.//
-				<<elseif $activeSlave.PLimb == 1>>
-					<<set $activeSlave.PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 5), $surgeryType = "PLimb interface3">>
+				<<elseif getSlave($AS).PLimb == 1>>
+					<<set getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 5), $surgeryType = "PLimb interface3">>
 					<<include "Surgery Degradation">>
 				<<else>>
-					<<set $activeSlave.PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20), $surgeryType = "PLimb interface2">>
+					<<set getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "PLimb interface2">>
 					<<include "Surgery Degradation">>
 				<</if>>
 			<<case "basicL" "sexL" "beautyL" "combatL" "cyberneticL">>
-				<<if $activeSlave.fuckdoll != 0>>
+				<<if getSlave($AS).fuckdoll != 0>>
 					//Since a Fuckdoll can't use prosthetic limbs the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
-				<<elseif hasAllNaturalLimbs($activeSlave)>>
+				<<elseif hasAllNaturalLimbs(getSlave($AS))>>
 					//Since $he needs an amputated limb to attach prosthetics the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
-				<<elseif $activeSlave.PLimb == 0>>
+				<<elseif getSlave($AS).PLimb == 0>>
 					//Since $he must have a prosthetic interface installed to attach prosthetic limbs the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
 				<<else>>
 					<<if _p.id == "basicL">>
-						<<set _state = App.Desc.limbChange().currentLimbs($activeSlave), _change = upgradeLimbs($activeSlave, 2)>>
+						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction($activeSlave, _state)>>
+							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
 						<<else>>
 							//Since $he already has more advanced prosthetic limbs attached the <<= setup.prosthetics.basicL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "sexL">>
-						<<set _state = App.Desc.limbChange().currentLimbs($activeSlave), _change = upgradeLimbs($activeSlave, 3)>>
+						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 3)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction($activeSlave, _state)>>
+							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
 						<<else>>
 							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.sexL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "beautyL">>
-						<<set _state = App.Desc.limbChange().currentLimbs($activeSlave), _change = upgradeLimbs($activeSlave, 4)>>
+						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 4)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction($activeSlave, _state)>>
+							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
 						<<else>>
 							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.beautyL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "combatL">>
-						<<set _state = App.Desc.limbChange().currentLimbs($activeSlave), _change = upgradeLimbs($activeSlave, 5)>>
+						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 5)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction($activeSlave, _state)>>
+							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
 						<<else>>
 							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.combatL.name>> will be put into storage.//
 						<</if>>
 					<<else>>
-						<<if $activeSlave.PLimb == 2>>
-							<<set _state = App.Desc.limbChange().currentLimbs($activeSlave), _change = upgradeLimbs($activeSlave, 2)>>
+						<<if getSlave($AS).PLimb == 2>>
+							<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
 							<<if _change>>
-								<<= App.Desc.limbChange().reaction($activeSlave, _state)>>
+								<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
 							<</if>>
 						<<else>>
 							//Since $he must have <<= addA(setup.prosthetics.interfaceP2.name)>> installed to attach cybernetic limbs the <<= setup.prosthetics.cyberneticL.name>> will be put into storage.//
@@ -180,21 +178,21 @@ that are ready be sent down.
 					<</if>>
 				<</if>>
 			<<case "interfaceTail">>
-				<<set $activeSlave.PTail = 1, $activeSlave.tail = "none", $activeSlave.tailColor = "none", cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10), $surgeryType = "tailInterface">>
+				<<set getSlave($AS).PTail = 1, getSlave($AS).tail = "none", getSlave($AS).tailColor = "none", cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 10), $surgeryType = "tailInterface">>
 				<<include "Surgery Degradation">>
 			<<case "modT" "sexT" "combatT">>
-				<<if $activeSlave.PTail == 0>>
+				<<if getSlave($AS).PTail == 0>>
 					//Since $he must have <<= addA(setup.prosthetics.interfaceTail.name)>> installed to attach tails the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
-				<<elseif $activeSlave.tail != "none">>
+				<<elseif getSlave($AS).tail != "none">>
 					//Since $he currently has a tail attached the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
 				<<elseif _p.id == "modT">>
 					//Since installing <<= addA(setup.prosthetics.modT.name)>> is complicated it can't be automated.//
 					/*Reason: there are different designs player can choose from.*/
 				<<elseif _p.id == "combatT">>
-					<<set $prostheticsConfig = "attachTail", $activeSlave.tail = "combat", $activeSlave.tailColor = "jet black">>
+					<<set $prostheticsConfig = "attachTail", getSlave($AS).tail = "combat", getSlave($AS).tailColor = "jet black">>
 					<<include "Prosthetics Configuration">>
 				<<elseif _p.id == "sexT">>
-					<<set $prostheticsConfig = "attachTail", $activeSlave.tail = "sex", $activeSlave.tailColor = "pink">>
+					<<set $prostheticsConfig = "attachTail", getSlave($AS).tail = "sex", getSlave($AS).tailColor = "pink">>
 					<<include "Prosthetics Configuration">>
 				<</if>>
 			<<default>>
@@ -203,8 +201,6 @@ that are ready be sent down.
 			<br>
 		<</if>>
 	<</for>>
-	/* updates the interrogated slave */
-	<<set $slaves[_i] = $activeSlave>>
 <</for>> /* end of the $slaves loop */
 
 /* This needs to be down here to over-ride any Surgery Degradation calls */
diff --git a/src/uncategorized/newSlaveIntro.tw b/src/uncategorized/newSlaveIntro.tw
index ff15013fe6b41d98de577a3eecbd127d2c80d920..5606b5a06c9f92b81f93fa257744627a67832137 100644
--- a/src/uncategorized/newSlaveIntro.tw
+++ b/src/uncategorized/newSlaveIntro.tw
@@ -7,2413 +7,4 @@
 <<if $nextLink != "AS Dump" && passage() != "Bulk Slave Intro">>
 	<<set $returnTo = $nextLink, $nextLink = "AS Dump">>
 <</if>>
-
-<<set $desc = SlaveTitle($activeSlave)>>
-<<run Enunciate($activeSlave)>>
-<<setPlayerPronouns>>
-<<setLocalPronouns $activeSlave>>
-
-/* use the secondary location if the primary mark target won't work for this slave */
-<<set _brandTarget = $brandTarget.primary, _scarTarget = $scarTarget.primary>>
-<<if $activeSlave.dick == 0>>
-	<<if $brandTarget.primary == "penis">>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if $scarTarget.primary == "penis">>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if $activeSlave.balls == 0 && $activeSlave.scrotum == 0>>
-	<<if ["left testicle", "right testicle"].includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ["left testicle", "right testicle"].includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if $activeSlave.earShape == "none">>
-	<<if ["left ear", "right ear"].includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ["left ear", "right ear"].includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if getLeftArmID($activeSlave) !== 1>>
-	<<if ([
-		"left upper arm", "left lower arm", "left wrist", "left hand"
-		]).includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ([
-		"left upper arm", "left lower arm", "left wrist", "left hand"
-		]).includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if getRightArmID($activeSlave) !== 1>>
-	<<if ([
-		"right upper arm", "right lower arm", "right wrist", "right hand"
-		]).includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ([
-		"right upper arm", "right lower arm", "right wrist", "right hand"
-		]).includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if getLeftLegID($activeSlave) !== 1>>
-	<<if ([
-		"left thigh", "left calve", "left ankle", "left foot"
-		]).includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ([
-		"left thigh", "left calve", "left ankle", "left foot"
-		]).includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-<<if getRightLegID($activeSlave) !== 1>>
-	<<if ([
-		"right thigh", "right calve", "right ankle", "right foot"
-		]).includes($brandTarget.primary)>>
-		<<set _brandTarget = $brandTarget.secondary>>
-	<</if>>
-	<<if ([
-		"right thigh", "right calve", "right ankle", "right foot"
-		]).includes($scarTarget.primary)>>
-		<<set _scarTarget = $scarTarget.secondary>>
-	<</if>>
-<</if>>
-
-<p>
-The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlave)>></span> <<if !hasAnyLegs($activeSlave)>><<if isAmputee($activeSlave)>>lies<<else>>rests<</if>> helplessly on your desk,<<elseif !canStand($activeSlave)>>kneels before your desk,<<else>>stands before your desk,<</if>>
-<<if $activeSlave.devotion < -50>>
-	<<if canSee($activeSlave)>>looking you in the eyes<<else>>directly facing you<</if>> with defiance.
-<<elseif $activeSlave.devotion < -20>>
-	<<if canSee($activeSlave)>>looking at<<else>>facing<</if>> you sullenly.
-<<elseif $activeSlave.trust <= 50>>
-	staring <<if canSee($activeSlave)>>at the ground<<else>>blankly downwards<</if>> in fear.
-<<elseif $activeSlave.devotion <= 50>>
-	waiting meekly for orders.
-<<elseif canSmell($activeSlave) && $activeSlave.career == "a breeding bull" && canGetPregnant($PC)>>
-	sniffing the air and <<if canSee($activeSlave)>>staring<<else>>pointing $his nose<</if>> intently at your crotch.
-<<elseif $activeSlave.origin == "$He submitted to enslavement to escape the hard life of an old world whore.">>
-	wondering what one of your working <<= $girl>>s can expect.
-<<elseif ($activeSlave.origin == "$He submitted to enslavement to get access to modern prenatal care.") || ($activeSlave.origin == "$He sold $himself into slavery out of fear that life on the streets was endangering $his pregnancy.")>>
-	hoping you'll give $him modern medicine right away.
-<<elseif $activeSlave.origin == "$He submitted to enslavement as $his only way to obtain surgery to transform $him into a woman.">>
-	hoping you'll schedule $his reassignment surgery immediately.
-<<elseif $activeSlave.origin == "$He submitted to enslavement out of a misguided desire to join a sexually libertine society.">>
-	hoping $he's made the right decision, and fantasizing a little, to go by $his half-hard dick.
-<<elseif $activeSlave.origin == "$He submitted to enslavement for a better chance at survival than $he had as a migrant.">>
-	wondering what life as a sex slave will be like.
-<<elseif $activeSlave.origin == "$He came to you to escape being sold to a cruel master after $his producer informed $him of $his debt.">>
-	wondering if $he will get to continue to perform for your people even as a slave.
-<<else>>
-	looking shyly at you and blushing.
-<</if>>
-
-<<if $gingering != 0 && $beforeGingering != 0 && $beforeGingering.ID == $activeSlave.ID>> /* extra checks to ensure gingering state is not left over from a different slave that was inspected but not purchased */
-	<<set _seed = "sale">>
-	<<if $gingeringDetected == 1>>
-		<<if $gingeringDetection == "slaver">>
-			<<set _seed = "sale, as your slaving experience revealed">>
-		<<elseif $gingeringDetection == "mercenary">>
-			<<set _seed = "sale, as the seller admitted in the face of your intimidating reputation">>
-		<<elseif $gingeringDetection == "force">>
-			<<set _seed = "sale, as the seller admitted in the face of your deadly reputation">>
-		<<else>>
-			<<set _seed = "sale, as you suspected">>
-		<</if>>
-	<</if>>
-
-	<<if $gingering == "antidepressant">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given antidepressants to make $him seem less fearful for _seed. $He is much less trusting than $he appeared in the market.
-	<<elseif $gingering == "depressant">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given a depressant to make $him seem less hateful for _seed. $He is much less obedient than $he appeared in the market.
-	<<elseif $gingering == "stimulant">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given a stimulant to make $him seem healthier for _seed. $He is much less vital than $he appeared in the market.
-	<<elseif $gingering == "vasoconstrictor">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given a vasoconstrictor to make $his cock seem more feminine for _seed. It's larger and more apt to become hard than it appeared in the market.
-	<<elseif $gingering == "vasodilator">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given a vasodilator to give $him an excessive erection for _seed. $His dick is somewhat smaller than it appeared in the market, and $he's not really hard all the time.
-	<<elseif $gingering == "aphrodisiac">>
-		$His intake toxicology reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> given aphrodisiacs to make $him horny and attracted to everyone for _seed. $His true sex drive and sexuality remain to be discovered.
-	<<else>>
-		A close inspection of $his anus reveals that $he was<<if $gingeringDetected == 1>> indeed<</if>> doctored with an irritant to make $him present $his butt when shown for _seed. $He is not an actual anal sex enthusiast.
-	<</if>>
-<</if>>
-
-<<run removeGingering()>> /* retrieve original $activeSlave without gingering effects */
-
-
-<<if $seeRace == 1>>
-	<<if $activeSlave.override_Race != 1>>
-		<<set $activeSlave.origRace = $activeSlave.race>>
-	<</if>>
-	<<if $activeSlave.race != $activeSlave.origRace>>
-		A blood test reveals that $he was originally $activeSlave.origRace, not $activeSlave.race<<if $PC.skill.medicine >= 50 || $PC.skill.slaving >= 50>>, just as you suspected<</if>>.
-	<</if>>
-<</if>>
-<<if $activeSlave.skin != $activeSlave.origSkin>>
-	<<if ($activeSlave.skin != "sun tanned") && ($activeSlave.skin != "spray tanned")>>
-		An epidermis scan reveals that $his skin was originally $activeSlave.origSkin, not $activeSlave.skin<<if $PC.skill.medicine >= 75 || $PC.skill.slaving >= 75>>, just as you suspected<</if>>.
-	<</if>>
-<</if>>
-
-
-<<set $activeSlave.oldDevotion = $activeSlave.devotion>>
-<<set $activeSlave.oldTrust = $activeSlave.trust>>
-
-<<if $activeSlave.vagina < 0>>
-	<<set $activeSlave.skill.vaginal = 0>>
-<</if>>
-
-<<if $activeSlave.nipplesPiercing > 0>>
-	<<if $activeSlave.nipples == "partially inverted">>
-		<<set $activeSlave.nipples = "cute">>
-	<<elseif $activeSlave.nipples == "inverted">>
-	<<set $activeSlave.nipples = "puffy">>
-	<</if>>
-<</if>>
-
-<<if ($activeSlave.vagina > -1) && ($activeSlave.dick == 0)>>
-	<<if ($PC.dick == 0) && ($PC.boobs >= 300)>>
-		$He looks to you and sees a fellow woman, and is @@.mediumaquamarine;a little less afraid@@ that you will rape and abuse $him.
-		<<set $activeSlave.trust += 4>>
-	<</if>>
-<<elseif $activeSlave.career == "a Futanari Sister">>
-	<<if ($PC.dick != 0) && ($PC.boobs >= 300) && ($PC.vagina != -1)>>
-		<<if $TFS.schoolPresent == 1>>
-			$He's heard of you, of course, as both a futanari yourself and a generous patron of the Sisters. $He feels @@.mediumaquamarine;very lucky@@ that $he's going to be your slave, not to mention @@.hotpink;almost desperate@@ to have sex with you.
-			<<set $activeSlave.devotion += 10>>
-			<<set $activeSlave.trust += 10>>
-		<<else>>
-			<<if canSee($activeSlave)>>Seeing<<else>>Learning<</if>> that you're a fellow futanari, $he's @@.mediumaquamarine;relieved@@ that $he's going to be your slave, not to mention @@.hotpink;quite eager@@ to have sex with you.
-			<<set $activeSlave.devotion += 5>>
-			<<set $activeSlave.trust += 5>>
-		<</if>>
-	<</if>>
-<<elseif ($activeSlave.boobs > 400) && ($activeSlave.dick > 0)>>
-	<<if ($PC.dick != 0) && (($PC.boobs >= 400) || ($PC.vagina != -1))>>
-		$He looks to you and sees a fellow intersex person, and is @@.mediumaquamarine;rather hopeful@@ that $he will find you a sympathetic owner.
-		<<set $activeSlave.trust += 4>>
-	<</if>>
-<</if>>
-
-<<if (($activeSlave.attrXX > 50) || ($activeSlave.energy > 95)) && ($activeSlave.behavioralFlaw != "hates women") && ($activeSlave.trust >= -20)>>
-	<<if ($PC.boobs >= 400)>>
-		$He seems to think you're pretty, and is more willing to @@.hotpink;try for your approval@@ than $he would otherwise be. $He glances at your rack when $he thinks you're not looking.
-		<<set $activeSlave.devotion += 4>>
-	<</if>>
-<</if>>
-
-<<if ($activeSlave.behavioralFlaw == "hates women") && ($activeSlave.devotion <= 50)>>
-	<<if ($PC.dick == 0) && ($PC.boobs >= 300)>>
-		$He obviously does not find you immediately attractive, and is @@.mediumorchid;less eager to conciliate you@@ than $he would otherwise be.
-		<<set $activeSlave.devotion -= 5>>
-	<</if>>
-<</if>>
-
-<<if (($activeSlave.attrXY > 50) || ($activeSlave.energy > 95)) && ($activeSlave.behavioralFlaw != "hates men") && ($activeSlave.trust >= -20)>>
-	<<if ($PC.dick == 0) && ($PC.boobs < 300)>>
-		$He seems to think you're handsome, and is more willing to @@.hotpink;try for your approval@@ than $he would otherwise be. $He glances at your crotch when $he thinks you're not looking.
-		<<set $activeSlave.devotion += 4>>
-	<</if>>
-<</if>>
-
-<<if ($activeSlave.devotion >= -20)>>
-	<<if ($activeSlave.actualAge > 35)>>
-		<<if ($PC.visualAge < 35)>>
-			<<if (($activeSlave.behavioralFlaw != "hates men") && ($PC.title == 1)) || (($activeSlave.behavioralFlaw != "hates women") && ($PC.title == 0))>>
-				$He tries to conceal $his surprise at your age, and keeps glancing at $his own naked body and then back at you, obviously wondering at such a @@.hotpink;gorgeous young _womanP's@@ interest in an old slave like $him.
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<</if>>
-	<<elseif ($activeSlave.actualAge < 25)>>
-		<<if ($PC.visualAge >= 50)>>
-			<<if (($activeSlave.behavioralFlaw != "hates men") && ($PC.title == 1)) || (($activeSlave.behavioralFlaw != "hates women") && ($PC.title == 0))>>
-				$He keeps stealing glances at your face, probably wrestling with $his feelings about your age. Eventually $he seems to relax and accept it, with a certain @@.hotpink;obvious thrill@@ at $his own willingness towards an older <<if $PC.title == 1>>man<<else>>lady<</if>>.
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $PC.pregKnown == 1>>
-	<<if $PC.career == "escort">>
-		<<if $PC.belly >= 1000>>
-			<<if $activeSlave.fetish == "pregnancy">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$He keeps stealing glances at your <<if $PC.belly >= 120000>>massive<<elseif $PC.belly >= 60000>>giant<<elseif $PC.belly >= 15000>>huge<<elseif $PC.belly >= 5000>>big<</if>> baby bump.<<if canAchieveErection($activeSlave)>> $His dick rapidly hardening.<</if>> You teasingly ask if $he'd like to feel it sometime, eliciting a delighted squeal from the $girl. @@.green;$He's a pregnancy fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					$He keeps stealing glances at your <<if $PC.belly >= 120000>>massive<<elseif $PC.belly >= 60000>>giant<<elseif $PC.belly >= 15000>>huge<<elseif $PC.belly >= 5000>>big<</if>> baby bump.<<if canAchieveErection($activeSlave)>> $His dick rapidly hardening.<</if>> As you inspect $him, you take care to gently brush your pregnancy across $him as you move. $He is practically bursting with lust by the end and @@.hotpink;eager to please you@@ so $he can be close to that belly.
-					<<set $activeSlave.devotion += 5>>
-				<</if>>
-			<<else>>
-				Your pregnancy is obvious to $him, which means two things: you won't be as capable of disciplining $him, leading $him to be @@.mediumorchid;more willing to disobey,@@ and you going to be demanding and needy, possibly even @@.gold;using force to get $him to do what you want.@@
-				<<set $activeSlave.trust -= 10>>
-				<<set $activeSlave.devotion -= 10>>
-			<</if>>
-		<</if>>
-	<<else>>
-		<<if $PC.belly >= 1500>>
-			<<if $activeSlave.fetish == "pregnancy">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$He keeps stealing glances at your <<if $PC.belly >= 120000>>massive<<elseif $PC.belly >= 60000>>giant<<elseif $PC.belly >= 15000>>huge<<elseif $PC.belly >= 5000>>big<</if>> baby bump.<<if canAchieveErection($activeSlave)>> $His dick rapidly hardening.<</if>> You teasingly ask if $he'd like to feel it sometime, eliciting a delighted squeal from the $girl. @@.green;$He's an impregnation fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					$He keeps stealing glances at your <<if $PC.belly >= 120000>>massive<<elseif $PC.belly >= 60000>>giant<<elseif $PC.belly >= 15000>>huge<<elseif $PC.belly >= 5000>>big<</if>> baby bump.<<if canAchieveErection($activeSlave)>> $His dick rapidly hardening.<</if>> As you inspect $him, you take care to gently brush your pregnancy across $him as you move. $He is practically bursting with lust by the end and @@.hotpink;eager to please you@@ so $he can be close to that belly.
-					<<set $activeSlave.devotion += 5>>
-				<</if>>
-			<<else>>
-				Your pregnancy is obvious to $him, which means two things: you won't be as capable of disciplining $him, leading $him to be @@.mediumorchid;more willing to disobey,@@ and you going to be demanding and needy, possibly even @@.gold;using force to get $him to do what you want.@@
-				<<set $activeSlave.trust -= 10>>
-				<<set $activeSlave.devotion -= 10>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $PC.boobs >= 1400>>
-	<<if $activeSlave.fetish == "boobs">>
-		<<if $activeSlave.fetishKnown == 0>>
-			$He keeps stealing glances at your huge rack<<if canAchieveErection($activeSlave)>>, $his dick rapidly hardening<</if>>. You stretch<<if canSee($activeSlave)>>, giving $him a fantastic view of your breasts,<</if>> before circling around behind $him and cupping $his chest. Between your tits against $his back, and how teasingly big you are pretending $his breasts will be, $he can't contain $himself; $he cums from overstimulation, completely pinning $him @@.green;as a breast fetishist!@@
-			<<set $activeSlave.fetishKnown = 1>>
-		<<else>>
-			$He keeps stealing glances at your huge rack<<if canAchieveErection($activeSlave)>>, $his dick rapidly hardening<</if>>. As you inspect $him, you take care to keep "accidentally" bumping $him with your tits, before "tripping" and landing with $his face buried in your chest. $He is practically bursting with lust by the end and @@.hotpink;eager to please you@@ so $he can be close to those boobs.
-			<<set $activeSlave.devotion += 5>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $enduringTrust > 20>>
-	<<if $activeSlave.trust >= -20>>
-		$He notices that most of the slaves $he sees around your penthouse seem to trust you, and hopes that $he can @@.mediumaquamarine;trust you,@@ too.
-		<<set $activeSlave.trust += 1>>
-	<</if>>
-<<else>>
-	<<if $activeSlave.trust < 50>>
-		$He notices that most of the slaves $he sees around your penthouse seem to fear you, and starts to @@.gold;fear you,@@ too.
-		<<set $activeSlave.trust -= 2>>
-	<</if>>
-<</if>>
-
-<<if $enduringDevotion > 20>>
-	<<if $activeSlave.devotion >= -20>>
-		$He sees that most of the slaves $he sees around your penthouse like you, and hopes that $he can @@.hotpink;like you@@ a little more $himself.
-		<<set $activeSlave.devotion += 1>>
-	<</if>>
-<<else>>
-	<<if $activeSlave.devotion <= 50>>
-		$He sees that most of the slaves $he sees around your penthouse dislike you, and starts to @@.mediumorchid;dislike you@@ a little more $himself.
-		<<set $activeSlave.devotion -= 2>>
-	<</if>>
-<</if>>
-
-<<if ($PC.career == "escort")>>
-	$He recognizes you as a whore, leading $him to @@.mediumaquamarine;fear you less;@@ however, $his views of you also leave $him less willing to @@.mediumorchid;listen to you.@@
-	<<set $activeSlave.devotion -= 10>>
-	<<set $activeSlave.trust += 10>>
-<<elseif ($PC.career == "servant")>>
-	<<if canSee($activeSlave)>>Seeing<<else>>Having<</if>> you in your <<if $PC.title == 0>>maid's dress<<else>>maid's outfit<</if>> @@.mediumaquamarine;calms $his fears;@@ however, $he is unwilling to @@.mediumorchid;listen to a servant.@@
-	<<set $activeSlave.devotion -= 3>>
-	<<set $activeSlave.trust += 10>>
-<<elseif ($PC.career == "gang")>>
-	$He recognizes you from various crime reports, @@.gold;filling $him with fear@@ over what life under you will be like.
-	<<set $activeSlave.trust -= 10>>
-<</if>>
-
-<<if ($PC.rumor == "force")>>
-	<<if ($activeSlave.devotion <= 20)>>
-		<<if ($activeSlave.trust > 0)>>
-			$He seems to have picked up rumors about your ruthlessness, and is @@.gold;horrified into obedience.@@
-			<<set $activeSlave.trust -= 25>>
-		<<elseif ($activeSlave.trust > -10)>>
-			$He seems to have picked up rumors about your ruthlessness, and is @@.gold;terrified into obedience.@@
-			<<set $activeSlave.trust -= 20>>
-		<<elseif ($activeSlave.trust >= -20)>>
-			$He seems to have picked up rumors about your ruthlessness, and is @@.gold;frightened into obedience.@@
-			<<set $activeSlave.trust -= 15>>
-		<</if>>
-	<</if>>
-<<elseif $PC.rumor == "diligence">>
-	$He seems to have picked up rumors about your probity, and is @@.mediumaquamarine;less afraid of you@@ and more @@.hotpink;willing to like you.@@
-	<<set $activeSlave.devotion += 5>>
-	<<set $activeSlave.trust += 5>>
-<</if>>
-
-<<if ($activeSlave.accent >= 3)>>
-	<<if ($activeSlave.trust > 20)>>
-		During the enslavement process so far, it became obvious to $him that $he can barely understand enough $language to understand orders. $He's @@.gold;frightened@@ by the sudden danger that $he won't be able to do what $he's told, even if $he wants to.
-		<<set $activeSlave.trust -= 5>>
-	<<elseif ($activeSlave.devotion <= 20)>>
-		<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant > 50>>
-			$He can barely understand the $language language, but $he's smart enough to understand everything that's happening to $him. Even so, @@.mediumaquamarine;some fear is deferred,@@ since the harsh realities of being a sex slave may not be clear to $him yet.
-			<<set $activeSlave.trust += 10>>
-		<<elseif $activeSlave.intelligence+$activeSlave.intelligenceImplant >= -15>>
-			$He can barely understand enough $language to understand what's happening to $him. This incapacity @@.mediumaquamarine;defers a great deal of fear,@@ since $his exact future hasn't become clear to $him yet.
-			<<set $activeSlave.trust += 10>>
-		<<else>>
-			$He can barely understand $language at all, and scarcely understands what's happening to $him. This leaves $him @@.mediumaquamarine;unaware of how afraid $he should be,@@ since $he's too stupid to figure it out without being told.
-			<<set $activeSlave.trust += 25>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSSupremacist >= 50>>
-	<<if $activeSlave.race == $arcologies[0].FSSupremacistRace>>
-		$He realizes that $he's a member of the favored race in your arcology, and @@.mediumaquamarine;hopes@@ this will earn $him good treatment.
-		<<set $activeSlave.trust += 2>>
-	<<else>>
-		$He realizes that $he's not a member of the favored race in your arcology, and @@.gold;fears@@ this will earn $him extra abuse.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSSubjugationist >= 50>>
-	<<if $activeSlave.race != $arcologies[0].FSSubjugationistRace>>
-		$He realizes that $he's not one of the people considered subhuman here, and @@.mediumaquamarine;hopes@@ this will allow $him to avoid some abuse.
-		<<set $activeSlave.trust += 2>>
-	<<else>>
-		$He realizes that $he's one of the people considered subhuman here, and @@.gold;fears@@ $he will be unable to avoid degradation.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSIntellectualDependency >= 50>>
-	<<if $activeSlave.pregKnown == 1>>
-		$He realizes that your arcology values bearing children and @@.mediumaquamarine;hopes@@ $he'll be treated gently for being pregnant.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSSlaveProfessionalism >= 50>>
-	<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant >= 75>>
-		$He realizes that your arcology values intelligence and @@.mediumaquamarine;expects@@ $his intellect will prove $his worth.
-		<<set $activeSlave.trust += 2>>
-	<<elseif $activeSlave.intelligence+$activeSlave.intelligenceImplant < 10>>
-		$He realizes that your arcology values intelligence and @@.gold;worries@@ $he isn't smart enough to keep up with society's expectations.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSRepopulationFocus >= 50>>
-	<<if $activeSlave.pregKnown == 1>>
-		$He realizes that your arcology values bearing children and @@.mediumaquamarine;hopes@@ $he'll be treated gently for being pregnant.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSRestart >= 50>>
-	<<if $activeSlave.pregKnown == 0>>
-		$He recognizes $his place in society and @@.mediumaquamarine;is glad@@ $he isn't pregnant.
-		<<set $activeSlave.trust++>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSGenderRadicalist >= 50>>
-	<<if $activeSlave.dick > 0>>
-		<<if $activeSlave.anus > 0>>
-			$He realizes that your arcology accepts girls with dicks who take it up the ass, and since $he qualifies, $he @@.mediumaquamarine;hopes@@ to be well treated here.
-			<<set $activeSlave.trust += 2>>
-		<<else>>
-			$He realizes that girls with dicks take it up the butt here, and @@.gold;fears@@ for $his virgin anus.
-			<<set $activeSlave.trust -= 4>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSGenderFundamentalist >= 50>>
-	<<if $activeSlave.dick == 0>>
-		$He realizes that $he has a chance of conforming to gender roles in your arcology, and @@.mediumaquamarine;hopes@@ to avoid abuse this way.
-		<<set $activeSlave.trust += 2>>
-	<<else>>
-		$He realizes that your arcology's culture looks down on <<= $girl>>s like $him, and @@.gold;fears@@ $he'll be abused as a result.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSPaternalist >= 50>>
-	$He realizes that your arcology's citizens look after their slaves, you more than anyone, and @@.mediumaquamarine;hopes@@ $he'll do well here.
-	<<set $activeSlave.trust += 2>>
-<</if>>
-
-<<if $arcologies[0].FSDegradationist >= 50>>
-	$He realizes that for $him, your arcology is likely to be a hell unending, and is @@.gold;terrified.@@
-	<<set $activeSlave.trust -= 5>>
-<</if>>
-
-<<if $arcologies[0].FSBodyPurist >= 50>>
-	<<if $activeSlave.boobsImplant > 0 || $activeSlave.buttImplant > 0 || $activeSlave.waist < -95 || $activeSlave.lipsImplant > 0 || $activeSlave.faceImplant >= 30>>
-		$He realizes that your arcology disapproves of body modifications like $hers, and @@.gold;fears@@ that $he will be subjected to further surgery to purify $him.
-		<<set $activeSlave.trust -= 4>>
-	<<else>>
-		$He realizes that your arcology approves of natural bodies like $hers, and @@.mediumaquamarine;trusts@@ that $he will be allowed to keep it that way.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSTransformationFetishist >= 50>>
-	<<if $activeSlave.boobsImplant > 0 || $activeSlave.buttImplant > 0 || $activeSlave.waist < -95 || $activeSlave.lipsImplant > 0 || $activeSlave.faceImplant >= 30>>
-		$He realizes that your arcology approves of body modifications like $hers, and @@.mediumaquamarine;trusts@@ that $he will be valued for them.
-		<<set $activeSlave.trust += 2>>
-	<<else>>
-		$He realizes that your arcology disapproves of natural bodies like $hers, and @@.gold;fears@@ that $he will soon be a plastic surgeon's plaything.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSMaturityPreferentialist >= 50>>
-	<<if ($activeSlave.actualAge > 30)>>
-		$He realizes that your arcology appreciates older <<= $girl>>s, and @@.mediumaquamarine;hopes@@ it'll be nicer here.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSYouthPreferentialist >= 50>>
-	<<if ($activeSlave.actualAge > 30)>>
-		$He realizes that your arcology has little use for older <<= $girl>>s, and @@.gold;fears@@ $he'll be abused here.
-		<<set $activeSlave.trust -= 4>>
-	<<elseif ($activeSlave.actualAge < 18)>>
-		/% begin is a child block %/
-		/% Set _understands to 0 or 1 to reflect whether $he understands. %/
-		<<set _understands = 0>>
-		<<if ($activeSlave.origin == "$He was raised in a radical slave school that treated $him from a very young age, up to the point that $he never experienced male puberty.") || ($activeSlave.origin == "$He was raised in a radical slave school that treated $him with drugs and surgery from a very young age.") || ($activeSlave.origin == "$He was brought up in a radical slave school to match $his twin.")>>
-			<<set _understands = 1>>
-		<<elseif isSexuallyPure($activeSlave)>>
-			<<if $activeSlave.intelligence > random(0,50)>>
-				<<set _understands = 1>>
-			<</if>>
-		<<else>>
-			<<if $activeSlave.intelligence > random(-50,50)>>
-				<<set _understands = 1>>
-			<</if>>
-		<</if>>
-		<<if _understands < 1>>
-			$He has heard that you like little <<= $girl>>s and is @@.mediumaquamarine;reassured@@ because $he misunderstands what this means.
-			<<set $activeSlave.trust += 2>>
-		<<elseif ($activeSlave.sexualQuirk == "perverted")>>
-			$He has heard that you like little <<= $girl>>s and @@.mediumaquamarine;hopes@@ that matching your taste means $he will be treated well.
-			<<set $activeSlave.trust += 2>>
-		<<elseif ($activeSlave.sexualQuirk == "size queen") && $PC.dick != 0>>
-			$He has heard that you like little <<= $girl>>s and @@.hotpink;bites $his lip@@ at the thought of how big your dick will be inside $him.
-			<<set $activeSlave.devotion += 2>>
-		<<elseif ($activeSlave.sexualFlaw == "repressed") || ($activeSlave.sexualFlaw == "shamefast") || ($activeSlave.behavioralFlaw == "devout")>>
-			$He has heard that you like little <<= $girl>>s and is @@.gold;terrified@@ because $he understands exactly what that means.
-			<<set $activeSlave.trust -= 5>>
-		<<elseif ($activeSlave.sexualFlaw == "hates oral") || ($activeSlave.sexualFlaw == "hates anal") || ($activeSlave.sexualFlaw == "hates penetration") || (isSexuallyPure($activeSlave) && (50 >= random(1, 100)))>>
-			$He has heard that you like little <<= $girl>>s and @@.gold;fears@@ what you might do to $him.
-			<<set $activeSlave.trust -= 4>>
-		<<else>>
-			$He has heard that you like little <<= $girl>>s and @@.mediumaquamarine;hopes@@ that matching your taste means $he will be treated well.
-			<<set $activeSlave.trust += 2>>
-		<</if>>
-		/% end is a child block %/
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSPetiteAdmiration >= 50>>
-	<<if heightPass($activeSlave)>>
-		$He realizes that your arcology favors short slaves, and @@.mediumaquamarine;hopes@@ life will be good here.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSStatuesqueGlorification >= 50>>
-	<<if heightPass($activeSlave)>>
-		$He realizes that your arcology favors the tall, and @@.mediumaquamarine;hopes@@ $he'll retain some dignity.
-		<<set $activeSlave.trust += 2>>
-	<<else>>
-		$He realizes that your arcology favors the tall, and @@.gold;anticipates@@ $his lacking height to bring $him nothing but torment.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSSlimnessEnthusiast >= 50>>
-	<<if ($activeSlave.boobs < 500) && ($activeSlave.butt < 3) && ($activeSlave.weight <= 10) && ($activeSlave.muscles <= 30)>>
-		$He realizes that $he has a fashionable body for your arcology, and @@.mediumaquamarine;hopes@@ it'll earn $him some kindness.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSAssetExpansionist >= 50>>
-	<<if ($activeSlave.butt > 4) && ($activeSlave.boobs > 800)>>
-		$He realizes that $he has a fashionable body for your arcology, and @@.mediumaquamarine;hopes@@ it'll earn $him some kindness.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSPastoralist >= 50>>
-	<<if ($activeSlave.pregKnown == 1) || ($activeSlave.lactation > 0)>>
-		$He realizes that your arcology values slaves with motherly bodies, and @@.mediumaquamarine;hopes@@ $he won't be a low value slave for having one.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSPhysicalIdealist >= 50>>
-	<<if ($activeSlave.muscles > 5)>>
-		$He realizes that muscles are respected around here, and though $he isn't truly swole, $he's partway there; $he @@.mediumaquamarine;hopes@@ $he'll be kept healthy and strong.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSHedonisticDecadence >= 50>>
-	<<if $activeSlave.behavioralFlaw == "gluttonous">>
-		$He realizes that fat slaves are preferred around here, and as a glutton, can't wait to @@.mediumaquamarine;eat $himself sick.@@
-		<<set $activeSlave.trust += 2>>
-	<<elseif ($activeSlave.weight > 130) && $activeSlave.behavioralFlaw != "anorexic">>
-		$He realizes that fat slaves are preferred around here, and since $he is quite rotund already, $he @@.mediumaquamarine;hopes@@ $he'll fit right in. That and if the cafeteria is open 24/7.
-		<<set $activeSlave.trust += 2>>
-	<<elseif ($activeSlave.weight > 10) && $activeSlave.behavioralFlaw != "anorexic">>
-		$He realizes that chubby slaves are preferred around here, and though $he isn't truly fat, $he's partway there; $he @@.mediumaquamarine;hopes@@ $he'll be kept well fed and happy.
-		<<set $activeSlave.trust += 2>>
-	<<elseif $activeSlave.behavioralFlaw == "anorexic">>
-		$He realizes that fat slaves are preferred around here, and that means $he'll @@.gold;be made fat too.@@
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSChattelReligionist >= 50>>
-	<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant < -50>>
-		$His dimwitted mind naturally takes to being told $his role as a slave is righteous, and $he naïvely @@.mediumaquamarine;hopes@@ your arcology's religion will protect $him from harm.
-		<<set $activeSlave.trust += 2>>
-	<<elseif $activeSlave.intelligence+$activeSlave.intelligenceImplant > 50>>
-		$His intelligent mind @@.gold;fears@@ the consequences of living in an arcology in which slavery has taken on religious significance.
-		<<set $activeSlave.trust -= 4>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSRomanRevivalist >= 50>>
-	<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant > 50>>
-		Though $he knows it's not a truly authentic Roman restoration, $his intelligent mind grasps the potential benefits of Roman mores for slaves, and $he @@.mediumaquamarine;hopes@@ life in your arcology will be managed with virtue.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<<elseif $arcologies[0].FSAztecRevivalist >= 50>>
-	<<if $activeSlave.intelligenceImplant >= 15>>
-		Though $he knows it's not a truly authentic ancient Aztec restoration, $his educated mind grasps the potential benefits of ancient Aztec mores for slaves, and $he @@.mediumaquamarine;hopes@@ your arcology will make respectful use of $his devotion.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<<elseif $arcologies[0].FSEgyptianRevivalist >= 50>>
-	<<if $activeSlave.intelligenceImplant >= 15>>
-		Though $he knows it's not a truly authentic ancient Egyptian restoration, $his educated mind grasps the potential benefits of ancient Egyptian mores for slaves, and $he @@.mediumaquamarine;hopes@@ your arcology will make good and respectful use of $his learning.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<<elseif $arcologies[0].FSEdoRevivalist >= 50>>
-	<<if $activeSlave.intelligenceImplant >= 15>>
-		Though $he knows it's not a truly authentic feudal Japanese restoration, $his educated mind grasps the potential benefits of traditional Japanese mores for slaves, and $he @@.mediumaquamarine;hopes@@ your arcology will treat those who behave well with a modicum of honor.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<<elseif $arcologies[0].FSArabianRevivalist >= 50>>
-	<<if $activeSlave.intelligenceImplant >= 15>>
-		Though $he knows it's not a truly authentic restoration of the old Caliphate, $his educated mind grasps the potential benefits of old Arabian law and culture for slaves, and $he @@.mediumaquamarine;hopes@@ your arcology has absorbed enough of the old wisdom to respect slaves.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<<elseif $arcologies[0].FSChineseRevivalist >= 50>>
-	<<if $activeSlave.intelligenceImplant >= 15>>
-		Though $he knows it's not a truly authentic ancient Chinese restoration, $his educated mind grasps the potential benefits of ancient Confucian philosophy for slaves, and $he @@.mediumaquamarine;hopes@@ your arcology at least maintains some pretense of order and conservatism.
-		<<set $activeSlave.trust += 2>>
-	<</if>>
-<</if>>
-
-<<if $arcologies[0].FSPaternalistLaw == 1>>
-	$He <<if canSee($activeSlave)>>saw<<elseif canHear($activeSlave)>>overheard<<else>>passed through<</if>> a good deal of your arcology and its society on $his way to your penthouse, and was amazed by all the happy, healthy slaves. $He @@.mediumaquamarine;begins to trust@@ that $he'll be one of them, and @@.hotpink;anticipates meeting@@ the person who built this place.
-	<<set $activeSlave.trust += 4>>
-	<<set $activeSlave.devotion += 4>>
-<</if>>
-
-<<if $activeSlave.devotion < -50 && $activeSlave.rudeTitle != 1>>
-	<<if random(-100,0) >= $activeSlave.devotion>>
-		<<set $activeSlave.rudeTitle = 1>>
-		<<set $activeSlave.custom.title = setup.badNames.random()>>
-		<<set $activeSlave.custom.titleLisp = lispReplace($activeSlave.custom.title)>>
-	<</if>>
-<</if>>
-
-<<if $activeSlave.breedingMark == 1>>
-	$He knows $his duty in life and playfully splays $himself across your desk with $his legs spread wide. Unfortunately, $he must first be unwrapped before any breeding can begin, so for the time being, $he rights $himself and makes $his way to your crotch to show you just how much $he was worth the ¤.
-	<<set $activeSlave.counter.oral++, $oralTotal++>>
-
-<<else>>
-
-<<if $rulesAssistantAuto == 0>>
-	<br>
-	<span id="rules">
-	<<link "Apply default rules">>
-		<<replace "#rules">>
-			<<= DefaultRules($activeSlave)>>
-		<</replace>>
-	<</link>>
-	<<if $activeSlave.devotion <= 20>>
-		//Will not apply many cosmetic and surgical options since $he is not obedient//
-	<</if>>
-	</span>
-<</if>>
-
-<p id="introResult">
-//Now might be a good time to introduce $him to life in your stable of sex slaves//
-
-<<switch $activeSlave.origin>>
-<<case "$He submitted to enslavement to escape the hard life of an old world whore.">>
-	//and since $he wanted to leave the old world...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Brand $him on the _brandTarget to introduce $him to life as a slave whore">>
-		<<replace "#introResult">>
-			You tell $him you'll be marking $him as one of your working <<= $girl>>s. $He looks resigned as $he follows you to the body modification studio, and lets you strap $him down with $his _brandTarget bare. $He understands what's coming. You've got $him positioned just right<<if canDoAnal($activeSlave)>>, so your cock slides up $his experienced asshole easily<</if>>. You bring the brand in close so $he can feel the radiated heat, which breaks through even $his jaded exterior and makes $him tighten with fear. When you're close, you apply the brand<<if canDoAnal($activeSlave)>>, making the poor whore cinch $his sphincter down hard in agony, bringing you to climax<</if>>. $He knows you know how to @@.gold;apply pain,@@ now, and $he @@.mediumorchid;learns to dislike you@@ even as $his @@.red;wound@@ heals.
-		<</replace>>
-		<<set $activeSlave.brand[_brandTarget] = $brandDesign.primary>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<<run healthDamage($activeSlave, 10)>>
-		<<if canDoAnal($activeSlave)>>
-			<<= VCheck.Anal()>>
-		<</if>>
-	<</link>>
-	<br>
-<<case "$He submitted to enslavement to get access to modern prenatal care.">>
-	//and since $he's worried about $his child...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Manipulate $his fear for $his pregnancy">>
-		<<replace "#introResult">>
-			You place a curative injector on your desk, and describe its remarkable medical powers in detail, before mentioning its extreme cost. $His face rises at the first part and falls at the second. You tell $him that if $he's a perfect sex slave, $he'll get as much as $he needs, and that $he can start by sucking you off. $He grunts a little as $he hurries to get $his pregnant body down to $his knees, but
-			<<if $PC.dick > 0>>
-				works your cock
-			<<elseif $PC.vagina > 0>>
-				explores your vagina
-			<<else>>
-				licks you to orgasm
-			<</if>>
-			with almost desperate enthusiasm. You stroke $his hair comfortingly as $he does, and inject the healing dose into $his shoulder. $He murmurs $his @@.hotpink;gratitude@@ into your dick, but @@.gold;fears@@ for $his pregnancy.
-		<</replace>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust -= 10>>
-		<<set $activeSlave.counter.oral += 1>>
-		<<set $oralTotal += 1>>
-	<</link>>
-	<br>
-<<case "$He offered $himself as a slave to escape the horrors a blind $girl faces on the streets.">>
-	//and since $he loathes $his pregnancy...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Abort $his child">>
-		<<replace "#introResult">>
-			You loudly place an innocuous-looking drug injector on your desk, and let $him think about it for a long moment. Then, you declare exactly what made that sound: abortifacients. After a moment of comprehension, $his mood instantly improves. $He pledges to @@.hotpink;submit to you,@@ <<if $PC.dick != 0 && $PC.vagina != -1>>suck your cock, take it in $his pussy, take it up $his ass, eat you out<<elseif $PC.dick != 0>>suck your cock, take it in $his pussy, take it up $his ass<<else>>eat you out, worship you with $his pussy, serve you with $his ass<</if>>, anything, as long as you @@.mediumaquamarine;remove $his rape baby.@@ You observe that $he'll do all of those things, regardless of what you decide to do about $his pregnancy, but for now, you'll let $him terminate it; $he needs all the nutrients for $himself right now. $He thanks you through $his tears.
-		<</replace>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 3>>
-		<<run TerminatePregnancy($activeSlave)>>
-		<<set $activeSlave.counter.abortions++>>
-		<<set $abortionsTotal++>>
-	<</link>>
-	<br>
-<<case "$He submitted to enslavement for a better chance at survival than $he had as a migrant.">>
-	//and since $he's trying to do better than life as a migrant...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave)>>
-		<<link "Introduce $him to anal service">>
-			<<replace "#introResult">>
-				You carefully and patiently explain to the fearful anal virgin how skilled Free Cities slaves take dick up the butt. $He is allowed to go at $his own pace, hesitantly progressing from enemata to a single well-lubricated finger to a little plug. After a long while $he's beginning to get aroused, and you bring $him over to the couch to spoon. $He stiffens with fear but you take your time and just cuddle for a while before gently sodomizing $him. $He @@.hotpink;thanks@@ you for being a @@.mediumaquamarine;kind master,@@ and has @@.green;learned@@ the basics of taking it up the ass.
-			<</replace>>
-			<<set $activeSlave.devotion += 4>>
-			<<set $activeSlave.trust += 4>>
-			<<set $activeSlave.anus += 1>>
-			<<set $activeSlave.skill.anal += 10>>
-			<<= VCheck.Anal()>>
-		<</link>>
-
-		| <<link "Initiate $him with anal pain">>
-			<<replace "#introResult">>
-				You haul $him wordlessly into the bathroom. In a few minutes $he finds $himself standing obediently behind you, waiting for your next command with a clean colon and an uncomfortably large buttplug stretching $his virgin ass. Once it's been in long enough to prevent any damage, you push $his fearful form over the desk and pull it free. $He squeals with more embarrassment than pain at the feeling of $his first sodomy, but before long you're pounding $him hard enough that all $he can do is moan and desperately wonder when you'll be done with $his poor little butt. You keep $him around all day; by night, $his asshole is well broken in and @@.mediumorchid;so is $his spirit.@@ $He @@.gold;fears@@ you now, knowing this is only the beginning.
-			<</replace>>
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 10>>
-			<<set $activeSlave.anus += 1>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He begged to be enslaved to avoid starvation.">>
-	//and since $he enslaved $himself for a bite to eat...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Allow $him to stuff $himself">>
-		<<replace "#introResult">>
-			You point $him toward the nearest slave food dispenser and tell $him to drink as much as $he likes. $He @@.hotpink;eagerly complies,@@ quickly bloating $his long-deprived belly; between the psychoactive effects of slave food, the sudden end of $his long fast, and the look of @@.mediumaquamarine;slightly spaced-out contentment@@ on $his face as $he rubs $his sated belly, you have a feeling that you're witnessing the beginning of an @@.red;overeating habit@@.
-		<</replace>>
-		<<set $activeSlave.devotion += 10>>
-		<<set $activeSlave.trust += 10>>
-		<<if $activeSlave.behavioralQuirk == "fitness">>
-			<<set $activeSlave.behavioralQuirk = "none">>
-		<</if>>
-		<<set $activeSlave.behavioralFlaw = "gluttonous">>
-		<<set $activeSlave.inflation = 1, $activeSlave.inflationType = "food", $activeSlave.inflationMethod = 1>>
-	<</link>>
-	<br>
-<<case "$He submitted to enslavement as $his only way to obtain surgery to transform $him into a woman.">>
-	//and since $he came here for surgery...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Give $him $his surgery">>
-		<<replace "#introResult">>
-			When $he <<if canHear($activeSlave)>>hears<<else>>learns<</if>> $he'll be heading to surgery immediately, $he bursts into @@.hotpink;tears of gratitude@@ and makes to run around your desk to hug you before checking $himself. $He clearly doesn't want to put a foot wrong and isn't sure it would be appropriate. You solve $his dilemma by meeting $him with an embrace. $He @@.mediumaquamarine;cries into your chest@@ and promises to be your best slave. The surgery does affect $his @@.red;health@@ a little.
-		<</replace>>
-		<<set $activeSlave.devotion += 15>>
-		<<run surgeryDamage($activeSlave, 10)>>
-		<<set $activeSlave.trust += 10>>
-		<<set $activeSlave.vagina = 0>>
-		<<set $activeSlave.dick = 0>>
-		<<set $activeSlave.balls = 0>>
-	<</link>>
-	| <<link "Use $him as $he is">>
-		<<replace "#introResult">>
-			You announce that $he'll have to earn $his surgery — if $he ever gets it at all. $He only has time for a moment of shock and betrayal before $he finds $his face jammed into the cushions of your office couch. $He struggles, outraged, but only manages to avoid sodomy for a moment. $His resistance only makes it harder on $himself as you take $his anus without mercy. By the third time you buttfuck $him $he's learned to @@.hotpink;relax and let it happen.@@
-		<</replace>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.anus += 1>>
-		<<= VCheck.Anal()>>
-	<</link>>
-	<br>
-<<case "$He submitted to enslavement out of a misguided desire to join a sexually libertine society.">>
-	//and since $he's looking for a sexually libertine society...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave)>>
-		<<link "Disabuse $him of $his silly ideas">>
-			<<replace "#introResult">>
-				You rise from your desk and move in close. $He turns to face you but you roughly spin $him around. You haul $him up on tiptoe so you can use $him standing. As you get your <<if $PC.dick != 0>>cock<<else>>strap-on<</if>> lined up with $his sissy ass $he starts to struggle and protest, asking you to fuck in a more comfortable position. You tell $him to keep $his whore mouth shut, and administer an awful slap when $he tries to keep talking. $His stupid illusions about life as a sex slave @@.mediumorchid;melt away@@ with the burning sensation of a dick forcing its way up $his ass at a harsh angle. $He wilts a little as you abuse $his butt, and afterward, $he's in tears as $he stumbles to the bathroom to wash $his fuckhole. $He's regained $his composure by the time $he gets out but @@.gold;breaks down@@ when you tell $him to present $his asshole again.
-			<</replace>>
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 10>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<<if ($activeSlave.indentureRestrictions <= 0) && ($seeExtreme == 1)>>
-		| <<link "Cruelly castrate $him">>
-			<<replace "#introResult">>
-				You rise from your desk and move in close, wordlessly dominating $him without touch, tempting and overawing $him until $he's desperate with desire, $his prick stiff as iron. $He follows you willingly into the autosurgery and even allows you to strap $him in, face-down, without comment. $His fuckhole welcomes your <<if $PC.dick != 0>>cock<<else>>strap-on<</if>> and $he gasps with pleasure. $He climaxes with indecent speed, dripping $his cum onto the surgery floor. You keep fucking $him, but lean forward to whisper to $him that that was $his last hard-on. $He's completely confused and says nothing, but gradually realizes what the numb feeling around $his ballsack means. $He @@.gold;screams with horror@@ and @@.mediumorchid;sobs disconsolately@@ as the autosurgery disengages from $his clipped genitals and you disengage from $his <<if $PC.dick != 0>>cum-filled<<else>>wilting<</if>> butthole. $He gingerly stumbles back to your office with you and, without even being ordered to, arranges $himself on the couch with $his fuckhole ready. The gelding does affect $his @@.red;health@@ somewhat.
-			<</replace>>
-			<<set $activeSlave.devotion -= 10>>
-			<<set $activeSlave.trust -= -10>>
-			<<run surgeryDamage($activeSlave, 10)>>
-			<<set $activeSlave.balls = 0>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He asked to be enslaved out of naïve infatuation with you.">>
-	//and since $he is already infatuated with you...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave) || canDoVaginal($activeSlave)>>
-		<<link "Let $him show you what $he can do">>
-			<<if $activeSlave.vagina == 1>>
-				<<set _temp = random(1,100)>>
-			<<else>>
-				<<set _temp = 0>>
-			<</if>>
-			<<if _temp > 50>>
-				<<= VCheck.Vaginal()>>
-			<<else>>
-				<<= VCheck.Anal()>>
-			<</if>>
-			<<replace "#introResult">>
-				You tell $him that slaves working in your penthouse are all expected to please you, and $he'll have to impress you. $He smiles and even giggles a little, standing to strip off $his clothes, taking $his time and showing off $his fresh body. $He gets lewder and lewder, displaying youthful libido basking in the gaze of $his crush. $He slowly becomes more and more desperate to entice you, so you let $him keep going out of curiosity, to see how far $he'll go. $He goes to the point of reclining on the couch with $his legs back, spreading $himself and masturbating, and when that fails to get you out of your chair, $he begins to wink $his <<if _temp > 50>>pussy<<else>>anus<</if>> meaningfully. $He's clearly got a good idea of what many men like, and you head over and take $his tight <<if _temp > 50>>pussy<<else>>asshole<</if>>. You do it gently, making sure $he enjoys $himself, and $he becomes @@.mediumaquamarine;very hopeful@@ that $he was wrong to doubt you, and that $he really can @@.hotpink;be close to@@ the object of $his infatuation. $He bounces up off the couch afterward to wiggle $his butt at you, and even blows you a kiss as you get back to work.
-			<</replace>>
-			<<set $activeSlave.devotion += 4>>
-			<<set $activeSlave.trust += 4>>
-		<</link>>
-	<</if>>
-	<<if canDoAnal($activeSlave)>>
-		| <<link "Make sure $he knows $he made a mistake">>
-			<<replace "#introResult">>
-				You tell $him that slaves working in your penthouse are all expected to please you, and $he nods eagerly. $He strips, only starting to look doubtful again when you brusquely order $him to hurry up. You order $him to get down on the ground with $his face up, $his shoulders against the floor; $he does, and then you order $him to put $his legs over $his head. $He looks puzzled but does, flipping $himself flexibly up until $he's doing a shoulder stand, bent double with $his crotch over $his face. You stand over $him and shove <<if $PC.dick != 0>>your cock<<else>>a strap-on<</if>> down and into $his asshole. This is an advanced anal position and <<if $activeSlave.anus > 0>>although $he's clearly no<<else>>even for an<</if>> anal virgin it's well beyond $him. $He does $his best but tears begin to streak $his cheeks. After a while you tire of the position and flip $him over onto $his face to assrape $him doggy style. This allows you to whisper into $his ear that $his holes are your property now, to use, to abuse, or to sell. $He @@.mediumorchid;sobs in despair@@ at the latter, knowing $he's @@.gold;thrown $his life away.@@
-			<</replace>>
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 10>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He asked to be enslaved in the hope you'd treat a fellow woman well.">>
-	//and since $he's hoping to protect someone...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Build a connection with $him">>
-		<<replace "#introResult">>
-			You ask conversationally what experience $he has with women. $He takes a while to understand your point, but once $he does, $he blushes and <<say>>s, "Uh, I me<<ss>>ed around in <<s>>chool on<<c>>e or twi<<c>>e, but <<s>>in<<c>>e then, I've never —" and is then quiet, because you're making out with $him. $He's surprised and stiffens for a moment but perceptibly thinks things through and realizes $he's yours now, and then $he relaxes to let you have your way. You push $his boundaries pretty far, and before long you've got $him down <<if hasBothLegs($activeSlave)>>on $his knees<<else>>under your desk<</if>> eating you out. $He's badly discomfited but does $his best; $he's @@.mediumaquamarine;hopeful@@ that $he can do this, and slave life won't be so bad.
-		<</replace>>
-		<<set $activeSlave.trust += 4>>
-		<<set $activeSlave.counter.oral += 1>>
-		<<set $oralTotal += 1>>
-	<</link>>
-	<<if canDoAnal($activeSlave)>>
-		| <<link "Put $him in $his place">>
-			<<replace "#introResult">>
-				You stand up and tell $him to strip. $He hesitates, staring at you in dawning apprehension, until $he <<if canSee($activeSlave)>>sees<<else>>realizes<</if>> that you're stepping into a strap-on. $He unconsciously takes a step backward, but that's as far as $he gets before you cover the ground between you, grab $him by the throat, and push $his back until $he falls onto the couch. You drag the head of the fake phallus down over $his pussylips, and $he shivers, but you keep going, telling $him that you'll only be fucking $him there when $he's good. When $he's bad, you'll assfuck $him. <<if hasBothEyes($activeSlave)>>$His eyes fly open and $he <<else>> $He <</if>> tries to struggle, but you give $him a warning slap and then push yourself home. $He starts to cry, more from hopelessness than anal pain, knowing that @@.mediumorchid;you're a slaveowner like any other,@@ more likely to @@.gold;abuse $him@@ than be sympathetic.
-			<</replace>>
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 5>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He asked to be enslaved since $he felt you were $his only hope of becoming a prettier woman.">>
-	//and since $he desperately wants to be a prettier woman...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Start by fixing that voice of $hers">>
-		<<replace "#introResult">>
-			You schedule $him for some vocal surgery. You have $him brought in to be inspected the next day, and though $he's healing well, $he's not to speak yet. $He's sore enough to obey the stricture, but when $he stands naked before your desk $he clearly wants to communicate something. You approach $him and place a finger on $his fake lips, shushing $him, and you tell $him you understand. You kiss $him and push $him backward until $he falls onto the couch; $he smiles @@.mediumaquamarine;trustingly@@ and pulls $his legs apart and back, offering you $his asshole. $He has a stiff hard-on, a clear indication that $he's honestly @@.hotpink;attracted to you,@@ and though $he still doesn't speak, $he makes a sore little whinny of pleasure when you enter $his welcoming butt.
-		<</replace>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-		<<set $activeSlave.voice += 1>>
-		<<run surgeryDamage($activeSlave, 10)>>
-		<<= VCheck.Anal()>>
-	<</link>>
-	<<if canDoAnal($activeSlave)>>
-		| <<link "Let $him know $he'll have to earn $his rewards">>
-			<<replace "#introResult">>
-				You complete the induction and then perform a thorough inspection, noting down each area in which surgery or drugs could improve $his body. $He understands what you're doing and can barely contain $his excitement, but you tell $him that $he'll have to earn such improvements. $His face falls. You reassure $him that it won't take the years $he would have had to work to afford such things $himself, but that $he needs to be a good slave, and soon, for you to spend resources on $him. You let $him start by taking a rough buttfuck, bent over the desk. $His ass is tight and you are not merciful. $He gasps and moans but takes it all the same, @@.mediumaquamarine;trusting@@ that $he'll eventually be rewarded with transformation.
-			<</replace>>
-			<<set $activeSlave.trust += 10>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He was transformed and enslaved after $he fell into debt to you.">>
-	//and since $he's so deeply in debt to you...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave)>>
-		<<link "Introduce $him to getting fucked">>
-			<<replace "#introResult">>
-				You stand up, your <<if $PC.dick != 0>>erection<<if $PC.vagina != -1>> and the pussy beneath it<</if>><<else>>strap-on<</if>> suddenly becoming visible to $him, and order $him in a conversational tone of voice to <<if hasAnyLegs($activeSlave)>>kneel<<else>>get<</if>> on the couch and spread $his buttocks. The true nature of $his new life crashes down on $him in a moment, and $he cannot decide whether to run, to rage, or to cry. You solve $his dilemma for $him by seizing $him and flinging $him toward the couch, administering practiced cracks of your palm to $his ass until $he complies. $He sobs as $he feels the burning sensation of $his @@.lime;anal virginity being taken,@@ knowing that $he can now be @@.gold;used at will@@ and is now subject to the whims of someone who has shown themselves @@.mediumorchid;willing to rape $his anus.@@
-			<</replace>>
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 5>>
-			<<set $activeSlave.anus = 1>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He offered $himself to you as a slave to escape a life of boredom.">>
-	//and since $he was so bored...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave)>>
-		<<link "Make sure $he enjoys $his first butt stuff">>
-			<<replace "#introResult">>
-				You make no direct answer to $his nervous question about $his virgin butt, though you do wonder how $he was so selfish as to be a Free Cities party $girl without giving up $his asshole nightly. Instead, you ask $him about $himself, ensuring that $he learns the proper way to answer questions from $his <<= WrittenMaster($activeSlave)>>. $He answers readily, even when you turn the subject to $his sexual experiences, and $he starts to get visibly aroused. Before long, $he finds $himself sitting on your lap on the couch, making out with you while $he gives you a handjob. $He stiffens a little when you begin to play with $his butthole, but you take your time. Soon $he's taking a finger, and then two, and before long, $he's riding <<if $PC.dick != 0>>your cock<<else>>a strap-on<</if>>. $He doesn't climax to $his @@.lime;first buttsex,@@ but $he is learning to @@.mediumaquamarine;trust@@ that you won't hurt $him.
-			<</replace>>
-			<<set $activeSlave.trust += 10>>
-			<<set $activeSlave.anus = 1>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He sold $himself into slavery out of fear that life on the streets was endangering $his pregnancy.">>
-	//and since $he is here to protect $his pregnancy...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if canDoAnal($activeSlave)>>
-		<<link "Make sure $he enjoys $his first anal sex">>
-			<<replace "#introResult">>
-				You introduce $him to obedience and proper manners regarding $his <<= WrittenMaster($activeSlave)>> before sending $him off for a physical. That night, $he's returned to your room, and finds you doing business on a tablet in bed. $He looks doubtful, but obeys when you direct $him to get into bed<<if $PC.dick == 0>>, even after $he realizes you're wearing a strap-on<</if>>. You turn out the light and spoon $him from behind, kissing $his neck and ears, cupping $his swollen breasts, and running your hands across $his pregnant belly with its taut $activeSlave.skin skin. $He's awkward at first but $his body responds to the tenderness. Before long $he's humping $his pussy back and forth against <<if $PC.dick != 0>>your cock<<else>>the strap-on<</if>>. You tell $him no, not there, and begin to gently work <<if $PC.dick != 0>>your dickhead<<else>>its tip<</if>> up $his tight but relaxed ass. $He's unsure of $himself, but you keep $him nice and relaxed. $He doesn't climax to $his @@.lime;first buttsex,@@ but $he is learning to @@.mediumaquamarine;trust@@ that you won't hurt $him.
-			<</replace>>
-			<<set $activeSlave.trust += 10>>
-			<<set $activeSlave.anus = 1>>
-			<<= VCheck.Anal()>>
-		<</link>>
-	<</if>>
-	<br>
-<<case "$He offered $himself to you as a slave to escape the hard life of a free whore.">>
-	//and since $he is trying to escape the hard life of a free whore...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Clean up $his whorish appearance">>
-		<<replace "#introResult">>
-			$He's totally unsurprised when you send $him to the salon for a makeover. It takes several days of work before $he's brought back in for another inspection; when $he arrives, you wordlessly point $him to a full length mirror. $He <<if canSee($activeSlave)>>sees $himself<<else>><<if hasAnyArms($activeSlave)>>tenderly uses $his hand<<if hasBothArms($activeSlave)>>s<</if>> and finds $himself<<else>>stoically waits while you vividly describe $his new appearance. One<</if>><</if>> without tattoos, a hooker's haircut and piercings, a conventionally pretty $girl with subtle implants and a clean appearance. $He gasps <<if hasAnyArms($activeSlave)>>and covers $his mouth with a hand<<else>>but quickly closes $his mouth<</if>>, and then suddenly bursts into tears. "Thank you, <<Master>>," $he sobs. "I never would have thought."<<if hasAnyArms($activeSlave)>> $He reaches out to touch $his reflection. <<else>> $He pauses for just a moment. <</if>>"I <<if canSee($activeSlave)>>look<<else>>feel<</if>> like a nice $girl." $He is @@.hotpink;grateful to you@@ for <<if canSee($activeSlave)>>showing $him<<else>><<if hasAnyArms($activeSlave)>>letting $him feel<<else>>detailing<</if>><</if>> this new side of $himself, and has @@.mediumaquamarine;begun to trust@@ that being your slave will be less degrading than being a free prostitute.
-		<</replace>>
-		<<set $activeSlave.trust += 4>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.boobs -= $activeSlave.boobsImplant>>
-		<<set $activeSlave.boobsImplant = 0>>
-		<<set $activeSlave.boobsImplantType = "none">>
-		<<set $activeSlave.lips -= $activeSlave.lipsImplant>>
-		<<set $activeSlave.lipsImplant = 0>>
-		<<set $activeSlave.butt -= $activeSlave.buttImplant>>
-		<<set $activeSlave.buttImplant = 0>>
-		<<set $activeSlave.buttImplantType = "none">>
-		<<set $activeSlave.lipsPiercing = 0>>
-		<<set $activeSlave.tonguePiercing = 0>>
-		<<set $activeSlave.nosePiercing = 0>>
-		<<set $activeSlave.eyebrowPiercing = 0>>
-		<<set $activeSlave.navelPiercing = 0>>
-		<<set $activeSlave.nipplesPiercing = 0>>
-		<<set $activeSlave.clitPiercing = 0>>
-		<<set $activeSlave.hStyle = "neat">>
-		<<set $activeSlave.custom.tattoo = " ">>
-	<</link>>
-	<br>
-<<case "$He sold $himself into slavery to escape life on the streets.">>
-	//and since $he is trying to escape life on the streets...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Show off $his tits">>
-		<<replace "#introResult">>
-			You send $him out to be cleaned up and inducted, but have $him brought back afterward. You point out a clothes box on the couch and tell $him to get dressed, since you're about to make a tour of the club, and $he'll be accompanying you. $He obeys without comment, but gasps with shock to find that $he's been given a string sling bikini. $He climbs into it hesitantly, and finds that not only does its string bottom ride up between $his pussylips, it does not cover $his nipples at all: the strings part to either side of the nipples to let them stick through, bare. This can only generously be called clothing. $He accepts this with an obvious internal sigh, however, and walks dutifully behind you, $his bare feet slapping along as $his big butt bounces and $his tits constantly fall out of $his strings.
-			<<if $activeSlave.behavioralFlaw != "shamefast">>
-				After a while, though, $he notices that $he's getting a lot of very positive attention, and even starts to strut it a little. When you get back to the penthouse, you ask $him how $he felt. $He blushes. "Kinda hot, <<Master>>," $he <<say>>s, embarrassed. "I didn't think an older $girl like me would get <<s>>o many <<s>>tare<<s>>." You reach out to grope $his tits, and tell $him that with breasts like $hers, it's not surprising. $He @@.hotpink;likes you@@ for liking $his body, and has @@.mediumaquamarine;begun to trust@@ that even though $he's old, $he can find a place under you.
-				<<set $activeSlave.trust += 4>>
-				<<set $activeSlave.devotion += 4>>
-			<<else>>
-				After a while, though, $he notices that $he's getting a lot of very positive attention, but $he remains embarrassed. $His gait is clumsy and hesitant, and $he almost trips over $himself with mortification. When you get back to the penthouse, you ask $him how $he felt. $He blushes furiously. "T-terrible, <<Master>>," $he <<say>>s with feeling. "P-plea<<s>>e, I'll @@.hotpink;d-do anything you want@@ @@.gold;a<<s>> long a<<s>> you don't m-make me do that again.@@ Anything." $He thinks frantically, obviously trying to come up with the most abject idea $he can. "I'll b-be a whore! J-ju<<s>>t a<<s>> long a<<s>> I can do it, you know, in a room."
-				<<set $activeSlave.trust -= 4>>
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<</replace>>
-	<</link>>
-	<br>
-/*Written by DrPill, coded by Boney M*/
-<<case "$He was recruited into your service by $his older sibling.">>
-	<<setLocalPronouns $eventSlave 2>>
-	//and since $he was recruited by $his older _sister2...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Use $his big _sister2 as an example">>
-		<<replace "#introResult">>
-			Your new slave appears <<if $activeSlave.devotion < -10>>reluctant to assume $his new duties<<else>>unsure what $his new duties are<</if>>. You gesture towards $eventSlave.slaveName. _He2 is <<if $eventSlave.intelligence+$eventSlave.intelligenceImplant > 15>>bright enough<<elseif (($eventSlave.skill.vaginal + $eventSlave.skill.anal + $eventSlave.skill.oral) > 100)>>skilled enough<<else>>obedient enough<</if>> to understand you mean a demonstration is in order. $eventSlave.slaveName starts things off with a <<if $eventSlave.skill.entertainment >= 100>>masterful<<elseif $eventSlave.skill.entertainment > 10>>skillful<<else>>passable<</if>> striptease, culminating in _him2 <<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>bending over<<else>>kneeling<</if>> in front of you. _He2 eagerly moans as you enter _him2, begging for your seed<<if $eventSlave.energy > 95>> like the slut _he2 is<<elseif $eventSlave.skill.whoring > 30>> like the whore _he2 is<<elseif ($eventSlave.assignment == "serve in the master suite") || ($eventSlave.assignment == "please you")>> like the fucktoy _he2 is<</if>>. As you finish, _he2 <<if $eventSlave.fetish == "cumslut">>opens _his2 mouth and savors your gift, thanking you once _he2's swallowed enough to be able to talk again<<elseif ($eventSlave.fetish == "buttslut") || ($eventSlave.fetish == "submissive")>>collapses on the floor with _his2 ass high in the air, thanking you for painting _his2 hole white<<else>>thanks you<</if>>. Witnessing this display of servitude from $his big _sister2 @@.hotpink;eases $activeSlave.slaveName into $his new life,@@ and @@.mediumaquamarine;gives $him hope@@ $he can find a place here.
-		<</replace>>
-		<<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>
-			<<set $eventSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-		<<else>>
-			<<set $eventSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-		<</if>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-	<br>
-<<case "$He was recruited into your service by $his younger sibling.">>
-	<<setLocalPronouns $eventSlave 2>>
-	//and since $he was recruited by $his younger _sister2...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Use $his little _sister2 as an example">>
-		<<replace "#introResult">>
-			Your new slave appears <<if $activeSlave.devotion < -10>>reluctant to assume $his new duties<<else>>unsure what $his new duties are<</if>>. You gesture towards $eventSlave.slaveName. _He2 is <<if $eventSlave.intelligence+$eventSlave.intelligenceImplant > 15>>bright enough<<elseif (($eventSlave.skill.vaginal + $eventSlave.skill.anal + $eventSlave.skill.oral) > 100)>>skilled enough<<else>>obedient enough<</if>> to understand you mean a demonstration is in order. $eventSlave.slaveName starts things off with a <<if $eventSlave.skill.entertainment >= 100>>masterful<<elseif $eventSlave.skill.entertainment > 10>>skillful<<else>>passable<</if>> striptease, culminating in _him2 <<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>bending over<<else>>kneeling<</if>> in front of you. _He2 eagerly moans as you enter _him2, begging for your seed<<if $eventSlave.energy > 95>> like the slut _he2 is<<elseif $eventSlave.skill.whoring > 30>> like the whore _he2 is<<elseif ($eventSlave.assignment == "serve in the master suite") || ($eventSlave.assignment == "please you")>> like the fucktoy _he2 is<</if>>. As you finish, _he2 <<if $eventSlave.fetish == "cumslut">>opens _his2 mouth and savors your gift, thanking you once _he2's swallowed enough to be able to talk again<<elseif ($eventSlave.fetish == "buttslut") || ($eventSlave.fetish == "submissive")>>collapses on the floor with _his2 ass high in the air, thanking you for painting _his2 hole white<<else>>thanks you<</if>>. Witnessing this display of servitude from $his little _sister2 @@.hotpink;eases $activeSlave.slaveName into $his new life,@@ and @@.mediumaquamarine;gives $him hope@@ $he can find a place here.
-		<</replace>>
-		<<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>
-			<<set $eventSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-		<<else>>
-			<<set $eventSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-		<</if>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-	<br>
-<<case "$He was recruited into your service by $his twin.">>
-	<<setLocalPronouns $eventSlave 2>>
-	//and since $he was recruited by $his twin...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Use $his _sister2 as an example">>
-		<<replace "#introResult">>
-			Your new slave appears <<if $activeSlave.devotion < -10>>reluctant to assume $his new duties<<else>>unsure what $his new duties are<</if>>. You gesture towards $eventSlave.slaveName. _He2 is <<if $eventSlave.intelligence+$eventSlave.intelligenceImplant > 15>>bright enough<<elseif (($eventSlave.skill.vaginal + $eventSlave.skill.anal + $eventSlave.skill.oral) > 100)>>skilled enough<<else>>obedient enough<</if>> to understand you mean a demonstration is in order. $eventSlave.slaveName starts things off with a <<if $eventSlave.skill.entertainment >= 100>>masterful<<elseif $eventSlave.skill.entertainment > 10>>skillful<<else>>passable<</if>> striptease, culminating in _him2 <<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>bending over<<else>>kneeling<</if>> in front of you. _He2 eagerly moans as you enter _him2, begging for your seed<<if $eventSlave.energy > 95>> like the slut _he2 is<<elseif $eventSlave.skill.whoring > 30>> like the whore _he2 is<<elseif ($eventSlave.assignment == "serve in the master suite") || ($eventSlave.assignment == "please you")>> like the fucktoy _he2 is<</if>>. As you finish, _he2 <<if $eventSlave.fetish == "cumslut">>opens _his2 mouth and savors your gift, thanking you once _he2's swallowed enough to be able to talk again<<elseif ($eventSlave.fetish == "buttslut") || ($eventSlave.fetish == "submissive")>>collapses on the floor with _his2 ass high in the air, thanking you for painting _his2 hole white<<else>>thanks you<</if>>. Witnessing this display of servitude from $his twin _sister2 @@.hotpink;eases $activeSlave.slaveName into $his new life,@@ and @@.mediumaquamarine;gives $him hope@@ $he can find a place here.
-		<</replace>>
-		<<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>
-			<<set $eventSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-		<<else>>
-			<<set $eventSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-		<</if>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-	<br>
-<<case "$He was recruited into your service by $his parent.">>
-	<<setLocalPronouns $eventSlave 2>>
-	//and since $he was recruited by $his parent...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Use $his parent as an example">>
-		<<replace "#introResult">>
-			You gesture towards $eventSlave.slaveName. _He2 is <<if $eventSlave.intelligence+$eventSlave.intelligenceImplant > 15>>bright enough<<elseif (($eventSlave.skill.vaginal + $eventSlave.skill.anal + $eventSlave.skill.oral) > 100)>>skilled enough<<else>>obedient enough<</if>> to understand you mean a demonstration is in order. $eventSlave.slaveName starts things off with a <<if $eventSlave.skill.entertainment >= 100>>masterful<<elseif $eventSlave.skill.entertainment > 10>>skillful<<else>>passable<</if>> striptease, culminating in _him2 <<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>bending over<<else>>kneeling<</if>> in front of you. _He2 eagerly moans as you enter _him2, begging for your seed<<if $eventSlave.energy > 95>> like the slut _he2 is<<elseif $eventSlave.skill.whoring > 30>> like the whore _he2 is<<elseif ($eventSlave.assignment == "serve in the master suite") || ($eventSlave.assignment == "please you")>> like the fucktoy _he2 is<</if>>. As you finish, _he2 <<if $eventSlave.fetish == "cumslut">>opens _his2 mouth and savors your gift, thanking you once _he2's swallowed enough to be able to talk again<<elseif ($eventSlave.fetish == "buttslut") || ($eventSlave.fetish == "submissive")>>collapses on the floor with _his2 ass high in the air, thanking you for painting _his2 hole white<<else>>thanks you<</if>>. Witnessing this display of servitude from $his <<if $activeSlave.mother == $eventSlave.ID>>mother<<else>>father<</if>> @@.hotpink;eases $activeSlave.slaveName into $his new life,@@ and @@.mediumaquamarine;gives $him hope@@ $he can find a place here.
-		<</replace>>
-		<<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>
-			<<set $eventSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-		<<else>>
-			<<set $eventSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-		<</if>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-	<br>
-<<case "$He was recruited into your service by $his child.">>
-	<<setLocalPronouns $eventSlave 2>>
-	//and since $he was recruited by $his _daughter2...//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Use $his _daughter2 as an example">>
-		<<replace "#introResult">>
-			You gesture towards $eventSlave.slaveName. _He2 is <<if $eventSlave.intelligence+$eventSlave.intelligenceImplant > 15>>bright enough<<elseif (($eventSlave.skill.vaginal + $eventSlave.skill.anal + $eventSlave.skill.oral) > 100)>>skilled enough<<else>>obedient enough<</if>> to understand you mean a demonstration is in order. $eventSlave.slaveName starts things off with a <<if $eventSlave.skill.entertainment >= 100>>masterful<<elseif $eventSlave.skill.entertainment > 10>>skillful<<else>>passable<</if>> striptease, culminating in _him2 <<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>bending over<<else>>kneeling<</if>> in front of you. _He2 eagerly moans as you enter _him2, begging for your seed<<if $eventSlave.energy > 95>> like the slut _he2 is<<elseif $eventSlave.skill.whoring > 30>> like the whore _he2 is<<elseif ($eventSlave.assignment == "serve in the master suite") || ($eventSlave.assignment == "please you")>> like the fucktoy _he2 is<</if>>. As you finish, _he2 <<if $eventSlave.fetish == "cumslut">>opens _his2 mouth and savors your gift, thanking you once _he2's swallowed enough to be able to talk again<<elseif ($eventSlave.fetish == "buttslut") || ($eventSlave.fetish == "submissive")>>collapses on the floor with _his2 ass high in the air, thanking you for painting _his2 hole white<<else>>thanks you<</if>>. Witnessing this display of servitude from $his _daughter2 @@.hotpink;eases $activeSlave.slaveName into $his new life,@@ and @@.mediumaquamarine;gives $him hope@@ $he can find a place here.
-		<</replace>>
-		<<if ($eventSlave.anus > 0) && ($eventSlave.fetish != "cumslut")>>
-			<<set $eventSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-		<<else>>
-			<<set $eventSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-		<</if>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-	<br>
-<<default>>
-	<<if $activeSlave.origin>>
-		<<if ($activeSlave.origin.includes("enslavement for the attempted rape of a free"))>>
-			//and since $he tried to rape you...//
-			<br>&nbsp;&nbsp;&nbsp;&nbsp;
-			<<link "Show $him how a professional does it">>
-				<<replace "#introResult">>
-					Judging from $his earlier, amateur attempt, it's clear $activeSlave.slaveName is unfamiliar with the subtle nuances of a high-quality rape. You decide to clear your schedule for the rest of the day and teach $him yourself<<if $PC.dick == 0>>, with the help of your trusty strap-on, of course<</if>>. Once $he's bent over across your desk and properly restrained, $his lesson begins with you thrusting your <<if $PC.dick == 0>>fake <</if>>cock down $his throat as far as it will go. Over the course of the next several hours, you ensure that $he understands the fine points of nonconsensual oral<<if $activeSlave.vagina > -1>>, vaginal,<</if>> and anal intercourse as intimately as possible. When you're finally too tired to continue,you unshackle $his @@.red;bruised and bloody body@@ and ask $him what $he learned. $His voice hoarse from the same brutal fucking that has gaped $his @@.lime;asshole@@<<if $activeSlave.vagina > -1>> and @@.lime;pussy@@<</if>>, $he hesitantly replies that $he has @@.hotpink;learned a great deal about true dominance,@@ before fainting on the spot from a mixture of total exhaustion and pure terror. You've taught your student well.
-				<</replace>>
-				<<set $activeSlave.counter.oral += 15, $oralTotal += 15>>
-				<<set $activeSlave.anus = 2, $activeSlave.counter.anal += 15, $analTotal += 15>>
-				<<if $activeSlave.vagina > -1>>
-					<<set $activeSlave.vagina = 2, $activeSlave.counter.vaginal += 15, $vaginalTotal += 15>>
-				<</if>>
-				<<if isFertile($activeSlave) && $PC.dick > 0>>
-					<<= knockMeUp($activeSlave, 100, 0, -1, 1)>>
-				<</if>>
-				<<set $activeSlave.devotion += 100>>
-			<</link>>
-			| <<link "Show $him that $he could have just asked">>
-				<<replace "#introResult">>
-					You tell $activeSlave.slaveName that while $his previous attempt to fuck you was very flattering, $he should know that one can catch more flies with honey. Ordering the confused slave to lean back over and onto your desk, you proceed to gently <<if $activeSlave.dick > 0>>stroke<<else>>finger<</if>> $him until $his arousal overwhelms $his wariness of you. Once $he's finally relaxed, you climb on top of $him, and gently ease
-					<<if ($PC.vagina > 0) && (canPenetrate($activeSlave))>>
-						your pussy onto $his cock. $He shudders and moan softly as you slide yourself up and down $his shaft with steadily increasing speed. You keep your eyes locked on $hers all the while, as $his expression shifts from bewilderment to acceptance to ecstasy, as $he soon shoots $his seed up into you. Afterwards, you slip $his softening cock out of you, climb off of $him, and leave the exhausted and overwhelmed slave<<= $girl>> on your desk as you attend to business elsewhere. You think $he's @@.orangered;going to like it here.@@
-						<<set $activeSlave.counter.penetrative++, $penetrativeTotal++>>
-						<<if canImpreg($PC, $activeSlave)>>
-							<<= knockMeUp($PC, 100, 0, $activeSlave.ID, 1)>>
-						<</if>>
-					<<else>>
-						yourself into $his <<if $activeSlave.vagina > -1>>pussy<<else>>asshole<</if>>. $He shudders and moans softly as you piston your <<if $PC.dick > 0>>dick<<else>>strap-on<</if>> in and out of $his hole with steadily increasing intensity. You keep your eyes locked on $hers all the while, as $his expression shifts from bewilderment to acceptance to ecstasy, as you soon shoot your seed down into $him. Afterwards, you slip your <<if $PC.dick > 0>>softening<<else>>plastic<</if>> cock out of $him, climb off of $him, and leave the exhausted and overwhelmed slave<<= $girl>> on your desk as you attend to business elsewhere. You think $he's @@.orangered;going to like it here.@@
-						<<if $activeSlave.vagina > -1>>
-							<<= VCheck.Vaginal()>>
-							<<set $activeSlave.counter.vaginal++, $vaginalTotal++>>
-							<<if isFertile($activeSlave) && $PC.dick > 0>>
-								<<= knockMeUp($activeSlave, 100, 0, -1, 1)>>
-							<</if>>
-						<<else>>
-							<<= VCheck.Anal()>>
-							<<set $activeSlave.counter.anal++, $analTotal++>>
-						<</if>>
-					<</if>>
-				<</replace>>
-				<<set $activeSlave.trust += 100>>
-			<</link>>
-			<br>
-		<</if>>
-	<</if>>
-<</switch>>
-
-<br>//Have $him changed...//
-
-<<if $activeSlave.health.condition < -20>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Address $his medical issues">>
-		<<replace "#introResult">>
-			Since $he's in rough shape, you give $him a comprehensive medical exam with the help of the remote surgery. You apply care to @@.green;address@@ some of the most outstanding concerns. Whatever gratitude $he might have felt for prompt medical attention is balanced by fear of the remote surgery and the nagging (and accurate) feeling that $he's being treated like livestock, but $he does @@.mediumaquamarine;begin to hope@@ $he'll be well treated.
-			<<if $arcologies[0].FSPaternalist != "unset">>
-				Society @@.green;approves@@ of your promptly seeing to your stock's health; this advances the idea that all slaveowners should look after their slaves.
-				<<= FutureSocieties.Change("Paternalist", 2)>>
-			<</if>>
-		<</replace>>
-		<<run improveCondition($activeSlave, 10)>>
-		<<set $activeSlave.trust += 4>>
-	<</link>>
-<</if>>
-
-
-<<if ($activeSlave.hStyle.indexOf("shaved") == -1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Shave $his hair">>
-		<<replace "#introResult">>
-			You seize $him by the hair and drag $him to the auto salon. $He struggles and thrashes around but soon scrambles to keep up with you to relieve the pain in $his scalp as $he's dragged. You strap $him in and set the auto salon to shave. $He <<if canSee($activeSlave)>>looks around frantically<<else>>trembles with fear<</if>> as the manipulators with their buzzing shears descend menacingly. As they run quickly back and forth across $his head, $he begins to sob, obviously @@.gold;fearing@@ that $his new master is @@.mediumorchid;one to enjoy degradation and abuse.@@ $He has a strong indication that obedience is $his only hope.
-		<</replace>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.hStyle = "shaved">>
-		<<set $activeSlave.hLength = 0>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-<</if>>
-
-
-<<if $activeSlave.stampTat == 0>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Give $him a lewd tramp stamp">>
-	<<set $activeSlave.stampTat = either("advertisements", "degradation", "rude words")>>
-	<<replace "#introResult">>
-		You <<if $activeSlave.devotion > 20>>bring $him down to the studio, lay the compliant<<else>>drag $him down to the studio, strap the reluctant<</if>> slave in the chair face-down, and select a sufficiently lewd pattern to decorate $his lower back. When the automated tattooing is done, a topical application of analgesics leaves $him with nothing more than a slight ache there.
-		<<if canSee($activeSlave)>>
-			When allowed to stand, $he immediately turns $his back to one of the studio's full length mirrors and cranes around to read the writing just over $his butt. It's difficult, since, of course, it's reversed, and $he mouths the words letter by letter.
-			<<if $activeSlave.stampTat == "advertisements">>
-				$He mouths, 'Fuck my ass!'
-			<<elseif $activeSlave.stampTat == "rude words">>
-				'Rear Entrance,' $he mouths, and then understands the arrow pointing down between $his buttocks.
-			<<elseif $activeSlave.stampTat == "degradation">>
-				Struggling to read the ornate gothic lettering, $he slowly mouths, 'Anal Whore.'
-			<</if>>
-		<<elseif canHear($activeSlave)>>
-			When $he sits back up, you tell $him calmly and bluntly what $his new tattoo says:
-			<<if $activeSlave.stampTat == "advertisements">>
-				"Fuck My Ass."
-			<<elseif $activeSlave.stampTat == "rude words">>
-				"Rear Entrance."
-			<<elseif $activeSlave.stampTat == "degradation">>
-				"Anal Whore."
-			<</if>>
-		<<else>>
-			Judging by the expression on $his face, $he's either accurately judged the movement of the needle on $his skin or simply guessed correctly that $his new tattoo consists of
-			<<if $activeSlave.stampTat == "advertisements">>
-				blunt advertisements for anal sex.
-			<<elseif $activeSlave.stampTat == "rude words">>
-				mocking references to anal sex.
-			<<elseif $activeSlave.stampTat == "degradation">>
-				insulting invitations for anal sex.
-			<</if>>
-		<</if>>
-
-		<<if $activeSlave.devotion > 20>>
-			$He gasps at the realization that taking it up the ass is about to become a dominant part of $his life. $He isn't surprised by that, but it's a little stark, having $his status as an anal slave permanently written on $his lower back for anyone to see. Nevertheless, $he @@.hotpink;resolves to do $his best.@@
-			<<set $activeSlave.devotion += 5>>
-		<<else>>
-			$He begins to cry softly. It's not likely that $he didn't know, on some level, that $he's a sex slave, and that most sex slaves are expected to take it up the ass. $He's @@.gold;frightened@@ to find, though, that it's apparently so essential to $his existence that it's worth tattooing right over $his bottom. $He continues to weep as you lead $him out, but @@.hotpink;offers no real resistance.@@ $He's just mourning $his poor anal sphincter in an anticipatory sort of way.
-			<<set $activeSlave.devotion += 5>>
-			<<set $activeSlave.trust -= 5>>
-		<</if>>
-	<</replace>>
-	<</link>>
-<</if>>
-
-| <<link "Brand $him on the _brandTarget">>
-	<<replace "#introResult">>
-		You drag $him to the body modification studio and strap $him down with $his _brandTarget clear and defenseless. $He doesn't understand what's coming for a while, even as disinfectant is applied to $his _brandTarget. $He can't see the approaching brand, <<if !canSee($activeSlave)>>of course, <</if>>but eventually $he feels the radiated heat on $his skin and manages to get one inarticulate, wordless noise of terror out before the dreadful sizzling noise and the sweet smell of burning flesh. If $he didn't know $he was a slave before, @@.mediumorchid;$he does now,@@ and $he's got the @@.gold;agonizing@@ @@.red;injury@@ to prove it.
-		<<if ($arcologies[0].FSSubjugationistRace == $activeSlave.race) && ($arcologies[0].FSSubjugationist > 0)>>
-			Society @@.green;approves@@ of your purchase and branding of an inferior $activeSlave.race person; this advances the idea that $activeSlave.race people ought to be enslaved.
-			<<= FutureSocieties.Change("Subjugationist", 2)>>
-		<<elseif ($arcologies[0].FSSupremacistRace == $activeSlave.race) && ($arcologies[0].FSSupremacist > 0)>>
-			Society @@.red;disapproves@@ of your purchase and branding of <<= addA($activeSlave.race)>> person; this reduces support for the idea that $activeSlave.race people are superior.
-			<<= FutureSocieties.Change("Supremacist", -2)>>
-		<</if>>
-	<</replace>>
-	<<set $activeSlave.brand[_brandTarget] = $brandDesign.primary>>
-	<<set $activeSlave.devotion -= 5>>
-	<<set $activeSlave.trust -= 10>>
-	<<run healthDamage($activeSlave, 10)>>
-<</link>>
-
-
-<<if $activeSlave.dick == 0 && $activeSlave.vagina > -1>>
-	| <<link "Give $him a smart clitoral piercing">>
-		<<run cashX(forceNeg($SPcost), "slaveMod", $activeSlave)>>
-		<<replace "#introResult">>
-			You take $him to the body modification studio, strap $him down, pierce $his clit, and insert what appears to be a large stainless steel barbell piercing. It actually contains a device that can vibrate to give $him sexual stimulation or create subtle pain to suppress pleasure. It is connected wirelessly to the arcology, which can be given instructions to encourage or discourage sexual pleasure in specific situations. You run a quick test. On the oral setting, as soon as $he kneels in front of your crotch, $he gasps as the stimulation starts. On the anal setting, $he whimpers when you run a finger across $his asshole.
-		<</replace>>
-		<<set $activeSlave.clitPiercing = 3>>
-	<</link>>
-	//This option costs <<print cashFormat($SPcost)>>//
-<<else>>
-	| <<link "Give $him a smart frenulum piercing">>
-		<<run cashX(forceNeg($SPcost), "slaveMod", $activeSlave)>>
-		<<replace "#introResult">>
-			You take $him to the body modification studio, strap $him down, pierce $his frenulum, and insert what appears to be a large stainless steel barbell piercing. It actually contains a device that can vibrate to give $him sexual stimulation or create subtle pain to suppress pleasure. It is connected wirelessly to the arcology, which can be given instructions to encourage or discourage sexual pleasure in specific situations. You run a quick test. On the oral setting, as soon as $he kneels in front of your crotch, $he gasps as the stimulation starts. On the anal setting, $he whimpers and gets rock hard when you run a finger across $his asshole.
-		<</replace>>
-		<<set $activeSlave.clitPiercing = 3>>
-	<</link>>
-	//This option costs <<print cashFormat($SPcost)>>//
-<</if>>
-
-<br>&nbsp;&nbsp;&nbsp;&nbsp;
-
-<<if $activeSlave.indentureRestrictions <= 0>>
-	<<link "Whip $him until $he scars">>
-		<<run App.Medicine.Modification.addScourged($activeSlave)>>
-		<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>> /* Let's just bill once, for the whip and disinfectant. */
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<<run healthDamage($activeSlave, 10)>>
-		<<replace "#introResult">>
-			You drag $him to the
-			<<if $cellblock>>
-				$cellblockName
-			<<else>>
-				body modification studio
-			<</if>>
-			and
-			<<if !isAmputee($activeSlave)>>
-				chain $him spread-eagled
-			<<else>>
-				secure $him
-			<</if>>
-			with $his face to the wall and $his naked back defenseless. $He doesn't understand what's coming for a while, even as disinfectant is applied from $his neck
-			<<if hasAnyLegs($activeSlave)>>
-				to $his knee<<if hasBothLegs($activeSlave)>>s<</if>>.
-			<<else>>
-				down.
-			<</if>>
-			$He can't see you prepare the whip,
-			<<if !canSee($activeSlave)>>
-				of course,
-			<</if>>
-			<<if canHear($activeSlave)>>
-				but almost jumps out of $his $activeSlave.skin skin when you crack it behind $him.
-			<<else>>
-				and in $his deaf state every blow is a devastating surprise.
-			<</if>>
-			$He frantically
-			<<if !isAmputee($activeSlave)>>
-				wrenches $his <<if hasAnyArms($activeSlave)>>hand<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>><<if hasBothLegs($activeSlave)>>feet<<else>>foot<</if>><</if>> as you work up and down $his exposed back and limb<<if getLimbCount($activeSlave)>>s<</if>>, but is completely unable to get free.
-			<<else>>
-				twists and turns, but without limbs is powerless to escape the curling whip.
-			<</if>>
-			If $he didn't know $he was a slave before, @@.mediumorchid;$he does now,@@ and $he's got the @@.gold;agonizing@@ @@.red;injury@@ to prove it. What $he doesn't yet know is just how permanent this lashing's effects will be. The level of violence and the coating you used will leave $him scarred with the marks of slavery forever.
-			<<if ($arcologies[0].FSSubjugationistRace == $activeSlave.race) && ($arcologies[0].FSSubjugationist > 0)>>
-				Society @@.green;approves@@ of your purchase and whipping of an inferior $activeSlave.race person; this advances the idea that $activeSlave.race people ought to be enslaved.
-				<<= FutureSocieties.Change("Subjugationist", 2)>>
-			<<elseif ($arcologies[0].FSSupremacistRace == $activeSlave.race) && ($arcologies[0].FSSupremacist > 0)>>
-				Society @@.red;disapproves@@ of your purchase and whipping of <<= addA($activeSlave.race)>> person; this reduces support for the idea that $activeSlave.race people are superior.
-				<<= FutureSocieties.Change("Supremacist", -2)>>
-			<</if>>
-		<</replace>>
-	<</link>>
-
-	| <<link "Scar $him on the _scarTarget">>
-		<<replace "#introResult">>
-			You drag $him to the body modification studio and strap $him down with $his _scarTarget clear and defenseless. $He doesn't understand what's coming for a while, even as disinfectant is applied to $his _scarTarget. You have a wide selection of tools to create scars, the trick is to keep the wound from healing correctly afterwards. Of course, $he has no way of knowing that the pain you are inflicting as you cut into $his flesh will leave such a permanent mark, but the basic message is clear: if $he didn't know $he was a slave before, @@.mediumorchid;$he does now,@@ and $he's got the @@.gold;agonizing@@ @@.red;injury@@ to prove it.
-			<<if ($arcologies[0].FSSubjugationistRace == $activeSlave.race) && ($arcologies[0].FSSubjugationist > 0)>>
-				Society @@.green;approves@@ of your purchase and scarring of an inferior $activeSlave.race person; this advances the idea that $activeSlave.race people ought to be enslaved.
-				<<= FutureSocieties.Change("Subjugationist", 2)>>
-			<<elseif ($arcologies[0].FSSupremacistRace == $activeSlave.race) && ($arcologies[0].FSSupremacist > 0)>>
-				Society @@.red;disapproves@@ of your purchase and scarring of <<= addA($activeSlave.race)>> person; this reduces support for the idea that $activeSlave.race people are superior.
-				<<= FutureSocieties.Change("Supremacist", -2)>>
-			<</if>>
-		<</replace>>
-		<<run App.Medicine.Modification.addScar($activeSlave, _scarTarget, $scarDesign.primary)>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<<run healthDamage($activeSlave, 10)>>
-	<</link>>
-<</if>>
-
-<<if $arcologies[0].FSHedonisticDecadence >= 50 && $activeSlave.behavioralFlaw == "anorexic" && $activeSlave.weight < 10>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Force-feed $him">>
-		<<replace "#introResult">>
-			$He is much too thin and seems to shirk any offered food, but there is an easy solution to that. If $he refuses to eat, $he can be made to. Pulling the reluctant slave to the feeders, binding $him tightly to a chair, and attaching a hose to the slave food nozzle, you give $him a choice; suck the hose willingly or have it forced down $his throat. $He glares defiantly and keeps $his mouth firmly shut. You clamp down on $his nose, inevitably forcing $him to open $his mouth to breath. In that moment, you shove the hose in and down $his throat, carefully directing it down into $his stomach. $He gags as you turn on the flow, $his eyes filling with tears as $he feels the warm food travel down the tube and into $his stomach. $He sobs as $his belly steadily swells with unwelcome sustenance, $his eyes pleading with you, desperate to let you know $he'll be good. You ignore $him, letting $him fill until $his belly is noticeably distended compared to $his thin frame. Once you feel $he has had enough, you pull the hose from $his gut, spraying $his face with food in the process, and tell $him it will go right back in if $he doesn't keep it all down. $He @@.gold;nods fearfully,@@ anything to not go through that again. $He hobbles away once freed, one hand covering $his retching mouth and the other clasping $his @@.mediumorchid;hated, food bloated middle.@@
-		<</replace>>
-		<<set $activeSlave.devotion -= 10>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-<</if>>
-
-
-<<if ($activeSlave.pregKnown == 1) && ($seeExtreme == 1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Threaten $his pregnancy">>
-		<<replace "#introResult">>
-			You place an innocuous-looking drug injector on your desk, and let $him <<if !canSee($activeSlave)>>blindly <</if>>stare at it for a long moment. Then, you roll the cylinder slowly across the smooth surface, rotating it until its label comes into <<if canSee($activeSlave)>>$his<<else>>your<</if>> view. It depicts the silhouette of a pregnant woman, with a red "X" over it. Abortifacients. After an <<if !canSee($activeSlave)>>short period<<else>>instant<</if>> of horrified comprehension, $he flings $himself at your feet, crying, begging, promising. $He pledges to @@.hotpink;submit to you,@@ <<if $PC.dick != 0>><<if $PC.vagina != -1>>eat you out, <</if>>suck your cock, take it in $his pussy, take it up $his ass<<else>>eat you out, worship you with $his pussy, serve you with $his ass<</if>>, anything, as long as @@.gold;you don't hurt $his baby.@@ You observe that $he'll do all of those things, regardless of what you decide to do about $his pregnancy, but for now, you'll keep $him as a pregnant slut. $He thanks you through $his tears.
-		<</replace>>
-		<<set $activeSlave.devotion += 10>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-<</if>>
-
-
-<<if ($arcologies[0].FSRestart >= 10) && ($activeSlave.preg != -3) && ($activeSlave.ovaries > 0) && ($activeSlave.indentureRestrictions <= 0) && ($seeExtreme == 1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Sterilize $him">>
-		<<replace "#introResult">>
-			You drag $him to the remote surgery and strap $him face-up with $his<<if $activeSlave.bellyPreg >= 1500>> pregnant<</if>> stomach bare. $He doesn't understand what's coming for a while, even as $his belly is carefully cleaned and disinfected. $He begins to panic at the <<if canSee($activeSlave)>>sight<<elseif canHear($activeSlave)>>sound<<else>>touch<</if>> of the approaching surgical manipulators, cringing as they cut into $his<<if $activeSlave.bellyPreg >= 1500>> rounded<</if>> abdomen. $He squeals in horror as $his<<if $activeSlave.preg > $activeSlave.pregData.normalBirth/1.33>> swollen womb, still moving with life,<<elseif $activeSlave.bellyPreg >= 1500>> pregnant womb<<else>> womb<</if>> and ovaries are extracted<<if canSee($activeSlave)>> before $his eyes<</if>>.
-			<<if $activeSlave.fetish == "pregnancy" && $activeSlave.pregKnown == 1>>
-				<<if ($activeSlave.preg > $activeSlave.pregData.normalBirth/1.6) && (canSee($activeSlave))>>
-					$He watches $his exposed womb with absolute terror in $his eyes. A small handprint bulges against the organ; $he sobs once before $his mind @@.red;shatters at the sight.@@
-					<<set $activeSlave.fetish = "mindbroken", $activeSlave.fetishKnown = 1, $activeSlave.behavioralQuirk == "none", $activeSlave.behavioralFlaw == "none", $activeSlave.sexualQuirk == "none", $activeSlave.sexualFlaw == "none">>
-				<<else>>
-					$He will never carry another child, and @@.mediumorchid;deeply hates you@@ for snuffing out its life. Your absolute power over $his body is rightfully @@.gold;terrifying.@@
-					<<set $activeSlave.trust = -100, $activeSlave.devotion = -100>>
-				<</if>>
-			<<else>>
-				$He will never<<if $activeSlave.counter.birthsTotal > 0>> again<</if>> bear children, and will @@.mediumorchid;remember $his status@@ with every fucking $he receives. Your absolute power over $his body is rightfully @@.gold;terrifying.@@
-				<<set $activeSlave.trust -= 20, $activeSlave.devotion -= 50>>
-			<</if>>
-			Society @@.green;approves@@ of your promptly rendering $him sterile; this advances the idea that only the elite should breed.
-			<<= FutureSocieties.Change("Eugenics", 1)>>
-			<<if $activeSlave.pregKnown == 1>>
-				Since you snuffed out an unworthy life in the process, the sterilization has an even @@.green;bigger impact.@@
-				<<= FutureSocieties.Change("Eugenics", 2)>>
-			<</if>>
-		<</replace>>
-		<<set TerminatePregnancy($activeSlave)>>
-		<<set $activeSlave.preg = -3>>
-		<<set $activeSlave.ovaries = 0>>
-		<<set $activeSlave.counter.abortions++>>
-		<<set $abortionsTotal++>>
-		<<run surgeryDamage($activeSlave, 10)>>
-	<</link>>
-<</if>>
-
-<<if $activeSlave.balls > 0 && $activeSlave.vasectomy == 0 && $activeSlave.indentureRestrictions <= 1>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Snip $his vas deferens">>
-		<<replace "#introResult">>
-			You drag $him to the remote surgery and strap $him
-			<<if $activeSlave.scrotum > 0>>
-				face-down with $his
-				<<if hasAnyLegs($activeSlave)>>
-					leg<<if hasBothLegs($activeSlave)>>s<</if>> spread.
-				<<else>>
-					testicles exposed.
-				<</if>>
-				$He doesn't understand what's coming for a while, even as $his
-				<<if $activeSlave.balls == 1>>
-					tiny balls
-				<<elseif $activeSlave.balls == 2>>
-					small balls
-				<<elseif $activeSlave.balls == 3>>
-					balls
-				<<elseif $activeSlave.balls == 4>>
-					big balls
-				<<elseif $activeSlave.balls == 5>>
-					lemon-sized balls
-				<<elseif $activeSlave.balls < 10>>
-					fist-sized balls
-				<<else>>
-					hypertrophied balls
-				<</if>>
-				are carefully cleaned and disinfected. $He can't see the approaching surgical manipulators, <<if !canSee($activeSlave)>>of course, <</if>>but eventually $he feels a small tugging on the sides of $his scrotum even through the local anesthetic.
-			<<else>>
-				You drag $him to the remote surgery and strap $him face-up on the operating table. $He doesn't understand what's coming for a while, even as $his crotch and lower stomach are carefully cleaned and disinfected.
-				<<if !canSee($activeSlave)>>
-					$He can't see the approaching surgical manipulators, of course, but eventually $he feels a small pinch to either side of $his pubic mound even through the local anesthetic.
-				<<else>>
-					$He freezes at the sight of the surgical manipulators and squirms slightly as they slip into the sides of $his pubic mound.
-				<</if>>
-			<</if>>
-			$He gets one squeal of protest out before the surgery begins to apply healing agents. $He knows something was done to $his testicles and will @@.mediumorchid;remember this act@@ with every load $he shoots. $He @@.gold;can only imagine@@ what other modifications you may have planned for $him.
-		<</replace>>
-		<<set $activeSlave.vasectomy = 1>>
-		<<set $activeSlave.devotion -= 5>>
-		<<run surgeryDamage($activeSlave, 10)>>
-		<<set $activeSlave.trust -= 5>>
-	<</link>>
-<</if>>
-
-<<if ($activeSlave.balls > 0) && ($activeSlave.attrXY < 65) && ($seeExtreme == 1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Threaten $his balls">>
-		<<replace "#introResult">>
-			You ask $him how $he feels about taking cock up $his ass. A look of revulsion and fear crosses $his face, and $he backs away from you slightly, unconsciously protecting $his bottom. Before $he can muster any kind of response, you tell $him that you suspected as much, and that $he has balls. $He looks confused at the apparently unrelated remarks until you explain that in your arcology, balls are a privilege. If $he won't be a good little bitch, you'll take them away and turn $him into a bitch anyway. Only good <<= $girl>>s get to keep their hard-ons, and if $he isn't a good $girl, you'll make $him a good little $girl with a bouncing little bitchclit. $He begins to cry, @@.gold;sobbing and sobbing@@ as $he promises to @@.hotpink;be a good $girl.@@
-		<</replace>>
-		<<set $activeSlave.devotion += 10>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-<</if>>
-
-
-<<if ($activeSlave.indentureRestrictions <= 0) && ($seeExtreme == 1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if $activeSlave.balls > 0>>
-		<<link "Geld $him">>
-			<<replace "#introResult">>
-				You drag $him to the remote surgery and strap $him face-down with $his legs spread. $He doesn't understand what's happening, since the anesthetics totally deprive $him of any sensation. $He's so drugged and drowsy with @@.red;surgical recovery@@ that it takes a while for $him to figure out what's happened. When $he does, $his poor mind scarcely processes the @@.gold;horror@@ of what's happened. $He numbly carries on, terrified.
-				<<if $arcologies[0].FSGenderRadicalist != "unset">>
-					Society @@.green;approves@@ of your promptly gelding $him; this advances the idea that all societal inferiors can be made female.
-					<<= FutureSocieties.Change("GenderRadicalist", 2)>>
-				<</if>>
-				<<if $arcologies[0].FSRestart != "unset" && $activeSlave.pubertyXY == 1>>
-					Society @@.green;approves@@ of your promptly gelding $him; this advances the idea that all only the elite should breed.
-					<<= FutureSocieties.Change("Eugenics", 2)>>
-				<</if>>
-			<</replace>>
-			<<set $activeSlave.balls = 0, $activeSlave.scrotum = 0>>
-			<<run surgeryDamage($activeSlave, 10)>>
-			<<set $activeSlave.trust -= 50>>
-		<</link>> |
-	<</if>>
-	<<link "Remove $his genitalia">>
-		<<replace "#introResult">>
-			You drag $him to the remote surgery and strap $him face-down with $his legs spread. $He doesn't understand what's happening, since the anesthetics totally deprive $him of any sensation. $He's so drugged and drowsy with @@.red;surgical recovery@@ that it takes a while for $him to figure out what's happened. Eventually, though, $he realizes that $he's been reduced to the status of a genital null: the only remaining feature of $his newly smooth groin is a tiny soft hole, $his urethra.
-			<<if $activeSlave.scrotum > 0>>$He retains $his ballsack beneath this, though of course you can always remove that later if you decide to geld $him, too.<</if>>
-			$He almost passes out from @@.gold;sheer horror.@@ Instead, $he collapses and tries desperately to vomit. Fortunately, $he doesn't have anything to bring up. $He's reduced to impotent weeping and retching as $he begins to process the stress of having had $his parts cut off.
-			<<if $arcologies[0].FSRestart != "unset">>
-				Society @@.green;approves@@ of you stripping away everything from society's inferiors.
-				<<= FutureSocieties.Change("Eugenics", 3)>>
-			<</if>>
-		<</replace>>
-		<<set $activeSlave.chastityPenis = 0, $activeSlave.chastityVagina = 0, $activeSlave.dick = 0, $activeSlave.foreskin = 0, $activeSlave.ovaries = 0, $activeSlave.preg = -2, TerminatePregnancy($activeSlave), $activeSlave.counter.abortions++, $abortionsTotal++, $activeSlave.vagina = -1, $activeSlave.skill.vaginal = 0>>
-		<<run surgeryDamage($activeSlave, 10)>>
-		<<set $activeSlave.trust = Math.clamp($activeSlave.trust-100, -100, 100)>>
-	<</link>>
-<</if>>
-
-
-<<if hasAnyNaturalLegs($activeSlave) && ($activeSlave.indentureRestrictions <= 0) && ($seeExtreme == 1)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Clip $his Achilles tendons">>
-		<<replace "#introResult">>
-			You drag $him to the remote surgery and strap $him face-down with $his legs bare. $He doesn't understand what's coming for a while, even as $his lower legs are carefully cleaned and disinfected. $He can't see the approaching surgical manipulators, <<if !canSee($activeSlave)>>of course, <</if>>but eventually $he feels a tugging at $his lower legs even through the local anesthetic. $He gets one squeal of protest out before the surgery begins to apply healing agents. $He now requires special heels to walk, and will @@.mediumorchid;remember $his status@@ with every @@.red;painful@@ step $he takes. $He's barefoot, crawling, and @@.gold;frightened@@ for now, until you decide to give $him heels — if you ever do.
-		<</replace>>
-		<<set $activeSlave.heels = 1>>
-		<<set $activeSlave.devotion -= 5>>
-		<<run surgeryDamage($activeSlave, 10)>>
-		<<set $activeSlave.trust -= 20>>
-	<</link>>
-<</if>>
-
-<<if $activeSlave.lactation != 2 && $activeSlave.indentureRestrictions <= 0>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Implant $him with slow release lactation drugs">>
-		<<replace "#introResult">>
-			You <<if $activeSlave.devotion > 20>> guide the compliant $desc <<else>>have the $desc restrained and brought<</if>> to the remote surgery. The procedure is quick and @@.red;minimally invasive@@<<run surgeryDamage($activeSlave, 10)>>. Once the process is complete and the anesthesia subsides $he begins to feel a rising pressure within $his <<if $activeSlave.boobs > 2000 >>gigantic udders<<elseif $activeSlave.boobs > 900>>plentiful bosom<<elseif $activeSlave.boobs > 400>>healthy breasts<<else>>small breasts<</if>>. You then <<if $activeSlave.devotion > 20>>instruct $him to rest<<else>>have $him restrained<</if>> beside your desk until further notice. After a few hours $his increasing discomfort becomes obvious, and white droplets begin to appear across $his nipples and areola.
-			<<if $activeSlave.devotion > 20>>
-				<<if $activeSlave.fetish == "boobs">>
-					<<if $activeSlave.fetishKnown == 0>>
-						<<if $servantMilkers == 1>>
-							You escort $him to one of the many milkers installed in the penthouse and instruct $him in its use. The combined sensations of nipple stimulation and relief visibly overwhelm $him, and the resulting series of orgasms leaves $him exhausted.
-							@@.green;$He's a breast fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<<else>>
-							You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple, and the intense sensation of relief and stimulation overwhelms $him. It doesn't take long for $him to reach the first of a series of orgasms, leaving $him exhausted.
-							@@.green;$He's a breast fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<</if>>
-					<<else>>
-						<<if $servantMilkers == 1>>
-							You escort $him to one of the many milkers installed in the penthouse and instruct $him in its use. $His breast fetish makes the experience comparable to sex for &him, and the resulting series of orgasms leaves $him exhausted.
-						<<else>>
-							You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple. $His breast fetish makes the experience comparable to sex for $him, and the resulting series of orgasms leaves $him exhausted.
-						<</if>>
-					<</if>>
-					After allowing $him to rest for a bit you instruct $him to report for $his first assignment. As $he leaves your office $he can already feel the pressure returning to $his chest. @@.hotpink;As far as $he's concerned $his next milking can't come soon enough.@@
-					<<set $activeSlave.devotion += 5>>
-				<<else>>
-					<<if $servantMilkers == 1>>
-						You escort $him to one of the many milkers installed in the penthouse and instruct $him in its use. The sensation of relief is pleasant for $him, and $he moans softly as $his breasts drain into the apparatus.
-					<<else>>
-						You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple, and the sensation of relief is pleasant for $him. $He moans softly as you proceed to drain $his swollen breasts.
-					<</if>>
-					Once drained, you instruct $him to report for $his first assignment. As $he leaves your office $he can already feel the pressure returning to $his chest. $He appreciates that $his next milking will be a nice break from $his regular duties. @@.hotpink;It doesn't hurt that these breaks are both profitable for you, and pleasurable for $him.@@
-					<<set $activeSlave.devotion += 4>>
-				<</if>>
-			<<else>>
-				<<if $activeSlave.fetish == "boobs">>
-					<<if $activeSlave.fetishKnown == 0>>
-						<<if $servantMilkers == 1>>
-							You escort $him to one of the many milkers installed in the penthouse and attach $him to the device, leaving $his restraints in place. $He is still struggling when you activate the device, and the sudden sensation of relief catches $him off guard. A primal moan erupts from the bottom of $his throat, and it's obvious to both of you that $he's enjoying this in spite of $himself. $He spends the rest of $his milking trying and failing to hide the obvious pleasure $he's deriving from $his predicament.
-							@@.green;$He's a breast fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<<else>>
-							You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple. The sensation overwhelms $him almost immediately, and $his arms struggle against $his restraints as $he fights an impending orgasm. It's an amusing sight that goes on until $he is fully drained.
-							@@.green;$He's a breast fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<</if>>
-					<<else>>
-						<<if $servantMilkers == 1>>
-							You escort $him to one of the many milkers installed in the penthouse and attach $him to the device, leaving $his restraints in place. $His breast fetish soon overcomes $his obvious disdain for $his situation, and $he spends the milking red in the face, fighting an obviously impending orgasm.
-						<<else>>
-							You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple. With a sharp intake of breath $he begins to squirm, and $his breast fetish soon has $him moaning and writhing as $he fights an intense orgasm with tears of shame rolling down $his flushed cheeks.
-						<</if>>
-					<</if>>
-					As you summon <<if $HeadGirlID != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment, $he slumps dejectedly in $his restraints. $He is torn between the knowledge that $he is little more than livestock to you, and the disturbing realization that @@.hotpink;$he might enjoy $his new life as a human cow.@@
-					<<set $activeSlave.devotion += 4>>
-				<<else>>
-					<<if $servantMilkers == 1>>
-						You escort $him to one of the many milkers installed in the penthouse and attach $him to the device. $He struggles against $his restraints as you activate the device, drawing a brief pause and a look of confusion as $his breasts begin to drain. This only lasts a moment before $he returns to $his futile struggle, but $his milking continues regardless.
-					<<else>>
-						You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple as a look of confusion washes over $his features. $He then closes $his eyes tight and does $his best to ignore what's happening in a futile attempt to deny $his new reality, but you continue $his milking until $he is drained.
-					<</if>>
-					As you summon <<if $HeadGirlID != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment $he glares at you with disgust. $His resentment at the new burden you've saddled $him with is evident, and the regular milkings $he'll require will serve to remind $him of the @@.gold;disturbing fact that $he is little more than livestock to you.@@
-					<<set $activeSlave.devotion -= 4>>
-				<</if>>
-			<</if>>
-		<</replace>>
-		<<set $activeSlave.lactation = 2>>
-	<</link>>
-<</if>>
-
-<br>
-
-
-//...or show $him what it's like here for a slave//
-
-<<if $activeSlave.fetish != "mindbroken">>
-	/*
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Teach $him how to use $his throat">> TODO: write this <</link>>
-	*/
-
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Inseminate $him">>
-		<<replace "#introResult">>
-			You <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>lay the obedient $desc across your desk<<else>>force the resistant $desc to lie atop your desk<</if>> and gently <<if $PC.dick != 0>>slide your hard cock<<else>>push a special strap-on<</if>> <<if $activeSlave.vagina >= 0>>inside $his womanhood<<else>>up $his butt<</if>>. You take $him with care, enjoying $his body <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>and doing your best to give $him pleasure, too<<else>>without hurting $him<</if>>. <<if $PC.dick != 0>>You empty your balls inside $him, thrusting in as far as you can go<<else>>The strap-on contains a reservoir of faux ejaculate, and you give it to $him<</if>> as you climax. When $he feels the hot liquid jet into $him,
-			<<if $activeSlave.devotion > 20>>
-				<<if $activeSlave.fetish == "pregnancy">>
-					<<if $activeSlave.fetishKnown == 0>>
-						$he gasps with unaccustomed pleasure, and climaxes so strongly that $he cries a little from the pain in $his flexing abs. @@.green;$He's an impregnation fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$he climaxes, $his impregnation fetish displayed cutely on $his face.
-					<</if>>
-					As you let $him go, $he feels <<if $PC.dick != 0>>your<<else>>the fake<</if>> cum dripping out of $him, and $he @@.hotpink;feels like your property.@@
-					<<set $activeSlave.devotion += 5>>
-				<<else>>
-					$he gasps and does $his best to relax, accepting the flow. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't an impregnation fetishist, but $he is @@.hotpink;willing to submit@@ to have $his body used as your receptacle.
-					<<set $activeSlave.devotion += 4>>
-				<</if>>
-			<<else>>
-				<<if $activeSlave.fetish == "pregnancy">>
-					<<if $activeSlave.fetishKnown == 0>>
-						$he shudders with a sensation of perverted pleasure, and climaxes despite all $his feelings about the situation. @@.green;$He's an impregnation fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$he climaxes, $his impregnation fetish forcing $him to feel pleasure $his mind would prefer to reject.
-					<</if>>
-					As you let $him go, $he cries openly, <<if $PC.dick != 0>>your<<else>>the fake<</if>> cum dripping out of $him, and $he @@.hotpink;feels like your property.@@
-					<<set $activeSlave.devotion += 4>>
-				<<else>>
-					$he groans and struggles a little, disgusted to be filled by your fluids so immediately. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't an impregnation fetishist, and is @@.gold;afraid@@ that $he'll be treated as nothing more than your receptacle.
-					<<set $activeSlave.trust -= 4>>
-				<</if>>
-			<</if>>
-			<<= VCheck.Simple()>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-<<if $seePreg != 0>>
-	<<if isFertile($activeSlave) && $activeSlave.womb.length == 0 && $PC.dick != 0 && $activeSlave.eggType == "human">>
-		| <<link "Impregnate $him">>
-			<<replace "#introResult">>
-			You perform a careful medical examination to verify fertility, and then <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>lay the obedient $desc across your desk<<else>>restrain the resistant $desc in your office with $his ass in the air<</if>> and gently slide your hard cock <<if $activeSlave.mpreg == 1>>up $his butt<<else>>inside $his womanhood<</if>>. You take $him with care, enjoying $his body <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>and doing your best to give $him pleasure, too<<else>>without hurting $him<</if>>. You empty your balls inside $him, thrusting in as far as you can go as you climax. When $he feels the hot liquid jet into $him,
-			<<if $activeSlave.devotion > 20>>
-				<<if $activeSlave.fetish == "pregnancy">>
-					<<if $activeSlave.fetishKnown == 0>>
-						$he gasps with unaccustomed pleasure, and climaxes so strongly that $he cries a little from the pain in $his flexing abs. @@.green;$He's an impregnation fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$he climaxes, $his impregnation fetish displayed cutely on $his face.
-					<</if>>
-					As you let $him go, $he feels your cum dripping out of $him, and $he @@.hotpink;feels like your property.@@
-					<<set $activeSlave.devotion += 5>>
-				<<else>>
-					$he gasps and does $his best to relax, accepting the flow. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't an impregnation fetishist, but $he is @@.hotpink;willing to submit@@ to have $his body used as your receptacle.
-					<<set $activeSlave.devotion += 4>>
-				<</if>>
-			<<else>>
-				<<if $activeSlave.fetish == "pregnancy">>
-					<<if $activeSlave.fetishKnown == 0>>
-						$he shudders with a sensation of perverted pleasure, and climaxes despite all $his feelings about the situation. @@.green;$He's an impregnation fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$he climaxes, $his impregnation fetish forcing $him to feel pleasure $his mind would prefer to reject.
-					<</if>>
-					As you let $him go, $he cries openly, your cum dripping out of $him, and $he @@.hotpink;feels like your property.@@
-					<<set $activeSlave.devotion += 4>>
-				<<else>>
-					$he groans and struggles a little, disgusted to be filled by your fluids so immediately. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't an impregnation fetishist, and is @@.gold;afraid@@ that $he'll be treated as nothing more than your receptacle.
-					<<set $activeSlave.trust -= 4>>
-				<</if>>
-			<</if>>
-			<br><br>
-			Whenever you feel able, you drain your balls into $his <<if $activeSlave.mpreg == 1>>ass<<else>>cunt<</if>>, only releasing $him for other duties when scans verify a fertilized ovum.
-			<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant < -50>>
-				$He didn't properly understand the scans, so $he just thought it was bound, forced sex; $he won't realize what happened for some weeks at least.
-			<<elseif $activeSlave.fetish == "pregnancy">>
-				$He is thrilled to imagine $he might be pregnant.
-			<<else>>
-				$He wasn't sure what this was all about, but $he has $his suspicions.
-			<</if>>
-			<<if $activeSlave.mpreg == 1>>
-				<<= VCheck.Anal()>>
-			<<else>>
-				<<= VCheck.Vaginal()>>
-			<</if>>
-			<<if $arcologies[0].FSRestart != "unset" && $eugenicsFullControl != 1>>
-				The Societal Elite @@.green;disapprove@@ of this breach of eugenics.
-				<<set $failedElite += 5>>
-			<<elseif $arcologies[0].FSGenderFundamentalist != "unset" && $activeSlave.mpreg == 0>>
-				Society @@.green;approves@@ of your promptly putting a new slave in $him; this advances the idea that all slaves should bear their masters' babies.
-				<<= FutureSocieties.Change("GenderFundamentalist", 2)>>
-			<<elseif $arcologies[0].FSGenderFundamentalist != "unset">>
-				Society @@.red;is disgusted@@ by you promptly knocking up $his ass; babies come from women, not men.
-				<<= FutureSocieties.Change("GenderFundamentalist", -2)>>
-			<</if>>
-			<</replace>>
-			<<set $activeSlave.preg = 1>>
-			<<set $activeSlave.pregType = setPregType($activeSlave)>>
-			<<set $activeSlave.pregWeek = 1>>
-			<<set $activeSlave.pregKnown = 1>>
-			<<set $activeSlave.pregSource = -1>>
-			<<set WombImpregnate($activeSlave, $activeSlave.pregType, -1, 1)>>
-		<</link>>
-	<</if>>
-<</if>>
-
-<<if ($activeSlave.vagina == 0)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Tie $him up and take $his virginity">>
-		<<replace "#introResult">>
-			<<if ($activeSlave.devotion < -10)>>
-				<<if isAmputee($activeSlave)>>
-					You secure $his struggling, screeching body to the couch next to your desk with $his vagina exposed.
-				<<else>>
-					You cuff $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and secure $his struggling, screeching body to the couch next to your desk with $his legs spread.
-				<</if>>
-				$He wriggles and protests as you take $his virginity. $He's certainly entered your service in a way that colors $his impression of you @@.mediumorchid;with hatred@@ and @@.gold;fear.@@ @@.lime;$His tight little pussy has been broken in.@@
-				<<set $activeSlave.devotion -= 5>>
-			<<else>>
-				<<if isAmputee($activeSlave)>>
-					You secure $his unresisting body to the couch next to your desk with $his vagina exposed.
-				<<else>>
-					You cuff $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and secure $his unresisting body to the couch next to your desk with $his legs spread.
-				<</if>>
-				$He writhes and moans as you enter $his virgin pussy. You might not have even had to restrain $him for this, but being tied up and deflowered sends $him a message. $He's certainly entered your service in a way that colors $his impression of you @@.hotpink;with pain@@ and @@.gold;fear.@@ @@.lime;$His tight little pussy has been broken in.@@
-				<<set $activeSlave.devotion += 5>>
-			<</if>>
-			<<= VCheck.Vaginal()>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-
-<<if ($activeSlave.devotion < -10) && ($activeSlave.anus == 0)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Tie $him up and rape $his virgin ass">>
-		<<replace "#introResult">>
-			<<if isAmputee($activeSlave)>>
-				You secure $his struggling, screeching body to the couch next to your desk with $his butt sticking out.
-			<<else>>
-				You cuff $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and secure $his struggling, screeching body to the couch next to your desk so that $he's straddling the back of the couch with $his butt sticking out.
-			<</if>>
-			$He wriggles and protests until $he feels the lubricated head of <<if $PC.dick != 0>>your dick<<else>>a strap-on<</if>> pushing against $his virgin butthole, at which point $he desperately tries to break free. $He fails, but $he never stops writhing and begging you to take <<if $PC.dick != 0>>your cock<<else>>the phallus<</if>> out of $his asshole. $He's certainly entered your service in a way that colors $his impression of you @@.mediumorchid;with hatred@@ and @@.gold;fear.@@ @@.lime;$His tight little ass has been broken in.@@
-		<</replace>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<<set $activeSlave.anus = 1>>
-		<<= VCheck.Anal()>>
-	<</link>>
-<<elseif ($activeSlave.anus == 0)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Tie $him up and break $his ass in">>
-		<<replace "#introResult">>
-			<<if isAmputee($activeSlave)>>
-				You secure $his unresisting body to the couch next to your desk with $his butt sticking out.
-			<<else>>
-				You cuff $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and secure $his unresisting body to the couch next to your desk so that $he's straddling the back of the couch with $his butt sticking out.
-			<</if>>
-			$He lies obediently still until $he feels the lubricated head of your cock pushing against $his virgin butthole, at which point $he starts in surprise. When the pain of the buttfuck gets to be too much $he begs you to use $his <<if $activeSlave.dick == 0>>pussy<<else>>mouth<</if>> instead. $He gasps and cries as you slowly push your cock all the way up $his tight little butt, and in no time you fill $his rectum with cum. $He's certainly entered your service in a way that colors $his impression of you @@.mediumorchid;with pain@@ and @@.gold;fear.@@ @@.lime;$His tight little ass has been broken in.@@
-		<</replace>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<<set $activeSlave.anus = 1>>
-		<<= VCheck.Anal()>>
-	<</link>>
-<</if>>
-
-
-<<if $activeSlave.fetish != "mindbroken">>
-<br>&nbsp;&nbsp;&nbsp;&nbsp;
-<<link "Break $him in publicly">>
-	<<replace "#introResult">>
-		You <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>order the obedient slave to follow you<<else>>bring the resistant slave<</if>> out into the public areas below your penthouse, shove the nude $desc over a convenient handrail, and stick <<if $PC.dick != 0>>your dick<<else>>a strap-on<</if>>
-		<<if $activeSlave.vagina >= 0>>
-			<<if $activeSlave.vagina >= 3>>
-				inside $his gaping cunt, which is loose enough that the rough treatment doesn't bother $him physically.
-			<<elseif $activeSlave.vagina >= 2>>
-				inside $his accommodating pussy, which is welcoming enough that the rough insertion doesn't hurt $him physically.
-			<<else>>
-				inside $his tight little pussy, making the poor $girl writhe with discomfort.
-			<</if>>
-		<<else>>
-			<<if $activeSlave.anus >= 3>>
-				inside $his lewd anal slit, which is so used to being fucked that the rough treatment doesn't bother $him physically.
-			<<elseif $activeSlave.anus >= 2>>
-				up $his welcoming butt, which is loose enough that the rough insertion doesn't hurt $him physically.
-			<<else>>
-				up $his poor little butthole, making the poor $girl struggle desperately with anal pain.
-			<</if>>
-		<</if>>
-		You take $him, hard, while passersby stare at $him as $he gets pounded over a rail.
-		<<if $activeSlave.devotion > 20>>
-			<<if $activeSlave.fetish == "humiliation">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$He struggles under the stares, blushing furiously as $he takes a fuck in the open. $He orgasms despite $himself; @@.green;$he's a humiliation fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					As a humiliation fetishist, $he gets off on the stares, blushing cutely as $he takes a fuck in public.
-				<</if>>
-				$He follows you back inside your penthouse, leaking <<if $PC.dick != 0>>your cum<<else>>lube<</if>>, @@.hotpink;feeling like a slut.@@
-				<<set $activeSlave.devotion += 5>>
-			<<else>>
-				$He takes the fuck obediently, and does $his very best to ignore the stares. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't a humiliation fetishist, but $he is @@.hotpink;willing to submit@@ to being used as a sex slave in public.
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<<else>>
-			<<if $activeSlave.fetish == "humiliation">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$His struggles slowly diminish as the stares have a horribly embarrassing effect on $him. $He orgasms and then starts to cry, realizing what just happened; @@.green;$he's a humiliation fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					As a humiliation fetishist, $he can't stop $himself from getting off on being fucked in public.
-				<</if>>
-				As you pull $him back towards your penthouse, $he leaks <<if $PC.dick != 0>>your cum<<else>>lube<</if>> and @@.hotpink;feels like a slut.@@
-				<<set $activeSlave.devotion += 4>>
-			<<else>>
-				$He takes the fuck unwillingly, crying and doing $his best to ignore the stares. <<if $activeSlave.fetishKnown == 0>>It seems $he<<else>>$He<</if>> isn't a humiliation fetishist, and is @@.gold;rapidly learning what it means@@ to be your sex slave.
-			<<set $activeSlave.trust -= 4>>
-			<</if>>
-		<</if>>
-		<<= VCheck.Simple()>>
-	<</replace>>
-<</link>>
-<</if>>
-
-
-<<if ($activeSlave.devotion > 20)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Give $him a chance to impress">>
-		<<replace "#introResult">>
-			<<set _num = random(1,100)>>
-			$He seems reasonably obedient, so you give $him a chance to impress. You remain seated and offer $him your <<if $PC.dick != 0>>half-erect cock<<if $PC.vagina != -1>> and ready pussy<</if>><<else>>pussy, wet with arousal at the prospect of breaking in a new slut<</if>>. $He comes over obediently and gets <<if hasBothLegs($activeSlave)>>on $his knees<<else>>into position<</if>>. $He works hard and is clearly doing $his absolute best to please, so you let $him finish you with $his mouth. <<if $PC.dick != 0>><<if _num > 20>>$He <<if !canSee($activeSlave)>>looks up at<<else>>angles $his head towards<</if>> you as $he swallows<<elseif _num > 40>>$He even swallows your cum without being told<<elseif _num > 60>>$He smiles sloppily as your seed slowly dribbles from $his mouth and down $his chin<<elseif _num > 80>>$He struggles to contain your seed and makes a mess all over $his face as a result<<else>>$He struggles to contain your seed with $his mouth and it splatters over the floor as a result<</if>><<else>>$He worked $his tongue hard and did $his best<</if>>. You spend the rest of the day with $him <<if hasAnyLegs($activeSlave)>>kneeling<<else>>resting<</if>> on a cushion next to your chair. $He learns obedience, but also learns that you are @@.mediumaquamarine;fair@@ and @@.hotpink;reasonable.@@
-		<</replace>>
-		<<set $activeSlave.devotion += 4>>
-		<<set $activeSlave.trust += 4>>
-		<<set $activeSlave.counter.oral += 1>>
-		<<set $oralTotal += 1>>
-	<</link>>
-<</if>>
-
-
-<<if $activeSlave.fetish != "mindbroken">>
-
-<<if $activeSlave.bellyPreg >= 5000>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Make sure $he knows pregnant $women can still enjoy sex">>
-		<<replace "#introResult">>
-			You introduce $him to obedience and proper manners regarding $his <<= WrittenMaster($activeSlave)>> before sending $him off for a physical. That night, $he's returned to your room, and finds you doing business on a tablet in bed. $He looks doubtful, but obeys when you direct $him to get into bed<<if $PC.dick == 0>>, even after $he realizes you're wearing a strap-on<</if>>. You turn out the light and spoon $him from behind, kissing $his neck and ears, cupping $his swollen breasts, and running your hands across $his pregnant belly with its taut $activeSlave.skin skin. $He's awkward at first but $his body responds to the tenderness. Before long $he's humping $his pussy back and forth against <<if $PC.dick != 0>>your cock<<else>>the strap-on<</if>>. You begin to gently work <<if $PC.dick != 0>>your dickhead<<else>>its tip<</if>> up $his used pussy. $He's unsure of $himself, but you keep $him nice and relaxed. After several minutes of gentle loving, $he's nothing but a satisfied puddle in your arms. $He believes that $he can @@.mediumaquamarine;trust@@ you won't harm $him or $his child<<if $activeSlave.pregType > 1>>ren<</if>>.
-		<</replace>>
-		<<set $activeSlave.trust += 5>>
-		<<= VCheck.Vaginal()>>
-		<<if $activeSlave.fetish == "none" && random(1,100) > 60>>
-			<<set $activeSlave.fetish = "pregnancy", $activeSlave.fetishStrength = 10>>
-		<</if>>
-	<</link>>
-<</if>>
-
-<br>&nbsp;&nbsp;&nbsp;&nbsp;
-<<link "Tease $his nipples">>
-	<<replace "#introResult">>
-		You <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>instruct the obedient $desc<<else>>force the reluctant $desc<</if>> to lie atop your desk in front of you and go back to business. After a few minutes, you extend an idle hand and begin to fondle $his <<if $activeSlave.boobs > 2000>>massive tits<<elseif $activeSlave.boobs > 400>>healthy breasts<<else>>cute little boobs<</if>>, continuing your work with your other hand. You quickly focus your fiddling on $his $activeSlave.nipples nipples, the stimulation
-		<<if $activeSlave.nipples == "fuckable">>
-			<<if ($activeSlave.devotion > 20)>>quickly engorging them around your fingers<<else>>slowly engorging them around your fingers despite $his feelings<</if>>.
-		<<else>>
-			bringing them <<if ($activeSlave.devotion > 20)>>quickly erect<<else>>slowly erect despite $his feelings<</if>>.
-		<</if>>
-		<<if $activeSlave.devotion > 20>>
-			<<if $activeSlave.fetish == "boobs">>
-				<<if $activeSlave.fetishKnown == 0>>
-					The slave quickly becomes aroused. Before long, an orgasm convulses $his entire body, jiggling the feminine flesh under your hand delightfully. @@.green;$He's a breast fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					$His breast fixation makes this teasing quite pleasurable for $him, almost as good as a handjob. Before long, an orgasm convulses $his entire body, jiggling the feminine flesh under your hand delightfully.
-				<</if>>
-				When you tell $him to go, $he carefully rolls off your desk in such a way that @@.hotpink;$his nipples graze your cheek.@@
-				<<set $activeSlave.devotion += 5>>
-			<<else>>
-				$He accepts being treated as a desktop stress relief toy. $He's no breast fetishist, but $he's @@.hotpink;willing to be used@@ as a sex object.
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<<else>>
-			<<if $activeSlave.fetish == "boobs">>
-				<<if $activeSlave.fetishKnown == 0>>
-					The slave slowly becomes aroused despite $his reluctance. Soon, $he can no longer hide $his horniness and begins to pant, openly enjoying the sensations. @@.green;$He's a breast fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					The slave slowly becomes aroused despite $his reluctance. As a breast fetishist, playing with $his nipples is an excellent way to overcome $his petty resistance to being a sex object.
-				<</if>>
-				When you tell $him to go, $he gets off your desk gingerly, since $he's almost painfully aroused. @@.hotpink;$He feels like quite a slut@@ for wanting a fuck this badly after a little nipple play.
-				<<set $activeSlave.devotion += 4>>
-			<<else>>
-				$He expects something worse to happen immediately, and slowly relaxes when $he realizes that $he's nothing more than your desktop stress relief toy, at least for now. $He experiences the usual effects of nipple play, but seems @@.gold;somewhat frightened@@ to be dehumanized so thoroughly.
-				<<set $activeSlave.devotion -= 4>>
-			<</if>>
-		<</if>>
-	<</replace>>
-<</link>>
-
-<<if $PC.vagina != -1 && $activeSlave.dick > 0 && canAchieveErection($activeSlave)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Dominate $his penis and demonstrate $his place">>
-		<<replace "#introResult">>
-			As you inspect $him, you take note of $his fully functional <<if $activeSlave.dick == 1>>tiny dick<<elseif $activeSlave.dick == 2>>cute dick<<elseif $activeSlave.dick == 3>>dick<<elseif $activeSlave.dick == 4>>big dick<<elseif $activeSlave.dick == 5>>impressive dick<<elseif $activeSlave.dick == 6>>huge dick<</if>>. You roughly push $him up against a wall and begin fondling $his penis, grinning at the look of panic growing on $his face.
-			<<if $activeSlave.dick == 1>>
-				You scoff at $him as $his micropenis barely fills your palm.
-			<<elseif $activeSlave.dick == 2>>
-				You laugh at $him as $his small penis fills your palm.
-			<<elseif $activeSlave.dick == 3>>
-				You nod at $him as $his penis fills your hand.
-			<<elseif $activeSlave.dick == 4>>
-				You smirk at $him as $his big penis fills your hand.
-			<<elseif $activeSlave.dick == 5>>
-				You smile widely at $him, a dangerous look in your eyes, as you bring another hand to $his impressive dick.
-			<<elseif $activeSlave.dick == 6>>
-				You grin sadistically at $him as $his massive dick fills both of your hands.
-			<</if>>
-			As $he begins to moan with lust, you grip down tightly and force $him to the floor. You straddle $him and lower your dripping pussy onto $his face<<if $PC.dick != 0>>, your erect cock coming to rest on $his forehead<</if>>. You continue stroking your toy's rod as $he eagerly begins eating you out. As $his cock begins to throb, anticipating $his upcoming orgasm, you quickly bind the base of $his penis, denying $his release. You grind your cunt into $his face, telling $him that YOU are the one who'll be orgasming here, not $him. Only once you have initiated the new slave by soaking $his face in your cum do you release $his dick and lean back to avoid the coming blast. A few strokes later and your hand is coated in $his cum. You turn around and order the exhausted $girl to clean $his cum off your hand<<if $PC.dick != 0>> and to finish off $his twitching dick<</if>>; $he @@.hotpink;complies meekly,@@ knowing you are the @@.gold;dominant force@@ in $his life now.
-		<</replace>>
-		<<set $activeSlave.devotion += 5>>
-		<<set $activeSlave.trust -= 3>>
-	<</link>>
-<</if>>
-
-<<if $PC.belly >= 3000 && hasAnyArms($activeSlave)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Make $him worship your pregnancy">>
-		<<replace "#introResult">>
-			You aggressively approach $activeSlave.slaveName, forcing your pregnancy directly into $his <<if $activeSlave.height > 175>>stomach<<elseif $activeSlave.height < 155>>face<<else>>chest<</if>> until $he has no choice but to be pushed to the ground. Standing over $him, staring $him down as $he peeks around your firm globe of a middle, you order $him to worship your pregnancy.
-			<<if $activeSlave.fetish == "pregnancy">>
-				$He complies eagerly. $He begins with sucking your popped navel before running $his tongue across the taut, smooth surface of your pregnancy. Once $he has finished with your belly, $he lowers $himself under it to begin work on your needy pussy. Before long, $his overzealous efforts have you quaking in pleasure, rousing your child<<if $PC.pregType > 1>>ren<</if>>. Once $he finishes you off, $he returns to rubbing your belly, soothing your rowdy child<<if $PC.pregType > 1>>ren<</if>> and @@.hotpink;solidifying $his place@@ beneath you.
-				<<set $activeSlave.devotion += 15>>
-			<<else>>
-				$He shifts $his gaze between your middle and your face, not sure what to do. Losing patience, you toss a tube of cream at $him. $He shakily massages it onto your stretched skin, missing spots, much to your pleasure. With reason, you force $him onto $his back, turn around, and plant your needy cunt directly onto $his face. Struggling to breath under your weight, $he begins eating you out in desperation. After coaxing $him to massage your belly as $he does, you quickly climax across $his face and gently lift yourself off the coughing $girl. $He now @@.hotpink;understands $his place in life@@ and is @@.gold;terrified@@ about what $he will have to do if $he wants to survive.
-				<<set $activeSlave.devotion += 5>>
-				<<set $activeSlave.trust -= 5>>
-			<</if>>
-		<</replace>>
-	<</link>>
-	<<if $PC.preg >= 28>>
-		<<if $PC.pregMood == 2 && $PC.vagina > 0 && canPenetrate($activeSlave)>>
-			|
-			<<link "$He has a dick and you need it">>
-				<<replace "#introResult">>
-					You aggressively approach $activeSlave.slaveName, forcing your pregnancy directly into $his <<if $activeSlave.height > 175>>stomach<<elseif $activeSlave.height < 155>>face<<else>>chest<</if>> until $he has no choice but to be pushed to the ground. You quickly straddle $his face, forcing your oozing cunt over $his mouth as you eagerly stroke $his cock to full length.
-					<<if $activeSlave.fetish == "pregnancy">>
-						$He groans with disappointment as your pregnant pussy leaves $his reach, though $his displeasure is short lived as you greedily take $his entire dick into your aching snatch. You ride $him mercilessly, frequently smacking $him with your heavy belly. $He loves every minute of it, especially when $he feels your body tense up as $he lets loose $his load deep into you. Where most slaves would be begging for mercy, $he @@.hotpink;eagerly complies@@ as you adjust yourself and begin round two. You don't know what came over you, but when you wake up, you find $he's resting peacefully under your gravid mass. <<if $activeSlave.fetishKnown == 0>>It seems $he likes @@.green;being a pregnant _womanP's plaything.@@<<set $activeSlave.fetishKnown = 1>><<else>>You knew $he had a pregnancy fetish and the look on $his face confirms it.<</if>> A kick from within startles you from your thoughts; it would appear your child<<if $PC.pregType > 1>>ren<</if>> agree<<if $PC.pregType == 1>>s<</if>> that you'll have to have another ride sometime.
-						<<set $activeSlave.devotion += 15>>
-					<<else>>
-						$He coughs as your pregnant pussy vacates $his face, though $his relief is short lived as you greedily slam yourself down onto $his waiting dick. You ride $him mercilessly, frequently smacking $him with your heavy belly. $He hates every minute of it, choosing to alternate between begging you to stop and just openly weeping. You cum hard as you watch the look on $his face as $he unwillingly cums deep inside you. $He cries out in protest as you continue raping $him, but you don't care. All that matters is your satisfaction. This continues until you pass out from orgasmic exhaustion with $him still inside you. You are eventually awoken by $his desperate struggle to escape from beneath your gravid mass; $he quickly regrets $his choices as you remount $him for one last go. $He now @@.hotpink;better understands $his place as a toy@@ and is @@.gold;terrified@@ of your insatiable lust.
-						<<set $activeSlave.devotion += 5, $activeSlave.trust -= 15>>
-					<</if>>
-					<<set $activeSlave.counter.penetrative += 5, $penetrativeTotal += 5>>
-					<<set $activeSlave.counter.oral++, $oralTotal++>>
-				<</replace>>
-			<</link>>
-		<<elseif $PC.pregMood == 1 && $PC.lactation > 0>>
-			|
-			<<link "Take $him to your breast">>
-				<<replace "#introResult">>
-					You calmly approach $activeSlave.slaveName, pulling $him into a soothing embrace against your
-					<<if $PC.boobs >= 1400>>
-						enormous<<if $PC.boobsImplant > 0>>, fake<</if>>, milk laden breasts.
-					<<elseif $PC.boobs >= 1200>>
-						huge<<if $PC.boobsImplant > 0>>, fake<</if>>, milk laden breasts.
-					<<elseif $PC.boobs >= 1000>>
-						large<<if $PC.boobsImplant > 0>>, fake<</if>>, milk laden breasts.
-					<<elseif $PC.boobs >= 800>>
-						big, milk laden breasts.
-					<<elseif $PC.boobs >= 650>>
-						milk laden breasts.
-					<<elseif $PC.boobs >= 500>>
-						small, milk laden breasts.
-					<<elseif $PC.boobs >= 300>>
-						tiny, milk laden breasts.
-					<<else>>
-						milk swollen chest.
-					<</if>>
-					$He tries to squirm away from the moist spot growing under $his cheek, but you reveal your nipple and carefully direct $his mouth over it.
-					<<if $activeSlave.fetish == "pregnancy">>
-						Slowly $he begins to suckle from your swollen breast. You gently brush $his head as you try to hold back your pleasure — a wasted effort, as a hand sneaks its way to your <<if $PC.dick != 0>>growing erection and enthusiastically begins pumping away<<else>>wet pussy and enthusiastically begins rubbing your clit<</if>>. You clutch your pervy $girl closer to you as $he caresses your pregnancy with one hand and gets you off with the other. Before long you find yourself bucking your hips with lust, a queue for you to release $him from your nipple so $he may slide down your gravid dome of a belly to finish you off. Happy to serve $his pregnant <<= WrittenMaster($activeSlave)>>, $he returns to your chest, happy to relieve you of the pressure building in your neglected breast. <<if $activeSlave.fetishKnown == 0>>Judging by that show, @@.green;$he savors getting to be with a pregnant _womanP.@@<<set $activeSlave.fetishKnown = 1>><<else>>You knew $he had a pregnancy fetish and $his eagerness to serve a pregnant _womanP proves that.<</if>> A kick from within startles you from your thoughts; as you reach to soothe your child<<if $PC.pregType > 1>>ren<</if>>, you find your new slave @@.mediumaquamarine;already doting on them.@@ $He's already starting to @@.hotpink;show understanding of $his place.@@
-						<<set $activeSlave.devotion += 15, $activeSlave.trust += 15>>
-					<<elseif $activeSlave.fetish == "boobs">>
-						Eagerly $he begins to suckle from your swollen breast. You gently brush $his head as you try to hold back your pleasure — a wasted effort, as a hand sneaks its way to your neglected breast. $He massages it, careful not to encourage your lactation too much, as $he greedily sucks you dry. $He wastes no time in swapping to your other nipple, shifting $his ministrations to the one the just left. By the time your reserves are tapped out, both you and $he are quite content. You permit $him to rest against your chest for a little before you send $him on $his way. <<if $activeSlave.fetishKnown == 0>>Judging by $his enthusiasm, @@.green;$he savors getting $his mouth close to a pair of boobs.@@<<set $activeSlave.fetishKnown = 1>><<else>>You knew $he had a breast fetishist and $his eagerness to lighten a lactating _womanP proves that.<</if>> A kick from within startles you from your thoughts; you pat your gravid middle, reassuring your child<<if $PC.pregType > 1>>ren<</if>> that you'll make sure to save some milk for them. $He's already starting to @@.hotpink;show understanding of $his place@@ and even @@.mediumaquamarine;beginning to build trust@@ with you.
-						<<set $activeSlave.devotion += 15, $activeSlave.trust += 15>>
-					<<else>>
-						Reluctantly $he begins to suckle from your swollen breast. You gently brush $his head as you try to hold back your pleasure, but it is too much. As $he drinks deeper, you begin moaning with relief. At first $he tenses at <<if canHear($activeSlave)>>the sound<<else>>your body's shuddering<</if>>, fearing punishment, but soon realizes you have no intent on @@.mediumaquamarine;harming $him.@@ $He allows you to dote over $him as if $he were <<if $activeSlave.father == -1 || $activeSlave.mother == -1>>a<<else>>your<</if>> child, carefully moving to your other breast once the first runs dry. As $he drinks, $he begins to massage your taut middle, $his touch soft and gentle. When you both finish, you push $him <<if hasBothLegs($activeSlave)>>to $his feet<<else>>into an upright position<</if>> and send $him on $his way. @@.hotpink;$He stays and offers a hand to help you to your feet.@@ You are surprised by this display; it might be obedience, but $he also may view you in your gravid state as someone weak. As $he helps you back to your desk, $he shoulders all of your weight. It would appear $he is putting you first, for now.
-						<<set $activeSlave.devotion += 5, $activeSlave.trust += 5>>
-					<</if>>
-					<<set $PC.lactationDuration = 2>>
-				<</replace>>
-			<</link>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $activeSlave.belly + $PC.belly <= 2000>> /* got to be able to get close without bumps getting in the way */
-	<<if $PC.boobs >= 800 && $activeSlave.boobs <= $PC.boobs-200 && $activeSlave.boobs >= 300>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<link "Let your ample bust dominate $his pathetic one">>
-			<<replace "#introResult">>
-				You quickly strip off your shirt and draw close to $activeSlave.slaveName. You grab $him as $he attempts to back away and pull $his tits directly into your own. Both sets of <<if canSee($activeSlave)>>eyes lock onto their pair of breasts<<else>>tits push out against each other<</if>>, or they would have, had yours not utterly eclipsed their rival. Scoffing, you release the confused $girl and walk away; leaving $him cupping $his own breasts and feeling uncertain about $himself. $He gets caught up in how small they really are, and how much @@.hotpink;lesser@@ than $his <<= WrittenMaster($activeSlave)>> $he is.
-				<<if $activeSlave.fetish == "boobs">>
-					<<if $activeSlave.fetishKnown == 0>>
-						You did note one thing when your breasts touched: $his nipples <<if $activeSlave.nipples == "fuckable">>tightened around yours<<else>>got very hard<</if>>. @@.green;Looks like $he's a breast fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<</if>>
-				<</if>>
-			<</replace>>
-			<<set $activeSlave.devotion += 5>>
-		<</link>>
-	<</if>>
-
-	<<if canAchieveErection($activeSlave) && canSee($activeSlave) && $PC.dick >= 4 && $PC.dick > $activeSlave.dick && $activeSlave.trust >= -20>>
-		<<if $activeSlave.energy > 95 || ($activeSlave.attrXX > 50 && $activeSlave.behavioralFlaw != "hates women" && $PC.boobs >= 400) || ($activeSlave.attrXY > 50 && $activeSlave.behavioralFlaw != "hates men" && $PC.boobs < 300) || ($PC.belly >= 1500 && $activeSlave.fetish == "pregnancy") || ($PC.boobs >= 1400 && $activeSlave.fetish == "pregnancy")>>
-			<br>&nbsp;&nbsp;&nbsp;&nbsp;
-			<<link "Show $him what a real erection looks like">>
-				<<replace "#introResult">>
-					$His arousal is obvious with the hard-on $he is sporting, but $he still has much to learn about size. You pop your own dick out and draw close to $activeSlave.slaveName, grabbing and pulling $him close enough for your erections to touch. Both sets of eyes lock onto the crossed swords, or more appropriately, your sword and $his dagger. You give the confused $girl plenty of time to internalize what a real dick looks like as you @@.hotpink;thoroughly emasculate@@ $him.
-				<</replace>>
-				<<set $activeSlave.devotion += 5>>
-				<span id="introResult2">
-					<br><<link "Push $his head down">>
-						<<replace "#introResult2">>
-							You place a hand on $his head and guide $him
-							<<if $activeSlave.fetish == "cumslut">>
-								downwards, right onto your penis. As you slip deeper into your throat, you find $him hungrily sucking your cock like an addict looking for their next hit. You let $him drain every last drop from your balls before pulling out. $He licks the last bit off the tip before opening wide again, @@.hotpink;begging you for more.@@ @@.green;You have quite the cum fiend on your hands.@@
-								<<set $activeSlave.fetishKnown = 1>>
-							<<else>>
-								downwards. $He stares at your penis before hesitantly slipping it into $his mouth
-								<<if $activeSlave.skill.oral >= 100>>
-									and practically sucking the life out of you; it stands to reason that $he may have known exactly what $he was doing this whole time.
-								<<elseif $activeSlave.skill.oral > 60>>
-									and giving you a rather surprising blowjob; $he's well on $his way already, it seems.
-								<<elseif $activeSlave.skill.oral > 30>>
-									and giving you a forgettable blowjob; $he'll learn how to properly suck dick under your ownership.
-								<<else>>
-									and giving you an awful blowjob; $he'll get plenty of practice under your ownership.
-								<</if>>
-								Only once $he has swallowed the last of your cum do you allow $him to clean $himself up and @@.hotpink;reflect on what $he just did.@@
-							<</if>>
-							<<set $activeSlave.devotion += 5>>
-							<<set $activeSlave.counter.oral++, $oralTotal++>>
-						<</replace>>
-					<</link>>
-				</span>
-			<</link>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if ($activeSlave.accent >= 3) && ($activeSlave.anus < 2) && ($activeSlave.intelligence+$activeSlave.intelligenceImplant <= 50) && ($activeSlave.devotion < 10) && (canSee($activeSlave))>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Force understanding of $his situation past the language barrier">>
-		<<replace "#introResult">>
-			With a rudimentary understanding of $language, at best, it's doubtful that $he understands exactly what $his immediate future is likely to entail. You resolve to let $him in on the secret, and stand up, drawing $his attention to your hands with gestures. You point first at $him, and then at your left hand, which you form into a hole shape. Then you point at yourself with your left hand, and then at your right hand, whose index and middle fingers you extend, straight and together. Then, you take your left hand hole and your right hand phallus, and fuck the former with the latter, hard enough that the sex act depicted is obviously fun for your right hand only. $He watches raptly, and when comprehension dawns across $his face, @@.gold;$he starts to sob.@@
-			<<if $activeSlave.vagina > -1>>
-				$He indicates $his vagina tentatively, almost hopefully, but you shake your head, and emphasize the circular, anus-like shape of your left hand. $He cries harder.
-			<<else>>
-				$He wilts, pressing $his thighs together in an unconscious attempt to shield $his anus from its future as a target for $his <<= WrittenMaster($activeSlave)>>'s attentions.
-			<</if>>
-		<<set $activeSlave.trust -= 10>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-<<if $activeSlave.inflation == 0 && hasAnyArms($activeSlave) && canTaste($activeSlave) && $arcologies[0].FSHedonisticDecadenceResearch == 1>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Offer $him a slave-food cookie">>
-		<<replace "#introResult">>
-			You pull a big plate of specially formulated cookies from your desk, place it before $him and invite $him to have some. $He looks between the plate and you, unsure of what to make of this development, but eventually grabs one and
-			<<if $activeSlave.behavioralFlaw == "gluttonous">>
-				shoves it into $his mouth. $He nearly chokes in surprise at the taste before greedily reaching for another.
-			<<elseif $activeSlave.behavioralFlaw == "anorexic">>
-				nibbles the edge of it. $He gasps in surprise at the taste before greedily shoving the entire thing into $his mouth and reaching for another.
-			<<else>>
-				takes a bite. $He gasps in surprise at the taste before greedily shoving the entire thing into $his mouth and reaching for another.
-			<</if>>
-			In minutes, $he's managed to devour every last cookie on the plate. Before $he can even pout that they're all gone, you place another pair of plates <<if canSmell($activeSlave)>>under $his nose<<else>>in front of $him<</if>>. $He promptly rushes for them,
-			<<if $activeSlave.behavioralFlaw == "gluttonous">>
-				paying no mind to $his bloated belly bumping into your desk, and resumes stuffing $himself.
-			<<elseif $activeSlave.behavioralFlaw == "anorexic">>
-				only to recoil in shock when $his bloated belly
-				<<if $activeSlave.belly >= 1000>>
-					reaches your desk earlier than expected.
-				<<else>>
-					pushes into your desk.
-				<</if>>
-				$He gropes it uncomfortably before being overwhelmed by the addictive substance and going back to stuffing $himself.
-			<<else>>
-				barely noticing $his bloated belly
-				<<if $activeSlave.belly >= 1000>>
-					hanging heavier than ever,
-				<<else>>
-					bumping into your desk,
-				<</if>>
-				and resumes stuffing $himself.
-			<</if>>
-			By the time $he's done, $his belly
-			<<if $activeSlave.belly >= 1000>>
-				is positively heaving. You can clearly see the defined mass of $his stomach resting atop $his swollen abdomen.
-			<<else>>
-				is positively huge. You can clearly see $his swollen stomach at the top of the mass of food inside $him.
-			<</if>>
-			$He collapses onto $his rear and belches loudly.
-			<<if $arcologies[0].FSDegradationist != "unset">>
-				It doesn't take long for the tainted food to do its thing. $He groans loudly and clutches $his bulging midriff, $his body in severe distress thanks to the modified slave food. $He rolls onto $his back and does everything $he can to soothe the turmoil growing inside $him. You know it won't do any good, but $he doesn't. $He'll spend however long it takes $his body to digest the food in anguish; $he'll regret ever touching the stuff. But you know better, $his growing addiction will have $him scarfing it down at every chance, but for now, all $he can do is @@.mediumorchid;blame you@@ and @@.gold;curse your trickery.@@
-				<<set $activeSlave.devotion -= 20, $activeSlave.trust -= 20>>
-			<<else>>
-				<<if $activeSlave.behavioralFlaw == "gluttonous">>
-					$He @@.mediumaquamarine;happily pats $his full belly;@@ $he's going to @@.hotpink;like it here.@@
-					<<set $activeSlave.devotion += 10, $activeSlave.trust += 10>>
-				<<elseif $activeSlave.behavioralFlaw == "anorexic">>
-					Only then does $he regain composure enough to realize what $he's done. $He @@.mediumorchid; bursts into tears@@ as $he rubs $his disgustingly full belly. $He glares at you, only to catch you toying with even more food. $He begins to @@.gold;tremble@@ when $he realizes how easily you did this to $him. You're sure $he'll change $his tune in time, since you know $he'll be unable to resist sneaking treats.
-					<<set $activeSlave.devotion -= 10, $activeSlave.trust -= 10>>
-				<<else>>
-					$He @@.hotpink;happily pats $his full belly,@@ before coming to $his senses and realizing what $he did. $He begins to @@.gold;tremble@@ as it dawns on $him just how easily you manipulated $him.
-					<<set $activeSlave.devotion += 5, $activeSlave.trust -= 10>>
-				<</if>>
-			<</if>>
-			Given the availability of the slave treats and $his burgeoning addiction to them, $he'll likely keep $himself stuffed unless you force $him to stop.
-			<<if $activeSlave.preg > 0 || $activeSlave.bellyImplant > 0>>
-				<<set $activeSlave.inflation = 1>>
-			<<else>>
-				<<set $activeSlave.inflation = 2>>
-			<</if>>
-			<<set $activeSlave.inflationType = "food", $activeSlave.inflationMethod = 1>>
-			<<run SetBellySize($activeSlave)>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-<</if>> /* CLOSES MINDBROKEN CHECK*/
-
-
-<br>&nbsp;&nbsp;&nbsp;&nbsp;
-<<link "Spank $him">>
-	<<replace "#introResult">>
-		You <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>lay the obedient $desc across your knees<<else>>force the resistant $desc to lie across your knees<</if>> and run a voluptuary hand across $his <<if $activeSlave.butt > 6>>jiggling<<elseif $activeSlave.butt > 3>>delightfully big<<elseif $activeSlave.butt > 1>>cute<<else>>skinny<</if>> buttocks, enjoying the way $he tenses up reflexively at the extreme vulnerability of $his position. $He feels the change in your posture as you bring your hand back, and <<if ($activeSlave.devotion > 20) || ($activeSlave.trust < -20)>>can't stop $himself from wriggling<<else>>struggles desperately<</if>> for just a moment before your palm smacks against $his ass.
-		<<if $activeSlave.devotion > 20>>
-			<<if $activeSlave.fetish == "masochist">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$He gasps with profound shock, and you feel $his whole body stiffen atop your thighs as $he tries to work out what just happened. With each spank, $he grows more and more aroused until $he finally reaches orgasm. @@.green;$He's a pain fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					$He groans with guilty pleasure, feeling the pain radiate out from $his buttocks with each swat, feeling it seem to circle around $his hips to pool between $his legs. Before long, $he reaches a quivering orgasm.
-				<</if>>
-				$He gets up very gingerly, but bites $his lip cutely, @@.hotpink;feeling a strange satisfaction.@@
-				<<set $activeSlave.devotion += 5>>
-			<<else>>
-				$He gasps with pain, but does not object or try to escape. $He accepts the spanking dutifully. You're not particularly harsh, and $he gets up after you tire of swatting $his bottom @@.hotpink;without resentment@@ of this treatment.
-				<<set $activeSlave.devotion += 4>>
-			<</if>>
-		<<else>>
-			<<if $activeSlave.fetish == "masochist">>
-				<<if $activeSlave.fetishKnown == 0>>
-					$He gasps with profound shock, and you feel $his whole body stiffen atop your thighs as $he tries to work out what just happened. $He begins to cry as $he realizes that having $his bottom beaten is getting $him horny. @@.green;$He's a pain fetishist!@@
-					<<set $activeSlave.fetishKnown = 1>>
-				<<else>>
-					$He groans with unwilling pleasure, feeling the pain radiate out from $his buttocks with each swat, feeling it seem to circle around $his hips to pool between $his legs. $He does not orgasm, but feels much more pleasure than $he's willing to admit.
-				<</if>>
-				When you let $him up, $he's sobbing, more from humiliation than pain. However, $he @@.hotpink;submits@@ to an uncomfortable groping of $his buttocks, which are pleasingly warm from the spanking.
-				<<set $activeSlave.devotion += 4>>
-			<<else>>
-				$He gasps with pain, and starts to wriggle off you until you pin $him with your other hand. $He accepts the rest of the spanking unhappily. You're not particularly harsh, and $he gets up after you tire of swatting $his bottom, @@.gold;fearful@@ due to the humiliation rather than the pain.
-				<<set $activeSlave.trust -= 4>>
-			<</if>>
-		<</if>>
-	<</replace>>
-<</link>>
-
-
-<<if hasAnyArms($activeSlave)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Tie $him up and give $him a good whipping">>
-		<<replace "#introResult">>
-			You cuff $his wrist<<if hasBothArms($activeSlave)>>s<</if>> and tie the cuffs to a hook in the ceiling so $he's forced up on tiptoe. Reflecting that sometimes the old ways are best, you take a whip to $him. It's soft leather and you have some skill, so $his skin isn't broken, but you lash $his buttocks and every stroke draws a scream. After a while, the pain grows dull for $him and $he slumps in $his bindings, moaning. You switch to $his nipples, bringing $him back to howling life as $he dances on tiptoe and tries to dodge. <<if $activeSlave.dick != 0>>You finish by taking the whip to $his penis, leaving $him sobbing and begging. <</if>>If $he was wondering what kind of master $his new master is, now $he @@.gold;knows.@@ $He will remember @@.mediumorchid;what you can do to $him.@@
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-
-<<if ($activeSlave.anus != 0)>>
-	<<if ($activeSlave.vagina > 0)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<link "Use a machine on $his holes">>
-			<<replace "#introResult">>
-				$He's not a virgin anywhere, so you'll have to go more extreme to provide a properly introductory level of sexual torture equivalent to defloration. You tie your new slave down on <<if hasAnyArms($activeSlave)>>hand<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>knee<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and bring a fuckmachine up from storage. It fits over $him as $he whimpers down there on <<if hasAllLimbs($activeSlave)>>all fours<<else>>the floor<</if>>, and you adjust the pistons so that the tip of a dildo rests inside each of $his holes. You add lube when necessary, <<if $assistant.personality == 0>>but the rest of the day is monotonous machine rape for $him.<<else>>but for $him the rest of the day is machine rape controlled by your sultry-voiced personal assistant program. Your assistant is an exquisite sexual torturer, ensuring that the experience is never damaging and always overwhelming. $He keeps up a steady stream of degrading verbal abuse while $he works.<</if>> Any resistance to you is @@.mediumorchid;worn down@@ and replaced with a germ of @@.gold;fear.@@ @@.lime;The slave's holes are nicely stretched.@@
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 10>>
-			<<set $activeSlave.counter.vaginal += 1>>
-			<<set $vaginalTotal += 1>>
-			<<set $activeSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-			<<set $activeSlave.anus += 1>>
-			<<set $activeSlave.vagina += 1>>
-			<</replace>>
-		<</link>>
-	<<elseif ($activeSlave.vagina == -1)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<link "Use a machine on $his asshole">>
-			<<replace "#introResult">>
-				$He's not an anal virgin, so you'll have to go more extreme to provide an introductory level of sexual torture equivalent to defloration. You tie your new slave down on <<if hasAnyArms($activeSlave)>>hand<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>knee<<if hasBothLegs($activeSlave)>>s<</if>><</if>> and bring a fuckmachine up from storage. It fits over $him as $he whimpers down there on <<if hasAllLimbs($activeSlave)>>all fours<<else>>the floor<</if>>, and you adjust the pistons so that the tip of a dildo rests inside $his asshole. You add lube when necessary, <<if $assistant.personality == 0>>but the rest of the day is monotonous machine rape for $him.<<else>>but for $him the rest of the day is machine rape controlled by your sultry-voiced personal assistant program. Your assistant is an exquisite sexual torturer, ensuring that the experience is never damaging and always overwhelming. $He keeps up a steady stream of degrading verbal abuse while $he works.<</if>> Any resistance to you is @@.mediumorchid;worn down@@ and replaced with a germ of @@.gold;fear.@@ @@.lime;The slave's asshole is nicely stretched.@@ /*Written and coded by Boney M*/
-			<<set $activeSlave.devotion -= 5>>
-			<<set $activeSlave.trust -= 10>>
-			<<set $activeSlave.counter.anal += 1>>
-			<<set $analTotal += 1>>
-			<<set $activeSlave.anus += 1>>
-			<</replace>>
-		<</link>>
-	<</if>>
-<</if>>
-
-
-<<if canTalk($activeSlave) && !isAmputee($activeSlave)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Make $him an office ornament for the day">>
-		<<replace "#introResult">>
-			You cuff $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>>, bend
-			<<if getLimbCount($activeSlave) === 4>>
-				all four limbs
-			<<elseif getLimbCount($activeSlave) === 3>>
-				all three limbs
-			<<elseif getLimbCount($activeSlave) === 2>>
-				both limbs
-			<<else>>
-				$his limb
-			<</if>>
-			behind $him, secure them together, and hoist $him up to the ceiling on a rope. $He hangs up there like a sex slave chandelier<<if $activeSlave.boobs >= 650>>, breasts swaying in a lovely way whenever $he struggles<</if>>. Periodically you let $him down to stretch $his aching <<if ((!hasAnyArms($activeSlave) && !hasBothLegs($activeSlave)) || (!hasBothArms($activeSlave) && !hasAnyLegs($activeSlave)))>>limb<<else>>limbs<</if>>, but whenever you put your decoration back up you add some clips with little bells on them: first the nose, then the nipples, and finally the <<if $activeSlave.dick != 0>>dickhead<<else>>pussylips<</if>>. By the end of the day your work is accompanied by the sweet sounds of ringing bells and @@.gold;agonized pleas@@ for mercy in $his <<if $activeSlave.voice == 3>>high, girlish<<elseif $activeSlave.voice == 2>>feminine<<else>>deep<</if>> voice. $He will remember @@.mediumorchid;what you can do to $him.@@
-		<</replace>>
-		<<set $activeSlave.devotion -= 5>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-<</if>>
-
-<<if $cumSlaves > 3>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Fill $him with cum">>
-		<<replace "#introResult">>
-			You securely restrain your new slave<<if $dairyRestraintsSetting > 1>> in a milking stall<</if>>. A quick finger check of $his holes allows you to choose wisely from a selection of hollow phalluses, which push into your helpless new slave's
-			mouth,
-			<<set $activeSlave.counter.oral += 1>>
-			<<set $oralTotal += 1>>
-			<<if ($activeSlave.vagina > 0)>>
-				vagina,
-				<<set $activeSlave.counter.vaginal += 1>>
-				<<set $vaginalTotal += 1>>
-			<</if>>
-			<<if $activeSlave.anus > 0>>
-				and squeeze into $his ass,
-				<<set $activeSlave.counter.anal += 1>>
-				<<set $analTotal += 1>>
-			<</if>>
-			with no room to spare.
-			Your choices attach to long tubes, and with a fiendish smile you turn a valve. The lines run white with a mixture of fluids from your other slaves, and your newest slave begins to fill.
-			<<if $activeSlave.devotion > 20>>
-				<<if $activeSlave.fetish == "cumslut">>
-					<<if $activeSlave.fetishKnown == 0>>
-						$He seems genuinely eager to hold as much of the fluid as $he can. @@.green;$He's a cum fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$He groans with guilty pleasure, feeling $his muscles ache and $his torso swell. Before long, $he reaches a quivering orgasm.
-					<</if>>
-					As you release $him $he swings up with difficulty, but @@.hotpink;$he seems very pleased@@ to stay plugged for now.
-					<<set $activeSlave.devotion += 5>>
-				<<else>>
-					<<if !hasBothEyes($activeSlave)>>$His face swells<<else>>$His eyes swell<</if>> with shock, but $he does not flinch or make a sound. When $he seems full enough, you shut off the valve, deciding to leave $him plugged for now. $He waddles off @@.hotpink;without apparent resentment@@ of this treatment.
-					<<set $activeSlave.devotion += 4>>
-				<</if>>
-			<<else>>
-				<<if $activeSlave.fetish == "cumslut">>
-					<<if $activeSlave.fetishKnown == 0>>
-						<<if !hasBothEyes($activeSlave)>>$His face swells<<else>>$His eyes swell<</if>> with profound horror at the realization of what is happening, and begin to leak tears as $he realizes that having $his body swell with semen is getting $him horny. @@.green;$He's a cum fetishist!@@
-						<<set $activeSlave.fetishKnown = 1>>
-					<<else>>
-						$He groans with guilty pleasure, feeling $his muscles ache and $his torso swell. $He does not orgasm from $his "meal," but feels much more pleasure than $he's willing to admit.
-					<</if>>
-					When you let $him up, $he's sobbing, more from humiliation than discomfort. However, $he @@.hotpink;submits@@ to an uncomfortable groping of $his stomach, which is pleasingly taut from the inflation. You decide to leave $him plugged for a few hours, impressing on $him still more that $his fate is out of $his control.
-					<<set $activeSlave.devotion += 4>>
-				<<else>>
-					$He gasps in horror, and starts to struggle frantically against $his bonds. However, as the fluid reaches $him $he seems to resign $himself to $his fate. Once $he reaches a fullness you like you let $him up, @@.gold;fearful@@ due to the humiliation rather than the swelling. $He limps off to $his new duties as quickly as $he can, before you can unplug $him. Oh well!
-					<<set $activeSlave.trust -= 4>>
-				<</if>>
-			<</if>>
-			$His first day promises to be a full one!
-		<</replace>>
-	<</link>>
-<</if>>
-
-<<if hasAnyArms($activeSlave)>>
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Put a shock collar on $him and force $him to rape $himself">>
-		<<set $activeSlave.collar = "shock punishment">>
-		<<replace "#introResult">>
-			You put a shock collar on $him. Its function isn't immediately obvious, at least until you test it on the lowest power setting, making $him jump and look at you fearfully. This concern is compounded when you throw a big dildo at $him and tell $him to rape $himself. $He gapes at you incredulously until you give $him a stronger jolt and peremptorily tell $him to
-			<<if $activeSlave.vagina > 0>>
-				pick it up and pound $his own pussy. @@.mediumorchid;Reluctantly,@@ $he reaches down shakily, seats $himself, and slowly pushes the uncomfortably big phallus inside $his womanhood. $He starts to fuck $himself. Harder, you command. Wincing, $he works $his cunt faster. Harder, you repeat, giving $him another shock. @@.gold;$He begins to cry,@@ but obeys, sawing the big fake cock in and out, really raping $himself.
-				<<set $activeSlave.counter.vaginal += 1>>
-				<<set $vaginalTotal += 1>>
-			<<elseif $activeSlave.anus > 0>>
-				pick it up and pound $his own ass. @@.mediumorchid;Reluctantly,@@ $he reaches down shakily, seats $himself, and gradually shoves the already-lubricated phallus into $his ass. It's uncomfortably big for $his butt, but $he can manage it, and slowly starts to sodomize $himself. Harder, you command. Wincing, $he fucks $his ass faster. Harder, you repeat, giving $him another shock. @@.gold;$He begins to cry,@@ but obeys, sawing the big fake cock in and out, really assraping $himself.
-				<<set $activeSlave.counter.anal += 1>>
-				<<set $analTotal += 1>>
-			<<else>>
-				pick it up and facefuck $himself. @@.mediumorchid;Reluctantly,@@ $he reaches down shakily, seats $himself, and slowly swallows the uncomfortably big phallus. $He has to start over several times as $his gag reflex kicks in, but $he finally manages to hilt it. Harder, you command. Eyes rolling fearfully, $he withdraws it a few <<if $showInches == 2>>inches<<else>>centimeters<</if>> and shoves it down $his throat again. Harder, you repeat, giving $him another shock. @@.gold;$He begins to weep and gag,@@ but obeys, sawing the big fake cock in and out, really molesting $himself.
-				<<set $activeSlave.counter.oral += 1>>
-				<<set $oralTotal += 1>>
-			<</if>>
-			<<set $activeSlave.trust -= 10>>
-			<<set $activeSlave.devotion -= 5>>
-		<</replace>>
-	<</link>>
-<</if>>
-
-
-<<if ($dairy > 0) && ($dairyRestraintsSetting > 1) && ($seeExtreme == 1)>>
-	<br>//...in the Industrial Dairy//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Threaten $him with the Industrial Dairy">>
-		<<setNonlocalPronouns $seeDicks>>
-		<<replace "#introResult">>
-			You tell $him that it's in $his interests to be a good $girl. $He does not react immediately, perhaps wondering if you think such a trite statement will have a real impact, but then you <<if canSee($activeSlave)>>point at<<else>>direct $him towards<</if>> a wallscreen behind $him. $He turns, and beholds a live feed from the Industrial Dairy.
-			<<if canSee($activeSlave)>>
-				$He gazes spellbound at the interleaved row of bodies intertwined with machines in embraces far more intimate than any lover could hope to match. The gently heaving masses of breastflesh hold $his fascinated and horrified attention until one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.
-			<<elseif canHear($activeSlave)>>
-				$He listens entranced by the steady rhythm of the machines collecting their occupants' bodily fluids. The muffled moans and groans of the restrained slaves hold $his fascinated and horrified attention until one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.
-			<<else>>
-				Such a broadcast would normally be pointless for a senseless slave like $activeSlave.slaveName, but you've taken precautions to accommodate such slaves. The wallscreen is connected to the heating, air conditioning, and sprinkler systems in order to accurately replicate the aura of an industrial slave dairy, so $he is fully immersed in the scene when one of the machines fortuitously chooses this moment to cycle its occupant's anal hydration regimen.
-			<</if>>
-			It withdraws its horse-sized phallus from the slave's anus, leaving _hisU gape pulsing gently as it awaits the resumption of the endless assrape. $activeSlave.slaveName lets out a huge sob and turns to you, @@.gold;fear suffusing $him@@ as $he promises to @@.hotpink;be a good $girl.@@
-		<</replace>>
-		<<set $activeSlave.devotion += 10>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-	<<if ($activeSlave.lactation > 0 || (($dairySlimMaintainUpgrade == 0 || $dairySlimMaintain == 0) && ($activeSlave.boobs > 300 || $activeSlave.dick == 0 || $dairyImplantsSetting == 1) && $dairyImplantsSetting != 2)) || ($activeSlave.balls > 0)>>
-		<<if ($dairyPrepUpgrade == 1) && App.Entity.facilities.dairy.hasFreeSpace>>
-			| <<link "Send $him straight to the Industrial Dairy">>
-				<<set $activeSlave.choosesOwnAssignment = 0>>
-				<<set $activeSlave.anus = Math.clamp($activeSlave.anus, 3, 4)>>
-				<<if $activeSlave.vagina > -1>><<set $activeSlave.vagina = Math.clamp($activeSlave.vagina, 3, 4)>><</if>>
-				<<set $activeSlave.counter.anal += 10, $analTotal += 10>>
-				<<= assignJob($activeSlave, "dairy")>>
-				<<replace "#introResult">>
-					You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $dairyName. The new slave does not know what $dairyName is, not really, and $he doesn't know what being set up there means, either. If $he knew that you are able to send $him there so blithely only because it is equipped with a special preparatory raper that will seize $him, <<if $activeSlave.vagina > -1>>mercilessly fuck $his pussy and ass until both are gaped<<else>>ream $his anus until it's cavernously gaped<</if>>, and then consign $him to constant fucking by gargantuan machine phalli, $he might resist. But $he doesn't, so $he does not.
-				<</replace>>
-			<</link>>
-		<</if>>
-		<<if App.Entity.facilities.dairy.hasFreeSpace>>
-			| <<link "Break $him in for the Industrial Dairy">>
-				<<set $activeSlave.choosesOwnAssignment = 0>>
-				<<set $activeSlave.anus = Math.clamp($activeSlave.anus, 3, 4)>>
-				<<if $activeSlave.vagina > -1>>
-					<<set $activeSlave.vagina = Math.clamp($activeSlave.vagina, 3, 4)>>
-					<<set $activeSlave.counter.vaginal += 10>>
-					<<set $vaginalTotal += 10>>
-				<</if>>
-				<<set $activeSlave.counter.anal += 10, $analTotal += 10>>
-				<<= assignJob($activeSlave, "dairy")>>
-				<<replace "#introResult">>
-					Making use of <<if ($activeSlave.trust < -20) || ($activeSlave.devotion > 20)>>$his obedience<<else>>the compliance systems<</if>>, you restrain $him on one of the chairs in your office in an approximation of the position $he'll occupy in $dairyName. Then you put a mask on $him, like the ones the machines there feature, and turn it on, watching the slave squirm against $his restraints under the sudden bombardment of garish hardcore porn. Finally, you add a dildo gag, both to mimic the dildo that will feed $him, and to keep your office reasonably quiet. Then, for the rest of the day, you use $his vulnerable <<if $activeSlave.vagina > -1>>holes<<else>>asshole<</if>> as an outlet for your sexual energy. You are not gentle; in fact, the point of the whole exercise is to gape $him. By the evening $he's been fucked so hard that $he's stopped jerking against the chair when you pound <<if $PC.dick != 0>>your huge cock<<else>>a huge strap-on<</if>> in and out of $him, so you're obliged to get creative, sliding fingers in alongside <<if $PC.dick != 0>>yourself<<else>>it<</if>> to really blow $him out. Once that gets too easy, you start adding dildos for double <<if $activeSlave.vagina > -1>>and triple <</if>>penetration. By nighttime $he's properly prepared to take $dairyName's giant phalli, and you're bored, so you consign $him to $his fate. $He might have some opinion on how $he's spent $his day, but it's unlikely $he'll remember it by tomorrow, what with the forearm-sized dildos sliding in and out of $his throat<<if $activeSlave.vagina > -1>>, cunt,<</if>> and asshole.
-				<</replace>>
-			<</link>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $dairyPiping == 1>>
-	<<if (($cumPipeline > 88 && $cumPipeline != 0) || $arcologies[0].FSPastoralistLaw == 1) && $activeSlave.preg < 1 && $activeSlave.inflation == 0 && $activeSlave.bellyImplant <= 0>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<link "Give $him all the cum $he can drink">>
-			<<replace "#introResult">>
-				You securely restrain your new slave; for both $his own safety and so $he can't object to $his meal. You reassure $him <<if hasAnyEyes($activeSlave)>> and order $him to close $his eyes <</if>> and open wide for a treat. <<if canSee($activeSlave)>>Since $his eyes are contentedly closed<<else>>Blind as $he is<</if>>, $he doesn't see you reach for one of the phallus-tipped feeding tubes located throughout your penthouse. Before $he knows what's happening, you've forced the cocktube firmly into $his gaping maw and anchored it to $his head, causing $his entire body to tense up <<if $activeSlave.devotion <= 20>>in panic <</if>>once more.
-				<br><br>
-				You pause to examine the tap, making sure it is set to cum, before releasing the valve and unleashing a steady flow. The feeder bucks against $his face as thick, white liquid rushes downward towards the helpless slave.
-				<<if $activeSlave.devotion > 20>>
-					<<if $activeSlave.fetish == "cumslut">>
-						<<if $activeSlave.fetishKnown == 0>>
-							Stunningly, once $he gets over the impact of the wave of fresh semen, $he seems genuinely eager to hold as much of the fluid as $he can, stretching contentedly to $his reasonable limit. @@.green;$He's a cum fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<<else>>
-							$His belly steadily swells from a few months of apparent pregnancy, to "spent too much time at the buffet", until it finally stops wobbling, grows taut and forces $his belly button into an outie. Your cow groans not only with the weight and mounting pressure, but with guilt as well. Before long, $he reaches a quivering orgasm.
-						<</if>>
-						You stroke $his gurgling stomach slowly, before turning off the valve, unfastening $his binds and leaving your @@.hotpink;very pleased@@ cum balloon to savor $his meal. You'll make sure to set aside enough cum from your cumslaves for $him to drink $himself stupid with, and, glancing over your shoulder, find $him eagerly masturbating to $his cum filled gut. $He'll probably intend to keep $himself filled to the brim, which is fine by you.
-						<<set $activeSlave.devotion += 5>>
-					<<else>>
-						<<if !hasBothEyes($activeSlave)>>$His face swells<<else>>$His eyes swell<</if>> with horror, but $he does not flinch or make a sound. $His belly also swells, from a few months of apparent pregnancy, to 'spent too much time at the buffet', till it finally stops wobbling, grows taut and forces $his belly button into an outie. Satisfied, you shut off the valve, deciding to leave $him to consider $his cum filled middle. Time passes. $His <<if canSee($activeSlave)>>eyes are glazed over<<else>>blind face is distant<</if>>, but $he tolerates you slowly stroking $his stomach @@.hotpink;without apparent resentment.@@ You'll make sure to keep $him like this for the time being.
-						<<set $activeSlave.devotion += 4>>
-					<</if>>
-				<<else>>
-					<<if $activeSlave.fetish == "cumslut">>
-						<<if $activeSlave.fetishKnown == 0>>
-							<<if !hasBothEyes($activeSlave)>>$His face tenses<<else>>$His eyes swell<</if>> with profound horror at the realization of what is filling $him with a constant irresistible pressure, and $he begins to leak tears as $he realizes that $his rapidly swelling body is getting $him horny. @@.green;$He's a cum fetishist!@@
-							<<set $activeSlave.fetishKnown = 1>>
-						<<else>>
-							$His belly steadily swells from a few months of apparent pregnancy, to "spent too much time at the buffet", till it finally stops wobbling, grows taut and forces $his belly button into an outie. Your cow groans not only with the weight and quickly mounting pressure, but with guilt as well. $He does not orgasm from $his gluttonous "meal," but feels much more pleasure than $he's willing to admit.
-						<</if>>
-						Time passes, marked only by the slowing growth of $his waistline and the gurgling deep within $him. However, $he @@.hotpink;submits@@ to an uncomfortable groping of $his fluid distended stomach, which is pleasingly taut from $his massive cum meal. You decide to make it abundantly clear that if a single drop of cum leaves $his gullet, $he's getting the hose again, impressing on $him still more that $his fate is out of $his control.
-						<<set $activeSlave.devotion += 4>>
-					<<else>>
-						$He gasps in horror, and starts to struggle frantically against $his bonds, <<if $activeSlave.voice != 0>>struggling to scream in panic.<<else>>$his whole body begging to scream.<</if>> However, as the fluid reaches $him, $he seems to despair and resigns $himself to $his fate. As $his belly swells, you find yourself comparing $him first to a binge eater, then some of the chubbier citizens you've seen, a large pregnancy, and finally a water balloon ready to burst. You stop the flow, and rest a hand on $his heaving stomach. $His @@.gold;terrified@@ breathing and distant expression are clearly due to the pain, not lust, but it's also now quite clear to $him who is in charge here. You have little intent on letting $his stomach empty and will be setting aside enough cum to keep $him bloated into submission.
-						<<set $activeSlave.trust -= 4>>
-					<</if>>
-				<</if>>
-				<<set $activeSlave.inflation = 3, $activeSlave.inflationType = "cum", $activeSlave.inflationMethod = 1>>
-				<<run SetBellySize($activeSlave)>>
-			<</replace>>
-		<</link>>
-	<</if>>
-	<<if (($milkPipeline > 88 && $milkPipeline != 0) || $arcologies[0].FSPastoralistLaw == 1) && $activeSlave.preg < 1 && $activeSlave.inflation == 0 && $activeSlave.bellyImplant <= 0>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<link "Give $him all the milk $he can drink">>
-			<<replace "#introResult">>
-				You securely restrain your new slave; for both $his own safety and so $he can't object to $his meal. You reassure $him <<if hasAnyEyes($activeSlave)>> and order $him to close $his eyes <</if>> and open wide for a treat. <<if canSee($activeSlave)>>Since $his eyes are contentedly closed<<else>>Blind as $he is<</if>>, $he doesn't see you reach for one of the phallus-tipped feeding tubes located throughout your penthouse. Before $he knows what's happening, you've forced the cocktube firmly into $his gaping maw and anchored it to $his head, causing $his entire body to tense up <<if $activeSlave.devotion <= 20>>in panic <</if>>once more.
-				<br><br>
-				You pause to examine the tap, making sure it is set to milk, before releasing the valve and unleashing a steady flow. The feeder bucks against $his face as creamy, white liquid rushes downward towards the helpless slave.
-				<<if $activeSlave.behavioralFlaw == "gluttonous">>
-					<<if !hasBothEyes($activeSlave)>>$His face swells<<else>>$His eyes swell<</if>> with horror, but $he does not flinch or make a sound. $His belly also swells, from a few months of apparent pregnancy, to "spent too much time at the buffet", till it finally stops wobbling, grows taut and forces $his belly button into an outie. Satisfied, you shut off the valve, deciding to leave $him to consider $his milk filled middle. Time passes. $His<<if canSee($activeSlave)>> eyes are glazed over<<else>> blind face is distant<</if>>, but $he tolerates you slowly stroking $his stomach @@.hotpink;without apparent resentment.@@ You'll make sure to keep $him like this for the time being.
-					<<set $activeSlave.devotion += 5>>
-				<<else>>
-					$He gasps in horror, and starts to struggle frantically against $his bonds, <<if $activeSlave.voice != 0>>struggling to scream in panic.<<else>>$his whole body begging to scream.<</if>> However, as the fluid reaches $him, $he seems to despair and resigns $himself to $his fate. As $his belly swells, you find yourself comparing $him first to a binge eater, then some of the chubbier citizens you've seen, a large pregnancy, and finally a water balloon ready to burst. You stop the flow, and rest a hand on $his heaving stomach. $His @@.gold;terrified@@ breathing and distant expression are clearly due to the pain, not lust, but it's also now quite clear to $him who is in charge here. You have little intent on letting $his stomach empty and will be setting aside enough milk to keep $him bloated into submission.
-					<<set $activeSlave.trust -= 3>>
-				<</if>>
-				<<set $activeSlave.inflation = 3, $activeSlave.inflationType = "milk", $activeSlave.inflationMethod = 1>>
-				<<run SetBellySize($activeSlave)>>
-			<</replace>>
-		<</link>>
-	<</if>>
-<</if>>
-
-<<if ($arcade > 0) && ($seeExtreme == 1)>>
-	<br>//...in the Arcade//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<link "Threaten $him with the Arcade">>
-		<<setNonlocalPronouns $seeDicks>>
-		<<replace "#introResult">>
-			You tell $him that it's in $his best interests to be a good $girl. $He does not react immediately, perhaps wondering if you think such a trite statement will have a real impact, but then you <<if canSee($activeSlave)>>point at<<else>>direct $him towards<</if>> a wallscreen behind $him. $He turns, and beholds a live feed from $arcadeName.
-			<<if canSee($activeSlave)>>
-				$He gazes at the row of butts sticking out of the wall at dick height, not quite realizing what $he's seeing. Then $he notices that there are citizens pumping away in front of two of the butts, and $he understands.
-			<<elseif canHear($activeSlave)>>
-				$He listens to the wet sounds of flesh on flesh, not quite realizing what $he's hearing. Then $he picks out the sound of your citizens grunting and moaning as they pound away, and $he understands.
-			<<else>>
-				Such a broadcast would normally be pointless for a senseless slave like $activeSlave.slaveName, but you've taken precautions to accommodate such slaves. The wallscreen is connected to the heating, air conditioning, and sprinkler systems in order to accurately replicate the feeling of standing in front of a row of restrained public sex slaves, so the meaning of the scene in front of $him is not lost.
-			<</if>>
-			As $he <<if canSee($activeSlave)>>watches<<elseif canHear($activeSlave)>>listens<<else>>stands there<</if>>, terrified, first one citizen and then the other finishes and steps away. The first arcade inmate's <<if $seeDicks != 100>>pussy<<else>>butt<</if>> is left looking sore until $arcadeName's systems cover _himU for a quick cleaning, and the second inmate's asshole <<if $seeDicks != 0>>has obviously seen severe use, since the poor _girlU doesn't have a pussy to spread the load<<else>>is loose enough that the machines have to clean up the cum it drools onto the floor<</if>>. $activeSlave.slaveName lets out a huge sob and turns to you, @@.gold;fear suffusing $him@@ as $he promises to @@.hotpink;be a good $girl.@@
-		<</replace>>
-		<<set $activeSlave.devotion += 10>>
-		<<set $activeSlave.trust -= 10>>
-	<</link>>
-
-	<<if (App.Entity.facilities.arcade.hasFreeSpace) || ($arcadeUpgradeFuckdolls == 2)>>
-		| <<link "Send $him straight to the Arcade">>
-			<<set $activeSlave.assignment = "be confined in the arcade">>
-			<<set $activeSlave.choosesOwnAssignment = 0>>
-			<<replace "#introResult">>
-				You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $arcadeName. The new slave does not know what $arcadeName is, not really, and $he doesn't know what being set up there means, either. $He'll be confined inside a small space, not too different from the indignities $he's suffered already. It's only when the restraints lock into place that $he'll understand $his doom. $His mouth will be forced open and presented at one wall of $arcadeName, and $his ass will protrude from its other side, $his holes available for public relief at both ends. $He'll probably refuse to believe the truth, until the first cockhead enters $his mouth<<if $activeSlave.vagina > -1>>, parts $his pussylips,<</if>> or presses against $his poor anus.
-				<<if ($arcade <= App.Entity.facilities.arcade.employeesIDs().size)>>Mere <<if $showInches == 2>>yards<<else>>meters<</if>> away, preparations to convert the least appealing Arcade slave into a Fuckdoll begin. As $activeSlave.slaveName is broken in by $his first customers, $he's blissfully unaware that $he's $arcade new slaves away from the same fate.<</if>>
-			<</replace>>
-		<</link>>
-	<</if>>
-<</if>>
-
-</p>
-
-<</if>>
-</p>
-<<= IncreasePCSkills('trading', 0.1)>>
-<<if $PC.skill.slaving < 100 && random($PC.skill.slaving,100) > 50>> <<= IncreasePCSkills('slaving', 0.5)>> <<else>> <<= IncreasePCSkills('slaving', 0.1)>> <</if>>
+<<includeDOM App.UI.newSlaveIntro($activeSlave)>>
diff --git a/src/uncategorized/pRivalryCapture.tw b/src/uncategorized/pRivalryCapture.tw
index eb8df43961a91ad5c373b4524a908d8c78eceb82..9490863e8b9622efb4de03e1bc099b14489b1d90 100644
--- a/src/uncategorized/pRivalryCapture.tw
+++ b/src/uncategorized/pRivalryCapture.tw
@@ -24,13 +24,12 @@
 <</if>>
 <<set _rivalType = _rivalType.random()>>
 
-<<set $oneTimeDisableDisability = 1>>
-<<if setup.filterRacesLowercase.includes($rivalRace)>><<set $fixedRace = $rivalRace>><</if>>
+<<if setup.filterRacesLowercase.includes($rivalRace)>>
+	<<set _race = $rivalRace>>
+<</if>>
 <<switch _rivalType>>
 <<case "expansionist shemale">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceImplant = 15>>
 	<<set $activeSlave.faceShape = "androgynous">>
@@ -62,9 +61,7 @@
 	<<set $activeSlave.hStyle = "luxurious">>
 	<<set $activeSlave.hLength = 80>>
 <<case "cum addict">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.chem = 3000>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceShape = "masculine">>
@@ -95,9 +92,7 @@
 	<<set $activeSlave.hStyle = "neat">>
 	<<set $activeSlave.hLength = 20>>
 <<case "hung shota">>
-	<<set $activeSlaveOneTimeMinAge = $minimumSlaveAge>>
-	<<set $activeSlaveOneTimeMaxAge = 18>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", {minAge: $minimumSlaveAge, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceShape = "androgynous">>
 	<<set $activeSlave.muscles = 0>>
@@ -126,9 +121,7 @@
 	<<set $activeSlave.sexualQuirk = "perverted">>
 	<<set $activeSlave.hLength = 20>>
 <<case "masculine">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 55>>
 	<<set $activeSlave.faceImplant = 0>>
 	<<set $activeSlave.faceShape = "masculine">>
@@ -155,9 +148,7 @@
 	<<set $activeSlave.hStyle = "neat">>
 	<<set $activeSlave.hLength = 5>>
 <<case "micropenis">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 55>>
 	<<set $activeSlave.faceImplant = 0>>
 	<<set $activeSlave.faceShape = "masculine">>
@@ -184,9 +175,7 @@
 	<<set $activeSlave.hStyle = "neat">>
 	<<set $activeSlave.hLength = 5>>
 <<case "bull dyke">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 55>>
 	<<set $activeSlave.faceImplant = 0>>
 	<<set $activeSlave.faceShape = "androgynous">>
@@ -221,13 +210,13 @@
 	<<set $activeSlave.hLength = 5>>
 <<case "breeder">>
 	<<if $pedo_mode == 1>>
-		<<set $activeSlaveOneTimeMinAge = ($fertilityAge + 6)>>
-		<<set $one_time_age_overrides_pedo_mode = 1>> /% Old enough to have been pregnant many times. %/
+		<<set _minAge = ($fertilityAge + 6)>>
+		<<set _pedo = 1>> /* Old enough to have been pregnant many times. */
 	<<else>>
-		<<set $activeSlaveOneTimeMinAge = 18>>
-		<<set $activeSlaveOneTimeMaxAge = 42>>
+		<<set _minAge = 18>>
+		<<set _maxAge = 42>>
 	<</if>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: _minAge, maxAge: _maxAge, ageOverridesPedoMode: _pedo, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.pubertyXX = 1>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceImplant = 0>>
@@ -259,7 +248,7 @@
 		<<set $activeSlave.pregType = either(3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8)>>
 		<<set $activeSlave.counter.birthsTotal = random(18,27)>>
 		<<set $activeSlave.bellySag = 2, $activeSlave.bellySagPreg = 2>>
-		<<set $activeSlave.pregAdaptation = (random(60,100) + ($activeSlave.birthsTotal * $activeSlave.geneticQuirks.uterineHypersensitivity))>>
+		<<set $activeSlave.pregAdaptation = (random(60,100) + ($activeSlave.counter.birthsTotal * $activeSlave.geneticQuirks.uterineHypersensitivity))>>
 	<</if>>
 	<<set $activeSlave.pregKnown = 1>>
 	<<set $activeSlave.pregWeek = $activeSlave.preg>>
@@ -279,9 +268,7 @@
 	<<set $activeSlave.hStyle = "luxurious">>
 	<<set $activeSlave.hLength = 80>>
 <<case "oppai loli">>
-	<<set $activeSlaveOneTimeMinAge = $minimumSlaveAge>>
-	<<set $activeSlaveOneTimeMaxAge = 12>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: $minimumSlaveAge, maxAge: 12, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceImplant = 0>>
 	<<set $activeSlave.faceShape = "cute">>
@@ -309,9 +296,7 @@
 	<<set $activeSlave.hStyle = "twin tails">>
 	<<set $activeSlave.hLength = 40>>
 <<case "cow">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 55>>
 	<<set $activeSlave.faceImplant = 0>>
 	<<set $activeSlave.faceShape = "cute">>
@@ -340,9 +325,7 @@
 	<<set $activeSlave.hStyle = "neat">>
 	<<set $activeSlave.hLength = 5>>
 <<case "bimbo">>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceImplant = 65>>
 	<<set $activeSlave.faceShape = "sensual">>
@@ -374,9 +357,7 @@
 	<<set $activeSlave.hStyle = "tails">>
 	<<set $activeSlave.hLength = 80>>
 <<default>>
-	<<set $activeSlaveOneTimeMinAge = 18>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: 18, maxAge: 42, race: _race, disableDisability: 1})>>
 	<<set $activeSlave.face = 100>>
 	<<set $activeSlave.faceImplant = 15>>
 	<<set $activeSlave.muscles = 0>>
diff --git a/src/uncategorized/pRivalryHostage.tw b/src/uncategorized/pRivalryHostage.tw
index e94b8d581e099f434e603736639edeb3a395f4c4..24534cc2a561f5c25a1bab39d1c9340661a5f039 100644
--- a/src/uncategorized/pRivalryHostage.tw
+++ b/src/uncategorized/pRivalryHostage.tw
@@ -4,14 +4,13 @@
 
 <<set $hostageAnnounced = 1>>
 
-<<set $oneTimeDisableDisability = 1>>
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
-<<set $activeSlave = GenerateNewSlave("XX")>>
+<<set $activeSlave = GenerateNewSlave("XX", {race: _race, disableDisability: 1})>>
 <<set $activeSlave.origin = "You were acquainted with $him before you were an arcology owner; your rival tried to use $him to manipulate you, but you rescued $him.">>
 <<set $activeSlave.boobs = 400>>
 <<set $activeSlave.butt = 3>>
diff --git a/src/uncategorized/pSlaveMedic.tw b/src/uncategorized/pSlaveMedic.tw
index 736f4f573cb775c97271ff91db61455537739df3..457232b761e39599d0f0cf526b23ad39f7021f24 100644
--- a/src/uncategorized/pSlaveMedic.tw
+++ b/src/uncategorized/pSlaveMedic.tw
@@ -4,16 +4,16 @@
 
 <<set $eventResults.slaveMedic = 0>>
 
-<<set $oneTimeDisableDisability = 1>>
 <<if $pedo_mode == 1>>
-	<<set $activeSlaveOneTimeMinAge = $minimumSlaveAge>>
-	<<set $activeSlaveOneTimeMaxAge = 18>>
+	<<set _minAge = $minimumSlaveAge>>
+	<<set _maxAge = 18>>
 <<else>>
-	<<set $activeSlaveOneTimeMinAge = 25>>
-	<<set $activeSlaveOneTimeMaxAge = 42>>
+	<<set _minAge = 25>>
+	<<set _maxAge = 42>>
 <</if>>
+<<set _genParam = {minAge: _minAge, maxAge: _maxAge, disableDisability: 1}>>
 <<if $seeDicks >= 100>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", _genParam)>>
 	<<set $activeSlave.dick = 5>>
 	<<set $activeSlave.foreskin = 0>>
 	<<set $activeSlave.balls = 3>>
@@ -21,7 +21,7 @@
 	<<set $activeSlave.butt = 3>>
 	<<set $activeSlave.attrXY = 70>>
 <<else>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", _genParam)>>
 	<<set $activeSlave.vagina = 2>>
 	<<set $activeSlave.preg = -1>>
 	<<set $activeSlave.ovaries = 1>>
diff --git a/src/uncategorized/reAWOL.tw b/src/uncategorized/reAWOL.tw
index e9df601411f8f6f5b4fb25149bb73c82950e88b5..651c3acc5f6865cf8d57404255f1ebc937f58929 100644
--- a/src/uncategorized/reAWOL.tw
+++ b/src/uncategorized/reAWOL.tw
@@ -3,23 +3,22 @@
 <<set $nextButton = "Continue", $nextLink = "RIE Eligibility Check", $returnTo = "RIE Eligibility Check">>
 
 <<if $pedo_mode == 1>>
-	<<set $activeSlaveOneTimeMinAge = 21>>
+	<<set _minAge = 21>>
 <<else>>
-	<<set $activeSlaveOneTimeMinAge = 38>>
+	<<set _minAge = 38>>
 <</if>>
-<<set $activeSlaveOneTimeMaxAge = 43>>
-<<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $oneTimeDisableDisability = 1>>
+
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
+<<set _genParam = {minAge: _minAge, maxAge: 43, ageOverridesPedoMode: 1, race: _race, disableDisability: 1}>>
 <<if $seeDicks != 100>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", _genParam)>>
 <<else>>
-	<<set $activeSlave = GenerateNewSlave("XY")>>
+	<<set $activeSlave = GenerateNewSlave("XY", _genParam)>>
 <</if>>
 <<set $activeSlave.origin = "You sentenced $him to enslavement as a punishment for dereliction of $his duty to you as a mercenary and for theft.">>
 <<set $activeSlave.career = "a soldier">>
diff --git a/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw b/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
index 81ef873928769a8250fce13c17f992b965a120c4..b13df215d74c715b69b23bceea735afd08e4889d 100644
--- a/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
+++ b/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
@@ -6,10 +6,7 @@
 <<set $introType = "egyptian", $newSlavesDone = 0, $newSlaveIndex = 0, _newSlaves = []>>
 
 <<set _contractCost = 3000>>
-<<set $oneTimeDisableDisability = 1>>
-<<set $activeSlaveOneTimeMinAge = Math.max(16, $minimumSlaveAge + 3)>>
-<<set $activeSlaveOneTimeMaxAge = 29>>
-<<set $activeSlave = GenerateNewSlave()>>
+<<set $activeSlave = GenerateNewSlave(null, {minAge: Math.max(16, $minimumSlaveAge + 3), maxAge: 29, disableDisability: 1})>>
 <<run generateSalonModifications($activeSlave)>>
 <<set $activeSlave.origin = "$He offered $himself to you for enslavement hoping you would preserve $his incestuous relationship with $his sibling.">>
 <<set $activeSlave.devotion = random(5,15)>>
diff --git a/src/uncategorized/reMilfTourist.tw b/src/uncategorized/reMilfTourist.tw
index 067c85cc5a7287b1722d49feac7b012bd0a3abe7..14ca8e626ccc0c58e4bb1c663578f08268bfce12 100644
--- a/src/uncategorized/reMilfTourist.tw
+++ b/src/uncategorized/reMilfTourist.tw
@@ -6,17 +6,13 @@
 <<set _milfSlave = $slaves[_milfed]>>
 <<run Enunciate(_milfSlave)>>
 
-<<set $activeSlaveOneTimeMinAge = 36>>
-<<set $activeSlaveOneTimeMaxAge = 42>>
-<<set $one_time_age_overrides_pedo_mode = 1>> /* middle-aged */
-<<set $oneTimeDisableDisability = 1>>
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
-<<set $activeSlave = GenerateNewSlave("XX")>>
+<<set $activeSlave = GenerateNewSlave("XX", {minAge: 36, maxAge: 42, ageOverridesPedoMode: 1, race: _race, disableDisability: 1})>>
 <<set $activeSlave.origin = "$He came to your arcology as a tourist and found $himself enslaved.">>
 <<set $activeSlave.devotion = random(-70,-55)>>
 <<set $activeSlave.trust = random(-45,-25)>>
diff --git a/src/uncategorized/rePokerNight.tw b/src/uncategorized/rePokerNight.tw
index d5e1767247ec97fe3ef76c2bf1c8cfad2ef6993c..e727fda708c79ef1e84955d7f1e915a5cf938ca0 100644
--- a/src/uncategorized/rePokerNight.tw
+++ b/src/uncategorized/rePokerNight.tw
@@ -54,20 +54,17 @@ On a particularly lackadaisical evening, you find yourself alerted to a message
 				<</link>>
 				<br>
 				<<link "Up the ante">>
-					<<set $activeSlaveOneTimeMinAge = 38>>
-					<<set $activeSlaveOneTimeMaxAge = 43>>
-					<<set $one_time_age_overrides_pedo_mode = 1>>
-					<<set $oneTimeDisableDisability = 1>>
 					<<if $arcologies[0].FSSupremacistLawME == 1>>
-						<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+						<<set _race = $arcologies[0].FSSupremacistRace>>
 					<<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 						<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-						<<set $fixedRace = _races.random()>>
+						<<set _race = _races.random()>>
 					<</if>>
+					<<set _genParam = {minAge: 38, maxAge: 43, ageOverridesPedoMode: 1, race: _race, disableDisability:1}>>
 					<<if $seeDicks != 100>>
-						<<set $activeSlave = GenerateNewSlave("XX")>>
+						<<set $activeSlave = GenerateNewSlave("XX", _genParam)>>
 					<<else>>
-						<<set $activeSlave = GenerateNewSlave("XY")>>
+						<<set $activeSlave = GenerateNewSlave("XY", _genParam)>>
 					<</if>>
 					<<set $activeSlave.origin = "$He put $himself up as collateral at a poker game, and lost.">>
 					<<set $activeSlave.career = "a soldier">>
diff --git a/src/uncategorized/reRecruit.tw b/src/uncategorized/reRecruit.tw
index d0a2ce1dcb1610037dd30fb74f05c8c96d9770d3..75438776fa7c6825381604fdcb73e7a613d22dd3 100644
--- a/src/uncategorized/reRecruit.tw
+++ b/src/uncategorized/reRecruit.tw
@@ -1645,8 +1645,7 @@
 <<set $activeSlaveOneTimeMaxAge = 12>>
 <<set $oneTimeDisableDisability = 1>>
 <<set $fixedRace = "asian">>
-<<set $fixedNationality = "Stateless">>
-<<set $activeSlave = GenerateNewSlave("XX")>>
+<<set $activeSlave = GenerateNewSlave("XX", {nationality: "Stateless"})>>
 <<set $activeSlave.origin = "$He offered to sell you $his body and you graciously accepted.">>
 <<set $activeSlave.devotion = random(-45,-25)>>
 <<set $activeSlave.trust = random(-15,0)>>
@@ -1697,6 +1696,8 @@
 <<set $activeSlave.weight = random(-100, -50)>>
 <<set $activeSlave.hips = -1>>
 <<set $activeSlave.boobs += random(3,6)*100>>
+<<set $activeSlave.lactation = 1>>
+<<set $activeSlave.lactationDuration = 2>>
 <<if $activeSlave.actualAge < 13>>
 	<<set $activeSlave.career = setup.veryYoungCareers.random()>>
 <<else>>
@@ -1705,7 +1706,9 @@
 <<set $activeSlave.vagina = 3>>
 <<set $activeSlave.anus = 0>>
 <<set $activeSlave.accent = 0>>
+<<set $activeSlave.geneticQuirks.uterineHypersensitivity = 0>>
 <<set $activeSlave.preg = 0>>
+<<set $activeSlave.pregWeek = -4>>
 <<set $activeSlave.counter.births = 1>>
 <<set $activeSlave.counter.birthsTotal = 1>>
 <<set $activeSlave.bellySag = 5>>
diff --git a/src/uncategorized/reShelterInspection.tw b/src/uncategorized/reShelterInspection.tw
index 7351407d3d4b7f6202e7290bb94d9db6648029d7..0f7ca419cc211b295d9b3d485fe4077fca284e33 100644
--- a/src/uncategorized/reShelterInspection.tw
+++ b/src/uncategorized/reShelterInspection.tw
@@ -2,18 +2,15 @@
 
 <<set $nextButton = " ", $nextLink = "RIE Eligibility Check">>
 
-<<set $activeSlaveOneTimeMinAge = 36>>
-<<set $activeSlaveOneTimeMaxAge = 42>>
-<<set $one_time_age_overrides_pedo_mode = 1>>
-<<set $oneTimeDisableDisability = 1>>
+
 <<if $arcologies[0].FSSupremacistLawME == 1>>
-	<<set $fixedRace = $arcologies[0].FSSupremacistRace>>
+	<<set _race = $arcologies[0].FSSupremacistRace>>
 <<elseif $arcologies[0].FSSubjugationistLawME == 1>>
 	<<set _races = setup.filterRacesLowercase.filter(race => race !== $arcologies[0].FSSubjugationistRace)>>
-	<<set $fixedRace = _races.random()>>
+	<<set _race = _races.random()>>
 <</if>>
 <<set _ContractCost = 10000>>
-<<set $activeSlave = GenerateNewSlave("XX")>>
+<<set $activeSlave = GenerateNewSlave("XX", {minAge: 36, maxAge: 42, ageOverridesPedoMode: 1, race: _race, disableDisability: 1})>>
 <<set $activeSlave.origin = "$He was once an inspector for the Shelter, but you put a stop to that.">>
 <<set $activeSlave.career = "a charity worker">>
 <<set $activeSlave.devotion = random(-90,-75)>>
diff --git a/src/uncategorized/remoteSurgery.tw b/src/uncategorized/remoteSurgery.tw
index 27c311d0f3d86170e9e9e654c76bf6687db34ab5..9ac7709b946e758484da457335fe2e2fea8a29bb 100644
--- a/src/uncategorized/remoteSurgery.tw
+++ b/src/uncategorized/remoteSurgery.tw
@@ -8,7 +8,6 @@
 
 <<set $nextButton = "Confirm changes", $nextLink = "Slave Interact">>
 
-<<set $AS = $activeSlave.ID>>
 <<run App.Utils.setLocalPronouns(getSlave($AS))>>
 <<run updateHealth(getSlave($AS))>>
 
diff --git a/src/uncategorized/saLongTermEffects.tw b/src/uncategorized/saLongTermEffects.tw
index 50b9566585541bebaa777dbbeff2593cd6ac0b6f..219177b62d880bd9362f49c876624a06fe9a987a 100644
--- a/src/uncategorized/saLongTermEffects.tw
+++ b/src/uncategorized/saLongTermEffects.tw
@@ -6363,7 +6363,7 @@
 		<</if>>
 	<</if>>
 	<<if $slaves[$i].face > 80>>
-		<<if $slaves[$i].face <= 95>>
+		<<if $slaves[$i].face < 100>>
 			<<if $slaves[$i].trust > 95>>
 				$He is so healthy and trusting that @@.lime;the minute flaws in $his facial appearance seem to be smoothed away@@ every time $he sleeps.
 				<<= faceIncrease($slaves[$i], 5)>>
diff --git a/src/uncategorized/saRules.tw b/src/uncategorized/saRules.tw
index 5c539518af8a68bdae7ab3440e38bd9b5ec5663b..8a6e9aab602fb4b2fa20adaa8d16de614732f23a 100644
--- a/src/uncategorized/saRules.tw
+++ b/src/uncategorized/saRules.tw
@@ -1591,7 +1591,7 @@
 			<<if ($slaves[$i].attrKnown == 0)>>
 				<<if ($week-$slaves[$i].weekAcquired > 4) && $slaves[$i].energy > 20>>
 					<<set $slaves[$i].attrKnown = 1>>
-					<<= capFirstChar($assistant.name)>> has been monitoring $him choice of entertainment, analyzing $his sexuality. It seems $he is
+					<<= capFirstChar($assistant.name)>> has been monitoring $his choice of entertainment, analyzing $his sexuality. It seems $he is
 					<<saRulesAttractionDiscovery>>
 				<</if>>
 			<</if>>
diff --git a/src/uncategorized/salon.tw b/src/uncategorized/salon.tw
index 8fade6d11b58ccc74f46962690d630131f950f57..40e4e8d6b826fff50902559e31453a202499bee5 100644
--- a/src/uncategorized/salon.tw
+++ b/src/uncategorized/salon.tw
@@ -1,7 +1,6 @@
 :: Salon [nobr jump-from-safe]
 
 <<set $nextButton = "Confirm changes", $nextLink = "Slave Interact", $encyclopedia = "The Auto Salon">>
-<<set $AS = $activeSlave.ID>>
 <<run App.Utils.setLocalPronouns(getSlave($AS))>>
 
 <<if $showInches == 2>>
diff --git a/src/uncategorized/seCustomSlaveDelivery.tw b/src/uncategorized/seCustomSlaveDelivery.tw
index 0cb1f84118a80b9939e782ebaa5e8b09628d6af7..b78b56d769bf99c43d96baed374d661f66c4b4df 100644
--- a/src/uncategorized/seCustomSlaveDelivery.tw
+++ b/src/uncategorized/seCustomSlaveDelivery.tw
@@ -104,19 +104,19 @@
 <</if>>
 <<set $one_time_age_overrides_pedo_mode = 1>>
 <<if $customSlave.nationality != "Nationality is unimportant">>
-	<<set $fixedNationality = $customSlave.nationality>>
+	<<set _nationality = $customSlave.nationality>>
 <</if>>
 <<if $customSlave.race != "ethnicity is unimportant">>
 	<<set $fixedRace = $customSlave.race>>
 <</if>>
 <<if $customSlave.sex == 2>>
-	<<set $activeSlave = GenerateNewSlave("XY", {mature: 0})>>
+	<<set $activeSlave = GenerateNewSlave("XY", {mature: 0, nationality: _nationality})>>
 	<<set $activeSlave.dick = $customSlave.dick>>
 	<<set $activeSlave.balls = $customSlave.balls>>
 	<<set $activeSlave.scrotum = $activeSlave.balls>>
 	<<set $activeSlave.foreskin = $activeSlave.dick>>
 <<else>>
-	<<set $activeSlave = GenerateNewSlave("XX", {mature: 0})>>
+	<<set $activeSlave = GenerateNewSlave("XX", {mature: 0, nationality: _nationality})>>
 	<<if $customSlave.virgin == 0>>
 		<<set $activeSlave.vagina = $customSlave.virgin>>
 	<</if>>
diff --git a/src/uncategorized/slaveInteract.tw b/src/uncategorized/slaveInteract.tw
index 6af60640cd79bce7bc11834103ddb9fa5b909232..90d552b63c22c6daec5427e2ad0e8b41b0c6d180 100644
--- a/src/uncategorized/slaveInteract.tw
+++ b/src/uncategorized/slaveInteract.tw
@@ -3,7 +3,6 @@
 <<set $nextButton = "Confirm changes", $nextLink = "Main">>
 <<set _SL = $slaves.length, _CL = $canines.length, _HL = $hooved.length, _FL = $felines.length>>
 <<set $AS = $activeSlave.ID>>
-<<set $activeSlave = getSlave($AS)>>
 <<run App.Utils.setLocalPronouns(getSlave($AS))>>
 <<run Enunciate(getSlave($AS))>>
 <style>
@@ -81,15 +80,15 @@
 	</span>
 </p>
 <div class="tab-bar">
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Description'), jQuery('#LSD').empty().append(App.Desc.longSlave(getSlave(V.activeSlave.ID)))" id="tab Description">Description</button>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Description'), jQuery('#LSD').empty().append(App.Desc.longSlave(getSlave(V.AS)))" id="tab Description">Description</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Modify')" id="tab Modify">Modify</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Work')" id="tab Work">Work</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Appearance'), App.UI.Wardrobe.refreshAll(getSlave(V.activeSlave.ID))" id="tab Appearance">Appearance</button>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Appearance'), App.UI.Wardrobe.refreshAll(getSlave(V.AS))" id="tab Appearance">Appearance</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'PhysicalRegimen')" id="tab PhysicalRegimen">Physical Regimen</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Rules')" id="tab Rules">Rules</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Financial')" id="tab Financial">Financial</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Customize'), App.UI.SlaveInteract.custom(getSlave(V.activeSlave.ID))" id="tab Customize">Customize</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'family-tab'), renderFamilyTree(V.slaves, V.activeSlave.ID)" id="tab family-tab">Family</button>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Customize'), App.UI.SlaveInteract.custom(getSlave(V.AS))" id="tab Customize">Customize</button>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'family-tab'), renderFamilyTree(V.slaves, V.AS)" id="tab family-tab">Family</button>
 </div>
 
 <div id="art-frame">
@@ -146,7 +145,7 @@
 	<div class="content">
 		<p id="work"></p>
 		<script>
-			App.UI.SlaveInteract.work(getSlave(V.activeSlave.ID))
+			App.UI.SlaveInteract.work(getSlave(V.AS))
 		</script>
 	</div>
 </div>
@@ -155,56 +154,56 @@
 	<div class="content">
 		<p>
 			<span id="clothes"></span>
-			/*<script>App.UI.Wardrobe.clothes(getSlave(V.activeSlave.ID))</script>*/
+			/*<script>App.UI.Wardrobe.clothes(getSlave(V.AS))</script>*/
 
 			<<if getSlave($AS).fuckdoll == 0>>
 				<span id="collar"></span>
-				/*<script>App.UI.Wardrobe.collar(getSlave(V.activeSlave.ID))</script>*/
+				/*<script>App.UI.Wardrobe.collar(getSlave(V.AS))</script>*/
 
 				<span id="faceAccessory"></span>
 
 				<span id="mouthAccessory"></span>
 
 				<span id="armAccessory"></span>
-				/*<script>App.UI.Wardrobe.armAccessory(getSlave(V.activeSlave.ID))</script>*/
+				/*<script>App.UI.Wardrobe.armAccessory(getSlave(V.AS))</script>*/
 
 				<<if hasAnyLegs(getSlave($AS))>>
 					<span id="shoes"></span>
-					/*<script>App.UI.Wardrobe.shoes(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.shoes(getSlave(V.AS))</script>*/
 
 					<span id="legAccessory"></span>
-					/*<script>App.UI.Wardrobe.legAccessory(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.legAccessory(getSlave(V.AS))</script>*/
 				<</if>>
 
 				<span id="bellyAccessory"></span>
-				/*<script>App.UI.Wardrobe.bellyAccessory(getSlave(V.activeSlave.ID))</script>*/
+				/*<script>App.UI.Wardrobe.bellyAccessory(getSlave(V.AS))</script>*/
 
 				<span id="buttplug"></span>
-				/*<script>App.UI.Wardrobe.buttplug(getSlave(V.activeSlave.ID))</script>*/
+				/*<script>App.UI.Wardrobe.buttplug(getSlave(V.AS))</script>*/
 
 				<<if isItemAccessible.entry("tail", "buttplugAttachment") && getSlave($AS).buttplug != "none">>
 					<span id="buttplugAttachment"></span>
-					/*<script>App.UI.Wardrobe.buttplugAttachment(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.buttplugAttachment(getSlave(V.AS))</script>*/
 				<</if>>
 
 				<<if getSlave($AS).vagina > -1>>
 					<span id="vaginalAccessory"></span>
-					/*<script>App.UI.Wardrobe.vaginalAccessory(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.vaginalAccessory(getSlave(V.AS))</script>*/
 					<span id="vaginalAttachment"></span>
-					/*<script>App.UI.Wardrobe.vaginalAttachment(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.vaginalAttachment(getSlave(V.AS))</script>*/
 				<</if>>
 
 				<<if getSlave($AS).dick > 0>>
 					<span id="dickAccessory"></span>
-					/*<script>App.UI.Wardrobe.dickAccessory(getSlave(V.activeSlave.ID))</script>*/
+					/*<script>App.UI.Wardrobe.dickAccessory(getSlave(V.AS))</script>*/
 				<</if>>
 
 				<span id="chastity"></span>
-				/*<script>App.UI.Wardrobe.chastity(getSlave(V.activeSlave.ID))</script>*/
+				/*<script>App.UI.Wardrobe.chastity(getSlave(V.AS))</script>*/
 
 				<h3>Shopping</h3>
 				<span id="shopping"></span>
-				<script>App.UI.Wardrobe.shopping(getSlave(V.activeSlave.ID))</script>
+				<script>App.UI.Wardrobe.shopping(getSlave(V.AS))</script>
 
 			<</if>> /* CLOSES FUCKDOLL CHECK */
 		</p>
@@ -215,22 +214,22 @@
 	<div class="content">
 		<p>
 			<span id="drugs"></span>
-			<script>App.UI.SlaveInteract.drugs(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.drugs(getSlave(V.AS))</script>
 
 			<span id="curatives"></span>
-			<script>App.UI.SlaveInteract.curatives(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.curatives(getSlave(V.AS))</script>
 
 			<span id="aphrodisiacs"></span>
-			<script>App.UI.SlaveInteract.aphrodisiacs(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.aphrodisiacs(getSlave(V.AS))</script>
 
 			<span id="fertility-block"></span>
-			<script>App.UI.SlaveInteract.fertility(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.fertility(getSlave(V.AS))</script>
 
 			<span id="incubator"></span>
-			<script>App.UI.SlaveInteract.incubator(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.incubator(getSlave(V.AS))</script>
 
 			<span id="nursery"></span>
-			<script>App.UI.SlaveInteract.nursery(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.nursery(getSlave(V.AS))</script>
 
 			<<if $propOutcome == 1 && $arcologies[0].FSRestart != "unset">>
 				<<if getSlave($AS).breedingMark == 0 && getSlave($AS).fuckdoll == 0 && getSlave($AS).eggType == "human" && isFertile(getSlave($AS)) && getSlave($AS).preg == 0>>
@@ -240,19 +239,19 @@
 			<</if>>
 
 			<span id="bloating"></span>
-			<script>App.UI.SlaveInteract.bloating(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.bloating(getSlave(V.AS))</script>
 
 			<span id="hormones"></span>
-			<script>App.UI.SlaveInteract.hormones(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.hormones(getSlave(V.AS))</script>
 
 			<span id="diet"></span>
-			<script>App.UI.SlaveInteract.diet(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.diet(getSlave(V.AS))</script>
 
 			<span id="diet-base"></span>
-			<script>App.UI.SlaveInteract.dietBase(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.dietBase(getSlave(V.AS))</script>
 
 			<span id="snacks"></span>
-			<script>App.UI.SlaveInteract.snacks(getSlave(V.activeSlave.ID))</script>
+			<script>App.UI.SlaveInteract.snacks(getSlave(V.AS))</script>
 		</p>
 	</div>
 </div>
@@ -260,7 +259,7 @@
 <div id="Rules" class="tab-content">
 	<div class="content">
 		<span id="rules"></span>
-		<script>App.UI.SlaveInteract.rules(getSlave(V.activeSlave.ID))</script>
+		<script>App.UI.SlaveInteract.rules(getSlave(V.AS))</script>
 	</div>
 </div>
 
@@ -324,7 +323,7 @@
 
 		<h3>Financial</h3>
 		<p>
-			<<includeDOM slaveExpenses(V.activeSlave)>>
+			<<includeDOM slaveExpenses(getSlave(V.AS))>>
 		</p>
 
 		<p>
@@ -364,7 +363,7 @@
 <div id="Customize" class="tab-content">
 	<div class="content">
 		<span id="custom"></span>
-		<script>App.UI.SlaveInteract.custom(getSlave(V.activeSlave.ID))</script>
+		<script>App.UI.SlaveInteract.custom(getSlave(V.AS))</script>
 
 		/*<<include "Add custom descriptors">>*/
 	</div>
diff --git a/src/uncategorized/walkPastList.tw b/src/uncategorized/walkPastList.tw
index adc79014a67ec6b5ddc9f7e80dec35453b435af3..fece903eb53124d7c02d9829ea386e98ab0d45f3 100644
--- a/src/uncategorized/walkPastList.tw
+++ b/src/uncategorized/walkPastList.tw
@@ -10,42 +10,42 @@
 <h2>Choose target</h2>
 <<link "Lips">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FLips")>>
+	<<includeDOM walkPastAll("FLips")>>
 	<</replace>>
 <</link>><br>
 <<link "Boobs">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FBoobs")>>
+	<<includeDOM walkPastAll("FBoobs")>>
 	<</replace>>
 <</link>><br>
 <<link "Butt">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FButt")>>
+	<<includeDOM walkPastAll("FButt")>>
 	<</replace>>
 <</link>> |
 <<link "Anus">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FAnus")>>
+	<<includeDOM walkPastAll("FAnus")>>
 	<</replace>>
 <</link>> |
 <<link "Vagina">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FVagina")>>
+	<<includeDOM walkPastAll("FVagina")>>
 	<</replace>>
 <</link>> //or ass if they lack one//<br>
 <<link "Dick">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FDick")>>
+	<<includeDOM walkPastAll("FDick")>>
 	<</replace>>
 <</link>><br>
 <<link "Rival">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FRival")>>
+	<<includeDOM walkPastAll("FRival")>>
 	<</replace>>
 <</link>><br>
 <<link "Relation">>
 	<<replace "#wplist">>
-	<<print walkPastAll("FRelation")>>
+	<<includeDOM walkPastAll("FRelation")>>
 	<</replace>>
 <</link>>
 
diff --git a/src/utility/slaveCreationWidgets.tw b/src/utility/slaveCreationWidgets.tw
index a5f750d8e5e63c400eb005c629fd4e7d67b727ad..37743cf33fdaf899db4e4357d6baa1e5bddc69a5 100644
--- a/src/utility/slaveCreationWidgets.tw
+++ b/src/utility/slaveCreationWidgets.tw
@@ -648,11 +648,7 @@
  Call as <<JFCSlave>>
 %/
 <<widget "JFCSlave">>
-	<<set $activeSlaveOneTimeMinAge = 20>>
-	<<set $activeSlaveOneTimeMaxAge = 36>>
-	<<set $one_time_age_overrides_pedo_mode = 1>>
-	<<set $oneTimeDisableDisability = 1>>
-	<<set $activeSlave = GenerateNewSlave("XX")>>
+	<<set $activeSlave = GenerateNewSlave("XX", {minAge: 20, maxAge: 36, ageOverridesPedoMode: 1, disableDisability: 1})>>
 	<<set $activeSlave.weight = random(-30,20), $activeSlave.waist = random(-30,10), $activeSlave.face = random(40,60)>>
 	<<if $activeSlave.faceShape == "masculine">>
 		<<set $activeSlave.faceShape = "sensual">>
diff --git a/submodules/sugarcube-2 b/submodules/sugarcube-2
index c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f..7bf41720322c05d1ada96df689644fac938f9ffc 160000
--- a/submodules/sugarcube-2
+++ b/submodules/sugarcube-2
@@ -1 +1 @@
-Subproject commit c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f
+Subproject commit 7bf41720322c05d1ada96df689644fac938f9ffc
diff --git a/themes/light/main.css b/themes/light/main.css
index 0e05e085faf9561d8d707ec5c949256593b852ad..bb19aad20a90d43911f0aa1687dbce8a2d0e35eb 100644
--- a/themes/light/main.css
+++ b/themes/light/main.css
@@ -1,3 +1,7 @@
 .lrgRender > div.mask, .lrgVector > div.mask {
 	background: linear-gradient(90deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.8) 60%, rgba(255, 255, 255, 0));
 }
+
+.clear-formatting {
+	color: var(--text-color);
+}