Compare commits
2 commits
42d5add810
...
885d1f5dc3
Author | SHA1 | Date | |
---|---|---|---|
885d1f5dc3 | |||
0abb22130f |
9 changed files with 591 additions and 241 deletions
25
common/package-lock.json
generated
25
common/package-lock.json
generated
|
@ -19,9 +19,11 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^5.0.0",
|
"@types/chai": "^5.0.0",
|
||||||
|
"@types/chai-spies": "^1.0.6",
|
||||||
"@types/mocha": "^10.0.8",
|
"@types/mocha": "^10.0.8",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"chai-as-promised": "^8.0.0",
|
"chai-as-promised": "^8.0.0",
|
||||||
|
"chai-spies": "^1.1.0",
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"mocha": "^10.7.3"
|
"mocha": "^10.7.3"
|
||||||
}
|
}
|
||||||
|
@ -2199,6 +2201,16 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chai-spies": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-xkk4HmhBB9OQeTAifa9MJ+6R5/Rq9+ungDe4JidZD+vqZVeiWZwc2i7/pd1ZKjyGlSBIQePoWdyUyFUGT0rv5w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/chai": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||||
|
@ -2514,6 +2526,19 @@
|
||||||
"chai": ">= 2.1.2 < 6"
|
"chai": ">= 2.1.2 < 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chai-spies": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ikaUhQvQWchRYj2K54itFp3nrcxaFRpSDQxDlRzSn9aWgu9Pi7lD8yFxTso4WnQ39+WZ69oB/qOvqp+isJIIWA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chai": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
|
|
@ -24,9 +24,11 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^5.0.0",
|
"@types/chai": "^5.0.0",
|
||||||
|
"@types/chai-spies": "^1.0.6",
|
||||||
"@types/mocha": "^10.0.8",
|
"@types/mocha": "^10.0.8",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"chai-as-promised": "^8.0.0",
|
"chai-as-promised": "^8.0.0",
|
||||||
|
"chai-spies": "^1.1.0",
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"mocha": "^10.7.3"
|
"mocha": "^10.7.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,8 @@ export class BaseEventEmitter {
|
||||||
if(eventType.includes(" ")) { // Unlisten to several different events with the same listener.
|
if(eventType.includes(" ")) { // Unlisten to several different events with the same listener.
|
||||||
let found = false
|
let found = false
|
||||||
for(const type of eventType.split(" "))
|
for(const type of eventType.split(" "))
|
||||||
found ||= this.off(eventType, eventListener)
|
found ||= this.off(type, eventListener)
|
||||||
|
return found
|
||||||
} else {
|
} else {
|
||||||
if(!this.constructor.emits.includes(eventType)) {
|
if(!this.constructor.emits.includes(eventType)) {
|
||||||
const className = this.constructor.name
|
const className = this.constructor.name
|
||||||
|
|
|
@ -137,9 +137,9 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
parif(elem, contents) {
|
parif(elem, contents) {
|
||||||
elem = elem.toString()
|
elem = elem.toString()
|
||||||
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
if(elem[0] !== "(" && elem.at(-1) !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
||||||
return this.par(elem)
|
return this.par(elem)
|
||||||
if(elem[0] === "(" && elem[elem.length - 1] === ")")
|
if(elem[0] === "(" && elem.at(-1) === ")")
|
||||||
return elem.removeEnclosure()
|
return elem.removeEnclosure()
|
||||||
return elem
|
return elem
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,16 @@
|
||||||
* Replaces latin characters with their uppercase versions.
|
* Replaces latin characters with their uppercase versions.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
String.prototype.toLatinUppercase = String.prototype.toLatinUppercase || function() {
|
String.prototype.toLatinUppercase = function() {
|
||||||
return this.replace(/[a-z]/g, function(match) {
|
return this.replace(/[a-z]/g, function(match) {
|
||||||
return match.toUpperCase()
|
return match.toUpperCase()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the 'enclosers' of a string (e.g. quotes, parentheses, brackets...)
|
* Removes the first and last character of a string
|
||||||
|
* Used to remove enclosing characters like quotes, parentheses, brackets...
|
||||||
|
* @note Does NOT check for their existence ahead of time.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
String.prototype.removeEnclosure = function() {
|
String.prototype.removeEnclosure = function() {
|
||||||
|
@ -43,126 +45,126 @@ String.prototype.removeEnclosure = function() {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
Number.prototype.toDecimalPrecision = function(decimalPlaces = 0) {
|
Number.prototype.toDecimalPrecision = function(decimalPlaces = 0) {
|
||||||
const p = Math.pow(10, decimalPlaces);
|
const p = Math.pow(10, decimalPlaces)
|
||||||
const n = (this * p) * (1 + Number.EPSILON);
|
const n = (this * p) * (1 + Number.EPSILON)
|
||||||
return Math.round(n) / p;
|
return Math.round(n) / p
|
||||||
}
|
}
|
||||||
|
|
||||||
const powerpos = {
|
const CHARACTER_TO_POWER = new Map([
|
||||||
"-": "⁻",
|
["-", "⁻"],
|
||||||
"+": "⁺",
|
["+", "⁺"],
|
||||||
"=": "⁼",
|
["=", "⁼"],
|
||||||
" ": " ",
|
[" ", " "],
|
||||||
"(": "⁽",
|
["(", "⁽"],
|
||||||
")": "⁾",
|
[")", "⁾"],
|
||||||
"0": "⁰",
|
["0", "⁰"],
|
||||||
"1": "¹",
|
["1", "¹"],
|
||||||
"2": "²",
|
["2", "²"],
|
||||||
"3": "³",
|
["3", "³"],
|
||||||
"4": "⁴",
|
["4", "⁴"],
|
||||||
"5": "⁵",
|
["5", "⁵"],
|
||||||
"6": "⁶",
|
["6", "⁶"],
|
||||||
"7": "⁷",
|
["7", "⁷"],
|
||||||
"8": "⁸",
|
["8", "⁸"],
|
||||||
"9": "⁹",
|
["9", "⁹"],
|
||||||
"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", "ᵖ"],
|
||||||
"r": "ʳ",
|
["r", "ʳ"],
|
||||||
"s": "ˢ",
|
["s", "ˢ"],
|
||||||
"t": "ᵗ",
|
["t", "ᵗ"],
|
||||||
"u": "ᵘ",
|
["u", "ᵘ"],
|
||||||
"v": "ᵛ",
|
["v", "ᵛ"],
|
||||||
"w": "ʷ",
|
["w", "ʷ"],
|
||||||
"x": "ˣ",
|
["x", "ˣ"],
|
||||||
"y": "ʸ",
|
["y", "ʸ"],
|
||||||
"z": "ᶻ"
|
["z", "ᶻ"]
|
||||||
}
|
])
|
||||||
|
|
||||||
const exponents = [
|
const CHARACTER_TO_INDICE = new Map([
|
||||||
"⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹"
|
["-", "₋"],
|
||||||
|
["+", "₊"],
|
||||||
|
["=", "₌"],
|
||||||
|
["(", "₍"],
|
||||||
|
[")", "₎"],
|
||||||
|
[" ", " "],
|
||||||
|
["0", "₀"],
|
||||||
|
["1", "₁"],
|
||||||
|
["2", "₂"],
|
||||||
|
["3", "₃"],
|
||||||
|
["4", "₄"],
|
||||||
|
["5", "₅"],
|
||||||
|
["6", "₆"],
|
||||||
|
["7", "₇"],
|
||||||
|
["8", "₈"],
|
||||||
|
["9", "₉"],
|
||||||
|
["a", "ₐ"],
|
||||||
|
["e", "ₑ"],
|
||||||
|
["h", "ₕ"],
|
||||||
|
["i", "ᵢ"],
|
||||||
|
["j", "ⱼ"],
|
||||||
|
["k", "ₖ"],
|
||||||
|
["l", "ₗ"],
|
||||||
|
["m", "ₘ"],
|
||||||
|
["n", "ₙ"],
|
||||||
|
["o", "ₒ"],
|
||||||
|
["p", "ₚ"],
|
||||||
|
["r", "ᵣ"],
|
||||||
|
["s", "ₛ"],
|
||||||
|
["t", "ₜ"],
|
||||||
|
["u", "ᵤ"],
|
||||||
|
["v", "ᵥ"],
|
||||||
|
["x", "ₓ"]
|
||||||
|
])
|
||||||
|
|
||||||
|
const EXPONENTS = [
|
||||||
|
"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"
|
||||||
]
|
]
|
||||||
|
|
||||||
const exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g')
|
const EXPONENTS_REG = new RegExp("([" + EXPONENTS.join("") + "]+)", "g")
|
||||||
|
|
||||||
const indicepos = {
|
/**
|
||||||
"-": "₋",
|
* Put a text in sup position
|
||||||
"+": "₊",
|
* @param {string} text
|
||||||
"=": "₌",
|
* @return {string}
|
||||||
"(": "₍",
|
*/
|
||||||
")": "₎",
|
|
||||||
" ": " ",
|
|
||||||
"0": "₀",
|
|
||||||
"1": "₁",
|
|
||||||
"2": "₂",
|
|
||||||
"3": "₃",
|
|
||||||
"4": "₄",
|
|
||||||
"5": "₅",
|
|
||||||
"6": "₆",
|
|
||||||
"7": "₇",
|
|
||||||
"8": "₈",
|
|
||||||
"9": "₉",
|
|
||||||
"a": "ₐ",
|
|
||||||
"e": "ₑ",
|
|
||||||
"h": "ₕ",
|
|
||||||
"i": "ᵢ",
|
|
||||||
"j": "ⱼ",
|
|
||||||
"k": "ₖ",
|
|
||||||
"l": "ₗ",
|
|
||||||
"m": "ₘ",
|
|
||||||
"n": "ₙ",
|
|
||||||
"o": "ₒ",
|
|
||||||
"p": "ₚ",
|
|
||||||
"r": "ᵣ",
|
|
||||||
"s": "ₛ",
|
|
||||||
"t": "ₜ",
|
|
||||||
"u": "ᵤ",
|
|
||||||
"v": "ᵥ",
|
|
||||||
"x": "ₓ",
|
|
||||||
}
|
|
||||||
// Put a text in sup position
|
|
||||||
export function textsup(text) {
|
export function textsup(text) {
|
||||||
let ret = ""
|
let ret = ""
|
||||||
text = text.toString()
|
text = text.toString()
|
||||||
for (let i = 0; i < text.length; i++) {
|
for(let letter of text)
|
||||||
if(Object.keys(powerpos).indexOf(text[i]) >= 0) {
|
ret += CHARACTER_TO_POWER.has(letter) ? CHARACTER_TO_POWER.get(letter) : letter
|
||||||
ret += powerpos[text[i]]
|
|
||||||
} else {
|
|
||||||
ret += text[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put a text in sub position
|
/**
|
||||||
|
* Put a text in sub position
|
||||||
|
* @param {string} text
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
export function textsub(text) {
|
export function textsub(text) {
|
||||||
let ret = ""
|
let ret = ""
|
||||||
text = text.toString()
|
text = text.toString()
|
||||||
for (let i = 0; i < text.length; i++) {
|
for(let letter of text)
|
||||||
if(Object.keys(indicepos).indexOf(text[i]) >= 0) {
|
ret += CHARACTER_TO_INDICE.has(letter) ? CHARACTER_TO_INDICE.get(letter) : letter
|
||||||
ret += indicepos[text[i]]
|
|
||||||
} else {
|
|
||||||
ret += text[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplifies (mathematically) a mathematical expression.
|
* Simplifies (mathematically) a mathematical expression.
|
||||||
|
* @deprecated
|
||||||
* @param {string} str - Expression to parse
|
* @param {string} str - Expression to parse
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
|
@ -185,37 +187,43 @@ export function simplifyExpression(str) {
|
||||||
// n1 & n3 are multiplied, opeM is the main operation (- or +).
|
// n1 & n3 are multiplied, opeM is the main operation (- or +).
|
||||||
// Putting all n in form of number
|
// Putting all n in form of number
|
||||||
//n2 = n2 == undefined ? 1 : parseFloat(n)
|
//n2 = n2 == undefined ? 1 : parseFloat(n)
|
||||||
n1 = m1 === undefined ? 1 : eval(m1 + '1')
|
n1 = m1 === undefined ? 1 : eval(m1 + "1")
|
||||||
n2 = m2 === undefined ? 1 : eval('1' + m2)
|
n2 = m2 === undefined ? 1 : eval("1" + m2)
|
||||||
n3 = m3 === undefined ? 1 : eval(m3 + '1')
|
n3 = m3 === undefined ? 1 : eval(m3 + "1")
|
||||||
n4 = m4 === undefined ? 1 : eval('1' + m4)
|
n4 = m4 === undefined ? 1 : eval("1" + m4)
|
||||||
//let [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n))
|
//let [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n))
|
||||||
// Falling back to * in case it does not exist (the corresponding n would be 1)
|
// Falling back to * in case it does not exist (the corresponding n would be 1)
|
||||||
[ope2, ope4] = [ope2, ope4].map(ope => ope === '/' ? '/' : '*')
|
[ope2, ope4] = [ope2, ope4].map(ope => ope === "/" ? "/" : "*")
|
||||||
let coeff1 = n1*n2
|
let coeff1 = n1 * n2
|
||||||
let coeff2 = n3*n4
|
let coeff2 = n3 * n4
|
||||||
let coefficient = coeff1+coeff2-(opeM === '-' ? 2*coeff2 : 0)
|
let coefficient = coeff1 + coeff2 - (opeM === "-" ? 2 * coeff2 : 0)
|
||||||
|
|
||||||
return `${coefficient} * π`
|
return `${coefficient} * π`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[ // Removing parenthesis when content is only added from both sides.
|
[ // Removing parenthesis when content is only added from both sides.
|
||||||
/(^|[+-] |\()\(([^)(]+)\)($| [+-]|\))/g,
|
/(^|[+-] |\()\(([^)(]+)\)($| [+-]|\))/g,
|
||||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
function(match, b4, middle, after) {
|
||||||
|
return `${b4}${middle}${after}`
|
||||||
|
}
|
||||||
],
|
],
|
||||||
[ // Removing parenthesis when content is only multiplied.
|
[ // Removing parenthesis when content is only multiplied.
|
||||||
/(^|[*\/] |\()\(([^)(+-]+)\)($| [*\/+-]|\))/g,
|
/(^|[*\/] |\()\(([^)(+-]+)\)($| [*\/+-]|\))/g,
|
||||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
function(match, b4, middle, after) {
|
||||||
|
return `${b4}${middle}${after}`
|
||||||
|
}
|
||||||
],
|
],
|
||||||
[ // Removing parenthesis when content is only multiplied.
|
[ // Removing parenthesis when content is only multiplied.
|
||||||
/(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
|
/(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
|
||||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
function(match, b4, middle, after) {
|
||||||
|
return `${b4}${middle}${after}`
|
||||||
|
}
|
||||||
],
|
],
|
||||||
[// Simplification additions/subtractions.
|
[// Simplification additions/subtractions.
|
||||||
/(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g,
|
/(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g,
|
||||||
function(match, b4, n1, op1, middle, op2, n2, after) {
|
function(match, b4, n1, op1, middle, op2, n2, after) {
|
||||||
let total
|
let total
|
||||||
if(op2 === '+') {
|
if(op2 === "+") {
|
||||||
total = parseFloat(n1) + parseFloat(n2)
|
total = parseFloat(n1) + parseFloat(n2)
|
||||||
} else {
|
} else {
|
||||||
total = parseFloat(n1) - parseFloat(n2)
|
total = parseFloat(n1) - parseFloat(n2)
|
||||||
|
@ -226,12 +234,12 @@ export function simplifyExpression(str) {
|
||||||
[// Simplification multiplications/divisions.
|
[// Simplification multiplications/divisions.
|
||||||
/([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
|
/([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
|
||||||
function(match, n1, op1, middle, op2, n2) {
|
function(match, n1, op1, middle, op2, n2) {
|
||||||
if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === '/' &&
|
if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === "/" &&
|
||||||
(parseInt(n1) / parseInt(n2)) % 1 !== 0) {
|
(parseInt(n1) / parseInt(n2)) % 1 !== 0) {
|
||||||
// Non int result for int division.
|
// Non int result for int division.
|
||||||
return `(${n1} / ${n2}) ${op1} ${middle}`
|
return `(${n1} / ${n2}) ${op1} ${middle}`
|
||||||
} else {
|
} else {
|
||||||
if(op2 === '*') {
|
if(op2 === "*") {
|
||||||
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
|
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
|
||||||
} else {
|
} else {
|
||||||
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
|
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
|
||||||
|
@ -245,17 +253,17 @@ export function simplifyExpression(str) {
|
||||||
let str = middle
|
let str = middle
|
||||||
// Replace all groups
|
// Replace all groups
|
||||||
while(/\([^)(]+\)/g.test(str))
|
while(/\([^)(]+\)/g.test(str))
|
||||||
str = str.replace(/\([^)(]+\)/g, '')
|
str = str.replace(/\([^)(]+\)/g, "")
|
||||||
// There shouldn't be any more parenthesis
|
// There shouldn't be any more parenthesis
|
||||||
// If there is, that means the 2 parenthesis are needed.
|
// If there is, that means the 2 parenthesis are needed.
|
||||||
if(!str.includes(')') && !str.includes('(')) {
|
if(!str.includes(")") && !str.includes("(")) {
|
||||||
return middle
|
return middle
|
||||||
} else {
|
} else {
|
||||||
return `(${middle})`
|
return `(${middle})`
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
// Simple simplifications
|
// Simple simplifications
|
||||||
// [/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'],
|
// [/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'],
|
||||||
// [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
|
// [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
|
||||||
|
@ -286,24 +294,35 @@ export function simplifyExpression(str) {
|
||||||
/**
|
/**
|
||||||
* Transforms a mathematical expression to make it readable by humans.
|
* Transforms a mathematical expression to make it readable by humans.
|
||||||
* NOTE: Will break parsing of expression.
|
* NOTE: Will break parsing of expression.
|
||||||
|
* @deprecated
|
||||||
* @param {string} str - Expression to parse.
|
* @param {string} str - Expression to parse.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function makeExpressionReadable(str) {
|
export function makeExpressionReadable(str) {
|
||||||
let replacements = [
|
let replacements = [
|
||||||
// letiables
|
// letiables
|
||||||
[/pi/g, 'π'],
|
[/pi/g, "π"],
|
||||||
[/Infinity/g, '∞'],
|
[/Infinity/g, "∞"],
|
||||||
[/inf/g, '∞'],
|
[/inf/g, "∞"],
|
||||||
// Other
|
// Other
|
||||||
[/ \* /g, '×'],
|
[/ \* /g, "×"],
|
||||||
[/ \^ /g, '^'],
|
[/ \^ /g, "^"],
|
||||||
[/\^\(([\d\w+-]+)\)/g, function(match, p1) { return textsup(p1) }],
|
[/\^\(([\d\w+-]+)\)/g, function(match, p1) {
|
||||||
[/\^([\d\w+-]+)/g, function(match, p1) { return textsup(p1) }],
|
return textsup(p1)
|
||||||
[/_\(([\d\w+-]+)\)/g, function(match, p1) { return textsub(p1) }],
|
}],
|
||||||
[/_([\d\w+-]+)/g, function(match, p1) { return textsub(p1) }],
|
[/\^([\d\w+-]+)/g, function(match, p1) {
|
||||||
[/\[([^\[\]]+)\]/g, function(match, p1) { return textsub(p1) }],
|
return textsup(p1)
|
||||||
[/(\d|\))×/g, '$1'],
|
}],
|
||||||
|
[/_\(([\d\w+-]+)\)/g, function(match, p1) {
|
||||||
|
return textsub(p1)
|
||||||
|
}],
|
||||||
|
[/_([\d\w+-]+)/g, function(match, p1) {
|
||||||
|
return textsub(p1)
|
||||||
|
}],
|
||||||
|
[/\[([^\[\]]+)\]/g, function(match, p1) {
|
||||||
|
return textsub(p1)
|
||||||
|
}],
|
||||||
|
[/(\d|\))×/g, "$1"],
|
||||||
[/integral\((.+),\s?(.+),\s?["'](.+)["'],\s?["'](.+)["']\)/g, function(match, a, b, p1, body, p2, p3, by, p4) {
|
[/integral\((.+),\s?(.+),\s?["'](.+)["'],\s?["'](.+)["']\)/g, function(match, a, b, p1, body, p2, p3, by, p4) {
|
||||||
if(a.length < b.length) {
|
if(a.length < b.length) {
|
||||||
return `∫${textsub(a)}${textsup(b)} ${body} d${by}`
|
return `∫${textsub(a)}${textsup(b)} ${body} d${by}`
|
||||||
|
@ -312,7 +331,7 @@ export function makeExpressionReadable(str) {
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
[/derivative\(?["'](.+)["'], ?["'](.+)["'], ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) {
|
[/derivative\(?["'](.+)["'], ?["'](.+)["'], ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) {
|
||||||
return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx`
|
return `d(${body.replace(new RegExp(by, "g"), "x")})/dx`
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -324,6 +343,48 @@ export function makeExpressionReadable(str) {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {[RegExp, string][]} */
|
||||||
|
const replacements = [
|
||||||
|
// Greek letters
|
||||||
|
[/(\W|^)al(pha)?(\W|$)/g, "$1α$3"],
|
||||||
|
[/(\W|^)be(ta)?(\W|$)/g, "$1β$3"],
|
||||||
|
[/(\W|^)ga(mma)?(\W|$)/g, "$1γ$3"],
|
||||||
|
[/(\W|^)de(lta)?(\W|$)/g, "$1δ$3"],
|
||||||
|
[/(\W|^)ep(silon)?(\W|$)/g, "$1ε$3"],
|
||||||
|
[/(\W|^)ze(ta)?(\W|$)/g, "$1ζ$3"],
|
||||||
|
[/(\W|^)et(a)?(\W|$)/g, "$1η$3"],
|
||||||
|
[/(\W|^)th(eta)?(\W|$)/g, "$1θ$3"],
|
||||||
|
[/(\W|^)io(ta)?(\W|$)/g, "$1ι$3"],
|
||||||
|
[/(\W|^)ka(ppa)?(\W|$)/g, "$1κ$3"],
|
||||||
|
[/(\W|^)la(mbda)?(\W|$)/g, "$1λ$3"],
|
||||||
|
[/(\W|^)mu(\W|$)/g, "$1μ$2"],
|
||||||
|
[/(\W|^)nu(\W|$)/g, "$1ν$2"],
|
||||||
|
[/(\W|^)xi(\W|$)/g, "$1ξ$2"],
|
||||||
|
[/(\W|^)rh(o)?(\W|$)/g, "$1ρ$3"],
|
||||||
|
[/(\W|^)si(gma)?(\W|$)/g, "$1σ$3"],
|
||||||
|
[/(\W|^)ta(u)?(\W|$)/g, "$1τ$3"],
|
||||||
|
[/(\W|^)up(silon)?(\W|$)/g, "$1υ$3"],
|
||||||
|
[/(\W|^)ph(i)?(\W|$)/g, "$1φ$3"],
|
||||||
|
[/(\W|^)ch(i)?(\W|$)/g, "$1χ$3"],
|
||||||
|
[/(\W|^)ps(i)?(\W|$)/g, "$1ψ$3"],
|
||||||
|
[/(\W|^)om(ega)?(\W|$)/g, "$1ω$3"],
|
||||||
|
// Capital greek letters
|
||||||
|
[/(\W|^)gga(mma)?(\W|$)/g, "$1Γ$3"],
|
||||||
|
[/(\W|^)gde(lta)?(\W|$)/g, "$1Δ$3"],
|
||||||
|
[/(\W|^)gth(eta)?(\W|$)/g, "$1Θ$3"],
|
||||||
|
[/(\W|^)gla(mbda)?(\W|$)/g, "$1Λ$3"],
|
||||||
|
[/(\W|^)gxi(\W|$)/g, "$1Ξ$2"],
|
||||||
|
[/(\W|^)gpi(\W|$)/g, "$1Π$2"],
|
||||||
|
[/(\W|^)gsi(gma)?(\W|$)/g, "$1Σ$3"],
|
||||||
|
[/(\W|^)gph(i)?(\W|$)/g, "$1Φ$3"],
|
||||||
|
[/(\W|^)gps(i)?(\W|$)/g, "$1Ψ$3"],
|
||||||
|
[/(\W|^)gom(ega)?(\W|$)/g, "$1Ω$3"],
|
||||||
|
// Array elements
|
||||||
|
[/\[([^\]\[]+)\]/g, function(match, p1) {
|
||||||
|
return textsub(p1)
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a variable name to make it readable by humans.
|
* Parses a variable name to make it readable by humans.
|
||||||
*
|
*
|
||||||
|
@ -332,65 +393,24 @@ export function makeExpressionReadable(str) {
|
||||||
* @returns {string} - The parsed name
|
* @returns {string} - The parsed name
|
||||||
*/
|
*/
|
||||||
export function parseName(str, removeUnallowed = true) {
|
export function parseName(str, removeUnallowed = true) {
|
||||||
let replacements = [
|
for(const replacement of replacements)
|
||||||
// Greek letters
|
|
||||||
[/([^a-z]|^)al(pha)?([^a-z]|$)/g, '$1α$3'],
|
|
||||||
[/([^a-z]|^)be(ta)?([^a-z]|$)/g, '$1β$3'],
|
|
||||||
[/([^a-z]|^)ga(mma)?([^a-z]|$)/g, '$1γ$3'],
|
|
||||||
[/([^a-z]|^)de(lta)?([^a-z]|$)/g, '$1δ$3'],
|
|
||||||
[/([^a-z]|^)ep(silon)?([^a-z]|$)/g, '$1ε$3'],
|
|
||||||
[/([^a-z]|^)ze(ta)?([^a-z]|$)/g, '$1ζ$3'],
|
|
||||||
[/([^a-z]|^)et(a)?([^a-z]|$)/g, '$1η$3'],
|
|
||||||
[/([^a-z]|^)th(eta)?([^a-z]|$)/g, '$1θ$3'],
|
|
||||||
[/([^a-z]|^)io(ta)?([^a-z]|$)/g, '$1ι$3'],
|
|
||||||
[/([^a-z]|^)ka(ppa)([^a-z]|$)?/g, '$1κ$3'],
|
|
||||||
[/([^a-z]|^)la(mbda)?([^a-z]|$)/g, '$1λ$3'],
|
|
||||||
[/([^a-z]|^)mu([^a-z]|$)/g, '$1μ$2'],
|
|
||||||
[/([^a-z]|^)nu([^a-z]|$)/g, '$1ν$2'],
|
|
||||||
[/([^a-z]|^)xi([^a-z]|$)/g, '$1ξ$2'],
|
|
||||||
[/([^a-z]|^)rh(o)?([^a-z]|$)/g, '$1ρ$3'],
|
|
||||||
[/([^a-z]|^)si(gma)?([^a-z]|$)/g, '$1σ$3'],
|
|
||||||
[/([^a-z]|^)ta(u)?([^a-z]|$)/g, '$1τ$3'],
|
|
||||||
[/([^a-z]|^)up(silon)?([^a-z]|$)/g, '$1υ$3'],
|
|
||||||
[/([^a-z]|^)ph(i)?([^a-z]|$)/g, '$1φ$3'],
|
|
||||||
[/([^a-z]|^)ch(i)?([^a-z]|$)/g, '$1χ$3'],
|
|
||||||
[/([^a-z]|^)ps(i)?([^a-z]|$)/g, '$1ψ$3'],
|
|
||||||
[/([^a-z]|^)om(ega)?([^a-z]|$)/g, '$1ω$3'],
|
|
||||||
// Capital greek letters
|
|
||||||
[/([^a-z]|^)gga(mma)?([^a-z]|$)/g, '$1Γ$3'],
|
|
||||||
[/([^a-z]|^)gde(lta)?([^a-z]|$)/g, '$1Δ$3'],
|
|
||||||
[/([^a-z]|^)gth(eta)?([^a-z]|$)/g, '$1Θ$3'],
|
|
||||||
[/([^a-z]|^)gla(mbda)?([^a-z]|$)/g, '$1Λ$3'],
|
|
||||||
[/([^a-z]|^)gxi([^a-z]|$)/g, '$1Ξ$2'],
|
|
||||||
[/([^a-z]|^)gpi([^a-z]|$)/g, '$1Π$2'],
|
|
||||||
[/([^a-z]|^)gsi(gma)([^a-z]|$)?/g, '$1Σ$3'],
|
|
||||||
[/([^a-z]|^)gph(i)?([^a-z]|$)/g, '$1Φ$3'],
|
|
||||||
[/([^a-z]|^)gps(i)?([^a-z]|$)/g, '$1Ψ$3'],
|
|
||||||
[/([^a-z]|^)gom(ega)?([^a-z]|$)/g, '$1Ω$3'],
|
|
||||||
// Underscores
|
|
||||||
// [/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }],
|
|
||||||
// [/_([^" ]+)/g, function(match, p1) { return textsub(p1) }],
|
|
||||||
// Array elements
|
|
||||||
[/\[([^\]\[]+)\]/g, function(match, p1) { return textsub(p1) }],
|
|
||||||
// Removing
|
|
||||||
[/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , ''],
|
|
||||||
]
|
|
||||||
if(!removeUnallowed) replacements.pop()
|
|
||||||
// Replacements
|
|
||||||
for(let replacement of replacements)
|
|
||||||
str = str.replace(replacement[0], replacement[1])
|
str = str.replace(replacement[0], replacement[1])
|
||||||
|
if(removeUnallowed)
|
||||||
|
str = str.replace(/[xnπℝℕ\\∪∩\]\[ ()^/÷*×+=\d¹²³⁴⁵⁶⁷⁸⁹⁰-]/g, "")
|
||||||
|
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms camel case strings to a space separated one.
|
* Transforms camel case strings to a space separated one.
|
||||||
*
|
*
|
||||||
|
* @deprecated
|
||||||
* @param {string} label - Camel case to parse
|
* @param {string} label - Camel case to parse
|
||||||
* @returns {string} Parsed label.
|
* @returns {string} Parsed label.
|
||||||
*/
|
*/
|
||||||
export function camelCase2readable(label) {
|
export function camelCase2readable(label) {
|
||||||
let parsed = parseName(label, false)
|
let parsed = parseName(label, false)
|
||||||
return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1")
|
return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g, " $1")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -398,12 +418,12 @@ export function camelCase2readable(label) {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function getRandomColor() {
|
export function getRandomColor() {
|
||||||
let clrs = '0123456789ABCDEF';
|
let clrs = "0123456789ABCDEF"
|
||||||
let color = '#';
|
let color = "#"
|
||||||
for(let i = 0; i < 6; i++) {
|
for(let i = 0; i < 6; i++) {
|
||||||
color += clrs[Math.floor(Math.random() * (16-5*(i%2===0)))];
|
color += clrs[Math.floor(Math.random() * (16 - 5 * (i % 2 === 0)))]
|
||||||
}
|
}
|
||||||
return color;
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -412,16 +432,15 @@ export function getRandomColor() {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function escapeHTML(str) {
|
export function escapeHTML(str) {
|
||||||
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') ;
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses exponents and replaces them with expression values
|
* Parses exponents and replaces them with expression values
|
||||||
* @param {string} expression - The expression to replace in.
|
* @param {string} expression - The expression to replace in.
|
||||||
* @return {string} The parsed expression
|
* @return {string} The parsed expression
|
||||||
*/
|
*/
|
||||||
export function exponentsToExpression(expression) {
|
export function exponentsToExpression(expression) {
|
||||||
return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join(''))
|
return expression.replace(EXPONENTS_REG, (m, exp) => "^" + exp.split("").map((x) => EXPONENTS.indexOf(x)).join(""))
|
||||||
}
|
}
|
||||||
|
|
122
common/test/general/events.mjs
Normal file
122
common/test/general/events.mjs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/**
|
||||||
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseEventEmitter, BaseEvent } from "../../src/events.mjs"
|
||||||
|
|
||||||
|
import { describe, it } from "mocha"
|
||||||
|
import { expect, use } from "chai"
|
||||||
|
import spies from "chai-spies"
|
||||||
|
|
||||||
|
// Setting up modules
|
||||||
|
const { spy } = use(spies)
|
||||||
|
|
||||||
|
|
||||||
|
class MockEmitter extends BaseEventEmitter {
|
||||||
|
static emits = ["example1", "example2"]
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockEvent1 extends BaseEvent {
|
||||||
|
constructor() {
|
||||||
|
super("example1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockEvent2 extends BaseEvent {
|
||||||
|
constructor(parameter) {
|
||||||
|
super("example2")
|
||||||
|
this.parameter = parameter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sandbox = spy.sandbox()
|
||||||
|
|
||||||
|
describe("EventsEmitters", function() {
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
sandbox.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should forward events to all of its listeners", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
const listener1 = spy()
|
||||||
|
const listener2 = spy()
|
||||||
|
emitter.on("example1", listener1)
|
||||||
|
emitter.on("example1", listener2)
|
||||||
|
emitter.emit(new MockEvent1())
|
||||||
|
expect(listener1).to.have.been.called.once
|
||||||
|
expect(listener2).to.have.been.called.once
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should forward multiple events to a singular listener", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
const listener = spy()
|
||||||
|
const mockEvent1 = new MockEvent1()
|
||||||
|
const mockEvent2 = new MockEvent2(3)
|
||||||
|
emitter.on("example1 example2", listener)
|
||||||
|
emitter.emit(mockEvent1)
|
||||||
|
emitter.emit(mockEvent2)
|
||||||
|
expect(listener).to.have.been.called.twice
|
||||||
|
expect(listener).to.also.have.been.called.with.exactly(mockEvent1)
|
||||||
|
expect(listener).to.also.have.been.called.with.exactly(mockEvent2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to have listeners remvoed", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
const listener = spy()
|
||||||
|
emitter.on("example1", listener)
|
||||||
|
const removedFromEventItDoesntListenTo = emitter.off("example2", listener)
|
||||||
|
const removedFromEventItListensTo = emitter.off("example1", listener)
|
||||||
|
const removedFromEventASecondTime = emitter.off("example1", listener)
|
||||||
|
expect(removedFromEventItDoesntListenTo).to.be.false
|
||||||
|
expect(removedFromEventItListensTo).to.be.true
|
||||||
|
expect(removedFromEventASecondTime).to.be.false
|
||||||
|
emitter.on("example1 example2", listener)
|
||||||
|
const removedFromBothEvents = emitter.off("example1 example2", listener)
|
||||||
|
expect(removedFromBothEvents).to.be.true
|
||||||
|
emitter.on("example1", listener)
|
||||||
|
const removedFromOneOfTheEvents = emitter.off("example1 example2", listener)
|
||||||
|
expect(removedFromOneOfTheEvents).to.be.true
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to remove one listener listening to one event when said listener listens to multiple", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
const listener = spy()
|
||||||
|
const mockEvent1 = new MockEvent1()
|
||||||
|
const mockEvent2 = new MockEvent2(3)
|
||||||
|
emitter.on("example1 example2", listener)
|
||||||
|
// Disable listener for example1
|
||||||
|
emitter.off("example1", listener)
|
||||||
|
emitter.emit(mockEvent1)
|
||||||
|
emitter.emit(mockEvent2)
|
||||||
|
expect(listener).to.have.been.called.once
|
||||||
|
expect(listener).to.also.have.been.called.with.exactly(mockEvent2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't be able to add listen/unlisten to, or emit inexistant events", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
const listener = spy()
|
||||||
|
expect(() => emitter.on("inexistant", listener)).to.throw(Error)
|
||||||
|
expect(() => emitter.off("inexistant", listener)).to.throw(Error)
|
||||||
|
expect(() => emitter.emit(new BaseEvent("inexistant"))).to.throw(Error)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't be able to emit non-events", function() {
|
||||||
|
const emitter = new MockEmitter()
|
||||||
|
expect(() => emitter.emit("not-an-event")).to.throw(Error)
|
||||||
|
})
|
||||||
|
})
|
183
common/test/general/utils.mjs
Normal file
183
common/test/general/utils.mjs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { textsup, textsub, parseName, getRandomColor, escapeHTML, exponentsToExpression } from "../../src/utils.mjs"
|
||||||
|
|
||||||
|
|
||||||
|
import { describe, it } from "mocha"
|
||||||
|
import { expect } from "chai"
|
||||||
|
|
||||||
|
describe("Extensions", function() {
|
||||||
|
describe("#String.toLatinUppercase", function() {
|
||||||
|
it("should be able to transform latin characters from strings to their uppercase version", function() {
|
||||||
|
expect("abc".toLatinUppercase()).to.equal("ABC")
|
||||||
|
expect("abCd".toLatinUppercase()).to.equal("ABCD")
|
||||||
|
expect("ab123cd456".toLatinUppercase()).to.equal("AB123CD456")
|
||||||
|
expect("ABC".toLatinUppercase()).to.equal("ABC")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't transform non latin characters to their uppercase version", function() {
|
||||||
|
expect("abαπ".toLatinUppercase()).to.equal("ABαπ")
|
||||||
|
expect("abαπ".toLatinUppercase()).to.not.equal("abαπ".toUpperCase())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#String.removeEnclosure", function() {
|
||||||
|
it("should be able to remove the first and last characters", function() {
|
||||||
|
expect("[1+t]".removeEnclosure()).to.equal("1+t")
|
||||||
|
expect('"a+b+c*d"'.removeEnclosure()).to.equal("a+b+c*d")
|
||||||
|
expect("(pi/2)".removeEnclosure()).to.equal("pi/2")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#Number.toDecimalPrecision", function() {
|
||||||
|
it("should be able to round a number to a fixed decimal precision", function() {
|
||||||
|
expect(123.456789.toDecimalPrecision()).to.equal(123)
|
||||||
|
expect(123.456789.toDecimalPrecision(1)).to.equal(123.5)
|
||||||
|
expect(123.456789.toDecimalPrecision(2)).to.equal(123.46)
|
||||||
|
expect(123.456789.toDecimalPrecision(3)).to.equal(123.457)
|
||||||
|
expect(123.456789.toDecimalPrecision(4)).to.equal(123.4568)
|
||||||
|
expect(123.456789.toDecimalPrecision(5)).to.equal(123.45679)
|
||||||
|
expect(123.456789.toDecimalPrecision(6)).to.equal(123.456789)
|
||||||
|
expect(123.111111.toDecimalPrecision(5)).to.equal(123.11111)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Utils", function() {
|
||||||
|
describe("#textsup", function() {
|
||||||
|
it("should transform characters which have a sup unicode equivalent", function() {
|
||||||
|
expect(textsup("-+=()")).to.equal("⁻⁺⁼⁽⁾")
|
||||||
|
expect(textsup("0123456789")).to.equal("⁰¹²³⁴⁵⁶⁷⁸⁹")
|
||||||
|
expect(textsup("abcdefghijklmnoprstuvwxyz")).to.equal("ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖʳˢᵗᵘᵛʷˣʸᶻ")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't transform characters without a sup equivalent", function() {
|
||||||
|
expect(textsup("ABCDEFGHIJKLMNOPQRSTUVWXYZq")).to.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZq")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should partially transform strings which only have a few characters with a sup equivalent", function() {
|
||||||
|
expect(textsup("ABCabcABC")).to.equal("ABCᵃᵇᶜABC")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#textsub", function() {
|
||||||
|
it("should transform characters which have a sub unicode equivalent", function() {
|
||||||
|
expect(textsub("-+=()")).to.equal("₋₊₌₍₎")
|
||||||
|
expect(textsub("0123456789")).to.equal("₀₁₂₃₄₅₆₇₈₉")
|
||||||
|
expect(textsub("aehijklmnoprstuvx")).to.equal("ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't transform characters without a sub equivalent", function() {
|
||||||
|
expect(textsub("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).to.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
expect(textsub("bcdfgqyz")).to.equal("bcdfgqyz")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should partially transform strings which only have a few characters with a sub equivalent", function() {
|
||||||
|
expect(textsub("ABC123ABC")).to.equal("ABC₁₂₃ABC")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#parseName", function() {
|
||||||
|
it("should parse greek letter names", function() {
|
||||||
|
const shorthands = {
|
||||||
|
"α": ["al", "alpha"],
|
||||||
|
"β": ["be", "beta"],
|
||||||
|
"γ": ["ga", "gamma"],
|
||||||
|
"δ": ["de", "delta"],
|
||||||
|
"ε": ["ep", "epsilon"],
|
||||||
|
"ζ": ["ze", "zeta"],
|
||||||
|
"η": ["et", "eta"],
|
||||||
|
"θ": ["th", "theta"],
|
||||||
|
"κ": ["ka", "kappa"],
|
||||||
|
"λ": ["la", "lambda"],
|
||||||
|
"μ": ["mu"],
|
||||||
|
"ν": ["nu"],
|
||||||
|
"ξ": ["xi"],
|
||||||
|
"ρ": ["rh", "rho"],
|
||||||
|
"σ": ["si", "sigma"],
|
||||||
|
"τ": ["ta", "tau"],
|
||||||
|
"υ": ["up", "upsilon"],
|
||||||
|
"φ": ["ph", "phi"],
|
||||||
|
"χ": ["ch", "chi"],
|
||||||
|
"ψ": ["ps", "psi"],
|
||||||
|
"ω": ["om", "omega"],
|
||||||
|
"Γ": ["gga", "ggamma"],
|
||||||
|
"Δ": ["gde", "gdelta"],
|
||||||
|
"Θ": ["gth", "gtheta"],
|
||||||
|
"Λ": ["gla", "glambda"],
|
||||||
|
"Ξ": ["gxi"],
|
||||||
|
"Π": ["gpi"],
|
||||||
|
"Σ": ["gsi", "gsigma"],
|
||||||
|
"Φ": ["gph", "gphi"],
|
||||||
|
"Ψ": ["gps", "gpsi"],
|
||||||
|
"Ω": ["gom", "gomega"],
|
||||||
|
}
|
||||||
|
for(const [char, shorts] of Object.entries(shorthands)) {
|
||||||
|
expect(parseName(char)).to.equal(char)
|
||||||
|
for(const short of shorts)
|
||||||
|
expect(parseName(short)).to.equal(char)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should parse array elements into sub", function() {
|
||||||
|
expect(parseName("u[n+1]")).to.equal("uₙ₊₁")
|
||||||
|
expect(parseName("u[(n+x)]")).to.equal("u₍ₙ₊ₓ₎")
|
||||||
|
expect(parseName("u[A]")).to.equal("uA")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove disallowed characters when indicated", function() {
|
||||||
|
const disallowed = "xnπℝℕ\\∪∩[] ()^/^/÷*×+=1234567890¹²³⁴⁵⁶⁷⁸⁹⁰-"
|
||||||
|
expect(parseName(disallowed)).to.equal("")
|
||||||
|
expect(parseName("AA" + disallowed)).to.equal("AA")
|
||||||
|
expect(parseName(disallowed, false)).to.equal(disallowed)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to do all three at once", function() {
|
||||||
|
expect(parseName("al[n+1]+n")).to.equal("αₙ₊₁")
|
||||||
|
expect(parseName("al[n+1]+n", false)).to.equal("αₙ₊₁+n")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getRandomColor", function() {
|
||||||
|
it("should provide a valid color", function() {
|
||||||
|
const colorReg = /^#[A-F\d]{6}$/
|
||||||
|
for(let i = 0; i < 50; i++)
|
||||||
|
expect(getRandomColor()).to.match(colorReg)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#escapeHTML", function() {
|
||||||
|
it("should should escape ampersands", function() {
|
||||||
|
expect(escapeHTML("&")).to.equal("&")
|
||||||
|
expect(escapeHTML("High & Mighty")).to.equal("High & Mighty")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should escape injected HTML tags", function() {
|
||||||
|
expect(escapeHTML("<script>alert('Injected!')</script>")).to.equal("<script>alert('Injected!')</script>")
|
||||||
|
expect(escapeHTML('<a href="javascript:alert()">Link</a>')).to.equal('<a href="javascript:alert()">Link</a>')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#exponentsToExpression", function() {
|
||||||
|
it("should transform exponents to power expression", function() {
|
||||||
|
expect(exponentsToExpression("x¹²³⁴⁵⁶⁷⁸⁹⁰")).to.equal("x^1234567890")
|
||||||
|
expect(exponentsToExpression("x¹²+y³⁴")).to.equal("x^12+y^34")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -19,12 +19,10 @@ import * as fs from "./mock/fs.mjs";
|
||||||
import Qt from "./mock/qt.mjs";
|
import Qt from "./mock/qt.mjs";
|
||||||
import { MockHelper } from "./mock/helper.mjs";
|
import { MockHelper } from "./mock/helper.mjs";
|
||||||
import { MockLatex } from "./mock/latex.mjs";
|
import { MockLatex } from "./mock/latex.mjs";
|
||||||
import Modules from "../src/module/index.mjs";
|
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
globalThis.Helper = new MockHelper()
|
globalThis.Helper = new MockHelper()
|
||||||
globalThis.Latex = new MockLatex()
|
globalThis.Latex = new MockLatex()
|
||||||
Modules.Latex.initialize({ latex: Latex, helper: Helper })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup()
|
setup()
|
||||||
|
|
|
@ -19,46 +19,46 @@
|
||||||
import { describe, it } from "mocha"
|
import { describe, it } from "mocha"
|
||||||
import { expect } from "chai"
|
import { expect } from "chai"
|
||||||
|
|
||||||
import { Domain, parseDomainSimple } from "../../src/math/domain.mjs"
|
// import { Domain, parseDomainSimple } from "../../src/math/domain.mjs"
|
||||||
|
//
|
||||||
describe("math.domain", function() {
|
// describe("math.domain", function() {
|
||||||
describe("#parseDomainSimple", function() {
|
// describe("#parseDomainSimple", function() {
|
||||||
it("returns predefined domains", function() {
|
// it("returns predefined domains", function() {
|
||||||
const predefinedToCheck = [
|
// const predefinedToCheck = [
|
||||||
// Real domains
|
// // Real domains
|
||||||
{ domain: Domain.R, shortcuts: ["R", "ℝ"] },
|
// { domain: Domain.R, shortcuts: ["R", "ℝ"] },
|
||||||
// Zero exclusive real domains
|
// // Zero exclusive real domains
|
||||||
{ domain: Domain.RE, shortcuts: ["RE", "R*", "ℝ*"] },
|
// { domain: Domain.RE, shortcuts: ["RE", "R*", "ℝ*"] },
|
||||||
// Real positive domains
|
// // Real positive domains
|
||||||
{ domain: Domain.RP, shortcuts: ["RP", "R+", "ℝ⁺", "ℝ+"] },
|
// { domain: Domain.RP, shortcuts: ["RP", "R+", "ℝ⁺", "ℝ+"] },
|
||||||
// Zero-exclusive real positive domains
|
// // Zero-exclusive real positive domains
|
||||||
{ domain: Domain.RPE, shortcuts: ["RPE", "REP", "R+*", "R*+", "ℝ*⁺", "ℝ⁺*", "ℝ*+", "ℝ+*"] },
|
// { domain: Domain.RPE, shortcuts: ["RPE", "REP", "R+*", "R*+", "ℝ*⁺", "ℝ⁺*", "ℝ*+", "ℝ+*"] },
|
||||||
// Real negative domain
|
// // Real negative domain
|
||||||
{ domain: Domain.RM, shortcuts: ["RM", "R-", "ℝ⁻", "ℝ-"] },
|
// { domain: Domain.RM, shortcuts: ["RM", "R-", "ℝ⁻", "ℝ-"] },
|
||||||
// Zero-exclusive real negative domains
|
// // Zero-exclusive real negative domains
|
||||||
{ domain: Domain.RME, shortcuts: ["RME", "REM", "R-*", "R*-", "ℝ⁻*", "ℝ*⁻", "ℝ-*", "ℝ*-"] },
|
// { domain: Domain.RME, shortcuts: ["RME", "REM", "R-*", "R*-", "ℝ⁻*", "ℝ*⁻", "ℝ-*", "ℝ*-"] },
|
||||||
// Natural integers domain
|
// // Natural integers domain
|
||||||
{ domain: Domain.N, shortcuts: ["ℕ", "N", "ZP", "Z+", "ℤ⁺", "ℤ+"] },
|
// { domain: Domain.N, shortcuts: ["ℕ", "N", "ZP", "Z+", "ℤ⁺", "ℤ+"] },
|
||||||
// Zero-exclusive natural integers domain
|
// // Zero-exclusive natural integers domain
|
||||||
{ domain: Domain.NE, shortcuts: ["NE", "NP", "N*", "N+", "ℕ*", "ℕ⁺", "ℕ+", "ZPE", "ZEP", "Z+*", "Z*+", "ℤ⁺*", "ℤ*⁺", "ℤ+*", "ℤ*+"] },
|
// { domain: Domain.NE, shortcuts: ["NE", "NP", "N*", "N+", "ℕ*", "ℕ⁺", "ℕ+", "ZPE", "ZEP", "Z+*", "Z*+", "ℤ⁺*", "ℤ*⁺", "ℤ+*", "ℤ*+"] },
|
||||||
// Logarithmic natural domains
|
// // Logarithmic natural domains
|
||||||
{ domain: Domain.NLog, shortcuts: ["NLOG", "ℕˡᵒᵍ", "ℕLOG"] },
|
// { domain: Domain.NLog, shortcuts: ["NLOG", "ℕˡᵒᵍ", "ℕLOG"] },
|
||||||
// All integers domains
|
// // All integers domains
|
||||||
{ domain: Domain.Z, shortcuts: ["Z", "ℤ"] },
|
// { domain: Domain.Z, shortcuts: ["Z", "ℤ"] },
|
||||||
// Zero-exclusive all integers domain
|
// // Zero-exclusive all integers domain
|
||||||
{ domain: Domain.ZE, shortcuts: ["ZE", "Z*", "ℤ*"] },
|
// { domain: Domain.ZE, shortcuts: ["ZE", "Z*", "ℤ*"] },
|
||||||
// Negative integers domain
|
// // Negative integers domain
|
||||||
{ domain: Domain.ZM, shortcuts: ["ZM", "Z-", "ℤ⁻", "ℤ-"] },
|
// { domain: Domain.ZM, shortcuts: ["ZM", "Z-", "ℤ⁻", "ℤ-"] },
|
||||||
// Zero-exclusive negative integers domain
|
// // Zero-exclusive negative integers domain
|
||||||
{ domain: Domain.ZME, shortcuts: ["ZME", "ZEM", "Z-*", "Z*-", "ℤ⁻*", "ℤ*⁻", "ℤ-*", "ℤ*-"] },
|
// { domain: Domain.ZME, shortcuts: ["ZME", "ZEM", "Z-*", "Z*-", "ℤ⁻*", "ℤ*⁻", "ℤ-*", "ℤ*-"] },
|
||||||
]
|
// ]
|
||||||
|
//
|
||||||
// Real domains
|
// // Real domains
|
||||||
for(const { domain, shortcuts } of predefinedToCheck)
|
// for(const { domain, shortcuts } of predefinedToCheck)
|
||||||
for(const shortcut of shortcuts)
|
// for(const shortcut of shortcuts)
|
||||||
expect(parseDomainSimple(shortcut)).to.be.equal(domain)
|
// expect(parseDomainSimple(shortcut)).to.be.equal(domain)
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
it("")
|
// it("")
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
|
|
Loading…
Reference in a new issue