From 9ab8801eee65b43af2d692907edcbd05c02ef219 Mon Sep 17 00:00:00 2001 From: barsdeveloper Date: Fri, 1 Apr 2022 18:05:47 +0200 Subject: [PATCH] Fix link creation bug --- dist/font/roboto-bold.woff | Bin dist/font/roboto-bold.woff2 | Bin dist/font/roboto-light.woff | Bin dist/font/roboto-light.woff2 | Bin dist/font/roboto-regular.woff | Bin dist/font/roboto-regular.woff2 | Bin dist/ueblueprint.js | 2638 ++++++++++++++++---------------- js/element/LinkElement.js | 10 +- package.json | 2 +- 9 files changed, 1327 insertions(+), 1323 deletions(-) mode change 100755 => 100644 dist/font/roboto-bold.woff mode change 100755 => 100644 dist/font/roboto-bold.woff2 mode change 100755 => 100644 dist/font/roboto-light.woff mode change 100755 => 100644 dist/font/roboto-light.woff2 mode change 100755 => 100644 dist/font/roboto-regular.woff mode change 100755 => 100644 dist/font/roboto-regular.woff2 diff --git a/dist/font/roboto-bold.woff b/dist/font/roboto-bold.woff old mode 100755 new mode 100644 diff --git a/dist/font/roboto-bold.woff2 b/dist/font/roboto-bold.woff2 old mode 100755 new mode 100644 diff --git a/dist/font/roboto-light.woff b/dist/font/roboto-light.woff old mode 100755 new mode 100644 diff --git a/dist/font/roboto-light.woff2 b/dist/font/roboto-light.woff2 old mode 100755 new mode 100644 diff --git a/dist/font/roboto-regular.woff b/dist/font/roboto-regular.woff old mode 100755 new mode 100644 diff --git a/dist/font/roboto-regular.woff2 b/dist/font/roboto-regular.woff2 old mode 100755 new mode 100644 diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index 2920532..371289c 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -143,27 +143,27 @@ class Configuration { */ const html = String.raw; -// @ts-check - -/** - * @typedef {import("../element/IElement").default} IElement - */ -class ITemplate { - - /** - * @param {IElement} entity - */ - render(entity) { - return "" - } - - /** - * @param {IElement} element - */ - apply(element) { - // TODO replace with the safer element.setHTML(...) when it will be availableBreack - element.innerHTML = this.render(element); - } +// @ts-check + +/** + * @typedef {import("../element/IElement").default} IElement + */ +class ITemplate { + + /** + * @param {IElement} entity + */ + render(entity) { + return "" + } + + /** + * @param {IElement} element + */ + apply(element) { + // TODO replace with the safer element.setHTML(...) when it will be availableBreack + element.innerHTML = this.render(element); + } } // @ts-check @@ -493,71 +493,71 @@ class FastSelectionModel { } -// @ts-check - -/** - * @typedef {import("../Blueprint").default} Blueprint - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../input/IContext").default} IContext - * @typedef {import("../template/ITemplate").default} ITemplate - */ - -class IElement extends HTMLElement { - - static tagName = "" - - /** @type {Blueprint} */ - blueprint - - /** @type {IEntity} */ - entity - - /** @type {ITemplate} */ - template - - /** @type {IContext[]} */ - inputObjects = [] - - constructor(entity, template) { - super(); - this.blueprint = null; - this.entity = entity; - this.template = template; - this.inputObjects = []; - } - - getTemplate() { - return this.template - } - - connectedCallback() { - this.blueprint = this.closest("ueb-blueprint"); - this.template.apply(this); - this.inputObjects = this.createInputObjects(); - } - - disconnectedCallback() { - this.inputObjects.forEach(v => v.unlistenDOMElement()); - } - - /** @param {IElement} element */ - isSameGraph(element) { - return this.blueprint && this.blueprint == element?.blueprint - } - - /** - * @template {IContext} T - * @param {new (...args: any[]) => T} type - * @returns {T} - */ - getInputObject(type) { - return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type)) - } - - // Subclasses will want to override - createInputObjects() { - return [] - } +// @ts-check + +/** + * @typedef {import("../Blueprint").default} Blueprint + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../input/IContext").default} IContext + * @typedef {import("../template/ITemplate").default} ITemplate + */ + +class IElement extends HTMLElement { + + static tagName = "" + + /** @type {Blueprint} */ + blueprint + + /** @type {IEntity} */ + entity + + /** @type {ITemplate} */ + template + + /** @type {IContext[]} */ + inputObjects = [] + + constructor(entity, template) { + super(); + this.blueprint = null; + this.entity = entity; + this.template = template; + this.inputObjects = []; + } + + getTemplate() { + return this.template + } + + connectedCallback() { + this.blueprint = this.closest("ueb-blueprint"); + this.template.apply(this); + this.inputObjects = this.createInputObjects(); + } + + disconnectedCallback() { + this.inputObjects.forEach(v => v.unlistenDOMElement()); + } + + /** @param {IElement} element */ + isSameGraph(element) { + return this.blueprint && this.blueprint == element?.blueprint + } + + /** + * @template {IContext} T + * @param {new (...args: any[]) => T} type + * @returns {T} + */ + getInputObject(type) { + return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type)) + } + + // Subclasses will want to override + createInputObjects() { + return [] + } } // @ts-check @@ -608,43 +608,43 @@ class SelectorTemplate extends ITemplate { } } -// @ts-check - -class SelectorElement extends IElement { - - static tagName = "ueb-selector" - - constructor() { - super({}, new SelectorTemplate()); - this.selectionModel = null; - /** @type {SelectorTemplate} */ - this.template; - } - - /** - * Create a selection rectangle starting from the specified position - * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element) - */ - startSelecting(initialPosition) { - this.template.applyStartSelecting(this, initialPosition); - this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction); - } - - /** - * Move selection rectagle to the specified final position. The initial position was specified by startSelecting() - * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element) - */ - doSelecting(finalPosition) { - this.template.applyDoSelecting(this, finalPosition); - this.selectionModel.selectTo(finalPosition); - } - - finishSelecting() { - this.template.applyFinishSelecting(this); - this.selectionModel = null; - } -} - +// @ts-check + +class SelectorElement extends IElement { + + static tagName = "ueb-selector" + + constructor() { + super({}, new SelectorTemplate()); + this.selectionModel = null; + /** @type {SelectorTemplate} */ + this.template; + } + + /** + * Create a selection rectangle starting from the specified position + * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element) + */ + startSelecting(initialPosition) { + this.template.applyStartSelecting(this, initialPosition); + this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction); + } + + /** + * Move selection rectagle to the specified final position. The initial position was specified by startSelecting() + * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element) + */ + doSelecting(finalPosition) { + this.template.applyDoSelecting(this, finalPosition); + this.selectionModel.selectTo(finalPosition); + } + + finishSelecting() { + this.template.applyFinishSelecting(this); + this.selectionModel = null; + } +} + customElements.define(SelectorElement.tagName, SelectorElement); // @ts-check @@ -791,62 +791,62 @@ class BlueprintTemplate extends ITemplate { } } -// @ts-check - -/** - * @typedef {import("../Blueprint").default} Blueprint - */ -class IContext { - - /** @type {HTMLElement} */ - target - - /** @type {Blueprint} */ - blueprint - - /** @type {Object} */ - options - - #hasFocus = false - - get hasFocus() { - return this.#hasFocus - } - - set hasFocus(_) { - } - - constructor(target, blueprint, options) { - this.target = target; - this.blueprint = blueprint; - this.options = options; - let self = this; - this.blueprintFocusHandler = _ => { - this.#hasFocus = true; - self.listenEvents(); - }; - this.blueprintUnfocusHandler = _ => { - self.unlistenEvents(); - this.#hasFocus = false; - }; - if (options?.wantsFocusCallback ?? false) { - this.blueprint.addEventListener("blueprint-focus", this.blueprintFocusHandler); - this.blueprint.addEventListener("blueprint-unfocus", this.blueprintUnfocusHandler); - } - } - - unlistenDOMElement() { - this.unlistenEvents(); - this.blueprint.removeEventListener("blueprint-focus", this.blueprintFocusHandler); - this.blueprint.removeEventListener("blueprint-unfocus", this.blueprintUnfocusHandler); - } - - /* Subclasses will probabily override the following methods */ - listenEvents() { - } - - unlistenEvents() { - } +// @ts-check + +/** + * @typedef {import("../Blueprint").default} Blueprint + */ +class IContext { + + /** @type {HTMLElement} */ + target + + /** @type {Blueprint} */ + blueprint + + /** @type {Object} */ + options + + #hasFocus = false + + get hasFocus() { + return this.#hasFocus + } + + set hasFocus(_) { + } + + constructor(target, blueprint, options) { + this.target = target; + this.blueprint = blueprint; + this.options = options; + let self = this; + this.blueprintFocusHandler = _ => { + this.#hasFocus = true; + self.listenEvents(); + }; + this.blueprintUnfocusHandler = _ => { + self.unlistenEvents(); + this.#hasFocus = false; + }; + if (options?.wantsFocusCallback ?? false) { + this.blueprint.addEventListener("blueprint-focus", this.blueprintFocusHandler); + this.blueprint.addEventListener("blueprint-unfocus", this.blueprintUnfocusHandler); + } + } + + unlistenDOMElement() { + this.unlistenEvents(); + this.blueprint.removeEventListener("blueprint-focus", this.blueprintFocusHandler); + this.blueprint.removeEventListener("blueprint-unfocus", this.blueprintUnfocusHandler); + } + + /* Subclasses will probabily override the following methods */ + listenEvents() { + } + + unlistenEvents() { + } } // @ts-check @@ -998,66 +998,66 @@ class Utility { } } -// @ts-check - -class IEntity { - - static attributes = {} - - constructor(options = {}) { - /** - * @param {String[]} prefix - * @param {Object} target - * @param {Object} properties - */ - const defineAllAttributes = (prefix, target, properties) => { - let fullKey = prefix.concat(""); - const last = fullKey.length - 1; - for (let property of Object.getOwnPropertyNames(properties)) { - fullKey[last] = property; - // Not instanceof because all objects are instenceof Object, exact match needed - if (properties[property]?.constructor === Object) { - target[property] = {}; - defineAllAttributes(fullKey, target[property], properties[property]); - continue - } - /* - * The value can either be: - * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World"). - * - TypeInitialization: contains the maximum amount of information about the attribute. - * - A type: the default value will be default constructed object without arguments. - * - A proper value. - */ - const value = Utility.objectGet(options, fullKey); - if (value !== undefined) { - target[property] = value; - continue - } - let defaultValue = properties[property]; - if (defaultValue instanceof TypeInitialization) { - if (!defaultValue.showDefault) { - target[property] = undefined; // to preserve the order - continue - } - defaultValue = defaultValue.value; - } - if (defaultValue instanceof Array) { - target[property] = []; - continue - } - if (defaultValue instanceof Function) { - defaultValue = TypeInitialization.sanitize(new defaultValue()); - } - target[property] = TypeInitialization.sanitize(defaultValue); - } - }; - // @ts-expect-error - defineAllAttributes([], this, this.constructor.attributes); - } - - empty() { - return true - } +// @ts-check + +class IEntity { + + static attributes = {} + + constructor(options = {}) { + /** + * @param {String[]} prefix + * @param {Object} target + * @param {Object} properties + */ + const defineAllAttributes = (prefix, target, properties) => { + let fullKey = prefix.concat(""); + const last = fullKey.length - 1; + for (let property of Object.getOwnPropertyNames(properties)) { + fullKey[last] = property; + // Not instanceof because all objects are instenceof Object, exact match needed + if (properties[property]?.constructor === Object) { + target[property] = {}; + defineAllAttributes(fullKey, target[property], properties[property]); + continue + } + /* + * The value can either be: + * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World"). + * - TypeInitialization: contains the maximum amount of information about the attribute. + * - A type: the default value will be default constructed object without arguments. + * - A proper value. + */ + const value = Utility.objectGet(options, fullKey); + if (value !== undefined) { + target[property] = value; + continue + } + let defaultValue = properties[property]; + if (defaultValue instanceof TypeInitialization) { + if (!defaultValue.showDefault) { + target[property] = undefined; // to preserve the order + continue + } + defaultValue = defaultValue.value; + } + if (defaultValue instanceof Array) { + target[property] = []; + continue + } + if (defaultValue instanceof Function) { + defaultValue = TypeInitialization.sanitize(new defaultValue()); + } + target[property] = TypeInitialization.sanitize(defaultValue); + } + }; + // @ts-expect-error + defineAllAttributes([], this, this.constructor.attributes); + } + + empty() { + return true + } } // @ts-check @@ -1130,32 +1130,32 @@ class GuidEntity extends IEntity { } } -// @ts-check - -class IdentifierEntity extends IEntity { - - static attributes = { - value: String, - } - - constructor(options = {}) { - // Not instanceof to pick also primitive string - if (options.constructor === String) { - options = { - value: options - }; - } - super(options); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } +// @ts-check + +class IdentifierEntity extends IEntity { + + static attributes = { + value: String, + } + + constructor(options = {}) { + // Not instanceof to pick also primitive string + if (options.constructor === String) { + options = { + value: options + }; + } + super(options); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } } // @ts-check @@ -1188,28 +1188,28 @@ class IntegerEntity extends IEntity { } } -// @ts-check - -class KeyBindingEntity extends IEntity { - - static attributes = { - ActionName: "", - bShift: false, - bCtrl: false, - bAlt: false, - bCmd: false, - Key: IdentifierEntity, - } - - constructor(options = {}) { - super(options); - /** @type {String} */ this.ActionName; - /** @type {Boolean} */ this.bShift; - /** @type {Boolean} */ this.bCtrl; - /** @type {Boolean} */ this.bAlt; - /** @type {Boolean} */ this.bCmd; - /** @type {IdentifierEntity} */ this.Key; - } +// @ts-check + +class KeyBindingEntity extends IEntity { + + static attributes = { + ActionName: "", + bShift: false, + bCtrl: false, + bAlt: false, + bCmd: false, + Key: IdentifierEntity, + } + + constructor(options = {}) { + super(options); + /** @type {String} */ this.ActionName; + /** @type {Boolean} */ this.bShift; + /** @type {Boolean} */ this.bCtrl; + /** @type {Boolean} */ this.bAlt; + /** @type {Boolean} */ this.bCmd; + /** @type {IdentifierEntity} */ this.Key; + } } // @ts-check @@ -1732,84 +1732,84 @@ class SerializerFactory { } } -// @ts-check - -class ISerializer { - - static grammar = Parsimmon.createLanguage(new Grammar()) - - constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { - this.entityType = entityType; - this.prefix = prefix ?? ""; - this.separator = separator ?? ","; - this.trailingSeparator = trailingSeparator ?? false; - this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "="; - this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join(".")); - } - - writeValue(value) { - if (value === null) { - return "()" - } - const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v); - // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) - switch (value?.constructor) { - case Function: - return this.writeValue(value()) - case Boolean: - return Utility.FirstCapital(value.toString()) - case Number: - return value.toString() - case String: - return `"${value}"` - } - if (value instanceof Array) { - return `(${value.map(v => serialize(v) + ",").join("")})` - } - if (value instanceof IEntity) { - return serialize(value) - } - } - - /** - * @param {String[]} key - * @param {Object} object - * @returns {String} - */ - subWrite(key, object) { - let result = ""; - let fullKey = key.concat(""); - const last = fullKey.length - 1; - for (const property of Object.getOwnPropertyNames(object)) { - fullKey[last] = property; - const value = object[property]; - if (object[property]?.constructor === Object) { - // Recursive call when finding an object - result += (result.length ? this.separator : "") - + this.subWrite(fullKey, value); - } else if (value !== undefined && this.showProperty(object, fullKey, value)) { - result += (result.length ? this.separator : "") - + this.prefix - + this.attributeKeyPrinter(fullKey) - + this.attributeValueConjunctionSign - + this.writeValue(value); - } - } - if (this.trailingSeparator && result.length && fullKey.length === 1) { - // append separator at the end if asked and there was printed content - result += this.separator; - } - return result - } - - showProperty(object, attributeKey, attributeValue) { - const attributes = this.entityType.attributes; - const attribute = Utility.objectGet(attributes, attributeKey); - if (attribute instanceof TypeInitialization) { - return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault - } - return true - } +// @ts-check + +class ISerializer { + + static grammar = Parsimmon.createLanguage(new Grammar()) + + constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) { + this.entityType = entityType; + this.prefix = prefix ?? ""; + this.separator = separator ?? ","; + this.trailingSeparator = trailingSeparator ?? false; + this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "="; + this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join(".")); + } + + writeValue(value) { + if (value === null) { + return "()" + } + const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v); + // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically) + switch (value?.constructor) { + case Function: + return this.writeValue(value()) + case Boolean: + return Utility.FirstCapital(value.toString()) + case Number: + return value.toString() + case String: + return `"${value}"` + } + if (value instanceof Array) { + return `(${value.map(v => serialize(v) + ",").join("")})` + } + if (value instanceof IEntity) { + return serialize(value) + } + } + + /** + * @param {String[]} key + * @param {Object} object + * @returns {String} + */ + subWrite(key, object) { + let result = ""; + let fullKey = key.concat(""); + const last = fullKey.length - 1; + for (const property of Object.getOwnPropertyNames(object)) { + fullKey[last] = property; + const value = object[property]; + if (object[property]?.constructor === Object) { + // Recursive call when finding an object + result += (result.length ? this.separator : "") + + this.subWrite(fullKey, value); + } else if (value !== undefined && this.showProperty(object, fullKey, value)) { + result += (result.length ? this.separator : "") + + this.prefix + + this.attributeKeyPrinter(fullKey) + + this.attributeValueConjunctionSign + + this.writeValue(value); + } + } + if (this.trailingSeparator && result.length && fullKey.length === 1) { + // append separator at the end if asked and there was printed content + result += this.separator; + } + return result + } + + showProperty(object, attributeKey, attributeValue) { + const attributes = this.entityType.attributes; + const attribute = Utility.objectGet(attributes, attributeKey); + if (attribute instanceof TypeInitialization) { + return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault + } + return true + } } // @ts-check @@ -1899,90 +1899,90 @@ class Copy extends IContext { } } -// @ts-check - -class IKeyboardShortcut extends IContext { - - /** @type {KeyBindingEntity[]} */ - #activationKeys - - constructor(target, blueprint, options = {}) { - options.wantsFocusCallback = true; - options.activationKeys ??= []; - if (!(options.activationKeys instanceof Array)) { - options.activationKeys = [options.activationKeys]; - } - options.activationKeys = options.activationKeys.map(v => { - if (v instanceof KeyBindingEntity) { - return v - } - if (v.constructor === String) { - // @ts-expect-error - const parsed = ISerializer.grammar.KeyBinding.parse(v); - if (parsed.status) { - return parsed.value - } - } - throw new Error("Unexpected key value") - }); - - super(target, blueprint, options); - - this.#activationKeys = this.options.activationKeys ?? []; - - const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; - const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; - const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; - - let self = this; - /** @param {KeyboardEvent} e */ - this.keyDownHandler = e => { - if ( - self.#activationKeys.some(keyEntry => - wantsShift(keyEntry) == e.shiftKey - && wantsCtrl(keyEntry) == e.ctrlKey - && wantsAlt(keyEntry) == e.altKey - && this.blueprint.settings.Keys[keyEntry.Key] == e.code - )) { - self.fire(); - document.removeEventListener("keydown", self.keyDownHandler); - document.addEventListener("keyup", self.keyUpHandler); - } - }; - - /** @param {KeyboardEvent} e */ - this.keyUpHandler = e => { - if ( - self.#activationKeys.some(keyEntry => - keyEntry.bShift && e.key == "Shift" - || keyEntry.bCtrl && e.key == "Control" - || keyEntry.bAlt && e.key == "Alt" - || keyEntry.bCmd && e.key == "Meta" // Unsure about this, what key is that? - || this.blueprint.settings.Keys[keyEntry.Key] == e.code - )) { - self.unfire(); - document.removeEventListener("keyup", this.keyUpHandler); - document.addEventListener("keydown", this.keyDownHandler); - } - }; - - } - - listenEvents() { - document.addEventListener("keydown", this.keyDownHandler); - } - - unlistenEvents() { - document.removeEventListener("keydown", this.keyDownHandler); - } - - // Subclasses will want to override - - fire() { - } - - unfire() { - } +// @ts-check + +class IKeyboardShortcut extends IContext { + + /** @type {KeyBindingEntity[]} */ + #activationKeys + + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true; + options.activationKeys ??= []; + if (!(options.activationKeys instanceof Array)) { + options.activationKeys = [options.activationKeys]; + } + options.activationKeys = options.activationKeys.map(v => { + if (v instanceof KeyBindingEntity) { + return v + } + if (v.constructor === String) { + // @ts-expect-error + const parsed = ISerializer.grammar.KeyBinding.parse(v); + if (parsed.status) { + return parsed.value + } + } + throw new Error("Unexpected key value") + }); + + super(target, blueprint, options); + + this.#activationKeys = this.options.activationKeys ?? []; + + const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; + const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; + const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; + + let self = this; + /** @param {KeyboardEvent} e */ + this.keyDownHandler = e => { + if ( + self.#activationKeys.some(keyEntry => + wantsShift(keyEntry) == e.shiftKey + && wantsCtrl(keyEntry) == e.ctrlKey + && wantsAlt(keyEntry) == e.altKey + && this.blueprint.settings.Keys[keyEntry.Key] == e.code + )) { + self.fire(); + document.removeEventListener("keydown", self.keyDownHandler); + document.addEventListener("keyup", self.keyUpHandler); + } + }; + + /** @param {KeyboardEvent} e */ + this.keyUpHandler = e => { + if ( + self.#activationKeys.some(keyEntry => + keyEntry.bShift && e.key == "Shift" + || keyEntry.bCtrl && e.key == "Control" + || keyEntry.bAlt && e.key == "Alt" + || keyEntry.bCmd && e.key == "Meta" // Unsure about this, what key is that? + || this.blueprint.settings.Keys[keyEntry.Key] == e.code + )) { + self.unfire(); + document.removeEventListener("keyup", this.keyUpHandler); + document.addEventListener("keydown", this.keyDownHandler); + } + }; + + } + + listenEvents() { + document.addEventListener("keydown", this.keyDownHandler); + } + + unlistenEvents() { + document.removeEventListener("keydown", this.keyDownHandler); + } + + // Subclasses will want to override + + fire() { + } + + unfire() { + } } // @ts-check @@ -2007,72 +2007,72 @@ class KeyboardCanc extends IKeyboardShortcut { } } -// @ts-check - -class IPointing extends IContext { - - constructor(target, blueprint, options) { - super(target, blueprint, options); - this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement; - } - - /** - * @param {MouseEvent} mouseEvent - */ - locationFromEvent(mouseEvent) { - return this.blueprint.compensateTranslation( - Utility.convertLocation( - [mouseEvent.clientX, mouseEvent.clientY], - this.movementSpace)) - } +// @ts-check + +class IPointing extends IContext { + + constructor(target, blueprint, options) { + super(target, blueprint, options); + this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement; + } + + /** + * @param {MouseEvent} mouseEvent + */ + locationFromEvent(mouseEvent) { + return this.blueprint.compensateTranslation( + Utility.convertLocation( + [mouseEvent.clientX, mouseEvent.clientY], + this.movementSpace)) + } } -// @ts-check - -class IMouseWheel extends IPointing { - - /** @type {(e: WheelEvent) => void} */ - #mouseWheelHandler - - /** @type {(e: WheelEvent) => void} */ - #mouseParentWheelHandler - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options) { - options.wantsFocusCallback = true; - super(target, blueprint, options); - this.looseTarget = options?.looseTarget ?? true; - let self = this; - - this.#mouseWheelHandler = e => { - e.preventDefault(); - const location = self.locationFromEvent(e); - self.wheel(Math.sign(e.deltaY), location); - }; - this.#mouseParentWheelHandler = e => e.preventDefault(); - - if (this.blueprint.focused) { - this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); - } - } - - listenEvents() { - this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); - } - - unlistenEvents() { - this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); - } - - /* Subclasses will override the following method */ - wheel(variation, location) { - } +// @ts-check + +class IMouseWheel extends IPointing { + + /** @type {(e: WheelEvent) => void} */ + #mouseWheelHandler + + /** @type {(e: WheelEvent) => void} */ + #mouseParentWheelHandler + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options) { + options.wantsFocusCallback = true; + super(target, blueprint, options); + this.looseTarget = options?.looseTarget ?? true; + let self = this; + + this.#mouseWheelHandler = e => { + e.preventDefault(); + const location = self.locationFromEvent(e); + self.wheel(Math.sign(e.deltaY), location); + }; + this.#mouseParentWheelHandler = e => e.preventDefault(); + + if (this.blueprint.focused) { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + } + } + + listenEvents() { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); + } + + unlistenEvents() { + this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); + } + + /* Subclasses will override the following method */ + wheel(variation, location) { + } } // @ts-check @@ -2104,34 +2104,34 @@ class Zoom extends IMouseWheel { } } -// @ts-check - -class KeyboardEnableZoom extends IKeyboardShortcut { - - /** @type {} */ - #zoomInputObject - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: blueprint.settings.enableZoomIn - }; - super(target, blueprint, options); - } - - fire() { - this.#zoomInputObject = this.blueprint.getInputObject(Zoom); - this.#zoomInputObject.enableZoonIn = true; - } - - unfire() { - this.#zoomInputObject.enableZoonIn = false; - } +// @ts-check + +class KeyboardEnableZoom extends IKeyboardShortcut { + + /** @type {} */ + #zoomInputObject + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options = { + ...options, + activationKeys: blueprint.settings.enableZoomIn + }; + super(target, blueprint, options); + } + + fire() { + this.#zoomInputObject = this.blueprint.getInputObject(Zoom); + this.#zoomInputObject.enableZoonIn = true; + } + + unfire() { + this.#zoomInputObject.enableZoonIn = false; + } } // @ts-check @@ -2322,360 +2322,362 @@ class LinkTemplate extends ITemplate { } } -// @ts-check - -/** - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("./LinkMessageElement").default} LinkMessageElement - */ -class LinkElement extends IElement { - - static tagName = "ueb-link" - /** @type {PinElement} */ - #source - /** @type {PinElement} */ - #destination - #nodeDeleteHandler - #nodeDragSourceHandler - #nodeDragDestinatonHandler - sourceLocation = [0, 0] - /** @type {SVGPathElement} */ - pathElement - /** @type {LinkMessageElement} */ - linkMessageElement - originatesFromInput = false - destinationLocation = [0, 0] - - /** - * @param {PinElement} source - * @param {PinElement} destination - */ - constructor(source, destination) { - super({}, new LinkTemplate()); - /** @type {import("../template/LinkTemplate").default} */ - this.template; - const self = this; - this.#nodeDeleteHandler = _ => self.remove(); - this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value); - this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value); - if (source) { - this.setSourcePin(source); - } - if (destination) { - this.setDestinationPin(destination); - } - if (source && destination) { - this.#linkPins(); - } - } - - #linkPins() { - this.#source.linkTo(this.#destination); - this.#destination.linkTo(this.#source); - } - - #unlinkPins() { - if (this.#source && this.#destination) { - this.#source.unlinkFrom(this.#destination); - this.#destination.unlinkFrom(this.#source); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.#unlinkPins(); - } - - /** - * @returns {Number[]} - */ - getSourceLocation() { - return this.sourceLocation - } - - /** - * @param {Number[]} offset - */ - addSourceLocation(offset) { - const location = [ - this.sourceLocation[0] + offset[0], - this.sourceLocation[1] + offset[1] - ]; - this.sourceLocation = location; - this.template.applyFullLocation(this); - } - - /** - * @param {Number[]} location - */ - setSourceLocation(location = null) { - if (location == null) { - location = this.#source.template.getLinkLocation(this.#source); - } - this.sourceLocation = location; - this.template.applySourceLocation(this); - } - - /** - * @returns {Number[]} - */ - getDestinationLocation() { - return this.destinationLocation - } - - /** - * @param {Number[]} offset - */ - addDestinationLocation(offset) { - const location = [ - this.destinationLocation[0] + offset[0], - this.destinationLocation[1] + offset[1] - ]; - this.setDestinationLocation(location); - } - - /** - * @param {Number[]} location - */ - setDestinationLocation(location = null) { - if (location == null) { - location = this.#destination.template.getLinkLocation(this.#destination); - } - this.destinationLocation = location; - this.template.applyFullLocation(this); - } - - /** - * @returns {PinElement} - */ - getSourcePin() { - return this.#source - } - - /** - * @param {PinElement} pin - */ - setSourcePin(pin) { - if (this.#source) { - const nodeElement = this.#source.getNodeElement(); - nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); - if (this.#destination) { - this.#unlinkPins(); - } - } - this.#source = pin; - if (this.#source) { - const nodeElement = this.#source.getNodeElement(); - this.originatesFromInput = pin.isInput(); - nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); - this.setSourceLocation(); - if (this.#destination) { - this.#linkPins(); - } - } - } - - /** - * @returns {PinElement} - */ - getDestinationPin() { - return this.#destination - } - - /** - * @param {PinElement} pin - */ - setDestinationPin(pin) { - if (this.#destination) { - const nodeElement = this.#destination.getNodeElement(); - nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); - if (this.#source) { - this.#unlinkPins(); - } - } - this.#destination = pin; - if (this.#destination) { - const nodeElement = this.#destination.getNodeElement(); - nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); - nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); - this.setDestinationLocation(); - if (this.#source) { - this.#linkPins(); - } - } - } - - /** - * @param {LinkMessageElement} linkMessage - */ - setLinkMessage(linkMessage) { - if (linkMessage) { - this.template.applyLinkMessage(this, linkMessage); - } else if (this.linkMessageElement) { - this.linkMessageElement.remove(); - this.linkMessageElement = null; - } - } - - startDragging() { - this.template.applyStartDragging(this); - } - - finishDragging() { - this.template.applyFinishDragging(this); - } -} - +// @ts-check + +/** + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("./LinkMessageElement").default} LinkMessageElement + */ +class LinkElement extends IElement { + + static tagName = "ueb-link" + /** @type {PinElement} */ + #source + /** @type {PinElement} */ + #destination + #nodeDeleteHandler + #nodeDragSourceHandler + #nodeDragDestinatonHandler + sourceLocation = [0, 0] + /** @type {SVGPathElement} */ + pathElement + /** @type {LinkMessageElement} */ + linkMessageElement + originatesFromInput = false + destinationLocation = [0, 0] + + /** + * @param {PinElement} source + * @param {PinElement} destination + */ + constructor(source, destination) { + super({}, new LinkTemplate()); + /** @type {import("../template/LinkTemplate").default} */ + this.template; + const self = this; + this.#nodeDeleteHandler = _ => self.remove(); + this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value); + this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value); + if (source) { + this.setSourcePin(source); + } + if (destination) { + this.setDestinationPin(destination); + } + if (source && destination) { + this.#linkPins(); + } + } + + #linkPins() { + this.#source.linkTo(this.#destination); + this.#destination.linkTo(this.#source); + } + + #unlinkPins() { + if (this.#source && this.#destination) { + this.#source.unlinkFrom(this.#destination); + this.#destination.unlinkFrom(this.#source); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.#unlinkPins(); + } + + /** + * @returns {Number[]} + */ + getSourceLocation() { + return this.sourceLocation + } + + /** + * @param {Number[]} offset + */ + addSourceLocation(offset) { + const location = [ + this.sourceLocation[0] + offset[0], + this.sourceLocation[1] + offset[1] + ]; + this.sourceLocation = location; + this.template.applyFullLocation(this); + } + + /** + * @param {Number[]} location + */ + setSourceLocation(location = null) { + if (location == null) { + location = this.#source.template.getLinkLocation(this.#source); + } + this.sourceLocation = location; + this.template.applySourceLocation(this); + } + + /** + * @returns {Number[]} + */ + getDestinationLocation() { + return this.destinationLocation + } + + /** + * @param {Number[]} offset + */ + addDestinationLocation(offset) { + const location = [ + this.destinationLocation[0] + offset[0], + this.destinationLocation[1] + offset[1] + ]; + this.setDestinationLocation(location); + } + + /** + * @param {Number[]} location + */ + setDestinationLocation(location = null) { + if (location == null) { + location = this.#destination.template.getLinkLocation(this.#destination); + } + this.destinationLocation = location; + this.template.applyFullLocation(this); + } + + /** + * @returns {PinElement} + */ + getSourcePin() { + return this.#source + } + + /** + * @param {PinElement} pin + */ + setSourcePin(pin) { + if (this.#source) { + const settings = this.#source.blueprint.settings; + const nodeElement = this.#source.getNodeElement(); + nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); + if (this.#destination) { + this.#unlinkPins(); + } + } + this.#source = pin; + if (this.#source) { + const nodeElement = this.#source.getNodeElement(); + const settings = this.#source.blueprint.settings; + this.originatesFromInput = pin.isInput(); + nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler); + this.setSourceLocation(); + if (this.#destination) { + this.#linkPins(); + } + } + } + + /** + * @returns {PinElement} + */ + getDestinationPin() { + return this.#destination + } + + /** + * @param {PinElement} pin + */ + setDestinationPin(pin) { + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + if (this.#source) { + this.#unlinkPins(); + } + } + this.#destination = pin; + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + this.setDestinationLocation(); + if (this.#source) { + this.#linkPins(); + } + } + } + + /** + * @param {LinkMessageElement} linkMessage + */ + setLinkMessage(linkMessage) { + if (linkMessage) { + this.template.applyLinkMessage(this, linkMessage); + } else if (this.linkMessageElement) { + this.linkMessageElement.remove(); + this.linkMessageElement = null; + } + } + + startDragging() { + this.template.applyStartDragging(this); + } + + finishDragging() { + this.template.applyFinishDragging(this); + } +} + customElements.define(LinkElement.tagName, LinkElement); -// @ts-check - -/** - * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses. - */ -class IMouseClickDrag extends IPointing { - - /** @type {(e: MouseEvent) => void} */ - #mouseDownHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseStartedMovingHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseMoveHandler - - /** @type {(e: MouseEvent) => void} */ - #mouseUpHandler - - #trackingMouse = false - - started = false - - constructor(target, blueprint, options) { - super(target, blueprint, options); - this.clickButton = options?.clickButton ?? 0; - this.exitAnyButton = options?.exitAnyButton ?? true; - this.moveEverywhere = options?.moveEverywhere ?? false; - this.looseTarget = options?.looseTarget ?? false; - this.consumeClickEvent = options?.consumeClickEvent ?? true; - this.clickedPosition = [0, 0]; - - const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace; - let self = this; - - this.#mouseDownHandler = e => { - this.blueprint.setFocused(true); - switch (e.button) { - case self.clickButton: - // Either doesn't matter or consider the click only when clicking on the parent, not descandants - if (self.looseTarget || e.target == e.currentTarget) { - if (this.consumeClickEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Attach the listeners - movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler); - document.addEventListener("mouseup", self.#mouseUpHandler); - self.clickedPosition = self.locationFromEvent(e); - self.clicked(self.clickedPosition); - } - break - default: - if (!self.exitAnyButton) { - self.#mouseUpHandler(e); - } - break - } - }; - - this.#mouseStartedMovingHandler = e => { - if (this.consumeClickEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Delegate from now on to self.#mouseMoveHandler - movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); - movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler); - // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin); - self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; - // Do actual actions - self.startDrag(); - self.started = true; - }; - - this.#mouseMoveHandler = e => { - if (this.consumeClickEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - const location = self.locationFromEvent(e); - const movement = [e.movementX, e.movementY]; - self.dragTo(location, movement); - if (self.#trackingMouse) { - self.blueprint.mousePosition = self.locationFromEvent(e); - } - }; - - this.#mouseUpHandler = e => { - if (!self.exitAnyButton || e.button == self.clickButton) { - if (this.consumeClickEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Remove the handlers of "mousemove" and "mouseup" - movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); - movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler); - document.removeEventListener("mouseup", self.#mouseUpHandler); - if (self.started) { - self.endDrag(); - } - self.unclicked(); - if (self.#trackingMouse) { - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end); - this.target.dispatchEvent(dragEvent); - self.#trackingMouse = false; - } - self.started = false; - } - }; - - this.target.addEventListener("mousedown", this.#mouseDownHandler); - if (this.clickButton == 2) { - this.target.addEventListener("contextmenu", e => e.preventDefault()); - } - } - - getEvent(eventName) { - return new CustomEvent(eventName, { - detail: { - tracker: this - }, - bubbles: true, - cancelable: true - }) - } - - unlistenDOMElement() { - super.unlistenDOMElement(); - this.target.removeEventListener("mousedown", this.#mouseDownHandler); - if (this.clickButton == 2) ; - } - - /* Subclasses will override the following methods */ - clicked(location) { - } - - startDrag(location) { - } - - dragTo(location, movement) { - } - - endDrag() { - } - - unclicked(location) { - } +// @ts-check + +/** + * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses. + */ +class IMouseClickDrag extends IPointing { + + /** @type {(e: MouseEvent) => void} */ + #mouseDownHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseStartedMovingHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseMoveHandler + + /** @type {(e: MouseEvent) => void} */ + #mouseUpHandler + + #trackingMouse = false + + started = false + + constructor(target, blueprint, options) { + super(target, blueprint, options); + this.clickButton = options?.clickButton ?? 0; + this.exitAnyButton = options?.exitAnyButton ?? true; + this.moveEverywhere = options?.moveEverywhere ?? false; + this.looseTarget = options?.looseTarget ?? false; + this.consumeClickEvent = options?.consumeClickEvent ?? true; + this.clickedPosition = [0, 0]; + + const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace; + let self = this; + + this.#mouseDownHandler = e => { + this.blueprint.setFocused(true); + switch (e.button) { + case self.clickButton: + // Either doesn't matter or consider the click only when clicking on the parent, not descandants + if (self.looseTarget || e.target == e.currentTarget) { + if (this.consumeClickEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Attach the listeners + movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler); + document.addEventListener("mouseup", self.#mouseUpHandler); + self.clickedPosition = self.locationFromEvent(e); + self.clicked(self.clickedPosition); + } + break + default: + if (!self.exitAnyButton) { + self.#mouseUpHandler(e); + } + break + } + }; + + this.#mouseStartedMovingHandler = e => { + if (this.consumeClickEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Delegate from now on to self.#mouseMoveHandler + movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); + movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler); + // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false + const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin); + self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; + // Do actual actions + self.startDrag(); + self.started = true; + }; + + this.#mouseMoveHandler = e => { + if (this.consumeClickEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + const location = self.locationFromEvent(e); + const movement = [e.movementX, e.movementY]; + self.dragTo(location, movement); + if (self.#trackingMouse) { + self.blueprint.mousePosition = self.locationFromEvent(e); + } + }; + + this.#mouseUpHandler = e => { + if (!self.exitAnyButton || e.button == self.clickButton) { + if (this.consumeClickEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Remove the handlers of "mousemove" and "mouseup" + movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler); + movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler); + document.removeEventListener("mouseup", self.#mouseUpHandler); + if (self.started) { + self.endDrag(); + } + self.unclicked(); + if (self.#trackingMouse) { + const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end); + this.target.dispatchEvent(dragEvent); + self.#trackingMouse = false; + } + self.started = false; + } + }; + + this.target.addEventListener("mousedown", this.#mouseDownHandler); + if (this.clickButton == 2) { + this.target.addEventListener("contextmenu", e => e.preventDefault()); + } + } + + getEvent(eventName) { + return new CustomEvent(eventName, { + detail: { + tracker: this + }, + bubbles: true, + cancelable: true + }) + } + + unlistenDOMElement() { + super.unlistenDOMElement(); + this.target.removeEventListener("mousedown", this.#mouseDownHandler); + if (this.clickButton == 2) ; + } + + /* Subclasses will override the following methods */ + clicked(location) { + } + + startDrag(location) { + } + + dragTo(location, movement) { + } + + endDrag() { + } + + unclicked(location) { + } } // @ts-check @@ -2818,193 +2820,193 @@ class MouseMoveNodes extends IMouseClickDrag { } } -// @ts-check - -/** - * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate - * @typedef {import("../entity/IntegerEntity").default} IntegerEntity - */ -class ISelectableDraggableElement extends IElement { - - constructor(...args) { - super(...args); - this.dragObject = null; - this.location = [0, 0]; - this.selected = false; - /** @type {SelectableDraggableTemplate} */ - this.template; - - let self = this; - this.dragHandler = (e) => { - self.addLocation(e.detail.value); - }; - } - - createInputObjects() { - return [ - new MouseMoveNodes(this, this.blueprint, { - looseTarget: true - }), - ] - } - - /** - * @param {Number[]} value - */ - setLocation(value = [0, 0]) { - const d = [value[0] - this.location[0], value[1] - this.location[1]]; - this.location = value; - this.template.applyLocation(this); - if (this.blueprint) { - const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { - detail: { - value: d - }, - bubbles: false, - cancelable: true - }); - this.dispatchEvent(dragLocalEvent); - } - } - - addLocation(value) { - this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]); - } - - setSelected(value = true) { - if (this.selected == value) { - return - } - this.selected = value; - if (this.selected) { - this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); - } else { - this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); - } - this.template.applySelected(this); - } - - dispatchDragEvent(value) { - if (!this.selected) { - this.blueprint.unselectAll(); - this.setSelected(true); - } - const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { - detail: { - value: value - }, - bubbles: true, - cancelable: true - }); - this.dispatchEvent(dragEvent); - } - - snapToGrid() { - let snappedLocation = this.blueprint.snapToGrid(this.location); - if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) { - this.setLocation(snappedLocation); - } - } +// @ts-check + +/** + * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate + * @typedef {import("../entity/IntegerEntity").default} IntegerEntity + */ +class ISelectableDraggableElement extends IElement { + + constructor(...args) { + super(...args); + this.dragObject = null; + this.location = [0, 0]; + this.selected = false; + /** @type {SelectableDraggableTemplate} */ + this.template; + + let self = this; + this.dragHandler = (e) => { + self.addLocation(e.detail.value); + }; + } + + createInputObjects() { + return [ + new MouseMoveNodes(this, this.blueprint, { + looseTarget: true + }), + ] + } + + /** + * @param {Number[]} value + */ + setLocation(value = [0, 0]) { + const d = [value[0] - this.location[0], value[1] - this.location[1]]; + this.location = value; + this.template.applyLocation(this); + if (this.blueprint) { + const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { + detail: { + value: d + }, + bubbles: false, + cancelable: true + }); + this.dispatchEvent(dragLocalEvent); + } + } + + addLocation(value) { + this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]); + } + + setSelected(value = true) { + if (this.selected == value) { + return + } + this.selected = value; + if (this.selected) { + this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + } else { + this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + } + this.template.applySelected(this); + } + + dispatchDragEvent(value) { + if (!this.selected) { + this.blueprint.unselectAll(); + this.setSelected(true); + } + const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { + detail: { + value: value + }, + bubbles: true, + cancelable: true + }); + this.dispatchEvent(dragEvent); + } + + snapToGrid() { + let snappedLocation = this.blueprint.snapToGrid(this.location); + if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) { + this.setLocation(snappedLocation); + } + } } -// @ts-check - -/** - * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement - */ -class LinkMessageTemplate extends ITemplate { - - /** - * @param {LinkMessageElement} linkMessage - */ - render(linkMessage) { - return html` - - - ` - } - - /** - * Applies the style to the element. - * @param {LinkMessageElement} linkMessage - */ - apply(linkMessage) { - super.apply(linkMessage); - const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( - linkMessage.linkElement.getSourcePin(), - linkMessage.linkElement.getDestinationPin() - ); - linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); - if (linkMessage.linkElement) { - linkMessageSetup(); - } else { - window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage); - } - } - +// @ts-check + +/** + * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement + */ +class LinkMessageTemplate extends ITemplate { + + /** + * @param {LinkMessageElement} linkMessage + */ + render(linkMessage) { + return html` + + + ` + } + + /** + * Applies the style to the element. + * @param {LinkMessageElement} linkMessage + */ + apply(linkMessage) { + super.apply(linkMessage); + const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( + linkMessage.linkElement.getSourcePin(), + linkMessage.linkElement.getDestinationPin() + ); + linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); + if (linkMessage.linkElement) { + linkMessageSetup(); + } else { + window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage); + } + } + +} + +// @ts-check + +/** + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("./LinkElement").default} LinkElement + * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval + */ +class LinkMessageElement extends IElement { + + static tagName = "ueb-link-message" + static convertType = _ => new LinkMessageElement( + "ueb-icon-conver-type", + /** @type {LinkRetrieval} */ + (s, d) => `Convert ${s.getType()} to ${d.getType()}.` + ) + static correct = _ => new LinkMessageElement( + "ueb-icon-correct", + /** @type {LinkRetrieval} */ + (s, d) => "" + ) + static directionsIncompatible = _ => new LinkMessageElement( + "ueb-icon-directions-incompatible", + /** @type {LinkRetrieval} */ + (s, d) => "Directions are not compatbile." + ) + static placeNode = _ => new LinkMessageElement( + "ueb-icon-place-node", + /** @type {LinkRetrieval} */ + (s, d) => "Place a new node." + ) + static replaceLink = _ => new LinkMessageElement( + "ueb-icon-replace-link", + /** @type {LinkRetrieval} */ + (s, d) => "Replace existing input connections." + ) + static sameNode = _ => new LinkMessageElement( + "ueb-icon-same-node", + /** @type {LinkRetrieval} */ + (s, d) => "Both are on the same node." + ) + static typesIncompatible = _ => new LinkMessageElement( + "ueb-icon-types-incompatible", + /** @type {LinkRetrieval} */ + (s, d) => `${s.getType()} is not compatible with ${d.getType()}.` + ) + + /** @type {String} */ + icon + /** @type {String} */ + message + /** @type {LinkElement} */ + linkElement + + constructor(icon, message) { + super({}, new LinkMessageTemplate()); + this.icon = icon; + this.message = message; + } + } -// @ts-check - -/** - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("./LinkElement").default} LinkElement - * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval - */ -class LinkMessageElement extends IElement { - - static tagName = "ueb-link-message" - static convertType = _ => new LinkMessageElement( - "ueb-icon-conver-type", - /** @type {LinkRetrieval} */ - (s, d) => `Convert ${s.getType()} to ${d.getType()}.` - ) - static correct = _ => new LinkMessageElement( - "ueb-icon-correct", - /** @type {LinkRetrieval} */ - (s, d) => "" - ) - static directionsIncompatible = _ => new LinkMessageElement( - "ueb-icon-directions-incompatible", - /** @type {LinkRetrieval} */ - (s, d) => "Directions are not compatbile." - ) - static placeNode = _ => new LinkMessageElement( - "ueb-icon-place-node", - /** @type {LinkRetrieval} */ - (s, d) => "Place a new node." - ) - static replaceLink = _ => new LinkMessageElement( - "ueb-icon-replace-link", - /** @type {LinkRetrieval} */ - (s, d) => "Replace existing input connections." - ) - static sameNode = _ => new LinkMessageElement( - "ueb-icon-same-node", - /** @type {LinkRetrieval} */ - (s, d) => "Both are on the same node." - ) - static typesIncompatible = _ => new LinkMessageElement( - "ueb-icon-types-incompatible", - /** @type {LinkRetrieval} */ - (s, d) => `${s.getType()} is not compatible with ${d.getType()}.` - ) - - /** @type {String} */ - icon - /** @type {String} */ - message - /** @type {LinkElement} */ - linkElement - - constructor(icon, message) { - super({}, new LinkMessageTemplate()); - this.icon = icon; - this.message = message; - } - -} - customElements.define(LinkMessageElement.tagName, LinkMessageElement); // @ts-check @@ -3206,185 +3208,185 @@ class PinTemplate extends ITemplate { } } -// @ts-check - -/** - * @typedef {import("../element/PinElement").default} PinElement - */ -class ExecPinTemplate extends PinTemplate { - - /** - * @param {PinElement} pin - */ - renderIcon(pin) { - return html` - - - - ` - } +// @ts-check + +/** + * @typedef {import("../element/PinElement").default} PinElement + */ +class ExecPinTemplate extends PinTemplate { + + /** + * @param {PinElement} pin + */ + renderIcon(pin) { + return html` + + + + ` + } } -// @ts-check - -/** - * @typedef {import("../element/PinElement").default} PinElement - */ -class StringPinTemplate extends PinTemplate { - - /** - * @param {PinElement} pin - */ - renderInput(pin) { - const stopEventPropagation = "e => stopPropagation()"; - return html` - - - - ` - } +// @ts-check + +/** + * @typedef {import("../element/PinElement").default} PinElement + */ +class StringPinTemplate extends PinTemplate { + + /** + * @param {PinElement} pin + */ + renderInput(pin) { + const stopEventPropagation = "e => stopPropagation()"; + return html` + + + + ` + } +} + +// @ts-check + +/** + * @typedef {import("../entity/GuidEntity").default} GuidEntity + * @typedef {import("../entity/PinEntity").default} PinEntity + * @typedef {import("./NodeElement").default} NodeElement + */ +class PinElement extends IElement { + + static tagName = "ueb-pin" + + static #typeTemplateMap = { + "exec": ExecPinTemplate, + "string": StringPinTemplate, + } + + /** @type {NodeElement} */ + nodeElement + + /** @type {HTMLElement} */ + clickableElement + + /** @type {String} */ + #color + + constructor(entity) { + super( + entity, + new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)() + ); + + /** @type {PinEntity} */ + this.entity; + + /** @type {PinTemplate} */ + this.template; + } + + connectedCallback() { + super.connectedCallback(); + this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color"); + } + + createInputObjects() { + return [ + new MouseCreateLink(this.clickableElement, this.blueprint, { + moveEverywhere: true, + looseTarget: true + }), + ] + } + + /** @return {GuidEntity} */ + GetPinId() { + return this.entity.PinId + } + + /** @return {String} */ + GetPinIdValue() { + return this.GetPinId().value + } + + /** + * @returns {String} + */ + getPinName() { + return this.entity.PinName + } + + /** + * @returns {String} + */ + getPinDisplayName() { + return this.entity.PinName + } + + isInput() { + return this.entity.isInput() + } + + isOutput() { + return this.entity.isOutput() + } + + isConnected() { + return this.entity.isConnected() + } + + getType() { + return this.entity.getType() + } + + getClickableElement() { + return this.clickableElement + } + + getColor() { + return this.#color + } + + /** + * Returns The exact location where the link originates from or arrives at. + * @returns {Number[]} The location array + */ + getLinkLocation() { + return this.template.getLinkLocation(this) + } + + /** + * @returns {NodeElement} + */ + getNodeElement() { + return this.closest("ueb-node") + } + + getLinks() { + return this.entity.LinkedTo?.map(pinReference => + pinReference + ) ?? [] + } + + /** + * @param {PinElement} targetPinElement + */ + linkTo(targetPinElement) { + this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); + this.template.applyConnected(this); + } + + /** + * @param {PinElement} targetPinElement + */ + unlinkFrom(targetPinElement) { + this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); + this.template.applyConnected(this); + } } -// @ts-check - -/** - * @typedef {import("../entity/GuidEntity").default} GuidEntity - * @typedef {import("../entity/PinEntity").default} PinEntity - * @typedef {import("./NodeElement").default} NodeElement - */ -class PinElement extends IElement { - - static tagName = "ueb-pin" - - static #typeTemplateMap = { - "exec": ExecPinTemplate, - "string": StringPinTemplate, - } - - /** @type {NodeElement} */ - nodeElement - - /** @type {HTMLElement} */ - clickableElement - - /** @type {String} */ - #color - - constructor(entity) { - super( - entity, - new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)() - ); - - /** @type {PinEntity} */ - this.entity; - - /** @type {PinTemplate} */ - this.template; - } - - connectedCallback() { - super.connectedCallback(); - this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color"); - } - - createInputObjects() { - return [ - new MouseCreateLink(this.clickableElement, this.blueprint, { - moveEverywhere: true, - looseTarget: true - }), - ] - } - - /** @return {GuidEntity} */ - GetPinId() { - return this.entity.PinId - } - - /** @return {String} */ - GetPinIdValue() { - return this.GetPinId().value - } - - /** - * @returns {String} - */ - getPinName() { - return this.entity.PinName - } - - /** - * @returns {String} - */ - getPinDisplayName() { - return this.entity.PinName - } - - isInput() { - return this.entity.isInput() - } - - isOutput() { - return this.entity.isOutput() - } - - isConnected() { - return this.entity.isConnected() - } - - getType() { - return this.entity.getType() - } - - getClickableElement() { - return this.clickableElement - } - - getColor() { - return this.#color - } - - /** - * Returns The exact location where the link originates from or arrives at. - * @returns {Number[]} The location array - */ - getLinkLocation() { - return this.template.getLinkLocation(this) - } - - /** - * @returns {NodeElement} - */ - getNodeElement() { - return this.closest("ueb-node") - } - - getLinks() { - return this.entity.LinkedTo?.map(pinReference => - pinReference - ) ?? [] - } - - /** - * @param {PinElement} targetPinElement - */ - linkTo(targetPinElement) { - this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); - this.template.applyConnected(this); - } - - /** - * @param {PinElement} targetPinElement - */ - unlinkFrom(targetPinElement) { - this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity); - this.template.applyConnected(this); - } -} - customElements.define(PinElement.tagName, PinElement); // @ts-check @@ -3483,73 +3485,73 @@ class NodeTemplate extends SelectableDraggableTemplate { } } -// @ts-check - -class NodeElement extends ISelectableDraggableElement { - - static tagName = "ueb-node" - - /** - * @param {ObjectEntity} entity - */ - constructor(entity) { - super(entity, new NodeTemplate()); - /** @type {ObjectEntity} */ - this.entity; - /** @type {NodeTemplate} */ - this.template; - this.dragLinkObjects = []; - super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]); - } - - static fromSerializedObject(str) { - let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); - return new NodeElement(entity) - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.dispatchDeleteEvent(); - } - - getNodeName() { - return this.entity.getName() - } - - getPinElements() { - return this.template.getPinElements(this) - } - - /** - * @returns {PinEntity[]} - */ - getPinEntities() { - return this.entity.CustomProperties.filter(v => v instanceof PinEntity) - } - - connectedCallback() { - this.getAttribute("type")?.trim(); - super.connectedCallback(); - } - - setLocation(value = [0, 0]) { - let nodeType = this.entity.NodePosX.constructor; - // @ts-expect-error - this.entity.NodePosX = new nodeType(value[0]); - // @ts-expect-error - this.entity.NodePosY = new nodeType(value[1]); - super.setLocation(value); - } - - dispatchDeleteEvent(value) { - let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { - bubbles: true, - cancelable: true, - }); - this.dispatchEvent(deleteEvent); - } -} - +// @ts-check + +class NodeElement extends ISelectableDraggableElement { + + static tagName = "ueb-node" + + /** + * @param {ObjectEntity} entity + */ + constructor(entity) { + super(entity, new NodeTemplate()); + /** @type {ObjectEntity} */ + this.entity; + /** @type {NodeTemplate} */ + this.template; + this.dragLinkObjects = []; + super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]); + } + + static fromSerializedObject(str) { + let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); + return new NodeElement(entity) + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.dispatchDeleteEvent(); + } + + getNodeName() { + return this.entity.getName() + } + + getPinElements() { + return this.template.getPinElements(this) + } + + /** + * @returns {PinEntity[]} + */ + getPinEntities() { + return this.entity.CustomProperties.filter(v => v instanceof PinEntity) + } + + connectedCallback() { + this.getAttribute("type")?.trim(); + super.connectedCallback(); + } + + setLocation(value = [0, 0]) { + let nodeType = this.entity.NodePosX.constructor; + // @ts-expect-error + this.entity.NodePosX = new nodeType(value[0]); + // @ts-expect-error + this.entity.NodePosY = new nodeType(value[1]); + super.setLocation(value); + } + + dispatchDeleteEvent(value) { + let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { + bubbles: true, + cancelable: true, + }); + this.dispatchEvent(deleteEvent); + } +} + customElements.define(NodeElement.tagName, NodeElement); // @ts-check diff --git a/js/element/LinkElement.js b/js/element/LinkElement.js index 680c208..0e2c28a 100644 --- a/js/element/LinkElement.js +++ b/js/element/LinkElement.js @@ -137,9 +137,10 @@ export default class LinkElement extends IElement { */ setSourcePin(pin) { if (this.#source) { + const settings = this.#source.blueprint.settings const nodeElement = this.#source.getNodeElement() - nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler) - nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragSourceHandler) + nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler) if (this.#destination) { this.#unlinkPins() } @@ -147,9 +148,10 @@ export default class LinkElement extends IElement { this.#source = pin if (this.#source) { const nodeElement = this.#source.getNodeElement() + const settings = this.#source.blueprint.settings this.originatesFromInput = pin.isInput() - nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler) - nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragSourceHandler) + nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler) this.setSourceLocation() if (this.#destination) { this.#linkPins() diff --git a/package.json b/package.json index c91e39c..81c19b2 100755 --- a/package.json +++ b/package.json @@ -34,4 +34,4 @@ "dependencies": { "parsimmon": "^1.18.0" } -} \ No newline at end of file +}