Compare commits

..

No commits in common. "781a0f4aae7e5a0ea10d12476a4f99cab1833d8d" and "8e75f13e9a9417c60bf5729d705247a27f0621b3" have entirely different histories.

15 changed files with 112 additions and 337 deletions

View file

@ -36,7 +36,7 @@ class HistoryCommonAPI extends Module {
redo() { this.history.redo() } redo() { this.history.redo() }
clear() { this.history.clear() } clear() { this.history.clear() }
addToHistory(action) { this.history.addToHistory(action) } addToHistory(action) { this.history.addToHistory(action) }
unserialize(...data) { this.history.unserialize(...data) } unserialize(data) { this.history.unserialize(data) }
serialize() { return this.history.serialize() } serialize() { return this.history.serialize() }
} }
@ -124,7 +124,7 @@ export class Action {
} }
/** /**
* Returns a string with the HTML-formatted description of the action. * Returns a string with the HTML-formated description of the action.
* *
* @returns {string} * @returns {string}
*/ */

View file

@ -98,15 +98,15 @@ class IOAPI extends Module {
Modules.History.clear() Modules.History.clear()
// Importing settings // Importing settings
this.settings.saveFilename = filename this.settings.saveFilename = filename
this.settings.xzoom = parseFloat(data["xzoom"]) || 100 this.settings.xzoom = data["xzoom"]
this.settings.yzoom = parseFloat(data["yzoom"]) || 10 this.settings.yzoom = data["yzoom"]
this.settings.xmin = parseFloat(data["xmin"]) || 5/10 this.settings.xmin = data["xmin"]
this.settings.ymax = parseFloat(data["ymax"]) || 24 this.settings.ymax = data["ymax"]
this.settings.xaxisstep = data["xaxisstep"] || "4" this.settings.xaxisstep = data["xaxisstep"]
this.settings.yaxisstep = data["yaxisstep"] || "4" this.settings.yaxisstep = data["yaxisstep"]
this.settings.xlabel = data["xaxislabel"] || "" this.settings.xlabel = data["xaxislabel"]
this.settings.ylabel = data["yaxislabel"] || "" this.settings.ylabel = data["yaxislabel"]
this.settings.logscalex = data["logscalex"] === true this.settings.logscalex = data["logscalex"]
if("showxgrad" in data) if("showxgrad" in data)
this.settings.showxgrad = data["showxgrad"] this.settings.showxgrad = data["showxgrad"]
if("showygrad" in data) if("showygrad" in data)
@ -115,8 +115,8 @@ class IOAPI extends Module {
this.settings.linewidth = data["linewidth"] this.settings.linewidth = data["linewidth"]
if("textsize" in data) if("textsize" in data)
this.settings.textsize = data["textsize"] this.settings.textsize = data["textsize"]
this.rootElement.height = parseFloat(data["height"]) || 500 this.rootElement.height = data["height"]
this.rootElement.width = parseFloat(data["width"]) || 1000 this.rootElement.width = data["width"]
// Importing objects // Importing objects
Modules.Objects.currentObjects = {} Modules.Objects.currentObjects = {}
@ -129,8 +129,7 @@ class IOAPI extends Module {
if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) { if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) {
Modules.Objects.currentObjects[objType] = [] Modules.Objects.currentObjects[objType] = []
for(let objData of data['objects'][objType]) { for(let objData of data['objects'][objType]) {
/** @type {DrawableObject} */ let obj = new Modules.Objects.types[objType](...objData)
let obj = Modules.Objects.types[objType].import(...objData)
Modules.Objects.currentObjects[objType].push(obj) Modules.Objects.currentObjects[objType].push(obj)
Modules.Objects.currentObjectsByName[obj.name] = obj Modules.Objects.currentObjectsByName[obj.name] = obj
} }

View file

@ -26,8 +26,6 @@ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTra
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); } qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
/** @type {function(string, string): string} */ /** @type {function(string, string): string} */
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); } QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
/** @type {function(string): string} */
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
/** @type {function(string|boolean|int): string} */ /** @type {function(string|boolean|int): string} */
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }

View file

