380 lines
No EOL
12 KiB
JavaScript
380 lines
No EOL
12 KiB
JavaScript
/**
|
|
* 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 { parseDomain, Expression as Expr, Domain } from "./math/index.mjs"
|
|
import Objects from "./module/objects.mjs"
|
|
|
|
const NONE = class Empty {
|
|
}
|
|
|
|
let stringValuesValidators = {
|
|
"int": [parseInt, (x) => !isNaN(x)],
|
|
"double": [parseFloat, (x) => !isNaN(x)],
|
|
"string": [(x) => x, () => true]
|
|
}
|
|
let stringValidatorTypes = Object.keys(stringValuesValidators)
|
|
|
|
|
|
class PropertyType {
|
|
/**
|
|
* Validates if a value corresponds to the current property type, and if so, returns it.
|
|
* @param value
|
|
* @returns {null|object}
|
|
*/
|
|
parse(value) {
|
|
throw new TypeError(`validate function of ${typeof this} has not been implemented.`)
|
|
}
|
|
|
|
/**
|
|
* Exports the value of this property type.
|
|
* @param value
|
|
* @returns {string|number|bool|object}
|
|
*/
|
|
export(value) {
|
|
throw new TypeError(`export function of ${typeof this} has not been implemented.`)
|
|
}
|
|
}
|
|
|
|
|
|
export class Expression extends PropertyType {
|
|
constructor(...variables) {
|
|
super()
|
|
this.type = "Expression"
|
|
this.variables = variables
|
|
}
|
|
|
|
toString() {
|
|
return this.variables.length === 0 ? "Number" : `Expression(${this.variables.join(", ")})`
|
|
}
|
|
|
|
parse(value) {
|
|
let result = NONE
|
|
if(typeof value == "string")
|
|
try {
|
|
result = new Expr(value)
|
|
} catch(e) {
|
|
// Silently error and return null
|
|
console.trace()
|
|
console.log(`Error parsing expression ${value}:`)
|
|
console.error(e)
|
|
}
|
|
return result
|
|
}
|
|
|
|
export(value) {
|
|
if(value instanceof Expr)
|
|
return value.toEditableString()
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} is not an expression.`)
|
|
}
|
|
}
|
|
|
|
|
|
export class Enum extends PropertyType {
|
|
constructor(...values) {
|
|
super()
|
|
this.type = "Enum"
|
|
this.values = values
|
|
this.legacyValues = {}
|
|
this.translatedValues = values.map(x => qsTranslate("parameters", x))
|
|
}
|
|
|
|
toString() {
|
|
return `${this.type}(${this.values.join(", ")})`
|
|
}
|
|
|
|
parse(value) {
|
|
let result = NONE
|
|
if(this.values.includes(value))
|
|
result = value
|
|
else if(this.legacyValues[value])
|
|
result = this.legacyValues[value]
|
|
return result
|
|
}
|
|
|
|
export(value) {
|
|
if(this.values.includes(value))
|
|
return value
|
|
else if(this.legacyValues[value])
|
|
return this.legacyValues[value]
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(", ")}.`)
|
|
}
|
|
}
|
|
|
|
|
|
export class ObjectType extends PropertyType {
|
|
constructor(objType, allowNull = false) {
|
|
super()
|
|
this.type = "ObjectType"
|
|
this.objType = objType
|
|
this.allowNull = allowNull
|
|
}
|
|
|
|
toString() {
|
|
return this.objType
|
|
}
|
|
|
|
parse(name) {
|
|
let result = NONE
|
|
if(typeof name == "string" && name in Objects.currentObjectsByName) {
|
|
let obj = Objects.currentObjectsByName[name]
|
|
if(obj.type === this.objType || (this.objType === "ExecutableObject" && obj.execute)) {
|
|
result = obj
|
|
} else {
|
|
// Silently error and return null
|
|
console.trace()
|
|
console.error(new TypeError(`Object ${name} is of not of type ${this.objType}:`))
|
|
}
|
|
} else if(this.allowNull && (name == null || name === "null"))
|
|
result = null
|
|
return result
|
|
}
|
|
|
|
export(value) {
|
|
if(value == null && this.allowNull)
|
|
return null
|
|
else if(value.type === this.objType || (this.objType === "ExecutableObject" && value.execute))
|
|
return value.name
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`)
|
|
}
|
|
}
|
|
|
|
|
|
export class List extends PropertyType {
|
|
constructor(type, format = /^.+$/, label = "", forbidAdding = false) {
|
|
super()
|
|
// type can be string, int and double.
|
|
this.type = "List"
|
|
this.valueType = type
|
|
if(!stringValidatorTypes.includes(this.valueType))
|
|
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(", ")}.`)
|
|
this.format = format
|
|
this.label = label
|
|
this.forbidAdding = forbidAdding
|
|
}
|
|
|
|
toString() {
|
|
return `${this.type}(${this.valueType}:${this.format})`
|
|
}
|
|
|
|
parse(value) {
|
|
let result = NONE
|
|
if(typeof value == "object" && value.__proto__ === Array) {
|
|
let valid = 0
|
|
for(let v of value) {
|
|
if(this.format.test(v)) {
|
|
v = stringValuesValidators[this.valueType][0](v)
|
|
if(stringValuesValidators[this.valueType][1](v))
|
|
valid++
|
|
}
|
|
}
|
|
if(valid === value.length) // Ensure every value is valid.
|
|
result = value
|
|
}
|
|
return result
|
|
}
|
|
|
|
export(value) {
|
|
if(typeof value == "object" && value.__proto__ === Array)
|
|
return value
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} not a list.`)
|
|
|
|
}
|
|
}
|
|
|
|
|
|
export class Dictionary extends PropertyType {
|
|
constructor(type, keyType = "string", format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = "", postKeyLabel = ": ", forbidAdding = false) {
|
|
super()
|
|
// type & keyType can be string, int and double.
|
|
this.type = "Dict"
|
|
this.valueType = type
|
|
this.keyType = keyType
|
|
this.format = format
|
|
this.keyFormat = keyFormat
|
|
this.preKeyLabel = preKeyLabel
|
|
this.postKeyLabel = postKeyLabel
|
|
this.forbidAdding = forbidAdding
|
|
}
|
|
|
|
toString() {
|
|
return `${this.type}(${this.keyType}:${this.keyFormat}: ${this.valueType}:${this.format})`
|
|
}
|
|
|
|
parse(value) {
|
|
let result = NONE
|
|
if(typeof value == "object" && value.__proto__ !== Array) {
|
|
let dict = []
|
|
for(let [k, v] of Object.entries(value)) {
|
|
if(this.format.test(v) && this.keyFormat.test(k)) {
|
|
k = stringValuesValidators[this.keyType][0](k)
|
|
v = stringValuesValidators[this.valueType][0](v)
|
|
if(stringValuesValidators[this.keyType][1](k))
|
|
if(stringValuesValidators[this.valueType][1](v))
|
|
dict[k] = v
|
|
}
|
|
}
|
|
if(Object.keys(dict).length === Object.keys(value).length)
|
|
result = value
|
|
}
|
|
return result
|
|
}
|
|
|
|
export(value) {
|
|
if(typeof value == "object" && value.__proto__ !== Array)
|
|
return value
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} not a dictionary.`)
|
|
}
|
|
}
|
|
|
|
// Common parameters for Enums
|
|
|
|
Enum.Position = new Enum(
|
|
QT_TRANSLATE_NOOP("parameters", "above"),
|
|
QT_TRANSLATE_NOOP("parameters", "below"),
|
|
QT_TRANSLATE_NOOP("parameters", "left"),
|
|
QT_TRANSLATE_NOOP("parameters", "right"),
|
|
QT_TRANSLATE_NOOP("parameters", "above-left"),
|
|
QT_TRANSLATE_NOOP("parameters", "above-right"),
|
|
QT_TRANSLATE_NOOP("parameters", "below-left"),
|
|
QT_TRANSLATE_NOOP("parameters", "below-right")
|
|
)
|
|
Enum.Position.legacyValues = {
|
|
"top": "above",
|
|
"bottom": "below",
|
|
"top-left": "above-left",
|
|
"top-right": "above-right",
|
|
"bottom-left": "below-left",
|
|
"bottom-right": "below-right"
|
|
}
|
|
|
|
Enum.Positioning = new Enum(
|
|
QT_TRANSLATE_NOOP("parameters", "center"),
|
|
QT_TRANSLATE_NOOP("parameters", "top"),
|
|
QT_TRANSLATE_NOOP("parameters", "bottom"),
|
|
QT_TRANSLATE_NOOP("parameters", "left"),
|
|
QT_TRANSLATE_NOOP("parameters", "right"),
|
|
QT_TRANSLATE_NOOP("parameters", "top-left"),
|
|
QT_TRANSLATE_NOOP("parameters", "top-right"),
|
|
QT_TRANSLATE_NOOP("parameters", "bottom-left"),
|
|
QT_TRANSLATE_NOOP("parameters", "bottom-right")
|
|
)
|
|
|
|
Enum.FunctionDisplayType = new Enum(
|
|
QT_TRANSLATE_NOOP("parameters", "application"),
|
|
QT_TRANSLATE_NOOP("parameters", "function")
|
|
)
|
|
|
|
Enum.BodePass = new Enum(
|
|
QT_TRANSLATE_NOOP("parameters", "high"),
|
|
QT_TRANSLATE_NOOP("parameters", "low")
|
|
)
|
|
|
|
|
|
Enum.XCursorValuePosition = new Enum(
|
|
QT_TRANSLATE_NOOP("parameters", "Next to target"),
|
|
QT_TRANSLATE_NOOP("parameters", "With label"),
|
|
QT_TRANSLATE_NOOP("parameters", "Hidden")
|
|
)
|
|
|
|
/**
|
|
* Ensures whether a provided value is of the corresponding type.
|
|
* @param {string|PropertyType} propertyType
|
|
* @param {string|number|boolean|array|object}value
|
|
* @returns {Object}
|
|
*/
|
|
export function ensureTypeSafety(propertyType, value) {
|
|
let result
|
|
let error = false
|
|
if(typeof propertyType == "string")
|
|
switch(propertyType) {
|
|
case "string":
|
|
result = value
|
|
error = typeof value !== "string"
|
|
break
|
|
case "number":
|
|
result = parseFloat(value)
|
|
error = isNaN(result)
|
|
break
|
|
case "boolean":
|
|
result = value
|
|
error = value !== true && value !== false
|
|
break
|
|
case "Domain":
|
|
try {
|
|
error = typeof value !== "string"
|
|
if(!error)
|
|
result = parseDomain(value)
|
|
} catch(e) {
|
|
// Parse domain sometimes returns an empty set when it cannot parse a domain.
|
|
// It's okay though, it shouldn't be user parsed value, so returning an empty set
|
|
// is okay for a corrupted file, rather than erroring.
|
|
console.trace()
|
|
console.log(`Error parsing domain ${value}:`)
|
|
console.error(e)
|
|
error = true
|
|
}
|
|
break
|
|
}
|
|
else if(propertyType instanceof PropertyType) {
|
|
result = propertyType.parse(value)
|
|
error = result === NONE
|
|
} else
|
|
throw new TypeError(`Importation error: Unknown property type ${propertyType}.`)
|
|
if(error) {
|
|
console.trace()
|
|
throw new TypeError(`Importation error: Couldn't parse ${JSON.stringify(value)} as ${propertyType}.`)
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Serializes a property by its type to export into JSON.
|
|
* @param {string|PropertyType} propertyType
|
|
* @param value
|
|
* @returns {Object}
|
|
*/
|
|
export function serializesByPropertyType(propertyType, value) {
|
|
let result
|
|
if(typeof propertyType == "string")
|
|
switch(propertyType) {
|
|
case "string":
|
|
result = value.toString()
|
|
break
|
|
case "number":
|
|
result = parseFloat(value)
|
|
break
|
|
case "boolean":
|
|
result = value === true
|
|
break
|
|
case "Domain":
|
|
if(value instanceof Domain)
|
|
result = value.toString()
|
|
else
|
|
throw new TypeError(`Exportation error: ${value} is not a domain.`)
|
|
break
|
|
}
|
|
else if(propertyType instanceof PropertyType) {
|
|
result = propertyType.export(value)
|
|
} else
|
|
throw new TypeError(`Exportation error: Unknown property type ${propertyType}.`)
|
|
return result
|
|
} |