diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js
index 984280f..95761ce 100644
--- a/dist/ueblueprint.js
+++ b/dist/ueblueprint.js
@@ -1,14 +1,3 @@
-class TypeInitialization {
- constructor(value, showDefault = true, type = Utility.getType(value)) {
- if (type.prototype.constructor.name != value.constructor.name) {
- throw new Error("Default value expected to be of the same type.")
- }
- this.value = value;
- this.showDefault = showDefault;
- this.type = type;
- }
-}
-
class Utility {
static clamp(val, min, max) {
return Math.min(Math.max(val, min), max)
@@ -107,1163 +96,14 @@ class Utility {
}
}
-class Pointing {
-
- constructor(target, blueprint, options) {
- /** @type {HTMLElement} */
- this.target = target;
- /** @type {import("../Blueprint").Blueprint}" */
- this.blueprint = blueprint;
- this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement;
- }
-
- getLocation(mouseEvent) {
- const scaleCorrection = 1 / Utility.getScale(this.target);
- const targetOffset = this.movementSpace.getBoundingClientRect();
- let location = [
- (mouseEvent.clientX - targetOffset.x) * scaleCorrection,
- (mouseEvent.clientY - targetOffset.y) * scaleCorrection
- ];
- return location
+class TypeInitialization {
+ constructor(value, showDefault = true, type = Utility.getType(value)) {
+ this.value = value;
+ this.showDefault = showDefault;
+ this.type = type;
}
}
-/**
- * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses.
- */
-class MouseClickDrag extends Pointing {
- 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.started = false;
- this.clickedPosition = [0, 0];
- const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
- let self = this;
-
- this.mouseDownHandler = function (e) {
- 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) {
- e.stopPropagation();
- self.started = false;
- // Attach the listeners
- movementListenedElement.addEventListener('mousemove', self.mouseStartedMovingHandler);
- document.addEventListener('mouseup', self.mouseUpHandler);
- self.clickedPosition = self.getLocation(e);
- self.clicked(self.clickedPosition);
- }
- break
- default:
- if (!self.exitAnyButton) {
- self.mouseUpHandler(e);
- }
- break
- }
- };
-
- this.mouseStartedMovingHandler = function (e) {
- e.preventDefault();
- e.stopPropagation();
-
- // Delegate from now on to self.mouseMoveHandler
- movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler);
- movementListenedElement.addEventListener('mousemove', self.mouseMoveHandler);
-
- // Do actual actions
- self.startDrag();
- self.started = true;
- };
-
- this.mouseMoveHandler = function (e) {
- e.preventDefault();
- e.stopPropagation();
- const location = self.getLocation(e);
- const movement = [e.movementX, e.movementY];
- self.dragTo(location, movement);
- };
-
- this.mouseUpHandler = function (e) {
- if (!self.exitAnyButton || e.button == self.clickButton) {
- // Remove the handlers of "mousemove" and "mouseup"
- movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler);
- movementListenedElement.removeEventListener('mousemove', self.mouseMoveHandler);
- document.removeEventListener('mouseup', self.mouseUpHandler);
- self.endDrag();
- }
- };
-
- this.target.addEventListener('mousedown', this.mouseDownHandler);
- if (this.clickButton == 2) {
- this.target.addEventListener('contextmenu', this.preventDefault);
- }
- }
-
- preventDefault(e) {
- e.preventDefault();
- }
-
- unlistenDOMElement() {
- this.target.removeEventListener('mousedown', this.mouseDownHandler);
- if (this.clickButton == 2) {
- this.target.removeEventListener('contextmenu', this.preventDefault);
- }
- }
-
- /* Subclasses will override the following methods */
- clicked(location) {
- }
-
- startDrag() {
- }
-
- dragTo(location, movement) {
- }
-
- endDrag() {
- }
-}
-
-class DragScroll extends MouseClickDrag {
-
- dragTo(location, movement) {
- this.blueprint.scrollDelta([-movement[0], -movement[1]]);
- }
-
-}
-
-class Select extends MouseClickDrag {
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.stepSize = options?.stepSize;
- this.mousePosition = [0, 0];
- this.selectorElement = this.blueprint.selectorElement;
- }
-
- startDrag() {
- this.selectorElement.startSelecting(this.clickedPosition);
- }
-
- dragTo(location, movement) {
- this.selectorElement.doSelecting(location);
- }
-
- endDrag() {
- if (this.started) {
- this.selectorElement.finishSelecting();
- } else {
- this.blueprint.unselectAll();
- }
- }
-}
-
-class MouseWheel extends Pointing {
-
- /**
- *
- * @param {HTMLElement} target
- * @param {import("../Blueprint").Blueprint} blueprint
- * @param {Object} options
- */
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.looseTarget = options?.looseTarget ?? true;
- let self = this;
-
- this.mouseWheelHandler = function (e) {
- e.preventDefault();
- const location = self.getLocation(e);
- self.wheel(Math.sign(e.deltaY), location);
- };
-
- this.movementSpace.addEventListener('wheel', this.mouseWheelHandler, false);
- // Prevent movement space from being scrolled
- this.movementSpace.parentElement?.addEventListener('wheel', e => e.preventDefault());
- }
-
- /* Subclasses will override the following method */
- wheel(variation, location) {
-
- }
-}
-
-class Zoom extends MouseWheel {
- wheel(variation, location) {
- let zoomLevel = this.blueprint.getZoom();
- zoomLevel -= variation;
- this.blueprint.setZoom(zoomLevel, location);
- }
-}
-
-/**
- * A Graph Entity is an element that can stay directly (as a first child) on the blueprint grid. Those entities are either nodes or links
- */
-class GraphEntity extends HTMLElement {
- /**
- *
- * @param {import("../template/Template").default} template The template to render this node
- */
- constructor(template) {
- super();
- /** @type {import("../Blueprint").Blueprint}" */
- this.blueprint = null;
- this.template = template;
- }
-
- connectedCallback() {
- this.blueprint = this.closest('u-blueprint');
- this.append(...this.template.getElements(this));
- }
-
- // Subclasses want to rewrite this
- render() {
- return ''
- }
-}
-
-/**
- * @typedef {import("../graph/GraphNode").default} GraphNode
- */
-class Template {
-
- /**
- * Computes the html content of the target element.
- * @param {GraphNode} element Target element
- * @returns The computed html
- */
- render(element) {
- return ``
- }
-
- /**
- * Returns the html elements rendered by this template.
- * @param {GraphNode} element Target element
- * @returns The rendered elements
- */
- getElements(element) {
- let aDiv = document.createElement('div');
- aDiv.innerHTML = this.render(element);
- return aDiv.childNodes
- }
-}
-
-class BlueprintTemplate extends Template {
- header(element) {
- return `
-
- `
- }
-
- overlay() {
- return `
-
- `
- }
-
- /**
- *
- * @param {import("../Blueprint").Blueprint} element
- * @returns
- */
- viewport(element) {
- return `
-
- `
- }
-
- /**
- * Computes the html content of the target element.
- * @param {HTMLElement} element Target element
- * @returns The computed html
- */
- render(element) {
- return `
- ${this.header(element)}
- ${this.overlay(element)}
- ${this.viewport(element)}
- `
- }
-}
-
-class OrderedIndexArray {
-
- /**
- * @param {(arrayElement: number) => number} compareFunction A function that, given acouple of elements of the array telles what order are they on.
- * @param {(number|array)} value Initial length or array to copy from
- */
- constructor(comparisonValueSupplier = (a) => a, value = null) {
- this.array = new Uint32Array(value);
- this.comparisonValueSupplier = comparisonValueSupplier;
- this.length = 0;
- this.currentPosition = 0;
- }
-
- /**
- *
- * @param {number} index The index of the value to return
- * @returns The element of the array
- */
- get(index) {
- if (index >= 0 && index < this.length) {
- return this.array[index]
- }
- return null
- }
-
- /**
- * Returns the array used by this object.
- * @returns The array.
- */
- getArray() {
- return this.array
- }
-
- /**
- * Get the position that the value supplied should (or does) occupy in the aray.
- * @param {number} value The value to look for (it doesn't have to be part of the array).
- * @returns The position index.
- */
- getPosition(value) {
- let l = 0;
- let r = this.length;
- while (l < r) {
- let m = Math.floor((l + r) / 2);
- if (this.comparisonValueSupplier(this.array[m]) < value) {
- l = m + 1;
- } else {
- r = m;
- }
- }
- return l
- }
-
- reserve(length) {
- if (this.array.length < length) {
- let newArray = new Uint32Array(length);
- newArray.set(this.array);
- this.array = newArray;
- }
- }
-
- /**
- * Inserts the element in the array.
- * @param element {number} The value to insert into the array.
- * @returns {number} The position into occupied by value into the array.
- */
- insert(element, comparisonValue = null) {
- let position = this.getPosition(this.comparisonValueSupplier(element));
- if (
- position < this.currentPosition
- || comparisonValue != null && position == this.currentPosition && this.comparisonValueSupplier(element) < comparisonValue) {
- ++this.currentPosition;
- }
- /*
- let newArray = new Uint32Array(this.array.length + 1)
- newArray.set(this.array.subarray(0, position), 0)
- newArray[position] = element
- newArray.set(this.array.subarray(position), position + 1)
- this.array = newArray
- */
- this.shiftRight(position);
- this.array[position] = element;
- ++this.length;
- return position
- }
-
- /**
- * Removes the element from the array.
- * @param {number} value The value of the element to be remove.
- */
- remove(element) {
- let position = this.getPosition(this.comparisonValueSupplier(element));
- if (this.array[position] == element) {
- this.removeAt(position);
- }
- }
-
- /**
- * Removes the element into the specified position from the array.
- * @param {number} position The index of the element to be remove.
- */
- removeAt(position) {
- if (position < this.currentPosition) {
- --this.currentPosition;
- }
- /*
- let newArray = new Uint32Array(this.array.length - 1)
- newArray.set(this.array.subarray(0, position), 0)
- newArray.set(this.array.subarray(position + 1), position)
- this.array = newArray
- */
- this.shiftLeft(position);
- --this.length;
- return position
- }
-
- getNext() {
- if (this.currentPosition >= 0 && this.currentPosition < this.length) {
- return this.get(this.currentPosition)
- }
- return null
- }
-
- getNextValue() {
- if (this.currentPosition >= 0 && this.currentPosition < this.length) {
- return this.comparisonValueSupplier(this.get(this.currentPosition))
- } else {
- return Number.MAX_SAFE_INTEGER
- }
- }
-
- getPrev() {
- if (this.currentPosition > 0) {
- return this.get(this.currentPosition - 1)
- }
- return null
- }
-
- getPrevValue() {
- if (this.currentPosition > 0) {
- return this.comparisonValueSupplier(this.get(this.currentPosition - 1))
- } else {
- return Number.MIN_SAFE_INTEGER
- }
- }
-
- shiftLeft(leftLimit, steps = 1) {
- this.array.set(this.array.subarray(leftLimit + steps), leftLimit);
- }
-
- shiftRight(leftLimit, steps = 1) {
- this.array.set(this.array.subarray(leftLimit, -steps), leftLimit + steps);
- }
-}
-
-class FastSelectionModel {
-
- /**
- * @typedef {{
- * primaryInf: number,
- * primarySup: number,
- * secondaryInf: number,
- * secondarySup: number
- * }} BoundariesInfo
- * @typedef {{
- * primaryBoundary: number,
- * secondaryBoundary: number,
- * insertionPosition: number,
- * rectangle: number
- * onSecondaryAxis: Boolean
- * }} Metadata
- * @typedef {numeric} Rectangle
- * @param {number[]} initialPosition Coordinates of the starting point of selection [primaryAxisValue, secondaryAxisValue].
- * @param {Rectangle[]} rectangles Rectangles that can be selected by this object.
- * @param {(rect: Rectangle) => BoundariesInfo} boundariesFunc A function that, given a rectangle, it provides the boundaries of such rectangle.
- * @param {(rect: Rectangle, selected: bool) => void} selectFunc A function that selects or deselects individual rectangles.
- */
- constructor(initialPosition, rectangles, boundariesFunc, selectFunc) {
- this.initialPosition = initialPosition;
- this.finalPosition = initialPosition;
- /** @type Metadata[] */
- this.metadata = new Array(rectangles.length);
- this.primaryOrder = new OrderedIndexArray((element) => this.metadata[element].primaryBoundary);
- this.secondaryOrder = new OrderedIndexArray((element) => this.metadata[element].secondaryBoundary);
- this.selectFunc = selectFunc;
- this.rectangles = rectangles;
- this.primaryOrder.reserve(this.rectangles.length);
- this.secondaryOrder.reserve(this.rectangles.length);
- rectangles.forEach((rect, index) => {
- /** @type Metadata */
- let rectangleMetadata = {
- primaryBoundary: this.initialPosition[0],
- secondaryBoundary: this.initialPosition[1],
- rectangle: index, // used to move both expandings inside the this.metadata array
- onSecondaryAxis: false
- };
- this.metadata[index] = rectangleMetadata;
- selectFunc(rect, false); // Initially deselected (Eventually)
- const rectangleBoundaries = boundariesFunc(rect);
-
- // Secondary axis first because it may be inserted in this.secondaryOrder during the primary axis check
- if (this.initialPosition[1] < rectangleBoundaries.secondaryInf) { // Initial position is before the rectangle
- rectangleMetadata.secondaryBoundary = rectangleBoundaries.secondaryInf;
- } else if (rectangleBoundaries.secondarySup < this.initialPosition[1]) { // Initial position is after the rectangle
- rectangleMetadata.secondaryBoundary = rectangleBoundaries.secondarySup;
- } else {
- rectangleMetadata.onSecondaryAxis = true;
- }
-
- if (this.initialPosition[0] < rectangleBoundaries.primaryInf) { // Initial position is before the rectangle
- rectangleMetadata.primaryBoundary = rectangleBoundaries.primaryInf;
- this.primaryOrder.insert(index);
- } else if (rectangleBoundaries.primarySup < this.initialPosition[0]) { // Initial position is after the rectangle
- rectangleMetadata.primaryBoundary = rectangleBoundaries.primarySup;
- this.primaryOrder.insert(index);
- } else { // Initial lays inside the rectangle (considering just this axis)
- // Secondary order depends on primary order, if primary boundaries are not satisfied, the element is not watched for secondary ones
- if (rectangleBoundaries.secondarySup < this.initialPosition[1] || this.initialPosition[1] < rectangleBoundaries.secondaryInf) {
- this.secondaryOrder.insert(index);
- } else {
- selectFunc(rect, true);
- }
- }
- });
- this.primaryOrder.currentPosition = this.primaryOrder.getPosition(this.initialPosition[0]);
- this.secondaryOrder.currentPosition = this.secondaryOrder.getPosition(this.initialPosition[1]);
- this.computeBoundaries(this.initialPosition);
- }
-
- computeBoundaries() {
- this.boundaries = {
- // Primary axis negative expanding
- primaryN: {
- v: this.primaryOrder.getPrevValue(),
- i: this.primaryOrder.getPrev()
- },
- primaryP: {
- v: this.primaryOrder.getNextValue(),
- i: this.primaryOrder.getNext()
- },
- // Secondary axis negative expanding
- secondaryN: {
- v: this.secondaryOrder.getPrevValue(),
- i: this.secondaryOrder.getPrev()
- },
- // Secondary axis positive expanding
- secondaryP: {
- v: this.secondaryOrder.getNextValue(),
- i: this.secondaryOrder.getNext()
- }
- };
- }
-
- selectTo(finalPosition) {
- const direction = [
- Math.sign(finalPosition[0] - this.initialPosition[0]),
- Math.sign(finalPosition[1] - this.initialPosition[1])
- ];
- const primaryBoundaryCrossed = (index, added) => {
- if (this.metadata[index].onSecondaryAxis) {
- this.selectFunc(this.rectangles[index], added);
- } else {
- if (added) {
- this.secondaryOrder.insert(index, finalPosition[1]);
- const secondaryBoundary = this.metadata[index].secondaryBoundary;
- if (
- // If inserted before the current position
- Math.sign(finalPosition[1] - secondaryBoundary) == direction[1]
- // And after initial position
- && Math.sign(secondaryBoundary - this.initialPosition[1]) == direction[1]
- ) {
- // Secondary axis is already satisfied then
- this.selectFunc(this.rectangles[index], true);
- }
- } else {
- this.selectFunc(this.rectangles[index], false);
- this.secondaryOrder.remove(index);
- }
- }
- this.computeBoundaries(finalPosition);
- this.selectTo(finalPosition);
- };
-
- if (finalPosition[0] < this.boundaries.primaryN.v) {
- --this.primaryOrder.currentPosition;
- primaryBoundaryCrossed(
- this.boundaries.primaryN.i,
- this.initialPosition[0] > this.boundaries.primaryN.v && finalPosition[0] < this.initialPosition[0]);
- } else if (finalPosition[0] > this.boundaries.primaryP.v) {
- ++this.primaryOrder.currentPosition;
- primaryBoundaryCrossed(
- this.boundaries.primaryP.i,
- this.initialPosition[0] < this.boundaries.primaryP.v && this.initialPosition[0] < finalPosition[0]);
- }
-
-
- const secondaryBoundaryCrossed = (index, added) => {
- this.selectFunc(this.rectangles[index], added);
- this.computeBoundaries(finalPosition);
- this.selectTo(finalPosition);
- };
-
- if (finalPosition[1] < this.boundaries.secondaryN.v) {
- --this.secondaryOrder.currentPosition;
- secondaryBoundaryCrossed(
- this.boundaries.secondaryN.i,
- this.initialPosition[1] > this.boundaries.secondaryN.v && finalPosition[1] < this.initialPosition[1]);
- } else if (finalPosition[1] > this.boundaries.secondaryP.v) {
- ++this.secondaryOrder.currentPosition;
- secondaryBoundaryCrossed(
- this.boundaries.secondaryP.i,
- this.initialPosition[1] < this.boundaries.secondaryP.v && this.initialPosition[1] < finalPosition[1]);
- }
- this.finalPosition = finalPosition;
- }
-
-}
-
-class GraphSelector extends GraphEntity {
-
- constructor() {
- super(new Template());
- /**
- * @type {import("./GraphSelector").default}
- */
- this.selectionModel = null;
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.classList.add('ueb-selector');
- this.dataset.selecting = "false";
- }
-
- /**
- * Create a selection rectangle starting from the specified position
- * @param {number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element)
- */
- startSelecting(initialPosition) {
- initialPosition = this.blueprint.compensateTranslation(initialPosition);
- // Set initial position
- this.style.setProperty('--ueb-select-from-x', initialPosition[0]);
- this.style.setProperty('--ueb-select-from-y', initialPosition[1]);
- // Final position coincide with the initial position, at the beginning of selection
- this.style.setProperty('--ueb-select-to-x', initialPosition[0]);
- this.style.setProperty('--ueb-select-to-y', initialPosition[1]);
- this.dataset.selecting = "true";
- this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.nodes, 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) {
- finalPosition = this.blueprint.compensateTranslation(finalPosition);
- this.style.setProperty('--ueb-select-to-x', finalPosition[0]);
- this.style.setProperty('--ueb-select-to-y', finalPosition[1]);
- this.selectionModel.selectTo(finalPosition);
- }
-
- finishSelecting() {
- this.dataset.selecting = "false";
- this.selectionModel = null;
- }
-}
-
-customElements.define('u-selector', GraphSelector);
-
-/**
- * @typedef {import("./graph/GraphNode").default} GraphNode
- */
-class Blueprint extends GraphEntity {
-
- insertChildren() {
- this.querySelector('[data-nodes]').append(...this.nodes);
- }
-
- constructor() {
- super(new BlueprintTemplate());
- /** @type {GraphNode[]}" */
- this.nodes = new Array();
- this.expandGridSize = 400;
- /** @type {HTMLElement} */
- this.gridElement = null;
- /** @type {HTMLElement} */
- this.viewportElement = null;
- /** @type {HTMLElement} */
- this.overlayElement = null;
- /** @type {GraphSelector} */
- this.selectorElement = null;
- /** @type {HTMLElement} */
- this.nodesContainerElement = null;
- this.dragObject = null;
- this.selectObject = null;
- /** @type {Array} */
- this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0];
- /** @type {Array} */
- this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0];
- /** @type {number} */
- this.zoom = 0;
- /** @type {HTMLElement} */
- this.headerElement = null;
- /** @type {(node: GraphNode) => BoundariesInfo} */
- this.nodeBoundariesSupplier = (node) => {
- let rect = node.getBoundingClientRect();
- let gridRect = this.nodesContainerElement.getBoundingClientRect();
- const scaleCorrection = 1 / this.getScale();
- return {
- primaryInf: (rect.left - gridRect.left) * scaleCorrection,
- primarySup: (rect.right - gridRect.right) * scaleCorrection,
- // Counter intuitive here: the y (secondary axis is positive towards the bottom, therefore upper bound "sup" is bottom)
- secondaryInf: (rect.top - gridRect.top) * scaleCorrection,
- secondarySup: (rect.bottom - gridRect.bottom) * scaleCorrection
- }
- };
- /** @type {(node: GraphNode, selected: bool) => void}} */
- this.nodeSelectToggleFunction = (node, selected) => {
- node.setSelected(selected);
- };
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.classList.add('ueb', `ueb-zoom-${this.zoom}`);
-
- this.headerElement = this.querySelector('.ueb-viewport-header');
- console.assert(this.headerElement, "Header element not provided by the template.");
- this.overlayElement = this.querySelector('.ueb-viewport-overlay');
- console.assert(this.overlayElement, "Overlay element not provided by the template.");
- this.viewportElement = this.querySelector('.ueb-viewport-body');
- console.assert(this.viewportElement, "Viewport element not provided by the template.");
- this.gridElement = this.viewportElement.querySelector('.ueb-grid');
- console.assert(this.gridElement, "Grid element not provided by the template.");
- this.selectorElement = new GraphSelector();
- this.nodesContainerElement = this.querySelector('[data-nodes]');
- console.assert(this.nodesContainerElement, "Nodes container element not provided by the template.");
- this.nodesContainerElement.append(this.selectorElement);
- this.insertChildren();
-
- this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
- clickButton: 2,
- moveEverywhere: true,
- exitAnyButton: false
- });
-
- this.zoomObject = new Zoom(this.getGridDOMElement(), this, {
- looseTarget: true
- });
-
- this.selectObject = new Select(this.getGridDOMElement(), this, {
- clickButton: 0,
- moveEverywhere: true,
- exitAnyButton: true
- });
- }
-
- getGridDOMElement() {
- return this.gridElement
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this.dragObject.unlistenDOMElement();
- this.selectObject.unlistenDOMElement();
- }
-
- getScroll() {
- return [this.viewportElement.scrollLeft, this.viewportElement.scrollTop]
- }
-
- setScroll(value, smooth = false) {
- this.scroll = value;
- if (!smooth) {
- this.viewportElement.scroll(value[0], value[1]);
- } else {
- this.viewportElement.scroll({
- left: value[0],
- top: value[1],
- behavior: 'smooth'
- });
- }
- }
-
- scrollDelta(delta, smooth = false) {
- const scrollMax = this.getScrollMax();
- let currentScroll = this.getScroll();
- let finalScroll = [
- currentScroll[0] + delta[0],
- currentScroll[1] + delta[1]
- ];
- let expand = [0, 0];
- for (let i = 0; i < 2; ++i) {
- if (delta[i] < 0 && finalScroll[i] < 0.25 * this.expandGridSize) {
- // Expand if scrolling is diminishing and the remainig space is less that a quarter of an expansion step
- expand[i] = finalScroll[i];
- if (expand[i] > 0) {
- // Final scroll is still in rage (more than zero) but we want to expand to negative (left or top)
- expand[i] = -this.expandGridSize;
- }
- } else if (delta[i] > 0 && finalScroll[i] > scrollMax[i] - 0.25 * this.expandGridSize) {
- // Expand if scrolling is increasing and the remainig space is less that a quarter of an expansion step
- expand[i] = finalScroll[i] - scrollMax[i];
- if (expand[i] < 0) {
- // Final scroll is still in rage (less than the maximum scroll) but we want to expand to positive (right or bottom)
- expand[i] = this.expandGridSize;
- }
- }
- }
- if (expand[0] != 0 || expand[1] != 0) {
- this.seamlessExpand(this.progressiveSnapToGrid(expand[0]), this.progressiveSnapToGrid(expand[1]));
- currentScroll = this.getScroll();
- finalScroll = [
- currentScroll[0] + delta[0],
- currentScroll[1] + delta[1]
- ];
- }
- this.setScroll(finalScroll, smooth);
- }
-
- scrollCenter() {
- const scroll = this.getScroll();
- const offset = [
- this.translateValue[0] - scroll[0],
- this.translateValue[1] - scroll[1]
- ];
- const targetOffset = this.getViewportSize().map(size => size / 2);
- const deltaOffset = [
- offset[0] - targetOffset[0],
- offset[1] - targetOffset[1]
- ];
- this.scrollDelta(deltaOffset, true);
- }
-
- getExpandGridSize() {
- return this.expandGridSize
- }
-
- getViewportSize() {
- return [
- this.viewportElement.clientWidth,
- this.viewportElement.clientHeight
- ]
- }
-
- /**
- * Get the scroll limits
- * @return {array} The horizonal and vertical maximum scroll limits
- */
- getScrollMax() {
- return [
- this.viewportElement.scrollWidth - this.viewportElement.clientWidth,
- this.viewportElement.scrollHeight - this.viewportElement.clientHeight
- ]
- }
-
- /**
- * Expand the grid, considers the absolute value of params
- * @param {number} x - Horizontal expansion value
- * @param {number} y - Vertical expansion value
- */
- _expand(x, y) {
- x = Math.round(Math.abs(x));
- y = Math.round(Math.abs(y));
- this.additional = [this.additional[0] + x, this.additional[1] + y];
- if (this.gridElement) {
- this.gridElement.style.setProperty('--ueb-additional-x', this.additional[0]);
- this.gridElement.style.setProperty('--ueb-additional-y', this.additional[1]);
- }
- }
-
- /**
- * Moves the content of the grid according to the coordinates
- * @param {number} x - Horizontal translation value
- * @param {number} y - Vertical translation value
- */
- _translate(x, y) {
- x = Math.round(x);
- y = Math.round(y);
- this.translateValue = [this.translateValue[0] + x, this.translateValue[1] + y];
- if (this.gridElement) {
- this.gridElement.style.setProperty('--ueb-translate-x', this.translateValue[0]);
- this.gridElement.style.setProperty('--ueb-translate-y', this.translateValue[1]);
- }
- }
-
- /**
- * Expand the grind indefinitely, the content will remain into position
- * @param {number} x - Horizontal expand value (negative means left, positive means right)
- * @param {number} y - Vertical expand value (negative means top, positive means bottom)
- */
- seamlessExpand(x, y) {
- let scale = this.getScale();
- let scaledX = x / scale;
- let scaledY = y / scale;
- // First expand the grid to contain the additional space
- this._expand(scaledX, scaledY);
- // If the expansion is towards the left or top, then scroll back to give the illusion that the content is in the same position and translate it accordingly
- this._translate(scaledX < 0 ? -scaledX : 0, scaledY < 0 ? -scaledY : 0);
- if (x < 0) {
- this.viewportElement.scrollLeft -= x;
- }
- if (y < 0) {
- this.viewportElement.scrollTop -= y;
- }
- }
-
- progressiveSnapToGrid(x) {
- return this.expandGridSize * Math.round(x / this.expandGridSize + 0.5 * Math.sign(x))
- }
-
- getZoom() {
- return this.zoom
- }
-
- setZoom(zoom, center) {
- zoom = Utility.clamp(zoom, -12, 0);
- if (zoom == this.zoom) {
- return
- }
- let initialScale = this.getScale();
- this.classList.remove(`ueb-zoom-${this.zoom}`);
- this.classList.add(`ueb-zoom-${zoom}`);
- this.zoom = zoom;
-
-
- if (center) {
- let relativeScale = this.getScale() / initialScale;
- let newCenter = [
- relativeScale * center[0],
- relativeScale * center[1]
- ];
- this.scrollDelta([
- (newCenter[0] - center[0]) * initialScale,
- (newCenter[1] - center[1]) * initialScale
- ]);
- }
- }
-
- getScale() {
- return parseFloat(getComputedStyle(this.gridElement).getPropertyValue('--ueb-scale'))
- }
-
- compensateTranslation(position) {
- position[0] -= this.translateValue[0];
- position[1] -= this.translateValue[1];
- return position
- }
-
- /**
- * Unselect all nodes
- */
- unselectAll() {
- this.nodes.forEach(node => this.nodeSelectToggleFunction(node, false));
- }
-
- /**
- *
- * @param {...GraphNode} graphNodes
- */
- addNode(...graphNodes) {
- [...graphNodes].reduce(
- (s, e) => {
- s.push(e);
- return s
- },
- this.nodes);
- if (this.nodesContainerElement) {
- this.nodesContainerElement.append(...graphNodes);
- }
- }
-}
-
-customElements.define('u-blueprint', Blueprint);
-
-class Drag extends MouseClickDrag {
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.stepSize = parseInt(options?.stepSize);
- this.mousePosition = [0, 0];
- }
-
- snapToGrid(location) {
- return [
- this.stepSize * Math.round(location[0] / this.stepSize),
- this.stepSize * Math.round(location[1] / this.stepSize)
- ]
- }
-
- startDrag() {
- if (isNaN(this.stepSize) || this.stepSize <= 0) {
- this.stepSize = parseInt(getComputedStyle(this.target).getPropertyValue('--ueb-grid-snap'));
- if (isNaN(this.stepSize) || this.stepSize <= 0) {
- this.stepSize = 1;
- }
- }
- // Get the current mouse position
- this.mousePosition = this.stepSize != 1 ? this.snapToGrid(this.clickedPosition) : this.clickedPosition;
- }
-
- dragTo(location, movement) {
- const mousePosition = this.stepSize != 1 ? this.snapToGrid(location) : location;
- const d = [mousePosition[0] - this.mousePosition[0], mousePosition[1] - this.mousePosition[1]];
-
- if (d[0] == 0 && d[1] == 0) {
- return
- }
-
- this.target.dragDispatch(d);
-
- // Reassign the position of mouse
- this.mousePosition = mousePosition;
- }
-}
-
-class SelectableDraggable extends GraphEntity {
-
- constructor(template) {
- super(template);
- this.dragObject = null;
- this.location = [0, 0];
- this.selected = false;
-
- let self = this;
- this.dragHandler = (e) => {
- self.addLocation(e.detail.value);
- };
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.dragObject = new Drag(this, null, { // UDrag doesn't need blueprint
- looseTarget: true
- });
- }
-
- disconnectedCallback() {
- this.dragObject.unlistenDOMElement();
- }
-
- setLocation(value = [0, 0]) {
- this.location = value;
- this.style.setProperty('--ueb-position-x', this.location[0]);
- this.style.setProperty('--ueb-position-y', this.location[1]);
- }
-
- addLocation(value) {
- this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
- }
-
- dragDispatch(value) {
- if (!this.selected) {
- this.blueprint.unselectAll();
- this.setSelected(true);
- }
- let dragEvent = new CustomEvent('uDragSelected', {
- detail: {
- instigator: this,
- value: value
- },
- bubbles: false,
- cancelable: true,
- composed: false,
- });
- this.blueprint.dispatchEvent(dragEvent);
- }
-
- setSelected(value = true) {
- if (this.selected == value) {
- return
- }
- this.selected = value;
- if (this.selected) {
- this.classList.add('ueb-selected');
- this.blueprint.addEventListener('uDragSelected', this.dragHandler);
- } else {
- this.classList.remove('ueb-selected');
- this.blueprint.removeEventListener('uDragSelected', this.dragHandler);
- }
- }
-
-}
-
-class NodeTemplate extends Template {
-
- /**
- * Computes the html content of the target element.
- * @param {HTMLElement} element Target element
- * @returns The computed html
- */
- header(element) {
- return `
-
- `
- }
-
- /**
- * Computes the html content of the target element.
- * @param {HTMLElement} element Target element
- * @returns The computed html
- */
- body(element) {
- return `
-
-
-
- ${element.outputs.forEach((output, index) => `
-
- ${output.name}
-
-
- `) ?? ''}
-
-
- `
- }
-
- /**
- * Computes the html content of the target element.
- * @param {HTMLElement} element Target element
- * @returns The computed html
- */
- render(element) {
- return `
-
-
- ${this.header(element)}
- ${this.body(element)}
-
-
- `
- }
-}
-
-class GraphNode extends SelectableDraggable {
-
- constructor() {
- super(new NodeTemplate());
- this.graphNodeName = 'n/a';
- this.inputs = [];
- this.outputs = [];
- }
-
- connectedCallback() {
- this.getAttribute('type')?.trim();
- super.connectedCallback();
- this.classList.add('ueb-node');
- if (this.selected) {
- this.classList.add('ueb-selected');
- }
- this.style.setProperty('--ueb-position-x', this.location[0]);
- this.style.setProperty('--ueb-position-y', this.location[1]);
- }
-}
-
-customElements.define('u-node', GraphNode);
-
class Entity {
constructor(options = {}) {
/**
@@ -1296,6 +136,12 @@ class Entity {
continue
}
let defaultValue = properties[property];
+ if (defaultValue instanceof TypeInitialization) {
+ if (!defaultValue.showDefault) {
+ continue
+ }
+ defaultValue = defaultValue.value;
+ }
if (defaultValue instanceof Array) {
propertySetter(target, property, []);
defineAllAttributes(
@@ -1305,12 +151,6 @@ class Entity {
(t, _, v) => t.push(v));
continue
}
- if (defaultValue instanceof TypeInitialization) {
- if (!defaultValue.showDefault) {
- continue
- }
- defaultValue = defaultValue.value;
- }
if (defaultValue instanceof Function) {
defaultValue = Utility.sanitize(new defaultValue());
}
@@ -1321,7 +161,12 @@ class Entity {
}
}
-class Guid {
+class GuidEntity extends Entity {
+
+ static attributes = {
+ value: String
+ }
+
static generateGuid(random) {
let values = new Uint32Array(4);
if (random === true) {
@@ -1335,23 +180,37 @@ class Guid {
}
constructor(guid) {
- switch (guid?.constructor) {
- case String:
- this.value = guid;
- break
- case Guid:
- this.value = guid.value;
- break
- default:
- this.value = Guid.generateGuid(guid === true);
+ if (guid?.constructor === String) {
+ guid = {
+ value: guid
+ };
+ } else if (guid?.constructor === Boolean) {
+ guid = {
+ value: GuidEntity.generateGuid(guid == true)
+ };
}
+ super(guid);
}
- toString() {
- return this.value
+ getAttributes() {
+ return GuidEntity.attributes
}
}
+class LocalizedTextEntity extends Entity {
+
+ static attributes = {
+ namespace: "",
+ key: "",
+ value: ""
+ }
+
+ getAttributes() {
+ return LocalizedTextEntity.attributes
+ }
+
+}
+
class ObjectReferenceEntity extends Entity {
static attributes = {
@@ -1372,23 +231,22 @@ class ObjectReferenceEntity extends Entity {
}
}
-class LocalizedTextEntity extends Entity {
+class PinReferenceEntity extends Entity {
static attributes = {
- namespace: "",
- key: "",
- value: ""
+ objectName: String,
+ pinGuid: GuidEntity
}
getAttributes() {
- return LocalizedTextEntity.attributes
+ return PinReferenceEntity.attributes
}
-
}
class PinEntity$1 extends Entity {
+
static attributes = {
- PinId: Guid,
+ PinId: GuidEntity,
PinName: "",
PinFriendlyName: new TypeInitialization(new LocalizedTextEntity(), false),
PinToolTip: "",
@@ -1405,10 +263,10 @@ class PinEntity$1 extends Entity {
bIsWeakPointer: false,
bIsUObjectWrapper: false
},
- LinkedTo: Guid,
+ LinkedTo: [new TypeInitialization(null, false, PinReferenceEntity)],
DefaultValue: "",
AutogeneratedDefaultValue: "",
- PersistentGuid: Guid,
+ PersistentGuid: GuidEntity,
bHidden: false,
bNotConnectable: false,
bDefaultValueIsReadOnly: false,
@@ -1420,22 +278,14 @@ class PinEntity$1 extends Entity {
getAttributes() {
return PinEntity$1.attributes
}
+
+ isOutput() {
+ if (this.Direction === "EGPD_Output") {
+ return true
+ }
+ }
}
-var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
-
-function getDefaultExportFromCjs (x) {
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
-}
-
-var parsimmon_umd_min = {exports: {}};
-
-(function (module, exports) {
-!function(n,t){module.exports=t();}("undefined"!=typeof self?self:commonjsGlobal,function(){return function(n){var t={};function r(e){if(t[e])return t[e].exports;var u=t[e]={i:e,l:!1,exports:{}};return n[e].call(u.exports,u,u.exports,r),u.l=!0,u.exports}return r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e});},r.r=function(n){Object.defineProperty(n,"__esModule",{value:!0});},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},r.p="",r(r.s=0)}([function(n,t,r){function e(n){if(!(this instanceof e))return new e(n);this._=n;}var u=e.prototype;function o(n,t){for(var r=0;r>7),buf:function(n){var t=i(function(n,t,r,e){return n.concat(r===e.length-1?Buffer.from([t,0]).readUInt16BE(0):e.readUInt16BE(r))},[],n);return Buffer.from(f(function(n){return (n<<1&65535)>>8},t))}(r.buf)};}),r}function c(){return "undefined"!=typeof Buffer}function s(){if(!c())throw new Error("Buffer global does not exist; please use webpack if you need to parse Buffers in the browser.")}function l(n){s();var t=i(function(n,t){return n+t},0,n);if(t%8!=0)throw new Error("The bits ["+n.join(", ")+"] add up to "+t+" which is not an even number of bytes; the total should be divisible by 8");var r,u=t/8,o=(r=function(n){return n>48},i(function(n,t){return n||(r(t)?t:n)},null,n));if(o)throw new Error(o+" bit range requested exceeds 48 bit (6 byte) Number max.");return new e(function(t,r){var e=u+r;return e>t.length?x(r,u.toString()+" bytes"):b(e,i(function(n,t){var r=a(t,n.buf);return {coll:n.coll.concat(r.v),buf:r.buf}},{coll:[],buf:t.slice(r,e)},n).coll)})}function p(n,t){return new e(function(r,e){return s(),e+t>r.length?x(e,t+" bytes for "+n):b(e+t,r.slice(e,e+t))})}function h(n,t){if("number"!=typeof(r=t)||Math.floor(r)!==r||t<0||t>6)throw new Error(n+" requires integer length in range [0, 6].");var r;}function d(n){return h("uintBE",n),p("uintBE("+n+")",n).map(function(t){return t.readUIntBE(0,n)})}function v(n){return h("uintLE",n),p("uintLE("+n+")",n).map(function(t){return t.readUIntLE(0,n)})}function g(n){return h("intBE",n),p("intBE("+n+")",n).map(function(t){return t.readIntBE(0,n)})}function m(n){return h("intLE",n),p("intLE("+n+")",n).map(function(t){return t.readIntLE(0,n)})}function y(n){return n instanceof e}function E(n){return "[object Array]"==={}.toString.call(n)}function w(n){return c()&&Buffer.isBuffer(n)}function b(n,t){return {status:!0,index:n,value:t,furthest:-1,expected:[]}}function x(n,t){return E(t)||(t=[t]),{status:!1,index:-1,value:null,furthest:n,expected:t}}function B(n,t){if(!t)return n;if(n.furthest>t.furthest)return n;var r=n.furthest===t.furthest?function(n,t){if(function(){if(void 0!==e._supportsSet)return e._supportsSet;var n="undefined"!=typeof Set;return e._supportsSet=n,n}()&&Array.from){for(var r=new Set(n),u=0;u=0;){if(i in r){e=r[i].line,0===o&&(o=r[i].lineStart);break}"\n"===n.charAt(i)&&(u++,0===o&&(o=i+1)),i--;}var f=e+u,a=t-o;return r[t]={line:f,lineStart:o},{offset:t,line:f+1,column:a+1}}function _(n){if(!y(n))throw new Error("not a parser: "+n)}function L(n,t){return "string"==typeof n?n.charAt(t):n[t]}function O(n){if("number"!=typeof n)throw new Error("not a number: "+n)}function k(n){if("function"!=typeof n)throw new Error("not a function: "+n)}function P(n){if("string"!=typeof n)throw new Error("not a string: "+n)}var q=2,A=3,I=8,F=5*I,M=4*I,z=" ";function R(n,t){return new Array(t+1).join(n)}function U(n,t,r){var e=t-n.length;return e<=0?n:R(r,e)+n}function W(n,t,r,e){return {from:n-t>0?n-t:0,to:n+r>e?e:n+r}}function D(n,t){var r,e,u,o,a,c=t.index,s=c.offset,l=1;if(s===n.length)return "Got the end of the input";if(w(n)){var p=s-s%I,h=s-p,d=W(p,F,M+I,n.length),v=f(function(n){return f(function(n){return U(n.toString(16),2,"0")},n)},function(n,t){var r=n.length,e=[],u=0;if(r<=t)return [n.slice()];for(var o=0;o=4&&(r+=1),l=2,u=f(function(n){return n.length<=4?n.join(" "):n.slice(0,4).join(" ")+" "+n.slice(4).join(" ")},v),(a=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(a=2);}else {var g=n.split(/\r\n|[\n\r\u2028\u2029]/);r=c.column-1,e=c.line-1,o=W(e,q,A,g.length),u=g.slice(o.from,o.to),a=o.to.toString().length;}var m=e-o.from;return w(n)&&(a=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(a=2),i(function(t,e,u){var i,f=u===m,c=f?"> ":z;return i=w(n)?U((8*(o.from+u)).toString(16),a,"0"):U((o.from+u+1).toString(),a," "),[].concat(t,[c+i+" | "+e],f?[z+R(" ",a)+" | "+U("",r," ")+R("^",l)]:[])},[],u).join("\n")}function N(n,t){return ["\n","-- PARSING FAILED "+R("-",50),"\n\n",D(n,t),"\n\n",(r=t.expected,1===r.length?"Expected:\n\n"+r[0]:"Expected one of the following: \n\n"+r.join(", ")),"\n"].join("");var r;}function G(n){return void 0!==n.flags?n.flags:[n.global?"g":"",n.ignoreCase?"i":"",n.multiline?"m":"",n.unicode?"u":"",n.sticky?"y":""].join("")}function C(){for(var n=[].slice.call(arguments),t=n.length,r=0;r=2?O(t):t=0;var r=function(n){return RegExp("^(?:"+n.source+")",G(n))}(n),u=""+n;return e(function(n,e){var o=r.exec(n.slice(e));if(o){if(0<=t&&t<=o.length){var i=o[0],f=o[t];return b(e+i.length,f)}return x(e,"valid match group (0 to "+o.length+") in "+u)}return x(e,u)})}function X(n){return e(function(t,r){return b(r,n)})}function Y(n){return e(function(t,r){return x(r,n)})}function Z(n){if(y(n))return e(function(t,r){var e=n._(t,r);return e.index=r,e.value="",e});if("string"==typeof n)return Z(K(n));if(n instanceof RegExp)return Z(Q(n));throw new Error("not a string, regexp, or parser: "+n)}function $(n){return _(n),e(function(t,r){var e=n._(t,r),u=t.slice(r,e.index);return e.status?x(r,'not "'+u+'"'):b(r,null)})}function nn(n){return k(n),e(function(t,r){var e=L(t,r);return r=n.length?x(t,"any character/byte"):b(t+1,L(n,t))}),on=e(function(n,t){return b(n.length,n.slice(t))}),fn=e(function(n,t){return t=0}).desc(t)},e.optWhitespace=pn,e.Parser=e,e.range=function(n,t){return nn(function(r){return n<=r&&r<=t}).desc(n+"-"+t)},e.regex=Q,e.regexp=Q,e.sepBy=V,e.sepBy1=H,e.seq=C,e.seqMap=J,e.seqObj=function(){for(var n,t={},r=0,u=(n=arguments,Array.prototype.slice.call(n)),o=u.length,i=0;i255)throw new Error("Value specified to byte constructor ("+n+"=0x"+n.toString(16)+") is larger in value than a single byte.");var t=(n>15?"0x":"0x0")+n.toString(16);return e(function(r,e){var u=L(r,e);return u===n?b(e+1,u):x(e,t)})},buffer:function(n){return p("buffer",n).map(function(n){return Buffer.from(n)})},encodedString:function(n,t){return p("string",t).map(function(t){return t.toString(n)})},uintBE:d,uint8BE:d(1),uint16BE:d(2),uint32BE:d(4),uintLE:v,uint8LE:v(1),uint16LE:v(2),uint32LE:v(4),intBE:g,int8BE:g(1),int16BE:g(2),int32BE:g(4),intLE:m,int8LE:m(1),int16LE:m(2),int32LE:m(4),floatBE:p("floatBE",4).map(function(n){return n.readFloatBE(0)}),floatLE:p("floatLE",4).map(function(n){return n.readFloatLE(0)}),doubleBE:p("doubleBE",8).map(function(n){return n.readDoubleBE(0)}),doubleLE:p("doubleLE",8).map(function(n){return n.readDoubleLE(0)})},n.exports=e;}])});
-}(parsimmon_umd_min));
-
-var Parsimmon = /*@__PURE__*/getDefaultExportFromCjs(parsimmon_umd_min.exports);
-
class FunctionReferenceEntity extends Entity {
static attributes = {
MemberParent: ObjectReferenceEntity,
@@ -1470,10 +320,25 @@ class Integer extends Entity {
}
}
+var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+function getDefaultExportFromCjs (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+}
+
+var parsimmon_umd_min = {exports: {}};
+
+(function (module, exports) {
+!function(n,t){module.exports=t();}("undefined"!=typeof self?self:commonjsGlobal,function(){return function(n){var t={};function r(e){if(t[e])return t[e].exports;var u=t[e]={i:e,l:!1,exports:{}};return n[e].call(u.exports,u,u.exports,r),u.l=!0,u.exports}return r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e});},r.r=function(n){Object.defineProperty(n,"__esModule",{value:!0});},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},r.p="",r(r.s=0)}([function(n,t,r){function e(n){if(!(this instanceof e))return new e(n);this._=n;}var u=e.prototype;function o(n,t){for(var r=0;r>7),buf:function(n){var t=i(function(n,t,r,e){return n.concat(r===e.length-1?Buffer.from([t,0]).readUInt16BE(0):e.readUInt16BE(r))},[],n);return Buffer.from(f(function(n){return (n<<1&65535)>>8},t))}(r.buf)};}),r}function c(){return "undefined"!=typeof Buffer}function s(){if(!c())throw new Error("Buffer global does not exist; please use webpack if you need to parse Buffers in the browser.")}function l(n){s();var t=i(function(n,t){return n+t},0,n);if(t%8!=0)throw new Error("The bits ["+n.join(", ")+"] add up to "+t+" which is not an even number of bytes; the total should be divisible by 8");var r,u=t/8,o=(r=function(n){return n>48},i(function(n,t){return n||(r(t)?t:n)},null,n));if(o)throw new Error(o+" bit range requested exceeds 48 bit (6 byte) Number max.");return new e(function(t,r){var e=u+r;return e>t.length?x(r,u.toString()+" bytes"):b(e,i(function(n,t){var r=a(t,n.buf);return {coll:n.coll.concat(r.v),buf:r.buf}},{coll:[],buf:t.slice(r,e)},n).coll)})}function p(n,t){return new e(function(r,e){return s(),e+t>r.length?x(e,t+" bytes for "+n):b(e+t,r.slice(e,e+t))})}function h(n,t){if("number"!=typeof(r=t)||Math.floor(r)!==r||t<0||t>6)throw new Error(n+" requires integer length in range [0, 6].");var r;}function d(n){return h("uintBE",n),p("uintBE("+n+")",n).map(function(t){return t.readUIntBE(0,n)})}function v(n){return h("uintLE",n),p("uintLE("+n+")",n).map(function(t){return t.readUIntLE(0,n)})}function g(n){return h("intBE",n),p("intBE("+n+")",n).map(function(t){return t.readIntBE(0,n)})}function m(n){return h("intLE",n),p("intLE("+n+")",n).map(function(t){return t.readIntLE(0,n)})}function y(n){return n instanceof e}function E(n){return "[object Array]"==={}.toString.call(n)}function w(n){return c()&&Buffer.isBuffer(n)}function b(n,t){return {status:!0,index:n,value:t,furthest:-1,expected:[]}}function x(n,t){return E(t)||(t=[t]),{status:!1,index:-1,value:null,furthest:n,expected:t}}function B(n,t){if(!t)return n;if(n.furthest>t.furthest)return n;var r=n.furthest===t.furthest?function(n,t){if(function(){if(void 0!==e._supportsSet)return e._supportsSet;var n="undefined"!=typeof Set;return e._supportsSet=n,n}()&&Array.from){for(var r=new Set(n),u=0;u=0;){if(i in r){e=r[i].line,0===o&&(o=r[i].lineStart);break}"\n"===n.charAt(i)&&(u++,0===o&&(o=i+1)),i--;}var f=e+u,a=t-o;return r[t]={line:f,lineStart:o},{offset:t,line:f+1,column:a+1}}function _(n){if(!y(n))throw new Error("not a parser: "+n)}function L(n,t){return "string"==typeof n?n.charAt(t):n[t]}function O(n){if("number"!=typeof n)throw new Error("not a number: "+n)}function k(n){if("function"!=typeof n)throw new Error("not a function: "+n)}function P(n){if("string"!=typeof n)throw new Error("not a string: "+n)}var q=2,A=3,I=8,F=5*I,M=4*I,z=" ";function R(n,t){return new Array(t+1).join(n)}function U(n,t,r){var e=t-n.length;return e<=0?n:R(r,e)+n}function W(n,t,r,e){return {from:n-t>0?n-t:0,to:n+r>e?e:n+r}}function D(n,t){var r,e,u,o,a,c=t.index,s=c.offset,l=1;if(s===n.length)return "Got the end of the input";if(w(n)){var p=s-s%I,h=s-p,d=W(p,F,M+I,n.length),v=f(function(n){return f(function(n){return U(n.toString(16),2,"0")},n)},function(n,t){var r=n.length,e=[],u=0;if(r<=t)return [n.slice()];for(var o=0;o=4&&(r+=1),l=2,u=f(function(n){return n.length<=4?n.join(" "):n.slice(0,4).join(" ")+" "+n.slice(4).join(" ")},v),(a=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(a=2);}else {var g=n.split(/\r\n|[\n\r\u2028\u2029]/);r=c.column-1,e=c.line-1,o=W(e,q,A,g.length),u=g.slice(o.from,o.to),a=o.to.toString().length;}var m=e-o.from;return w(n)&&(a=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(a=2),i(function(t,e,u){var i,f=u===m,c=f?"> ":z;return i=w(n)?U((8*(o.from+u)).toString(16),a,"0"):U((o.from+u+1).toString(),a," "),[].concat(t,[c+i+" | "+e],f?[z+R(" ",a)+" | "+U("",r," ")+R("^",l)]:[])},[],u).join("\n")}function N(n,t){return ["\n","-- PARSING FAILED "+R("-",50),"\n\n",D(n,t),"\n\n",(r=t.expected,1===r.length?"Expected:\n\n"+r[0]:"Expected one of the following: \n\n"+r.join(", ")),"\n"].join("");var r;}function G(n){return void 0!==n.flags?n.flags:[n.global?"g":"",n.ignoreCase?"i":"",n.multiline?"m":"",n.unicode?"u":"",n.sticky?"y":""].join("")}function C(){for(var n=[].slice.call(arguments),t=n.length,r=0;r=2?O(t):t=0;var r=function(n){return RegExp("^(?:"+n.source+")",G(n))}(n),u=""+n;return e(function(n,e){var o=r.exec(n.slice(e));if(o){if(0<=t&&t<=o.length){var i=o[0],f=o[t];return b(e+i.length,f)}return x(e,"valid match group (0 to "+o.length+") in "+u)}return x(e,u)})}function X(n){return e(function(t,r){return b(r,n)})}function Y(n){return e(function(t,r){return x(r,n)})}function Z(n){if(y(n))return e(function(t,r){var e=n._(t,r);return e.index=r,e.value="",e});if("string"==typeof n)return Z(K(n));if(n instanceof RegExp)return Z(Q(n));throw new Error("not a string, regexp, or parser: "+n)}function $(n){return _(n),e(function(t,r){var e=n._(t,r),u=t.slice(r,e.index);return e.status?x(r,'not "'+u+'"'):b(r,null)})}function nn(n){return k(n),e(function(t,r){var e=L(t,r);return r=n.length?x(t,"any character/byte"):b(t+1,L(n,t))}),on=e(function(n,t){return b(n.length,n.slice(t))}),fn=e(function(n,t){return t=0}).desc(t)},e.optWhitespace=pn,e.Parser=e,e.range=function(n,t){return nn(function(r){return n<=r&&r<=t}).desc(n+"-"+t)},e.regex=Q,e.regexp=Q,e.sepBy=V,e.sepBy1=H,e.seq=C,e.seqMap=J,e.seqObj=function(){for(var n,t={},r=0,u=(n=arguments,Array.prototype.slice.call(n)),o=u.length,i=0;i255)throw new Error("Value specified to byte constructor ("+n+"=0x"+n.toString(16)+") is larger in value than a single byte.");var t=(n>15?"0x":"0x0")+n.toString(16);return e(function(r,e){var u=L(r,e);return u===n?b(e+1,u):x(e,t)})},buffer:function(n){return p("buffer",n).map(function(n){return Buffer.from(n)})},encodedString:function(n,t){return p("string",t).map(function(t){return t.toString(n)})},uintBE:d,uint8BE:d(1),uint16BE:d(2),uint32BE:d(4),uintLE:v,uint8LE:v(1),uint16LE:v(2),uint32LE:v(4),intBE:g,int8BE:g(1),int16BE:g(2),int32BE:g(4),intLE:m,int8LE:m(1),int16LE:m(2),int32LE:m(4),floatBE:p("floatBE",4).map(function(n){return n.readFloatBE(0)}),floatLE:p("floatLE",4).map(function(n){return n.readFloatLE(0)}),doubleBE:p("doubleBE",8).map(function(n){return n.readDoubleBE(0)}),doubleLE:p("doubleLE",8).map(function(n){return n.readDoubleLE(0)})},n.exports=e;}])});
+}(parsimmon_umd_min));
+
+var Parsimmon = /*@__PURE__*/getDefaultExportFromCjs(parsimmon_umd_min.exports);
+
class VariableReferenceEntity extends Entity {
+
static attributes = {
- MemberName: "",
- MemberGuid: Guid,
+ MemberName: String,
+ MemberGuid: GuidEntity,
bSelfContext: true
}
@@ -1493,7 +358,7 @@ class ObjectEntity extends Entity {
TargetType: new TypeInitialization(new ObjectReferenceEntity(), false),
NodePosX: Integer,
NodePosY: Integer,
- NodeGuid: Guid,
+ NodeGuid: GuidEntity,
CustomProperties: [PinEntity$1]
}
@@ -1516,8 +381,9 @@ class Grammar {
Integer = _ => P.regex(/[0-9]+/).map(v => new Integer(v)).desc("an integer")
String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")')
Word = _ => P.regex(/[a-zA-Z]+/).desc("a word")
- Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).desc("32 digit hexadecimal (accepts all the letters for safety) value")
- ReferencePath = _ => P.seq(P.string("/"), P.regex(/[0-9a-zA-Z_]+/).sepBy1(P.string(".")).tieWith("."))
+ Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new GuidEntity({ value: v })).desc("32 digit hexadecimal (accepts all the letters for safety) value")
+ PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/)
+ ReferencePath = _ => P.seq(P.string("/"), r.PathSymbol.sepBy1(P.string(".")).tieWith("."))
.tie()
.atLeast(2)
.tie()
@@ -1543,11 +409,11 @@ class Grammar {
AttributeAnyValue = r => P.alt(r.Null, r.None, r.Boolean, r.Number, r.Integer, r.String, r.Guid, r.Reference, r.LocalizedText)
LocalizedText = r => P.seqMap(
P.string("NSLOCTEXT").skip(P.optWhitespace).skip(P.string("(")),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // namespace
P.string(","),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // key
P.string(","),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // value
P.string(")"),
(_, namespace, __, key, ___, value, ____) => new LocalizedTextEntity({
namespace: namespace,
@@ -1555,8 +421,17 @@ class Grammar {
value: value
})
)
- static getGrammarForType(r, type, defaultGrammar) {
- switch (type) {
+ PinReference = r => P.seqMap(
+ r.PathSymbol,
+ P.whitespace,
+ r.Guid,
+ (objectName, _, pinGuid) => new PinReferenceEntity({
+ objectName: objectName,
+ pinGuid: pinGuid
+ })
+ )
+ static getGrammarForType(r, attributeType, defaultGrammar) {
+ switch (Utility.getType(attributeType)) {
case Boolean:
return r.Boolean
case Number:
@@ -1565,16 +440,34 @@ class Grammar {
return r.Integer
case String:
return r.String
- case Guid:
+ case GuidEntity:
return r.Guid
case ObjectReferenceEntity:
return r.Reference
case LocalizedTextEntity:
return r.LocalizedText
+ case PinReferenceEntity:
+ return r.PinReference
case FunctionReferenceEntity:
return r.FunctionReference
case PinEntity$1:
return r.Pin
+ case Array:
+ return P.seqMap(
+ P.string("("),
+ attributeType
+ .map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
+ .reduce((accum, cur) =>
+ !cur || accum === r.AttributeAnyValue
+ ? r.AttributeAnyValue
+ : accum.or(cur)
+ )
+ .trim(P.optWhitespace)
+ .sepBy(P.string(","))
+ .skip(P.regex(/,?\s*/)),
+ P.string(")"),
+ (_, grammar, __) => grammar
+ )
default:
return defaultGrammar
}
@@ -1585,26 +478,10 @@ class Grammar {
.chain(attributeName => {
const attributeKey = attributeName.split(".");
const attribute = attributeSupplier(attributeKey);
- const type = Utility.getType(attribute);
- let attributeValueGrammar = type === Array
- ? attribute
- .map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
- .reduce((accum, cur) =>
- !cur || accum === r.AttributeAnyValue
- ? r.AttributeAnyValue
- : accum.or(cur)
- )
- : Grammar.getGrammarForType(r, type, r.AttributeAnyValue);
- // After the attribute name (already parsed at this point, we continue with an equal sign (possibly surrounded by whitespace) then the expected attribute value)
- return attributeValueGrammar.map(attributeValue => type === Array
- ? entity => {
- /** @type {Array} */
- let array = Utility.objectGet(entity, attributeKey, []);
- array.push(attributeValue);
- return Utility.objectSet(entity, attributeKey, array, true)
- }
- : entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
- ) // returns attributeSetter
+ let attributeValueGrammar = Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue);
+ return attributeValueGrammar.map(attributeValue =>
+ entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
+ ) // returns attributeSetter: a function called with an object as argument that will set the correct attribute value
})
// Meta grammar
static CreateMultiAttributeGrammar = (r, keyGrammar, entityType, attributeSupplier) =>
@@ -1650,6 +527,7 @@ class Grammar {
return result
}
)
+ MultipleObject = r => r.Object.sepBy1(P.whitespace).trim(P.optWhitespace)
}
class Serializer {
@@ -1666,7 +544,7 @@ class Serializer {
case Boolean:
return Utility.FirstCapital(value.toString())
case ObjectReferenceEntity:
- case Guid:
+ case GuidEntity:
return value.toString()
case String:
return `"${value}"`
@@ -1748,35 +626,4 @@ class PinSerializer extends Serializer {
}
}
-class ObjectSerializer extends Serializer {
-
- showProperty(attributeKey, attributeValue) {
- switch (attributeKey.toString()) {
- case "Class":
- case "Name":
- // Serielized separately
- return false
- }
- return super.showProperty(attributeKey, attributeValue)
- }
-
- read(value) {
- const parseResult = Serializer.grammar.Object.parse(value);
- if (!parseResult.status) {
- console.error("Error when trying to parse the object.");
- return parseResult
- }
- return parseResult.value
- }
-
- write(object) {
- let result = `
-Begin Object Class=${object.Class} Name=${object.Name}
-${this.subWrite([], object, "\n", " ")}
-End Object
-`;
- return result
- }
-}
-
-export { Grammar, GraphNode, ObjectReferenceEntity, ObjectSerializer, PinEntity$1 as PinEntity, PinSerializer, Blueprint as UEBlueprint };
+export { PinSerializer };
diff --git a/js/Guid.js b/js/Guid.js
deleted file mode 100644
index afe5f66..0000000
--- a/js/Guid.js
+++ /dev/null
@@ -1,30 +0,0 @@
-export default class Guid {
- static generateGuid(random) {
- let values = new Uint32Array(4);
- if (random === true) {
- crypto.getRandomValues(values)
- }
- let result = ""
- values.forEach(n => {
- result += ('00000000' + n.toString(16).toUpperCase()).slice(-8)
- })
- return result
- }
-
- constructor(guid) {
- switch (guid?.constructor) {
- case String:
- this.value = guid
- break
- case Guid:
- this.value = guid.value
- break
- default:
- this.value = Guid.generateGuid(guid === true)
- }
- }
-
- toString() {
- return this.value
- }
-}
\ No newline at end of file
diff --git a/js/entity/Entity.js b/js/entity/Entity.js
index c5e3d6f..346c8d4 100644
--- a/js/entity/Entity.js
+++ b/js/entity/Entity.js
@@ -33,6 +33,12 @@ export default class Entity {
continue
}
let defaultValue = properties[property]
+ if (defaultValue instanceof TypeInitialization) {
+ if (!defaultValue.showDefault) {
+ continue
+ }
+ defaultValue = defaultValue.value
+ }
if (defaultValue instanceof Array) {
propertySetter(target, property, [])
defineAllAttributes(
@@ -42,12 +48,6 @@ export default class Entity {
(t, _, v) => t.push(v))
continue
}
- if (defaultValue instanceof TypeInitialization) {
- if (!defaultValue.showDefault) {
- continue
- }
- defaultValue = defaultValue.value
- }
if (defaultValue instanceof Function) {
defaultValue = Utility.sanitize(new defaultValue())
}
diff --git a/js/entity/GuidEntity.js b/js/entity/GuidEntity.js
new file mode 100644
index 0000000..cb80a5d
--- /dev/null
+++ b/js/entity/GuidEntity.js
@@ -0,0 +1,37 @@
+import Entity from "./Entity";
+
+export default class GuidEntity extends Entity {
+
+ static attributes = {
+ value: String
+ }
+
+ static generateGuid(random) {
+ let values = new Uint32Array(4);
+ if (random === true) {
+ crypto.getRandomValues(values)
+ }
+ let result = ""
+ values.forEach(n => {
+ result += ('00000000' + n.toString(16).toUpperCase()).slice(-8)
+ })
+ return result
+ }
+
+ constructor(guid) {
+ if (guid?.constructor === String) {
+ guid = {
+ value: guid
+ }
+ } else if (guid?.constructor === Boolean) {
+ guid = {
+ value: GuidEntity.generateGuid(guid == true)
+ }
+ }
+ super(guid)
+ }
+
+ getAttributes() {
+ return GuidEntity.attributes
+ }
+}
\ No newline at end of file
diff --git a/js/entity/ObjectEntity.js b/js/entity/ObjectEntity.js
index 8348f1a..7f4308b 100644
--- a/js/entity/ObjectEntity.js
+++ b/js/entity/ObjectEntity.js
@@ -1,6 +1,6 @@
import Entity from "./Entity"
import FunctionReferenceEntity from "./FunctionReferenceEntity"
-import Guid from "../Guid"
+import GuidEntity from "./GuidEntity"
import Integer from "./Integer"
import ObjectReferenceEntity from "./ObjectReferenceEntity"
import PinEntity from "./PinEntity"
@@ -18,7 +18,7 @@ export default class ObjectEntity extends Entity {
TargetType: new TypeInitialization(new ObjectReferenceEntity(), false),
NodePosX: Integer,
NodePosY: Integer,
- NodeGuid: Guid,
+ NodeGuid: GuidEntity,
CustomProperties: [PinEntity]
}
diff --git a/js/entity/PinEntity.js b/js/entity/PinEntity.js
index 169ae2a..a786334 100644
--- a/js/entity/PinEntity.js
+++ b/js/entity/PinEntity.js
@@ -1,12 +1,14 @@
-import Entity from "./Entity";
-import Guid from "../Guid";
-import ObjectReferenceEntity from "./ObjectReferenceEntity";
-import TypeInitialization from "./TypeInitialization";
-import LocalizedTextEntity from "./LocalizedTextEntity";
+import Entity from "./Entity"
+import GuidEntity from "./GuidEntity"
+import LocalizedTextEntity from "./LocalizedTextEntity"
+import ObjectReferenceEntity from "./ObjectReferenceEntity"
+import TypeInitialization from "./TypeInitialization"
+import PinReferenceEntity from "./PinReferenceEntity"
export default class PinEntity extends Entity {
+
static attributes = {
- PinId: Guid,
+ PinId: GuidEntity,
PinName: "",
PinFriendlyName: new TypeInitialization(new LocalizedTextEntity(), false),
PinToolTip: "",
@@ -23,10 +25,10 @@ export default class PinEntity extends Entity {
bIsWeakPointer: false,
bIsUObjectWrapper: false
},
- LinkedTo: Guid,
+ LinkedTo: [new TypeInitialization(null, false, PinReferenceEntity)],
DefaultValue: "",
AutogeneratedDefaultValue: "",
- PersistentGuid: Guid,
+ PersistentGuid: GuidEntity,
bHidden: false,
bNotConnectable: false,
bDefaultValueIsReadOnly: false,
@@ -38,4 +40,10 @@ export default class PinEntity extends Entity {
getAttributes() {
return PinEntity.attributes
}
-}
\ No newline at end of file
+
+ isOutput() {
+ if (this.Direction === "EGPD_Output") {
+ return true
+ }
+ }
+}
diff --git a/js/entity/PinReferenceEntity.js b/js/entity/PinReferenceEntity.js
new file mode 100644
index 0000000..bb19642
--- /dev/null
+++ b/js/entity/PinReferenceEntity.js
@@ -0,0 +1,14 @@
+import Entity from "./Entity"
+import GuidEntity from "./GuidEntity"
+
+export default class PinReferenceEntity extends Entity {
+
+ static attributes = {
+ objectName: String,
+ pinGuid: GuidEntity
+ }
+
+ getAttributes() {
+ return PinReferenceEntity.attributes
+ }
+}
\ No newline at end of file
diff --git a/js/entity/TypeInitialization.js b/js/entity/TypeInitialization.js
index bb35b99..2e539ab 100644
--- a/js/entity/TypeInitialization.js
+++ b/js/entity/TypeInitialization.js
@@ -2,9 +2,6 @@ import Utility from "../Utility"
export default class TypeInitialization {
constructor(value, showDefault = true, type = Utility.getType(value)) {
- if (type.prototype.constructor.name != value.constructor.name) {
- throw new Error("Default value expected to be of the same type.")
- }
this.value = value
this.showDefault = showDefault
this.type = type
diff --git a/js/entity/VariableReferenceEntity.js b/js/entity/VariableReferenceEntity.js
index 2f6ee09..da15cbf 100644
--- a/js/entity/VariableReferenceEntity.js
+++ b/js/entity/VariableReferenceEntity.js
@@ -1,11 +1,11 @@
-import Guid from "../Guid"
+import GuidEntity from "./GuidEntity"
import Entity from "./Entity"
-import ObjectReferenceEntity from "./ObjectReferenceEntity"
export default class VariableReferenceEntity extends Entity {
+
static attributes = {
- MemberName: "",
- MemberGuid: Guid,
+ MemberName: String,
+ MemberGuid: GuidEntity,
bSelfContext: true
}
diff --git a/js/export.js b/js/export.js
index e23b3e3..d0c7f90 100644
--- a/js/export.js
+++ b/js/export.js
@@ -1,9 +1,3 @@
-import Blueprint from "./Blueprint"
-import GraphNode from "./graph/GraphNode"
import PinSerializer from "./serialization/PinSerializer"
-import PinEntity from "./entity/PinEntity"
-import Grammar from "./serialization/Grammar"
-import ObjectReferenceEntity from "./entity/ObjectReferenceEntity"
-import ObjectSerializer from "./serialization/ObjectSerialize"
-export { Blueprint as UEBlueprint, GraphNode as GraphNode, PinSerializer as PinSerializer, PinEntity as PinEntity, Grammar as Grammar, ObjectReferenceEntity as ObjectReferenceEntity, ObjectSerializer as ObjectSerializer }
\ No newline at end of file
+export { PinSerializer as PinSerializer }
\ No newline at end of file
diff --git a/js/graph/GraphEntity.js b/js/graph/GraphEntity.js
index c2ed4c2..707cb8a 100644
--- a/js/graph/GraphEntity.js
+++ b/js/graph/GraphEntity.js
@@ -1,6 +1,3 @@
-/**
- * A Graph Entity is an element that can stay directly (as a first child) on the blueprint grid. Those entities are either nodes or links
- */
export default class GraphEntity extends HTMLElement {
/**
*
diff --git a/js/serialization/Grammar.js b/js/serialization/Grammar.js
index cd0dfd0..8dd20e3 100644
--- a/js/serialization/Grammar.js
+++ b/js/serialization/Grammar.js
@@ -1,5 +1,5 @@
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
-import Guid from "../Guid"
+import GuidEntity from "../entity/GuidEntity"
import Integer from "../entity/Integer"
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
import Parsimmon from "parsimmon"
@@ -7,6 +7,7 @@ import PinEntity from "../entity/PinEntity"
import Utility from "../Utility"
import ObjectEntity from "../entity/ObjectEntity"
import LocalizedTextEntity from "../entity/LocalizedTextEntity"
+import PinReferenceEntity from "../entity/PinReferenceEntity"
let P = Parsimmon
@@ -22,8 +23,9 @@ export default class Grammar {
Integer = _ => P.regex(/[0-9]+/).map(v => new Integer(v)).desc("an integer")
String = _ => P.regex(/(?:[^"\\]|\\")*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")')
Word = _ => P.regex(/[a-zA-Z]+/).desc("a word")
- Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).desc("32 digit hexadecimal (accepts all the letters for safety) value")
- ReferencePath = _ => P.seq(P.string("/"), P.regex(/[0-9a-zA-Z_]+/).sepBy1(P.string(".")).tieWith("."))
+ Guid = _ => P.regex(/[0-9a-zA-Z]{32}/).map(v => new GuidEntity({ value: v })).desc("32 digit hexadecimal (accepts all the letters for safety) value")
+ PathSymbol = _ => P.regex(/[0-9a-zA-Z_]+/)
+ ReferencePath = _ => P.seq(P.string("/"), r.PathSymbol.sepBy1(P.string(".")).tieWith("."))
.tie()
.atLeast(2)
.tie()
@@ -49,11 +51,11 @@ export default class Grammar {
AttributeAnyValue = r => P.alt(r.Null, r.None, r.Boolean, r.Number, r.Integer, r.String, r.Guid, r.Reference, r.LocalizedText)
LocalizedText = r => P.seqMap(
P.string("NSLOCTEXT").skip(P.optWhitespace).skip(P.string("(")),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // namespace
P.string(","),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // key
P.string(","),
- r.String.trim(P.optWhitespace),
+ r.String.trim(P.optWhitespace), // value
P.string(")"),
(_, namespace, __, key, ___, value, ____) => new LocalizedTextEntity({
namespace: namespace,
@@ -61,8 +63,17 @@ export default class Grammar {
value: value
})
)
- static getGrammarForType(r, type, defaultGrammar) {
- switch (type) {
+ PinReference = r => P.seqMap(
+ r.PathSymbol,
+ P.whitespace,
+ r.Guid,
+ (objectName, _, pinGuid) => new PinReferenceEntity({
+ objectName: objectName,
+ pinGuid: pinGuid
+ })
+ )
+ static getGrammarForType(r, attributeType, defaultGrammar) {
+ switch (Utility.getType(attributeType)) {
case Boolean:
return r.Boolean
case Number:
@@ -71,16 +82,34 @@ export default class Grammar {
return r.Integer
case String:
return r.String
- case Guid:
+ case GuidEntity:
return r.Guid
case ObjectReferenceEntity:
return r.Reference
case LocalizedTextEntity:
return r.LocalizedText
+ case PinReferenceEntity:
+ return r.PinReference
case FunctionReferenceEntity:
return r.FunctionReference
case PinEntity:
return r.Pin
+ case Array:
+ return P.seqMap(
+ P.string("("),
+ attributeType
+ .map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
+ .reduce((accum, cur) =>
+ !cur || accum === r.AttributeAnyValue
+ ? r.AttributeAnyValue
+ : accum.or(cur)
+ )
+ .trim(P.optWhitespace)
+ .sepBy(P.string(","))
+ .skip(P.regex(/,?\s*/)),
+ P.string(")"),
+ (_, grammar, __) => grammar
+ )
default:
return defaultGrammar
}
@@ -91,26 +120,10 @@ export default class Grammar {
.chain(attributeName => {
const attributeKey = attributeName.split(".")
const attribute = attributeSupplier(attributeKey)
- const type = Utility.getType(attribute)
- let attributeValueGrammar = type === Array
- ? attribute
- .map(v => Grammar.getGrammarForType(r, Utility.getType(v)))
- .reduce((accum, cur) =>
- !cur || accum === r.AttributeAnyValue
- ? r.AttributeAnyValue
- : accum.or(cur)
- )
- : Grammar.getGrammarForType(r, type, r.AttributeAnyValue)
- // After the attribute name (already parsed at this point, we continue with an equal sign (possibly surrounded by whitespace) then the expected attribute value)
- return attributeValueGrammar.map(attributeValue => type === Array
- ? entity => {
- /** @type {Array} */
- let array = Utility.objectGet(entity, attributeKey, [])
- array.push(attributeValue)
- return Utility.objectSet(entity, attributeKey, array, true)
- }
- : entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
- ) // returns attributeSetter
+ let attributeValueGrammar = Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
+ return attributeValueGrammar.map(attributeValue =>
+ entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
+ ) // returns attributeSetter: a function called with an object as argument that will set the correct attribute value
})
// Meta grammar
static CreateMultiAttributeGrammar = (r, keyGrammar, entityType, attributeSupplier) =>
@@ -156,4 +169,5 @@ export default class Grammar {
return result
}
)
+ MultipleObject = r => r.Object.sepBy1(P.whitespace).trim(P.optWhitespace)
}
\ No newline at end of file
diff --git a/js/serialization/Serializer.js b/js/serialization/Serializer.js
index e0018c3..e59b088 100644
--- a/js/serialization/Serializer.js
+++ b/js/serialization/Serializer.js
@@ -1,9 +1,9 @@
-import Parsimmon from "parsimmon"
import Grammar from "./Grammar"
-import Utility from "../Utility"
-import TypeInitialization from "../entity/TypeInitialization"
+import GuidEntity from "../entity/GuidEntity"
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
-import Guid from "../Guid"
+import Parsimmon from "parsimmon"
+import TypeInitialization from "../entity/TypeInitialization"
+import Utility from "../Utility"
export default class Serializer {
@@ -20,7 +20,7 @@ export default class Serializer {
case Boolean:
return Utility.FirstCapital(value.toString())
case ObjectReferenceEntity:
- case Guid:
+ case GuidEntity:
return value.toString()
case String:
return `"${value}"`
diff --git a/js/template/NodeTemplate.js b/js/template/NodeTemplate.js
index fd325cb..96b4b2f 100644
--- a/js/template/NodeTemplate.js
+++ b/js/template/NodeTemplate.js
@@ -1,18 +1,19 @@
+import { PinEntity } from "../../dist/ueblueprint"
import Template from "./Template"
export default class NodeTemplate extends Template {
/**
* Computes the html content of the target element.
- * @param {HTMLElement} element Target element
+ * @param {HTMLElement} entity Entity representing the element
* @returns The computed html
*/
- header(element) {
+ header(entity) {
return `
`
@@ -20,27 +21,30 @@ export default class NodeTemplate extends Template {
/**
* Computes the html content of the target element.
- * @param {HTMLElement} element Target element
+ * @param {import("../entity/ObjectEntity").default} entity Entity representing the element
* @returns The computed html
*/
- body(element) {
+ body(entity) {
+ let inputs = entity.CustomProperties.filter(v => v instanceof PinEntity)
+ let outputs = inputs.filter(v => v.isOutput())
+ inputs = inputs.filter(v => !v.isOutput())
return `
- ${element.outputs.forEach((output, index) => `
+ ${outputs.map((output, index) => `
${output.name}
-
+
- `) ?? ''}
+ `).join("") ?? ''}
`
@@ -48,15 +52,15 @@ export default class NodeTemplate extends Template {
/**
* Computes the html content of the target element.
- * @param {HTMLElement} element Target element
+ * @param {HTMLElement} entity Entity representing the element
* @returns The computed html
*/
- render(element) {
+ render(entity) {
return `
- ${this.header(element)}
- ${this.body(element)}
+ ${this.header(entity)}
+ ${this.body(entity)}
`
diff --git a/js/template/Template.js b/js/template/Template.js
index efcaedd..27b30d0 100644
--- a/js/template/Template.js
+++ b/js/template/Template.js
@@ -5,19 +5,19 @@ export default class Template {
/**
* Computes the html content of the target element.
- * @param {GraphNode} element Target element
+ * @param {Entity} entity Entity representing the element
* @returns The computed html
*/
- render(element) {
+ render(entity) {
return ``
}
/**
* Returns the html elements rendered by this template.
- * @param {GraphNode} element Target element
+ * @param {GraphNode} entity Entity representing the element
* @returns The rendered elements
*/
- getElements(element) {
+ getElements(entity) {
let aDiv = document.createElement('div')
aDiv.innerHTML = this.render(element)
return aDiv.childNodes
diff --git a/ueblueprint.html b/ueblueprint.html
index 91b4142..e01184b 100644
--- a/ueblueprint.html
+++ b/ueblueprint.html
@@ -14,17 +14,13 @@
Hello