diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index 52eaeb5..33d3587 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -398,27 +398,20 @@ class IEntity { static attributes = {} constructor(values) { - // @ts-expect-error - const attributes = this.constructor.attributes; - if (values.constructor !== Object && Object.getOwnPropertyNames(attributes).length == 1) { - // Where there is just one attribute, option can be the value of that attribute - values = { - [Object.getOwnPropertyNames(attributes)[0]]: values - }; - } /** - * @param {String[]} prefix * @param {Object} target * @param {Object} properties + * @param {Object} values + * @param {String} prefix */ - const defineAllAttributes = (prefix, target, properties, values) => { - let fullKey = prefix.concat(""); - const last = fullKey.length - 1; + const defineAllAttributes = (target, properties, values, prefix = "") => { for (let property of Utility.mergeArrays( Object.getOwnPropertyNames(properties), Object.getOwnPropertyNames(values ?? {}) )) { - fullKey[last] = property; + if (!(property in properties)) { + console.warn(`Property ${prefix}${property} is not defined in ${this.constructor.name}`); + } let defaultValue = properties[property]; const defaultType = (defaultValue instanceof TypeInitialization) ? defaultValue.type @@ -428,7 +421,7 @@ class IEntity { // Not instanceof because all objects are instenceof Object, exact match needed if (defaultType === Object) { target[property] = {}; - defineAllAttributes(fullKey, target[property], properties[property], values[property]); + defineAllAttributes(target[property], properties[property], values[property], property + "."); continue } /* @@ -438,7 +431,7 @@ class IEntity { * - A type: the default value will be default constructed object without arguments. * - A proper value. */ - const value = Utility.objectGet(values, fullKey); + const value = Utility.objectGet(values, [property]); if (value !== undefined) { target[property] = TypeInitialization.sanitize(value, defaultType); continue @@ -460,7 +453,15 @@ class IEntity { target[property] = TypeInitialization.sanitize(defaultValue, defaultType); } }; - defineAllAttributes([], this, attributes, values); + // @ts-expect-error + const attributes = this.constructor.attributes; + if (values.constructor !== Object && Object.getOwnPropertyNames(attributes).length == 1) { + // Where there is just one attribute, option can be the value of that attribute + values = { + [Object.getOwnPropertyNames(attributes)[0]]: values + }; + } + defineAllAttributes(this, attributes, values); } } @@ -579,6 +580,21 @@ class IntegerEntity extends IEntity { // @ts-check +class InvariantTextEntity extends IEntity { + + static lookbehind = "INVTEXT" + static attributes = { + value: String, + } + + constructor(options = {}) { + super(options); + /** @type {String} */ this.value; + } +} + +// @ts-check + class KeyBindingEntity extends IEntity { static attributes = { @@ -918,6 +934,8 @@ class Grammar { return r.Reference case LocalizedTextEntity: return r.LocalizedText + case InvariantTextEntity: + return r.InvariantText case PinReferenceEntity: return r.PinReference case FunctionReferenceEntity: @@ -959,7 +977,7 @@ class Grammar { /** * @template T - * @param {new () => T} entityType + * @param {new (values: Object) => T} entityType * @returns {Parsimmon.Parser} */ static createMultiAttributeGrammar = (r, entityType) => @@ -979,9 +997,9 @@ class Grammar { .skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma P.string(')'), (_, attributes, __) => { - let result = new entityType(); - attributes.forEach(attributeSetter => attributeSetter(result)); - return result + let values = {}; + attributes.forEach(attributeSetter => attributeSetter(values)); + return new entityType(values) } ) @@ -1066,6 +1084,12 @@ class Grammar { }) ) + InvariantText = r => r.String.trim(P.optWhitespace).wrap( + P.string(InvariantTextEntity.lookbehind).skip(P.optWhitespace).skip(P.string("(")), + P.string(")") + ) + .map(value => new InvariantTextEntity({ value: value })) + AttributeAnyValue = r => P.alt( r.Null, r.None, @@ -1074,8 +1098,9 @@ class Grammar { r.Integer, r.String, r.Guid, - r.Reference, - r.LocalizedText + r.LocalizedText, + r.InvariantText, + r.Reference ) PinReference = r => P.seqMap( @@ -3087,11 +3112,9 @@ class SelectableDraggableTemplate extends ITemplate { createInputObjects(element) { return [ ...super.createInputObjects(element), - ...[ - new MouseMoveNodes(element, element.blueprint, { - looseTarget: true - }), - ] + new MouseMoveNodes(element, element.blueprint, { + looseTarget: true + }), ] } @@ -4504,6 +4527,11 @@ function initializeSerializerFactory() { new GeneralSerializer(v => `${LocalizedTextEntity.lookbehind}(${v})`, LocalizedTextEntity, "", ", ", false, "", _ => "") ); + SerializerFactory.registerSerializer( + InvariantTextEntity, + new GeneralSerializer(v => `${InvariantTextEntity.lookbehind}(${v})`, InvariantTextEntity, "", ", ", false, "", _ => "") + ); + SerializerFactory.registerSerializer( PinReferenceEntity, new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "") diff --git a/js/entity/IEntity.js b/js/entity/IEntity.js index 180647c..8117b39 100644 --- a/js/entity/IEntity.js +++ b/js/entity/IEntity.js @@ -8,27 +8,20 @@ export default class IEntity { static attributes = {} constructor(values) { - // @ts-expect-error - const attributes = this.constructor.attributes - if (values.constructor !== Object && Object.getOwnPropertyNames(attributes).length == 1) { - // Where there is just one attribute, option can be the value of that attribute - values = { - [Object.getOwnPropertyNames(attributes)[0]]: values - } - } /** - * @param {String[]} prefix * @param {Object} target * @param {Object} properties + * @param {Object} values + * @param {String} prefix */ - const defineAllAttributes = (prefix, target, properties, values) => { - let fullKey = prefix.concat("") - const last = fullKey.length - 1 + const defineAllAttributes = (target, properties, values, prefix = "") => { for (let property of Utility.mergeArrays( Object.getOwnPropertyNames(properties), Object.getOwnPropertyNames(values ?? {}) )) { - fullKey[last] = property + if (!(property in properties)) { + console.warn(`Property ${prefix}${property} is not defined in ${this.constructor.name}`) + } let defaultValue = properties[property] const defaultType = (defaultValue instanceof TypeInitialization) ? defaultValue.type @@ -38,7 +31,7 @@ export default class IEntity { // Not instanceof because all objects are instenceof Object, exact match needed if (defaultType === Object) { target[property] = {} - defineAllAttributes(fullKey, target[property], properties[property], values[property]) + defineAllAttributes(target[property], properties[property], values[property], property + ".") continue } /* @@ -48,7 +41,7 @@ export default class IEntity { * - A type: the default value will be default constructed object without arguments. * - A proper value. */ - const value = Utility.objectGet(values, fullKey) + const value = Utility.objectGet(values, [property]) if (value !== undefined) { target[property] = TypeInitialization.sanitize(value, defaultType) continue @@ -70,6 +63,14 @@ export default class IEntity { target[property] = TypeInitialization.sanitize(defaultValue, defaultType) } } - defineAllAttributes([], this, attributes, values) + // @ts-expect-error + const attributes = this.constructor.attributes + if (values.constructor !== Object && Object.getOwnPropertyNames(attributes).length == 1) { + // Where there is just one attribute, option can be the value of that attribute + values = { + [Object.getOwnPropertyNames(attributes)[0]]: values + } + } + defineAllAttributes(this, attributes, values) } } diff --git a/js/entity/InvariantTextEntity.js b/js/entity/InvariantTextEntity.js new file mode 100644 index 0000000..292509b --- /dev/null +++ b/js/entity/InvariantTextEntity.js @@ -0,0 +1,16 @@ +// @ts-check + +import IEntity from "./IEntity" + +export default class InvariantTextEntity extends IEntity { + + static lookbehind = "INVTEXT" + static attributes = { + value: String, + } + + constructor(options = {}) { + super(options) + /** @type {String} */ this.value + } +} diff --git a/js/serialization/Grammar.js b/js/serialization/Grammar.js index 9a01cc3..d7d2a45 100755 --- a/js/serialization/Grammar.js +++ b/js/serialization/Grammar.js @@ -4,6 +4,7 @@ import FunctionReferenceEntity from "../entity/FunctionReferenceEntity" import GuidEntity from "../entity/GuidEntity" import IdentifierEntity from "../entity/IdentifierEntity" import IntegerEntity from "../entity/IntegerEntity" +import InvariantTextEntity from "../entity/InvariantTextEntity" import KeyBindingEntity from "../entity/KeyBindingEntity" import LocalizedTextEntity from "../entity/LocalizedTextEntity" import ObjectEntity from "../entity/ObjectEntity" @@ -38,6 +39,8 @@ export default class Grammar { return r.Reference case LocalizedTextEntity: return r.LocalizedText + case InvariantTextEntity: + return r.InvariantText case PinReferenceEntity: return r.PinReference case FunctionReferenceEntity: @@ -79,7 +82,7 @@ export default class Grammar { /** * @template T - * @param {new () => T} entityType + * @param {new (values: Object) => T} entityType * @returns {Parsimmon.Parser} */ static createMultiAttributeGrammar = (r, entityType) => @@ -99,9 +102,9 @@ export default class Grammar { .skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma P.string(')'), (_, attributes, __) => { - let result = new entityType() - attributes.forEach(attributeSetter => attributeSetter(result)) - return result + let values = {} + attributes.forEach(attributeSetter => attributeSetter(values)) + return new entityType(values) } ) @@ -186,6 +189,12 @@ export default class Grammar { }) ) + InvariantText = r => r.String.trim(P.optWhitespace).wrap( + P.string(InvariantTextEntity.lookbehind).skip(P.optWhitespace).skip(P.string("(")), + P.string(")") + ) + .map(value => new InvariantTextEntity({ value: value })) + AttributeAnyValue = r => P.alt( r.Null, r.None, @@ -194,8 +203,9 @@ export default class Grammar { r.Integer, r.String, r.Guid, - r.Reference, - r.LocalizedText + r.LocalizedText, + r.InvariantText, + r.Reference ) PinReference = r => P.seqMap( diff --git a/js/serialization/initializeSerializerFactory.js b/js/serialization/initializeSerializerFactory.js index e72562a..0e6c35c 100755 --- a/js/serialization/initializeSerializerFactory.js +++ b/js/serialization/initializeSerializerFactory.js @@ -16,6 +16,7 @@ import PinEntity from "../entity/PinEntity" import PinReferenceEntity from "../entity/PinReferenceEntity" import SerializerFactory from "./SerializerFactory" import ToStringSerializer from "./ToStringSerializer" +import InvariantTextEntity from "../entity/InvariantTextEntity" export default function initializeSerializerFactory() { @@ -44,6 +45,11 @@ export default function initializeSerializerFactory() { new GeneralSerializer(v => `${LocalizedTextEntity.lookbehind}(${v})`, LocalizedTextEntity, "", ", ", false, "", _ => "") ) + SerializerFactory.registerSerializer( + InvariantTextEntity, + new GeneralSerializer(v => `${InvariantTextEntity.lookbehind}(${v})`, InvariantTextEntity, "", ", ", false, "", _ => "") + ) + SerializerFactory.registerSerializer( PinReferenceEntity, new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "") diff --git a/js/template/SelectableDraggableTemplate.js b/js/template/SelectableDraggableTemplate.js index 6ab86df..f0c58e4 100755 --- a/js/template/SelectableDraggableTemplate.js +++ b/js/template/SelectableDraggableTemplate.js @@ -19,11 +19,9 @@ export default class SelectableDraggableTemplate extends ITemplate { createInputObjects(element) { return [ ...super.createInputObjects(element), - ...[ - new MouseMoveNodes(element, element.blueprint, { - looseTarget: true - }), - ] + new MouseMoveNodes(element, element.blueprint, { + looseTarget: true + }), ] }