@ -20,7 +20,6 @@ import { getRandomColor, textsub } from "../utils.mjs"
import Objects from "../objects.mjs" import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs" import Latex from "../math/latex.mjs"
import {Module} from "../modules.mjs" import {Module} from "../modules.mjs"
import {ensureTypeSafety, serializesByPropertyType} from "../parameters.mjs"
// This file contains the default data to be imported from all other objects // This file contains the default data to be imported from all other objects
@ -35,7 +34,7 @@ class ObjectsCommonAPI extends Module {
} }
/** /**
* Creates a new name for an object, based on the allowedLetters. * Creates a new name for an object, based on the \c allowedLetters.
* If variables with each of the allowedLetters is created, a subscript * If variables with each of the allowedLetters is created, a subscript
* number is added to the name. * number is added to the name.
* @param {string} allowedLetters * @param {string} allowedLetters
@ -57,7 +56,7 @@ class ObjectsCommonAPI extends Module {
} }
/** /**
* Registers the object obj in the object list. * Registers the object \c obj in the object list.
* @param {DrawableObject} obj - Object to be registered. * @param {DrawableObject} obj - Object to be registered.
*/ */
registerObject(obj) { registerObject(obj) {
@ -132,20 +131,6 @@ export class DrawableObject {
* @return {boolean} * @return {boolean}
*/ */
static executable() {return false} static executable() {return false}
/**
* Imports the object from its serialized form.
* @return {DrawableObject}
*/
static import(name, visible, color, labelContent, ...args) {
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
console.log('Importing', this.type(), name, args)
for(let [name, propType] of Object.entries(this.properties()))
if(!name.startsWith('comment')) {
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4]))
}
return new this(...importedArgs)
}
/** /**
* Base constructor for the object. * Base constructor for the object.
@ -153,7 +138,7 @@ export class DrawableObject {
* @param {boolean} visible - true if the object is visible, false otherwise. * @param {boolean} visible - true if the object is visible, false otherwise.
* @param {color|string} color - Color of the object (can be string or QColor) * @param {color|string} color - Color of the object (can be string or QColor)
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. * @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
* @constructor * @constructor()
*/ */
constructor(name, visible = true, color = null, labelContent = 'name + value') { constructor(name, visible = true, color = null, labelContent = 'name + value') {
if(color == null) color = getRandomColor() if(color == null) color = getRandomColor()
@ -165,18 +150,15 @@ export class DrawableObject {
this.requiredBy = [] this.requiredBy = []
this.requires = [] this.requires = []
} }
/** /**
* Serializes the object in an array that can be JSON serialized. * Serilizes the object in an array that can be JSON serialized.
* These parameters will be re-entered in the constructor when restored. * These parameters will be re-entered in the constructor when restored.
* @return {array} * @return {array}
*/ */
export() { export() {
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent] // Should return what will be inputed as arguments when a file is loaded (serializable form)
for(let [name, propType] of Object.entries(this.constructor.properties())) return [this.name, this.visible, this.color.toString(), this.labelContent]
if(!name.startsWith('comment'))
exportList.push(serializesByPropertyType(propType, this[name]))
return exportList
} }
/** /**
@ -200,7 +182,7 @@ export class DrawableObject {
} }
/** /**
* Readable string content of the label depending on the value of the latexContent. * Readable string content of the label depending on the value of the \c latexContent.
* @return {string} * @return {string}
*/ */
getLabel() { getLabel() {
@ -216,7 +198,7 @@ export class DrawableObject {
} }
/** /**
* Latex markup string content of the label depending on the value of the latexContent. * Latex markup string content of the label depending on the value of the \c latexContent.
* Every non latin character should be passed as latex symbols and formulas * Every non latin character should be passed as latex symbols and formulas
* should be in latex form. * should be in latex form.
* See ../latex.mjs for helper methods. * See ../latex.mjs for helper methods.
@ -293,15 +275,16 @@ export class DrawableObject {
} }
/** /**
* Abstract method. Draw the object onto the canvas with the. * Abstract method. Draw the object onto the \c canvas with the.
* @param {CanvasAPI} canvas * @param {CanvasAPI} canvas
* @param {CanvasRenderingContext2D} ctx
*/ */
draw(canvas) {} draw(canvas) {}
/** /**
* Applicates a drawFunction with two position arguments depending on * Applicates a \c drawFunction with two position arguments depending on
* both the posX and posY of where the label should be displayed, * both the \c posX and \c posY of where the label should be displayed,
* and the labelPosition which declares the label should be displayed * and the \c labelPosition which declares the label should be displayed
* relatively to that position. * relatively to that position.
* *
* @param {string|Enum} labelPosition - Position of the label relative to the marked position * @param {string|Enum} labelPosition - Position of the label relative to the marked position
@ -350,16 +333,17 @@ export class DrawableObject {
} }
/** /**
* Automatically draw text (by default the label of the object on the canvas * Automatically draw text (by default the label of the object on the \c canvas with
* depending on user settings. * the 2D context \c ctx depending on user settings.
* This method takes into account both the posX and posY of where the label * This method takes into account both the \c posX and \c posY of where the label
* should be displayed, including the labelPosition relative to it. * should be displayed, including the \c labelPosition relative to it.
* The text is get both through the getLatexFunction and getTextFunction * The text is get both through the \c getLatexFunction and \c getTextFunction
* depending on whether to use latex. * depending on whether to use latex.
* Then, it's displayed using the drawFunctionLatex (x,y,imageData) and * Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
* drawFunctionText (x,y,text) depending on whether to use latex. * \c drawFunctionText (x,y,text) depending on whether to use latex.
* *
* @param {CanvasAPI} canvas * @param {CanvasAPI} canvas
* @param {CanvasRenderingContext2D} ctx
* @param {string|Enum} labelPosition - Position of the label relative to the marked position * @param {string|Enum} labelPosition - Position of the label relative to the marked position
* @param {number} posX - Component of the marked position on the x-axis * @param {number} posX - Component of the marked position on the x-axis
* @param {number} posY - Component of the marked position on the y-axis * @param {number} posY - Component of the marked position on the y-axis

View file

@ -35,8 +35,8 @@ export default class Function extends ExecutableObject {
'comment', 'comment',
'Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}' 'Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
), ),
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', [QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
'comment2': QT_TRANSLATE_NOOP( 'comment2': QT_TRANSLATE_NOOP(
'comment', 'comment',
@ -81,7 +81,13 @@ export default class Function extends ExecutableObject {
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}` return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
} }
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(),
this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines]
}
execute(x = 1) { execute(x = 1) {
if(this.definitionDomain.includes(x)) if(this.definitionDomain.includes(x))
return this.expression.execute(x) return this.expression.execute(x)

View file

@ -81,6 +81,11 @@ export default class GainBode extends ExecutableObject {
\\end{array}` \\end{array}`
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.om_0.name, this.pass.toString(), this.gain.toEditableString(), this.labelPosition, this.labelX, this.omGraduation]
}
execute(x=1) { execute(x=1) {
if(typeof x == 'string') x = executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) { if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) {

View file

@ -64,6 +64,11 @@ export default class PhaseBode extends ExecutableObject {
this.labelX = labelX this.labelX = labelX
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.om_0.name, this.phase.toEditableString(), this.unit, this.labelPosition, this.labelX]
}
getReadableString() { getReadableString() {
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}` return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
} }

View file

@ -54,6 +54,10 @@ export default class Point extends DrawableObject {
return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)` return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)`
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
}
draw(canvas) { draw(canvas) {
let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
let pointSize = 8+(canvas.linewidth*2) let pointSize = 8+(canvas.linewidth*2)

View file

@ -26,34 +26,33 @@ export default class RepartitionFunction extends ExecutableObject {
static displayType(){return qsTr('Repartition')} static displayType(){return qsTr('Repartition')}
static displayTypeMultiple(){return qsTr('Repartition functions')} static displayTypeMultiple(){return qsTr('Repartition functions')}
static properties() {return { static properties() {return {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
'comment1': QT_TRANSLATE_NOOP( 'comment1': QT_TRANSLATE_NOOP(
'comment', 'comment',
'Note: Specify the probability for each value.' 'Note: Specify the probability for each value.'
), ),
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '), [QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
}} }}
static import(name, visible, color, labelContent, ...args) {
console.log(args, args.length)
if(args.length === 5) {
// Two legacy values no longer included.
args.shift()
args.shift()
}
return super.import(name, visible, color, labelContent, ...args)
}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = 'name + value',
probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) { beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
if(name == null) name = Common.getNewName('XYZUVW', "F_") if(name == null) name = Common.getNewName('XYZUVW', "F_")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.beginIncluded = beginIncluded
this.drawLineEnds = drawLineEnds
this.probabilities = probabilities this.probabilities = probabilities
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.labelX = labelX this.labelX = labelX
this.update() this.update()
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX]
}
getReadableString() { getReadableString() {
let keys = Object.keys(this.probabilities).sort((a,b) => a-b); let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
let varname = this.name.substring(this.name.indexOf("_")+1) let varname = this.name.substring(this.name.indexOf("_")+1)

View file

@ -55,9 +55,16 @@ export default class Sequence extends ExecutableObject {
this.labelX = labelX this.labelX = labelX
this.update() this.update()
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues,
this.labelPosition, this.labelX]
}
update() { update() {
console.log('Updating sequence', this.sequence) console.log('Updating sequence', this.sequence)
console.trace()
super.update() super.update()
if( if(
this.sequence == null || this.baseValues !== this.sequence.baseValues || this.sequence == null || this.baseValues !== this.sequence.baseValues ||

View file

@ -44,6 +44,10 @@ export default class SommeGainsBode extends ExecutableObject {
this.recalculateCache() this.recalculateCache()
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
}
getReadableString() { getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}` return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
} }

View file

@ -42,6 +42,10 @@ export default class SommePhasesBode extends ExecutableObject {
this.recalculateCache() this.recalculateCache()
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
}
getReadableString() { getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}` return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
} }

View file

@ -32,7 +32,7 @@ export default class Text extends DrawableObject {
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(), [QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning, [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
[QT_TRANSLATE_NOOP('prop','text')]: 'string', [QT_TRANSLATE_NOOP('prop','text')]: 'string',
'comment1': QT_TRANSLATE_NOOP( 'comment1': QT_TRANSLATE_NOOP(
'comment', 'comment',
'If you have latex enabled, you can use use latex markup in between $$ to create equations.' 'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
), ),
@ -78,6 +78,10 @@ export default class Text extends DrawableObject {
return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"` return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"`
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.text, this.disableLatex]
}
getLabel() { getLabel() {
return this.text return this.text
} }

View file

@ -29,8 +29,8 @@ export default class XCursor extends DrawableObject {
static displayType(){return qsTr('X Cursor')} static displayType(){return qsTr('X Cursor')}
static displayTypeMultiple(){return qsTr('X Cursors')} static displayTypeMultiple(){return qsTr('X Cursors')}
static properties() {return { static properties() {return {
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(), [QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject', true), [QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean', [QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number', [QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
@ -60,6 +60,12 @@ export default class XCursor extends DrawableObject {
this.targetValuePosition = targetValuePosition this.targetValuePosition = targetValuePosition
} }
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.x.toEditableString(), this.targetElement == null ? null : this.targetElement.name, this.labelPosition,
this.approximate, this.rounding, this.displayStyle, this.targetValuePosition]
}
getReadableString() { getReadableString() {
if(this.targetElement == null) return `${this.name} = ${this.x.toString()}` if(this.targetElement == null) return `${this.name} = ${this.x.toString()}`
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}` return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`

View file

@ -1,206 +1,72 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {parseDomain, Expression as Expr, Domain} from "./mathlib.mjs"
const NONE = class Empty {} export class Expression {
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) { constructor(...variables) {
super()
this.type = 'Expression' this.type = 'Expression'
this.variables = variables this.variables = variables
} }
toString() { toString() {
return this.variables.length === 0 ? 'Number' : `Expression(${this.variables.join(', ')})` 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 {
export class Enum extends PropertyType {
constructor(...values) { constructor(...values) {
super()
this.type = 'Enum' this.type = 'Enum'
this.values = values this.values = values
this.legacyValues = {}
this.translatedValues = values.map(x => qsTr(x)) this.translatedValues = values.map(x => qsTr(x))
} }
toString() { toString() {
return `${this.type}(${this.values.join(', ')})` return this.type
}
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 {
export class ObjectType extends PropertyType { constructor(objType) {
constructor(objType, allowNull=false) {
super()
this.type = 'ObjectType' this.type = 'ObjectType'
this.objType = objType this.objType = objType
this.allowNull = allowNull
} }
toString() { toString() {
return this.objType return this.objType
} }
parse(name) {
let result = NONE
if(typeof name == 'string' && name in Modules.Objects.currentObjectsByName) {
let obj = Modules.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 {
export class List extends PropertyType {
constructor(type, format = /^.+$/, label = '', forbidAdding = false) { constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
super()
// type can be string, int and double. // type can be string, int and double.
this.type = 'List' this.type = 'List'
this.valueType = type this.valueType = type
if(!stringValidatorTypes.includes(this.valueType))
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(', ')}.`)
this.format = format this.format = format
this.label = label this.label = label
this.forbidAdding = forbidAdding this.forbidAdding = forbidAdding
} }
toString() { toString() {
return `${this.type}(${this.valueType}:${this.format})` return this.objType
}
parse(value) {
let result = NONE
if(typeof value == 'object' && value.__proto__ === Array) {
let list = []
for(let v of value) {
if (this.format.test(v)) {
v = stringValuesValidators[this.valueType][0](v)
if(stringValuesValidators[this.valueType][1](v))
list.append(v)
}
}
if(list.length === 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 list.`)
} }
} }
export class Dictionary {
export class Dictionary extends PropertyType {
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) { constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
super()
// type & keyType can be string, int and double. // type & keyType can be string, int and double.
this.type = 'Dict' this.type = 'Dict'
this.valueType = type this.valueType = type
@ -211,35 +77,9 @@ export class Dictionary extends PropertyType {
this.postKeyLabel = postKeyLabel this.postKeyLabel = postKeyLabel
this.forbidAdding = forbidAdding this.forbidAdding = forbidAdding
} }
toString() { toString() {
return `${this.type}(${this.keyType}:${this.keyFormat}: ${this.valueType}:${this.format})` return 'Dictionary'
}
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.`)
} }
} }
@ -255,14 +95,6 @@ Enum.Position = new Enum(
QT_TR_NOOP('below-left'), QT_TR_NOOP('below-left'),
QT_TR_NOOP('below-right') QT_TR_NOOP('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( Enum.Positioning = new Enum(
QT_TR_NOOP('center'), QT_TR_NOOP('center'),
@ -293,86 +125,4 @@ Enum.XCursorValuePosition = new Enum(
QT_TR_NOOP('Hidden') QT_TR_NOOP('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
}