diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index e8ef9ce..bf37849 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -1,5 +1,151 @@ // @ts-check +class Configuration { + static deleteNodesKeyboardKey = "Delete" + static editTextEventName = { + begin: "ueb-edit-text-begin", + end: "ueb-edit-text-end", + } + static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom + static expandGridSize = 400 + static focusEventName = { + begin: "blueprint-focus", + end: "blueprint-unfocus", + } + static fontSize = "12px" + static gridAxisLineColor = "black" + static gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event + static gridLineColor = "#353535" + static gridLineWidth = 1 // pixel + static gridSet = 8 + static gridSetLineColor = "#161616" + static gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event + static gridSize = 16 // pixel + static keysSeparator = "+" + static linkCurveHeight = 15 // pixel + static linkCurveWidth = 80 // pixel + static linkMinWidth = 100 // pixel + /** + * @param {Number} start + * @param {Number} c1 + * @param {Number} c2 + */ + static linkRightSVGPath = (start, c1, c2) => { + let end = 100 - start; + return `M ${start} 0 C ${c1} 0, ${c2} 0, 50 50 S ${end - c1 + start} 100, ${end} 100` + } + static maxZoom = 7 + static minZoom = -12 + static nodeDeleteEventName = "ueb-node-delete" + static nodeDragEventName = "ueb-node-drag" + static nodeDragLocalEventName = "ueb-node-drag-local" + static nodeRadius = 8 // in pixel + static selectAllKeyboardKey = "(bCtrl=True,Key=A)" + static trackingMouseEventName = { + begin: "ueb-tracking-mouse-begin", + end: "ueb-tracking-mouse-end", + } + static ModifierKeys = [ + "Ctrl", + "Shift", + "Alt", + "Meta", + ] + static Keys = { + /* UE name: JS name */ + "Backspace": "Backspace", + "Tab": "Tab", + "LeftControl": "ControlLeft", + "RightControl": "ControlRight", + "LeftShift": "ShiftLeft", + "RightShift": "ShiftRight", + "LeftAlt": "AltLeft", + "RightAlt": "AltRight", + "Enter": "Enter", + "Pause": "Pause", + "CapsLock": "CapsLock", + "Escape": "Escape", + "Space": "Space", + "PageUp": "PageUp", + "PageDown": "PageDown", + "End": "End", + "Home": "Home", + "ArrowLeft": "Left", + "ArrowUp": "Up", + "ArrowRight": "Right", + "ArrowDown": "Down", + "PrintScreen": "PrintScreen", + "Insert": "Insert", + "Delete": "Delete", + "Zero": "Digit0", + "One": "Digit1", + "Two": "Digit2", + "Three": "Digit3", + "Four": "Digit4", + "Five": "Digit5", + "Six": "Digit6", + "Seven": "Digit7", + "Eight": "Digit8", + "Nine": "Digit9", + "A": "KeyA", + "B": "KeyB", + "C": "KeyC", + "D": "KeyD", + "E": "KeyE", + "F": "KeyF", + "G": "KeyG", + "H": "KeyH", + "I": "KeyI", + "K": "KeyK", + "L": "KeyL", + "M": "KeyM", + "N": "KeyN", + "O": "KeyO", + "P": "KeyP", + "Q": "KeyQ", + "R": "KeyR", + "S": "KeyS", + "T": "KeyT", + "U": "KeyU", + "V": "KeyV", + "W": "KeyW", + "X": "KeyX", + "Y": "KeyY", + "Z": "KeyZ", + "NumPadZero": "Numpad0", + "NumPadOne": "Numpad1", + "NumPadTwo": "Numpad2", + "NumPadThree": "Numpad3", + "NumPadFour": "Numpad4", + "NumPadFive": "Numpad5", + "NumPadSix": "Numpad6", + "NumPadSeven": "Numpad7", + "NumPadEight": "Numpad8", + "NumPadNine": "Numpad9", + "Multiply": "NumpadMultiply", + "Add": "NumpadAdd", + "Subtract": "NumpadSubtract", + "Decimal": "NumpadDecimal", + "Divide": "NumpadDivide", + "F1": "F1", + "F2": "F2", + "F3": "F3", + "F4": "F4", + "F5": "F5", + "F6": "F6", + "F7": "F7", + "F8": "F8", + "F9": "F9", + "F10": "F10", + "F11": "F11", + "F12": "F12", + "NumLock": "NumLock", + "ScrollLock": "ScrollLock", + } +} + +// @ts-check + /** * This solves the sole purpose of providing compression capability for html inside template literals strings. Check rollup.config.js function minifyHTML() */ @@ -41,10 +187,7 @@ const tagReplacement = { }; function sanitizeText(value) { - if (value.constructor === String) { - return value.replace(/[&<>'"]/g, tag => tagReplacement[tag]) - } - return value + return value.toString().replace(/[&<>'"]/g, tag => tagReplacement[tag]) } // @ts-check @@ -576,15 +719,15 @@ class BlueprintTemplate extends ITemplate { super.apply(blueprint); blueprint.classList.add("ueb", `ueb-zoom-${blueprint.zoom}`); Object.entries({ - "--ueb-font-size": sanitizeText(blueprint.settings.fontSize), - "--ueb-grid-size": `${sanitizeText(blueprint.settings.gridSize)}px`, - "--ueb-grid-line-width": `${sanitizeText(blueprint.settings.gridLineWidth)}px`, - "--ueb-grid-line-color": sanitizeText(blueprint.settings.gridLineColor), - "--ueb-grid-set": sanitizeText(blueprint.settings.gridSet), - "--ueb-grid-set-line-color": sanitizeText(blueprint.settings.gridSetLineColor), - "--ueb-grid-axis-line-color": sanitizeText(blueprint.settings.gridAxisLineColor), - "--ueb-node-radius": `${sanitizeText(blueprint.settings.nodeRadius)}px`, - "--ueb-link-min-width": sanitizeText(blueprint.settings.linkMinWidth) + "--ueb-font-size": sanitizeText(Configuration.fontSize), + "--ueb-grid-size": `${sanitizeText(Configuration.gridSize)}px`, + "--ueb-grid-line-width": `${sanitizeText(Configuration.gridLineWidth)}px`, + "--ueb-grid-line-color": sanitizeText(Configuration.gridLineColor), + "--ueb-grid-set": sanitizeText(Configuration.gridSet), + "--ueb-grid-set-line-color": sanitizeText(Configuration.gridSetLineColor), + "--ueb-grid-axis-line-color": sanitizeText(Configuration.gridAxisLineColor), + "--ueb-node-radius": `${sanitizeText(Configuration.nodeRadius)}px`, + "--ueb-link-min-width": sanitizeText(Configuration.linkMinWidth) }).forEach(entry => blueprint.style.setProperty(entry[0], entry[1])); blueprint.headerElement = blueprint.querySelector('.ueb-viewport-header'); blueprint.overlayElement = blueprint.querySelector('.ueb-viewport-overlay'); @@ -654,152 +797,6 @@ class BlueprintTemplate extends ITemplate { // @ts-check -class Configuration { - deleteNodesKeyboardKey = "Delete" - editTextEventName = { - begin: "ueb-edit-text-begin", - end: "ueb-edit-text-end", - } - enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom - expandGridSize = 400 - focusEventName = { - begin: "blueprint-focus", - end: "blueprint-unfocus", - } - fontSize = "12px" - gridAxisLineColor = "black" - gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event - gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event - gridLineColor = "#353535" - gridLineWidth = 1 // pixel - gridSet = 8 - gridSetLineColor = "#161616" - gridSize = 16 // pixel - keysSeparator = "+" - linkCurveHeight = 15 // pixel - linkCurveWidth = 80 // pixel - linkMinWidth = 100 // pixel - /** - * @param {Number} start - * @param {Number} c1 - * @param {Number} c2 - */ - linkRightSVGPath = (start, c1, c2) => { - let end = 100 - start; - return `M ${start} 0 C ${c1} 0, ${c2} 0, 50 50 S ${end - c1 + start} 100, ${end} 100` - } - maxZoom = 7 - minZoom = -12 - nodeDeleteEventName = "ueb-node-delete" - nodeDragEventName = "ueb-node-drag" - nodeDragLocalEventName = "ueb-node-drag-local" - nodeRadius = 8 // in pixel - selectAllKeyboardKey = "(bCtrl=True,Key=A)" - trackingMouseEventName = { - begin: "ueb-tracking-mouse-begin", - end: "ueb-tracking-mouse-end", - } - ModifierKeys = [ - "Ctrl", - "Shift", - "Alt", - "Meta", - ] - Keys = { - /* UE name: JS name */ - "Backspace": "Backspace", - "Tab": "Tab", - "LeftControl": "ControlLeft", - "RightControl": "ControlRight", - "LeftShift": "ShiftLeft", - "RightShift": "ShiftRight", - "LeftAlt": "AltLeft", - "RightAlt": "AltRight", - "Enter": "Enter", - "Pause": "Pause", - "CapsLock": "CapsLock", - "Escape": "Escape", - "Space": "Space", - "PageUp": "PageUp", - "PageDown": "PageDown", - "End": "End", - "Home": "Home", - "ArrowLeft": "Left", - "ArrowUp": "Up", - "ArrowRight": "Right", - "ArrowDown": "Down", - "PrintScreen": "PrintScreen", - "Insert": "Insert", - "Delete": "Delete", - "Zero": "Digit0", - "One": "Digit1", - "Two": "Digit2", - "Three": "Digit3", - "Four": "Digit4", - "Five": "Digit5", - "Six": "Digit6", - "Seven": "Digit7", - "Eight": "Digit8", - "Nine": "Digit9", - "A": "KeyA", - "B": "KeyB", - "C": "KeyC", - "D": "KeyD", - "E": "KeyE", - "F": "KeyF", - "G": "KeyG", - "H": "KeyH", - "I": "KeyI", - "K": "KeyK", - "L": "KeyL", - "M": "KeyM", - "N": "KeyN", - "O": "KeyO", - "P": "KeyP", - "Q": "KeyQ", - "R": "KeyR", - "S": "KeyS", - "T": "KeyT", - "U": "KeyU", - "V": "KeyV", - "W": "KeyW", - "X": "KeyX", - "Y": "KeyY", - "Z": "KeyZ", - "NumPadZero": "Numpad0", - "NumPadOne": "Numpad1", - "NumPadTwo": "Numpad2", - "NumPadThree": "Numpad3", - "NumPadFour": "Numpad4", - "NumPadFive": "Numpad5", - "NumPadSix": "Numpad6", - "NumPadSeven": "Numpad7", - "NumPadEight": "Numpad8", - "NumPadNine": "Numpad9", - "Multiply": "NumpadMultiply", - "Add": "NumpadAdd", - "Subtract": "NumpadSubtract", - "Decimal": "NumpadDecimal", - "Divide": "NumpadDivide", - "F1": "F1", - "F2": "F2", - "F3": "F3", - "F4": "F4", - "F5": "F5", - "F6": "F6", - "F7": "F7", - "F8": "F8", - "F9": "F9", - "F10": "F10", - "F11": "F11", - "F12": "F12", - "NumLock": "NumLock", - "ScrollLock": "ScrollLock", - } -} - -// @ts-check - /** * @typedef {import("../Blueprint").default} Blueprint */ @@ -839,21 +836,21 @@ class IContext { this.listenHandler = _ => self.listenEvents(); this.unlistenHandler = _ => self.unlistenEvents(); if (this.options.listenOnFocus) { - this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler); - this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler); + this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler); + this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler); } if (options?.unlistenOnTextEdit ?? false) { - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); + this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); + this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler); } } unlistenDOMElement() { this.unlistenEvents(); - this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler); - this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler); - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler); - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler); + this.blueprint.removeEventListener(Configuration.focusEventName.begin, this.listenHandler); + this.blueprint.removeEventListener(Configuration.focusEventName.end, this.unlistenHandler); + this.blueprint.removeEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); + this.blueprint.removeEventListener(Configuration.editTextEventName.end, this.listenHandler); } /* Subclasses will probabily override the following methods */ @@ -1459,6 +1456,8 @@ class ObjectEntity extends IEntity { CustomProperties: [PinEntity], } + static nameRegex = /(\w+)_(\d+)/ + constructor(options = {}) { super(options); /** @type {ObjectReferenceEntity} */ this.Class; @@ -1477,12 +1476,24 @@ class ObjectEntity extends IEntity { /** @type {PinEntity[]} */ this.CustomProperties; } - /** - * @returns {String} - */ - getName() { + getFullName() { return this.Name } + + getNameAndNumber() { + const result = this.getFullName().match(ObjectEntity.nameRegex); + if (result && result.length == 3) { + return [result[1], parseInt(result[2])] + } + } + + getDisplayName() { + return this.getNameAndNumber()[0] + } + + getNodeNumber() { + return /** @type {Number} */ (this.getNameAndNumber()[1]) + } } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; @@ -1962,7 +1973,7 @@ class IKeyboardShortcut extends IContext { wantsShift(keyEntry) == e.shiftKey && wantsCtrl(keyEntry) == e.ctrlKey && wantsAlt(keyEntry) == e.altKey - && this.blueprint.settings.Keys[keyEntry.Key] == e.code + && Configuration.Keys[keyEntry.Key] == e.code )) { if (options.consumeEvent) { e.stopImmediatePropagation(); @@ -1981,7 +1992,7 @@ class IKeyboardShortcut extends IContext { || keyEntry.bCtrl && e.key == "Control" || keyEntry.bAlt && e.key == "Alt" || keyEntry.bCmd && e.key == "Meta" - || this.blueprint.settings.Keys[keyEntry.Key] == e.code + || Configuration.Keys[keyEntry.Key] == e.code )) { if (options.consumeEvent) { e.stopImmediatePropagation(); @@ -2023,7 +2034,7 @@ class KeyboardCanc extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.deleteNodesKeyboardKey + activationKeys: Configuration.deleteNodesKeyboardKey }; super(target, blueprint, options); } @@ -2155,7 +2166,7 @@ class KeyboardEnableZoom extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.enableZoomIn + activationKeys: Configuration.enableZoomIn }; super(target, blueprint, options); } @@ -2185,7 +2196,7 @@ class KeyboardSelectAll extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.selectAllKeyboardKey + activationKeys: Configuration.selectAllKeyboardKey }; super(target, blueprint, options); } @@ -2278,6 +2289,10 @@ class LinkTemplate extends ITemplate { } link.classList.add("ueb-positioned"); link.pathElement = link.querySelector("path"); + const referencePin = link.sourcePin ?? link.destinationPin; + if (referencePin) { + link.style.setProperty("--ueb-pin-color", referencePin.getColor()); + } } /** @@ -2285,10 +2300,6 @@ class LinkTemplate extends ITemplate { */ applyStartDragging(link) { link.blueprint.dataset.creatingLink = "true"; - const referencePin = link.getSourcePin() ?? link.getDestinationPin(); - if (referencePin) { - link.style.setProperty("--ueb-pin-color", referencePin.getColor()); - } link.classList.add("ueb-link-dragging"); } @@ -2316,7 +2327,7 @@ class LinkTemplate extends ITemplate { */ applyFullLocation(link) { const dx = Math.max(Math.abs(link.sourceLocation[0] - link.destinationLocation[0]), 1); - const width = Math.max(dx, link.blueprint.settings.linkMinWidth); + const width = Math.max(dx, Configuration.linkMinWidth); Math.max(Math.abs(link.sourceLocation[1] - link.destinationLocation[1]), 1); const fillRatio = dx / width; const xInverted = link.originatesFromInput @@ -2344,7 +2355,7 @@ class LinkTemplate extends ITemplate { * fillRatio; let c2 = LinkTemplate.c2Clamped(xInverted ? -dx : dx) + start; c2 = Math.min(c2, LinkTemplate.c2DecreasingValue(width)); - const d = link.blueprint.settings.linkRightSVGPath(start, c1, c2); + const d = Configuration.linkRightSVGPath(start, c1, c2); // TODO move to CSS when Firefox will support property d and css will have enough functions link.pathElement?.setAttribute("d", d); } @@ -2370,10 +2381,60 @@ class LinkTemplate extends ITemplate { class LinkElement extends IElement { static tagName = "ueb-link" + /** @type {PinElement} */ #source + get sourcePin() { + return this.#source + } + set sourcePin(pin) { + if (this.#source) { + const nodeElement = this.#source.getNodeElement(); + nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(Configuration.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(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragSourceHandler); + this.setSourceLocation(); + if (this.#destination) { + this.#linkPins(); + } + } + } + /** @type {PinElement} */ #destination + get destinationPin() { + return this.#destination + } + set destinationPin(pin) { + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + if (this.#source) { + this.#unlinkPins(); + } + } + this.#destination = pin; + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement(); + nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler); + this.setDestinationLocation(); + if (this.#source) { + this.#linkPins(); + } + } + } + #nodeDeleteHandler #nodeDragSourceHandler #nodeDragDestinatonHandler @@ -2398,10 +2459,10 @@ class LinkElement extends IElement { this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value); this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value); if (source) { - this.setSourcePin(source); + this.sourcePin = source; } if (destination) { - this.setDestinationPin(destination); + this.destinationPin = destination; } if (source && destination) { this.#linkPins(); @@ -2484,71 +2545,6 @@ class LinkElement extends IElement { 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 */ @@ -2646,7 +2642,7 @@ class IMouseClickDrag extends IPointing { 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); + const dragEvent = self.getEvent(Configuration.trackingMouseEventName.begin); self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; // Do actual actions self.startDrag(); @@ -2679,7 +2675,7 @@ class IMouseClickDrag extends IPointing { } self.unclicked(); if (self.#trackingMouse) { - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end); + const dragEvent = self.getEvent(Configuration.trackingMouseEventName.end); this.target.dispatchEvent(dragEvent); self.#trackingMouse = false; } @@ -2800,20 +2796,20 @@ class MouseTracking extends IPointing { listenEvents() { this.listenMouseMove(); this.blueprint.addEventListener( - this.blueprint.settings.trackingMouseEventName.begin, + Configuration.trackingMouseEventName.begin, /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); this.blueprint.addEventListener( - this.blueprint.settings.trackingMouseEventName.end, + Configuration.trackingMouseEventName.end, /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler)); } unlistenEvents() { this.unlistenMouseMove(); this.blueprint.removeEventListener( - this.blueprint.settings.trackingMouseEventName.begin, + Configuration.trackingMouseEventName.begin, /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); this.blueprint.removeEventListener( - this.blueprint.settings.trackingMouseEventName.end, + Configuration.trackingMouseEventName.end, /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler) ); } @@ -2916,7 +2912,7 @@ class ISelectableDraggableElement extends IElement { this.location = value; this.template.applyLocation(this); if (this.blueprint) { - const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { + const dragLocalEvent = new CustomEvent(Configuration.nodeDragLocalEventName, { detail: { value: d }, @@ -2937,15 +2933,15 @@ class ISelectableDraggableElement extends IElement { } this.selected = value; if (this.selected) { - this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + this.blueprint.addEventListener(Configuration.nodeDragEventName, this.dragHandler); } else { - this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler); + this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler); } this.template.applySelected(this); } dispatchDragEvent(value) { - const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { + const dragEvent = new CustomEvent(Configuration.nodeDragEventName, { detail: { value: value }, @@ -2987,8 +2983,8 @@ class LinkMessageTemplate extends ITemplate { apply(linkMessage) { super.apply(linkMessage); const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( - linkMessage.linkElement.getSourcePin(), - linkMessage.linkElement.getDestinationPin() + linkMessage.linkElement.sourcePin, + linkMessage.linkElement.destinationPin ); linkMessage.linkElement = linkMessage.closest(LinkElement.tagName); if (linkMessage.linkElement) { @@ -3150,7 +3146,7 @@ class MouseCreateLink extends IMouseClickDrag { }); if (this.enteredPin) { this.blueprint.addGraphElement(this.link); - this.link.setDestinationPin(this.enteredPin); + this.link.destinationPin = this.enteredPin; this.link.setLinkMessage(null); this.link.finishDragging(); } else { @@ -3234,7 +3230,7 @@ class PinTemplate extends ITemplate { pin.dataset.advancedView = "true"; } pin.clickableElement = pin; - window.customElements.whenDefined("ueb-node").then(_ => pin.nodeElement = pin.closest("ueb-node")); + pin.nodeElement = pin.closest("ueb-node"); pin.getLinks().forEach(pinReference => { const targetPin = pin.blueprint.getPin(pinReference); if (targetPin) { @@ -3333,14 +3329,15 @@ class PinElement extends IElement { "string": StringPinTemplate, } + #color = "" + /** @type {NodeElement} */ nodeElement /** @type {HTMLElement} */ clickableElement - /** @type {String} */ - #color + connections = 0 constructor(entity) { super( @@ -3505,7 +3502,7 @@ class NodeTemplate extends SelectableDraggableTemplate {
- ${sanitizeText(node.entity.getName())} + ${sanitizeText(node.getNodeName())}
@@ -3529,7 +3526,9 @@ class NodeTemplate extends SelectableDraggableTemplate { if (node.selected) { node.classList.add("ueb-selected"); } - node.dataset.name = node.getNodeName(); + const name = node.entity.getNameAndNumber(); + node.dataset.name = sanitizeText(name[0]); + node.dataset.count = sanitizeText(name[1]); if (node.entity.AdvancedPinDisplay) { node.dataset.advancedDisplay = node.entity.AdvancedPinDisplay.toString(); } @@ -3583,7 +3582,7 @@ class NodeElement extends ISelectableDraggableElement { } getNodeName() { - return this.entity.getName() + return this.entity.getFullName() } getPinElements() { @@ -3612,7 +3611,7 @@ class NodeElement extends ISelectableDraggableElement { } dispatchDeleteEvent(value) { - let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { + let deleteEvent = new CustomEvent(Configuration.nodeDeleteEventName, { bubbles: true, cancelable: true, }); @@ -3753,8 +3752,6 @@ class Unfocus extends IContext { class Blueprint extends IElement { static tagName = "ueb-blueprint" - /** @type {Configuration} */ - settings /** @type {Number[]} */ #additional /** @type {Number[]} */ @@ -3819,13 +3816,12 @@ class Blueprint extends IElement { super({}, new BlueprintTemplate()); /** @type {BlueprintTemplate} */ this.template; - this.settings = settings; /** @type {Number} */ - this.gridSize = this.settings.gridSize; + this.gridSize = Configuration.gridSize; /** @type {Number[]} */ - this.#additional = [2 * this.settings.expandGridSize, 2 * this.settings.expandGridSize]; + this.#additional = [2 * Configuration.expandGridSize, 2 * Configuration.expandGridSize]; /** @type {Number[]} */ - this.#translateValue = [this.settings.expandGridSize, this.settings.expandGridSize]; + this.#translateValue = [Configuration.expandGridSize, Configuration.expandGridSize]; } /** @@ -3916,20 +3912,20 @@ class Blueprint extends IElement { let shrink = [0, 0]; let direction = [0, 0]; for (let i = 0; i < 2; ++i) { - if (delta[i] < 0 && finalScroll[i] < this.settings.gridExpandThreshold * this.settings.expandGridSize) { + if (delta[i] < 0 && finalScroll[i] < Configuration.gridExpandThreshold * Configuration.expandGridSize) { // Expand left/top - expand[i] = this.settings.expandGridSize; + expand[i] = Configuration.expandGridSize; direction[i] = -1; - if (maxScroll[i] - finalScroll[i] > this.settings.gridShrinkThreshold * this.settings.expandGridSize) { - shrink[i] = -this.settings.expandGridSize; + if (maxScroll[i] - finalScroll[i] > Configuration.gridShrinkThreshold * Configuration.expandGridSize) { + shrink[i] = -Configuration.expandGridSize; } } else if (delta[i] > 0 && finalScroll[i] - > maxScroll[i] - this.settings.gridExpandThreshold * this.settings.expandGridSize) { + > maxScroll[i] - Configuration.gridExpandThreshold * Configuration.expandGridSize) { // Expand right/bottom - expand[i] = this.settings.expandGridSize; + expand[i] = Configuration.expandGridSize; direction[i] = 1; - if (finalScroll[i] > this.settings.gridShrinkThreshold * this.settings.expandGridSize) { - shrink[i] = -this.settings.expandGridSize; + if (finalScroll[i] > Configuration.gridShrinkThreshold * Configuration.expandGridSize) { + shrink[i] = -Configuration.expandGridSize; } } } @@ -3964,7 +3960,7 @@ class Blueprint extends IElement { } getExpandGridSize() { - return this.settings.expandGridSize + return Configuration.expandGridSize } getViewportSize() { @@ -4025,7 +4021,7 @@ class Blueprint extends IElement { } progressiveSnapToGrid(x) { - return this.settings.expandGridSize * Math.round(x / this.settings.expandGridSize + 0.5 * Math.sign(x)) + return Configuration.expandGridSize * Math.round(x / Configuration.expandGridSize + 0.5 * Math.sign(x)) } getZoom() { @@ -4033,7 +4029,7 @@ class Blueprint extends IElement { } setZoom(zoom, center) { - zoom = Utility.clamp(zoom, this.settings.minZoom, this.settings.maxZoom); + zoom = Utility.clamp(zoom, Configuration.minZoom, Configuration.maxZoom); if (zoom == this.zoom) { return } @@ -4093,12 +4089,12 @@ class Blueprint extends IElement { getLinks([a, b] = []) { if (a == null != b == null) { const pin = a ?? b; - return this.links.filter(link => link.getSourcePin() == pin || link.getDestinationPin() == pin) + return this.links.filter(link => link.sourcePin == pin || link.destinationPin == pin) } if (a != null && b != null) { return this.links.filter(link => - link.getSourcePin() == a && link.getDestinationPin() == b - || link.getSourcePin() == b && link.getDestinationPin() == a) + link.sourcePin == a && link.destinationPin == b + || link.sourcePin == b && link.destinationPin == a) } return this.links } @@ -4121,33 +4117,27 @@ class Blueprint extends IElement { * @param {...IElement} graphElements */ addGraphElement(...graphElements) { - const intoArray = element => { - if (element instanceof NodeElement) { + graphElements.forEach(element => { + if (element instanceof NodeElement && !this.nodes.includes(element)) { + const nameAndCount = element.entity.getNameAndNumber(); + // Node with the same name and number exists already + let maximumCount = 0; + { + ( + this.nodesContainerElement?.querySelectorAll(`ueb-node[data-name="${nameAndCount[0]}"]`) + ?? this.nodes + ).forEach(node => maximumCount = Math.max( + maximumCount, + /** @type {NodeElement} node */(node).entity.getNodeNumber()) + ); + } + element.entity.Name = `${nameAndCount[0]}_${maximumCount + 1}`; this.nodes.push(element); - } else if (element instanceof LinkElement) { + } else if (element instanceof LinkElement && !this.links.includes(element)) { this.links.push(element); } - }; - if (this.nodesContainerElement) { - graphElements.forEach(element => { - if (element.closest(Blueprint.tagName) != this) { - // If not already the in target DOM position - this.nodesContainerElement.appendChild(element); - intoArray(element); - } - }); - } else { - graphElements - .filter(element => { - if (element instanceof NodeElement) { - return !this.nodes.includes(element) - } else if (element instanceof LinkElement) { - return !this.links.includes(element) - } - return false - }) - .forEach(intoArray); - } + this.nodesContainerElement?.appendChild(element); + }); } /** @@ -4183,8 +4173,8 @@ class Blueprint extends IElement { dispatchEditTextEvent(value) { const event = new CustomEvent( value - ? this.settings.editTextEventName.begin - : this.settings.editTextEventName.end + ? Configuration.editTextEventName.begin + : Configuration.editTextEventName.end ); this.dispatchEvent(event); } diff --git a/js/Blueprint.js b/js/Blueprint.js index 77246ea..c0e686e 100755 --- a/js/Blueprint.js +++ b/js/Blueprint.js @@ -26,8 +26,6 @@ import Zoom from "./input/mouse/Zoom" export default class Blueprint extends IElement { static tagName = "ueb-blueprint" - /** @type {Configuration} */ - settings /** @type {Number[]} */ #additional /** @type {Number[]} */ @@ -92,13 +90,12 @@ export default class Blueprint extends IElement { super({}, new BlueprintTemplate()) /** @type {BlueprintTemplate} */ this.template - this.settings = settings /** @type {Number} */ - this.gridSize = this.settings.gridSize + this.gridSize = Configuration.gridSize /** @type {Number[]} */ - this.#additional = [2 * this.settings.expandGridSize, 2 * this.settings.expandGridSize] + this.#additional = [2 * Configuration.expandGridSize, 2 * Configuration.expandGridSize] /** @type {Number[]} */ - this.#translateValue = [this.settings.expandGridSize, this.settings.expandGridSize] + this.#translateValue = [Configuration.expandGridSize, Configuration.expandGridSize] } /** @@ -189,20 +186,20 @@ export default class Blueprint extends IElement { let shrink = [0, 0] let direction = [0, 0] for (let i = 0; i < 2; ++i) { - if (delta[i] < 0 && finalScroll[i] < this.settings.gridExpandThreshold * this.settings.expandGridSize) { + if (delta[i] < 0 && finalScroll[i] < Configuration.gridExpandThreshold * Configuration.expandGridSize) { // Expand left/top - expand[i] = this.settings.expandGridSize + expand[i] = Configuration.expandGridSize direction[i] = -1 - if (maxScroll[i] - finalScroll[i] > this.settings.gridShrinkThreshold * this.settings.expandGridSize) { - shrink[i] = -this.settings.expandGridSize + if (maxScroll[i] - finalScroll[i] > Configuration.gridShrinkThreshold * Configuration.expandGridSize) { + shrink[i] = -Configuration.expandGridSize } } else if (delta[i] > 0 && finalScroll[i] - > maxScroll[i] - this.settings.gridExpandThreshold * this.settings.expandGridSize) { + > maxScroll[i] - Configuration.gridExpandThreshold * Configuration.expandGridSize) { // Expand right/bottom - expand[i] = this.settings.expandGridSize + expand[i] = Configuration.expandGridSize direction[i] = 1 - if (finalScroll[i] > this.settings.gridShrinkThreshold * this.settings.expandGridSize) { - shrink[i] = -this.settings.expandGridSize + if (finalScroll[i] > Configuration.gridShrinkThreshold * Configuration.expandGridSize) { + shrink[i] = -Configuration.expandGridSize } } } @@ -237,7 +234,7 @@ export default class Blueprint extends IElement { } getExpandGridSize() { - return this.settings.expandGridSize + return Configuration.expandGridSize } getViewportSize() { @@ -298,7 +295,7 @@ export default class Blueprint extends IElement { } progressiveSnapToGrid(x) { - return this.settings.expandGridSize * Math.round(x / this.settings.expandGridSize + 0.5 * Math.sign(x)) + return Configuration.expandGridSize * Math.round(x / Configuration.expandGridSize + 0.5 * Math.sign(x)) } getZoom() { @@ -306,7 +303,7 @@ export default class Blueprint extends IElement { } setZoom(zoom, center) { - zoom = Utility.clamp(zoom, this.settings.minZoom, this.settings.maxZoom) + zoom = Utility.clamp(zoom, Configuration.minZoom, Configuration.maxZoom) if (zoom == this.zoom) { return } @@ -366,12 +363,12 @@ export default class Blueprint extends IElement { getLinks([a, b] = []) { if (a == null != b == null) { const pin = a ?? b - return this.links.filter(link => link.getSourcePin() == pin || link.getDestinationPin() == pin) + return this.links.filter(link => link.sourcePin == pin || link.destinationPin == pin) } if (a != null && b != null) { return this.links.filter(link => - link.getSourcePin() == a && link.getDestinationPin() == b - || link.getSourcePin() == b && link.getDestinationPin() == a) + link.sourcePin == a && link.destinationPin == b + || link.sourcePin == b && link.destinationPin == a) } return this.links } @@ -394,33 +391,27 @@ export default class Blueprint extends IElement { * @param {...IElement} graphElements */ addGraphElement(...graphElements) { - const intoArray = element => { - if (element instanceof NodeElement) { + graphElements.forEach(element => { + if (element instanceof NodeElement && !this.nodes.includes(element)) { + const nameAndCount = element.entity.getNameAndNumber() + // Node with the same name and number exists already + let maximumCount = 0 + { + ( + this.nodesContainerElement?.querySelectorAll(`ueb-node[data-name="${nameAndCount[0]}"]`) + ?? this.nodes + ).forEach(node => maximumCount = Math.max( + maximumCount, + /** @type {NodeElement} node */(node).entity.getNodeNumber()) + ) + } + element.entity.Name = `${nameAndCount[0]}_${maximumCount + 1}` this.nodes.push(element) - } else if (element instanceof LinkElement) { + } else if (element instanceof LinkElement && !this.links.includes(element)) { this.links.push(element) } - } - if (this.nodesContainerElement) { - graphElements.forEach(element => { - if (element.closest(Blueprint.tagName) != this) { - // If not already the in target DOM position - this.nodesContainerElement.appendChild(element) - intoArray(element) - } - }) - } else { - graphElements - .filter(element => { - if (element instanceof NodeElement) { - return !this.nodes.includes(element) - } else if (element instanceof LinkElement) { - return !this.links.includes(element) - } - return false - }) - .forEach(intoArray) - } + this.nodesContainerElement?.appendChild(element) + }) } /** @@ -456,8 +447,8 @@ export default class Blueprint extends IElement { dispatchEditTextEvent(value) { const event = new CustomEvent( value - ? this.settings.editTextEventName.begin - : this.settings.editTextEventName.end + ? Configuration.editTextEventName.begin + : Configuration.editTextEventName.end ) this.dispatchEvent(event) } diff --git a/js/Configuration.js b/js/Configuration.js index 4149eeb..1533de6 100755 --- a/js/Configuration.js +++ b/js/Configuration.js @@ -1,57 +1,57 @@ // @ts-check export default class Configuration { - deleteNodesKeyboardKey = "Delete" - editTextEventName = { + static deleteNodesKeyboardKey = "Delete" + static editTextEventName = { begin: "ueb-edit-text-begin", end: "ueb-edit-text-end", } - enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom - expandGridSize = 400 - focusEventName = { + static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom + static expandGridSize = 400 + static focusEventName = { begin: "blueprint-focus", end: "blueprint-unfocus", } - fontSize = "12px" - gridAxisLineColor = "black" - gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event - gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event - gridLineColor = "#353535" - gridLineWidth = 1 // pixel - gridSet = 8 - gridSetLineColor = "#161616" - gridSize = 16 // pixel - keysSeparator = "+" - linkCurveHeight = 15 // pixel - linkCurveWidth = 80 // pixel - linkMinWidth = 100 // pixel + static fontSize = "12px" + static gridAxisLineColor = "black" + static gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event + static gridLineColor = "#353535" + static gridLineWidth = 1 // pixel + static gridSet = 8 + static gridSetLineColor = "#161616" + static gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event + static gridSize = 16 // pixel + static keysSeparator = "+" + static linkCurveHeight = 15 // pixel + static linkCurveWidth = 80 // pixel + static linkMinWidth = 100 // pixel /** * @param {Number} start * @param {Number} c1 * @param {Number} c2 */ - linkRightSVGPath = (start, c1, c2) => { + static linkRightSVGPath = (start, c1, c2) => { let end = 100 - start return `M ${start} 0 C ${c1} 0, ${c2} 0, 50 50 S ${end - c1 + start} 100, ${end} 100` } - maxZoom = 7 - minZoom = -12 - nodeDeleteEventName = "ueb-node-delete" - nodeDragEventName = "ueb-node-drag" - nodeDragLocalEventName = "ueb-node-drag-local" - nodeRadius = 8 // in pixel - selectAllKeyboardKey = "(bCtrl=True,Key=A)" - trackingMouseEventName = { + static maxZoom = 7 + static minZoom = -12 + static nodeDeleteEventName = "ueb-node-delete" + static nodeDragEventName = "ueb-node-drag" + static nodeDragLocalEventName = "ueb-node-drag-local" + static nodeRadius = 8 // in pixel + static selectAllKeyboardKey = "(bCtrl=True,Key=A)" + static trackingMouseEventName = { begin: "ueb-tracking-mouse-begin", end: "ueb-tracking-mouse-end", } - ModifierKeys = [ + static ModifierKeys = [ "Ctrl", "Shift", "Alt", "Meta", ] - Keys = { + static Keys = { /* UE name: JS name */ "Backspace": "Backspace", "Tab": "Tab", diff --git a/js/element/ISelectableDraggableElement.js b/js/element/ISelectableDraggableElement.js index 20eaaa0..1ff2731 100644 --- a/js/element/ISelectableDraggableElement.js +++ b/js/element/ISelectableDraggableElement.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../Configuration" import IElement from "./IElement" import MouseMoveNodes from "../input/mouse/MouseMoveNodes" @@ -39,7 +40,7 @@ export default class ISelectableDraggableElement extends IElement { this.location = value this.template.applyLocation(this) if (this.blueprint) { - const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, { + const dragLocalEvent = new CustomEvent(Configuration.nodeDragLocalEventName, { detail: { value: d }, @@ -60,15 +61,15 @@ export default class ISelectableDraggableElement extends IElement { } this.selected = value if (this.selected) { - this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler) + this.blueprint.addEventListener(Configuration.nodeDragEventName, this.dragHandler) } else { - this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler) + this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler) } this.template.applySelected(this) } dispatchDragEvent(value) { - const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, { + const dragEvent = new CustomEvent(Configuration.nodeDragEventName, { detail: { value: value }, diff --git a/js/element/LinkElement.js b/js/element/LinkElement.js index e9d6687..826e5c9 100644 --- a/js/element/LinkElement.js +++ b/js/element/LinkElement.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../Configuration" import IElement from "./IElement" import LinkTemplate from "../template/LinkTemplate" @@ -10,10 +11,60 @@ import LinkTemplate from "../template/LinkTemplate" export default class LinkElement extends IElement { static tagName = "ueb-link" + /** @type {PinElement} */ #source + get sourcePin() { + return this.#source + } + set sourcePin(pin) { + if (this.#source) { + const nodeElement = this.#source.getNodeElement() + nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.removeEventListener(Configuration.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(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragSourceHandler) + this.setSourceLocation() + if (this.#destination) { + this.#linkPins() + } + } + } + /** @type {PinElement} */ #destination + get destinationPin() { + return this.#destination + } + set destinationPin(pin) { + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement() + nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler) + if (this.#source) { + this.#unlinkPins() + } + } + this.#destination = pin + if (this.#destination) { + const nodeElement = this.#destination.getNodeElement() + nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler) + nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler) + this.setDestinationLocation() + if (this.#source) { + this.#linkPins() + } + } + } + #nodeDeleteHandler #nodeDragSourceHandler #nodeDragDestinatonHandler @@ -38,10 +89,10 @@ export default class LinkElement extends IElement { this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value) this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value) if (source) { - this.setSourcePin(source) + this.sourcePin = source } if (destination) { - this.setDestinationPin(destination) + this.destinationPin = destination } if (source && destination) { this.#linkPins() @@ -124,71 +175,6 @@ export default class LinkElement extends IElement { 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 */ diff --git a/js/element/NodeElement.js b/js/element/NodeElement.js index 20d5455..ed122ef 100644 --- a/js/element/NodeElement.js +++ b/js/element/NodeElement.js @@ -1,10 +1,11 @@ // @ts-check +import Configuration from "../Configuration" import ISelectableDraggableElement from "./ISelectableDraggableElement" import NodeTemplate from "../template/NodeTemplate" import ObjectEntity from "../entity/ObjectEntity" -import SerializerFactory from "../serialization/SerializerFactory" import PinEntity from "../entity/PinEntity" +import SerializerFactory from "../serialization/SerializerFactory" export default class NodeElement extends ISelectableDraggableElement { @@ -34,7 +35,7 @@ export default class NodeElement extends ISelectableDraggableElement { } getNodeName() { - return this.entity.getName() + return this.entity.getFullName() } getPinElements() { @@ -63,7 +64,7 @@ export default class NodeElement extends ISelectableDraggableElement { } dispatchDeleteEvent(value) { - let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, { + let deleteEvent = new CustomEvent(Configuration.nodeDeleteEventName, { bubbles: true, cancelable: true, }) diff --git a/js/element/PinElement.js b/js/element/PinElement.js index 7bd5214..5cd5191 100644 --- a/js/element/PinElement.js +++ b/js/element/PinElement.js @@ -20,14 +20,15 @@ export default class PinElement extends IElement { "string": StringPinTemplate, } + #color = "" + /** @type {NodeElement} */ nodeElement /** @type {HTMLElement} */ clickableElement - /** @type {String} */ - #color + connections = 0 constructor(entity) { super( diff --git a/js/entity/ObjectEntity.js b/js/entity/ObjectEntity.js index f66c3b2..89b27cb 100755 --- a/js/entity/ObjectEntity.js +++ b/js/entity/ObjectEntity.js @@ -29,6 +29,8 @@ export default class ObjectEntity extends IEntity { CustomProperties: [PinEntity], } + static nameRegex = /(\w+)_(\d+)/ + constructor(options = {}) { super(options) /** @type {ObjectReferenceEntity} */ this.Class @@ -47,10 +49,22 @@ export default class ObjectEntity extends IEntity { /** @type {PinEntity[]} */ this.CustomProperties } - /** - * @returns {String} - */ - getName() { + getFullName() { return this.Name } + + getNameAndNumber() { + const result = this.getFullName().match(ObjectEntity.nameRegex) + if (result && result.length == 3) { + return [result[1], parseInt(result[2])] + } + } + + getDisplayName() { + return this.getNameAndNumber()[0] + } + + getNodeNumber() { + return /** @type {Number} */ (this.getNameAndNumber()[1]) + } } diff --git a/js/input/IContext.js b/js/input/IContext.js index 774526b..762d6da 100644 --- a/js/input/IContext.js +++ b/js/input/IContext.js @@ -1,5 +1,7 @@ // @ts-check +import Configuration from "../Configuration" + /** * @typedef {import("../Blueprint").default} Blueprint */ @@ -39,21 +41,21 @@ export default class IContext { this.listenHandler = _ => self.listenEvents() this.unlistenHandler = _ => self.unlistenEvents() if (this.options.listenOnFocus) { - this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler) - this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler) + this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler) + this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler) } if (options?.unlistenOnTextEdit ?? false) { - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler) - this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler) + this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler) + this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler) } } unlistenDOMElement() { this.unlistenEvents() - this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler) - this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler) - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler) - this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler) + this.blueprint.removeEventListener(Configuration.focusEventName.begin, this.listenHandler) + this.blueprint.removeEventListener(Configuration.focusEventName.end, this.unlistenHandler) + this.blueprint.removeEventListener(Configuration.editTextEventName.begin, this.unlistenHandler) + this.blueprint.removeEventListener(Configuration.editTextEventName.end, this.listenHandler) } /* Subclasses will probabily override the following methods */ diff --git a/js/input/keybaord/IKeyboardShortcut.js b/js/input/keybaord/IKeyboardShortcut.js index 84663a1..02de2cd 100644 --- a/js/input/keybaord/IKeyboardShortcut.js +++ b/js/input/keybaord/IKeyboardShortcut.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IContext from "../IContext" import ISerializer from "../../serialization/ISerializer" import KeyBindingEntity from "../../entity/KeyBindingEntity" @@ -45,7 +46,7 @@ export default class IKeyboardShortcut extends IContext { wantsShift(keyEntry) == e.shiftKey && wantsCtrl(keyEntry) == e.ctrlKey && wantsAlt(keyEntry) == e.altKey - && this.blueprint.settings.Keys[keyEntry.Key] == e.code + && Configuration.Keys[keyEntry.Key] == e.code )) { if (options.consumeEvent) { e.stopImmediatePropagation() @@ -64,7 +65,7 @@ export default class IKeyboardShortcut extends IContext { || keyEntry.bCtrl && e.key == "Control" || keyEntry.bAlt && e.key == "Alt" || keyEntry.bCmd && e.key == "Meta" - || this.blueprint.settings.Keys[keyEntry.Key] == e.code + || Configuration.Keys[keyEntry.Key] == e.code )) { if (options.consumeEvent) { e.stopImmediatePropagation() diff --git a/js/input/keybaord/KeyboardCanc.js b/js/input/keybaord/KeyboardCanc.js index 67697b4..15942f6 100755 --- a/js/input/keybaord/KeyboardCanc.js +++ b/js/input/keybaord/KeyboardCanc.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IKeyboardShortcut from "./IKeyboardShortcut" export default class KeyboardCanc extends IKeyboardShortcut { @@ -12,7 +13,7 @@ export default class KeyboardCanc extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.deleteNodesKeyboardKey + activationKeys: Configuration.deleteNodesKeyboardKey } super(target, blueprint, options) } diff --git a/js/input/keybaord/KeyboardEnableZoom.js b/js/input/keybaord/KeyboardEnableZoom.js index 1aa3667..5994c48 100644 --- a/js/input/keybaord/KeyboardEnableZoom.js +++ b/js/input/keybaord/KeyboardEnableZoom.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IKeyboardShortcut from "./IKeyboardShortcut" import Zoom from "../mouse/Zoom" @@ -16,7 +17,7 @@ export default class KeyboardEnableZoom extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.enableZoomIn + activationKeys: Configuration.enableZoomIn } super(target, blueprint, options) } diff --git a/js/input/keybaord/KeyboardSelectAll.js b/js/input/keybaord/KeyboardSelectAll.js index e12071e..e2e3b45 100755 --- a/js/input/keybaord/KeyboardSelectAll.js +++ b/js/input/keybaord/KeyboardSelectAll.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IKeyboardShortcut from "./IKeyboardShortcut" /** @@ -15,7 +16,7 @@ export default class KeyboardSelectAll extends IKeyboardShortcut { constructor(target, blueprint, options = {}) { options = { ...options, - activationKeys: blueprint.settings.selectAllKeyboardKey + activationKeys: Configuration.selectAllKeyboardKey } super(target, blueprint, options) } diff --git a/js/input/mouse/IMouseClickDrag.js b/js/input/mouse/IMouseClickDrag.js index 45f96f6..0912cd6 100644 --- a/js/input/mouse/IMouseClickDrag.js +++ b/js/input/mouse/IMouseClickDrag.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IPointing from "./IPointing" /** @@ -74,7 +75,7 @@ export default class IMouseClickDrag extends IPointing { 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) + const dragEvent = self.getEvent(Configuration.trackingMouseEventName.begin) self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false // Do actual actions self.startDrag() @@ -107,7 +108,7 @@ export default class IMouseClickDrag extends IPointing { } self.unclicked() if (self.#trackingMouse) { - const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end) + const dragEvent = self.getEvent(Configuration.trackingMouseEventName.end) this.target.dispatchEvent(dragEvent) self.#trackingMouse = false } diff --git a/js/input/mouse/MouseCreateLink.js b/js/input/mouse/MouseCreateLink.js index 747f0d9..d4a8625 100755 --- a/js/input/mouse/MouseCreateLink.js +++ b/js/input/mouse/MouseCreateLink.js @@ -89,7 +89,7 @@ export default class MouseCreateLink extends IMouseClickDrag { }) if (this.enteredPin) { this.blueprint.addGraphElement(this.link) - this.link.setDestinationPin(this.enteredPin) + this.link.destinationPin = this.enteredPin this.link.setLinkMessage(null) this.link.finishDragging() } else { diff --git a/js/input/mouse/MouseTracking.js b/js/input/mouse/MouseTracking.js index 2f8da43..69fabde 100755 --- a/js/input/mouse/MouseTracking.js +++ b/js/input/mouse/MouseTracking.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../../Configuration" import IPointing from "./IPointing" export default class MouseTracking extends IPointing { @@ -55,20 +56,20 @@ export default class MouseTracking extends IPointing { listenEvents() { this.listenMouseMove() this.blueprint.addEventListener( - this.blueprint.settings.trackingMouseEventName.begin, + Configuration.trackingMouseEventName.begin, /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)) this.blueprint.addEventListener( - this.blueprint.settings.trackingMouseEventName.end, + Configuration.trackingMouseEventName.end, /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler)) } unlistenEvents() { this.unlistenMouseMove() this.blueprint.removeEventListener( - this.blueprint.settings.trackingMouseEventName.begin, + Configuration.trackingMouseEventName.begin, /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)) this.blueprint.removeEventListener( - this.blueprint.settings.trackingMouseEventName.end, + Configuration.trackingMouseEventName.end, /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler) ) } diff --git a/js/template/BlueprintTemplate.js b/js/template/BlueprintTemplate.js index 00e87b3..2fc8baf 100755 --- a/js/template/BlueprintTemplate.js +++ b/js/template/BlueprintTemplate.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../Configuration" import html from "./html" import ITemplate from "./ITemplate" import sanitizeText from "./sanitizeText" @@ -71,15 +72,15 @@ export default class BlueprintTemplate extends ITemplate { super.apply(blueprint) blueprint.classList.add("ueb", `ueb-zoom-${blueprint.zoom}`) Object.entries({ - "--ueb-font-size": sanitizeText(blueprint.settings.fontSize), - "--ueb-grid-size": `${sanitizeText(blueprint.settings.gridSize)}px`, - "--ueb-grid-line-width": `${sanitizeText(blueprint.settings.gridLineWidth)}px`, - "--ueb-grid-line-color": sanitizeText(blueprint.settings.gridLineColor), - "--ueb-grid-set": sanitizeText(blueprint.settings.gridSet), - "--ueb-grid-set-line-color": sanitizeText(blueprint.settings.gridSetLineColor), - "--ueb-grid-axis-line-color": sanitizeText(blueprint.settings.gridAxisLineColor), - "--ueb-node-radius": `${sanitizeText(blueprint.settings.nodeRadius)}px`, - "--ueb-link-min-width": sanitizeText(blueprint.settings.linkMinWidth) + "--ueb-font-size": sanitizeText(Configuration.fontSize), + "--ueb-grid-size": `${sanitizeText(Configuration.gridSize)}px`, + "--ueb-grid-line-width": `${sanitizeText(Configuration.gridLineWidth)}px`, + "--ueb-grid-line-color": sanitizeText(Configuration.gridLineColor), + "--ueb-grid-set": sanitizeText(Configuration.gridSet), + "--ueb-grid-set-line-color": sanitizeText(Configuration.gridSetLineColor), + "--ueb-grid-axis-line-color": sanitizeText(Configuration.gridAxisLineColor), + "--ueb-node-radius": `${sanitizeText(Configuration.nodeRadius)}px`, + "--ueb-link-min-width": sanitizeText(Configuration.linkMinWidth) }).forEach(entry => blueprint.style.setProperty(entry[0], entry[1])) blueprint.headerElement = blueprint.querySelector('.ueb-viewport-header') blueprint.overlayElement = blueprint.querySelector('.ueb-viewport-overlay') diff --git a/js/template/LinkMessageTemplate.js b/js/template/LinkMessageTemplate.js index 48ca258..bf4b5ba 100644 --- a/js/template/LinkMessageTemplate.js +++ b/js/template/LinkMessageTemplate.js @@ -27,8 +27,8 @@ export default class LinkMessageTemplate extends ITemplate { apply(linkMessage) { const a = super.apply(linkMessage) const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message( - linkMessage.linkElement.getSourcePin(), - linkMessage.linkElement.getDestinationPin() + linkMessage.linkElement.sourcePin, + linkMessage.linkElement.destinationPin ) linkMessage.linkElement = linkMessage.closest(LinkElement.tagName) if (linkMessage.linkElement) { diff --git a/js/template/LinkTemplate.js b/js/template/LinkTemplate.js index c0ba94c..e3fa9d2 100755 --- a/js/template/LinkTemplate.js +++ b/js/template/LinkTemplate.js @@ -1,5 +1,6 @@ // @ts-check +import Configuration from "../Configuration" import html from "./html" import ITemplate from "./ITemplate" import sanitizeText from "./sanitizeText" @@ -85,6 +86,10 @@ export default class LinkTemplate extends ITemplate { } link.classList.add("ueb-positioned") link.pathElement = link.querySelector("path") + const referencePin = link.sourcePin ?? link.destinationPin + if (referencePin) { + link.style.setProperty("--ueb-pin-color", referencePin.getColor()) + } } /** @@ -92,10 +97,6 @@ export default class LinkTemplate extends ITemplate { */ applyStartDragging(link) { link.blueprint.dataset.creatingLink = "true" - const referencePin = link.getSourcePin() ?? link.getDestinationPin() - if (referencePin) { - link.style.setProperty("--ueb-pin-color", referencePin.getColor()) - } link.classList.add("ueb-link-dragging") } @@ -123,7 +124,7 @@ export default class LinkTemplate extends ITemplate { */ applyFullLocation(link) { const dx = Math.max(Math.abs(link.sourceLocation[0] - link.destinationLocation[0]), 1) - const width = Math.max(dx, link.blueprint.settings.linkMinWidth) + const width = Math.max(dx, Configuration.linkMinWidth) const height = Math.max(Math.abs(link.sourceLocation[1] - link.destinationLocation[1]), 1) const fillRatio = dx / width const aspectRatio = width / height @@ -152,7 +153,7 @@ export default class LinkTemplate extends ITemplate { * fillRatio let c2 = LinkTemplate.c2Clamped(xInverted ? -dx : dx) + start c2 = Math.min(c2, LinkTemplate.c2DecreasingValue(width)) - const d = link.blueprint.settings.linkRightSVGPath(start, c1, c2) + const d = Configuration.linkRightSVGPath(start, c1, c2) // TODO move to CSS when Firefox will support property d and css will have enough functions link.pathElement?.setAttribute("d", d) } diff --git a/js/template/NodeTemplate.js b/js/template/NodeTemplate.js index f62570a..d1dabc5 100755 --- a/js/template/NodeTemplate.js +++ b/js/template/NodeTemplate.js @@ -22,7 +22,7 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
- ${sanitizeText(node.entity.getName())} + ${sanitizeText(node.getNodeName())}
@@ -46,7 +46,9 @@ export default class NodeTemplate extends SelectableDraggableTemplate { if (node.selected) { node.classList.add("ueb-selected") } - node.dataset.name = node.getNodeName() + const name = node.entity.getNameAndNumber() + node.dataset.name = sanitizeText(name[0]) + node.dataset.count = sanitizeText(name[1]) if (node.entity.AdvancedPinDisplay) { node.dataset.advancedDisplay = node.entity.AdvancedPinDisplay.toString() } diff --git a/js/template/PinTemplate.js b/js/template/PinTemplate.js index 9d13672..bce3052 100755 --- a/js/template/PinTemplate.js +++ b/js/template/PinTemplate.js @@ -71,7 +71,7 @@ export default class PinTemplate extends ITemplate { pin.dataset.advancedView = "true" } pin.clickableElement = pin - window.customElements.whenDefined("ueb-node").then(_ => pin.nodeElement = pin.closest("ueb-node")) + pin.nodeElement = pin.closest("ueb-node") pin.getLinks().forEach(pinReference => { const targetPin = pin.blueprint.getPin(pinReference) if (targetPin) { diff --git a/js/template/sanitizeText.js b/js/template/sanitizeText.js index f61eb01..c52446f 100755 --- a/js/template/sanitizeText.js +++ b/js/template/sanitizeText.js @@ -11,10 +11,7 @@ const tagReplacement = { } function sanitizeText(value) { - if (value.constructor === String) { - return value.replace(/[&<>'"]/g, tag => tagReplacement[tag]) - } - return value + return value.toString().replace(/[&<>'"]/g, tag => tagReplacement[tag]) } export default sanitizeText