diff --git a/common/package-lock.json b/common/package-lock.json
index ee15bf5..b90e4f5 100644
--- a/common/package-lock.json
+++ b/common/package-lock.json
@@ -19,11 +19,9 @@
},
"devDependencies": {
"@types/chai": "^5.0.0",
- "@types/chai-spies": "^1.0.6",
"@types/mocha": "^10.0.8",
"chai": "^5.1.1",
"chai-as-promised": "^8.0.0",
- "chai-spies": "^1.1.0",
"esm": "^3.2.25",
"mocha": "^10.7.3"
}
@@ -2201,16 +2199,6 @@
"dev": true,
"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": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -2526,19 +2514,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": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
diff --git a/common/package.json b/common/package.json
index 6940d87..9ef203c 100644
--- a/common/package.json
+++ b/common/package.json
@@ -24,11 +24,9 @@
},
"devDependencies": {
"@types/chai": "^5.0.0",
- "@types/chai-spies": "^1.0.6",
"@types/mocha": "^10.0.8",
"chai": "^5.1.1",
"chai-as-promised": "^8.0.0",
- "chai-spies": "^1.1.0",
"esm": "^3.2.25",
"mocha": "^10.7.3"
}
diff --git a/common/src/events.mjs b/common/src/events.mjs
index 702fc7d..dbdd00f 100644
--- a/common/src/events.mjs
+++ b/common/src/events.mjs
@@ -79,8 +79,7 @@ export class BaseEventEmitter {
if(eventType.includes(" ")) { // Unlisten to several different events with the same listener.
let found = false
for(const type of eventType.split(" "))
- found ||= this.off(type, eventListener)
- return found
+ found ||= this.off(eventType, eventListener)
} else {
if(!this.constructor.emits.includes(eventType)) {
const className = this.constructor.name
diff --git a/common/src/module/latex.mjs b/common/src/module/latex.mjs
index 4ff81cb..8ccbeb5 100644
--- a/common/src/module/latex.mjs
+++ b/common/src/module/latex.mjs
@@ -137,9 +137,9 @@ class LatexAPI extends Module {
*/
parif(elem, contents) {
elem = elem.toString()
- if(elem[0] !== "(" && elem.at(-1) !== ")" && contents.some(x => elem.indexOf(x) > 0))
+ if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
return this.par(elem)
- if(elem[0] === "(" && elem.at(-1) === ")")
+ if(elem[0] === "(" && elem[elem.length - 1] === ")")
return elem.removeEnclosure()
return elem
}
diff --git a/common/src/utils.mjs b/common/src/utils.mjs
index 0e451f4..094147d 100644
--- a/common/src/utils.mjs
+++ b/common/src/utils.mjs
@@ -21,16 +21,14 @@
* Replaces latin characters with their uppercase versions.
* @return {string}
*/
-String.prototype.toLatinUppercase = function() {
+String.prototype.toLatinUppercase = String.prototype.toLatinUppercase || function() {
return this.replace(/[a-z]/g, function(match) {
return match.toUpperCase()
})
}
/**
- * 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.
+ * Removes the 'enclosers' of a string (e.g. quotes, parentheses, brackets...)
* @return {string}
*/
String.prototype.removeEnclosure = function() {
@@ -45,126 +43,126 @@ String.prototype.removeEnclosure = function() {
* @return {number}
*/
Number.prototype.toDecimalPrecision = function(decimalPlaces = 0) {
- const p = Math.pow(10, decimalPlaces)
- const n = (this * p) * (1 + Number.EPSILON)
- return Math.round(n) / p
+ const p = Math.pow(10, decimalPlaces);
+ const n = (this * p) * (1 + Number.EPSILON);
+ return Math.round(n) / p;
}
-const CHARACTER_TO_POWER = new Map([
- ["-", "⁻"],
- ["+", "⁺"],
- ["=", "⁼"],
- [" ", " "],
- ["(", "⁽"],
- [")", "⁾"],
- ["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", "ᵖ"],
- ["r", "ʳ"],
- ["s", "ˢ"],
- ["t", "ᵗ"],
- ["u", "ᵘ"],
- ["v", "ᵛ"],
- ["w", "ʷ"],
- ["x", "ˣ"],
- ["y", "ʸ"],
- ["z", "ᶻ"]
-])
+const powerpos = {
+ "-": "⁻",
+ "+": "⁺",
+ "=": "⁼",
+ " ": " ",
+ "(": "⁽",
+ ")": "⁾",
+ "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": "ᵖ",
+ "r": "ʳ",
+ "s": "ˢ",
+ "t": "ᵗ",
+ "u": "ᵘ",
+ "v": "ᵛ",
+ "w": "ʷ",
+ "x": "ˣ",
+ "y": "ʸ",
+ "z": "ᶻ"
+}
-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 exponents = [
+ "⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹"
]
-const EXPONENTS_REG = new RegExp("([" + EXPONENTS.join("") + "]+)", "g")
+const exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g')
-/**
- * Put a text in sup position
- * @param {string} text
- * @return {string}
- */
+const indicepos = {
+ "-": "₋",
+ "+": "₊",
+ "=": "₌",
+ "(": "₍",
+ ")": "₎",
+ " ": " ",
+ "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) {
let ret = ""
text = text.toString()
- for(let letter of text)
- ret += CHARACTER_TO_POWER.has(letter) ? CHARACTER_TO_POWER.get(letter) : letter
+ for (let i = 0; i < text.length; i++) {
+ if(Object.keys(powerpos).indexOf(text[i]) >= 0) {
+ ret += powerpos[text[i]]
+ } else {
+ ret += text[i]
+ }
+ }
return ret
}
-/**
- * Put a text in sub position
- * @param {string} text
- * @return {string}
- */
+// Put a text in sub position
export function textsub(text) {
let ret = ""
text = text.toString()
- for(let letter of text)
- ret += CHARACTER_TO_INDICE.has(letter) ? CHARACTER_TO_INDICE.get(letter) : letter
+ for (let i = 0; i < text.length; i++) {
+ if(Object.keys(indicepos).indexOf(text[i]) >= 0) {
+ ret += indicepos[text[i]]
+ } else {
+ ret += text[i]
+ }
+ }
return ret
}
/**
* Simplifies (mathematically) a mathematical expression.
- * @deprecated
* @param {string} str - Expression to parse
* @returns {string}
*/
@@ -187,43 +185,37 @@ export function simplifyExpression(str) {
// n1 & n3 are multiplied, opeM is the main operation (- or +).
// Putting all n in form of number
//n2 = n2 == undefined ? 1 : parseFloat(n)
- n1 = m1 === undefined ? 1 : eval(m1 + "1")
- n2 = m2 === undefined ? 1 : eval("1" + m2)
- n3 = m3 === undefined ? 1 : eval(m3 + "1")
- n4 = m4 === undefined ? 1 : eval("1" + m4)
- //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)
- [ope2, ope4] = [ope2, ope4].map(ope => ope === "/" ? "/" : "*")
- let coeff1 = n1 * n2
- let coeff2 = n3 * n4
- let coefficient = coeff1 + coeff2 - (opeM === "-" ? 2 * coeff2 : 0)
-
+ n1 = m1 === undefined ? 1 : eval(m1 + '1')
+ n2 = m2 === undefined ? 1 : eval('1' + m2)
+ n3 = m3 === undefined ? 1 : eval(m3 + '1')
+ n4 = m4 === undefined ? 1 : eval('1' + m4)
+ //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)
+ [ope2, ope4] = [ope2, ope4].map(ope => ope === '/' ? '/' : '*')
+ let coeff1 = n1*n2
+ let coeff2 = n3*n4
+ let coefficient = coeff1+coeff2-(opeM === '-' ? 2*coeff2 : 0)
+
return `${coefficient} * π`
}
],
[ // Removing parenthesis when content is only added from both sides.
/(^|[+-] |\()\(([^)(]+)\)($| [+-]|\))/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.
/(^|[*\/] |\()\(([^)(+-]+)\)($| [*\/+-]|\))/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.
/(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
- function(match, b4, middle, after) {
- return `${b4}${middle}${after}`
- }
+ function(match, b4, middle, after) {return `${b4}${middle}${after}`}
],
[// Simplification additions/subtractions.
/(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g,
function(match, b4, n1, op1, middle, op2, n2, after) {
let total
- if(op2 === "+") {
+ if(op2 === '+') {
total = parseFloat(n1) + parseFloat(n2)
} else {
total = parseFloat(n1) - parseFloat(n2)
@@ -232,14 +224,14 @@ export function simplifyExpression(str) {
}
],
[// Simplification multiplications/divisions.
- /([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
+ /([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
function(match, n1, op1, middle, op2, n2) {
- if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === "/" &&
- (parseInt(n1) / parseInt(n2)) % 1 !== 0) {
+ if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === '/' &&
+ (parseInt(n1) / parseInt(n2)) % 1 !== 0) {
// Non int result for int division.
return `(${n1} / ${n2}) ${op1} ${middle}`
} else {
- if(op2 === "*") {
+ if(op2 === '*') {
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
} else {
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
@@ -253,17 +245,17 @@ export function simplifyExpression(str) {
let str = middle
// Replace all groups
while(/\([^)(]+\)/g.test(str))
- str = str.replace(/\([^)(]+\)/g, "")
+ str = str.replace(/\([^)(]+\)/g, '')
// There shouldn't be any more parenthesis
// If there is, that means the 2 parenthesis are needed.
- if(!str.includes(")") && !str.includes("(")) {
+ if(!str.includes(')') && !str.includes('(')) {
return middle
} else {
return `(${middle})`
}
-
+
}
- ]
+ ],
// Simple simplifications
// [/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'],
// [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
@@ -276,7 +268,7 @@ export function simplifyExpression(str) {
// [/(^| |\() /g, '$1'],
// [/ ($|\))/g, '$1'],
]
-
+
// Replacements
let found
do {
@@ -294,35 +286,24 @@ export function simplifyExpression(str) {
/**
* Transforms a mathematical expression to make it readable by humans.
* NOTE: Will break parsing of expression.
- * @deprecated
* @param {string} str - Expression to parse.
* @returns {string}
*/
export function makeExpressionReadable(str) {
let replacements = [
// letiables
- [/pi/g, "π"],
- [/Infinity/g, "∞"],
- [/inf/g, "∞"],
+ [/pi/g, 'π'],
+ [/Infinity/g, '∞'],
+ [/inf/g, '∞'],
// Other
- [/ \* /g, "×"],
- [/ \^ /g, "^"],
- [/\^\(([\d\w+-]+)\)/g, function(match, p1) {
- return textsup(p1)
- }],
- [/\^([\d\w+-]+)/g, function(match, p1) {
- return textsup(p1)
- }],
- [/_\(([\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"],
+ [/ \* /g, '×'],
+ [/ \^ /g, '^'],
+ [/\^\(([\d\w+-]+)\)/g, function(match, p1) { return textsup(p1) }],
+ [/\^([\d\w+-]+)/g, function(match, p1) { return textsup(p1) }],
+ [/_\(([\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) {
if(a.length < b.length) {
return `∫${textsub(a)}${textsup(b)} ${body} d${by}`
@@ -331,10 +312,10 @@ export function makeExpressionReadable(str) {
}
}],
[/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`
}]
]
-
+
// str = simplifyExpression(str)
// Replacements
for(let replacement of replacements)
@@ -343,48 +324,6 @@ export function makeExpressionReadable(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.
*
@@ -393,24 +332,65 @@ const replacements = [
* @returns {string} - The parsed name
*/
export function parseName(str, removeUnallowed = true) {
- for(const replacement of replacements)
+ let 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])
- if(removeUnallowed)
- str = str.replace(/[xnπℝℕ\\∪∩\]\[ ()^/÷*×+=\d¹²³⁴⁵⁶⁷⁸⁹⁰-]/g, "")
-
return str
}
/**
* Transforms camel case strings to a space separated one.
*
- * @deprecated
* @param {string} label - Camel case to parse
* @returns {string} Parsed label.
*/
export function camelCase2readable(label) {
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")
}
/**
@@ -418,12 +398,12 @@ export function camelCase2readable(label) {
* @returns {string}
*/
export function getRandomColor() {
- let clrs = "0123456789ABCDEF"
- let color = "#"
+ let clrs = '0123456789ABCDEF';
+ let color = '#';
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;
}
/**
@@ -432,15 +412,16 @@ export function getRandomColor() {
* @returns {string}
*/
export function escapeHTML(str) {
- return str.replace(/&/g, "&").replace(//g, ">")
+ return str.replace(/&/g,'&').replace(//g,'>') ;
}
+
/**
* Parses exponents and replaces them with expression values
* @param {string} expression - The expression to replace in.
* @return {string} The parsed expression
*/
export function exponentsToExpression(expression) {
- return expression.replace(EXPONENTS_REG, (m, exp) => "^" + exp.split("").map((x) => EXPONENTS.indexOf(x)).join(""))
+ return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join(''))
}
diff --git a/common/test/general/events.mjs b/common/test/general/events.mjs
deleted file mode 100644
index 15d44aa..0000000
--- a/common/test/general/events.mjs
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * 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 .
- */
-
-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)
- })
-})
\ No newline at end of file
diff --git a/common/test/general/utils.mjs b/common/test/general/utils.mjs
deleted file mode 100644
index 8078645..0000000
--- a/common/test/general/utils.mjs
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * 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 .
- */
-
-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("")).to.equal("<script>alert('Injected!')</script>")
- expect(escapeHTML('Link')).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")
- })
- })
-})
diff --git a/common/test/hooks.mjs b/common/test/hooks.mjs
index 0f3ebda..e48525e 100644
--- a/common/test/hooks.mjs
+++ b/common/test/hooks.mjs
@@ -19,10 +19,12 @@ import * as fs from "./mock/fs.mjs";
import Qt from "./mock/qt.mjs";
import { MockHelper } from "./mock/helper.mjs";
import { MockLatex } from "./mock/latex.mjs";
+import Modules from "../src/module/index.mjs";
function setup() {
globalThis.Helper = new MockHelper()
globalThis.Latex = new MockLatex()
+ Modules.Latex.initialize({ latex: Latex, helper: Helper })
}
setup()
diff --git a/common/test/math/domain.mjs b/common/test/math/domain.mjs
index e32dbd1..c00384f 100644
--- a/common/test/math/domain.mjs
+++ b/common/test/math/domain.mjs
@@ -19,46 +19,46 @@
import { describe, it } from "mocha"
import { expect } from "chai"
-// import { Domain, parseDomainSimple } from "../../src/math/domain.mjs"
-//
-// describe("math.domain", function() {
-// describe("#parseDomainSimple", function() {
-// it("returns predefined domains", function() {
-// const predefinedToCheck = [
-// // Real domains
-// { domain: Domain.R, shortcuts: ["R", "ℝ"] },
-// // Zero exclusive real domains
-// { domain: Domain.RE, shortcuts: ["RE", "R*", "ℝ*"] },
-// // Real positive domains
-// { domain: Domain.RP, shortcuts: ["RP", "R+", "ℝ⁺", "ℝ+"] },
-// // Zero-exclusive real positive domains
-// { domain: Domain.RPE, shortcuts: ["RPE", "REP", "R+*", "R*+", "ℝ*⁺", "ℝ⁺*", "ℝ*+", "ℝ+*"] },
-// // Real negative domain
-// { domain: Domain.RM, shortcuts: ["RM", "R-", "ℝ⁻", "ℝ-"] },
-// // Zero-exclusive real negative domains
-// { domain: Domain.RME, shortcuts: ["RME", "REM", "R-*", "R*-", "ℝ⁻*", "ℝ*⁻", "ℝ-*", "ℝ*-"] },
-// // Natural integers domain
-// { domain: Domain.N, shortcuts: ["ℕ", "N", "ZP", "Z+", "ℤ⁺", "ℤ+"] },
-// // Zero-exclusive natural integers domain
-// { domain: Domain.NE, shortcuts: ["NE", "NP", "N*", "N+", "ℕ*", "ℕ⁺", "ℕ+", "ZPE", "ZEP", "Z+*", "Z*+", "ℤ⁺*", "ℤ*⁺", "ℤ+*", "ℤ*+"] },
-// // Logarithmic natural domains
-// { domain: Domain.NLog, shortcuts: ["NLOG", "ℕˡᵒᵍ", "ℕLOG"] },
-// // All integers domains
-// { domain: Domain.Z, shortcuts: ["Z", "ℤ"] },
-// // Zero-exclusive all integers domain
-// { domain: Domain.ZE, shortcuts: ["ZE", "Z*", "ℤ*"] },
-// // Negative integers domain
-// { domain: Domain.ZM, shortcuts: ["ZM", "Z-", "ℤ⁻", "ℤ-"] },
-// // Zero-exclusive negative integers domain
-// { domain: Domain.ZME, shortcuts: ["ZME", "ZEM", "Z-*", "Z*-", "ℤ⁻*", "ℤ*⁻", "ℤ-*", "ℤ*-"] },
-// ]
-//
-// // Real domains
-// for(const { domain, shortcuts } of predefinedToCheck)
-// for(const shortcut of shortcuts)
-// expect(parseDomainSimple(shortcut)).to.be.equal(domain)
-// })
-//
-// it("")
-// })
-// })
+import { Domain, parseDomainSimple } from "../../src/math/domain.mjs"
+
+describe("math.domain", function() {
+ describe("#parseDomainSimple", function() {
+ it("returns predefined domains", function() {
+ const predefinedToCheck = [
+ // Real domains
+ { domain: Domain.R, shortcuts: ["R", "ℝ"] },
+ // Zero exclusive real domains
+ { domain: Domain.RE, shortcuts: ["RE", "R*", "ℝ*"] },
+ // Real positive domains
+ { domain: Domain.RP, shortcuts: ["RP", "R+", "ℝ⁺", "ℝ+"] },
+ // Zero-exclusive real positive domains
+ { domain: Domain.RPE, shortcuts: ["RPE", "REP", "R+*", "R*+", "ℝ*⁺", "ℝ⁺*", "ℝ*+", "ℝ+*"] },
+ // Real negative domain
+ { domain: Domain.RM, shortcuts: ["RM", "R-", "ℝ⁻", "ℝ-"] },
+ // Zero-exclusive real negative domains
+ { domain: Domain.RME, shortcuts: ["RME", "REM", "R-*", "R*-", "ℝ⁻*", "ℝ*⁻", "ℝ-*", "ℝ*-"] },
+ // Natural integers domain
+ { domain: Domain.N, shortcuts: ["ℕ", "N", "ZP", "Z+", "ℤ⁺", "ℤ+"] },
+ // Zero-exclusive natural integers domain
+ { domain: Domain.NE, shortcuts: ["NE", "NP", "N*", "N+", "ℕ*", "ℕ⁺", "ℕ+", "ZPE", "ZEP", "Z+*", "Z*+", "ℤ⁺*", "ℤ*⁺", "ℤ+*", "ℤ*+"] },
+ // Logarithmic natural domains
+ { domain: Domain.NLog, shortcuts: ["NLOG", "ℕˡᵒᵍ", "ℕLOG"] },
+ // All integers domains
+ { domain: Domain.Z, shortcuts: ["Z", "ℤ"] },
+ // Zero-exclusive all integers domain
+ { domain: Domain.ZE, shortcuts: ["ZE", "Z*", "ℤ*"] },
+ // Negative integers domain
+ { domain: Domain.ZM, shortcuts: ["ZM", "Z-", "ℤ⁻", "ℤ-"] },
+ // Zero-exclusive negative integers domain
+ { domain: Domain.ZME, shortcuts: ["ZME", "ZEM", "Z-*", "Z*-", "ℤ⁻*", "ℤ*⁻", "ℤ-*", "ℤ*-"] },
+ ]
+
+ // Real domains
+ for(const { domain, shortcuts } of predefinedToCheck)
+ for(const shortcut of shortcuts)
+ expect(parseDomainSimple(shortcut)).to.be.equal(domain)
+ })
+
+ it("")
+ })
+})