Refactoring, various fixes

This commit is contained in:
barsdeveloper
2022-03-24 22:54:41 +01:00
parent 4dd2929a9f
commit 8a4e60c9ae
43 changed files with 937 additions and 589 deletions

View File

@@ -3,6 +3,7 @@ import Configuration from "./Configuration"
import Copy from "./input/common/Copy"
import IElement from "./element/IElement"
import KeyboardCanc from "./input/keybaord/KeyboardCanc"
import KeyboardEnableZoom from "./input/keybaord/KeyboardEnableZoom"
import KeyboardSelectAll from "./input/keybaord/KeyboardSelectAll"
import LinkElement from "./element/LinkElement"
import MouseScrollGraph from "./input/mouse/MouseScrollGraph"
@@ -121,7 +122,8 @@ export default class Blueprint extends IElement {
moveEverywhere: true,
}),
new Unfocus(this.getGridDOMElement(), this),
new MouseTracking(this.getGridDOMElement(), this)
new MouseTracking(this.getGridDOMElement(), this),
new KeyboardEnableZoom(this.getGridDOMElement(), this),
]
}
@@ -257,7 +259,7 @@ export default class Blueprint extends IElement {
}
setZoom(zoom, center) {
zoom = Utility.clamp(zoom, -12, 0)
zoom = Utility.clamp(zoom, Configuration.minZoom, Configuration.maxZoom)
if (zoom == this.zoom) {
return
}

View File

@@ -1,5 +1,6 @@
export default class Configuration {
static deleteNodesKeyboardKey = "Delete"
static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom
static expandGridSize = 400
static fontSize = "13px"
static gridAxisLineColor = "black"
@@ -22,11 +23,13 @@ export default class Configuration {
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 = "Ctrl+A"
static selectAllKeyboardKey = "(bCtrl=True,Key=A)"
static trackingMouseEventName = {
begin: "ueb-tracking-mouse-begin",
end: "ueb-tracking-mouse-end"
@@ -41,6 +44,12 @@ export default class Configuration {
/* 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",
@@ -50,23 +59,23 @@ export default class Configuration {
"PageDown": "PageDown",
"End": "End",
"Home": "Home",
"ArrowLeft": "ArrowLeft",
"ArrowUp": "ArrowUp",
"ArrowRight": "ArrowRight",
"ArrowDown": "ArrowDown",
"ArrowLeft": "Left",
"ArrowUp": "Up",
"ArrowRight": "Right",
"ArrowDown": "Down",
"PrintScreen": "PrintScreen",
"Insert": "Insert",
"Delete": "Delete",
"Digit0": "Digit0",
"Digit1": "Digit1",
"Digit2": "Digit2",
"Digit3": "Digit3",
"Digit4": "Digit4",
"Digit5": "Digit5",
"Digit6": "Digit6",
"Digit7": "Digit7",
"Digit8": "Digit8",
"Digit9": "Digit9",
"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",
@@ -92,21 +101,21 @@ export default class Configuration {
"X": "KeyX",
"Y": "KeyY",
"Z": "KeyZ",
"Numpad0": "Numpad0",
"Numpad1": "Numpad1",
"Numpad2": "Numpad2",
"Numpad3": "Numpad3",
"Numpad4": "Numpad4",
"Numpad5": "Numpad5",
"Numpad6": "Numpad6",
"Numpad7": "Numpad7",
"Numpad8": "Numpad8",
"Numpad9": "Numpad9",
"NumpadMultiply": "NumpadMultiply",
"NumpadAdd": "NumpadAdd",
"NumpadSubtract": "NumpadSubtract",
"NumpadDecimal": "NumpadDecimal",
"NumpadDivide": "NumpadDivide",
"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",

View File

@@ -9,19 +9,27 @@ export default class IElement extends HTMLElement {
static tagName = ""
/** @type {Blueprint} */
blueprint
/** @type {IEntity} */
entity
/** @type {ITemplate} */
template
/** @type {IContext[]} */
inputObjects = []
/**
* @param {IEntity} entity The entity containing blueprint related data for this graph element
* @param {ITemplate} template The template to render this node
*/
constructor(entity, template) {
super()
/** @type {Blueprint} */
this.blueprint = null
/** @type {IEntity} */
this.entity = entity
/** @type {ITemplate} */
this.template = template
/** @type {IContext[]} */
this.inputObjects = []
}
@@ -39,14 +47,22 @@ export default class IElement extends HTMLElement {
this.inputObjects.forEach(v => v.unlistenDOMElement())
}
createInputObjects() {
return []
}
/**
* @param {IElement} element
*/
/** @param {IElement} element */
isSameGraph(element) {
return this.blueprint && this.blueprint == element?.blueprint
}
/**
* @template {} T
* @param {new () => T} type
* @returns {T}
*/
getInputObject(type) {
return this.inputObjects.find(object => object.constructor == type)
}
// Subclasses will want to override
createInputObjects() {
return []
}
}

View File

@@ -28,7 +28,7 @@ export default class IEntity {
* - A proper value.
*/
const value = Utility.objectGet(options, fullKey)
if (value !== null) {
if (value !== undefined) {
target[property] = value
continue
}

View File

@@ -1,7 +1,7 @@
import IEntity from "./IEntity"
export default class Identifier extends IEntity {
export default class IdentifierEntity extends IEntity {
static attributes = {
value: String,
@@ -15,11 +15,6 @@ export default class Identifier extends IEntity {
}
}
super(options)
/** @type {String} */
this.value
if (!this.value.match(/\w+/)) {
throw new Error("The value must be an identifier (/\w+/).")
}
}
valueOf() {

View File

@@ -1,12 +1,17 @@
import IdentifierEntity from "./IdentifierEntity"
import IEntity from "./IEntity"
export default class KeyBindingEntity extends IEntity {
static attributes = {
bCtrlDown: false,
bAltDown: false,
bShiftDown: false,
Key: String,
CommandName: String,
ActionName: "",
bShift: false,
bCtrl: false,
bAlt: false,
bCmd: false,
Key: IdentifierEntity,
}
constructor(options = {}) {
super(options)
}
}

View File

@@ -1,6 +1,6 @@
import FunctionReferenceEntity from "./FunctionReferenceEntity"
import GuidEntity from "./GuidEntity"
import Identifier from "./Identifier"
import IdentifierEntity from "./IdentifierEntity"
import IEntity from "./IEntity"
import IntegerEntity from "./IntegerEntity"
import ObjectReferenceEntity from "./ObjectReferenceEntity"
@@ -20,7 +20,7 @@ export default class ObjectEntity extends IEntity {
TargetType: new TypeInitialization(ObjectReferenceEntity, false, null),
NodePosX: IntegerEntity,
NodePosY: IntegerEntity,
AdvancedPinDisplay: new TypeInitialization(Identifier, false, null),
AdvancedPinDisplay: new TypeInitialization(IdentifierEntity, false, null),
NodeGuid: GuidEntity,
ErrorType: new TypeInitialization(IntegerEntity, false),
ErrorMsg: new TypeInitialization(String, false, ""),

View File

@@ -72,16 +72,14 @@ export default class PinEntity extends IEntity {
linkTo(targetObjectName, targetPinEntity) {
/** @type {PinReferenceEntity[]} */
this.LinkedTo
const linkFound = this.LinkedTo.find(
/** @type {PinReferenceEntity} */
pinReferenceEntity => {
return pinReferenceEntity.objectName == targetObjectName
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
})
const linkFound = this.LinkedTo?.find(pinReferenceEntity => {
return pinReferenceEntity.objectName == targetObjectName
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
})
if (!linkFound) {
this.LinkedTo.push(new PinReferenceEntity({
(this.LinkedTo ?? (this.LinkedTo = [])).push(new PinReferenceEntity({
objectName: targetObjectName,
pinGuid: targetPinEntity.PinId
pinGuid: targetPinEntity.PinId,
}))
return true
}
@@ -95,14 +93,16 @@ export default class PinEntity extends IEntity {
unlinkFrom(targetObjectName, targetPinEntity) {
/** @type {PinReferenceEntity[]} */
this.LinkedTo
const indexElement = this.LinkedTo.findIndex(
/** @type {PinReferenceEntity} */
pinReferenceEntity => {
return pinReferenceEntity.objectName == targetObjectName
&& pinReferenceEntity.pinGuid == targetPinEntity.PinId
})
const indexElement = this.LinkedTo.findIndex(pinReferenceEntity => {
return pinReferenceEntity.objectName == targetObjectName
&& pinReferenceEntity.pinGuid == targetPinEntity.PinId
})
if (indexElement >= 0) {
this.LinkedTo.splice(indexElement, 1)
if (this.LinkedTo.length == 1) {
this.LinkedTo = undefined
} else {
this.LinkedTo.splice(indexElement, 1)
}
return true
}
return false

View File

@@ -1,14 +1,36 @@
export default class IContext {
/** @type {HTMLElement} */
target
/** @type {import("../Blueprint").default}" */
blueprint
/** @type {Object} */
options
#hasFocus = false
get hasFocus() {
return this.#hasFocus
}
set hasFocus(_) {
}
constructor(target, blueprint, options) {
/** @type {HTMLElement} */
this.target = target
/** @type {import("../Blueprint").default}" */
this.blueprint = blueprint
this.options = options
let self = this
this.blueprintFocusHandler = _ => self.listenEvents()
this.blueprintUnfocusHandler = _ => self.unlistenEvents()
this.blueprintFocusHandler = _ => {
this.#hasFocus = true
self.listenEvents()
}
this.blueprintUnfocusHandler = _ => {
self.unlistenEvents()
this.#hasFocus = false
}
if (options?.wantsFocusCallback ?? false) {
this.blueprint.addEventListener("blueprint-focus", this.blueprintFocusHandler)
this.blueprint.addEventListener("blueprint-unfocus", this.blueprintUnfocusHandler)

View File

@@ -1,76 +1,71 @@
import Configuration from "../../Configuration"
import IContext from "../IContext"
import Parsimmon from "parsimmon"
let P = Parsimmon
class KeyGrammar {
// Creates a grammar where each alternative is the string from ModifierKey mapped to a number for bit or use
ModifierKey = r => P.alt(...Configuration.ModifierKeys.map((v, i) => P.string(v).map(_ => 1 << i)))
Key = r => P.alt(...Object.keys(Configuration.Keys).map(v => P.string(v))).map(v => Configuration.Keys[v])
KeyboardShortcut = r => P.alt(
P.seqMap(
P.seqMap(r.ModifierKey, P.optWhitespace, P.string(Configuration.keysSeparator), (v, _, __) => v)
.atLeast(1)
.map(v => v.reduce((acc, cur) => acc | cur)),
P.optWhitespace,
r.Key,
(modifierKeysFlag, _, key) => ({
key: key,
ctrlKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Ctrl"))),
shiftKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Shift"))),
altKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Alt"))),
metaKey: Boolean(modifierKeysFlag & (1 << Configuration.ModifierKeys.indexOf("Meta")))
})
),
r.Key.map(v => ({ key: v }))
)
.trim(P.optWhitespace)
}
import ISerializer from "../../serialization/ISerializer"
import KeyBindingEntity from "../../entity/KeyBindingEntity"
export default class IKeyboardShortcut extends IContext {
static keyGrammar = P.createLanguage(new KeyGrammar())
/** @type {KeyBindingEntity} */
#activationKeys
constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true
options.activationKeys ??= []
if (!(options.activationKeys instanceof Array)) {
options.activationKeys = [options.activationKeys]
}
options.activationKeys = options.activationKeys.map(v => {
if (v instanceof KeyBindingEntity) {
return v
}
if (v.constructor === String) {
const parsed = ISerializer.grammar.KeyBinding.parse(v)
if (parsed.status) {
return parsed.value
}
}
throw new Error("Unexpected key value")
})
super(target, blueprint, options)
/** @type {String[]} */
this.key = this.options.key
this.ctrlKey = options.ctrlKey ?? false
this.shiftKey = options.shiftKey ?? false
this.altKey = options.altKey ?? false
this.metaKey = options.metaKey ?? false
this.#activationKeys = this.options.activationKeys ?? []
let self = this
/** @param {KeyboardEvent} e */
this.keyDownHandler = e => {
if (
e.code == self.key
&& e.ctrlKey === self.ctrlKey
&& e.shiftKey === self.shiftKey
&& e.altKey === self.altKey
&& e.metaKey === self.metaKey
) {
self.fire()
self.#activationKeys.some(keyEntry =>
keyEntry.bShift === e.shiftKey
&& keyEntry.bCtrl === e.ctrlKey
&& keyEntry.bAlt === e.altKey
&& keyEntry.bCmd === e.metaKey
&& Configuration.Keys[keyEntry.Key] === e.code
)) {
e.preventDefault()
return true
self.fire()
document.removeEventListener("keydown", self.keyDownHandler)
document.addEventListener("keyup", self.keyUpHandler)
}
return false
}
}
/**
* @param {String} keyString
* @returns {Object}
*/
static keyOptionsParse(options, keyString) {
options = {
...options,
...IKeyboardShortcut.keyGrammar.KeyboardShortcut.parse(keyString).value
/** @param {KeyboardEvent} e */
this.keyUpHandler = e => {
if (
self.#activationKeys.some(keyEntry =>
keyEntry.bShift && e.key === "Shift"
|| keyEntry.bCtrl && e.key === "Control"
|| keyEntry.bAlt && e.key === "Alt"
|| keyEntry.bCmd && e.key === "Meta" // Unsure about this, what key is that?
|| Configuration.Keys[keyEntry.Key] === e.code
)) {
e.preventDefault()
self.unfire()
document.removeEventListener("keyup", this.keyUpHandler)
document.addEventListener("keydown", this.keyDownHandler)
}
}
return options
}
listenEvents() {
@@ -81,6 +76,11 @@ export default class IKeyboardShortcut extends IContext {
document.removeEventListener("keydown", this.keyDownHandler)
}
// Subclasses will want to override
fire() {
}
unfire() {
}
}

View File

@@ -1,7 +1,7 @@
import Configuration from "../../Configuration"
import IKeyboardShortcut from "./IKeyboardShortcut"
export default class KeyvoardCanc extends IKeyboardShortcut {
export default class KeyboardCanc extends IKeyboardShortcut {
/**
* @param {HTMLElement} target
@@ -9,7 +9,10 @@ export default class KeyvoardCanc extends IKeyboardShortcut {
* @param {OBject} options
*/
constructor(target, blueprint, options = {}) {
options = IKeyboardShortcut.keyOptionsParse(options, Configuration.deleteNodesKeyboardKey)
options = {
...options,
activationKeys: Configuration.deleteNodesKeyboardKey
}
super(target, blueprint, options)
}

View File

@@ -0,0 +1,31 @@
import Configuration from "../../Configuration"
import IKeyboardShortcut from "./IKeyboardShortcut"
import Zoom from "../mouse/Zoom"
export default class KeyboardEnableZoom extends IKeyboardShortcut {
/** @type {} */
#zoomInputObject
/**
* @param {HTMLElement} target
* @param {import("../../Blueprint").default} blueprint
* @param {OBject} options
*/
constructor(target, blueprint, options = {}) {
options = {
...options,
activationKeys: Configuration.enableZoomIn
}
super(target, blueprint, options)
}
fire() {
this.zoomInputObject = this.blueprint.getInputObject(Zoom)
zoomInputObject.enableZoonIn = true
}
unfire() {
this.#zoomInputObject.enableZoom = false
}
}

View File

@@ -9,7 +9,10 @@ export default class KeyboardSelectAll extends IKeyboardShortcut {
* @param {Object} options
*/
constructor(target, blueprint, options = {}) {
options = IKeyboardShortcut.keyOptionsParse(options, Configuration.selectAllKeyboardKey)
options = {
...options,
activationKeys: Configuration.selectAllKeyboardKey
}
super(target, blueprint, options)
}

View File

@@ -101,6 +101,7 @@ export default class IMouseClickDrag extends IPointing {
if (self.started) {
self.endDrag()
}
self.unclicked()
if (self.#trackingMouse) {
const dragEvent = self.getEvent(Configuration.trackingMouseEventName.end)
this.target.dispatchEvent(dragEvent)
@@ -146,4 +147,7 @@ export default class IMouseClickDrag extends IPointing {
endDrag() {
}
unclicked(location) {
}
}

View File

@@ -18,7 +18,11 @@ export default class Select extends IMouseClickDrag {
endDrag() {
if (this.started) {
this.selectorElement.finishSelecting()
} else {
}
}
unclicked() {
if (!this.started) {
this.blueprint.unselectAll()
}
}

View File

@@ -2,9 +2,27 @@ import IMouseWheel from "./IMouseWheel"
export default class Zoom extends IMouseWheel {
#enableZoonIn = false
get enableZoonIn() {
return this.#enableZoonIn
}
set enableZoonIn(value) {
value = Boolean(value)
if (value == this.#enableZoonIn) {
return
}
this.#enableZoonIn = value
}
wheel(variation, location) {
let zoomLevel = this.blueprint.getZoom()
zoomLevel -= variation
variation = -variation
if (!this.enableZoonIn && zoomLevel == 0 && variation > 0) {
return
}
zoomLevel += variation
this.blueprint.setZoom(zoomLevel, location)
}
}

View File

@@ -1,7 +1,8 @@
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
import GuidEntity from "../entity/GuidEntity"
import Identifier from "../entity/Identifier"
import IdentifierEntity from "../entity/IdentifierEntity"
import IntegerEntity from "../entity/IntegerEntity"
import KeyBindingEntity from "../entity/KeyBindingEntity"
import LocalizedTextEntity from "../entity/LocalizedTextEntity"
import ObjectEntity from "../entity/ObjectEntity"
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
@@ -15,102 +16,7 @@ let P = Parsimmon
export default class Grammar {
/** @param {Grammar} r */
InlineWhitespace = r => P.regex(/[^\S\n]+/).desc("inline whitespace")
/** @param {Grammar} r */
InlineOptWhitespace = r => P.regex(/[^\S\n]*/).desc("inline optional whitespace")
/** @param {Grammar} r */
WhitespaceNewline = r => P.regex(/[^\S\n]*\n\s*/).desc("whitespace with at least a newline")
/** @param {Grammar} r */
Null = r => P.seq(P.string("("), r.InlineOptWhitespace, P.string(")")).map(_ => null).desc("null: ()")
/** @param {Grammar} r */
None = r => P.string("None").map(_ => new ObjectReferenceEntity({ type: "None", path: "" })).desc("none")
/** @param {Grammar} r */
Boolean = r => P.alt(P.string("True"), P.string("False")).map(v => v === "True" ? true : false).desc("either True or False")
/** @param {Grammar} r */
Number = r => P.regex(/[\-\+]?[0-9]+(?:\.[0-9]+)?/).map(Number).desc("a number")
/** @param {Grammar} r */
Integer = r => P.regex(/[\-\+]?[0-9]+/).map(v => new IntegerEntity(v)).desc("an integer")
/** @param {Grammar} r */
String = r => P.regex(/(?:[^"\\]|\\.)*/).wrap(P.string('"'), P.string('"')).desc('string (with possibility to escape the quote using \")')
/** @param {Grammar} r */
Word = r => P.regex(/[a-zA-Z]+/).desc("a word")
/** @param {Grammar} r */
Guid = r => P.regex(/[0-9a-zA-Z]{32}/).map(v => new GuidEntity({ value: v })).desc("32 digit hexadecimal (accepts all the letters for safety) value")
/** @param {Grammar} */
Identifier = r => P.regex(/\w+/).map(v => new Identifier(v))
/** @param {Grammar} r */
PathSymbolEntity = r => P.regex(/[0-9a-zA-Z_]+/).map(v => new PathSymbolEntity({ value: v }))
/** @param {Grammar} r */
ReferencePath = r => P.seq(P.string("/"), r.PathSymbolEntity.map(v => v.toString()).sepBy1(P.string(".")).tieWith("."))
.tie()
.atLeast(2)
.tie()
.desc('a path (words with possibly underscore, separated by ".", separated by "/")')
/** @param {Grammar} r */
Reference = r => P.alt(
r.None,
...[r.ReferencePath.map(path => new ObjectReferenceEntity({ type: "", path: path }))].flatMap(
v => [v, v.trim(P.string('"'))]
),
P.seqMap(
r.Word,
P.optWhitespace,
P.alt(P.string('"'), P.string('\'"')).chain(
result => r.ReferencePath.skip(
P.string(result.split("").reverse().join(""))
)
),
(referenceType, _, referencePath) => new ObjectReferenceEntity({ type: referenceType, path: referencePath })
)
)
/** @param {Grammar} r */
AttributeName = r => r.Word.sepBy1(P.string(".")).tieWith(".").desc('words separated by ""')
/** @param {Grammar} r */
AttributeAnyValue = r => P.alt(r.Null, r.None, r.Boolean, r.Number, r.Integer, r.String, r.Guid, r.Reference, r.LocalizedText)
/** @param {Grammar} r */
LocalizedText = r => P.seqMap(
P.string(LocalizedTextEntity.lookbehind).skip(P.optWhitespace).skip(P.string("(")),
r.String.trim(P.optWhitespace), // namespace
P.string(","),
r.String.trim(P.optWhitespace), // key
P.string(","),
r.String.trim(P.optWhitespace), // value
P.string(")"),
(_, namespace, __, key, ___, value, ____) => new LocalizedTextEntity({
namespace: namespace,
key: key,
value: value
})
)
/** @param {Grammar} r */
PinReference = r => P.seqMap(
r.PathSymbolEntity,
P.whitespace,
r.Guid,
(objectName, _, pinGuid) => new PinReferenceEntity({
objectName: objectName,
pinGuid: pinGuid
})
)
/* --- Factory --- */
/** @param {Grammar} r */
static getGrammarForType(r, attributeType, defaultGrammar) {
@@ -125,7 +31,7 @@ export default class Grammar {
return r.String
case GuidEntity:
return r.Guid
case Identifier:
case IdentifierEntity:
return r.Identifier
case ObjectReferenceEntity:
return r.Reference
@@ -159,7 +65,7 @@ export default class Grammar {
}
/** @param {Grammar} r */
static CreateAttributeGrammar = (r, entityType, valueSeparator = P.string("=").trim(P.optWhitespace)) =>
static createAttributeGrammar = (r, entityType, valueSeparator = P.string("=").trim(P.optWhitespace)) =>
r.AttributeName.skip(valueSeparator)
.chain(attributeName => {
const attributeKey = attributeName.split(".")
@@ -172,7 +78,7 @@ export default class Grammar {
})
/** @param {Grammar} r */
static CreateMultiAttributeGrammar = (r, entityType) =>
static createMultiAttributeGrammar = (r, entityType) =>
/**
* Basically this creates a parser that looks for a string like 'Key (A=False,B="Something",)'
* Then it populates an object of type EntityType with the attribute values found inside the parentheses.
@@ -181,7 +87,7 @@ export default class Grammar {
entityType.lookbehind
? P.seq(P.string(entityType.lookbehind), P.optWhitespace, P.string("("))
: P.string("("),
Grammar.CreateAttributeGrammar(r, entityType)
Grammar.createAttributeGrammar(r, entityType)
.trim(P.optWhitespace)
.sepBy(P.string(","))
.skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma
@@ -192,11 +98,140 @@ export default class Grammar {
return result
})
/** @param {Grammar} r */
FunctionReference = r => Grammar.CreateMultiAttributeGrammar(r, FunctionReferenceEntity)
/* --- General --- */
/** @param {Grammar} r */
Pin = r => Grammar.CreateMultiAttributeGrammar(r, PinEntity)
InlineWhitespace = r => P.regex(/[^\S\n]+/).desc("inline whitespace")
/** @param {Grammar} r */
InlineOptWhitespace = r => P.regex(/[^\S\n]*/).desc("inline optional whitespace")
/** @param {Grammar} r */
MultilineWhitespace = r => P.regex(/[^\S\n]*\n\s*/).desc("whitespace with at least a newline")
/** @param {Grammar} r */
Null = r => P.seq(P.string("("), r.InlineOptWhitespace, P.string(")")).map(_ => null).desc("null: ()")
/** @param {Grammar} r */
Boolean = r => P.alt(P.string("True"), P.string("False")).map(v => v === "True" ? true : false)
.desc("either True or False")
/** @param {Grammar} r */
Number = r => P.regex(/[\-\+]?[0-9]+(?:\.[0-9]+)?/).map(Number).desc("a number")
/** @param {Grammar} r */
Word = r => P.regex(/[a-zA-Z]+/).desc("a word")
/** @param {Grammar} r */
String = r => P.regex(/(?:[^"\\]|\\.)*/).wrap(P.string('"'), P.string('"'))
.desc('string (with possibility to escape the quote using \")')
/** @param {Grammar} r */
ReferencePath = r => P.seq(
P.string("/"),
r.PathSymbol
.map(v => v.toString())
.sepBy1(P.string("."))
.tieWith(".")
)
.tie()
.atLeast(2)
.tie()
.desc('a path (words with possibly underscore, separated by ".", separated by "/")')
/** @param {Grammar} r */
AttributeName = r => r.Word.sepBy1(P.string(".")).tieWith(".").desc('words separated by ""')
/* --- Entity --- */
/** @param {Grammar} r */
None = r => P.string("None").map(_ => new ObjectReferenceEntity({ type: "None", path: "" })).desc("none")
/** @param {Grammar} r */
Integer = r => P.regex(/[\-\+]?[0-9]+/).map(v => new IntegerEntity(v)).desc("an integer")
/** @param {Grammar} r */
Guid = r => P.regex(/[0-9a-zA-Z]{32}/).map(v => new GuidEntity({ value: v }))
.desc("32 digit hexadecimal (accepts all the letters for safety) value")
/** @param {Grammar} */
Identifier = r => P.regex(/\w+/).map(v => new IdentifierEntity(v))
/** @param {Grammar} r */
PathSymbol = r => P.regex(/[0-9a-zA-Z_]+/).map(v => new PathSymbolEntity({ value: v }))
/** @param {Grammar} r */
Reference = r => P.alt(
r.None,
...[r.ReferencePath.map(path => new ObjectReferenceEntity({ type: "", path: path }))]
.flatMap(referencePath => [
referencePath, // version having just path
referencePath.trim(P.string('"')) // Version having path surround with double quotes
]),
P.seqMap(
r.Word, // Goes into referenceType
P.optWhitespace, // Goes into _ (ignored)
P.alt(...[r.ReferencePath].flatMap(referencePath => [
referencePath.wrap(P.string(`"`), P.string(`"`)),
referencePath.wrap(P.string(`'"`), P.string(`"'`))
])), // Goes into referencePath
(referenceType, _, referencePath) => new ObjectReferenceEntity({ type: referenceType, path: referencePath })
),
r.Word.map(type => new ObjectReferenceEntity({ type: type, path: "" })),
)
/** @param {Grammar} r */
LocalizedText = r => P.seqMap(
P.string(LocalizedTextEntity.lookbehind).skip(P.optWhitespace).skip(P.string("(")), // Goes into _ (ignored)
r.String.trim(P.optWhitespace), // Goes into namespace
P.string(","), // Goes into __ (ignored)
r.String.trim(P.optWhitespace), // Goes into key
P.string(","), // Goes into ___ (ignored)
r.String.trim(P.optWhitespace), // Goes into value
P.string(")"), // Goes into ____ (ignored)
(_, namespace, __, key, ___, value, ____) => new LocalizedTextEntity({
namespace: namespace,
key: key,
value: value
})
)
/** @param {Grammar} r */
AttributeAnyValue = r => P.alt(
r.Null,
r.None,
r.Boolean,
r.Number,
r.Integer,
r.String,
r.Guid,
r.Reference,
r.LocalizedText)
/** @param {Grammar} r */
PinReference = r => P.seqMap(
r.PathSymbol, // Goes into objectNAme
P.whitespace, // Goes into _ (ignored)
r.Guid, // Goes into pinGuid
(objectName, _, pinGuid) => new PinReferenceEntity({
objectName: objectName,
pinGuid: pinGuid
})
)
/** @param {Grammar} r */
FunctionReference = r => Grammar.createMultiAttributeGrammar(r, FunctionReferenceEntity)
/** @param {Grammar} r */
KeyBinding = r => P.alt(
r.Identifier.map(identifier => new KeyBindingEntity({
Key: identifier
})),
Grammar.createMultiAttributeGrammar(r, KeyBindingEntity)
)
/** @param {Grammar} r */
Pin = r => Grammar.createMultiAttributeGrammar(r, PinEntity)
/** @param {Grammar} r */
CustomProperties = r =>
@@ -216,10 +251,10 @@ export default class Grammar {
P
.alt(
r.CustomProperties,
Grammar.CreateAttributeGrammar(r, ObjectEntity)
Grammar.createAttributeGrammar(r, ObjectEntity)
)
.sepBy1(P.whitespace),
P.seq(r.WhitespaceNewline, P.string("End"), P.whitespace, P.string("Object")),
P.seq(r.MultilineWhitespace, P.string("End"), P.whitespace, P.string("Object")),
(_, attributes, __) => {
let result = new ObjectEntity()
attributes.forEach(attributeSetter => attributeSetter(result))

View File

@@ -2,7 +2,9 @@ import CustomSerializer from "./CustomSerializer"
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
import GeneralSerializer from "./GeneralSerializer"
import GuidEntity from "../entity/GuidEntity"
import IdentifierEntity from "../entity/IdentifierEntity"
import IntegerEntity from "../entity/IntegerEntity"
import KeyBindingEntity from "../entity/KeyBindingEntity"
import LocalizedTextEntity from "../entity/LocalizedTextEntity"
import ObjectEntity from "../entity/ObjectEntity"
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
@@ -12,29 +14,39 @@ import PinEntity from "../entity/PinEntity"
import PinReferenceEntity from "../entity/PinReferenceEntity"
import SerializerFactory from "./SerializerFactory"
import ToStringSerializer from "./ToStringSerializer"
import Identifier from "../entity/Identifier"
export default function initializeSerializerFactory() {
SerializerFactory.registerSerializer(
ObjectEntity,
new ObjectSerializer()
)
SerializerFactory.registerSerializer(
PinEntity,
new GeneralSerializer(v => `${PinEntity.lookbehind} (${v})`, PinEntity, "", ",", true)
)
SerializerFactory.registerSerializer(
FunctionReferenceEntity,
new GeneralSerializer(v => `(${v})`, FunctionReferenceEntity, "", ",", false)
)
SerializerFactory.registerSerializer(
KeyBindingEntity,
new GeneralSerializer(v => `(${v})`, KeyBindingEntity, "", ",", false)
)
SerializerFactory.registerSerializer(
LocalizedTextEntity,
new GeneralSerializer(v => `${LocalizedTextEntity.lookbehind}(${v})`, LocalizedTextEntity, "", ", ", false, "", _ => "")
)
SerializerFactory.registerSerializer(
PinReferenceEntity,
new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "")
)
SerializerFactory.registerSerializer(
ObjectReferenceEntity,
new CustomSerializer(
@@ -45,8 +57,12 @@ export default function initializeSerializerFactory() {
: ""
))
)
SerializerFactory.registerSerializer(Identifier, new ToStringSerializer(Identifier))
SerializerFactory.registerSerializer(IdentifierEntity, new ToStringSerializer(IdentifierEntity))
SerializerFactory.registerSerializer(PathSymbolEntity, new ToStringSerializer(PathSymbolEntity))
SerializerFactory.registerSerializer(GuidEntity, new ToStringSerializer(GuidEntity))
SerializerFactory.registerSerializer(IntegerEntity, new ToStringSerializer(IntegerEntity))
}

View File

@@ -28,6 +28,9 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
<div class="ueb-node-outputs"></div>
</div>
</div>
<div class="ueb-node-expand">
<span class="ueb-node-expand-icon"></span>
</div>
</div>
`
}

View File

@@ -12,7 +12,6 @@ export default class SelectorTemplate extends ITemplate {
*/
apply(selector) {
super.apply(selector)
selector.classList.add("ueb-positioned")
this.applyFinishSelecting(selector)
}