${escapeHTMLSpecialChars(this.$key)} = ${escapeHTMLSpecialChars(this.$value)}
, but for an inline item to be consumable, it must be of the format a = {b|c|d}
and cannot have anything outside of the curly brackets (e.g. you could not make a = {b|c|d}efg
consumable).`, this.$declarationLineNumber);
let result = evaluateText(this.$root, this.$parent, this.$value, {declarationLineNumber:this.$declarationLineNumber});
if(opts.keepEscapes) return result;
else return processEscapedCharacters(result);
}
}
// toString treats childless nodes differently to ones that have children (i.e. items that are themselves lists)
// this sounds like a terrible idea at first, but I've put a bit of thought into it and it seems like the best option
// so that newbies don't have to understand how the engine works just to simply do: [f = flower.selectOne]. The
// alternative would be to make them do [f = flower.selectOne.itemName] which is ridiculous.
// If you think about it, this approach actually makes a bit of sense. If `this` node is itself a list, then that's
// an important fundamental difference, and so a difference in behaviour isn't really too unexpected.
// What we're basically saying is that leaves are treated differently to branches. The awesome part about this
// approach is that it alls us to do `[a = animal.selectOne] ... [a.genus]` - i.e. we can access the *properties*
// of the selected animal. [a=animal.outputText] doesn't allow us to do this.
if(this.$children.length === 0) {
let result = evaluateText(this.$root, this.$parent, this.$text, {declarationLineNumber:this.$declarationLineNumber});
if(opts.keepEscapes) return result;
else return processEscapedCharacters(result);
}
if(this.$children.length > 0) {
let child = this.selectOne
if(typeof child === 'string') { // e.g., if it is an error message
return child;
}
let text = child.$text;
let declarationLineNumber = child.$declarationLineNumber;
let result = evaluateText(this.$root, child.$parent, text, {declarationLineNumber});
if(opts.keepEscapes) return result;
else return processEscapedCharacters(result);
}
};
// let addItemMethod = function(key) {
// this.$children.push(key);
// this[key] = undefined;
// console.log("THIS NEEDS FIXING! (see TODO, below this line)")
// // TODO: fix this? what if they want to add a child of the added child? needs to be a *proper* node?
// // TODO: implement $addChildren and $removeChildren methods ($addChild and $removeChild should really just be aliases of these)
// };
// let removeItemMethod = function(key) {
// this.$children.filter(k => k !== key);
// delete this[key];
// // TODO: verify that this works and makes sense
// };
let $oddsMethod = function() {
if(this.$oddsText == '1') return 1;
if( String(Number(this.$oddsText)) === this.$oddsText ) return Number(this.$oddsText);
// NOTE: must use $oddsText odds rather than this.$odds, because this is the definition of $odds! (it'd be recursive)
if(typeof this.$oddsText !== 'string') {
perchanceError(`This shouldn't happen. This may be a problem with the Perchance engine. Please report this bug to reddit.com/r/perchance. The odds property of this node isn't a string. Here's some extra details for the bug report: this.$text=${escapeHTMLSpecialChars(this.$text)}; this.$children=${escapeHTMLSpecialChars(this.$children.join(","))}; this.$declarationLineNumber=${escapeHTMLSpecialChars(this.$declarationLineNumber)}; this.$oddsText=${escapeHTMLSpecialChars(this.$oddsText)}`);
return 1;
}
let evaluatedOdds = oddsTextToNumber(this.$root, this.$parent, this.$oddsText, {declarationLineNumber:this.$declarationLineNumber});
if(typeof evaluatedOdds === 'number') {
return evaluatedOdds;
} else {
// TODO: potentially convert strings to numbers using eval (if it is a number, obviously). The reason
// I'm not doing it yet is because I may do string->number conversions automatically across the whole system.
// i.e. in the evaluateSquareBlock function itself.
perchanceError(`The '^' character is used to specify how likely an item is of being chosen during a random selection. This line appears to have a '^' character, but the text after that character (${escapeHTMLSpecialChars(this.$oddsText)}) isn't a number, or didn't evaluate to a number. You're allowed to use square and curly bracket expressions to randomly/dynamically determine the odds, but these expressions must evaluate to a number. In this case your odds expression didn't result in a number.`, this.$declarationLineNumber);
return 1;
}
};
// let shuffleMethod = function(numShuffles) {
//
// // TODO***: can't order object keys, so this method is useless on nodes (but useful for arrays and strings)
//
// if(numShuffles === undefined) {
//
// var array = this.$children;
// var currentIndex = array.length, temporaryValue, randomIndex;
// // While there remain elements to shuffle...
// while (0 !== currentIndex) {
// // Pick a remaining element...
// randomIndex = Math.floor(Math.random() * currentIndex);
// currentIndex -= 1;
// // And swap it with the current element.
// temporaryValue = array[currentIndex];
// array[currentIndex] = array[randomIndex];
// array[randomIndex] = temporaryValue;
// }
// array = array.map(c => this[c]); // map to actual node objects
// array.toString = function() { this.join(""); }
// return array;
//
// } else {
//
// let text = this.toString().split("");
// for(let i = 0; i < numShuffles; i++) {
// let i1 = Math.floor(text.length*Math.random());
// let i2 = Math.floor(text.length*Math.random());
// let c1 = text[i1];
// let c2 = text[i2];
// text[i1] = c2;
// text[i2] = c1;
// }
// return text.join("");
//
// }
//
// }
let joinItemsMethod = function(str) {
if(this.$output !== undefined) {
return this.$output.toString();
}
let arr = [];
for(let c of this.$children) {
arr.push(this[c].getName);
}
return arr.joinItems(str+"");
};
let selectUniqueMethod = function(...a) {
return this.consumableList.selectMany(...a);
};
let selectManyMethod = function(...a) {
let num;
if(a.length === 1) {
if(Array.isArray(a[0])) {
num = Number(a[0][Math.floor(Math.random()*a[0].length)]);
} else {
num = Number(a[0]);
}
} else if(a.length === 2) {
num = Number(a[0]) + Math.round(Math.random()*(a[1]-a[0]));
} else if(a.length > 2) {
num = Number(a[Math.floor(Math.random()*a.length)]);
}
if(isNaN(num) || typeof num !== 'number') {
if(a.length === 0) {
perchanceError(`You didn't give the selectMany() function any inputs when you used it on the node with the text "${escapeHTMLSpecialChars(this.$text || this)}". You need to give it at least one input so it knows how long the list/array should be.`);
} else {
perchanceError(`There's a problem with the inputs that you provided to the "selectMany" function when you used it on the node with the text "${escapeHTMLSpecialChars(this.$text || this)}". You have given it the following input${a.length == 1 ? "" : "s"}: ${escapeHTMLSpecialChars(a.join(", "))}. ${a.length == 1 ? "This" : "These"} input${a.length == 1 ? "" : "s"} should be numeric (i.e. ${a.length == 1 ? "it" : "they"} should be${a.length == 1 ? " a " : " "} number${a.length == 1 ? "" : "s"}).`);
}
return ["(syntax error)"];
}
let arr = [];
for(let i = 0; i < num; i++) {
arr.push(this.selectOne);
}
// overwrite default array behaviour (selects a random one)
// in the case where they don't specify a join() after the repeat:
arr.toString = function() { return this.join(""); };
return arr;
};
let titleCaseMethod = function() {
return this.toString().split(' ').map((s) => (s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase())).join(' ');
};
let sentenceCaseMethod = function() {
return this.toString().split(/([!?.]+)/g).map((s) => {
let i = s.search(/[a-zA-Z]/);
let a = s.slice(0, i);
let b = s.slice(i, i+1).toUpperCase();
let c = s.slice(i+1);
return a + b + c;
}).join("");
};
let upperCaseMethod = function() {
//debugger;
return this.toString().toUpperCase();
};
let lowerCaseMethod = function() {
return this.toString().toLowerCase();
};
let pluralFormMethod = function() {
// TODO***: fix this! if it doesn't detect it as a noun it omits it or ignores it - need a really good (maybe seperate from compromise?) lib to handle this
let input = this.toString();
let output = nlp(input).nouns().toPlural().out("text");
return output === "" ? input : output;
};
let singularFormMethod = function() {
// TODO***: fix this! if it doesn't detect it as a noun it omits it or ignores it - need a really good (maybe seperate from compromise?) lib to handle this
let input = this.toString();
let output = nlp(input).nouns().toSingular().out("text");
return output === "" ? input : output;
};
let nlpCompromiseAddedWords = {abolish:"Verb",abound:"Verb",abstract:"Verb",accent:"Verb",accomplish:"Verb",admonish:"Verb",alert:"Verb",ally:"Verb",appropriate:"Verb",astonish:"Verb",auction:"Verb",audition:"Verb",average:"Verb",awake:"Verb",award:"Verb",back:"Verb",backpedal:"Verb",banish:"Verb",bank:"Verb",bankrupt:"Verb",better:"Verb",bill:"Verb",blacklist:"Verb",bless:"Verb",blind:"Verb",bloody:"Verb",blossom:"Verb",board:"Verb",bob:"Verb",bombard:"Verb",bottle:"Verb",brave:"Verb",breakfast:"Verb",brief:"Verb",bus:"Verb",busy:"Verb",butcher:"Verb",butter:"Verb",cake:"Verb",calm:"Verb",captain:"Verb",care:"Verb",cash:"Verb",caution:"Verb",center:"Verb",cherish:"Verb",christen:"Verb",chronicle:"Verb",chuck:"Verb",circumvent:"Verb",clean:"Verb",clear:"Verb",club:"Verb",commission:"Verb",complete:"Verb",comply:"Verb",conceal:"Verb",condition:"Verb",content:"Verb",contrive:"Verb",cool:"Verb",correct:"Verb",corrupt:"Verb",critique:"Verb",cup:"Verb",dawn:"Verb",degenerate:"Verb",demolish:"Verb",deprive:"Verb",derive:"Verb",design:"Verb",diminish:"Verb",disable:"Verb",discard:"Verb",disregard:"Verb",don:"Verb",double:"Verb",down:"Verb",dry:"Verb",dull:"Verb",elaborate:"Verb",embellish:"Verb",empty:"Verb",engineer:"Verb",enlist:"Verb",equal:"Verb",erect:"Verb",exact:"Verb",faint:"Verb",fake:"Verb",fancy:"Verb",fine:"Verb",firm:"Verb",fish:"Verb",fit:"Verb",flatter:"Verb",flicker:"Verb",flourish:"Verb",fly:"Verb",ford:"Verb",forward:"Verb",foster:"Verb",foul:"Verb",free:"Verb",frequent:"Verb",fund:"Verb",furnish:"Verb",further:"Verb",garnish:"Verb",gossip:"Verb",grace:"Verb",grant:"Verb",ground:"Verb",group:"Verb",herald:"Verb",hoist:"Verb",hollow:"Verb",hope:"Verb",house:"Verb",hum:"Verb",humble:"Verb",ice:"Verb",impoverish:"Verb",indent:"Verb",index:"Verb",initial:"Verb",institute:"Verb",inventory:"Verb",knot:"Verb",last:"Verb",lavish:"Verb",lean:"Verb",light:"Verb",lobby:"Verb",long:"Verb",lower:"Verb",lunch:"Verb",mail:"Verb",man:"Verb",mark:"Verb",marshal:"Verb",mature:"Verb",mean:"Verb",mellow:"Verb",milk:"Verb",mimic:"Verb",mine:"Verb",model:"Verb",moderate:"Verb",motion:"Verb",mute:"Verb",narrow:"Verb",near:"Verb",nestle:"Verb",nick:"Verb",number:"Verb",nurse:"Verb",obscure:"Verb",oil:"Verb",open:"Verb",outlive:"Verb",overcrowd:"Verb",overdo:"Verb",overeat:"Verb",overflow:"Verb",overhaul:"Verb",overhear:"Verb",overheat:"Verb",overload:"Verb",overlook:"Verb",overpower:"Verb",overrule:"Verb",oversee:"Verb",overshadow:"Verb",oversleep:"Verb",overthrow:"Verb",overturn:"Verb",own:"Verb",panic:"Verb",part:"Verb",partition:"Verb",patent:"Verb",patrol:"Verb",pedal:"Verb",pepper:"Verb",perfect:"Verb",perish:"Verb",petition:"Verb",plant:"Verb",please:"Verb",ply:"Verb",police:"Verb",polish:"Verb",position:"Verb",post:"Verb",pound:"Verb",power:"Verb",press:"Verb",pressure:"Verb",prime:"Verb",pucker:"Verb",punish:"Verb",query:"Verb",quiz:"Verb",rain:"Verb",rally:"Verb",ration:"Verb",rear:"Verb",rebel:"Verb",rebound:"Verb",refurbish:"Verb",relish:"Verb",repeal:"Verb",replenish:"Verb",requisition:"Verb",research:"Verb",reserve:"Verb",rev:"Verb",revive:"Verb",right:"Verb",ring:"Verb",rival:"Verb",round:"Verb",salt:"Verb",sanction:"Verb",scent:"Verb",school:"Verb",secure:"Verb",separate:"Verb",service:"Verb",shut:"Verb",signal:"Verb",silhouette:"Verb",single:"Verb",slow:"Verb",smooth:"Verb",snicker:"Verb",snow:"Verb",sour:"Verb",space:"Verb",spare:"Verb",speed:"Verb",spike:"Verb",spiral:"Verb",spy:"Verb",square:"Verb",stable:"Verb",station:"Verb",steady:"Verb",steam:"Verb",stuff:"Verb",sue:"Verb",tame:"Verb",tan:"Verb",taper:"Verb",tarnish:"Verb",tender:"Verb",tense:"Verb",thin:"Verb",thunder:"Verb",till:"Verb",time:"Verb",top:"Verb",total:"Verb",trouble:"Verb",undercut:"Verb",underline:"Verb",undertake:"Verb",undervalue:"Verb",up:"Verb",upset:"Verb",utter:"Verb",vanish:"Verb",varnish:"Verb",void:"Verb",wade:"Verb",warm:"Verb",water:"Verb",weather:"Verb",wed:"Verb",welcome:"Verb",woo:"Verb",wound:"Verb"};
let pastTenseMethod = function() {
let word = this.toString();
if(word === "hope" || word === "hopes" || word === "hoped" || word === "hoping") return "hoped";
if(word === "bill" || word === "bills" || word === "billed" || word === "billing") return "billed";
if(word === "dawn" || word === "dawns" || word === "dawned" || word === "dawning") return "dawned";
//return nlp(this.toString(), nlpCompromiseAddedWords).sentences().toPastTense().out("text")
let out = nlp("They "+this.toString(), nlpCompromiseAddedWords).verbs().conjugate()[0];
return out ? out.PastTense : this.toString()+"ed";
};
let futureTenseMethod = function() {
let word = this.toString();
if(word === "hope" || word === "hopes" || word === "hoped" || word === "hoping") return "will hope";
if(word === "bill" || word === "bills" || word === "billed" || word === "billing") return "will bill";
if(word === "dawn" || word === "dawns" || word === "dawned" || word === "dawning") return "will dawn";
//return nlp(this.toString(), nlpCompromiseAddedWords).sentences().toFutureTense().out("text")
let out = nlp("They "+this.toString(), nlpCompromiseAddedWords).verbs().conjugate()[0];
return out ? out.FutureTense : this.toString()+"ed";
};
let presentTenseMethod = function() {
let word = this.toString();
if(word === "hope" || word === "hopes" || word === "hoped" || word === "hoping") return "hopes";
if(word === "bill" || word === "bills" || word === "billed" || word === "billing") return "bills";
if(word === "dawn" || word === "dawns" || word === "dawned" || word === "dawning") return "dawns";
//return nlp(this.toString(), nlpCompromiseAddedWords).sentences().toPresentTense().out("text")
let out = nlp("They "+this.toString(), nlpCompromiseAddedWords).verbs().conjugate()[0];
return out ? out.PresentTense : this.toString()+"ed";
};
let negativeFormMethod = function() {
return nlp(this.toString()).sentences().toNegative().out("text");
};
// let ordinalMethod = function() {
// return nlp.getOrdinal(this.toString());
// };
// let withArticleMethod = function() {
// return nlp.getWithArticle(this.toString());
// };
function evaluateSquareBlock(root, thisRef, expression, ctxInfo={}) {
// NOTE: if the `with` keyword ever gets actually deprecated (very, very unlikely - 2ality's
// article is a bit misleading I think), you can just craft up a function declaration with each
// of the root variables so they get passed in as the evaled function's arguments (simple!)
// new Function(param1, ..., paramN, funcBody) -> http://www.2ality.com/2014/01/eval.html
// only thing is: i need to make sure the getters are still triggered on the proxy when I grab the properties (why wouldn't they be?). hmm
// oh but also I don't want to trigger ALL of them (could be a couple of thousand params for every single square bloc executed...)
let originalExpression = expression;
expression = expression.trim();
//expression = prependReturnKeywordIfNeeded(expression);
//let evalJSCode = eval.bind(thisRef);
// **WITHIN a node**, we don't want references to `this` to result in "$output"
// Otherwise we're very limited in terms of how we can interact with "local" items (can't use selectOne, etc. EVEN IN `$output` (since it would reference itself no matter what))
let proxiedThisRef;
if(Object.getOwnPropertyDescriptor(thisRef, "$output")) {
proxiedThisRef = new Proxy(thisRef, {
get: function(target, property, receiver) {
if(property === "$output") {
return undefined;
} else if(property === "___isLocalCall") {
return true;
} else {
let desc = Object.getOwnPropertyDescriptor(target, property);
if(!desc) {
//perchanceError(`The '${property}' property doesn't exist within '${target.$text}'.`, ctxInfo.declarationLineNumber);
return undefined;
}
if(desc.get) {
return desc.get.bind(proxiedThisRef)();
} else if(desc.value && typeof desc.value === 'function') {
return desc.value.bind(proxiedThisRef);
} else {
return target[property];
}
}
}
});
thisRef = proxiedThisRef;
}
try {
let tempHasHandlerHolder;
if(root.___isProxy) {
tempHasHandlerHolder = root.___proxyHandler.has;
root.___proxyHandler.has = window.rootProxyHasHandler_Greedy.bind(root.___proxyHandler);
}
let result = (function(){ return eval("with(root){"+expression+"}"); }).apply(thisRef);
if(root.___isProxy) root.___proxyHandler.has = tempHasHandlerHolder; //.bind(root.___proxyHandler);
if(result === undefined) {
perchanceError(`The expression '[${escapeHTMLSpecialChars(originalExpression)}]' returned nothing (undefined
). There doesn't seem to be anything particularly wrong with your syntax, but you may be trying to reference a list, property or function that doesn't exist. Here are some common causes of this error: "+moduleName+"
has $output
set to [this]
. Perchance does this by default, so this line should be removed. That is, if you don't specify a top-level $output
(the list you want to export) for your generator, then Perchance will export your whole hierarchy.", nodeData.declarationLineNumber);
return;
}
Object.defineProperty(nodeData.parentNode, lines[i].primitiveKey, {
get:function() {
// MARKER:odj29hfi3j0d2kj0hx24f
if(node.$isPlainPrimitive) {
// When a user writes `thing.prop` we want them to be able to access the proper string (with escape chars removed), but the problem
// is that we use this same getter in the evaluateText process, and we definitely don't want to remove escapes during that process
// because it gets recursively evaluated so we'd be removing backslashes and evaluating blocks that should be evaluated. The toString
// method also had this problem, but I solved it by passing in a {keepEscapes:true} object when it is used in the evaluateText process,
// but since this is a getter, we can't pass in arguments, so I've used mega hax to do it (use global var). See the evaluateText function
// file for the actual location where this var is altered. It should always be false except for when it is used during the evaluateText process.
if(typeof node.$value === "string" && !window.__primitiveValueGetterKeepEscapesYolo) return processEscapedCharacters(node.$value);
else return node.$value;
}
if(typeof node.$value === 'number') {
return node.$value;
} else if(typeof node.$value === 'string') {
let splitted = splitTextAtAllBlocks(node.$value);
if(splitted.length === 1) {
if(splitted[0][0] === "[" /*&& splitted[0][splitted[0].length-1] === "]"*/) {
// we don't want to evaluateText on this because it'll be coerced into a string (we want to preserve "direct" references)
let expression = node.$value.substr(1,node.$value.length-2);
let result = evaluateSquareBlock(node.$root, node.$parent, expression, {declarationLineNumber:node.$declarationLineNumber});
return result;
} else if(splitted[0][0] === "{" /*&& splitted[0][splitted[0].length-1] === "}"*/) {
//let expression = node.$value.substr(1,node.$value.length-2);
//return evaluateCurlyBlock(node.$root, node.$parent, expression, {declarationLineNumber:node.$declarationLineNumber});
//return "{"+expression+"}";
return node; // for square blocks we evaluate it to get a direct reference, but there's no need for this with curly blocks (we let toString do the evalation)
} else {
// // plain text (no curly or square blocks) - LATER: why do we need to evaluate it then? not necessary?
//return evaluateText(node.$root, node.$parent, node.$value, {declarationLineNumber:node.$declarationLineNumber});
return processEscapedCharacters(node.$value); // <-- WAIT - this will never occur?? The if(node.$isPlainPrimitive) above will catch all plain text nodes.
}
} else {
// we've found a non-direct reference that's not plain text.
// we need to return the *actual node* because otherwise if we have `output = [prefix]-blah` (for example)
// then we couldn't call `output.selectMany(3)` on it because `output` would be a plain string (like "pseudo-blah", for example).
// note that when used on $value nodes, selectOne will just return the node itself
return node;
// var n = node.createClone;
// n.$nodeType = "text";
// Object.defineProperty(lines[i].node, "$text", {value:n.$value, writable:true, configurable:true});
// Object.defineProperty(lines[i].node, "$oddsText", {value:"1", writable:true, configurable:true});
// delete n.$key;
// delete n.$value;
// return n;
}
} else if(typeof node.$value === 'boolean') {
return node.$value;
} else {
//console.error("Something went wrong? $value should be either a number, string, or boolean, right?");
return node.$value; // June 11th 2019: It's a direct reference to another node, or a reference to an objext or something. (I thought I was tyding direct references up somewhere else, but that wouldn't cut it anyway because they can change the value now that there's a setter (e.g. to a POJO or whatever))
}
},
set:function(v) { // June 11th 2019: Added this setter - why wasn't it set already? If it's not set then we can't change it and we get problems like this: https://www.reddit.com/r/perchance/comments/bz2smn/one_of_the_example_generator_is_broken_i_think_i/
node.$value = v;
return v;
},
configurable:true,
});
Object.defineProperty(lines[i].node, "$nodeType", {value:"value", writable:true, configurable:true});
Object.defineProperty(lines[i].node, "$text", {value:lines[i].primitiveKey, writable:true, configurable:true});
nodeData.parentNode.$valueChildren.push(lines[i].primitiveKey);
}
}
// add $children property to each node (an array of all of the keys of the node's non-primitive, non-function children that are leaf nodes (i.e. that are themselves childless))
// TODO: final confirmation: this should be just the keys, right? or would it be more useful to people as nodes? what are use cases of this property? [animal.$children] - actually yeah, it makes sense that it returns a key - if it returned an object then a random child of THAT object would be chosen!
for(let node of allNodes) {
let keys = Object.keys(node).filter(k => node[k].$nodeType === 'normal').sort((k1,k2) => node[k1].$declarationLineNumber-node[k2].$declarationLineNumber); // filter out primitives and sort by the actual order of the list
Object.defineProperty(node, "$children", {value:keys, writable:true, configurable:true});
}
// // error if any of the **direct children of root** aren't valid javascript identifiers.
// // this is necessary for two reasons:
// // 1. calling [else] will throw an error - but [thing.else] will not
// // 2. calling [my list] will throw an error - but [thing["my list"]] will not
// let warnLineNumbers = [];
// for(let key of root.node.$children) {
// if(!isValidJavaScriptIdentifier(key)) {
// warnLineNumbers.push( root.node[key].$declarationLineNumber );
// }
// }
// if(warnLineNumbers.length > 0) {
// if(warnLineNumbers.length > 10) {
// warnLineNumbers = warnLineNumbers.slice(1,9);
// warnLineNumbers.push("...");
// }
// console.warn(`Warning at line number${(warnLineNumbers.length > 1 ? "s" : "")+" "+warnLineNumbers.join(", ")}. All *top-level* items must only include letters, numbers and underscore characters (no spaces or special characters). They also must not start with a number. They also must not be any of these "reserved" names: break, do, instanceof, typeof, case, else, new, var, catch, finally, return, void, continue, for, switch, while, debugger, function, this, with, default, if, throw, delete, in, try class, enum, extends, super, const, export, import, implements, interface, let, package, private, protected, public, static, yield`);
// }
// TODO: add module name to all errors, or print module name when you catch errors
// TODO: add "need help?" link to all errors that links to community (reddit, probably)
// extract import statements:
let importedModuleNamesArray = [];
ignorePerchanceErrors = true;
for(let line of lines) {
if(line.nodeType === 'function' || line.node.$isPlainPrimitive) continue;
let names = collectImportedModuleNamesFromText(line.text);
importedModuleNamesArray.push(...names);
}
ignorePerchanceErrors = false;
Object.defineProperty(root.node, "$imports", {value:[...new Set(importedModuleNamesArray)], writable:true, configurable:true});
Object.defineProperty(root.node, "$allNodes", {value:allNodes, writable:true, configurable:true});
// for(let node of allNodes) {
// if(node.$constructorFunction.prototype !== undefined) {debugger;}
// Object.defineProperties(node.$constructorFunction.prototype, Object.getOwnPropertyDescriptors(node));
// }
const allowedDollarVariables = ["$output", "$preprocess"];
for(let node of root.node.$allNodes) {
if(node.$nodeType === "value" && node.$text[0] === "$" && !allowedDollarVariables.includes(node.$text)) {
perchanceError(`There's a problem with the '${moduleName}' generator. You've created a property with a name that starts with "$". This is highly discouraged because dollar-sign variables are reserved for special features such as $output and $preprocess. New features that are added to Perchance in the future may use other dollar-sign variable names and these may conflict with your variable-name choice and thus cause errors.`, node.$declarationLineNumber);
continue;
}
if(node.$parent !== root.node) continue;
if(!isValidJavaScriptIdentifier(node.$text) || node.$text === "update") {
perchanceError(`There's a problem with the '${moduleName}' generator. You've created a top-level list called "${escapeHTMLSpecialChars(node.$text)}", which is not allowed. Unfortunately top-level list names are subject to some strict rules:${this.capturedCalls.map(c=>c.name).join("→")}
doesn't seem to exist in the ${target.obj.$moduleName}
generator.`);
// if(property === Symbol.toPrimitive) return function() { return "(error)"; }
return "(error)";
}
}
let finalResult = result[property];
if(typeof finalResult === 'function' && !finalResult.___isProxy) {
finalResult = finalResult.bind(result);
}
return finalResult;
} else {
// need to return new proxy
let newHandler = {};
Object.assign(newHandler, this);
newHandler.capturedCalls = this.capturedCalls.slice(0);
newHandler.capturedCalls.push({type:"get", name:property});
let np = new Proxy(target, newHandler)
return np;
}
},
apply: function(target, thisArg, args) {
// // return a new proxy:
// let newHandler = {};
// Object.assign(newHandler, this);
// newHandler.capturedCalls = this.capturedCalls.slice(0);
// // add arguments to last call that was captured
// newHandler.capturedCalls.push({type:"apply", args});
// let np = new Proxy(target, newHandler);
// return np;
// i commented out the above code and added this because we always want functions to resolve immediately. otherwise we get this problem (see comment in bottom left): https://i.imgur.com/SY9MOt8.png -- as another example, people needed to write [a = dice("1d6").selectOne] instead of just [a = dice("1d6")]
// maybe I should make exceptions for joinItems and those other inbuilt functions?
this.capturedCalls.push({type:"apply", args});
return this.executeChain(target, this.capturedCalls);
},
set: function(target, property, value, receiver) {
// resolve it if it's a proxy (to prevent infinite loops when you do something like [abc=abc] (where abc is an already-defined top-level node), since that would set abc to its own proxy, so when you resolve it, it would resolve to the proxy, and the loop continues)
if(value && value.___isProxy) {
value = value.___proxyHandler.executeChain(value.___proxyTarget, value.___proxyHandler.capturedCalls);
}
let obj = this.executeChain(target, this.capturedCalls);
if(typeof obj !== 'object') {
perchanceError(`You tried to set ${escapeHTMLSpecialChars(this.capturedCalls.map(c=>c.name).join("."))}
to ${escapeHTMLSpecialChars(value)}
, but it appears that ${escapeHTMLSpecialChars(this.capturedCalls.map(c=>c.name).join("."))}
does not exist, or some other strange error has occurred.`);
return;
}
Object.defineProperty(obj, property, {value, writable:true, configurable:true}); // importantly we're executing defineProperty on an actual object, NOT a proxy
this.userDefinedNonNodePropertyIds[ this.getUserDefinedNonNodePropertyId(property, this.capturedCalls) ] = true;
return value;
},
isExtensible: function(target) { return Object.isExtensible(this.executeChain(target, this.capturedCalls)); },
preventExtensions: function(target) { return Object.preventExtensions(this.executeChain(target, this.capturedCalls)); },
getOwnPropertyDescriptor: function(target, prop) { return Object.getOwnPropertyDescriptor(this.executeChain(target, this.capturedCalls), prop); },
defineProperty: function(target, property, descriptor) { return Object.defineProperty(this.executeChain(target, this.capturedCalls), property, descriptor); },
has: function(target, prop) { return (prop in this.executeChain(target, this.capturedCalls)); },
deleteProperty: function(target, property) { return delete this.executeChain(target, this.capturedCalls)[property]; },
ownKeys: function(target) { return Reflect.ownKeys(this.executeChain(target, this.capturedCalls)); }
});
window.rootProxyHasHandler = rootProxy.___proxyHandler.has;
// add this module to modulespace now otherwise our functions won't work for the direct-reference checks
// yolo
window.moduleSpace[moduleName] = rootProxy;
// EDIT: don't need this any more now that we've got isFunctionNode?
// make user-defined functions execute immediately upon being called (i.e. don't return a proxy)
// for(let i = 0; i < allNodes.length; i++) {
// if(allNodes[i].$nodeType === "function") {
// let node = allNodes[i];
// // we found a function, now lets build a call chain to it
// let calls = [];
// while(node.$parent) {
// calls.unshift(node.$parent.$text);
// node = node.$parent;
// }
// calls.shift(); // remove "$preprocess
function must be placed above all your other lists - at the very top of the Perchance code editor.`);
return;
}
if(root.node.$preprocess && !doNotPreprocess) {
let preprocess;
if(typeof root.node.$preprocess.nodeType === 'function') {
preprocess = root.node.$preprocess;
} else {
if(!root.node.$preprocess.$value.startsWith("{import:")) return perchanceError(`There's a problem with the '${moduleName}' generator. The special $preprocess
property must be defined as a function, or an imported function.`);
let preprocessorName = root.node.$preprocess.$value.slice(8, -1);
let module = window.moduleSpace[preprocessorName];
if(!module) return perchanceError(`There's a problem with the '${moduleName}' generator. The preprocessor '${preprocessorName}' could not be found?`);
if(!module.$output || !module.$output.$nodeType === "function") return perchanceError(`There's a problem with the '${moduleName}' generator. The '${preprocessorName}' was imported into '${moduleName}' as a preprocessor function, but the $output
of '${preprocessorName}' doesn't exist or is not a function?`);
preprocess = module.$output;
}
let newInputText = preprocess(root.perchanceCode);
return createPerchanceTree(newInputText, moduleName, backupModuleName, true);
}
return rootProxy;
//return root.node;
// notes:
// TODO: remove todos that are already done...
// TODO: primitive declarations using "=" and update toString so it ignores primitive-valued properties
// TODO: in toString: add {a|b|c} notation handling (recusive)
// TODO: add array handling: we just treat ["a", "b^2", "c"] like {a|b^2|c} when Array.toString() is called (use the same back-end function)
// TODO: if we across duplicate sibling nodes, what do we do? just make the latter overwrite the prior? add the odds together?
}
// this is very hacky...
function duplicatePerchanceNode(originalNode) {
originalNode = originalNode.getSelf;
// NOTE: node will be unreferencable from parent (since the node to be duplicated already holds that key).
// I think this is fine? They can always add it to the parent if some weird situation required that.
let root = createPerchanceTree(originalNode.$perchanceCode, null, originalNode.$moduleName);
root = root.getSelf; // get target object, not proxy
for(let node of root.$allNodes) {
if(node === root) continue;
node.$id = originalNode.$root.getSelf.$allNodes.length;
originalNode.$root.getSelf.$allNodes.push(node);
node.$root = originalNode.$root.getSelf;
node.$moduleName = originalNode.$moduleName;
// I think this is causing some problems when it tries to get a node of a "directly linked" property:
//node.$declarationLineNumber = getCorrespondingOriginalNode(node).$declarationLineNumber;
}
function getCorrespondingOriginalNode(clonedNode) {
let propertyChain = [];
let n = clonedNode;
while(n.$parent) { // get chain up to cloned root
propertyChain.unshift(n.$text);
n = n.$parent;
}
n = originalNode.$parent.getSelf
while(propertyChain.length) { // evaluate chain from original node's parent
n = n[propertyChain[0]];
propertyChain.shift();
}
return n;
}
let node = root[originalNode.$text];
node.$parent = originalNode.$parent.getSelf;
return node;
}
function escapeHTMLSpecialChars(unsafe) {
return unsafe.toString()
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function collectImportedModuleNamesFromText(text) {
let blocks = splitTextAtAllBlocks(text);
let moduleNames = [];
for(let b of blocks) {
if(b[0] === "{") {
let match = /^\{import:([a-z0-9\-]+)\}$/.exec(b);
if(match && match.length > 1) {
moduleNames.push( /^\{import:([a-z0-9\-]+)\}$/.exec(b)[1] );
} else {
moduleNames.push( ...collectImportedModuleNamesFromText( b.substr(1, b.length-2) ) );
}
}
}
return [...new Set(moduleNames)];
}
function evaluateTextAsNumber(rootNode, thisRef, text, ctxInfo) {
// NOTE: This function is NOT for evaluating odds expressions!
// NOTE: this only returns the number if it can be "exactly" converted
// into a number - it doesn't evaluate the string's mathematical operators
// or anything like that. It only returns if text === String(Number(text))
// because that mean indicates that the deliberately typed a number, and even if
// they didn't, the toString conversion that occurs when this number is output
// will give **exacttly the same result anyway**.
let value = evaluateText(rootNode, thisRef, text, ctxInfo);
if(value === String(Number(value))) {
return Number(value);
} else {
return false;
}
}
function evaluateText(rootNode, thisRef, text, ctxInfo, previousEvaluationText) {
// IMPORTANT: Some curly functions may "return themselves" i.e. {a} may return "{a}" rather than "a" or "an" - this
// behaviour is allowable because evaluateText is called recursively until no unescaped curly/square blocks
// exist. The reason curly functions may do this is because parts of their arguments (the two strings either side of
// "{a}" in the case of the "{a}" function) may not have been evaluated yet, and it may rely on those parts having
// been evaluated. e.g. in the text "{a} [animal] is over there", {a} must wait until [animal] has been evaluated.
// ALSO NOTE: previousEvaluationText is needed because (for example) if we evaluate "created {a}", there's nothing after {a} for
// us to detemrine whether we should use "a" or "an". But "created {a}" is still valid because they could be using it
// as part of a larger sentence. If `text` evaluates to `previousEvaluationText`, then we just return it rather than
// recuring infinitely.
// ALSO NOTE: I had to change this so that instead of [passing through once and then checking if there are any unevaluated blocks that would
// require another pass-over], it does the recursion *within* the loop - otherwise things get executed out of order (e.g. assignment of
// variables and stuff). The user expects their variable assignments to happen in the order that the final output text is printed in.
// Edit: Oh, but I still need to recurse at the end, because some curly blocks like {a} may only be executable once all the blocks are
// joined together into one string.
// ALSO NOTE: This function DOESN'T call processEscapedCharacters on the output. That must be called on the result.
var bracketRegex = /[\[\]{}]/; // note: this is used further down too
if(!bracketRegex.test(text)) return text; // if it has no brackets, nothing needs evaluating
var blocks = splitTextAtAllBlocks(text);
// iterate through and evaluate each block
var evaluatedBlocks = [];
var block, leftBlocks, rightBlocks;
for(let i = 0; i < blocks.length; i++) {
block = blocks[i];
leftBlocks = blocks.slice(0,i);
rightBlocks = blocks.slice(i+1);
if(block[0] === "{") {
let input = block.substr(1,block.length-2);
let result = evaluateCurlyBlock(rootNode, thisRef, input, leftBlocks, rightBlocks, ctxInfo);
if((typeof result === "object" || typeof result === "function") && result.$nodeType) {
// This could be a Perchance node due to import blocks. See note below the `evaluateSquareBlock` call below for explanation.
result = result.toString({keepEscapes:true});
} else {
result = result + "";
}
// the first few conditions here are just performance optimisations:
if(result !== block && bracketRegex.test(result) && (result[0] === "[" || result[0] === "{" || splitTextAtAllBlocks(result).length > 1)) {
result = evaluateText(rootNode, thisRef, result, ctxInfo, result);
}
evaluatedBlocks.push(result);
} else if(block[0] === "[") {
let input = block.substr(1,block.length-2);
window.__primitiveValueGetterKeepEscapesYolo = true; // see MARKER:odj29hfi3j0d2kj0hx24f for explanation
let result = evaluateSquareBlock(rootNode, thisRef, input, ctxInfo);
window.__primitiveValueGetterKeepEscapesYolo = false;
if((typeof result === "object" || typeof result === "function") && result.$nodeType) {
// If it's a Perchance node, we keep the escape characters, since if we remove them here, then we would unescape square/curly character (for example) and
// then they'd get executed (in the code that follows this block) even though that's obviously not what the user wants. See the comments at the top of the toString
// function for more context.
result = result.toString({keepEscapes:true});
} else {
result = result + "";
}
// the first few conditions here are just performance optimisations:
if(result !== block && bracketRegex.test(result) && (result[0] === "[" || result[0] === "{" || splitTextAtAllBlocks(result).length > 1)) {
result = evaluateText(rootNode, thisRef, result, ctxInfo, result);
}
evaluatedBlocks.push(result);
} else {
evaluatedBlocks.push(block);
}
}
if(previousEvaluationText === evaluatedBlocks.join("")) return previousEvaluationText; // see note at top of this function for explanation
// if there's anything but plain text (i.e. if there's any curly/square blocks), recurse
var b, completelyEvaluated = true;
for(let i = 0; i < evaluatedBlocks.length; i++) {
b = evaluatedBlocks[i];
if(!bracketRegex.test(b)) continue;
//if(b === undefined) { b = evaluatedBlocks[i] = "(not found)"; }
if(b[0] === "[" || b[0] === "{" || splitTextAtAllBlocks(b).length > 1) {
completelyEvaluated = false;
break;
}
}
if(!completelyEvaluated) {
return evaluateText(rootNode, thisRef, evaluatedBlocks.join(""), ctxInfo, evaluatedBlocks.join(""));
} else {
return evaluatedBlocks.join("");
}
}
function processEscapedCharacters(text) {
//TODO***: when they have an item like `\ hello \` and they repeat it like [item][item][item]... you end up with `hello \ hello \ hello \ ...` because the two backslashes join up.
// I think this is pretty easy: if there's an unescaped backslash on the end, remove it when you create the node.
// If there's one at the start and it's just escaping a space, then remove that backslash too.
// TODO***: allow them to escape forward slashes?
// TODO***: empty character "\e"?
// look for input characters that follow an unescaped backslash
// and replace them with the output character and remove the backslash
let escapableCharacters = [
{input:"=", output:"="},
{input:"{", output:"{"},
{input:"}", output:"}"},
{input:"[", output:"["},
{input:"]", output:"]"},
{input:"^", output:"^"},
{input:"|", output:"|"},
{input:"n", output:"\n"},
{input:"t", output:"\t"},
{input:"s", output:" "},
];
let escaped = false;
for(let i = 0; i < text.length; i++) {
if(i !== 0) {
if(text[i-1] !== "\\") {
escaped = false;
}
}
if(text[i] === "\\") {
if(escaped) { // handle the special case of the backslash character:
escaped = false;
text = text.substr(0, i-1) + "\\" + text.substr(i+1);
i--;
} else {
if(i === text.length-1) {
return text.substr(0, text.length-1); // if last character is an unescaped backslash, return text without it
} else {
escaped = true;
}
}
continue;
}
if(escaped) {
for(let e of escapableCharacters) {
if(text[i] === e.input) {
text = text.substr(0, i-1) + e.output + text.substr(i+1);
i--;
break;
}
}
}
}
return text;
}
function evaluateCurlyBlock(rootNode, thisRef, block, leftBlocks, rightBlocks, ctxInfo) {
// `block` is a string of the *contents* of the curly block
for(let fn of curlyFunctions) {
// note that recursion can occur here since some curly functions may neet to
// resolve their arguments to plain text. So they'll call evaluatedText, which
// will call this function (if curly brackets are found), and so the loop loops.
let evaluatedBlock = fn(rootNode, thisRef, block, leftBlocks, rightBlocks, ctxInfo);
if(evaluatedBlock !== false) {
if(typeof evaluatedBlock === 'string' && String(Number(evaluatedBlock)) === evaluatedBlock) {
return Number(evaluatedBlock);
} else {
return evaluatedBlock;
}
}
}
if(block === "s") {
perchanceError(`Your curly block "{${escapeHTMLSpecialChars(block)}}
" doesn't appear to have the correct syntax. It is meant to be used for something like I have {1-3} [fruit]{s}
. Notice that there needs to be a number preceding the [fruit]
block. If you just want the plural of [fruit]
then you can use [fruit.pluralForm]
.`, ctxInfo.declarationLineNumber);
} else {
perchanceError(`Your curly block "{${escapeHTMLSpecialChars(block)}}
" doesn't appear to have the correct syntax. Curly brackets (these ones: {}) are special characters in Perchance. They're used to do all sorts of fancy stuff which you can learn about in the tutorial. If you didn't intend to use a special function, and instead just wanted to actually use curly brackets as literal characters, then you can put a backslash character before them like so: \\{...\\}`, ctxInfo.declarationLineNumber);
}
return "(invalid curly block)";
}
// function evaluateCurlylessBlock(rootNode, text) {
// // TODO: allow passing in the declarationLineNumber and other details to this function?
// let arr = splitTextAtSquareBlocks(text);
// let resultText = "";
// for(let e of arr) {
// if(e[0] === "[") {
// let jsExpr = e.substr(1, e.length-2);
// resultText += evaluateSquareBlock(rootNode, rootNode, jsExpr, {declarationLineNumber:null});
// } else {
// resultText += e;
// }
// }
// return resultText;
// }
function getTextOddsDetails(text) {
// JUST returns text on either side of unescaped "^" that is in a TEXT block, with some minor validation (just to help users debug)
let blocks = splitTextAtAllBlocks(text);
let blockI = 0;
let charI = 0;
let overallI = 0;
let currentBlock = blocks[0];
let inText = null;
function inTextBlock(checkI) {
// NOTE: we hold the state outside of the function so that we can start from where we left off
if(overallI > checkI) {
// start again if previous check state is past the current one
blockI = 0;
charI = 0;
overallI = 0;
currentBlock = blocks[0];
inText = null;
}
while(true) {
if(blockI > blocks.length-1) {
console.error("The checkI value (index to be checked to see if it's within a TEXT block) seems to be greater than the length of the whole text.");
return false;
}
// skip block if checkI doesn't occur within it
if(charI === 0) {
if(overallI + blocks[blockI].length-1 < checkI) {
overallI += blocks[blockI].length; //don't subtract 1 because that would put overallI at last character of current block - we want it to be at the first character of nect block
charI = 0; // not necessary since it already is zero, but just for expliciteness
blockI++; // only increase block index after adding block size to overallI
continue;
}
}
if(charI === 0) {
if(blocks[blockI][0] === "[" || blocks[blockI][0] === "{") {
inText = false;
} else {
inText = true;
}
}
if(overallI === checkI) {
return inText;
}
if(charI === blocks[blockI].length-1) { // if we've reached the end of a block
overallI++;
blockI++;
charI = 0;
} else {
charI++;
overallI++;
}
}
}
//text = text.trim(); // <-- I commented this out because inline OR notation like { a ^1| b ^2} would return "a " or "b " instead or " a " or " b "
let escaped = false;
for(let i = 0; i < text.length; i++) {
if(i !== 0) {
if(text[i-1] !== "\\") { escaped = false; }
}
if(text[i] === "\\") { escaped = !escaped; }
if(!escaped && inTextBlock(i) && text[i] === NODE_ODDS_INDICATOR_CHARACTER) {
let odds = text.substr(i+1).trim();
// tiny bit of validation, just to help out users, but definitely not an full validation (which would be basically impossible to do, since we'd need to simulate every possible combination of potentially huge programs)
if(!/[!0-9\[\{]/.test(odds[0])) {
perchanceError(`There is a problem with this odds declaration: "${escapeHTMLSpecialChars(odds)}" (the full text of the line with the error is "${escapeHTMLSpecialChars(text)}"). The text after the "^" character defines the chance that that item will be selected (the higher the number the greater the chance). Odds declarations should only contain numbers, mathematical/logical operators, and are also allowed to have curly and square bracket blocks which will evaluate to number characters (these will be computed on-the-fly). If you'd like to use the "^" character literally (i.e. not to declare odds), you need to put a backslash character before it like so: "\\^"`);
return false;
}
return {
odds,
textWithoutOdds: text.substr(0,i), // don't trim() - allow caller to decide, since different notations have difference whitespace preferences
};
}
}
return false;
}
function oddsTextToNumber(rootNode, thisRef, text) {
// optimisations:
if(text === "1") { // default value:
return 1;
} else if(!/[^0-9]/.test(text)) { // all digits:
return Number(text);
} else if(String(Number(text)) === text) {
return Number(text);
} else {
// complex expressions:
// NOTE: there's an important distinction between converting primitive declarations to numbers,
// and converting odds declarations to numbers. We only convert primitive declarations to
// numbers if Number.toString() would return the same string anyway. On the other hand, odds
// declarations can contain strings with math symbols
let evaluatedText = evaluateText(rootNode, thisRef, text);
if(evaluatedText === "false") evaluatedText = "0";
else if(evaluatedText === "true") evaluatedText = "1";
// TODO: check that this works
if(/[^0-9\/*&|%!.\(\)e\-]/.test(evaluatedText)) {
perchanceError(`Your odds declaration: "${escapeHTMLSpecialChars(text)}", evaluated to: "${escapeHTMLSpecialChars(evaluatedText)}" which contains characters that aren't allowed in odds declarations. The text after the "^" character defines the chance that that item will be selected (the higher the number the greater the chance). Odds declarations should only contain numbers, mathematical/logical operators, and are also allowed to have curly and square bracket blocks which will evaluate to number characters (these will be computed on-the-fly). If you'd like to use the "^" character literally (i.e. not to declare odds), you need to put a backslash character before it like so: "\\^"`);
return false;
}
let num = false;
try { num = eval(evaluatedText); } catch(e) {/* no need to catch */}
return num;
}
}
function splitTextAtSquareBlocks(text) {
let escaped = false;
let unclosedBracketCount = 0;
let inJSExpr = false;
let inJSExprString1 = false, inJSExprString2 = false, inJSExprString3 = false;
let expressionArray = [];
for(let i = 0; i < text.length; i++) {
if(i !== 0) {
if(text[i-1] !== "\\") { escaped = false; }
}
if(text[i] === "\\") { escaped = !escaped; }
// skip strings in js expressions (square brackets in strings would mess
// with our unclosedBracketCount):
if(inJSExpr && !inJSExprString1 && !escaped && text[i] === "\"") { inJSExprString1 = true; continue; }
if(inJSExprString1 && !escaped && text[i] !== "\"") { continue; }
if(inJSExprString1 && !escaped && text[i] === "\"") { inJSExprString1 = false; continue; }
if(inJSExpr && !inJSExprString2 && !escaped && text[i] === "'") { inJSExprString2 = true; continue; }
if(inJSExprString2 && !escaped && text[i] !== "'") { continue; }
if(inJSExprString2 && !escaped && text[i] === "'") { inJSExprString2 = false; continue; }
if(inJSExpr && !inJSExprString3 && !escaped && text[i] === "`") { inJSExprString3 = true; continue; }
if(inJSExprString3 && !escaped && text[i] !== "`") { continue; }
if(inJSExprString3 && !escaped && text[i] === "`") { inJSExprString3 = false; continue; }
// skip escaped brackets within js expressions (i.e. within regex declarations):
if(inJSExpr && escaped && (text[i] === "[" || text[i] === "]")) { continue; }
if(!escaped && text[i] === "[") {
unclosedBracketCount++;
// if we weren't already inJSExpr, then this is the start of the a JSExpr:
if(!inJSExpr && i !== 0) {
expressionArray.push(text.substr(0, i));
text = text.substr(i);
i = 0; // will be incremented to 0 on next loop, as required (we've already processed the first char, "[")
}
inJSExpr = true;
continue;
}
if(!escaped && text[i] === "]") {
unclosedBracketCount--;
// we're only out of the js expression when we've closed all brackets
if(inJSExpr && unclosedBracketCount === 0) {
inJSExpr = false;
expressionArray.push(text.substr(0, i+1));
text = text.substr(i+1);
i = -1; // will be incremented to 0 on next loop as required (we haven't yet processed the first character of the new string (the character after "]"))
}
}
// last expression:
if(i === text.length-1 && text.length !== 0) {
expressionArray.push(text);
}
}
if(unclosedBracketCount !== 0) {
perchanceError(`It appears that you've got a mismatch in your opening and closing square brackets. For each opening square bracket, there should be a closing one. If you'd like to use a literal square bracket (i.e. you want to actually display one, rather than using them to output a random list item, then you need to put a "backslash" before it like so: "\\[". Here's the text that seems to be causing the error: ${escapeHTMLSpecialChars(text)}`); return false; } return expressionArray; } function getInlineFunctionDetails(lineText) { let escaped = false; let openIndex, opened = false; let closeIndex, closed = false; for(let i = 0; i < lineText.length; i++) { if(i !== 0) { if(lineText[i-1] !== "\\") { escaped = false; } } if(lineText[i] === "\\") { escaped = !escaped; } if(lineText[i] === "(" && !escaped) { opened = true; openIndex = i; } if(lineText[i] === ")" && !escaped && opened) { closed = true; closeIndex = i; } if(lineText[i] === "=" && !escaped && opened && closed && lineText[i+1] === ">") { let argString = lineText.substr(openIndex+1, closeIndex-openIndex-1); let args = argString.split(",").map(a => a.trim()); let name = lineText.substr(0, openIndex).trim(); let body = lineText.substr(i+2).trim(); if(name.trim() === "") { return false; } if(body.trim() === "") { return false; } if(areValidFunctionArguments(args) || argString === "") { if(!isValidJavaScriptIdentifier(name)) return false; args = args.filter(a => a !== ""); // for zero arg case return { args, name, body }; } } } return false; } function getFunctionHeaderDetails(lineText) { lineText = lineText.trim(); let l = lineText.length; if(lineText[l-2] === "=" && lineText[l-1] === ">") { lineText = lineText.substr(0, l-2).trim(); l = lineText.length; } else { return false; } let escaped = false; let opened = false; let openIndex; for(let i = 0; i < lineText.length; i++) { if(i !== 0) { if(lineText[i-1] !== "\\") { escaped = false; } } if(lineText[i] === "\\") { escaped = !escaped; } if(lineText[i] === "(" && !escaped) { opened = true; openIndex = i; } if(lineText[i] === ")" && !escaped && opened) { let argString = lineText.substr(openIndex+1, i-openIndex-1); let args = argString.split(",").map(a => a.trim()); let name = lineText.substr(0, openIndex).trim(); if(name.trim() === "") { return false; } if(areValidFunctionArguments(args) || argString === "") { if(!isValidJavaScriptIdentifier(name)) return false; args = args.filter(a => a !== ""); // for zero arg case return { args, name }; } } } return false; } function areValidFunctionArguments(args) { for(let a of args) { if(!isValidJavaScriptIdentifier(a.replace("...","").replace("=",""))) { return false; } } return true; } function isValidJavaScriptIdentifier(str) { return /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|await|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D])(?:[\$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])*$/.test(str); } function getPrimitiveNodeDetails(lineText) { // NOTE: this function interprets some strings as numbers and casts them using Number() // note that this is *just a shorthand*. The reason it's okay to do is because it // only interprets strings as numbers if Number(that_number).toString() would return // the same string anyway. If the user wants to do something like: // prop = [this.age]/[this.height] // then they should do this instead: // prop = [this.age/this.height] // because the prior will return something like "10/140" (i.e. a string, rather than a number) // remember: the lineText could have square blocks which can of course have equals signs in them. // But since primitive node keys can't be dynamic we can just return false if we see an unescaped square // or curly bracket before we find an unescaped equals sign let escaped = false; for(let i = 0; i < lineText.length; i++) { if(i !== 0) { if(lineText[i-1] !== "\\") { escaped = false; } } if(lineText[i] === "\\") { escaped = !escaped; } if((lineText[i] === "[" || lineText[i] === "{") && !escaped) { // found square or curly block before unescaped equals sign, so it's not a primitive node return false; } if(lineText[i] === "=" && !escaped) { let key = lineText.substr(0, i).trim(); let valueText = lineText.substr(i+1).trim(); if(key === "" || valueText === "") { return false; } let value; if(valueText === String(Number(valueText))) { value = Number(valueText); } else if(valueText === "true") { // TODO: test that this works (with filter/where, etc.) value = true; } else if(valueText === "false") { value = false; } else { value = valueText; } return {key, value}; } } return false; } // function isMostLikelyAnIntentionalNumericString(valueText) { // if(isNaN(valueText)) { return false; } // if(/[^0-9.\-]+/.test(valueText)) { return false; } // if(valueText.split("-").length > 2) { return false; } // if(valueText.split(".").length > 2) { return false; } // // if(valueText.split("-").length === 2) { // if(valueText[0] !== "-") { return false; } // if(valueText[0] === "-" && valueText[1] === ".") { return false; } // } // // if(valueText.split(".").length === 2) { // if(valueText[0] === ".") { return false; } // if(valueText[valueText.length-1] === ".") { return false; } // // let beforeDecimal = valueText.split(".")[0]; // let afterDecimal = valueText.split(".")[1]; // if(afterDecimal[afterDecimal.length-1] === "0") { return false; } // e.g. 20.0 shouldn't be treated as a number (they'd have just put in "20") // if(beforeDecimal[0] === "0" && beforeDecimal.length > 1) { return false; } // e.g. 020 shouldn't be treated as a number // } // // return true; // // } function splitTextAtCurlyBlocks(text) { // remember: we must ignore curly brackets if we're inside a js expression if(!text.includes("{") && !text.includes("}")) return [text]; let escaped = false; let lastCurlyOpenIndex = null; let inJSExpr = false; let unclosedSquareBracketCount = 0; let inJSExprString1 = false; let inJSExprString2 = false; let inJSExprString3 = false; let inCurlyExpr = false; let unclosedCurlyBracketCount = 0; let blocks = []; for(let i = 0; i < text.length; i++) { if(i !== 0) { if(text[i-1] !== "\\") { escaped = false; } } if(text[i] === "\\") { escaped = !escaped; } //////////////////////////////////// // skip strings in js expressions // //////////////////////////////////// //(square brackets in strings would mess with our unclosedSquareBracketCount which we use to skip js expressions): if(inJSExpr && !inJSExprString1 && !escaped && text[i] === "\"") { inJSExprString1 = true; continue; } if(inJSExprString1 && !escaped && text[i] !== "\"") { continue; } if(inJSExprString1 && !escaped && text[i] === "\"") { inJSExprString1 = false; continue; } if(inJSExpr && !inJSExprString2 && !escaped && text[i] === "'") { inJSExprString2 = true; continue; } if(inJSExprString2 && !escaped && text[i] !== "'") { continue; } if(inJSExprString2 && !escaped && text[i] === "'") { inJSExprString2 = false; continue; } if(inJSExpr && !inJSExprString3 && !escaped && text[i] === "`") { inJSExprString3 = true; continue; } if(inJSExprString3 && !escaped && text[i] !== "`") { continue; } if(inJSExprString3 && !escaped && text[i] === "`") { inJSExprString3 = false; continue; } // skip escaped brackets within js expressions (i.e. escaped brackets within regex declarations): if(inJSExpr && /*--->*/escaped/*<---*/ && (text[i] === "[" || text[i] === "]")) { continue; } ///////////////////////////////////// // skip JS expressions // ///////////////////////////////////// // note that we're guarunteed to NOT be in a string for this block (since the // above block skips strings while we're in js expressions) if(!inJSExpr && text[i] === "[" && !escaped) { inJSExpr = true; unclosedSquareBracketCount++; continue; } if(inJSExpr && text[i] === "[") { // `escaped` is irrelevant, we're inJSExpr and not in string unclosedSquareBracketCount++; continue; } if(inJSExpr && text[i] === "]") { // `escaped` is irrelevant, we're inJSExpr and not in string unclosedSquareBracketCount--; if(unclosedSquareBracketCount === 0) { inJSExpr = false; } } ////////////////////////////////////// // extract curly blocks // ////////////////////////////////////// if(text[i] === "{" && !escaped && !inJSExpr) { unclosedCurlyBracketCount++; // if we're not yet in a curly expression, then this is the start of one: if(!inCurlyExpr && i !== 0) { blocks.push(text.substr(0, i)); text = text.substr(i); i = 0; } inCurlyExpr = true; continue; } if(text[i] === "}" && !escaped && !inJSExpr) { unclosedCurlyBracketCount--; // we're only out of the curly expression when we've closed all curly brackets that were opened if(inCurlyExpr && unclosedCurlyBracketCount === 0) { blocks.push(text.substr(0, i+1)); text = text.substr(i+1); i = -1; inCurlyExpr = false; } } // add last expression (if it didn't end in a curly block - in that case text.length === 0) if(i === text.length-1 && text.length !== 0) { blocks.push(text); } } if(unclosedSquareBracketCount !== 0) { perchanceError(`It appears that you've got a mismatch in your opening and closing square brackets. For each opening square bracket, there should be a closing one. If you'd like to use a literal square bracket (i.e. you want to actually display one, rather than using them to output a random list item, then you need to put a "backslash" before it like so: "\\[". Here's the text that seems to be causing the error:
${escapeHTMLSpecialChars(text)}`); return ["(syntax error)"]; } if(unclosedCurlyBracketCount !== 0) { perchanceError(`It appears that you've got a mismatch in your opening and closing curly brackets. For each opening curly bracket, there should be a closing one. If you'd like to use a literal curly bracket (i.e. you want to actually display one, rather than using them to do {import:noun} and stuff like that, then you need to put a "backslash" before it like so: "\\{". Here's the text that seems to be causing the error:
${escapeHTMLSpecialChars(text)}`); return ["(syntax error)"]; } return blocks; } function splitTextAtAllBlocks(text) { let curlySplitParts = splitTextAtCurlyBlocks(text); let blocks = []; // this array will contain curly blocks, text blocks, and square blocks for(let part of curlySplitParts) { if(part[0] !== "{") { let squareSplitParts = splitTextAtSquareBlocks(part); if(squareSplitParts) { blocks.push(...squareSplitParts); } else { console.error("Something is wrong?"); } } else { blocks.push(part); } } return blocks; } function stripCommentFromLine(line) { // NOTE: comment must either start at start of line, or have a space before it. // e.g. in http://example.com , "//example.com" isn't considered a comment because // it doesn't have a preceding space let i, escaped = false; for(i = 0; i < line.length; i++) { if(i !== 0) { if(line[i-1] !== "\\") { escaped = false; } } if(line[i] === "\\") { escaped = !escaped; } if(line[i] === "/" && !escaped && line[i+1] === "/") { if(i === 0) { break; } else { if(line[i-1] === " " || line[i-1] === "\t") { break; } } } } return line.substr(0,i); } function normaliseLineIndentsToTabs(line) { let i, eqSpaceCount = 0; //equivalent spaces (1 tab = 2 spaces) for(i = 0; i < line.length; i++) { if(line[i] === " ") { eqSpaceCount++; } else if(line[i] === "\t") { eqSpaceCount += 2; } else { break; } } if(eqSpaceCount%2 !== 0) { return false; //odd number of spaces } else { return "\t".repeat(parseInt(eqSpaceCount/2)) + line.substr(i); } } Object.defineProperty(String.prototype, "titleCase", {get:titleCaseMethod}); Object.defineProperty(Number.prototype, "titleCase", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "titleCase", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "sentenceCase", {get:sentenceCaseMethod}); Object.defineProperty(Number.prototype, "sentenceCase", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "sentenceCase", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "upperCase", {get:upperCaseMethod}); Object.defineProperty(Number.prototype, "upperCase", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "upperCase", {get:function(){return this.valueOf();}}) Object.defineProperty(String.prototype, "lowerCase", {get:lowerCaseMethod}); Object.defineProperty(Number.prototype, "lowerCase", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "lowerCase", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "replaceText", {value:replaceTextMethod}); Object.defineProperty(Number.prototype, "replaceText", {value:replaceTextMethod}); Object.defineProperty(Boolean.prototype, "replaceText", {value:replaceTextMethod}); Object.defineProperty(String.prototype, "pastTense", {get:pastTenseMethod}); Object.defineProperty(Number.prototype, "pastTense", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "pastTense", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "presentTense", {get:presentTenseMethod}); Object.defineProperty(Number.prototype, "presentTense", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "presentTense", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "futureTense", {get:futureTenseMethod}); Object.defineProperty(Number.prototype, "futureTense", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "futureTense", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "getSelf", {get:function() { return this+""; }}); Object.defineProperty(Number.prototype, "getSelf", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "getSelf", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "getOdds", {get:function() { return 1; }}); Object.defineProperty(Number.prototype, "getOdds", {get:function(){ return 1; }}); Object.defineProperty(Boolean.prototype, "getOdds", {get:function(){ return 1; }}); // WHAT IS THIS? Object.defineProperty(String.prototype, "getName", {get:function() { return this+""; }}); Object.defineProperty(Number.prototype, "getName", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "getName", {get:function(){return this.valueOf();}}) Object.defineProperty(String.prototype, "evaluateItem", {get:function() { return this.valueOf(); }}); Object.defineProperty(Number.prototype, "evaluateItem", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "evaluateItem", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, Symbol.toPrimitive, {value:function(hint) { return this.valueOf(); }}); // i don't think these two are necessary? Object.defineProperty(Number.prototype, Symbol.toPrimitive, {value:function(hint) { return this.valueOf(); }}); Object.defineProperty(Boolean.prototype, Symbol.toPrimitive, {value:function(hint) { return this.valueOf(); }}); Object.defineProperty(String.prototype, "selectMany", {value:__selectManyMethodStringNum}); Object.defineProperty(Number.prototype, "selectMany", {value:__selectManyMethodStringNum}); Object.defineProperty(Boolean.prototype, "selectMany", {value:__selectManyMethodStringNum}); function __selectManyMethodStringNum(num) { let arr = []; for(let i = 0; i < num; i++) { arr.push(this); } // overwrite default array behaviour (selects a random one) // in the case where they don't specify a join() after the repeat: arr.toString = function() { return this.join(""); }; return arr; } Object.defineProperty(String.prototype, "selectOne", {get:function() { return this.valueOf(); }}); Object.defineProperty(Number.prototype, "selectOne", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "selectOne", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "singularForm", {get:function(n) { let input = this; let output = nlp(input).nouns().toSingular().out("text"); return output === "" ? input : output; }}); Object.defineProperty(Number.prototype, "singularForm", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "singularForm", {get:function(){return this.valueOf();}}); Object.defineProperty(String.prototype, "pluralForm", {get:function(n) { let input = this; let output = nlp(input).nouns().toPlural().out("text"); return output === "" ? input : output; }}); Object.defineProperty(Number.prototype, "pluralForm", {get:function(){return this.valueOf();}}); Object.defineProperty(Boolean.prototype, "pluralForm", {get:function(){return this.valueOf();}}); // TODO***: add the rest of the node methods here and to Array (e.g. pastTense, etc.) // Object.defineProperty(String.prototype, "shuffleCharacters", {value:function(numShuffles) { // // if(numShuffles === undefined) { // // var array = this.split(""); // var currentIndex = array.length, temporaryValue, randomIndex; // // While there remain elements to shuffle... // while (0 !== currentIndex) { // // Pick a remaining element... // randomIndex = Math.floor(Math.random() * currentIndex); // currentIndex -= 1; // // And swap it with the current element. // temporaryValue = array[currentIndex]; // array[currentIndex] = array[randomIndex]; // array[randomIndex] = temporaryValue; // } // return array.join("") // // } else { // // let array = this.split(""); // for(let i = 0; i < numShuffles; i++) { // let i1 = Math.floor(array.length*Math.random()); // let i2 = Math.floor(array.length*Math.random()); // let c1 = array[i1]; // let c2 = array[i2]; // array[i1] = c2; // array[i2] = c1; // } // return array.join(""); // // TODO: why not join back into a string? // // } // // }}); Array.prototype.toString = function () { // default behaviour of arrays when converting to string is to choose a random element. // altering the toString method is how you make it not do this by default (this is how selectMany works) // let items = []; // for(let item of this) { // items.push(item+""); // } // return chooseRandomTextByOdds(window.root, window.root, items); return this.selectOne+""; }; Object.defineProperty(Array.prototype, "getOdds", {get:function() { return 1; }}); Object.defineProperty(Array.prototype, "getSelf", {get:function() { return this; }}); Object.defineProperty(Array.prototype, "getLength", {get:function() { return this.length; }}); Object.defineProperty(Array.prototype, "consumableList", {get:function() { let proxy = new Proxy(this, { alreadyConsumedItems:new Set(), get: function(target, property) { if(property === "selectOne") { let selectedNode = arraySelectOneMethod.bind(proxy)(); if(selectedNode) { this.alreadyConsumedItems.add(selectedNode); } return selectedNode; } else if(property === "$alreadyConsumedItems") { return this.alreadyConsumedItems; } else if(property === "getLength") { return target.length - this.alreadyConsumedItems.size; } else { // TODO: strangely, the return value here seems to be automatically bound to this proxy. // so it also handles toString and valueOf methods like we want it to return target[property]; } }, }); return proxy; }}); Object.defineProperty(Array.prototype, "selectAll", {get: function() { return this; }}); Object.defineProperty(Array.prototype, "selectMany", {value:selectManyMethod}); let arraySelectOneMethod = function() { //return this[Math.floor(Math.random()*this.length)]; let candidates = []; /*{item, odds}*/ for(let a of this) { if(a.$nodeType) { // if it's a node candidates.push({item:a, odds:a.$odds}); } else if(typeof a === 'string') { let details = getTextOddsDetails(a); let odds = details ? Number(details.odds) : 1; // <-- this doesn't allow dynamic odds notation in arrays. Can always add it later if it turns out that it would be useful. let item = details ? details.textWithoutOdds : a; candidates.push({item, odds}); } else { candidates.push({item:a, odds:1}); } } if(this.$alreadyConsumedItems) { candidates = candidates.filter(c => !this.$alreadyConsumedItems.has(c.item)); } let oddsTotal = candidates.reduce((a,v) => { return a+v.odds; }, 0); let stopOddsSum = oddsTotal*Math.random(); let oddsRunningSum = 0; for(let c of candidates) { oddsRunningSum += c.odds; if(oddsRunningSum >= stopOddsSum) { return c.item; } } if(this.$alreadyConsumedItems) { perchanceError("It looks like your
consumableList
ran out of items. This is the list in question: "+this.slice(0, 100).join(", "));
} else {
perchanceError("Something went wrong! Error: selectOne from array returned nothing.
Please report this on reddit.com/r/perchance since it's likely a bug with the engine.");
}
};
Object.defineProperty(Array.prototype, "selectOne", {get:arraySelectOneMethod});
Array.prototype.shuffleItems = function(numShuffles) {
// TODO: test random shuffle ()
if(numShuffles === undefined) {
var array = this;
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
} else {
let items = this;
for(let i = 0; i < numShuffles; i++) {
let i1 = Math.floor(items.length*Math.random());
let i2 = Math.floor(items.length*Math.random());
let c1 = items[i1];
let c2 = items[i2];
items[i1] = c2;
items[i2] = c1;
}
return items;
}
};
Object.defineProperty(Array.prototype, Symbol.toPrimitive, {value:function(hint) {
return this.toString();
}});
// don't need this because we've got joinItems, right?
// Object.defineProperty(Array.prototype, "evaluateItem", {get:function() {
// return this.toString();
// }});
Object.defineProperty(Array.prototype, "joinItems", {value:function(str="") {
return this.join(str);
}});
// TODO***: make all methods configurable like in nodeMethods?
Object.defineProperty(Array.prototype, "pluralForm", {get:function() {
return this.map(a => a.pluralForm);
}});
Object.defineProperty(Array.prototype, "singularForm", {get:function() {
return this.map(a => a.singularForm);
}});
Object.defineProperty(Array.prototype, "pastTense", {get:function() {
return this.map(a => a.pastTense);
}});
Object.defineProperty(Array.prototype, "presentTense", {get:function() {
return this.map(a => a.presentTense);
}});
Object.defineProperty(Array.prototype, "futureTense", {get:function() {
return this.map(a => a.futureTense);
}});
Object.defineProperty(Array.prototype, "titleCase", {get:function() {
return this.map(a => a.titleCase);
}});
Object.defineProperty(Array.prototype, "upperCase", {get:function() {
return this.map(a => a.upperCase);
}});
Object.defineProperty(Array.prototype, "lowerCase", {get:function() {
return this.map(a => a.lowerCase);
}});
Object.defineProperty(Array.prototype, "sentenceCase", {get:function() {
return this.map(a => a.sentenceCase);
}});
Object.defineProperty(Array.prototype, "replaceText", {value:function(p1, p2, p3) {
return this.map(a => a.replaceText(p1, p2, p3));
}});
let curlyFunctions = [curlyFunction_Or, curlyFunction_Import, curlyFunction_Range, curlyFunction_S, curlyFunction_A];
function curlyFunction_Import(rootNode, thisRef, text, leftBlocks, rightBlocks, ctxInfo) {
if(!text.includes(":")) return false;
let match = /^import:([a-z0-9\-]+)$/.exec(text);
if(!match) {
return false;
} else {
let moduleName = match[1];
let moduleRef = rootNode.$moduleSpace[moduleName];
if( moduleRef ) {
return moduleRef;
} else {
perchanceError(`You've tried to import a generator that doesn't exist. The generator named '${escapeHTMLSpecialChars(moduleName)}' was not found. You can see for yourself by visiting that generator: ${moduleName}. There's a list of useful generators that may help you find what you're looking for.`, ctxInfo.declarationLineNumber);
return `(cannot find generator '${moduleName}')`;
}
}
}
function curlyFunction_A(rootNode, thisRef, text, leftBlocks, rightBlocks) {
if(text === "a" || text === "A") {
let right = removeHtmlTagsFromBlocksArray(rightBlocks); //rightBlocks.filter(b => b.replace(/<[^>]+?>/g, '').trim() !== "");
if(right[0] === undefined || (right[0] && (right[0].trim()[0] === "{"|| right[0].trim()[0] === "["))) {
return text === "a" ? "{a}" : "{A}";
} else {
let aOrAn = AvsAnSimple.query(right[0].trim().replace(/<[^>]+?>/g, '').split(/[\s$%&*@#,!=+\-.?:;'"(){}\[\]\/<>`~_|]/g)[0].trim());
if(text === "A") {
if(aOrAn === "a") aOrAn = "A";
else aOrAn = "An";
}
return aOrAn;
}
} else {
return false;
}
}
function curlyFunction_Range(rootNode, thisRef, text, leftBlocks, rightBlocks) {
if(!text.includes("-")) return false;
let match;
// TODO: written numbers: {twenty-fifty five} ?
// TODO: custom increments: {0-100:0.5} ? not sure about notation yet
// {1-10}
match = /^([0-9]+)-([0-9]+)$/.exec(text);
if(match) {
let min = Number(match[1]);
let max = Number(match[2]);
if(min > max) { [min, max] = [max, min]; }
return min+Math.round(Math.random()*(max-min))
}
// {a-z}
match = /^([a-z])-([a-z])$/.exec(text);
if(match) {
let min = Number(match[1].charCodeAt(0));
let max = Number(match[2].charCodeAt(0));
if(min > max) { [min, max] = [max, min]; }
return String.fromCharCode( min+Math.round(Math.random()*(max-min)) );
}
// {A-Z}
match = /^([A-Z])-([A-Z])$/.exec(text);
if(match) {
let min = Number(match[1].charCodeAt(0));
let max = Number(match[2].charCodeAt(0));
if(min > max) { [min, max] = [max, min]; }
return String.fromCharCode( min+Math.round(Math.random()*(max-min)) );
}
return false;
}
function curlyFunction_S(rootNode, thisRef, text, leftBlocks, rightBlocks) {
// there are {1..10} person{s} <-- can't do this, must manually handle the 2 cases
// I have {1..10} coin{s} <-- can do this :)
if(text === "s") {
// TODO: fix this with some proper NLP to handle written numbers: "twenty five"
// search through leftBlocks backwards for the first number.
// if a non-text block is found before a number is, return "{s}"
for(let i = leftBlocks.length-1; i >= 0; i--) {
let block = leftBlocks[i];
if(block[0] === "{" || block[0] === "[") {
return "{s}";
}
let numbers = block.split(/[\s,!?\-()/.]+/).filter(a => /[0-9]+/.test(a));
if(numbers.length > 0) {
let num = Number(numbers[numbers.length-1]);
return num === 1 ? "" : "s";
}
}
}
return false;
}
function curlyFunction_Or(rootNode, thisRef, text, leftBlocks, rightBlocks) {
if(!text.includes("|")) return false;
// no need to evaluate the arguments. we can just extract them (with their odds)
// and select a random one. We could evaluate the args but in that case it would be
// important that we extract all the *arguments* first (and their odds), then
// choose a random one (otherwise NEW "|" and "^" characters could end up in the evaluated text)
// and mess up our interpretation of the arguments.
// pulled the bulk of this curlyFunction_Or function out into its own function for use elsewhere:
let args = splitUpCurlyOrBlock(text);
if(args === false) return false;
// now we need to extract odds and choose a random one
// note that in curly "or" notation, *spaces matter* before and after the text.
// thus we don't trim the string after extracting the odds.
return chooseRandomTextByOdds(rootNode, thisRef, args);
// TODO: sort out Array.toString
// LOOK AT Array.toString definition - try to extract out a common function
// (or you could just call) .toString on `parts` lol
// hmm. Array.toString relies on window.root being available which isn't great.
// I think first step is to test whether it works, then extract out as much as I
// can into functions. Array.toString is more of a handy hack - I don't want to rely
// on it if I can help it.
}
function splitUpCurlyOrBlock(text) {
// note this returns false if it's not a valid curly OR block.
// also, it expects that the outer curly brackets have already been removed
// the easiest way to be find all the unescaped "|" characters is to split it
// up and then search through *only text blocks*:
let blocks = splitTextAtAllBlocks(text);
let args = [];
let currentArg = "";
for(let j = 0; j < blocks.length; j++) {
let block = blocks[j];
if(block[0] !== "{" && block[0] !== "[") {
let escaped = false;
for(let i = 0; i < block.length; i++) {
if(i === 0) {
// if we're at the start of the block, it's not escaped:
escaped = false;
} else {
// if we're not at start, it's not escaped if the last char wasn't a backslash:
if(block[i-1] !== "\\") { escaped = false; }
}
// toggle escapedness at each consecutive backslash:
if(block[i] === "\\") { escaped = !escaped; }
if(block[i] === "|" && !escaped) {
currentArg += block.substr(0,i);
args.push(currentArg);
currentArg = "";
block = block.substr(i+1);
i = -1; // will get incremented to zero on loop
} else if(i === block.length-1) {
currentArg += block;
}
}
if(j === blocks.length-1) {
args.push(currentArg);
}
} else {
currentArg += block;
if(j === blocks.length-1) {
args.push(currentArg);
}
}
}
if(args.length === 0 || args.length === 1) {
// this is not the correct curly function to interpret this curly block
return false;
}
return args;
}
function chooseRandomTextByOdds(rootNode, thisRef, textArray) {
// extract odds
let items = [];
for(let text of textArray) {
text = text+""; // cast to string (needed for Array.toString);
let odds, textWithoutOdds;
let details = getTextOddsDetails(text);
// TODO: fix this up to suit the new way of doign things
if(!details) {
textWithoutOdds = text;
odds = 1;
items.push({text:textWithoutOdds, odds});
continue;
}
textWithoutOdds = details.textWithoutOdds;
odds = details.odds;
if(typeof odds === 'string' && (odds[0] === "[" || /[0-9]/.test(odds[0]) )) { // if it's a dynamic odds expression:
let evaluatedOdds = oddsTextToNumber(rootNode, thisRef, odds);
if(evaluatedOdds < 0) {
perchanceError(`The dynamic odds expression "${escapeHTMLSpecialChars(odds)}" in ${escapeHTMLSpecialChars("{"+textArray.join("|")+"}")} resolved to a *negative* number. Odds must be positive numbers, since it doesn't make sense for something to have a negative chance of being selected. Remember that you can try executing this odds expression in the console (bottom-left panel in the editor interface) and see what it yeilds. Also remember that "^" is a special character that is used to declare the probability that the item will be selected, and if you want to use it as a literal character (i.e. not to declare odds), then you need to put a "backslash" before it like so: "\\^"`)
return false;
}
if(evaluatedOdds === false) {
perchanceError(`The dynamic odds expression "${escapeHTMLSpecialChars(odds)}" in ${escapeHTMLSpecialChars("{"+textArray.join("|")+"}")} didn't resolve to a number. Remember that you can try executing this odds expression in the console (bottom-left panel in the editor interface) and see what it yeilds. Also remember that "^" is a special character that is used to declare the probability that the item will be selected, and if you want to use it as a literal character (i.e. not to declare odds), then you need to put a "backslash" before it like so: "\\^"`)
return false;
}
odds = evaluatedOdds;
}
items.push({text:textWithoutOdds, odds});
}
// select one based on odds
let oddsSum = 0;
let oddsArray = [];
for(let item of items) {
oddsSum += item.odds;
oddsArray.push(item.odds);
}
if(items.length === 0) {
console.warn(`Warning: Tried to get random selection from an empty array?`);
return "";
}
// choose random position in oddsSum range
let oddsSumStopPoint = oddsSum*Math.random();
let selectedItem;
let oddsSumForChoice = 0;
for(let i = 0; i < items.length; i++) {
oddsSumForChoice += oddsArray[i];
if(oddsSumForChoice >= oddsSumStopPoint) {
selectedItem = items[i];
break;
}
}
return selectedItem.text;
}
// doesn't need to be perfect! just a rough clean to help the {a/A} blocks
function removeHtmlTagsFromBlocksArray(blocks) {
const joinTag = "sdoxci892387uoooJOINoooTAGooohkdsiyygjd970sjgs";
let str = blocks.join(joinTag);
let charArr = str.split("");
let inTag = false;
for(let i = 0; i < charArr.length; i++) {
if(charArr[i] === "<" && !inTag) {
inTag = true;
charArr[i] = "";
} else if(charArr[i] === ">" && inTag) {
inTag = false;
charArr[i] = "";
} else if(inTag) {
charArr[i] = "";
}
}
return charArr.join("").split(joinTag).filter(c => c.trim() !== "");
}
let perchanceTreeRoot = createPerchanceTree(`
username
names = [this.selectMany(100).joinItems("\\n")]
[fancify(username1.evaluateItem)]
[username1]
[vapor_username]^0.1
username1
[modstart1]{_|}[base]{|.xo^0.02}
[base]{_|}[modend1]{|.xo^0.02}
modstart1
midnight
moon
space
eternal
galatic
jupiter
cosmo
astro
milky
star
moonlight
galaxy
pale^0.2
saturn
night
naked
darker
peachy
sadly
sad
honey
bby^0.1
aesthicc^0.1
dxpressed ^0.3
soft
modend1
city
caramel
aurora
mochi
ology
gaze
nebula
melon
skies
tea
flower
peach
lust
daydream
babygirl^0.2
ramen
æsthetic
tears
oats^0.2
child
angel
base
almond
pluto
bluemoon
ailurophobia
assemblage
becoming
beleaguer
brood
bucolic
bungalow
chatoyant
comely
conflate
cynosure
dalliance
demesne
demure
denouement
desuetude
desultory
diaphanous
dissemble
dulcet
ebullience
effervescent
efflorescence
elision
elixir
eloquence
embrocate
emollient
ephemeral
epiphany
erstwhile
ethereal
evanescent
evocative
fetching
felicity
forbearance
fugacious
furtive
gambol
glamor
gossamer
halcyon
harbinger
imbrication
imbroglio
imbue
incipient
ineffable
ingenue
inglenook
insouciance
inure
labyrinthine
lagniappe
lagoon
languor
lassitude
leisure
lilt
lissome
lithe
love
mellifluous
moiety
murmurous
nemesis
offing
onomatopoeia
opulent
palimpsest
panacea
panoply
pastiche
penumbra
plethora
propinquity
pyrrhic
quintessential
ratatouille
ravel
redolent
riparian
ripple
scintilla
sempiternal
seraglio
serendipity
summery
sumptuous
surreptitious
susurrus
talisman
tintinnabulation
umbrella
untoward
vestigial
wafture
wherewithal
woebegone
beans
tempeh
tofu
nuts
dried fruit
tahini
hummus
legumes
ailurophile
assemblage
becoming
beleaguer
brood
bucolic
bungalow
chatoyant
comely
conflate
cynosure
dalliance
demesne
demure
denouement
desuetude
desultory
diaphanous
dissemble
dulcet
ebullience
effervescent
efflorescence
elision
elixir
eloquence
embrocation
emollient
ephemeral
epiphany
erstwhile
ethereal
evanescent
evocative
fetching
felicity
forbearance
fugacious
furtive
gambol
glamour
gossamer
halcyon
harbinger
imbrication
imbroglio
imbue
incipient
ineffable
ingénue
inglenook
insouciance
inure
labyrinthine
lagniappe
lagoon
languor
lassitude
leisure
lilt
lissome
lithe
love
mellifluous
moiety
mondegreen
murmurous
nemesis
offing
onomatopoeia
opulent
palimpsest
panacea
panoply
pastiche
penumbra
petrichor
plethora
propinquity
pyrrhic
quintessential
ratatouille
ravel
redolent
riparian
ripple
scintilla
sempiternal
seraglio
serendipity
summery
sumptuous
surreptitious
susquehanna
susurrous
talisman
tintinnabulation
umbrella
untoward
vestigial
wafture
wherewithal
woebegone
komorebi
shinrinyoku
wabi-sabi
kogarashi
mono no aware
itadakimasu
ojama-shimasu
otsukaresamadesu
natsukashii
mottainai
kuidaore
shoganai
shibui
bakkushan
tsundoku
vapor_username
{[vapor_start][vapor_adjective]_|[vapor_adjective]}_[vapor_noun]{[vapor_end]||}
{[vapor_start][vapor_adjective]_|[vapor_adjective]}[vapor_noun]{[vapor_end]||}
{[vapor_start][vapor_adjective]_|[vapor_adjective]}[vapor_noun]{[vapor_end]||}
vapor_noun
AOL
nightscape
bedroom
ecstasy
kid
pepsi
crystal
nostalgia
night
remnant
past
future
glue
audio
loop
statue
pizza
gameboy
palm
arizona
windows
aether
saint
macintosh
gradient
sega
software
discette
edition
1994
plus
hotline
system
consumer
identities
plaza
vapor
aesthetic
mesh
dream
broadcast
sunset
pixels
vapor_end
.txt
65
95
deluxe
99.99
アライブ
美学
情報
スレッド
デスク
.light
___
회사
vapor_start
ultra
pseudo
mega
super
contra
trans
micro
macro
infra
poly
retro
uni
epi
extra
mini
proto
meta
quadri
hepta
multi
penta
vapor_adjective
visual
80s
90s
luxury
floral
holographic
blank
virtual
analog
nocturnal
macro
thrifty
pink
corporate
hard
nostalgic
euphoric
interstellar
japanese
chilled
slow
futuristic
old_cyber
`);
function forward(text) {
return perchanceTreeRoot.username.names.evaluateItem;
}
var luni = new Lunicode();
luni.tools.creepify.options.maxHeight = 10;
function createMap(chars) {
var alphanum = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
var i = 0;
var map = {};
for(var c of chars) {
map[ alphanum[i] ] = c;
i++;
}
return JSON.stringify(map);
}
//createMap("𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡")
function fancify(text) {
text = text.trim();
if(text === "") {
return "";
}
var finalText = "";
var n = Math.round(Math.random()*61);
switch(n) {
case 0: finalText += text; break;
case 1: finalText += applyCharMap(oldEnglishCharMap, text); break;
case 2: finalText += applyCharMap(medievalCharMap, text); break;
case 3: finalText += fullCrazy(text); break;
case 4: finalText += applyCharMap(cursiveCharMap, text); break;
case 5: finalText += scriptify(text); break;
case 6: finalText += applyCharMap(doubleStruckCharMap, text); break;
case 7: finalText += applyCharMap(wideTextCharMap, text); break;
case 8: finalText += cuteText(text); break;
case 9: finalText += luni.tools.tiny.encode(text); break;
case 10: finalText += luni.tools.flip.encode(text); break;
//case 11: finalText += luni.tools.roundsquares.encode(text); break;
//case 12: finalText += luni.tools.squares.encode(text); break;
//case 13: finalText += applyCharMap(squaresCharMap, text); break;
case 14: finalText += luni.tools.mirror.encode(text); break;
case 15: finalText += luni.tools.creepify.encode(text); break;
case 16: finalText += applyCharMap(invertedSquaresCharMap, text); break;
case 17: finalText += applyCharMap(subscriptCharMap, text); break;
case 18: finalText += applyCharMap(superscriptCharMap, text); break;
case 19: finalText += luni.tools.bubbles.encode(text); break;
case 20: finalText += applyCharMap(squiggleCharMap, text); break;
case 21: finalText += applyCharMap(squiggle2CharMap, text); break;
case 22: finalText += applyCharMap(squiggle3CharMap, text); break;
case 23: finalText += applyCharMap(squiggle4CharMap, text); break;
case 24: finalText += applyCharMap(squiggle5CharMap, text); break;
case 25: finalText += applyCharMap(squiggle6CharMap, text); break;
case 26: finalText += applyCharMap(boldCharMap, text); break;
case 27: finalText += applyCharMap(boldSansCharMap, text); break;
case 28: finalText += applyCharMap(italicCharMap, text); break;
case 29: finalText += applyCharMap(boldItalicCharMap, text); break;
case 30: finalText += applyCharMap(monospaceCharMap, text); break;
case 31: finalText += applyCharMap(upperAnglesCharMap, text); break;
case 32: finalText += applyCharMap(greekCharMap, text); break;
case 33: finalText += applyCharMap(symbolsCharMap, text); break;
case 34: finalText += applyCharMap(currencyCharMap, text); break;
case 35: finalText += applyCharMap(asianStyleCharMap, text); break;
case 36: finalText += applyCharMap(asianStyle2CharMap, text); break;
//case 37: finalText += thickBlockFramed(text); break;
//case 38: finalText += diametricAngleFrame(text); break;
case 39: finalText += wavyJoiner(text); break;
case 40: finalText += dottyJoiner(text); break;
case 41: finalText += kirbyHug(text); break;
case 42: finalText += vaporwaveText(text); break;
case 43: finalText += littleSparkles(text); break;
//case 44: finalText += weirdBox(text); break;
case 45: finalText += firework(text); break;
case 46: finalText += applyCharMap(bentTextCharMap, text); break;
case 47: finalText += applyCharMap(neonCharMap, text); break;
case 48: finalText += applyCharMap(futureAlienCharMap, text); break;
case 49: finalText += strikeThrough(text); break;
case 50: finalText += tildeStrikeThrough(text); break;
case 51: finalText += slashThrough(text); break;
case 52: finalText += underline(text); break;
case 53: finalText += doubleUnderline(text); break;
case 54: finalText += stinky(text); break;
case 55: finalText += heartsBetween(text); break;
case 56: finalText += arrowBelow(text); break;
case 57: finalText += crossAboveBelow(text); break;
case 58: finalText += wingdings(text); break;
case 59: finalText += cuteText(text); break;
//case 60: finalText += crazyWithFlourishOrSymbols(text); break;
case 61: finalText += cuteText(text); break;
default: finalText += text;
}
return finalText.trim();
}
function crazyWithFlourishOrSymbols(text) {
if(Math.random() < 0.7) return wrapInSymbols(crazifyText(text), 2);
else return wrapInFlourish(crazifyText(text), 2);
}
function strikeThrough(text) {
return text.split("").join("̶")+"̶";
}
function tildeStrikeThrough(text) {
return text.split("").join("̴")+"̴";
}
function underline(text) {
return text.split("").join("̲")+"̲";
}
function doubleUnderline(text) {
return text.split("").join("̳")+"̳";
}
function slashThrough(text) {
return text.split("").join("̷")+"̷";
}
function stinky(text) {
return text.split("").join("̾")+"̾"
}
function heartsBetween(text) {
return text.split("").join("♥");
}
function arrowBelow(text) {
return text.split("").join("͎")+"͎";
}
function crossAboveBelow(text) {
return text.split("").join("͓̽")+"͓̽";
}
const wingdingsCharMap =
{"0":"📁︎","1":"📂︎","2":"📄︎","3":"🗏︎","4":"🗐︎","5":"🗄︎","6":"⌛︎","7":"🖮︎","8":"🖰︎","9":"🖲︎","!":"✏︎","\"":"✂︎","#":"✁︎","$":"👓︎","%":"🕭︎","&":"🕮︎","'":"🕯︎","(":"🕿︎",")":"✆︎","*":"🖂︎","+":"🖃︎",",":"📪︎","-":"📫︎",".":"📬︎","/":"📭︎",":":"🖳︎",";":"🖴︎","<":"🖫︎","=":"🖬︎",">":"✇︎","?":"✍︎","A":"✌︎","B":"👌︎","C":"👍︎","D":"👎︎","E":"☜︎","F":"☞︎","G":"☝︎","H":"☟︎","I":"✋︎","J":"☺︎","K":"😐︎","L":"☹︎","M":"💣︎","N":"☠︎","O":"⚐︎","P":"🏱︎","Q":"✈︎","R":"☼︎","S":"💧︎","T":"❄︎","U":"🕆︎","V":"✞︎","W":"🕈︎","X":"✠︎","Y":"✡︎","Z":"☪︎","[":"☯︎","\\":"ॐ︎","]":"☸︎","^":"♈︎","_":"♉︎","`":"♊︎","a":"♋︎","b":"♌︎","c":"♍︎","d":"♎︎","e":"♏︎","f":"♐︎","g":"♑︎","h":"♒︎","i":"♓︎","j":"🙰","k":"🙵","l":"●︎","m":"❍︎","n":"■︎","o":"□︎","p":"◻︎","q":"❑︎","r":"❒︎","s":"⬧︎","t":"⧫︎","u":"◆︎","v":"❖︎","w":"⬥︎","x":"⌧︎","y":"⍓︎","z":"⌘︎","{":"❀︎","|":"✿︎","}":"❝︎","~":"❞︎","":"▯︎","€":"⓪︎","":"①︎","‚":"②︎","ƒ":"③︎","„":"④︎","…":"⑤︎","†":"⑥︎","‡":"⑦︎","ˆ":"⑧︎","‰":"⑨︎","Š":"⑩︎","‹":"⓿︎","Œ":"❶︎","":"❷︎","Ž":"❸︎","":"❹︎","":"❺︎","‘":"❻︎","’":"❼︎","“":"❽︎","”":"❾︎","•":"❿︎","–":"◻︎","—":"◻︎","˜":"◻︎","™":"◻︎","š":"◻︎","›":"◻︎","œ":"◻︎","":"◻︎","ž":"·︎","Ÿ":"•︎","¡":"○︎","¢":"⭕︎","£":"◻︎","¤":"◉︎","¥":"◎︎","¦":"◻︎","§":"▪︎","¨":"◻︎","©":"◻︎","ª":"✦︎","«":"★︎","¬":"✶︎","®":"✹︎","¯":"✵︎","°":"◻︎","±":"⌖︎","²":"⟡︎","³":"⌑︎","´":"◻︎","µ":"✪︎","¶":"✰︎","·":"🕐︎","¸":"🕑︎","¹":"🕒︎","º":"🕓︎","»":"🕔︎","¼":"🕕︎","½":"🕖︎","¾":"🕗︎","¿":"🕘︎","À":"🕙︎","Á":"🕚︎","Â":"🕛︎","Ã":"◻︎","Ä":"◻︎","Å":"◻︎","Æ":"◻︎","Ç":"◻︎","È":"◻︎","É":"◻︎","Ê":"◻︎","Ë":"◻︎","Ì":"◻︎","Í":"◻︎","Î":"◻︎","Ï":"◻︎","Ð":"◻︎","Ñ":"◻︎","Ò":"◻︎","Ó":"◻︎","Ô":"◻︎","Õ":"⌫︎","Ö":"⌦︎","×":"◻︎","Ø":"➢︎","Ù":"◻︎","Ú":"◻︎","Û":"◻︎","Ü":"➲︎","Ý":"◻︎","Þ":"◻︎","ß":"◻︎","à":"◻︎","á":"◻︎","â":"◻︎","ã":"◻︎","ä":"◻︎","å":"◻︎","æ":"◻︎","ç":"◻︎","è":"➔︎","é":"◻︎","ê":"◻︎","ë":"◻︎","ì":"◻︎","í":"◻︎","î":"◻︎","ï":"⇦︎","ð":"⇨︎","ñ":"⇧︎","ò":"⇩︎","ó":"⬄︎","ô":"⇳︎","õ":"⬀︎","ö":"⬁︎","÷":"⬃︎","ø":"⬂︎","ù":"▭︎","ú":"▫︎","û":"✗︎","ü":"✓︎","ý":"☒︎","þ":"☑︎","ÿ":"◻︎"};
function wingdings(text) {
return text.split("").map(function(a) {return wingdingsCharMap[a] ? wingdingsCharMap[a] : a}).join("");
}
const vaporwaveCharMap = {" ":" ", "`" : "`","1" : "1","2" : "2","3" : "3","4" : "4","5" : "5","6" : "6","7" : "7","8" : "8","9" : "9","0" : "0","-" : "-","=" : "=","~" : "~","!" : "!","@" : "@","#" : "#","$" : "$","%" : "%","^" : "^","&" : "&","*" : "*","(" : "(",")" : ")","_" : "_","+" : "+","q" : "q","w" : "w","e" : "e","r" : "r","t" : "t","y" : "y","u" : "u","i" : "i","o" : "o","p" : "p","[" : "[","]" : "]","\\" : "\\","Q" : "Q","W" : "W","E" : "E","R" : "R","T" : "T","Y" : "Y","U" : "U","I" : "I","O" : "O","P" : "P","{" : "{","}" : "}","|" : "|","a" : "a","s" : "s","d" : "d","f" : "f","g" : "g","h" : "h","j" : "j","k" : "k","l" : "l",";" : ";","'" : "'","A" : "A","S" : "S","D" : "D","F" : "F","G" : "G","H" : "H","J" : "J","K" : "K","L" : "L",":" : ":","\"" : "\"","z" : "z","x" : "x","c" : "c","v" : "v","b" : "b","n" : "n","m" : "m","," : ",","." : ".","/" : "/","Z" : "Z","X" : "X","C" : "C","V" : "V","B" : "B","N" : "N","M" : "M","<" : "<",">" : ">","?" : "?"};
function vaporwaveText(text) {
var numSpaces = text.split(" ").length;
text = applyCharMap(vaporwaveCharMap, text);
var asianChars = getAsianChars(Math.max(3, numSpaces));
if(numSpaces > 6) asianChars = asianChars.split("").map(c => c+[""," "][Math.round(Math.random()*0.6)]).join("");
var outputs = [];
outputs.push( text+" "+asianChars );
outputs.push( text.replace(/ /g, "░").replace(/ae/, "æ").replace(/A/g, "Λ").replace(/E/g, function() { return Math.random() > 0.5 ? "Ξ" : "Σ"; }).replace(/O/g, "♢")+" ("+asianChars+")" );
outputs.push("【"+text+"】");
return outputs[Math.floor(Math.random()*outputs.length)];
}
function getAsianChars(n) {
if(!n) n = 1;
var chars = "リサフランク現代のコンピュ竹内 まりや若者が履く流行のスニーカー真夜中のドアホットドッグマスターストライカーソニーブギ新しい日の誕生ライフ - ヒスイ蒸気波 無線゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚゛゜ゝゞゟ亜哀挨愛曖悪握圧扱宛嵐安案暗以衣位囲医依委威為畏胃尉異移萎偉椅彙意違維慰遺緯域育壱逸茨芋引印因咽姻員院淫陰飲隠韻右宇羽雨唄鬱畝浦運雲永泳英映栄営詠影鋭衛易疫益液駅悦越謁閲円延沿炎怨宴媛援園煙猿遠鉛塩演縁艶汚王凹央応往押旺欧殴桜翁奥横岡屋億憶臆虞乙俺卸音恩温穏下化火加可仮何花佳価果河苛科";
var str = "";
for(var i = 0; i < n; i++) {
str += chars[Math.floor(Math.random()*chars.length)];
}
return str;
}
const flourishArray = ["★·.·´¯`·.·★ [[text]] ★·.·´¯`·.·★", "▁ ▂ ▄ ▅ ▆ ▇ █ [[text]] █ ▇ ▆ ▅ ▄ ▂ ▁", "°°°·.°·..·°¯°·._.· [[text]] ·._.·°¯°·.·° .·°°°", "¸,ø¤º°`°º¤ø,¸¸,ø¤º° [[text]] °º¤ø,¸¸,ø¤º°`°º¤ø,¸", "ıllıllı [[text]] ıllıllı", "•?((¯°·._.• [[text]] •._.·°¯))؟•", "▌│█║▌║▌║ [[text]] ║▌║▌║█│▌", "׺°”˜`”°º× [[text]] ׺°”˜`”°º×", "•••´º´•» [[text]] «•´º´•••", "*•.¸♡ [[text]] ♡¸.•*", "╰☆☆ [[text]] ☆☆╮", ".•°¤*(¯`★´¯)*¤° [[text]] °¤*(¯´★`¯)*¤°•.", "(¯´•._.• [[text]] •._.•´¯)", "¸„.-•~¹°”ˆ˜¨ [[text]] ¨˜ˆ”°¹~•-.„¸", "░▒▓█ [[text]] █▓▒░", "░▒▓█►─═ [[text]] ═─◄█▓▒░", "★彡 [[text]] 彡★", "•´¯`•. [[text]] .•´¯`•", "§.•´¨'°÷•..× [[text]] ×,.•´¨'°÷•..§", "•°¯`•• [[text]] ••´¯°•", "(¯`*•.¸,¤°´✿.。.:* [[text]] *.:。.✿`°¤,¸.•*´¯)", "|!¤*'~``~'*¤!| [[text]] |!¤*'~``~'*¤!|", "•._.••´¯``•.¸¸.•` [[text]] `•.¸¸.•´´¯`••._.•", "¸„.-•~¹°”ˆ˜¨ [[text]] ¨˜ˆ”°¹~•-.„¸", "(¯´•._.• [[text]] •._.•´¯)", "••¤(`פ [[text]] ¤×´)¤••", "•´¯`•» [[text]] «•´¯`•", " .o0×X×0o. [[text]] .o0×X×0o.", "¤¸¸.•´¯`•¸¸.•..>> [[text]] <<..•.¸¸•´¯`•.¸¸¤", "—(••÷ [[text]] ÷••)—", "¸,ø¤º°`°º¤ø,¸ [[text]] ¸,ø¤º°`°º¤ø,¸", "`•.¸¸.•´´¯`••._.• [[text]] •._.••`¯´´•.¸¸.•`", ",-*' ^ '~*-.,_,.-*~ [[text]] ~*-.,_,.-*~' ^ '*-,", "`•.,¸¸,.•´¯ [[text]] ¯`•.,¸¸,.•´", "↤↤↤↤↤ [[text]] ↦↦↦↦↦", "➶➶➶➶➶ [[text]] ➷➷➷➷➷", "↫↫↫↫↫ [[text]] ↬↬↬↬↬", "·.¸¸.·♩♪♫ [[text]] ♫♪♩·.¸¸.·", "【。_。】 [[text]] 【。_。】", "|I{•------» [[text]] «------•}I|", "▀▄▀▄▀▄ [[text]] ▄▀▄▀▄▀", "(-_-) [[text]] (-_-)", "•´¯`•. [[text]] .•´¯`•", "-漫~*'¨¯¨'*·舞~ [[text]] ~舞*'¨¯¨'*·~漫-", "๑۞๑,¸¸,ø¤º°`°๑۩ [[text]] ๑۩ ,¸¸,ø¤º°`°๑۞๑", ".•°¤*(¯`★´¯)*¤° [[text]] °¤*(¯´★`¯)*¤°•.", "••.•´¯`•.•• [[text]] ••.•´¯`•.••", "¤¸¸.•´¯`•¸¸.•..>> [[text]] <<..•.¸¸•´¯`•.¸¸¤", "◦•●◉✿ [[text]] ✿◉●•◦", "╚»★«╝ [[text]] ╚»★«╝", "-·=»‡«=·- [[text]] -·=»‡«=·-", "∙∙·▫▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ [[text]] ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫▫·∙∙", "¸¸♬·¯·♩¸¸♪·¯·♫¸¸ [[text]] ¸¸♫·¯·♪¸¸♩·¯·♬¸¸", "ஜ۩۞۩ஜ [[text]] ஜ۩۞۩ஜ", "¤ (¯´☆✭.¸_)¤ [[text]] ¤(_¸.✭☆´¯) ¤", "(¯`·.¸¸.·´¯`·.¸¸.-> [[text]] <-.¸¸.·´¯`·.¸¸.·´¯)", "✿.。.:* ☆:**:. [[text]] .:**:.☆*.:。.✿", ".•♫•♬• [[text]] •♬•♫•.", "ღ(¯`◕‿◕´¯) ♫ ♪ ♫ [[text]] ♫ ♪ ♫ (¯`◕‿◕´¯)ღ", "«-(¯`v´¯)-« [[text]] »-(¯`v´¯)-»"];
function wrapInFlourish(text) {
return flourishArray[Math.floor(Math.random()*flourishArray.length)].replace("[[text]]", text);
}
function wrapInSymbols(text, number) {
return randomSymbols(number) +" "+ text +" "+ randomSymbols(number)
}
function firework(text) {
return text.split("").join("҉")+ "҉";
}
function weirdBox(text) {
return text.replace(/([^\s])/g,"[̲̅$1]");
}
function littleSparkles(text) {
return "˜”*°•.˜”*°• "+text+" •°*”˜.•°*”˜";
}
function kirbyHug(text) {
return "(っ◔◡◔)っ ♥ " + text + " ♥"
}
function dottyJoiner(text) {
return "░" + text.split("").join("░") + "░";
}
function wavyJoiner(text) {
return "≋" + text.split("").join("≋") + "≋";
}
function diametricAngleFrame(text) {
return text.replace(/([^\s])/g,"『$1』");
}
function thickBlockFramed(text) {
return text.replace(/([^\s])/g,"【$1】");
}
function applyCharMap(map, text) {
let out = "";
for(let c of text.split("")) {
if(map[c] !== undefined) out += map[c];
else if(map[c.toLowerCase()] !== undefined) out += map[c.toLowerCase()];
else out += c;
}
return out;
}
/* eslint-disable */
const futureAlienCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ᗩ","b":"ᗷ","c":"ᑢ","d":"ᕲ","e":"ᘿ","f":"ᖴ","g":"ᘜ","h":"ᕼ","i":"ᓰ","j":"ᒚ","k":"ᖽᐸ","l":"ᒪ","m":"ᘻ","n":"ᘉ","o":"ᓍ","p":"ᕵ","q":"ᕴ","r":"ᖇ","s":"S","t":"ᖶ","u":"ᑘ","v":"ᐺ","w":"ᘺ","x":"᙭","y":"ᖻ","z":"ᗱ","A":"ᗩ","B":"ᗷ","C":"ᑢ","D":"ᕲ","E":"ᘿ","F":"ᖴ","G":"ᘜ","H":"ᕼ","I":"ᓰ","J":"ᒚ","K":"ᖽᐸ","L":"ᒪ","M":"ᘻ","N":"ᘉ","O":"ᓍ","P":"ᕵ","Q":"ᕴ","R":"ᖇ","S":"S","T":"ᖶ","U":"ᑘ","V":"ᐺ","W":"ᘺ","X":"᙭","Y":"ᖻ","Z":"ᗱ"};
const squiggle6CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ค","b":"๖","c":"¢","d":"໓","e":"ē","f":"f","g":"ງ","h":"h","i":"i","j":"ว","k":"k","l":"l","m":"๓","n":"ຖ","o":"໐","p":"p","q":"๑","r":"r","s":"Ş","t":"t","u":"น","v":"ง","w":"ຟ","x":"x","y":"ฯ","z":"ຊ","A":"ค","B":"๖","C":"¢","D":"໓","E":"ē","F":"f","G":"ງ","H":"h","I":"i","J":"ว","K":"k","L":"l","M":"๓","N":"ຖ","O":"໐","P":"p","Q":"๑","R":"r","S":"Ş","T":"t","U":"น","V":"ง","W":"ຟ","X":"x","Y":"ฯ","Z":"ຊ"};
const squiggle5CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ą","b":"ც","c":"ƈ","d":"ɖ","e":"ɛ","f":"ʄ","g":"ɠ","h":"ɧ","i":"ı","j":"ʝ","k":"ƙ","l":"Ɩ","m":"ɱ","n":"ŋ","o":"ơ","p":"℘","q":"զ","r":"ཞ","s":"ʂ","t":"ɬ","u":"ų","v":"۷","w":"ῳ","x":"ҳ","y":"ყ","z":"ʑ","A":"ą","B":"ც","C":"ƈ","D":"ɖ","E":"ɛ","F":"ʄ","G":"ɠ","H":"ɧ","I":"ı","J":"ʝ","K":"ƙ","L":"Ɩ","M":"ɱ","N":"ŋ","O":"ơ","P":"℘","Q":"զ","R":"ཞ","S":"ʂ","T":"ɬ","U":"ų","V":"۷","W":"ῳ","X":"ҳ","Y":"ყ","Z":"ʑ"};
const asianStyle2CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ム","b":"乃","c":"ᄃ","d":"り","e":"乇","f":"キ","g":"ム","h":"ん","i":"ノ","j":"フ","k":"ズ","l":"レ","m":"ᄊ","n":"刀","o":"の","p":"ア","q":"ゐ","r":"尺","s":"丂","t":"イ","u":"ひ","v":"√","w":"W","x":"メ","y":"リ","z":"乙","A":"ム","B":"乃","C":"ᄃ","D":"り","E":"乇","F":"キ","G":"ム","H":"ん","I":"ノ","J":"フ","K":"ズ","L":"レ","M":"ᄊ","N":"刀","O":"の","P":"ア","Q":"ゐ","R":"尺","S":"丂","T":"イ","U":"ひ","V":"√","W":"W","X":"メ","Y":"リ","Z":"乙"};
const asianStyleCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"卂","b":"乃","c":"匚","d":"ᗪ","e":"乇","f":"千","g":"Ꮆ","h":"卄","i":"丨","j":"フ","k":"Ҝ","l":"ㄥ","m":"爪","n":"几","o":"ㄖ","p":"卩","q":"Ɋ","r":"尺","s":"丂","t":"ㄒ","u":"ㄩ","v":"ᐯ","w":"山","x":"乂","y":"ㄚ","z":"乙","A":"卂","B":"乃","C":"匚","D":"ᗪ","E":"乇","F":"千","G":"Ꮆ","H":"卄","I":"丨","J":"フ","K":"Ҝ","L":"ㄥ","M":"爪","N":"几","O":"ㄖ","P":"卩","Q":"Ɋ","R":"尺","S":"丂","T":"ㄒ","U":"ㄩ","V":"ᐯ","W":"山","X":"乂","Y":"ㄚ","Z":"乙"};
const squaresCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"🄰","b":"🄱","c":"🄲","d":"🄳","e":"🄴","f":"🄵","g":"🄶","h":"🄷","i":"🄸","j":"🄹","k":"🄺","l":"🄻","m":"🄼","n":"🄽","o":"🄾","p":"🄿","q":"🅀","r":"🅁","s":"🅂","t":"🅃","u":"🅄","v":"🅅","w":"🅆","x":"🅇","y":"🅈","z":"🅉","A":"🄰","B":"🄱","C":"🄲","D":"🄳","E":"🄴","F":"🄵","G":"🄶","H":"🄷","I":"🄸","J":"🄹","K":"🄺","L":"🄻","M":"🄼","N":"🄽","O":"🄾","P":"🄿","Q":"🅀","R":"🅁","S":"🅂","T":"🅃","U":"🅄","V":"🅅","W":"🅆","X":"🅇","Y":"🅈","Z":"🅉"};
const squiggle4CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"Ꮧ","b":"Ᏸ","c":"ፈ","d":"Ꮄ","e":"Ꮛ","f":"Ꭶ","g":"Ꮆ","h":"Ꮒ","i":"Ꭵ","j":"Ꮰ","k":"Ꮶ","l":"Ꮭ","m":"Ꮇ","n":"Ꮑ","o":"Ꭷ","p":"Ꭾ","q":"Ꭴ","r":"Ꮢ","s":"Ꮥ","t":"Ꮦ","u":"Ꮼ","v":"Ꮙ","w":"Ꮗ","x":"ጀ","y":"Ꭹ","z":"ፚ","A":"Ꮧ","B":"Ᏸ","C":"ፈ","D":"Ꮄ","E":"Ꮛ","F":"Ꭶ","G":"Ꮆ","H":"Ꮒ","I":"Ꭵ","J":"Ꮰ","K":"Ꮶ","L":"Ꮭ","M":"Ꮇ","N":"Ꮑ","O":"Ꭷ","P":"Ꭾ","Q":"Ꭴ","R":"Ꮢ","S":"Ꮥ","T":"Ꮦ","U":"Ꮼ","V":"Ꮙ","W":"Ꮗ","X":"ጀ","Y":"Ꭹ","Z":"ፚ"};
const neonCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ᗩ","b":"ᗷ","c":"ᑕ","d":"ᗪ","e":"E","f":"ᖴ","g":"G","h":"ᕼ","i":"I","j":"ᒍ","k":"K","l":"ᒪ","m":"ᗰ","n":"ᑎ","o":"O","p":"ᑭ","q":"ᑫ","r":"ᖇ","s":"ᔕ","t":"T","u":"ᑌ","v":"ᐯ","w":"ᗯ","x":"᙭","y":"Y","z":"ᘔ","A":"ᗩ","B":"ᗷ","C":"ᑕ","D":"ᗪ","E":"E","F":"ᖴ","G":"G","H":"ᕼ","I":"I","J":"ᒍ","K":"K","L":"ᒪ","M":"ᗰ","N":"ᑎ","O":"O","P":"ᑭ","Q":"ᑫ","R":"ᖇ","S":"ᔕ","T":"T","U":"ᑌ","V":"ᐯ","W":"ᗯ","X":"᙭","Y":"Y","Z":"ᘔ"};
const squiggle3CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ǟ","b":"ɮ","c":"ƈ","d":"ɖ","e":"ɛ","f":"ʄ","g":"ɢ","h":"ɦ","i":"ɨ","j":"ʝ","k":"ӄ","l":"ʟ","m":"ʍ","n":"ռ","o":"օ","p":"ք","q":"զ","r":"ʀ","s":"ֆ","t":"ȶ","u":"ʊ","v":"ʋ","w":"ա","x":"Ӽ","y":"ʏ","z":"ʐ","A":"ǟ","B":"ɮ","C":"ƈ","D":"ɖ","E":"ɛ","F":"ʄ","G":"ɢ","H":"ɦ","I":"ɨ","J":"ʝ","K":"ӄ","L":"ʟ","M":"ʍ","N":"ռ","O":"օ","P":"ք","Q":"զ","R":"ʀ","S":"ֆ","T":"ȶ","U":"ʊ","V":"ʋ","W":"ա","X":"Ӽ","Y":"ʏ","Z":"ʐ"};
const monospaceCharMap = {"0":"𝟶","1":"𝟷","2":"𝟸","3":"𝟹","4":"𝟺","5":"𝟻","6":"𝟼","7":"𝟽","8":"𝟾","9":"𝟿","a":"𝚊","b":"𝚋","c":"𝚌","d":"𝚍","e":"𝚎","f":"𝚏","g":"𝚐","h":"𝚑","i":"𝚒","j":"𝚓","k":"𝚔","l":"𝚕","m":"𝚖","n":"𝚗","o":"𝚘","p":"𝚙","q":"𝚚","r":"𝚛","s":"𝚜","t":"𝚝","u":"𝚞","v":"𝚟","w":"𝚠","x":"𝚡","y":"𝚢","z":"𝚣","A":"𝙰","B":"𝙱","C":"𝙲","D":"𝙳","E":"𝙴","F":"𝙵","G":"𝙶","H":"𝙷","I":"𝙸","J":"𝙹","K":"𝙺","L":"𝙻","M":"𝙼","N":"𝙽","O":"𝙾","P":"𝙿","Q":"𝚀","R":"𝚁","S":"𝚂","T":"𝚃","U":"𝚄","V":"𝚅","W":"𝚆","X":"𝚇","Y":"𝚈","Z":"𝚉"};
const boldItalicCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"𝙖","b":"𝙗","c":"𝙘","d":"𝙙","e":"𝙚","f":"𝙛","g":"𝙜","h":"𝙝","i":"𝙞","j":"𝙟","k":"𝙠","l":"𝙡","m":"𝙢","n":"𝙣","o":"𝙤","p":"𝙥","q":"𝙦","r":"𝙧","s":"𝙨","t":"𝙩","u":"𝙪","v":"𝙫","w":"𝙬","x":"𝙭","y":"𝙮","z":"𝙯","A":"𝘼","B":"𝘽","C":"𝘾","D":"𝘿","E":"𝙀","F":"𝙁","G":"𝙂","H":"𝙃","I":"𝙄","J":"𝙅","K":"𝙆","L":"𝙇","M":"𝙈","N":"𝙉","O":"𝙊","P":"𝙋","Q":"𝙌","R":"𝙍","S":"𝙎","T":"𝙏","U":"𝙐","V":"𝙑","W":"𝙒","X":"𝙓","Y":"𝙔","Z":"𝙕"};
const boldCharMap = {"0":"𝟎","1":"𝟏","2":"𝟐","3":"𝟑","4":"𝟒","5":"𝟓","6":"𝟔","7":"𝟕","8":"𝟖","9":"𝟗","a":"𝐚","b":"𝐛","c":"𝐜","d":"𝐝","e":"𝐞","f":"𝐟","g":"𝐠","h":"𝐡","i":"𝐢","j":"𝐣","k":"𝐤","l":"𝐥","m":"𝐦","n":"𝐧","o":"𝐨","p":"𝐩","q":"𝐪","r":"𝐫","s":"𝐬","t":"𝐭","u":"𝐮","v":"𝐯","w":"𝐰","x":"𝐱","y":"𝐲","z":"𝐳","A":"𝐀","B":"𝐁","C":"𝐂","D":"𝐃","E":"𝐄","F":"𝐅","G":"𝐆","H":"𝐇","I":"𝐈","J":"𝐉","K":"𝐊","L":"𝐋","M":"𝐌","N":"𝐍","O":"𝐎","P":"𝐏","Q":"𝐐","R":"𝐑","S":"𝐒","T":"𝐓","U":"𝐔","V":"𝐕","W":"𝐖","X":"𝐗","Y":"𝐘","Z":"𝐙"};
const boldSansCharMap = {"0":"𝟬","1":"𝟭","2":"𝟮","3":"𝟯","4":"𝟰","5":"𝟱","6":"𝟲","7":"𝟳","8":"𝟴","9":"𝟵","a":"𝗮","b":"𝗯","c":"𝗰","d":"𝗱","e":"𝗲","f":"𝗳","g":"𝗴","h":"𝗵","i":"𝗶","j":"𝗷","k":"𝗸","l":"𝗹","m":"𝗺","n":"𝗻","o":"𝗼","p":"𝗽","q":"𝗾","r":"𝗿","s":"𝘀","t":"𝘁","u":"𝘂","v":"𝘃","w":"𝘄","x":"𝘅","y":"𝘆","z":"𝘇","A":"𝗔","B":"𝗕","C":"𝗖","D":"𝗗","E":"𝗘","F":"𝗙","G":"𝗚","H":"𝗛","I":"𝗜","J":"𝗝","K":"𝗞","L":"𝗟","M":"𝗠","N":"𝗡","O":"𝗢","P":"𝗣","Q":"𝗤","R":"𝗥","S":"𝗦","T":"𝗧","U":"𝗨","V":"𝗩","W":"𝗪","X":"𝗫","Y":"𝗬","Z":"𝗭"};
const italicCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"𝘢","b":"𝘣","c":"𝘤","d":"𝘥","e":"𝘦","f":"𝘧","g":"𝘨","h":"𝘩","i":"𝘪","j":"𝘫","k":"𝘬","l":"𝘭","m":"𝘮","n":"𝘯","o":"𝘰","p":"𝘱","q":"𝘲","r":"𝘳","s":"𝘴","t":"𝘵","u":"𝘶","v":"𝘷","w":"𝘸","x":"𝘹","y":"𝘺","z":"𝘻","A":"𝘈","B":"𝘉","C":"𝘊","D":"𝘋","E":"𝘌","F":"𝘍","G":"𝘎","H":"𝘏","I":"𝘐","J":"𝘑","K":"𝘒","L":"𝘓","M":"𝘔","N":"𝘕","O":"𝘖","P":"𝘗","Q":"𝘘","R":"𝘙","S":"𝘚","T":"𝘛","U":"𝘜","V":"𝘝","W":"𝘞","X":"𝘟","Y":"𝘠","Z":"𝘡"};
const squiggle2CharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"α","b":"Ⴆ","c":"ƈ","d":"ԃ","e":"ҽ","f":"ϝ","g":"ɠ","h":"ԋ","i":"ι","j":"ʝ","k":"ƙ","l":"ʅ","m":"ɱ","n":"ɳ","o":"σ","p":"ρ","q":"ϙ","r":"ɾ","s":"ʂ","t":"ƚ","u":"υ","v":"ʋ","w":"ɯ","x":"x","y":"ყ","z":"ȥ","A":"A","B":"B","C":"C","D":"D","E":"E","F":"F","G":"G","H":"H","I":"I","J":"J","K":"K","L":"L","M":"M","N":"N","O":"O","P":"P","Q":"Q","R":"R","S":"S","T":"T","U":"U","V":"V","W":"W","X":"X","Y":"Y","Z":"Z"};
const currencyCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"₳","b":"฿","c":"₵","d":"Đ","e":"Ɇ","f":"₣","g":"₲","h":"Ⱨ","i":"ł","j":"J","k":"₭","l":"Ⱡ","m":"₥","n":"₦","o":"Ø","p":"₱","q":"Q","r":"Ɽ","s":"₴","t":"₮","u":"Ʉ","v":"V","w":"₩","x":"Ӿ","y":"Ɏ","z":"Ⱬ","A":"₳","B":"฿","C":"₵","D":"Đ","E":"Ɇ","F":"₣","G":"₲","H":"Ⱨ","I":"ł","J":"J","K":"₭","L":"Ⱡ","M":"₥","N":"₦","O":"Ø","P":"₱","Q":"Q","R":"Ɽ","S":"₴","T":"₮","U":"Ʉ","V":"V","W":"₩","X":"Ӿ","Y":"Ɏ","Z":"Ⱬ"};
const symbolsCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"å","b":"ß","c":"¢","d":"Ð","e":"ê","f":"£","g":"g","h":"h","i":"ï","j":"j","k":"k","l":"l","m":"m","n":"ñ","o":"ð","p":"þ","q":"q","r":"r","s":"§","t":"†","u":"µ","v":"v","w":"w","x":"x","y":"¥","z":"z","A":"Ä","B":"ß","C":"Ç","D":"Ð","E":"È","F":"£","G":"G","H":"H","I":"Ì","J":"J","K":"K","L":"L","M":"M","N":"ñ","O":"Ö","P":"þ","Q":"Q","R":"R","S":"§","T":"†","U":"Ú","V":"V","W":"W","X":"×","Y":"¥","Z":"Z"};
const greekCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"α","b":"в","c":"¢","d":"∂","e":"є","f":"ƒ","g":"g","h":"н","i":"ι","j":"נ","k":"к","l":"ℓ","m":"м","n":"η","o":"σ","p":"ρ","q":"q","r":"я","s":"ѕ","t":"т","u":"υ","v":"ν","w":"ω","x":"χ","y":"у","z":"z","A":"α","B":"в","C":"¢","D":"∂","E":"є","F":"ƒ","G":"g","H":"н","I":"ι","J":"נ","K":"к","L":"ℓ","M":"м","N":"η","O":"σ","P":"ρ","Q":"q","R":"я","S":"ѕ","T":"т","U":"υ","V":"ν","W":"ω","X":"χ","Y":"у","Z":"z"};
const bentTextCharMap = {"0":"⊘","1":"𝟙","2":"ϩ","3":"Ӡ","4":"५","5":"Ƽ","6":"Ϭ","7":"7","8":"𝟠","9":"९","a":"ą","b":"ҍ","c":"ç","d":"ժ","e":"ҽ","f":"ƒ","g":"ց","h":"հ","i":"ì","j":"ʝ","k":"ҟ","l":"Ӏ","m":"ʍ","n":"ղ","o":"օ","p":"ք","q":"զ","r":"ɾ","s":"ʂ","t":"է","u":"մ","v":"ѵ","w":"ա","x":"×","y":"վ","z":"Հ","A":"Ⱥ","B":"β","C":"↻","D":"Ꭰ","E":"Ɛ","F":"Ƒ","G":"Ɠ","H":"Ƕ","I":"į","J":"ل","K":"Ҡ","L":"Ꝉ","M":"Ɱ","N":"ហ","O":"ට","P":"φ","Q":"Ҩ","R":"འ","S":"Ϛ","T":"Ͳ","U":"Ա","V":"Ỽ","W":"చ","X":"ჯ","Y":"Ӌ","Z":"ɀ"};
const upperAnglesCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"Λ","b":"B","c":"ᄃ","d":"D","e":"Σ","f":"F","g":"G","h":"Ή","i":"I","j":"J","k":"K","l":"ᄂ","m":"M","n":"П","o":"Ө","p":"P","q":"Q","r":"Я","s":"Ƨ","t":"Ƭ","u":"Ц","v":"V","w":"Щ","x":"X","y":"Y","z":"Z","A":"Λ","B":"B","C":"ᄃ","D":"D","E":"Σ","F":"F","G":"G","H":"Ή","I":"I","J":"J","K":"K","L":"ᄂ","M":"M","N":"П","O":"Ө","P":"P","Q":"Q","R":"Я","S":"Ƨ","T":"Ƭ","U":"Ц","V":"V","W":"Щ","X":"X","Y":"Y","Z":"Z"};
const subscriptCharMap = {"0":"₀","1":"₁","2":"₂","3":"₃","4":"₄","5":"₅","6":"₆","7":"₇","8":"₈","9":"₉","a":"ₐ","b":"b","c":"c","d":"d","e":"ₑ","f":"f","g":"g","h":"ₕ","i":"ᵢ","j":"ⱼ","k":"ₖ","l":"ₗ","m":"ₘ","n":"ₙ","o":"ₒ","p":"ₚ","q":"q","r":"ᵣ","s":"ₛ","t":"ₜ","u":"ᵤ","v":"ᵥ","w":"w","x":"ₓ","y":"y","z":"z","A":"ₐ","B":"B","C":"C","D":"D","E":"ₑ","F":"F","G":"G","H":"ₕ","I":"ᵢ","J":"ⱼ","K":"ₖ","L":"ₗ","M":"ₘ","N":"ₙ","O":"ₒ","P":"ₚ","Q":"Q","R":"ᵣ","S":"ₛ","T":"ₜ","U":"ᵤ","V":"ᵥ","W":"W","X":"ₓ","Y":"Y","Z":"Z","+":"₊","-":"₋","=":"₌","(":"₍",")":"₎"};
const superscriptCharMap = {"0":"⁰","1":"¹","2":"²","3":"³","4":"⁴","5":"⁵","6":"⁶","7":"⁷","8":"⁸","9":"⁹","a":"ᵃ","b":"ᵇ","c":"ᶜ","d":"ᵈ","e":"ᵉ","f":"ᶠ","g":"ᵍ","h":"ʰ","i":"ⁱ","j":"ʲ","k":"ᵏ","l":"ˡ","m":"ᵐ","n":"ⁿ","o":"ᵒ","p":"ᵖ","q":"q","r":"ʳ","s":"ˢ","t":"ᵗ","u":"ᵘ","v":"ᵛ","w":"ʷ","x":"ˣ","y":"ʸ","z":"ᶻ","A":"ᴬ","B":"ᴮ","C":"ᶜ","D":"ᴰ","E":"ᴱ","F":"ᶠ","G":"ᴳ","H":"ᴴ","I":"ᴵ","J":"ᴶ","K":"ᴷ","L":"ᴸ","M":"ᴹ","N":"ᴺ","O":"ᴼ","P":"ᴾ","Q":"Q","R":"ᴿ","S":"ˢ","T":"ᵀ","U":"ᵁ","V":"ⱽ","W":"ᵂ","X":"ˣ","Y":"ʸ","Z":"ᶻ","+":"⁺","-":"⁻","=":"⁼","(":"⁽",")":"⁾"};
const squiggleCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"ค","b":"๒","c":"ς","d":"๔","e":"є","f":"Ŧ","g":"ﻮ","h":"ђ","i":"เ","j":"ן","k":"к","l":"ɭ","m":"๓","n":"ภ","o":"๏","p":"ק","q":"ợ","r":"г","s":"ร","t":"Շ","u":"ย","v":"ש","w":"ฬ","x":"א","y":"ץ","z":"չ","A":"ค","B":"๒","C":"ς","D":"๔","E":"є","F":"Ŧ","G":"ﻮ","H":"ђ","I":"เ","J":"ן","K":"к","L":"ɭ","M":"๓","N":"ภ","O":"๏","P":"ק","Q":"ợ","R":"г","S":"ร","T":"Շ","U":"ย","V":"ש","W":"ฬ","X":"א","Y":"ץ","Z":"չ"};
const doubleStruckCharMap = {"0":"𝟘","1":"𝟙","2":"𝟚","3":"𝟛","4":"𝟜","5":"𝟝","6":"𝟞","7":"𝟟","8":"𝟠","9":"𝟡","a":"𝕒","b":"𝕓","c":"𝕔","d":"𝕕","e":"𝕖","f":"𝕗","g":"𝕘","h":"𝕙","i":"𝕚","j":"𝕛","k":"𝕜","l":"𝕝","m":"𝕞","n":"𝕟","o":"𝕠","p":"𝕡","q":"𝕢","r":"𝕣","s":"𝕤","t":"𝕥","u":"𝕦","v":"𝕧","w":"𝕨","x":"𝕩","y":"𝕪","z":"𝕫","A":"𝔸","B":"𝔹","C":"ℂ","D":"𝔻","E":"𝔼","F":"𝔽","G":"𝔾","H":"ℍ","I":"𝕀","J":"𝕁","K":"𝕂","L":"𝕃","M":"𝕄","N":"ℕ","O":"𝕆","P":"ℙ","Q":"ℚ","R":"ℝ","S":"𝕊","T":"𝕋","U":"𝕌","V":"𝕍","W":"𝕎","X":"𝕏","Y":"𝕐","Z":"ℤ"};
const medievalCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"𝖆","b":"𝖇","c":"𝖈","d":"𝖉","e":"𝖊","f":"𝖋","g":"𝖌","h":"𝖍","i":"𝖎","j":"𝖏","k":"𝖐","l":"𝖑","m":"𝖒","n":"𝖓","o":"𝖔","p":"𝖕","q":"𝖖","r":"𝖗","s":"𝖘","t":"𝖙","u":"𝖚","v":"𝖛","w":"𝖜","x":"𝖝","y":"𝖞","z":"𝖟","A":"𝕬","B":"𝕭","C":"𝕮","D":"𝕯","E":"𝕰","F":"𝕱","G":"𝕲","H":"𝕳","I":"𝕴","J":"𝕵","K":"𝕶","L":"𝕷","M":"𝕸","N":"𝕹","O":"𝕺","P":"𝕻","Q":"𝕼","R":"𝕽","S":"𝕾","T":"𝕿","U":"𝖀","V":"𝖁","W":"𝖂","X":"𝖃","Y":"𝖄","Z":"𝖅"};
const invertedSquaresCharMap = {q:"🆀",w:"🆆",e:"🅴",r:"🆁",t:"🆃",y:"🆈",u:"🆄",i:"🅸",o:"🅾",p:"🅿",a:"🅰",s:"🆂",d:"🅳",f:"🅵",g:"🅶",h:"🅷",j:"🅹",k:"🅺",l:"🅻",z:"🆉",x:"🆇",c:"🅲",v:"🆅",b:"🅱",n:"🅽",m:"🅼"}
const cursiveCharMap = {"0":"0","1":"1","2":"2","3":"3","4":"4","5":"5","6":"6","7":"7","8":"8","9":"9","a":"𝓪","b":"𝓫","c":"𝓬","d":"𝓭","e":"𝓮","f":"𝓯","g":"𝓰","h":"𝓱","i":"𝓲","j":"𝓳","k":"𝓴","l":"𝓵","m":"𝓶","n":"𝓷","o":"𝓸","p":"𝓹","q":"𝓺","r":"𝓻","s":"𝓼","t":"𝓽","u":"𝓾","v":"𝓿","w":"𝔀","x":"𝔁","y":"𝔂","z":"𝔃","A":"𝓐","B":"𝓑","C":"𝓒","D":"𝓓","E":"𝓔","F":"𝓕","G":"𝓖","H":"𝓗","I":"𝓘","J":"𝓙","K":"𝓚","L":"𝓛","M":"𝓜","N":"𝓝","O":"𝓞","P":"𝓟","Q":"𝓠","R":"𝓡","S":"𝓢","T":"𝓣","U":"𝓤","V":"𝓥","W":"𝓦","X":"𝓧","Y":"𝓨","Z":"𝓩"};
const oldEnglishCharMap = {"a":"𝔞","b":"𝔟","c":"𝔠","d":"𝔡","e":"𝔢","f":"𝔣","g":"𝔤","h":"𝔥","i":"𝔦","j":"𝔧","k":"𝔨","l":"𝔩","m":"𝔪","n":"𝔫","o":"𝔬","p":"𝔭","q":"𝔮","r":"𝔯","s":"𝔰","t":"𝔱","u":"𝔲","v":"𝔳","w":"𝔴","x":"𝔵","y":"𝔶","z":"𝔷","A":"𝔄","B":"𝔅","C":"ℭ","D":"𝔇","E":"𝔈","F":"𝔉","G":"𝔊","H":"ℌ","I":"ℑ","J":"𝔍","K":"𝔎","L":"𝔏","M":"𝔐","N":"𝔑","O":"𝔒","P":"𝔓","Q":"𝔔","R":"ℜ","S":"𝔖","T":"𝔗","U":"𝔘","V":"𝔙","W":"𝔚","X":"𝔛","Y":"𝔜","Z":"ℨ"};
const wideTextCharMap = {"`" : "`","1" : "1","2" : "2","3" : "3","4" : "4","5" : "5","6" : "6","7" : "7","8" : "8","9" : "9","0" : "0","-" : "-","=" : "=","~" : "~","!" : "!","@" : "@","#" : "#","$" : "$","%" : "%","^" : "^","&" : "&","*" : "*","(" : "(",")" : ")","_" : "_","+" : "+","q" : "q","w" : "w","e" : "e","r" : "r","t" : "t","y" : "y","u" : "u","i" : "i","o" : "o","p" : "p","[" : "[","]" : "]","\\" : "\\","Q" : "Q","W" : "W","E" : "E","R" : "R","T" : "T","Y" : "Y","U" : "U","I" : "I","O" : "O","P" : "P","{" : "{","}" : "}","|" : "|","a" : "a","s" : "s","d" : "d","f" : "f","g" : "g","h" : "h","j" : "j","k" : "k","l" : "l",";" : ";","'" : "'","A" : "A","S" : "S","D" : "D","F" : "F","G" : "G","H" : "H","J" : "J","K" : "K","L" : "L",":" : ":","\"" : "\"","z" : "z","x" : "x","c" : "c","v" : "v","b" : "b","n" : "n","m" : "m","," : ",","." : ".","/" : "/","Z" : "Z","X" : "X","C" : "C","V" : "V","B" : "B","N" : "N","M" : "M","<" : "<",">" : ">","?" : "?"}
// Lunicode.js
// from lunicode.com
// on GitHub: https://github.com/combatwombat/Lunicode.js
// Copyright © 2012 Robert Gerlach - robsite.net
function Lunicode(){this.tools={flip:{init:function(){for(i in this.map)this.map[this.map[i]]=i},encode:function(i){for(var r,t=[],o=0,h=i.length;h>o;o++)r=i.charAt(o),o>0&&("̤"==r||"̗"==r||"̖"==r||"̮"==r)?(r=this.map[i.charAt(o-1)+r],t.pop()):(r=this.map[r],"undefined"==typeof r&&(r=i.charAt(o))),t.push(r);return t.reverse().join("")},decode:function(i){for(var r,t=[],o=0,h=i.length;h>o;o++)r=i.charAt(o),o>0&&("̤"==r||"̗"==r||"̖"==r||"̮"==r)?(r=this.map[i.charAt(o-1)+r],t.pop()):(r=this.map[r],"undefined"==typeof r&&(r=i.charAt(o))),t.push(r);return t.reverse().join("")},map:{a:"ɐ",b:"q",c:"ɔ",d:"p",e:"ǝ",f:"ɟ",g:"ɓ",h:"ɥ",i:"ı",j:"ɾ",k:"ʞ",l:"l",m:"ɯ",n:"u",r:"ɹ",t:"ʇ",v:"ʌ",w:"ʍ",y:"ʎ",A:"∀",B:"ᙠ",C:"Ɔ",D:"ᗡ",E:"Ǝ",F:"Ⅎ",G:"⅁",J:"ſ",K:"⋊",L:"˥",M:"W",P:"Ԁ",Q:"Ό",R:"ᴚ",T:"⊥",U:"∩",V:"Λ",Y:"⅄",1:"⇂",2:"ᄅ",3:"Ɛ",4:"ㄣ",5:"ގ",6:"9",7:"ㄥ","&":"⅋",".":"˙",'"':"„",";":"؛","[":"]","(":")","{":"}","?":"¿","!":"¡","'":",","<":">","‾":"_","¯":"_","‿":"⁀","⁅":"⁆","∴":"∵","\r":"\n","ß":"ᙠ","̈":"̤","ä":"ɐ̤","ö":"o̤","ü":"n̤","Ä":"∀̤","Ö":"O̤","Ü":"∩̤","´":" ̗","é":"ǝ̗","á":"ɐ̗","ó":"o̗","ú":"n̗","É":"Ǝ̗","Á":"∀̗","Ó":"O̗","Ú":"∩̗","`":" ̖","è":"ǝ̖","à":"ɐ̖","ò":"o̖","ù":"n̖","È":"Ǝ̖","À":"∀̖","Ò":"O̖","Ù":"∩̖","^":" ̮","ê":"ǝ̮","â":"ɐ̮","ô":"o̮","û":"n̮","Ê":"Ǝ̮","Â":"∀̮","Ô":"O̮","Û":"∩̮"}},mirror:{init:function(){for(i in this.map)this.map[this.map[i]]=i},encode:function(i){for(var r,t=[],o=[],h=0,n=i.length;n>h;h++)r=i.charAt(h),h>0&&("̈"==r||"̀"==r||"́"==r||"̂"==r)?(r=this.map[i.charAt(h-1)+r],t.pop()):(r=this.map[r],"undefined"==typeof r&&(r=i.charAt(h))),"\n"==r?(o.push(t.reverse().join("")),t=[]):t.push(r);return o.push(t.reverse().join("")),o.join("\n")},decode:function(i){for(var r,t=[],o=[],h=0,n=i.length;n>h;h++)r=i.charAt(h),h>0&&("̈"==r||"̀"==r||"́"==r||"̂"==r)?(r=this.map[i.charAt(h-1)+r],t.pop()):(r=this.map[r],"undefined"==typeof r&&(r=i.charAt(h))),"\n"==r?(o.push(t.reverse().join("")),t=[]):t.push(r);return o.push(t.reverse().join("")),o.join("\n")},map:{a:"ɒ",b:"d",c:"ɔ",e:"ɘ",f:"Ꮈ",g:"ǫ",h:"ʜ",j:"ꞁ",k:"ʞ",l:"|",n:"ᴎ",p:"q",r:"ɿ",s:"ꙅ",t:"ƚ",y:"ʏ",z:"ƹ",B:"ᙠ",C:"Ɔ",D:"ᗡ",E:"Ǝ",F:"ꟻ",G:"Ꭾ",J:"Ⴑ",K:"⋊",L:"⅃",N:"Ͷ",P:"ꟼ",Q:"Ọ",R:"Я",S:"Ꙅ",Z:"Ƹ",1:"",2:"",3:"",4:"",5:"",6:"",7:"","&":"",";":"","[":"]","(":")","{":"}","?":"⸮","<":">","ä":"ɒ̈","ß":"ᙠ","´":"`","é":"ɘ̀","á":"ɒ̀","ó":"ò","ú":"ù","É":"Ǝ̀","Á":"À","Ó":"Ò","Ú":"Ù","`":"´","è":"ɘ́","à":"ɒ́","È":"Ǝ́","ê":"ɘ̂","â":"ɒ̂","Ê":"Ǝ̂","Ø":"ᴓ","ø":"ᴓ"}},creepify:{init:function(){for(var i=768;789>=i;i++)this.diacriticsTop.push(String.fromCharCode(i));for(var i=790;819>=i;i++)794!=i&&795!=i&&this.diacriticsBottom.push(String.fromCharCode(i));this.diacriticsTop.push(String.fromCharCode(794)),this.diacriticsTop.push(String.fromCharCode(795));for(var i=820;824>=i;i++)this.diacriticsMiddle.push(String.fromCharCode(i));for(var i=825;828>=i;i++)this.diacriticsBottom.push(String.fromCharCode(i));for(var i=829;836>=i;i++)this.diacriticsTop.push(String.fromCharCode(i));this.diacriticsTop.push(String.fromCharCode(836)),this.diacriticsBottom.push(String.fromCharCode(837)),this.diacriticsTop.push(String.fromCharCode(838)),this.diacriticsBottom.push(String.fromCharCode(839)),this.diacriticsBottom.push(String.fromCharCode(840)),this.diacriticsBottom.push(String.fromCharCode(841)),this.diacriticsTop.push(String.fromCharCode(842)),this.diacriticsTop.push(String.fromCharCode(843)),this.diacriticsTop.push(String.fromCharCode(844)),this.diacriticsBottom.push(String.fromCharCode(845)),this.diacriticsBottom.push(String.fromCharCode(846)),this.diacriticsTop.push(String.fromCharCode(848)),this.diacriticsTop.push(String.fromCharCode(849)),this.diacriticsTop.push(String.fromCharCode(850)),this.diacriticsBottom.push(String.fromCharCode(851)),this.diacriticsBottom.push(String.fromCharCode(852)),this.diacriticsBottom.push(String.fromCharCode(853)),this.diacriticsBottom.push(String.fromCharCode(854)),this.diacriticsTop.push(String.fromCharCode(855)),this.diacriticsTop.push(String.fromCharCode(856)),this.diacriticsBottom.push(String.fromCharCode(857)),this.diacriticsBottom.push(String.fromCharCode(858)),this.diacriticsTop.push(String.fromCharCode(859)),this.diacriticsBottom.push(String.fromCharCode(860)),this.diacriticsTop.push(String.fromCharCode(861)),this.diacriticsTop.push(String.fromCharCode(861)),this.diacriticsBottom.push(String.fromCharCode(863)),this.diacriticsTop.push(String.fromCharCode(864)),this.diacriticsTop.push(String.fromCharCode(865))},encode:function(r){var t,o="";for(i in r){if(t=r[i],this.options.middle&&(t+=this.diacriticsMiddle[Math.floor(Math.random()*this.diacriticsMiddle.length)]),this.options.top)for(var h=this.diacriticsTop.length-1,n=0,a=this.options.maxHeight-Math.random()*(this.options.randomization/100*this.options.maxHeight);a>n;n++)t+=this.diacriticsTop[Math.floor(Math.random()*h)];if(this.options.bottom)for(var s=this.diacriticsBottom.length-1,n=0,a=this.options.maxHeight-Math.random()*(this.options.randomization/100*this.options.maxHeight);a>n;n++)t+=this.diacriticsBottom[Math.floor(Math.random()*s)];o+=t}return o},decode:function(r){var t,o="";for(i in r)t=r[i].charCodeAt(0),(768>t||t>865)&&(o+=r[i]);return o},diacriticsTop:[],diacriticsMiddle:[],diacriticsBottom:[],options:{top:!0,middle:!0,bottom:!0,maxHeight:15,randomization:100}},bubbles:{init:function(){for(var i=49;57>=i;i++)this.map[String.fromCharCode(i)]=String.fromCharCode(i+9263);this.map[0]="⓪";for(var i=65;90>=i;i++)this.map[String.fromCharCode(i)]=String.fromCharCode(i+9333);for(var i=97;122>=i;i++)this.map[String.fromCharCode(i)]=String.fromCharCode(i+9327);for(i in this.map)this.mapInverse[this.map[i]]=i},encode:function(r){var t,o="",h=!0;for(i in r)t=this.map[r[i]],"undefined"==typeof t&&(r[i].charCodeAt(0)>=33?(t=r[i]+String.fromCharCode(8413),h||(t=String.fromCharCode(8239)+String.fromCharCode(160)+String.fromCharCode(160)+String.fromCharCode(8239)+t)):t=r[i]),o+=t,h="\n"==t;return o},decode:function(r){var t,o="",h="";for(i in r)t=this.mapInverse[r[i]],o+="undefined"==typeof t?r[i]:t;for(i in o)t=o[i].charCodeAt(0),160!=t&&8239!=t&&8413!=t&&(h+=o[i]);return h},map:{},mapInverse:{}},squares:{init:function(){},encode:function(r){var t,o="",h=!0;for(i in r)r[i].charCodeAt(0)>=33?(t=r[i]+String.fromCharCode(8414),h||(t=String.fromCharCode(8239)+String.fromCharCode(160)+String.fromCharCode(160)+String.fromCharCode(8239)+t)):t=r[i],o+=t,h="\n"==t;return o},decode:function(r){var t,o="";for(i in r)t=r[i].charCodeAt(0),160!=t&&8239!=t&&8414!=t&&(o+=r[i]);return o}},roundsquares:{init:function(){},encode:function(r){var t,o="",h=!0;for(i in r)r[i].charCodeAt(0)>=33?(t=r[i]+String.fromCharCode(8419),h||(t=String.fromCharCode(160)+String.fromCharCode(160)+String.fromCharCode(160)+t)):t=r[i],o+=t,h="\n"==t;return o},decode:function(r){var t,o="";for(i in r)t=r[i].charCodeAt(0),160!=t&&8239!=t&&8419!=t&&(o+=r[i]);return o}},bent:{init:function(){for(i in this.map)this.map[this.map[i]]=i},encode:function(i){for(var r,t="",o=0,h=i.length;h>o;o++)r=this.map[i.charAt(o)],"undefined"==typeof r&&(r=i.charAt(o)),t+=r;return t},decode:function(i){for(var r,t="",o=0,h=i.length;h>o;o++)r=this.map[i.charAt(o)],"undefined"==typeof r&&(r=i.charAt(o)),t+=r;return t},map:{a:"ą",b:"ҍ",c:"ç",d:"ժ",e:"ҽ",f:"ƒ",g:"ց",h:"հ",i:"ì",j:"ʝ",k:"ҟ",l:"Ӏ",m:"ʍ",n:"ղ",o:"օ",p:"ք",q:"զ",r:"ɾ",s:"ʂ",t:"է",u:"մ",v:"ѵ",w:"ա",x:"×",y:"վ",z:"Հ",A:"Ⱥ",B:"β",C:"↻",D:"Ꭰ",E:"Ɛ",F:"Ƒ",G:"Ɠ",H:"Ƕ",I:"į",J:"ل",K:"Ҡ",L:"Ꝉ",M:"Ɱ",N:"ហ",O:"ට",P:"φ",Q:"Ҩ",R:"འ",S:"Ϛ",T:"Ͳ",U:"Ա",V:"Ỽ",W:"చ",X:"ჯ",Y:"Ӌ",Z:"ɀ",0:"⊘",1:"������",2:"ϩ",3:"Ӡ",4:"५",5:"Ƽ",6:"Ϭ",7:"7",8:"������",9:"९","&":"⅋","(":"{",")":"}","{":"(","}":")","ä":"ą̈","ö":"օ̈","ü":"մ̈","Ä":"Ⱥ̈","Ö":"ට̈","Ü":"Ա̈","é":"ҽ́","á":"ą́","ó":"օ́","ú":"մ́","É":"Ɛ́","Á":"Ⱥ́","Ó":"ට́","Ú":"Ա́","è":"ҽ̀","à":"ą̀","ò":"օ̀","ù":"մ̀","È":"Ɛ̀","À":"Ⱥ̀","Ò":"ට̀","Ù":"Ա̀","ê":"ҽ̂","â":"ą̂","ô":"օ̂","û":"մ̂","Ê":"Ɛ̂","Â":"Ⱥ̂","Ô":"ට̂","Û":"Ա̂"}},tiny:{init:function(){for(i in this.map)this.map[this.map[i]]=i},encode:function(i){var r,t="";i=i.toUpperCase();for(var o=0,h=i.length;h>o;o++)r=this.map[i.charAt(o)],"undefined"==typeof r&&(r=i.charAt(o)),t+=r;return t},decode:function(i){for(var r,t="",o=0,h=i.length;h>o;o++)r=this.map[i.charAt(o)],"undefined"==typeof r&&(r=i.charAt(o)),t+=r;return t},map:{A:"ᴀ",B:"ʙ",C:"ᴄ",D:"ᴅ",E:"ᴇ",F:"ꜰ",G:"ɢ",H:"ʜ",I:"ɪ",J:"ᴊ",K:"ᴋ",L:"ʟ",M:"ᴍ",N:"ɴ",O:"ᴏ",P:"ᴘ",Q:"Q",R:"ʀ",S:"ꜱ",T:"ᴛ",U:"ᴜ",V:"ᴠ",W:"ᴡ",X:"x",Y:"ʏ",Z:"ᴢ"}}};for(i in this.tools)this.tools[i].init();this.getHTML=function(i){for(var r,t="",o=!0,h=0,n=0,a=0,s=i.length;s>a;a++)r=i.charCodeAt(a),10==r||13==r?(t+="