From f5fd01d35bc1cd5f66b573d766b9f4009aee674a Mon Sep 17 00:00:00 2001 From: barsdeveloper Date: Thu, 2 Mar 2023 19:44:20 +0100 Subject: [PATCH] Keep possibly brand names just initials --- CONTRIBUTING.md | 2 +- README.md | 2 +- dist/ueblueprint.js | 9374 ++++++++++++++++----------------- empty.html | 2 +- glassy-nodes.html | 2 +- index.html | 2 +- js/entity/FormatTextEntity.js | 21 + js/entity/ObjectEntity.js | 2 +- js/serialization/Grammar.js | 12 +- package.json | 2 +- 10 files changed, 4722 insertions(+), 4699 deletions(-) create mode 100644 js/entity/FormatTextEntity.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb74a30..8d5289d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ Getting started with the development of this application is very easy because it Before starting, the gentle reader might want to make sure to be familiar with the [Lit](https://lit.dev/) library and its element [lifecycle](https://lit.dev/docs/components/lifecycle/). This library is used extensively throught the application to keep the HTML elements in sync with the data and avoid updating the DOM too often. The original author is aware that there are way more popular alternatives out there like React, Vue and Svelte, but the design of Lit fits very well into the original design of this application: vanilla JavaScript and object-oriented. This allowed the introduction of Lit with a relatively small amount of changes to the existing code, yes because the decision to use Lit was made at a later point in time. One important detail is that it does not make use of the shadow DOM (part of the Web Components), the real reason is that the development started without using Lit but it is still nice to be able to have a global CSS style (which wouldn't be possibile with a shadow root) so that restyling the library is just a matter of adding another CSS file and rewrite a few properties. -The only other external library that is used here is the awesome [Parsimmon](https://github.com/jneen/parsimmon): a very small but capable text parsing library used to deserialize the text produced by the Unreal Engine Blueprint Editor. +The only other external library that is used here is the awesome [Parsimmon](https://github.com/jneen/parsimmon): a very small but capable text parsing library used to deserialize the text produced by the UE Blueprint Editor. ## Setup diff --git a/README.md b/README.md index 0acf692..174cb06 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # UEBlueprint -A stand alone editor implementation of the Unreal Engine's Blueprint visual language. (WIP) +A stand alone editor implementation of the UE's Blueprint visual language. (WIP) ## Features: diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index 24c0ff6..c003667 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -1,29 +1,29 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const t$2=window,e$3=t$2.ShadowRoot&&(void 0===t$2.ShadyCSS||t$2.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$3=Symbol(),n$3=new WeakMap;class o$3{constructor(t,e,n){if(this._$cssResult$=!0,n!==s$3)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$3&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=n$3.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&n$3.set(s,t));}return t}toString(){return this.cssText}}const r$2=t=>new o$3("string"==typeof t?t:t+"",void 0,s$3),i$3=(t,...e)=>{const n=1===t.length?t[0]:e.reduce(((e,s,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[n+1]),t[0]);return new o$3(n,t,s$3)},S$1=(s,n)=>{e$3?s.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((e=>{const n=document.createElement("style"),o=t$2.litNonce;void 0!==o&&n.setAttribute("nonce",o),n.textContent=e.cssText,s.appendChild(n);}));},c$1=e$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$2(e)})(t):t; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var s$2;const e$2=window,r$1=e$2.trustedTypes,h$1=r$1?r$1.emptyScript:"",o$2=e$2.reactiveElementPolyfillSupport,n$2={toAttribute(t,i){switch(i){case Boolean:t=t?h$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},a$1=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:n$2,reflect:!1,hasChanged:a$1};class d$1 extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u();}static addInitializer(t){var i;this.finalize(),(null!==(i=this.h)&&void 0!==i?i:this.h=[]).push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Ep(s,i);void 0!==e&&(this._$Ev.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(c$1(i));}else void 0!==i&&s.push(c$1(i));return s}static _$Ep(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$ES)&&void 0!==i?i:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$ES)||void 0===i||i.splice(this._$ES.indexOf(t)>>>0,1);}_$Eg(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Ei.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return S$1(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$EO(t,i,s=l$2){var e;const r=this.constructor._$Ep(t,s);if(void 0!==r&&!0===s.reflect){const h=(void 0!==(null===(e=s.converter)||void 0===e?void 0:e.toAttribute)?s.converter:n$2).toAttribute(i,s.type);this._$El=t,null==h?this.removeAttribute(r):this.setAttribute(r,h),this._$El=null;}}_$AK(t,i){var s;const e=this.constructor,r=e._$Ev.get(t);if(void 0!==r&&this._$El!==r){const t=e.getPropertyOptions(r),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:n$2;this._$El=r,this[r]=h.fromAttribute(i,t.type),this._$El=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||a$1)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$E_=this._$Ej());}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,i)=>this[i]=t)),this._$Ei=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$Ek();}catch(t){throw i=!1,this._$Ek(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$ES)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$EO(i,this[i],t))),this._$EC=void 0),this._$Ek();}updated(t){}firstUpdated(t){}}d$1.finalized=!0,d$1.elementProperties=new Map,d$1.elementStyles=[],d$1.shadowRootOptions={mode:"open"},null==o$2||o$2({ReactiveElement:d$1}),(null!==(s$2=e$2.reactiveElementVersions)&&void 0!==s$2?s$2:e$2.reactiveElementVersions=[]).push("1.4.2"); - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var t$1;const i$2=window,s$1=i$2.trustedTypes,e$1=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,o$1=`lit$${(Math.random()+"").slice(9)}$`,n$1="?"+o$1,l$1=`<${n$1}>`,h=document,r=(t="")=>h.createComment(t),d=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,c=t=>u(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]),v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,a=/-->/g,f=/>/g,_=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),m=/'/g,p=/"/g,$=/^(?:script|style|textarea|title)$/i,g=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),y=g(1),x=Symbol.for("lit-noChange"),b=Symbol.for("lit-nothing"),T=new WeakMap,A=h.createTreeWalker(h,129,null,!1),E=(t,i)=>{const s=t.length-1,n=[];let h,r=2===i?"":"",d=v;for(let i=0;i"===u[0]?(d=null!=h?h:v,c=-1):void 0===u[1]?c=-2:(c=d.lastIndex-u[2].length,e=u[1],d=void 0===u[3]?_:'"'===u[3]?p:m):d===p||d===m?d=_:d===a||d===f?d=v:(d=_,h=void 0);const y=d===_&&t[i+1].startsWith("/>")?" ":"";r+=d===v?s+l$1:c>=0?(n.push(e),s.slice(0,c)+"$lit$"+s.slice(c)+o$1+y):s+o$1+(-2===c?(n.push(void 0),i):y);}const u=r+(t[s]||"")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==e$1?e$1.createHTML(u):u,n]};class C{constructor({strings:t,_$litType$:i},e){let l;this.parts=[];let h=0,d=0;const u=t.length-1,c=this.parts,[v,a]=E(t,i);if(this.el=C.createElement(v,e),A.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=s$1?s$1.emptyScript:"";for(let s=0;s2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=b;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P$1(this,t,i,0),n=!d(t)||t!==this._$AH&&t!==x,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(r(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l}; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var l,o;class s extends d$1{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const i=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Z(i,this.renderRoot,this.renderOptions);}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0);}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1);}render(){return x}}s.finalized=!0,s._$litElement$=!0,null===(l=globalThis.litElementHydrateSupport)||void 0===l||l.call(globalThis,{LitElement:s});const n=globalThis.litElementPolyfillSupport;null==n||n({LitElement:s});(null!==(o=globalThis.litElementVersions)&&void 0!==o?o:globalThis.litElementVersions=[]).push("3.2.2"); - +/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const t$2=window,e$3=t$2.ShadowRoot&&(void 0===t$2.ShadyCSS||t$2.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$3=Symbol(),n$3=new WeakMap;class o$3{constructor(t,e,n){if(this._$cssResult$=!0,n!==s$3)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$3&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=n$3.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&n$3.set(s,t));}return t}toString(){return this.cssText}}const r$2=t=>new o$3("string"==typeof t?t:t+"",void 0,s$3),i$3=(t,...e)=>{const n=1===t.length?t[0]:e.reduce(((e,s,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[n+1]),t[0]);return new o$3(n,t,s$3)},S$1=(s,n)=>{e$3?s.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((e=>{const n=document.createElement("style"),o=t$2.litNonce;void 0!==o&&n.setAttribute("nonce",o),n.textContent=e.cssText,s.appendChild(n);}));},c$1=e$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$2(e)})(t):t; + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */var s$2;const e$2=window,r$1=e$2.trustedTypes,h$1=r$1?r$1.emptyScript:"",o$2=e$2.reactiveElementPolyfillSupport,n$2={toAttribute(t,i){switch(i){case Boolean:t=t?h$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},a$1=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:n$2,reflect:!1,hasChanged:a$1};class d$1 extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u();}static addInitializer(t){var i;this.finalize(),(null!==(i=this.h)&&void 0!==i?i:this.h=[]).push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Ep(s,i);void 0!==e&&(this._$Ev.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(c$1(i));}else void 0!==i&&s.push(c$1(i));return s}static _$Ep(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$ES)&&void 0!==i?i:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$ES)||void 0===i||i.splice(this._$ES.indexOf(t)>>>0,1);}_$Eg(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Ei.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return S$1(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$EO(t,i,s=l$2){var e;const r=this.constructor._$Ep(t,s);if(void 0!==r&&!0===s.reflect){const h=(void 0!==(null===(e=s.converter)||void 0===e?void 0:e.toAttribute)?s.converter:n$2).toAttribute(i,s.type);this._$El=t,null==h?this.removeAttribute(r):this.setAttribute(r,h),this._$El=null;}}_$AK(t,i){var s;const e=this.constructor,r=e._$Ev.get(t);if(void 0!==r&&this._$El!==r){const t=e.getPropertyOptions(r),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:n$2;this._$El=r,this[r]=h.fromAttribute(i,t.type),this._$El=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||a$1)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$E_=this._$Ej());}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,i)=>this[i]=t)),this._$Ei=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$Ek();}catch(t){throw i=!1,this._$Ek(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$ES)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$EO(i,this[i],t))),this._$EC=void 0),this._$Ek();}updated(t){}firstUpdated(t){}}d$1.finalized=!0,d$1.elementProperties=new Map,d$1.elementStyles=[],d$1.shadowRootOptions={mode:"open"},null==o$2||o$2({ReactiveElement:d$1}),(null!==(s$2=e$2.reactiveElementVersions)&&void 0!==s$2?s$2:e$2.reactiveElementVersions=[]).push("1.4.2"); + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +var t$1;const i$2=window,s$1=i$2.trustedTypes,e$1=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,o$1=`lit$${(Math.random()+"").slice(9)}$`,n$1="?"+o$1,l$1=`<${n$1}>`,h=document,r=(t="")=>h.createComment(t),d=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,c=t=>u(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]),v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,a=/-->/g,f=/>/g,_=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),m=/'/g,p=/"/g,$=/^(?:script|style|textarea|title)$/i,g=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),y=g(1),x=Symbol.for("lit-noChange"),b=Symbol.for("lit-nothing"),T=new WeakMap,A=h.createTreeWalker(h,129,null,!1),E=(t,i)=>{const s=t.length-1,n=[];let h,r=2===i?"":"",d=v;for(let i=0;i"===u[0]?(d=null!=h?h:v,c=-1):void 0===u[1]?c=-2:(c=d.lastIndex-u[2].length,e=u[1],d=void 0===u[3]?_:'"'===u[3]?p:m):d===p||d===m?d=_:d===a||d===f?d=v:(d=_,h=void 0);const y=d===_&&t[i+1].startsWith("/>")?" ":"";r+=d===v?s+l$1:c>=0?(n.push(e),s.slice(0,c)+"$lit$"+s.slice(c)+o$1+y):s+o$1+(-2===c?(n.push(void 0),i):y);}const u=r+(t[s]||"")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==e$1?e$1.createHTML(u):u,n]};class C{constructor({strings:t,_$litType$:i},e){let l;this.parts=[];let h=0,d=0;const u=t.length-1,c=this.parts,[v,a]=E(t,i);if(this.el=C.createElement(v,e),A.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=s$1?s$1.emptyScript:"";for(let s=0;s2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=b;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P$1(this,t,i,0),n=!d(t)||t!==this._$AH&&t!==x,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(r(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l}; + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */var l,o;class s extends d$1{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const i=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Z(i,this.renderRoot,this.renderOptions);}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0);}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1);}render(){return x}}s.finalized=!0,s._$litElement$=!0,null===(l=globalThis.litElementHydrateSupport)||void 0===l||l.call(globalThis,{LitElement:s});const n=globalThis.litElementPolyfillSupport;null==n||n({LitElement:s});(null!==(o=globalThis.litElementVersions)&&void 0!==o?o:globalThis.litElementVersions=[]).push("3.2.2"); + /** * @typedef {import("./element/NodeElement").default} NodeElement * @typedef {import("./element/PinElement").default} PinElement @@ -285,76 +285,76 @@ class Configuration { "NumLock": "NumLock", "ScrollLock": "ScrollLock", } -} - -/** @typedef {import("../Blueprint").default} Blueprint */ - -/** @template {HTMLElement} T */ -class IInput { - - /** @type {T} */ - #target - get target() { - return this.#target - } - - /** @type {Blueprint} */ - #blueprint - get blueprint() { - return this.#blueprint - } - - /** @type {Object} */ - options - - - listenHandler = () => this.listenEvents() - unlistenHandler = () => this.unlistenEvents() - - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options.consumeEvent ??= false; - options.listenOnFocus ??= false; - options.unlistenOnTextEdit ??= false; - this.#target = target; - this.#blueprint = blueprint; - this.options = options; - } - - setup() { - if (this.options.listenOnFocus) { - this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler); - this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler); - } - if (this.options.unlistenOnTextEdit) { - this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); - this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler); - } - if (this.blueprint.focused) { - this.listenEvents(); - } - } - - cleanup() { - this.unlistenEvents(); - this.blueprint.removeEventListener(Configuration.focusEventName.begin, this.listenHandler); - this.blueprint.removeEventListener(Configuration.focusEventName.end, this.unlistenHandler); - this.blueprint.removeEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); - this.blueprint.removeEventListener(Configuration.editTextEventName.end, this.listenHandler); - } - - /* Subclasses will probabily override the following methods */ - listenEvents() { - } - - unlistenEvents() { - } -} - +} + +/** @typedef {import("../Blueprint").default} Blueprint */ + +/** @template {HTMLElement} T */ +class IInput { + + /** @type {T} */ + #target + get target() { + return this.#target + } + + /** @type {Blueprint} */ + #blueprint + get blueprint() { + return this.#blueprint + } + + /** @type {Object} */ + options + + + listenHandler = () => this.listenEvents() + unlistenHandler = () => this.unlistenEvents() + + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options.consumeEvent ??= false; + options.listenOnFocus ??= false; + options.unlistenOnTextEdit ??= false; + this.#target = target; + this.#blueprint = blueprint; + this.options = options; + } + + setup() { + if (this.options.listenOnFocus) { + this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler); + this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler); + } + if (this.options.unlistenOnTextEdit) { + this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); + this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler); + } + if (this.blueprint.focused) { + this.listenEvents(); + } + } + + cleanup() { + this.unlistenEvents(); + this.blueprint.removeEventListener(Configuration.focusEventName.begin, this.listenHandler); + this.blueprint.removeEventListener(Configuration.focusEventName.end, this.unlistenHandler); + this.blueprint.removeEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); + this.blueprint.removeEventListener(Configuration.editTextEventName.end, this.listenHandler); + } + + /* Subclasses will probabily override the following methods */ + listenEvents() { + } + + unlistenEvents() { + } +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -392,37 +392,37 @@ class SerializerFactory { // @ts-expect-error return SerializerFactory.#serializers.get(entity) } -} - -/** @typedef {import("./IEntity").AttributeDeclarations} AttributeDeclarations */ - -class SubAttributesDeclaration { - - /** @param {AttributeDeclarations} attributes */ - constructor(attributes) { - this.attributes = attributes; - } -} - -/** @typedef {import("./IEntity").AnyValueConstructor<*>} AnyValueConstructor */ - -class UnionType { - - #types - get types() { - return this.#types - } - - /** @param {...AnyValueConstructor} types */ - constructor(...types) { - this.#types = types; - } - - getFirstType() { - return this.#types[0] - } -} - +} + +/** @typedef {import("./IEntity").AttributeDeclarations} AttributeDeclarations */ + +class SubAttributesDeclaration { + + /** @param {AttributeDeclarations} attributes */ + constructor(attributes) { + this.attributes = attributes; + } +} + +/** @typedef {import("./IEntity").AnyValueConstructor<*>} AnyValueConstructor */ + +class UnionType { + + #types + get types() { + return this.#types + } + + /** @param {...AnyValueConstructor} types */ + constructor(...types) { + this.#types = types; + } + + getFirstType() { + return this.#types[0] + } +} + /** * @typedef {import("./element/IElement").default} IElement * @typedef {import("./entity/IEntity").AnyValue} AnyValue @@ -846,258 +846,258 @@ class Utility { }; requestAnimationFrame(doAnimation); } -} - -/** - * @typedef {(entity: IEntity) => AnyValue} ValueSupplier - * @typedef {(entity: IEntity) => AnyValueConstructor} TypeSupplier - * @typedef {IEntity | String | Number | BigInt | Boolean} AnySimpleValue - * @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue - * @typedef {{ - * [key: String]: AttributeInformation | AnyValue | SubAttributesDeclaration - * }} AttributeDeclarations - * @typedef {typeof IEntity} EntityConstructor - * @typedef {{ - * type?: AnyValueConstructor | AnyValueConstructor[] | UnionType | TypeSupplier, - * value?: AnyValue | ValueSupplier, - * showDefault?: Boolean, - * nullable?: Boolean, - * ignored?: Boolean, - * serialized?: Boolean, - * expected?: Boolean, - * predicate?: (value: AnyValue) => Boolean, - * }} AttributeInformation - */ - -/** - * @template {AnyValue} T - * @typedef {(new () => T) | EntityConstructor | StringConstructor | NumberConstructor | BigIntConstructor - * | BooleanConstructor | ArrayConstructor} AnyValueConstructor - */ - -class IEntity { - - /** @type {AttributeDeclarations} */ - static attributes = {} - static defaultAttribute = { - showDefault: true, - nullable: false, - ignored: false, - serialized: false, - expected: false, - } - - constructor(values = {}, suppressWarns = false) { - /** - * @param {Object} target - * @param {Object} attributes - * @param {Object} values - * @param {String} prefix - */ - const defineAllAttributes = (target, attributes, values = {}, prefix = "") => { - const valuesNames = Object.keys(values); - const attributesNames = Object.keys(attributes); - const allAttributesNames = Utility.mergeArrays(attributesNames, valuesNames); - for (let attributeName of allAttributesNames) { - let value = Utility.objectGet(values, [attributeName]); - /** @type {AttributeInformation} */ - let attribute = attributes[attributeName]; - - if (attribute instanceof SubAttributesDeclaration) { - target[attributeName] = {}; - defineAllAttributes( - target[attributeName], - attribute.attributes, - values[attributeName], - attributeName + "." - ); - continue - } - - if (!suppressWarns) { - if (!(attributeName in attributes)) { - console.warn( - `UEBlueprint: Attribute ${prefix}${attributeName} in the serialized data is not defined in ` - + `${this.constructor.name}.attributes` - ); - } else if ( - valuesNames.length > 0 - && !(attributeName in values) - && !(!attribute.showDefault || attribute.ignored) - ) { - console.warn( - `UEBlueprint: ${this.constructor.name} will add attribute ${prefix}${attributeName} not ` - + "defined in the serialized data" - ); - } - } - - if (!attribute) { - // Remember attributeName can come from the values and be not defined in the attributes - target[attributeName] = value; - continue - } - - let defaultValue = attribute.value; - let defaultType = attribute.type; - if (attribute.serialized && defaultType instanceof Function) { - // If the attribute is serialized, the type must contain a function providing the type - defaultType = /** @type {TypeSupplier} */(defaultType)(this); - } - if (defaultType instanceof Array) { - defaultType = Array; - } - if (defaultValue instanceof Function) { - defaultValue = defaultValue(this); - } - if (defaultType === undefined) { - defaultType = Utility.getType(defaultValue); - } - const assignAttribute = !attribute.predicate - ? v => target[attributeName] = v - : v => { - Object.defineProperties(target, { - ["#" + attributeName]: { - writable: true, - enumerable: false, - }, - [attributeName]: { - enumerable: true, - get() { - return this["#" + attributeName] - }, - set(v) { - if (!attribute.predicate?.(v)) { - console.warn( - `UEBlueprint: Tried to assign attribute ${prefix}${attributeName} to ` - + `${this.constructor.name} not satisfying the predicate` - ); - return - } - this["#" + attributeName] = v; - } - }, - }); - this[attributeName] = v; - }; - - if (value !== undefined) { - // Remember value can still be null - if (value?.constructor === String && attribute.serialized && defaultType !== String) { - value = SerializerFactory - .getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType)) - .deserialize(/** @type {String} */(value)); - } - assignAttribute(Utility.sanitize(value, /** @type {AnyValueConstructor<*>} */(defaultType))); - continue // We have a value, need nothing more - } - if (defaultType instanceof UnionType) { - if (defaultValue != undefined) { - defaultType = defaultType.types.find( - type => defaultValue instanceof type || defaultValue.constructor == type - ) ?? defaultType.getFirstType(); - } else { - defaultType = defaultType.getFirstType(); - } - } - if (defaultValue === undefined) { - defaultValue = Utility.sanitize(new /** @type {AnyValueConstructor<*>} */(defaultType)()); - } - if (!attribute.showDefault) { - assignAttribute(undefined); // Declare undefined to preserve the order of attributes - continue - } - if (attribute.serialized) { - if (defaultType !== String && defaultValue.constructor === String) { - defaultValue = SerializerFactory - .getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType)) - .deserialize(defaultValue); - } - } - assignAttribute(Utility.sanitize( - /** @type {AnyValue} */(defaultValue), - /** @type {AnyValueConstructor} */(defaultType) - )); - } - }; - const attributes = /** @type {typeof IEntity} */(this.constructor).attributes; - if (values.constructor !== Object && Object.keys(attributes).length === 1) { - // Where there is just one attribute, option can be the value of that attribute - values = { - [Object.keys(attributes)[0]]: values - }; - } - defineAllAttributes(this, attributes, values); - } - - /** @param {AttributeDeclarations} attributes */ - static cleanupAttributes(attributes, prefix = "") { - for (const attributeName in attributes) { - if (attributes[attributeName] instanceof SubAttributesDeclaration) { - this.cleanupAttributes( - /** @type {SubAttributesDeclaration} */(attributes[attributeName]).attributes, - prefix + "." + attributeName - ); - continue - } - if (attributes[attributeName].constructor !== Object) { - attributes[attributeName] = { - value: attributes[attributeName], - }; - } - const attribute = /** @type {AttributeInformation} */(attributes[attributeName]); - if (attribute.type === undefined && !(attribute.value instanceof Function)) { - attribute.type = Utility.getType(attribute.value); - } - attributes[attributeName] = { - ...IEntity.defaultAttribute, - ...attribute, - }; - if (attribute.value === undefined && attribute.type === undefined) { - throw new Error( - `UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}` - + attributeName - ) - } - if (attribute.value === null) { - attributes[attributeName].nullable = true; - } - } - } - - static isValueOfType(value, type) { - return value != null && (value instanceof type || value.constructor === type) - } - - static expectsAllKeys() { - return !Object.values(this.attributes) - .filter(/** @param {AttributeInformation} attribute */attribute => !attribute.ignored) - .some(/** @param {AttributeInformation} attribute */attribute => !attribute.expected) - } - - unexpectedKeys() { - return Object.keys(this).length - - Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length - } - - /** @param {IEntity} other */ - equals(other) { - const thisKeys = Object.keys(this); - const otherKeys = Object.keys(this); - if (thisKeys.length != otherKeys.length) { - return false - } - for (const key of thisKeys) { - if (this[key] instanceof IEntity && !this[key].equals(other[key])) { - return false - } else if (!Utility.equals(this[key], other[key])) { - return false - } - } - return true - } -} - +} + +/** + * @typedef {(entity: IEntity) => AnyValue} ValueSupplier + * @typedef {(entity: IEntity) => AnyValueConstructor} TypeSupplier + * @typedef {IEntity | String | Number | BigInt | Boolean} AnySimpleValue + * @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue + * @typedef {{ + * [key: String]: AttributeInformation | AnyValue | SubAttributesDeclaration + * }} AttributeDeclarations + * @typedef {typeof IEntity} EntityConstructor + * @typedef {{ + * type?: AnyValueConstructor | AnyValueConstructor[] | UnionType | TypeSupplier, + * value?: AnyValue | ValueSupplier, + * showDefault?: Boolean, + * nullable?: Boolean, + * ignored?: Boolean, + * serialized?: Boolean, + * expected?: Boolean, + * predicate?: (value: AnyValue) => Boolean, + * }} AttributeInformation + */ + +/** + * @template {AnyValue} T + * @typedef {(new () => T) | EntityConstructor | StringConstructor | NumberConstructor | BigIntConstructor + * | BooleanConstructor | ArrayConstructor} AnyValueConstructor + */ + +class IEntity { + + /** @type {AttributeDeclarations} */ + static attributes = {} + static defaultAttribute = { + showDefault: true, + nullable: false, + ignored: false, + serialized: false, + expected: false, + } + + constructor(values = {}, suppressWarns = false) { + /** + * @param {Object} target + * @param {Object} attributes + * @param {Object} values + * @param {String} prefix + */ + const defineAllAttributes = (target, attributes, values = {}, prefix = "") => { + const valuesNames = Object.keys(values); + const attributesNames = Object.keys(attributes); + const allAttributesNames = Utility.mergeArrays(attributesNames, valuesNames); + for (let attributeName of allAttributesNames) { + let value = Utility.objectGet(values, [attributeName]); + /** @type {AttributeInformation} */ + let attribute = attributes[attributeName]; + + if (attribute instanceof SubAttributesDeclaration) { + target[attributeName] = {}; + defineAllAttributes( + target[attributeName], + attribute.attributes, + values[attributeName], + attributeName + "." + ); + continue + } + + if (!suppressWarns) { + if (!(attributeName in attributes)) { + console.warn( + `UEBlueprint: Attribute ${prefix}${attributeName} in the serialized data is not defined in ` + + `${this.constructor.name}.attributes` + ); + } else if ( + valuesNames.length > 0 + && !(attributeName in values) + && !(!attribute.showDefault || attribute.ignored) + ) { + console.warn( + `UEBlueprint: ${this.constructor.name} will add attribute ${prefix}${attributeName} not ` + + "defined in the serialized data" + ); + } + } + + if (!attribute) { + // Remember attributeName can come from the values and be not defined in the attributes + target[attributeName] = value; + continue + } + + let defaultValue = attribute.value; + let defaultType = attribute.type; + if (attribute.serialized && defaultType instanceof Function) { + // If the attribute is serialized, the type must contain a function providing the type + defaultType = /** @type {TypeSupplier} */(defaultType)(this); + } + if (defaultType instanceof Array) { + defaultType = Array; + } + if (defaultValue instanceof Function) { + defaultValue = defaultValue(this); + } + if (defaultType === undefined) { + defaultType = Utility.getType(defaultValue); + } + const assignAttribute = !attribute.predicate + ? v => target[attributeName] = v + : v => { + Object.defineProperties(target, { + ["#" + attributeName]: { + writable: true, + enumerable: false, + }, + [attributeName]: { + enumerable: true, + get() { + return this["#" + attributeName] + }, + set(v) { + if (!attribute.predicate?.(v)) { + console.warn( + `UEBlueprint: Tried to assign attribute ${prefix}${attributeName} to ` + + `${this.constructor.name} not satisfying the predicate` + ); + return + } + this["#" + attributeName] = v; + } + }, + }); + this[attributeName] = v; + }; + + if (value !== undefined) { + // Remember value can still be null + if (value?.constructor === String && attribute.serialized && defaultType !== String) { + value = SerializerFactory + .getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType)) + .deserialize(/** @type {String} */(value)); + } + assignAttribute(Utility.sanitize(value, /** @type {AnyValueConstructor<*>} */(defaultType))); + continue // We have a value, need nothing more + } + if (defaultType instanceof UnionType) { + if (defaultValue != undefined) { + defaultType = defaultType.types.find( + type => defaultValue instanceof type || defaultValue.constructor == type + ) ?? defaultType.getFirstType(); + } else { + defaultType = defaultType.getFirstType(); + } + } + if (defaultValue === undefined) { + defaultValue = Utility.sanitize(new /** @type {AnyValueConstructor<*>} */(defaultType)()); + } + if (!attribute.showDefault) { + assignAttribute(undefined); // Declare undefined to preserve the order of attributes + continue + } + if (attribute.serialized) { + if (defaultType !== String && defaultValue.constructor === String) { + defaultValue = SerializerFactory + .getSerializer(/** @type {AnyValueConstructor<*>} */(defaultType)) + .deserialize(defaultValue); + } + } + assignAttribute(Utility.sanitize( + /** @type {AnyValue} */(defaultValue), + /** @type {AnyValueConstructor} */(defaultType) + )); + } + }; + const attributes = /** @type {typeof IEntity} */(this.constructor).attributes; + if (values.constructor !== Object && Object.keys(attributes).length === 1) { + // Where there is just one attribute, option can be the value of that attribute + values = { + [Object.keys(attributes)[0]]: values + }; + } + defineAllAttributes(this, attributes, values); + } + + /** @param {AttributeDeclarations} attributes */ + static cleanupAttributes(attributes, prefix = "") { + for (const attributeName in attributes) { + if (attributes[attributeName] instanceof SubAttributesDeclaration) { + this.cleanupAttributes( + /** @type {SubAttributesDeclaration} */(attributes[attributeName]).attributes, + prefix + "." + attributeName + ); + continue + } + if (attributes[attributeName].constructor !== Object) { + attributes[attributeName] = { + value: attributes[attributeName], + }; + } + const attribute = /** @type {AttributeInformation} */(attributes[attributeName]); + if (attribute.type === undefined && !(attribute.value instanceof Function)) { + attribute.type = Utility.getType(attribute.value); + } + attributes[attributeName] = { + ...IEntity.defaultAttribute, + ...attribute, + }; + if (attribute.value === undefined && attribute.type === undefined) { + throw new Error( + `UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}` + + attributeName + ) + } + if (attribute.value === null) { + attributes[attributeName].nullable = true; + } + } + } + + static isValueOfType(value, type) { + return value != null && (value instanceof type || value.constructor === type) + } + + static expectsAllKeys() { + return !Object.values(this.attributes) + .filter(/** @param {AttributeInformation} attribute */attribute => !attribute.ignored) + .some(/** @param {AttributeInformation} attribute */attribute => !attribute.expected) + } + + unexpectedKeys() { + return Object.keys(this).length + - Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length + } + + /** @param {IEntity} other */ + equals(other) { + const thisKeys = Object.keys(this); + const otherKeys = Object.keys(this); + if (thisKeys.length != otherKeys.length) { + return false + } + for (const key of thisKeys) { + if (this[key] instanceof IEntity && !this[key].equals(other[key])) { + return false + } else if (!Utility.equals(this[key], other[key])) { + return false + } + } + return true + } +} + class IntegerEntity extends IEntity { static attributes = { @@ -1124,8 +1124,8 @@ class IntegerEntity extends IEntity { toString() { return this.value.toString() } -} - +} + class ByteEntity extends IntegerEntity { static attributes = { @@ -1143,36 +1143,36 @@ class ByteEntity extends IntegerEntity { constructor(values = 0) { super(values); } -} - -class SymbolEntity extends IEntity { - - static attributes = { - value: "", - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } -} - +} + +class SymbolEntity extends IEntity { + + static attributes = { + value: "", + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } +} + class EnumEntity extends SymbolEntity { -} - +} + class GuidEntity extends IEntity { static attributes = { @@ -1210,8 +1210,8 @@ class GuidEntity extends IEntity { toString() { return this.value } -} - +} + class ObjectReferenceEntity extends IEntity { static attributes = { @@ -1237,8 +1237,8 @@ class ObjectReferenceEntity extends IEntity { getName() { return this.path.match(/[^\.\/]+$/)?.[0] ?? "" } -} - +} + class FunctionReferenceEntity extends IEntity { static attributes = { @@ -1265,37 +1265,37 @@ class FunctionReferenceEntity extends IEntity { /** @type {ObjectReferenceEntity} */ this.MemberParent; /** @type {String} */ this.MemberName; } -} - -class IdentifierEntity extends IEntity { - - static attributes = { - value: "", - } - - static { - this.cleanupAttributes(this.attributes); - } - - static attributeConverter = { - fromAttribute: (value, type) => new IdentifierEntity(value), - toAttribute: (value, type) => value.toString() - } - - constructor(values) { - super(values); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } -} - +} + +class IdentifierEntity extends IEntity { + + static attributes = { + value: "", + } + + static { + this.cleanupAttributes(this.attributes); + } + + static attributeConverter = { + fromAttribute: (value, type) => new IdentifierEntity(value), + toAttribute: (value, type) => value.toString() + } + + constructor(values) { + super(values); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } +} + class Integer64Entity extends IEntity { static attributes = { @@ -1322,324 +1322,324 @@ class Integer64Entity extends IEntity { toString() { return this.value.toString() } -} - -class InvariantTextEntity extends IEntity { - - static lookbehind = "INVTEXT" - static attributes = { - value: "", - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.value; - } -} - -class KeyBindingEntity extends IEntity { - - static attributes = { - ActionName: "", - bShift: false, - bCtrl: false, - bAlt: false, - bCmd: false, - Key: { - type: IdentifierEntity - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values = {}) { - values.ActionName = values.ActionName ?? ""; - values.bShift = values.bShift ?? false; - values.bCtrl = values.bCtrl ?? false; - values.bAlt = values.bAlt ?? false; - values.bCmd = values.bCmd ?? false; - super(values); - /** @type {String} */ this.ActionName; - /** @type {Boolean} */ this.bShift; - /** @type {Boolean} */ this.bCtrl; - /** @type {Boolean} */ this.bAlt; - /** @type {Boolean} */ this.bCmd; - /** @type {IdentifierEntity} */ this.Key; - } -} - -class RealUnitEntity extends IEntity { - - static attributes = { - value: 0, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values = 0) { - super(values); - this.value = Utility.clamp(this.value, 0, 1); - } - - valueOf() { - return this.value - } - - toString() { - return this.value.toFixed(6) - } -} - -class LinearColorEntity extends IEntity { - - static attributes = { - R: { - type: RealUnitEntity, - expected: true, - }, - G: { - type: RealUnitEntity, - expected: true, - }, - B: { - type: RealUnitEntity, - expected: true, - }, - A: { - type: RealUnitEntity, - value: () => new RealUnitEntity(1), - }, - H: { - type: RealUnitEntity, - showDefault: true, - ignored: true, - }, - S: { - type: RealUnitEntity, - showDefault: true, - ignored: true, - }, - V: { - type: RealUnitEntity, - showDefault: true, - ignored: true, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - /** @param {Number} x */ - static linearToSRGB(x) { - if (x <= 0) { - return 0 - } else if (x >= 1) { - return 1 - } else if (x < 0.0031308) { - return x * 12.92 - } else { - return Math.pow(x, 1 / 2.4) * 1.055 - 0.055 - } - } - - /** @param {Number} x */ - static sRGBtoLinear(x) { - if (x <= 0) { - return 0 - } else if (x >= 1) { - return 1 - } else if (x < 0.04045) { - return x / 12.92 - } else { - return Math.pow((x + 0.055) / 1.055, 2.4) - } - } - - static getWhite() { - return new LinearColorEntity({ - R: 1, - G: 1, - B: 1, - }) - } - - constructor(values) { - if (values instanceof Array) { - values = { - R: values[0] ?? 0, - G: values[1] ?? 0, - B: values[2] ?? 0, - A: values[3] ?? 1, - }; - } - super(values); - /** @type {RealUnitEntity} */ this.R; - /** @type {RealUnitEntity} */ this.G; - /** @type {RealUnitEntity} */ this.B; - /** @type {RealUnitEntity} */ this.A; - /** @type {RealUnitEntity} */ this.H; - /** @type {RealUnitEntity} */ this.S; - /** @type {RealUnitEntity} */ this.V; - this.#updateHSV(); - } - - #updateHSV() { - const r = this.R.value; - const g = this.G.value; - const b = this.B.value; - if (Utility.approximatelyEqual(r, g) && Utility.approximatelyEqual(r, b) && Utility.approximatelyEqual(g, b)) { - this.S.value = 0; - this.V.value = r; - return - } - const max = Math.max(r, g, b); - const min = Math.min(r, g, b); - const d = max - min; - let h; - switch (max) { - case min: - h = 0; - break - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break - case g: - h = (b - r) / d + 2; - break - case b: - h = (r - g) / d + 4; - break - } - h /= 6; - this.H.value = h; - this.S.value = max == 0 ? 0 : d / max; - this.V.value = max; - } - - /** - * @param {Number} r - * @param {Number} g - * @param {Number} b - * @param {Number} a - */ - setFromRGBA(r, g, b, a = 1) { - this.R.value = r; - this.G.value = g; - this.B.value = b; - this.A.value = a; - this.#updateHSV(); - } - - /** - * @param {Number} h - * @param {Number} s - * @param {Number} v - * @param {Number} a - */ - setFromHSVA(h, s, v, a = 1) { - const i = Math.floor(h * 6); - const f = h * 6 - i; - const p = v * (1 - s); - const q = v * (1 - f * s); - const t = v * (1 - (1 - f) * s); - const values = [v, q, p, p, t, v]; - const [r, g, b] = [values[i % 6], values[(i + 4) % 6], values[(i + 2) % 6]]; - this.R.value = r; - this.G.value = g; - this.B.value = b; - this.A.value = a; - this.H.value = h; - this.S.value = s; - this.V.value = v; - } - - /** - * @param {Number} x - * @param {Number} y - * @param {Number} v - * @param {Number} a - */ - setFromWheelLocation(x, y, v, a) { - const [r, theta] = Utility.getPolarCoordinates(x, y, true); - this.setFromHSVA(1 - theta / (2 * Math.PI), r, v, a); - } - - toRGBA() { - return [ - Math.round(this.R.value * 255), - Math.round(this.G.value * 255), - Math.round(this.B.value * 255), - Math.round(this.A.value * 255), - ] - } - - toSRGBA() { - return [ - Math.round(LinearColorEntity.linearToSRGB(this.R.value) * 255), - Math.round(LinearColorEntity.linearToSRGB(this.G.value) * 255), - Math.round(LinearColorEntity.linearToSRGB(this.B.value) * 255), - Math.round(this.A.value * 255), - ] - } - - toRGBAString() { - return this - .toRGBA() - .map(v => v.toString(16).toUpperCase().padStart(2, "0")) - .join("") - } - - toSRGBAString() { - return this - .toSRGBA() - .map(v => v.toString(16).toUpperCase().padStart(2, "0")) - .join("") - } - - toHSVA() { - return [this.H.value, this.S.value, this.V.value, this.A.value] - } - - toNumber() { - return ( - Math.round(this.R.value * 0xff) << 24) - + (Math.round(this.G.value * 0xff) << 16) - + (Math.round(this.B.value * 0xff) << 8) - + Math.round(this.A.value * 0xff) - } - - /** @param {Number} number */ - setFromRGBANumber(number) { - this.A.value = (number & 0xff) / 0xff; - this.B.value = ((number >> 8) & 0xff) / 0xff; - this.G.value = ((number >> 16) & 0xff) / 0xff; - this.R.value = ((number >> 24) & 0xff) / 0xff; - this.#updateHSV(); - } - - /** @param {Number} number */ - setFromSRGBANumber(number) { - this.A.value = (number & 0xff) / 0xff; - this.B.value = LinearColorEntity.sRGBtoLinear(((number >> 8) & 0xff) / 0xff); - this.G.value = LinearColorEntity.sRGBtoLinear(((number >> 16) & 0xff) / 0xff); - this.R.value = LinearColorEntity.sRGBtoLinear(((number >> 24) & 0xff) / 0xff); - this.#updateHSV(); - } - - toString() { - return Utility.printLinearColor(this) - } -} - +} + +class InvariantTextEntity extends IEntity { + + static lookbehind = "INVTEXT" + static attributes = { + value: "", + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.value; + } +} + +class KeyBindingEntity extends IEntity { + + static attributes = { + ActionName: "", + bShift: false, + bCtrl: false, + bAlt: false, + bCmd: false, + Key: { + type: IdentifierEntity + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values = {}) { + values.ActionName = values.ActionName ?? ""; + values.bShift = values.bShift ?? false; + values.bCtrl = values.bCtrl ?? false; + values.bAlt = values.bAlt ?? false; + values.bCmd = values.bCmd ?? false; + super(values); + /** @type {String} */ this.ActionName; + /** @type {Boolean} */ this.bShift; + /** @type {Boolean} */ this.bCtrl; + /** @type {Boolean} */ this.bAlt; + /** @type {Boolean} */ this.bCmd; + /** @type {IdentifierEntity} */ this.Key; + } +} + +class RealUnitEntity extends IEntity { + + static attributes = { + value: 0, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values = 0) { + super(values); + this.value = Utility.clamp(this.value, 0, 1); + } + + valueOf() { + return this.value + } + + toString() { + return this.value.toFixed(6) + } +} + +class LinearColorEntity extends IEntity { + + static attributes = { + R: { + type: RealUnitEntity, + expected: true, + }, + G: { + type: RealUnitEntity, + expected: true, + }, + B: { + type: RealUnitEntity, + expected: true, + }, + A: { + type: RealUnitEntity, + value: () => new RealUnitEntity(1), + }, + H: { + type: RealUnitEntity, + showDefault: true, + ignored: true, + }, + S: { + type: RealUnitEntity, + showDefault: true, + ignored: true, + }, + V: { + type: RealUnitEntity, + showDefault: true, + ignored: true, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + /** @param {Number} x */ + static linearToSRGB(x) { + if (x <= 0) { + return 0 + } else if (x >= 1) { + return 1 + } else if (x < 0.0031308) { + return x * 12.92 + } else { + return Math.pow(x, 1 / 2.4) * 1.055 - 0.055 + } + } + + /** @param {Number} x */ + static sRGBtoLinear(x) { + if (x <= 0) { + return 0 + } else if (x >= 1) { + return 1 + } else if (x < 0.04045) { + return x / 12.92 + } else { + return Math.pow((x + 0.055) / 1.055, 2.4) + } + } + + static getWhite() { + return new LinearColorEntity({ + R: 1, + G: 1, + B: 1, + }) + } + + constructor(values) { + if (values instanceof Array) { + values = { + R: values[0] ?? 0, + G: values[1] ?? 0, + B: values[2] ?? 0, + A: values[3] ?? 1, + }; + } + super(values); + /** @type {RealUnitEntity} */ this.R; + /** @type {RealUnitEntity} */ this.G; + /** @type {RealUnitEntity} */ this.B; + /** @type {RealUnitEntity} */ this.A; + /** @type {RealUnitEntity} */ this.H; + /** @type {RealUnitEntity} */ this.S; + /** @type {RealUnitEntity} */ this.V; + this.#updateHSV(); + } + + #updateHSV() { + const r = this.R.value; + const g = this.G.value; + const b = this.B.value; + if (Utility.approximatelyEqual(r, g) && Utility.approximatelyEqual(r, b) && Utility.approximatelyEqual(g, b)) { + this.S.value = 0; + this.V.value = r; + return + } + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const d = max - min; + let h; + switch (max) { + case min: + h = 0; + break + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break + case g: + h = (b - r) / d + 2; + break + case b: + h = (r - g) / d + 4; + break + } + h /= 6; + this.H.value = h; + this.S.value = max == 0 ? 0 : d / max; + this.V.value = max; + } + + /** + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + */ + setFromRGBA(r, g, b, a = 1) { + this.R.value = r; + this.G.value = g; + this.B.value = b; + this.A.value = a; + this.#updateHSV(); + } + + /** + * @param {Number} h + * @param {Number} s + * @param {Number} v + * @param {Number} a + */ + setFromHSVA(h, s, v, a = 1) { + const i = Math.floor(h * 6); + const f = h * 6 - i; + const p = v * (1 - s); + const q = v * (1 - f * s); + const t = v * (1 - (1 - f) * s); + const values = [v, q, p, p, t, v]; + const [r, g, b] = [values[i % 6], values[(i + 4) % 6], values[(i + 2) % 6]]; + this.R.value = r; + this.G.value = g; + this.B.value = b; + this.A.value = a; + this.H.value = h; + this.S.value = s; + this.V.value = v; + } + + /** + * @param {Number} x + * @param {Number} y + * @param {Number} v + * @param {Number} a + */ + setFromWheelLocation(x, y, v, a) { + const [r, theta] = Utility.getPolarCoordinates(x, y, true); + this.setFromHSVA(1 - theta / (2 * Math.PI), r, v, a); + } + + toRGBA() { + return [ + Math.round(this.R.value * 255), + Math.round(this.G.value * 255), + Math.round(this.B.value * 255), + Math.round(this.A.value * 255), + ] + } + + toSRGBA() { + return [ + Math.round(LinearColorEntity.linearToSRGB(this.R.value) * 255), + Math.round(LinearColorEntity.linearToSRGB(this.G.value) * 255), + Math.round(LinearColorEntity.linearToSRGB(this.B.value) * 255), + Math.round(this.A.value * 255), + ] + } + + toRGBAString() { + return this + .toRGBA() + .map(v => v.toString(16).toUpperCase().padStart(2, "0")) + .join("") + } + + toSRGBAString() { + return this + .toSRGBA() + .map(v => v.toString(16).toUpperCase().padStart(2, "0")) + .join("") + } + + toHSVA() { + return [this.H.value, this.S.value, this.V.value, this.A.value] + } + + toNumber() { + return ( + Math.round(this.R.value * 0xff) << 24) + + (Math.round(this.G.value * 0xff) << 16) + + (Math.round(this.B.value * 0xff) << 8) + + Math.round(this.A.value * 0xff) + } + + /** @param {Number} number */ + setFromRGBANumber(number) { + this.A.value = (number & 0xff) / 0xff; + this.B.value = ((number >> 8) & 0xff) / 0xff; + this.G.value = ((number >> 16) & 0xff) / 0xff; + this.R.value = ((number >> 24) & 0xff) / 0xff; + this.#updateHSV(); + } + + /** @param {Number} number */ + setFromSRGBANumber(number) { + this.A.value = (number & 0xff) / 0xff; + this.B.value = LinearColorEntity.sRGBtoLinear(((number >> 8) & 0xff) / 0xff); + this.G.value = LinearColorEntity.sRGBtoLinear(((number >> 16) & 0xff) / 0xff); + this.R.value = LinearColorEntity.sRGBtoLinear(((number >> 24) & 0xff) / 0xff); + this.#updateHSV(); + } + + toString() { + return Utility.printLinearColor(this) + } +} + class LocalizedTextEntity extends IEntity { static lookbehind = "NSLOCTEXT" @@ -1663,8 +1663,8 @@ class LocalizedTextEntity extends IEntity { toString() { return Utility.capitalFirstLetter(this.value) } -} - +} + class MacroGraphReferenceEntity extends IEntity { static attributes = { @@ -1694,8 +1694,8 @@ class MacroGraphReferenceEntity extends IEntity { const colonIndex = this.MacroGraph.path.search(":"); return this.MacroGraph.path.substring(colonIndex + 1) } -} - +} + class PathSymbolEntity extends IEntity { static attributes = { @@ -1718,8 +1718,8 @@ class PathSymbolEntity extends IEntity { toString() { return this.value } -} - +} + class PinReferenceEntity extends IEntity { static attributes = { @@ -1740,147 +1740,147 @@ class PinReferenceEntity extends IEntity { /** @type {PathSymbolEntity} */ this.objectName; /** @type {GuidEntity} */ this.pinGuid; } -} - -class PinTypeEntity extends IEntity { - - static attributes = { - TerminalCategory: { - value: "", - showDefault: false, - }, - TerminalSubCategory: { - value: "", - showDefault: false, - }, - bTerminalIsConst: { - value: false, - showDefault: false, - }, - bTerminalIsWeakPointer: { - value: false, - showDefault: false, - }, - bTerminalIsUObjectWrapper: { - value: false, - showDefault: false, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.TerminalCategory; - /** @type {String} */ this.TerminalSubCategory; - /** @type {Boolean} */ this.bTerminalIsConst; - /** @type {Boolean} */ this.bTerminalIsWeakPointer; - /** @type {Boolean} */ this.bTerminalIsUObjectWrapper; - } -} - -class RotatorEntity extends IEntity { - - static attributes = { - R: { - value: 0, - }, - P: { - value: 0, - }, - Y: { - value: 0, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {Number} */ this.R; - /** @type {Number} */ this.P; - /** @type {Number} */ this.Y; - } - - getRoll() { - return this.R - } - - getPitch() { - return this.P - } - - getYaw() { - return this.Y - } -} - -class SimpleSerializationRotatorEntity extends RotatorEntity { -} - -class Vector2DEntity extends IEntity { - - static attributes = { - X: { - value: 0, - expected: true, - }, - Y: { - value: 0, - expected: true, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {Number} */ this.X; - /** @type {Number} */ this.Y; - } -} - -class SimpleSerializationVector2DEntity extends Vector2DEntity { -} - -class VectorEntity extends IEntity { - - static attributes = { - X: { - value: 0, - expected: true, - }, - Y: { - value: 0, - expected: true, - }, - Z: { - value: 0, - expected: true, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {Number} */ this.X; - /** @type {Number} */ this.Y; - /** @type {Number} */ this.Z; - } -} - -class SimpleSerializationVectorEntity extends VectorEntity { -} - +} + +class PinTypeEntity extends IEntity { + + static attributes = { + TerminalCategory: { + value: "", + showDefault: false, + }, + TerminalSubCategory: { + value: "", + showDefault: false, + }, + bTerminalIsConst: { + value: false, + showDefault: false, + }, + bTerminalIsWeakPointer: { + value: false, + showDefault: false, + }, + bTerminalIsUObjectWrapper: { + value: false, + showDefault: false, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.TerminalCategory; + /** @type {String} */ this.TerminalSubCategory; + /** @type {Boolean} */ this.bTerminalIsConst; + /** @type {Boolean} */ this.bTerminalIsWeakPointer; + /** @type {Boolean} */ this.bTerminalIsUObjectWrapper; + } +} + +class RotatorEntity extends IEntity { + + static attributes = { + R: { + value: 0, + }, + P: { + value: 0, + }, + Y: { + value: 0, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {Number} */ this.R; + /** @type {Number} */ this.P; + /** @type {Number} */ this.Y; + } + + getRoll() { + return this.R + } + + getPitch() { + return this.P + } + + getYaw() { + return this.Y + } +} + +class SimpleSerializationRotatorEntity extends RotatorEntity { +} + +class Vector2DEntity extends IEntity { + + static attributes = { + X: { + value: 0, + expected: true, + }, + Y: { + value: 0, + expected: true, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {Number} */ this.X; + /** @type {Number} */ this.Y; + } +} + +class SimpleSerializationVector2DEntity extends Vector2DEntity { +} + +class VectorEntity extends IEntity { + + static attributes = { + X: { + value: 0, + expected: true, + }, + Y: { + value: 0, + expected: true, + }, + Z: { + value: 0, + expected: true, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {Number} */ this.X; + /** @type {Number} */ this.Y; + /** @type {Number} */ this.Z; + } +} + +class SimpleSerializationVectorEntity extends VectorEntity { +} + /** * @typedef {import("./IEntity").AnyValue} AnyValue * @typedef {import("lit").CSSResult} CSSResult @@ -2147,300 +2147,300 @@ class PinEntity extends IEntity { ?? Configuration.pinColor[this.PinType.PinCategory] ?? Configuration.pinColor["default"] } -} - -class SVGIcon { - - static array = y` - - - - - - - - - - - - ` - - static branchNode = y` - - - - - - - ` - - static breakStruct = y` - - - - - - ` - - static cast = y` - - - - - - - - - ` - - static close = y` - - - - - ` - - static correct = y` - - - - ` - - static delegate = y` - - - - ` - - static doN = y` - - - - - ` - - static enum = y` - - - - - ` - - static event = y` - - - - - - - ` - - static execPin = y` - - - - ` - - static expandIcon = y` - - - - ` - - static forEachLoop = y` - - - - - - - - - ` - - static functionSymbol = y` - - - - ` - - static gamepad = y` - - - - ` - - static genericPin = y` - - - - - ` - - static keyboard = y` - - - - ` - - static loop = y` - - - - - - - - - - - - ` - - static macro = y` - - - - ` - - static map = y` - - - - - - - - - ` - - static makeArray = y` - - - - - - - - - - - - - - ` - - static makeMap = y` - - - - - - - - - - - ` - - static makeSet = y` - - - - - - - - - ` - - static makeStruct = y` - - - - - - ` - - static mouse = y` - - - - - ` - - static questionMark = y` - - - - - ` - - static referencePin = y` - - - - ` - - static reject = y` - - - - - ` - - static set = y` - - - - - - - ` - - static select = y` - - - - - - - - - ` - - static sequence = y` - - - - - - - - - - ` - - static touchpad = y` - - - - - ` -} - +} + +class SVGIcon { + + static array = y` + + + + + + + + + + + + ` + + static branchNode = y` + + + + + + + ` + + static breakStruct = y` + + + + + + ` + + static cast = y` + + + + + + + + + ` + + static close = y` + + + + + ` + + static correct = y` + + + + ` + + static delegate = y` + + + + ` + + static doN = y` + + + + + ` + + static enum = y` + + + + + ` + + static event = y` + + + + + + + ` + + static execPin = y` + + + + ` + + static expandIcon = y` + + + + ` + + static forEachLoop = y` + + + + + + + + + ` + + static functionSymbol = y` + + + + ` + + static gamepad = y` + + + + ` + + static genericPin = y` + + + + + ` + + static keyboard = y` + + + + ` + + static loop = y` + + + + + + + + + + + + ` + + static macro = y` + + + + ` + + static map = y` + + + + + + + + + ` + + static makeArray = y` + + + + + + + + + + + + + + ` + + static makeMap = y` + + + + + + + + + + + ` + + static makeSet = y` + + + + + + + + + ` + + static makeStruct = y` + + + + + + ` + + static mouse = y` + + + + + ` + + static questionMark = y` + + + + + ` + + static referencePin = y` + + + + ` + + static reject = y` + + + + + ` + + static set = y` + + + + + + + ` + + static select = y` + + + + + + + + + ` + + static sequence = y` + + + + + + + + + + ` + + static touchpad = y` + + + + + ` +} + class VariableReferenceEntity extends IEntity { static attributes = { @@ -2468,8 +2468,8 @@ class VariableReferenceEntity extends IEntity { /** @type {GuidEntity} */ this.GuidEntity; /** @type {Boolean} */ this.bSelfContext; } -} - +} + class ObjectEntity extends IEntity { static attributes = { @@ -3042,7 +3042,7 @@ class ObjectEntity extends IEntity { if (hidValue.includes("Mouse")) { return SVGIcon.mouse } else if (hidValue.includes("Gamepad_Special")) { - return SVGIcon.keyboard // This is called Touchpad in Unreal Engine + return SVGIcon.keyboard // This is called Touchpad in UE } else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) { return SVGIcon.gamepad } else if (hidValue.includes("Touch")) { @@ -3056,43 +3056,43 @@ class ObjectEntity extends IEntity { } return SVGIcon.functionSymbol } -} - -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(a(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=f(t,n.buf);return {coll:n.coll.concat(r.v),buf:r.buf}},{coll:[],buf:t.slice(r,e)},n).coll)})}function h(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 p(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 p("uintBE",n),h("uintBE("+n+")",n).map(function(t){return t.readUIntBE(0,n)})}function v(n){return p("uintLE",n),h("uintLE("+n+")",n).map(function(t){return t.readUIntLE(0,n)})}function g(n){return p("intBE",n),h("intBE("+n+")",n).map(function(t){return t.readIntBE(0,n)})}function m(n){return p("intLE",n),h("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)||"\r"===n.charAt(i)&&"\n"!==n.charAt(i+1))&&(u++,0===o&&(o=i+1)),i--;}var a=e+u,f=t-o;return r[t]={line:a,lineStart:o},{offset:t,line:a+1,column:f+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,f,c=t.index,s=c.offset,l=1;if(s===n.length)return "Got the end of the input";if(w(n)){var h=s-s%I,p=s-h,d=W(h,F,M+I,n.length),v=a(function(n){return a(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=a(function(n){return n.length<=4?n.join(" "):n.slice(0,4).join(" ")+" "+n.slice(4).join(" ")},v),(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=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),f=o.to.toString().length;}var m=e-o.from;return w(n)&&(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=2),i(function(t,e,u){var i,a=u===m,c=a?"> ":z;return i=w(n)?U((8*(o.from+u)).toString(16),f,"0"):U((o.from+u+1).toString(),f," "),[].concat(t,[c+i+" | "+e],a?[z+R(" ",f)+" | "+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],a=o[t];return b(e+i.length,a)}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))}),an=e(function(n,t){return t=0}).desc(t)},e.optWhitespace=hn,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 h("buffer",n).map(function(n){return Buffer.from(n)})},encodedString:function(n,t){return h("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:h("floatBE",4).map(function(n){return n.readFloatBE(0)}),floatLE:h("floatLE",4).map(function(n){return n.readFloatLE(0)}),doubleBE:h("doubleBE",8).map(function(n){return n.readDoubleBE(0)}),doubleLE:h("doubleLE",8).map(function(n){return n.readDoubleLE(0)})},n.exports=e;}])}); -}(parsimmon_umd_min)); - -var Parsimmon = /*@__PURE__*/getDefaultExportFromCjs(parsimmon_umd_min.exports); - -class UnknownKeysEntity extends IEntity { - - static attributes = { - lookbehind: - { - value: "", - showDefault: false, - ignore: true, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.lookbehind; - } -} - +} + +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(a(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=f(t,n.buf);return {coll:n.coll.concat(r.v),buf:r.buf}},{coll:[],buf:t.slice(r,e)},n).coll)})}function h(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 p(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 p("uintBE",n),h("uintBE("+n+")",n).map(function(t){return t.readUIntBE(0,n)})}function v(n){return p("uintLE",n),h("uintLE("+n+")",n).map(function(t){return t.readUIntLE(0,n)})}function g(n){return p("intBE",n),h("intBE("+n+")",n).map(function(t){return t.readIntBE(0,n)})}function m(n){return p("intLE",n),h("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)||"\r"===n.charAt(i)&&"\n"!==n.charAt(i+1))&&(u++,0===o&&(o=i+1)),i--;}var a=e+u,f=t-o;return r[t]={line:a,lineStart:o},{offset:t,line:a+1,column:f+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,f,c=t.index,s=c.offset,l=1;if(s===n.length)return "Got the end of the input";if(w(n)){var h=s-s%I,p=s-h,d=W(h,F,M+I,n.length),v=a(function(n){return a(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=a(function(n){return n.length<=4?n.join(" "):n.slice(0,4).join(" ")+" "+n.slice(4).join(" ")},v),(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=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),f=o.to.toString().length;}var m=e-o.from;return w(n)&&(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=2),i(function(t,e,u){var i,a=u===m,c=a?"> ":z;return i=w(n)?U((8*(o.from+u)).toString(16),f,"0"):U((o.from+u+1).toString(),f," "),[].concat(t,[c+i+" | "+e],a?[z+R(" ",f)+" | "+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],a=o[t];return b(e+i.length,a)}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))}),an=e(function(n,t){return t=0}).desc(t)},e.optWhitespace=hn,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 h("buffer",n).map(function(n){return Buffer.from(n)})},encodedString:function(n,t){return h("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:h("floatBE",4).map(function(n){return n.readFloatBE(0)}),floatLE:h("floatLE",4).map(function(n){return n.readFloatLE(0)}),doubleBE:h("doubleBE",8).map(function(n){return n.readDoubleBE(0)}),doubleLE:h("doubleLE",8).map(function(n){return n.readDoubleLE(0)})},n.exports=e;}])}); +}(parsimmon_umd_min)); + +var Parsimmon = /*@__PURE__*/getDefaultExportFromCjs(parsimmon_umd_min.exports); + +class UnknownKeysEntity extends IEntity { + + static attributes = { + lookbehind: + { + value: "", + showDefault: false, + ignore: true, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.lookbehind; + } +} + // @ts-nocheck /** @@ -3667,146 +3667,146 @@ class Grammar { return result } ) -} - -/** - * @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor - * @typedef {import("../entity/IEntity").AnyValue} AnyValue - * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor - */ - -/** @template {AnyValue} T */ -class ISerializer { - - static grammar = Parsimmon.createLanguage(new Grammar()) - - /** @param {AnyValueConstructor} entityType */ - constructor( - entityType, - attributePrefix = "", - attributeSeparator = ",", - trailingSeparator = false, - attributeValueConjunctionSign = "=", - attributeKeyPrinter = k => k.join(".") - ) { - this.entityType = entityType; - this.attributePrefix = attributePrefix; - this.attributeSeparator = attributeSeparator; - this.trailingSeparator = trailingSeparator; - this.attributeValueConjunctionSign = attributeValueConjunctionSign; - this.attributeKeyPrinter = attributeKeyPrinter; - } - - /** - * @param {String} value - * @returns {T} - */ - deserialize(value) { - return this.read(value) - } - - /** @param {T} object */ - serialize(object, insideString = false, entity = object) { - return this.write(entity, object, insideString) - } - - /** - * @protected - * @param {String} value - * @returns {T} - */ - read(value) { - throw new Error("Not implemented") - } - - /** - * @protected - * @param {T} object - * @param {Boolean} insideString - * @returns {String} - */ - write(entity, object, insideString) { - throw new Error("Not implemented") - } - - /** - * @protected - * @param {AnyValue} value - * @param {String[]} fullKey - * @param {Boolean} insideString - */ - writeValue(entity, value, fullKey, insideString) { - const type = Utility.getType(value); - // @ts-expect-error - const serializer = SerializerFactory.getSerializer(type); - if (!serializer) { - throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`) - } - return serializer.write( - value instanceof IEntity ? value : entity, - value, - insideString - ) - } - - /** - * @protected - * @param {String[]} key - * @param {Object} object - * @param {Boolean} insideString - * @returns {String} - */ - subWrite(entity, key, object, insideString) { - let result = ""; - let fullKey = key.concat(""); - const last = fullKey.length - 1; - const attributes = /** @type {EntityConstructor} */(object.constructor).attributes; - const keys = attributes - ? Utility.mergeArrays( - Object.keys(attributes), - Object.keys(object) - ) - : Object.keys(object); - for (const property of keys) { - fullKey[last] = property; - const value = object[property]; - if (value?.constructor === Object) { - // Recursive call when finding an object - result += (result.length ? this.attributeSeparator : "") - + this.subWrite(entity, fullKey, value, insideString); - } else if (value !== undefined && this.showProperty(entity, object, fullKey, value)) { - const isSerialized = Utility.isSerialized(entity, fullKey); - result += (result.length ? this.attributeSeparator : "") - + this.attributePrefix - + this.attributeKeyPrinter(fullKey) - + this.attributeValueConjunctionSign - + ( - isSerialized - ? `"${this.writeValue(entity, value, fullKey, true)}"` - : this.writeValue(entity, value, fullKey, insideString) - ); - } - } - if (this.trailingSeparator && result.length && fullKey.length === 1) { - // append separator at the end if asked and there was printed content - result += this.attributeSeparator; - } - return result - } - - showProperty(entity, object, attributeKey, attributeValue) { - const attributes = /** @type {EntityConstructor} */(this.entityType).attributes; - const attribute = Utility.objectGet(attributes, attributeKey); - if (attribute?.constructor === Object) { - if (attribute.ignored) { - return false - } - return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault - } - return true - } -} - +} + +/** + * @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor + * @typedef {import("../entity/IEntity").AnyValue} AnyValue + * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor + */ + +/** @template {AnyValue} T */ +class ISerializer { + + static grammar = Parsimmon.createLanguage(new Grammar()) + + /** @param {AnyValueConstructor} entityType */ + constructor( + entityType, + attributePrefix = "", + attributeSeparator = ",", + trailingSeparator = false, + attributeValueConjunctionSign = "=", + attributeKeyPrinter = k => k.join(".") + ) { + this.entityType = entityType; + this.attributePrefix = attributePrefix; + this.attributeSeparator = attributeSeparator; + this.trailingSeparator = trailingSeparator; + this.attributeValueConjunctionSign = attributeValueConjunctionSign; + this.attributeKeyPrinter = attributeKeyPrinter; + } + + /** + * @param {String} value + * @returns {T} + */ + deserialize(value) { + return this.read(value) + } + + /** @param {T} object */ + serialize(object, insideString = false, entity = object) { + return this.write(entity, object, insideString) + } + + /** + * @protected + * @param {String} value + * @returns {T} + */ + read(value) { + throw new Error("Not implemented") + } + + /** + * @protected + * @param {T} object + * @param {Boolean} insideString + * @returns {String} + */ + write(entity, object, insideString) { + throw new Error("Not implemented") + } + + /** + * @protected + * @param {AnyValue} value + * @param {String[]} fullKey + * @param {Boolean} insideString + */ + writeValue(entity, value, fullKey, insideString) { + const type = Utility.getType(value); + // @ts-expect-error + const serializer = SerializerFactory.getSerializer(type); + if (!serializer) { + throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`) + } + return serializer.write( + value instanceof IEntity ? value : entity, + value, + insideString + ) + } + + /** + * @protected + * @param {String[]} key + * @param {Object} object + * @param {Boolean} insideString + * @returns {String} + */ + subWrite(entity, key, object, insideString) { + let result = ""; + let fullKey = key.concat(""); + const last = fullKey.length - 1; + const attributes = /** @type {EntityConstructor} */(object.constructor).attributes; + const keys = attributes + ? Utility.mergeArrays( + Object.keys(attributes), + Object.keys(object) + ) + : Object.keys(object); + for (const property of keys) { + fullKey[last] = property; + const value = object[property]; + if (value?.constructor === Object) { + // Recursive call when finding an object + result += (result.length ? this.attributeSeparator : "") + + this.subWrite(entity, fullKey, value, insideString); + } else if (value !== undefined && this.showProperty(entity, object, fullKey, value)) { + const isSerialized = Utility.isSerialized(entity, fullKey); + result += (result.length ? this.attributeSeparator : "") + + this.attributePrefix + + this.attributeKeyPrinter(fullKey) + + this.attributeValueConjunctionSign + + ( + isSerialized + ? `"${this.writeValue(entity, value, fullKey, true)}"` + : this.writeValue(entity, value, fullKey, insideString) + ); + } + } + if (this.trailingSeparator && result.length && fullKey.length === 1) { + // append separator at the end if asked and there was printed content + result += this.attributeSeparator; + } + return result + } + + showProperty(entity, object, attributeKey, attributeValue) { + const attributes = /** @type {EntityConstructor} */(this.entityType).attributes; + const attribute = Utility.objectGet(attributes, attributeKey); + if (attribute?.constructor === Object) { + if (attribute.ignored) { + return false + } + return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault + } + return true + } +} + class ObjectSerializer extends ISerializer { constructor() { @@ -3863,8 +3863,8 @@ ${this.subWrite(entity, [], object, insideString) End Object\n`; return result } -} - +} + class Copy extends IInput { static #serializer = new ObjectSerializer() @@ -3899,187 +3899,187 @@ class Copy extends IInput { const value = this.getSerializedText(); navigator.clipboard.writeText(value); } -} - -/** - * @typedef {import("../element/IElement").default} IElement - * @typedef {import("../input/IInput").default} IInput - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -/** @template {IElement} T */ -class ITemplate { - - /** @type {T} */ - element - - get blueprint() { - return this.element.blueprint - } - - /** @type {IInput[]} */ - #inputObjects = [] - get inputObjects() { - return this.#inputObjects - } - - /** @param {T} element */ - initialize(element) { - this.element = element; - } - - createInputObjects() { - return /** @type {IInput[]} */([]) - } - - /** - * @template {IInput} T - * @param {new () => T} type - */ - getInputObject(type) { - return /** @type {T} */(this.inputObjects.find(object => object.constructor == type)) - } - - setup() { - this.#inputObjects.forEach(v => v.setup()); - } - - cleanup() { - this.#inputObjects.forEach(v => v.cleanup()); - } - - /** @param {PropertyValues} changedProperties */ - willUpdate(changedProperties) { - } - - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - } - - render() { - return y`` - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - } - - /** @param {PropertyValues} changedProperties */ - updated(changedProperties) { - } - - inputSetup() { - this.#inputObjects = this.createInputObjects(); - } -} - -/** @typedef {import("../../Blueprint").default} Blueprint */ - -/** - * @template {HTMLElement} T - * @extends IInput - */ -class IKeyboardShortcut extends IInput { - - /** @type {KeyBindingEntity[]} */ - #activationKeys - - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options.activateAnyKey ??= false; - options.activationKeys ??= []; - options.consumeEvent ??= true; - options.listenOnFocus ??= true; - options.unlistenOnTextEdit ??= true; // No shortcuts when inside of a text field - 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); - - this.#activationKeys = this.options.activationKeys ?? []; - - const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; - const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; - const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; - - let self = this; - /** @param {KeyboardEvent} e */ - this.keyDownHandler = e => { - if ( - this.options.activateAnyKey - || self.#activationKeys.some(keyEntry => - wantsShift(keyEntry) == e.shiftKey - && wantsCtrl(keyEntry) == e.ctrlKey - && wantsAlt(keyEntry) == e.altKey - && Configuration.Keys[keyEntry.Key] == e.code - ) - ) { - if (options.consumeEvent) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - self.fire(); - document.removeEventListener("keydown", self.keyDownHandler); - document.addEventListener("keyup", self.keyUpHandler); - } - }; - - /** @param {KeyboardEvent} e */ - this.keyUpHandler = e => { - if ( - this.options.activateAnyKey - || self.#activationKeys.some(keyEntry => - keyEntry.bShift && e.key == "Shift" - || keyEntry.bCtrl && e.key == "Control" - || keyEntry.bAlt && e.key == "Alt" - || keyEntry.bCmd && e.key == "Meta" - || Configuration.Keys[keyEntry.Key] == e.code - ) - ) { - if (options.consumeEvent) { - e.stopImmediatePropagation(); - } - self.unfire(); - document.removeEventListener("keyup", this.keyUpHandler); - document.addEventListener("keydown", this.keyDownHandler); - } - }; - } - - listenEvents() { - document.addEventListener("keydown", this.keyDownHandler); - } - - unlistenEvents() { - document.removeEventListener("keydown", this.keyDownHandler); - } - - // Subclasses will want to override - - fire() { - } - - unfire() { - } -} - +} + +/** + * @typedef {import("../element/IElement").default} IElement + * @typedef {import("../input/IInput").default} IInput + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +/** @template {IElement} T */ +class ITemplate { + + /** @type {T} */ + element + + get blueprint() { + return this.element.blueprint + } + + /** @type {IInput[]} */ + #inputObjects = [] + get inputObjects() { + return this.#inputObjects + } + + /** @param {T} element */ + initialize(element) { + this.element = element; + } + + createInputObjects() { + return /** @type {IInput[]} */([]) + } + + /** + * @template {IInput} T + * @param {new () => T} type + */ + getInputObject(type) { + return /** @type {T} */(this.inputObjects.find(object => object.constructor == type)) + } + + setup() { + this.#inputObjects.forEach(v => v.setup()); + } + + cleanup() { + this.#inputObjects.forEach(v => v.cleanup()); + } + + /** @param {PropertyValues} changedProperties */ + willUpdate(changedProperties) { + } + + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + } + + render() { + return y`` + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + } + + /** @param {PropertyValues} changedProperties */ + updated(changedProperties) { + } + + inputSetup() { + this.#inputObjects = this.createInputObjects(); + } +} + +/** @typedef {import("../../Blueprint").default} Blueprint */ + +/** + * @template {HTMLElement} T + * @extends IInput + */ +class IKeyboardShortcut extends IInput { + + /** @type {KeyBindingEntity[]} */ + #activationKeys + + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options.activateAnyKey ??= false; + options.activationKeys ??= []; + options.consumeEvent ??= true; + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; // No shortcuts when inside of a text field + 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); + + this.#activationKeys = this.options.activationKeys ?? []; + + const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"; + const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"; + const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"; + + let self = this; + /** @param {KeyboardEvent} e */ + this.keyDownHandler = e => { + if ( + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => + wantsShift(keyEntry) == e.shiftKey + && wantsCtrl(keyEntry) == e.ctrlKey + && wantsAlt(keyEntry) == e.altKey + && Configuration.Keys[keyEntry.Key] == e.code + ) + ) { + if (options.consumeEvent) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + self.fire(); + document.removeEventListener("keydown", self.keyDownHandler); + document.addEventListener("keyup", self.keyUpHandler); + } + }; + + /** @param {KeyboardEvent} e */ + this.keyUpHandler = e => { + if ( + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => + keyEntry.bShift && e.key == "Shift" + || keyEntry.bCtrl && e.key == "Control" + || keyEntry.bAlt && e.key == "Alt" + || keyEntry.bCmd && e.key == "Meta" + || Configuration.Keys[keyEntry.Key] == e.code + ) + ) { + if (options.consumeEvent) { + e.stopImmediatePropagation(); + } + self.unfire(); + document.removeEventListener("keyup", this.keyUpHandler); + document.addEventListener("keydown", this.keyDownHandler); + } + }; + } + + listenEvents() { + document.addEventListener("keydown", this.keyDownHandler); + } + + unlistenEvents() { + document.removeEventListener("keydown", this.keyDownHandler); + } + + // Subclasses will want to override + + fire() { + } + + unfire() { + } +} + class KeyboardCanc extends IKeyboardShortcut { /** @@ -4095,75 +4095,75 @@ class KeyboardCanc extends IKeyboardShortcut { fire() { this.blueprint.removeGraphElement(...this.blueprint.getNodes(true)); } -} - -/** - * @template {HTMLElement} T - * @extends {IInput} - */ -class IPointing extends IInput { - - constructor(target, blueprint, options = {}) { - options.ignoreTranslateCompensate ??= false; - options.ignoreScale ??= false; - options.movementSpace ??= blueprint.getGridDOMElement() ?? document.documentElement; - super(target, blueprint, options); - /** @type {HTMLElement} */ - this.movementSpace = options.movementSpace; - } - - /** @param {MouseEvent} mouseEvent */ - locationFromEvent(mouseEvent) { - const location = Utility.convertLocation( - [mouseEvent.clientX, mouseEvent.clientY], - this.movementSpace, - this.options.ignoreScale - ); - return this.options.ignoreTranslateCompensate - ? location - : this.blueprint.compensateTranslation(location[0], location[1]) - } -} - -class IMouseWheel extends IPointing { - - /** @param {WheelEvent} e */ - #mouseWheelHandler = e => { - e.preventDefault(); - const location = this.locationFromEvent(e); - this.wheel(Math.sign(e.deltaY * Configuration.mouseWheelFactor), location); - } - - /** @param {WheelEvent} e */ - #mouseParentWheelHandler = e => e.preventDefault() - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options.listenOnFocus = true; - options.strictTarget ??= false; - super(target, blueprint, options); - this.strictTarget = options.strictTarget; - } - - listenEvents() { - this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); - } - - unlistenEvents() { - this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); - this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); - } - - /* Subclasses will override the following method */ - wheel(variation, location) { - } -} - +} + +/** + * @template {HTMLElement} T + * @extends {IInput} + */ +class IPointing extends IInput { + + constructor(target, blueprint, options = {}) { + options.ignoreTranslateCompensate ??= false; + options.ignoreScale ??= false; + options.movementSpace ??= blueprint.getGridDOMElement() ?? document.documentElement; + super(target, blueprint, options); + /** @type {HTMLElement} */ + this.movementSpace = options.movementSpace; + } + + /** @param {MouseEvent} mouseEvent */ + locationFromEvent(mouseEvent) { + const location = Utility.convertLocation( + [mouseEvent.clientX, mouseEvent.clientY], + this.movementSpace, + this.options.ignoreScale + ); + return this.options.ignoreTranslateCompensate + ? location + : this.blueprint.compensateTranslation(location[0], location[1]) + } +} + +class IMouseWheel extends IPointing { + + /** @param {WheelEvent} e */ + #mouseWheelHandler = e => { + e.preventDefault(); + const location = this.locationFromEvent(e); + this.wheel(Math.sign(e.deltaY * Configuration.mouseWheelFactor), location); + } + + /** @param {WheelEvent} e */ + #mouseParentWheelHandler = e => e.preventDefault() + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options.listenOnFocus = true; + options.strictTarget ??= false; + super(target, blueprint, options); + this.strictTarget = options.strictTarget; + } + + listenEvents() { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); + } + + unlistenEvents() { + this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); + } + + /* Subclasses will override the following method */ + wheel(variation, location) { + } +} + class Zoom extends IMouseWheel { #enableZoonIn = false @@ -4186,33 +4186,33 @@ class Zoom extends IMouseWheel { zoomLevel += variation; this.blueprint.setZoom(zoomLevel, location); } -} - -class KeyboardEnableZoom extends IKeyboardShortcut { - - /** @type {Zoom} */ - #zoomInputObject - - /** - * @param {HTMLElement} target - * @param {import("../../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options.activationKeys = Configuration.enableZoomIn; - super(target, blueprint, options); - } - - fire() { - this.#zoomInputObject = this.blueprint.getInputObject(Zoom); - this.#zoomInputObject.enableZoonIn = true; - } - - unfire() { - this.#zoomInputObject.enableZoonIn = false; - } -} - +} + +class KeyboardEnableZoom extends IKeyboardShortcut { + + /** @type {Zoom} */ + #zoomInputObject + + /** + * @param {HTMLElement} target + * @param {import("../../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options.activationKeys = Configuration.enableZoomIn; + super(target, blueprint, options); + } + + fire() { + this.#zoomInputObject = this.blueprint.getInputObject(Zoom); + this.#zoomInputObject.enableZoonIn = true; + } + + unfire() { + this.#zoomInputObject.enableZoonIn = false; + } +} + /** @typedef {import("../../Blueprint").default} Blueprint */ class KeyboardSelectAll extends IKeyboardShortcut { @@ -4229,489 +4229,489 @@ class KeyboardSelectAll extends IKeyboardShortcut { fire() { this.blueprint.selectAll(); } -} - -/** - * @typedef {import("../Blueprint").default} Blueprint - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../input/IInput").default} IInput - * @typedef {import("../template/ITemplate").default} ITemplate - * @typedef {import("lit").PropertyDeclarations} PropertyDeclarations - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -/** - * @template {IEntity} T - * @template {ITemplate} U - */ -class IElement extends s { - - #nextUpdatedCallbacks = [] - - /** @type {Blueprint} */ - #blueprint - get blueprint() { - return this.#blueprint - } - set blueprint(v) { - this.#blueprint = v; - } - - /** @type {T} */ - #entity - get entity() { - return this.#entity - } - set entity(entity) { - this.#entity = entity; - } - - /** @type {U} */ - #template - get template() { - return this.#template - } - - isInitialized = false - isSetup = false - - /** @type {IInput[]} */ - inputObjects = [] - - /** - * @param {T} entity - * @param {U} template - */ - initialize(entity, template) { - this.requestUpdate(); - this.#entity = entity; - this.#template = template; - this.#template.initialize(this); - if (this.isConnected) { - this.updateComplete.then(() => this.setup()); - } - this.isInitialized = true; - } - - connectedCallback() { - super.connectedCallback(); - this.blueprint = /** @type {Blueprint} */(this.closest("ueb-blueprint")); - if (this.isInitialized) { - this.requestUpdate(); - this.updateComplete.then(() => this.setup()); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - if (this.isSetup) { - this.updateComplete.then(() => this.cleanup()); - } - this.acknowledgeDelete(); - } - - createRenderRoot() { - return this - } - - /** @param {PropertyValues} changedProperties */ - shouldUpdate(changedProperties) { - return this.isInitialized && this.isConnected - } - - setup() { - this.template.setup(); - this.isSetup = true; - } - - cleanup() { - this.template.cleanup(); - this.isSetup = false; - } - - /** @param {PropertyValues} changedProperties */ - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - this.template.willUpdate(changedProperties); - } - - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - this.template.update(changedProperties); - } - - render() { - return this.template.render() - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.template.firstUpdated(changedProperties); - this.template.inputSetup(); - } - - /** @param {PropertyValues} changedProperties */ - updated(changedProperties) { - super.updated(changedProperties); - this.template.updated(changedProperties); - // Remember the array might change while iterating - for (const f of this.#nextUpdatedCallbacks) { - f(changedProperties); - } - this.#nextUpdatedCallbacks = []; - } - - addNextUpdatedCallbacks(callback, requestUpdate = false) { - this.#nextUpdatedCallbacks.push(callback); - if (requestUpdate) { - this.requestUpdate(); - } - } - - acknowledgeDelete() { - let deleteEvent = new CustomEvent(Configuration.removeEventName); - this.dispatchEvent(deleteEvent); - } - - /** @param {IElement} element */ - isSameGraph(element) { - return this.blueprint && this.blueprint == element?.blueprint - } - - /** - * @template {IInput} V - * @param {new (...args: any[]) => V} type - */ - getInputObject(type) { - return /** @type {V} */(this.template.inputObjects.find(object => object.constructor == type)) - } -} - -/** - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../template/IDraggableTemplate").default} IDraggableTemplate - * @typedef {CustomEvent<{ - * value: [Number, Number] - * }>} DragEvent - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -/** - * @template {IEntity} T - * @template {IDraggableTemplate} U - * @extends {IElement} - */ -class IDraggableElement extends IElement { - - static properties = { - ...super.properties, - locationX: { - type: Number, - attribute: false, - }, - locationY: { - type: Number, - attribute: false, - }, - sizeX: { - type: Number, - attribute: false, - }, - sizeY: { - type: Number, - attribute: false, - }, - } - static dragEventName = Configuration.dragEventName - static dragGeneralEventName = Configuration.dragGeneralEventName - - constructor() { - super(); - this.locationX = 0; - this.locationY = 0; - this.sizeX = 0; - this.sizeY = 0; - } - - computeSizes() { - const bounding = this.getBoundingClientRect(); - this.sizeX = this.blueprint.scaleCorrect(bounding.width); - this.sizeY = this.blueprint.scaleCorrect(bounding.height); - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.computeSizes(); - } - - /** - * @param {Number} x - * @param {Number} y - */ - setLocation(x, y, acknowledge = true) { - const dx = x - this.locationX; - const dy = y - this.locationY; - this.locationX = x; - this.locationY = y; - if (this.blueprint && acknowledge) { - const dragLocalEvent = new CustomEvent( - /** @type {typeof IDraggableElement} */(this.constructor).dragEventName, - { - detail: { - value: [dx, dy], - }, - bubbles: false, - cancelable: true, - } - ); - this.dispatchEvent(dragLocalEvent); - } - } - - /** - * @param {Number} x - * @param {Number} y - */ - addLocation(x, y, acknowledge = true) { - this.setLocation(this.locationX + x, this.locationY + y, acknowledge); - } - - /** @param {Number[]} value */ - acknowledgeDrag(value) { - const dragEvent = new CustomEvent( - /** @type {typeof IDraggableElement} */(this.constructor).dragGeneralEventName, - { - detail: { - value: value - }, - bubbles: true, - cancelable: true - } - ); - this.dispatchEvent(dragEvent); - } - - snapToGrid() { - const snappedLocation = Utility.snapToGrid(this.locationX, this.locationY, Configuration.gridSize); - if (this.locationX != snappedLocation[0] || this.locationY != snappedLocation[1]) { - this.setLocation(snappedLocation[0], snappedLocation[1]); - } - } - - topBoundary(justSelectableArea = false) { - return this.template.topBoundary(justSelectableArea) - } - - rightBoundary(justSelectableArea = false) { - return this.template.rightBoundary(justSelectableArea) - } - - bottomBoundary(justSelectableArea = false) { - return this.template.bottomBoundary(justSelectableArea) - } - - leftBoundary(justSelectableArea = false) { - return this.template.leftBoundary(justSelectableArea) - } -} - -/** - * @typedef {import("../../Blueprint").default} Blueprint - * @typedef {import("../../element/IElement").default} IElement - */ - -/** - * @template {IElement} T - * @extends {IPointing} - */ -class IMouseClickDrag extends IPointing { - - /** @param {MouseEvent} e */ - #mouseDownHandler = e => { - this.blueprint.setFocused(true); - switch (e.button) { - case this.options.clickButton: - // Either doesn't matter or consider the click only when clicking on the parent, not descandants - if (!this.options.strictTarget || e.target == e.currentTarget) { - if (this.options.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Attach the listeners - this.#movementListenedElement.addEventListener("mousemove", this.#mouseStartedMovingHandler); - document.addEventListener("mouseup", this.#mouseUpHandler); - this.clickedPosition = this.locationFromEvent(e); - this.blueprint.mousePosition[0] = this.clickedPosition[0]; - this.blueprint.mousePosition[1] = this.clickedPosition[1]; - if (this.target instanceof IDraggableElement) { - this.clickedOffset = [ - this.clickedPosition[0] - this.target.locationX, - this.clickedPosition[1] - this.target.locationY, - ]; - } - this.clicked(this.clickedPosition); - } - break - default: - if (!this.options.exitAnyButton) { - this.#mouseUpHandler(e); - } - break - } - } - - /** @param {MouseEvent} e */ - #mouseStartedMovingHandler = e => { - if (this.options.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Delegate from now on to this.#mouseMoveHandler - this.#movementListenedElement.removeEventListener("mousemove", this.#mouseStartedMovingHandler); - this.#movementListenedElement.addEventListener("mousemove", this.#mouseMoveHandler); - // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false - const dragEvent = this.getEvent(Configuration.trackingMouseEventName.begin); - this.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; - const location = this.locationFromEvent(e); - // Do actual actions - this.lastLocation = Utility.snapToGrid(this.clickedPosition[0], this.clickedPosition[1], this.stepSize); - this.startDrag(location); - this.started = true; - } - - /** @param {MouseEvent} e */ - #mouseMoveHandler = e => { - if (this.options.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - const location = this.locationFromEvent(e); - const movement = [e.movementX, e.movementY]; - this.dragTo(location, movement); - if (this.#trackingMouse) { - this.blueprint.mousePosition = location; - } - if (this.options.scrollGraphEdge) { - const movementNorm = Math.sqrt(movement[0] * movement[0] + movement[1] * movement[1]); - const threshold = this.blueprint.scaleCorrect(Configuration.edgeScrollThreshold); - const leftThreshold = this.blueprint.template.gridLeftVisibilityBoundary() + threshold; - const rightThreshold = this.blueprint.template.gridRightVisibilityBoundary() - threshold; - let scrollX = 0; - if (location[0] < leftThreshold) { - scrollX = location[0] - leftThreshold; - } else if (location[0] > rightThreshold) { - scrollX = location[0] - rightThreshold; - } - const topThreshold = this.blueprint.template.gridTopVisibilityBoundary() + threshold; - const bottomThreshold = this.blueprint.template.gridBottomVisibilityBoundary() - threshold; - let scrollY = 0; - if (location[1] < topThreshold) { - scrollY = location[1] - topThreshold; - } else if (location[1] > bottomThreshold) { - scrollY = location[1] - bottomThreshold; - } - scrollX = Utility.clamp(this.blueprint.scaleCorrectReverse(scrollX) ** 3 * movementNorm * 0.6, -20, 20); - scrollY = Utility.clamp(this.blueprint.scaleCorrectReverse(scrollY) ** 3 * movementNorm * 0.6, -20, 20); - this.blueprint.scrollDelta(scrollX, scrollY); - } - } - - /** @param {MouseEvent} e */ - #mouseUpHandler = e => { - if (!this.options.exitAnyButton || e.button == this.options.clickButton) { - if (this.options.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Remove the handlers of "mousemove" and "mouseup" - this.#movementListenedElement.removeEventListener("mousemove", this.#mouseStartedMovingHandler); - this.#movementListenedElement.removeEventListener("mousemove", this.#mouseMoveHandler); - document.removeEventListener("mouseup", this.#mouseUpHandler); - if (this.started) { - this.endDrag(); - } - this.unclicked(); - if (this.#trackingMouse) { - const dragEvent = this.getEvent(Configuration.trackingMouseEventName.end); - this.target.dispatchEvent(dragEvent); - this.#trackingMouse = false; - } - this.started = false; - } - } - - #trackingMouse = false - #movementListenedElement - #draggableElement - - clickedOffset = [0, 0] - clickedPosition = [0, 0] - lastLocation = [0, 0] - started = false - stepSize = 1 - - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options = {}) { - options.clickButton ??= 0; - options.consumeEvent ??= true; - options.draggableElement ??= target; - options.exitAnyButton ??= true; - options.moveEverywhere ??= false; - options.movementSpace ??= blueprint?.getGridDOMElement(); - options.repositionOnClick ??= false; - options.scrollGraphEdge ??= false; - options.strictTarget ??= false; - super(target, blueprint, options); - this.stepSize = parseInt(options?.stepSize ?? Configuration.gridSize); - this.#movementListenedElement = this.options.moveEverywhere ? document.documentElement : this.movementSpace; - this.#draggableElement = /** @type {HTMLElement} */(this.options.draggableElement); - - this.listenEvents(); - } - - listenEvents() { - super.listenEvents(); - this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler); - if (this.options.clickButton == 2) { - this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()); - } - } - - unlistenEvents() { - super.unlistenEvents(); - this.#draggableElement.removeEventListener("mousedown", this.#mouseDownHandler); - } - - getEvent(eventName) { - return new CustomEvent(eventName, { - detail: { - tracker: this - }, - bubbles: true, - cancelable: true - }) - } - - /* Subclasses will override the following methods */ - clicked(location) { - } - - startDrag(location) { - } - - dragTo(location, offset) { - } - - endDrag() { - } - - unclicked(location) { - } -} - +} + +/** + * @typedef {import("../Blueprint").default} Blueprint + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../input/IInput").default} IInput + * @typedef {import("../template/ITemplate").default} ITemplate + * @typedef {import("lit").PropertyDeclarations} PropertyDeclarations + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +/** + * @template {IEntity} T + * @template {ITemplate} U + */ +class IElement extends s { + + #nextUpdatedCallbacks = [] + + /** @type {Blueprint} */ + #blueprint + get blueprint() { + return this.#blueprint + } + set blueprint(v) { + this.#blueprint = v; + } + + /** @type {T} */ + #entity + get entity() { + return this.#entity + } + set entity(entity) { + this.#entity = entity; + } + + /** @type {U} */ + #template + get template() { + return this.#template + } + + isInitialized = false + isSetup = false + + /** @type {IInput[]} */ + inputObjects = [] + + /** + * @param {T} entity + * @param {U} template + */ + initialize(entity, template) { + this.requestUpdate(); + this.#entity = entity; + this.#template = template; + this.#template.initialize(this); + if (this.isConnected) { + this.updateComplete.then(() => this.setup()); + } + this.isInitialized = true; + } + + connectedCallback() { + super.connectedCallback(); + this.blueprint = /** @type {Blueprint} */(this.closest("ueb-blueprint")); + if (this.isInitialized) { + this.requestUpdate(); + this.updateComplete.then(() => this.setup()); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (this.isSetup) { + this.updateComplete.then(() => this.cleanup()); + } + this.acknowledgeDelete(); + } + + createRenderRoot() { + return this + } + + /** @param {PropertyValues} changedProperties */ + shouldUpdate(changedProperties) { + return this.isInitialized && this.isConnected + } + + setup() { + this.template.setup(); + this.isSetup = true; + } + + cleanup() { + this.template.cleanup(); + this.isSetup = false; + } + + /** @param {PropertyValues} changedProperties */ + willUpdate(changedProperties) { + super.willUpdate(changedProperties); + this.template.willUpdate(changedProperties); + } + + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + super.update(changedProperties); + this.template.update(changedProperties); + } + + render() { + return this.template.render() + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.template.firstUpdated(changedProperties); + this.template.inputSetup(); + } + + /** @param {PropertyValues} changedProperties */ + updated(changedProperties) { + super.updated(changedProperties); + this.template.updated(changedProperties); + // Remember the array might change while iterating + for (const f of this.#nextUpdatedCallbacks) { + f(changedProperties); + } + this.#nextUpdatedCallbacks = []; + } + + addNextUpdatedCallbacks(callback, requestUpdate = false) { + this.#nextUpdatedCallbacks.push(callback); + if (requestUpdate) { + this.requestUpdate(); + } + } + + acknowledgeDelete() { + let deleteEvent = new CustomEvent(Configuration.removeEventName); + this.dispatchEvent(deleteEvent); + } + + /** @param {IElement} element */ + isSameGraph(element) { + return this.blueprint && this.blueprint == element?.blueprint + } + + /** + * @template {IInput} V + * @param {new (...args: any[]) => V} type + */ + getInputObject(type) { + return /** @type {V} */(this.template.inputObjects.find(object => object.constructor == type)) + } +} + +/** + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../template/IDraggableTemplate").default} IDraggableTemplate + * @typedef {CustomEvent<{ + * value: [Number, Number] + * }>} DragEvent + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +/** + * @template {IEntity} T + * @template {IDraggableTemplate} U + * @extends {IElement} + */ +class IDraggableElement extends IElement { + + static properties = { + ...super.properties, + locationX: { + type: Number, + attribute: false, + }, + locationY: { + type: Number, + attribute: false, + }, + sizeX: { + type: Number, + attribute: false, + }, + sizeY: { + type: Number, + attribute: false, + }, + } + static dragEventName = Configuration.dragEventName + static dragGeneralEventName = Configuration.dragGeneralEventName + + constructor() { + super(); + this.locationX = 0; + this.locationY = 0; + this.sizeX = 0; + this.sizeY = 0; + } + + computeSizes() { + const bounding = this.getBoundingClientRect(); + this.sizeX = this.blueprint.scaleCorrect(bounding.width); + this.sizeY = this.blueprint.scaleCorrect(bounding.height); + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.computeSizes(); + } + + /** + * @param {Number} x + * @param {Number} y + */ + setLocation(x, y, acknowledge = true) { + const dx = x - this.locationX; + const dy = y - this.locationY; + this.locationX = x; + this.locationY = y; + if (this.blueprint && acknowledge) { + const dragLocalEvent = new CustomEvent( + /** @type {typeof IDraggableElement} */(this.constructor).dragEventName, + { + detail: { + value: [dx, dy], + }, + bubbles: false, + cancelable: true, + } + ); + this.dispatchEvent(dragLocalEvent); + } + } + + /** + * @param {Number} x + * @param {Number} y + */ + addLocation(x, y, acknowledge = true) { + this.setLocation(this.locationX + x, this.locationY + y, acknowledge); + } + + /** @param {Number[]} value */ + acknowledgeDrag(value) { + const dragEvent = new CustomEvent( + /** @type {typeof IDraggableElement} */(this.constructor).dragGeneralEventName, + { + detail: { + value: value + }, + bubbles: true, + cancelable: true + } + ); + this.dispatchEvent(dragEvent); + } + + snapToGrid() { + const snappedLocation = Utility.snapToGrid(this.locationX, this.locationY, Configuration.gridSize); + if (this.locationX != snappedLocation[0] || this.locationY != snappedLocation[1]) { + this.setLocation(snappedLocation[0], snappedLocation[1]); + } + } + + topBoundary(justSelectableArea = false) { + return this.template.topBoundary(justSelectableArea) + } + + rightBoundary(justSelectableArea = false) { + return this.template.rightBoundary(justSelectableArea) + } + + bottomBoundary(justSelectableArea = false) { + return this.template.bottomBoundary(justSelectableArea) + } + + leftBoundary(justSelectableArea = false) { + return this.template.leftBoundary(justSelectableArea) + } +} + +/** + * @typedef {import("../../Blueprint").default} Blueprint + * @typedef {import("../../element/IElement").default} IElement + */ + +/** + * @template {IElement} T + * @extends {IPointing} + */ +class IMouseClickDrag extends IPointing { + + /** @param {MouseEvent} e */ + #mouseDownHandler = e => { + this.blueprint.setFocused(true); + switch (e.button) { + case this.options.clickButton: + // Either doesn't matter or consider the click only when clicking on the parent, not descandants + if (!this.options.strictTarget || e.target == e.currentTarget) { + if (this.options.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Attach the listeners + this.#movementListenedElement.addEventListener("mousemove", this.#mouseStartedMovingHandler); + document.addEventListener("mouseup", this.#mouseUpHandler); + this.clickedPosition = this.locationFromEvent(e); + this.blueprint.mousePosition[0] = this.clickedPosition[0]; + this.blueprint.mousePosition[1] = this.clickedPosition[1]; + if (this.target instanceof IDraggableElement) { + this.clickedOffset = [ + this.clickedPosition[0] - this.target.locationX, + this.clickedPosition[1] - this.target.locationY, + ]; + } + this.clicked(this.clickedPosition); + } + break + default: + if (!this.options.exitAnyButton) { + this.#mouseUpHandler(e); + } + break + } + } + + /** @param {MouseEvent} e */ + #mouseStartedMovingHandler = e => { + if (this.options.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Delegate from now on to this.#mouseMoveHandler + this.#movementListenedElement.removeEventListener("mousemove", this.#mouseStartedMovingHandler); + this.#movementListenedElement.addEventListener("mousemove", this.#mouseMoveHandler); + // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false + const dragEvent = this.getEvent(Configuration.trackingMouseEventName.begin); + this.#trackingMouse = this.target.dispatchEvent(dragEvent) == false; + const location = this.locationFromEvent(e); + // Do actual actions + this.lastLocation = Utility.snapToGrid(this.clickedPosition[0], this.clickedPosition[1], this.stepSize); + this.startDrag(location); + this.started = true; + } + + /** @param {MouseEvent} e */ + #mouseMoveHandler = e => { + if (this.options.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + const location = this.locationFromEvent(e); + const movement = [e.movementX, e.movementY]; + this.dragTo(location, movement); + if (this.#trackingMouse) { + this.blueprint.mousePosition = location; + } + if (this.options.scrollGraphEdge) { + const movementNorm = Math.sqrt(movement[0] * movement[0] + movement[1] * movement[1]); + const threshold = this.blueprint.scaleCorrect(Configuration.edgeScrollThreshold); + const leftThreshold = this.blueprint.template.gridLeftVisibilityBoundary() + threshold; + const rightThreshold = this.blueprint.template.gridRightVisibilityBoundary() - threshold; + let scrollX = 0; + if (location[0] < leftThreshold) { + scrollX = location[0] - leftThreshold; + } else if (location[0] > rightThreshold) { + scrollX = location[0] - rightThreshold; + } + const topThreshold = this.blueprint.template.gridTopVisibilityBoundary() + threshold; + const bottomThreshold = this.blueprint.template.gridBottomVisibilityBoundary() - threshold; + let scrollY = 0; + if (location[1] < topThreshold) { + scrollY = location[1] - topThreshold; + } else if (location[1] > bottomThreshold) { + scrollY = location[1] - bottomThreshold; + } + scrollX = Utility.clamp(this.blueprint.scaleCorrectReverse(scrollX) ** 3 * movementNorm * 0.6, -20, 20); + scrollY = Utility.clamp(this.blueprint.scaleCorrectReverse(scrollY) ** 3 * movementNorm * 0.6, -20, 20); + this.blueprint.scrollDelta(scrollX, scrollY); + } + } + + /** @param {MouseEvent} e */ + #mouseUpHandler = e => { + if (!this.options.exitAnyButton || e.button == this.options.clickButton) { + if (this.options.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Remove the handlers of "mousemove" and "mouseup" + this.#movementListenedElement.removeEventListener("mousemove", this.#mouseStartedMovingHandler); + this.#movementListenedElement.removeEventListener("mousemove", this.#mouseMoveHandler); + document.removeEventListener("mouseup", this.#mouseUpHandler); + if (this.started) { + this.endDrag(); + } + this.unclicked(); + if (this.#trackingMouse) { + const dragEvent = this.getEvent(Configuration.trackingMouseEventName.end); + this.target.dispatchEvent(dragEvent); + this.#trackingMouse = false; + } + this.started = false; + } + } + + #trackingMouse = false + #movementListenedElement + #draggableElement + + clickedOffset = [0, 0] + clickedPosition = [0, 0] + lastLocation = [0, 0] + started = false + stepSize = 1 + + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options = {}) { + options.clickButton ??= 0; + options.consumeEvent ??= true; + options.draggableElement ??= target; + options.exitAnyButton ??= true; + options.moveEverywhere ??= false; + options.movementSpace ??= blueprint?.getGridDOMElement(); + options.repositionOnClick ??= false; + options.scrollGraphEdge ??= false; + options.strictTarget ??= false; + super(target, blueprint, options); + this.stepSize = parseInt(options?.stepSize ?? Configuration.gridSize); + this.#movementListenedElement = this.options.moveEverywhere ? document.documentElement : this.movementSpace; + this.#draggableElement = /** @type {HTMLElement} */(this.options.draggableElement); + + this.listenEvents(); + } + + listenEvents() { + super.listenEvents(); + this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler); + if (this.options.clickButton == 2) { + this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()); + } + } + + unlistenEvents() { + super.unlistenEvents(); + this.#draggableElement.removeEventListener("mousedown", this.#mouseDownHandler); + } + + getEvent(eventName) { + return new CustomEvent(eventName, { + detail: { + tracker: this + }, + bubbles: true, + cancelable: true + }) + } + + /* Subclasses will override the following methods */ + clicked(location) { + } + + startDrag(location) { + } + + dragTo(location, offset) { + } + + endDrag() { + } + + unclicked(location) { + } +} + class MouseScrollGraph extends IMouseClickDrag { startDrag() { @@ -4725,8 +4725,8 @@ class MouseScrollGraph extends IMouseClickDrag { endDrag() { this.blueprint.scrolling = false; } -} - +} + class MouseTracking extends IPointing { /** @type {IPointing} */ @@ -4789,32 +4789,32 @@ class MouseTracking extends IPointing { /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler) ); } -} - -/** - * @typedef {import("./IElement").default} IElement - * @typedef {new (...args) => IElement} ElementConstructor - */ - -class ElementFactory { - - /** @type {Map} */ - static #elementConstructors = new Map() - - /** - * @param {String} tagName - * @param {ElementConstructor} entityConstructor - */ - static registerElement(tagName, entityConstructor) { - ElementFactory.#elementConstructors.set(tagName, entityConstructor); - } - - /** @param {String} tagName */ - static getConstructor(tagName) { - return ElementFactory.#elementConstructors.get(tagName) - } -} - +} + +/** + * @typedef {import("./IElement").default} IElement + * @typedef {new (...args) => IElement} ElementConstructor + */ + +class ElementFactory { + + /** @type {Map} */ + static #elementConstructors = new Map() + + /** + * @param {String} tagName + * @param {ElementConstructor} entityConstructor + */ + static registerElement(tagName, entityConstructor) { + ElementFactory.#elementConstructors.set(tagName, entityConstructor); + } + + /** @param {String} tagName */ + static getConstructor(tagName) { + return ElementFactory.#elementConstructors.get(tagName) + } +} + /** @typedef {import("../../element/NodeElement").NodeElementConstructor} NodeElementConstructor */ class Paste extends IInput { @@ -4867,8 +4867,8 @@ class Paste extends IInput { this.blueprint.addGraphElement(...nodes); return true } -} - +} + class Select extends IMouseClickDrag { constructor(target, blueprint, options = {}) { @@ -4896,8 +4896,8 @@ class Select extends IMouseClickDrag { this.blueprint.unselectAll(); } } -} - +} + class Unfocus extends IInput { /** @param {MouseEvent} e */ @@ -4927,8 +4927,8 @@ class Unfocus extends IInput { unlistenEvents() { document.removeEventListener("click", this.#clickHandler); } -} - +} + /** * @typedef {import("../Blueprint").default} Blueprint * @typedef {import("../element/PinElement").default} PinElement @@ -5155,75 +5155,75 @@ class BlueprintTemplate extends ITemplate { avgY = nodes.length > 0 ? Math.round(avgY / (2 * nodes.length)) : 0; this.centerViewport(avgX, avgY, smooth); } -} - -/** - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../template/ITemplate").default} ITemplate - */ - -/** - * @template {IEntity} T - * @template {ITemplate} U - * @extends {IElement} - */ -class IFromToPositionedElement extends IElement { - - static properties = { - ...super.properties, - fromX: { - type: Number, - attribute: false, - }, - fromY: { - type: Number, - attribute: false, - }, - toX: { - type: Number, - attribute: false, - }, - toY: { - type: Number, - attribute: false, - }, - } - - constructor() { - super(); - this.fromX = 0; - this.fromY = 0; - this.toX = 0; - this.toY = 0; - } - - /** @param {Number[]} param0 */ - setBothLocations([x, y]) { - this.fromX = x; - this.fromY = y; - this.toX = x; - this.toY = y; - } - - /** - * @param {Number} x - * @param {Number} y - */ - addSourceLocation(x, y) { - this.fromX += x; - this.fromY += y; - } - - /** - * @param {Number} x - * @param {Number} y - */ - addDestinationLocation(x, y) { - this.toX += x; - this.toY += y; - } -} - +} + +/** + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../template/ITemplate").default} ITemplate + */ + +/** + * @template {IEntity} T + * @template {ITemplate} U + * @extends {IElement} + */ +class IFromToPositionedElement extends IElement { + + static properties = { + ...super.properties, + fromX: { + type: Number, + attribute: false, + }, + fromY: { + type: Number, + attribute: false, + }, + toX: { + type: Number, + attribute: false, + }, + toY: { + type: Number, + attribute: false, + }, + } + + constructor() { + super(); + this.fromX = 0; + this.fromY = 0; + this.toX = 0; + this.toY = 0; + } + + /** @param {Number[]} param0 */ + setBothLocations([x, y]) { + this.fromX = x; + this.fromY = y; + this.toX = x; + this.toY = y; + } + + /** + * @param {Number} x + * @param {Number} y + */ + addSourceLocation(x, y) { + this.fromX += x; + this.fromY += y; + } + + /** + * @param {Number} x + * @param {Number} y + */ + addDestinationLocation(x, y) { + this.toX += x; + this.toY += y; + } +} + /** * @typedef {import("../element/IFromToPositionedElement").default} IFromToPositionedElement * @typedef {import("lit").PropertyValues} PropertyValues @@ -5259,95 +5259,95 @@ class IFromToPositionedTemplate extends ITemplate { this.element.style.height = `${height}px`; } } -} - -class KnotEntity extends ObjectEntity { - - /** - * @param {Object} options - * @param {PinEntity} pinReferenceForType - */ - constructor(options = {}, pinReferenceForType = undefined) { - super(options, true); - this.Class = new ObjectReferenceEntity("/Script/BlueprintGraph.K2Node_Knot"); - this.Name = "K2Node_Knot"; - const inputPinEntity = new PinEntity( - { - PinName: "InputPin", - }, - true - ); - const outputPinEntity = new PinEntity( - { - PinName: "OutputPin", - Direction: "EGPD_Output", - }, - true - ); - if (pinReferenceForType) { - inputPinEntity.copyTypeFrom(pinReferenceForType); - outputPinEntity.copyTypeFrom(pinReferenceForType); - } - this.CustomProperties = [inputPinEntity, outputPinEntity]; - } -} - -/** @typedef {import("../../Blueprint").default} Blueprint */ - -/** - * @template {HTMLElement} T - * @extends {IPointing} - */ -class MouseDbClick extends IPointing { - - /** @param {Number[]} location */ - static ignoreDbClick = location => { } - - /** @param {MouseEvent} e */ - #mouseDbClickHandler = e => { - if (!this.options.strictTarget || e.target === e.currentTarget) { - if (this.options.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - this.clickedPosition = this.locationFromEvent(e); - this.blueprint.mousePosition[0] = this.clickedPosition[0]; - this.blueprint.mousePosition[1] = this.clickedPosition[1]; - this.dbclicked(this.clickedPosition); - } - } - - #onDbClick - get onDbClick() { - return this.#onDbClick - } - set onDbClick(value) { - this.#onDbClick = value; - } - - clickedPosition = [0, 0] - - constructor(target, blueprint, options = {}, onDbClick = MouseDbClick.ignoreDbClick) { - options.consumeEvent ??= true; - options.strictTarget ??= false; - super(target, blueprint, options); - this.#onDbClick = onDbClick; - this.listenEvents(); - } - - listenEvents() { - this.target.addEventListener("dblclick", this.#mouseDbClickHandler); - } - - unlistenEvents() { - this.target.removeEventListener("dblclick", this.#mouseDbClickHandler); - } - - /* Subclasses will override the following method */ - dbclicked(location) { - this.onDbClick(location); - } -} - +} + +class KnotEntity extends ObjectEntity { + + /** + * @param {Object} options + * @param {PinEntity} pinReferenceForType + */ + constructor(options = {}, pinReferenceForType = undefined) { + super(options, true); + this.Class = new ObjectReferenceEntity("/Script/BlueprintGraph.K2Node_Knot"); + this.Name = "K2Node_Knot"; + const inputPinEntity = new PinEntity( + { + PinName: "InputPin", + }, + true + ); + const outputPinEntity = new PinEntity( + { + PinName: "OutputPin", + Direction: "EGPD_Output", + }, + true + ); + if (pinReferenceForType) { + inputPinEntity.copyTypeFrom(pinReferenceForType); + outputPinEntity.copyTypeFrom(pinReferenceForType); + } + this.CustomProperties = [inputPinEntity, outputPinEntity]; + } +} + +/** @typedef {import("../../Blueprint").default} Blueprint */ + +/** + * @template {HTMLElement} T + * @extends {IPointing} + */ +class MouseDbClick extends IPointing { + + /** @param {Number[]} location */ + static ignoreDbClick = location => { } + + /** @param {MouseEvent} e */ + #mouseDbClickHandler = e => { + if (!this.options.strictTarget || e.target === e.currentTarget) { + if (this.options.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + this.clickedPosition = this.locationFromEvent(e); + this.blueprint.mousePosition[0] = this.clickedPosition[0]; + this.blueprint.mousePosition[1] = this.clickedPosition[1]; + this.dbclicked(this.clickedPosition); + } + } + + #onDbClick + get onDbClick() { + return this.#onDbClick + } + set onDbClick(value) { + this.#onDbClick = value; + } + + clickedPosition = [0, 0] + + constructor(target, blueprint, options = {}, onDbClick = MouseDbClick.ignoreDbClick) { + options.consumeEvent ??= true; + options.strictTarget ??= false; + super(target, blueprint, options); + this.#onDbClick = onDbClick; + this.listenEvents(); + } + + listenEvents() { + this.target.addEventListener("dblclick", this.#mouseDbClickHandler); + } + + unlistenEvents() { + this.target.removeEventListener("dblclick", this.#mouseDbClickHandler); + } + + /* Subclasses will override the following method */ + dbclicked(location) { + this.onDbClick(location); + } +} + /** * @typedef {import("../element/LinkElement").default} LinkElement * @typedef {import("../element/LinkElement").LinkElementConstructor} LinkElementConstructor @@ -5532,313 +5532,313 @@ class LinkTemplate extends IFromToPositionedTemplate { ` : b} ` } -} - -/** - * @typedef {import("../element/IDraggableElement").DragEvent} DragEvent - * @typedef {import("./PinElement").default} PinElement - * @typedef {import("lit").TemplateResult<1>} TemplateResult - * @typedef {typeof LinkElement} LinkElementConstructor - */ - -/** @extends {IFromToPositionedElement} */ -class LinkElement extends IFromToPositionedElement { - - static properties = { - ...super.properties, - source: { - type: String, - reflect: true, - }, - destination: { - type: String, - reflect: true, - }, - dragging: { - type: Boolean, - attribute: "data-dragging", - converter: Utility.booleanConverter, - reflect: true, - }, - originatesFromInput: { - type: Boolean, - attribute: false, - }, - svgPathD: { - type: String, - attribute: false, - }, - linkMessageIcon: { - type: String, - attribute: false, - }, - linkMessageText: { - type: String, - attribute: false, - }, - } - - /** @type {PinElement} */ - #sourcePin - get sourcePin() { - return this.#sourcePin - } - set sourcePin(pin) { - this.#setPin(pin, false); - } - - /** @type {PinElement} */ - #destinationPin - get destinationPin() { - return this.#destinationPin - } - set destinationPin(pin) { - this.#setPin(pin, true); - } - - #nodeDeleteHandler = () => this.remove() - /** @param {DragEvent} e */ - #nodeDragSourceHandler = e => this.addSourceLocation(...e.detail.value) - /** @param {DragEvent} e */ - #nodeDragDestinatonHandler = e => this.addDestinationLocation(...e.detail.value) - #nodeReflowSourceHandler = e => this.setSourceLocation() - #nodeReflowDestinatonHandler = e => this.setDestinationLocation() - - /** @type {TemplateResult | nothing} */ - linkMessageIcon = b - /** @type {TemplateResult | nothing} */ - linkMessageText = b - - /** @type {SVGPathElement} */ - pathElement - - constructor() { - super(); - this.source = null; - this.destination = null; - this.dragging = false; - this.originatesFromInput = false; - this.startPercentage = 0; - this.svgPathD = ""; - this.startPixels = 0; - } - - /** - * @param {PinElement} source - * @param {PinElement?} destination - */ - static newObject(source, destination) { - const result = new LinkElement(); - result.initialize(source, destination); - return result - } - - /** - * @param {PinElement} source - * @param {PinElement?} destination - */ - initialize(source, destination) { - super.initialize({}, new LinkTemplate()); - if (source) { - this.sourcePin = source; - if (!destination) { - this.toX = this.fromX; - this.toY = this.fromY; - } - } - if (destination) { - this.destinationPin = destination; - if (!source) { - this.fromX = this.toX; - this.fromY = this.toY; - } - } - } - - /** - * @param {PinElement} pin - * @param {Boolean} isDestinationPin - */ - #setPin(pin, isDestinationPin) { - const getCurrentPin = () => isDestinationPin ? this.destinationPin : this.sourcePin; - if (getCurrentPin() == pin) { - return - } - if (getCurrentPin()) { - const nodeElement = getCurrentPin().getNodeElement(); - nodeElement.removeEventListener(Configuration.removeEventName, this.#nodeDeleteHandler); - nodeElement.removeEventListener( - Configuration.nodeDragEventName, - isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler - ); - nodeElement.removeEventListener( - Configuration.nodeReflowEventName, - isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler - ); - this.#unlinkPins(); - } - isDestinationPin - ? this.#destinationPin = pin - : this.#sourcePin = pin; - if (getCurrentPin()) { - const nodeElement = getCurrentPin().getNodeElement(); - nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler); - nodeElement.addEventListener( - Configuration.nodeDragEventName, - isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler - ); - nodeElement.addEventListener( - Configuration.nodeReflowEventName, - isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler - ); - isDestinationPin - ? this.setDestinationLocation() - : (this.setSourceLocation(), this.originatesFromInput = this.sourcePin.isInput()); - this.#linkPins(); - } - } - - #linkPins() { - if (this.sourcePin && this.destinationPin) { - this.sourcePin.linkTo(this.destinationPin); - this.destinationPin.linkTo(this.sourcePin); - } - } - - #unlinkPins() { - if (this.sourcePin && this.destinationPin) { - this.sourcePin.unlinkFrom(this.destinationPin, false); - this.destinationPin.unlinkFrom(this.sourcePin, false); - } - } - - cleanup() { - super.cleanup(); - this.#unlinkPins(); - this.sourcePin = null; - this.destinationPin = null; - } - - /** @param {Number[]?} location */ - setSourceLocation(location = null, canPostpone = true) { - if (location == null) { - const self = this; - if (canPostpone && (!this.hasUpdated || !this.sourcePin.hasUpdated)) { - Promise.all([this.updateComplete, this.sourcePin.updateComplete]) - .then(() => self.setSourceLocation(null, false)); - return - } - location = this.sourcePin.template.getLinkLocation(); - } - const [x, y] = location; - this.fromX = x; - this.fromY = y; - } - - /** @param {Number[]?} location */ - setDestinationLocation(location = null, canPostpone = true) { - if (location == null) { - const self = this; - if (canPostpone && (!this.hasUpdated || !this.destinationPin.hasUpdated)) { - Promise.all([this.updateComplete, this.destinationPin.updateComplete]) - .then(() => self.setDestinationLocation(null, false)); - return - } - location = this.destinationPin.template.getLinkLocation(); - } - this.toX = location[0]; - this.toY = location[1]; - } - - getInputPin() { - if (this.sourcePin?.isInput()) { - return this.sourcePin - } - return this.destinationPin - } - - /** @param {PinElement} pin */ - setInputPin(pin) { - if (this.sourcePin?.isInput()) { - this.sourcePin = pin; - } - this.destinationPin = pin; - } - - getOutputPin() { - if (this.destinationPin?.isOutput()) { - return this.destinationPin - } - return this.sourcePin - } - - /** @param {PinElement} pin */ - setOutputPin(pin) { - if (this.destinationPin?.isOutput()) { - this.destinationPin = pin; - } - this.sourcePin = pin; - } - - startDragging() { - this.dragging = true; - } - - finishDragging() { - this.dragging = false; - } - - removeMessage() { - this.linkMessageIcon = b; - this.linkMessageText = b; - } - - setMessageConvertType() { - this.linkMessageIcon = "ueb-icon-conver-type"; - this.linkMessageText = `Convert ${this.sourcePin.pinType} to ${this.destinationPin.pinType}.`; - } - - setMessageCorrect() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = b; - } - - setMessageReplace() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = b; - } - - setMessageDirectionsIncompatible() { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = y`Directions are not compatbile.`; - } - - setMessagePlaceNode() { - this.linkMessageIcon = "ueb-icon-place-node"; - this.linkMessageText = y`Place a new node.`; - } - - setMessageReplaceLink() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = y`Replace existing input connections.`; - } - - setMessageReplaceOutputLink() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = y`Replace existing output connections.`; - } - - setMessageSameNode() { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = y`Both are on the same node.`; - } - - setMEssagetypesIncompatible() { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = y`${this.sourcePin.pinType} is not compatible with ${this.destinationPin.pinType}.`; - } -} - +} + +/** + * @typedef {import("../element/IDraggableElement").DragEvent} DragEvent + * @typedef {import("./PinElement").default} PinElement + * @typedef {import("lit").TemplateResult<1>} TemplateResult + * @typedef {typeof LinkElement} LinkElementConstructor + */ + +/** @extends {IFromToPositionedElement} */ +class LinkElement extends IFromToPositionedElement { + + static properties = { + ...super.properties, + source: { + type: String, + reflect: true, + }, + destination: { + type: String, + reflect: true, + }, + dragging: { + type: Boolean, + attribute: "data-dragging", + converter: Utility.booleanConverter, + reflect: true, + }, + originatesFromInput: { + type: Boolean, + attribute: false, + }, + svgPathD: { + type: String, + attribute: false, + }, + linkMessageIcon: { + type: String, + attribute: false, + }, + linkMessageText: { + type: String, + attribute: false, + }, + } + + /** @type {PinElement} */ + #sourcePin + get sourcePin() { + return this.#sourcePin + } + set sourcePin(pin) { + this.#setPin(pin, false); + } + + /** @type {PinElement} */ + #destinationPin + get destinationPin() { + return this.#destinationPin + } + set destinationPin(pin) { + this.#setPin(pin, true); + } + + #nodeDeleteHandler = () => this.remove() + /** @param {DragEvent} e */ + #nodeDragSourceHandler = e => this.addSourceLocation(...e.detail.value) + /** @param {DragEvent} e */ + #nodeDragDestinatonHandler = e => this.addDestinationLocation(...e.detail.value) + #nodeReflowSourceHandler = e => this.setSourceLocation() + #nodeReflowDestinatonHandler = e => this.setDestinationLocation() + + /** @type {TemplateResult | nothing} */ + linkMessageIcon = b + /** @type {TemplateResult | nothing} */ + linkMessageText = b + + /** @type {SVGPathElement} */ + pathElement + + constructor() { + super(); + this.source = null; + this.destination = null; + this.dragging = false; + this.originatesFromInput = false; + this.startPercentage = 0; + this.svgPathD = ""; + this.startPixels = 0; + } + + /** + * @param {PinElement} source + * @param {PinElement?} destination + */ + static newObject(source, destination) { + const result = new LinkElement(); + result.initialize(source, destination); + return result + } + + /** + * @param {PinElement} source + * @param {PinElement?} destination + */ + initialize(source, destination) { + super.initialize({}, new LinkTemplate()); + if (source) { + this.sourcePin = source; + if (!destination) { + this.toX = this.fromX; + this.toY = this.fromY; + } + } + if (destination) { + this.destinationPin = destination; + if (!source) { + this.fromX = this.toX; + this.fromY = this.toY; + } + } + } + + /** + * @param {PinElement} pin + * @param {Boolean} isDestinationPin + */ + #setPin(pin, isDestinationPin) { + const getCurrentPin = () => isDestinationPin ? this.destinationPin : this.sourcePin; + if (getCurrentPin() == pin) { + return + } + if (getCurrentPin()) { + const nodeElement = getCurrentPin().getNodeElement(); + nodeElement.removeEventListener(Configuration.removeEventName, this.#nodeDeleteHandler); + nodeElement.removeEventListener( + Configuration.nodeDragEventName, + isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler + ); + nodeElement.removeEventListener( + Configuration.nodeReflowEventName, + isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler + ); + this.#unlinkPins(); + } + isDestinationPin + ? this.#destinationPin = pin + : this.#sourcePin = pin; + if (getCurrentPin()) { + const nodeElement = getCurrentPin().getNodeElement(); + nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler); + nodeElement.addEventListener( + Configuration.nodeDragEventName, + isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler + ); + nodeElement.addEventListener( + Configuration.nodeReflowEventName, + isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler + ); + isDestinationPin + ? this.setDestinationLocation() + : (this.setSourceLocation(), this.originatesFromInput = this.sourcePin.isInput()); + this.#linkPins(); + } + } + + #linkPins() { + if (this.sourcePin && this.destinationPin) { + this.sourcePin.linkTo(this.destinationPin); + this.destinationPin.linkTo(this.sourcePin); + } + } + + #unlinkPins() { + if (this.sourcePin && this.destinationPin) { + this.sourcePin.unlinkFrom(this.destinationPin, false); + this.destinationPin.unlinkFrom(this.sourcePin, false); + } + } + + cleanup() { + super.cleanup(); + this.#unlinkPins(); + this.sourcePin = null; + this.destinationPin = null; + } + + /** @param {Number[]?} location */ + setSourceLocation(location = null, canPostpone = true) { + if (location == null) { + const self = this; + if (canPostpone && (!this.hasUpdated || !this.sourcePin.hasUpdated)) { + Promise.all([this.updateComplete, this.sourcePin.updateComplete]) + .then(() => self.setSourceLocation(null, false)); + return + } + location = this.sourcePin.template.getLinkLocation(); + } + const [x, y] = location; + this.fromX = x; + this.fromY = y; + } + + /** @param {Number[]?} location */ + setDestinationLocation(location = null, canPostpone = true) { + if (location == null) { + const self = this; + if (canPostpone && (!this.hasUpdated || !this.destinationPin.hasUpdated)) { + Promise.all([this.updateComplete, this.destinationPin.updateComplete]) + .then(() => self.setDestinationLocation(null, false)); + return + } + location = this.destinationPin.template.getLinkLocation(); + } + this.toX = location[0]; + this.toY = location[1]; + } + + getInputPin() { + if (this.sourcePin?.isInput()) { + return this.sourcePin + } + return this.destinationPin + } + + /** @param {PinElement} pin */ + setInputPin(pin) { + if (this.sourcePin?.isInput()) { + this.sourcePin = pin; + } + this.destinationPin = pin; + } + + getOutputPin() { + if (this.destinationPin?.isOutput()) { + return this.destinationPin + } + return this.sourcePin + } + + /** @param {PinElement} pin */ + setOutputPin(pin) { + if (this.destinationPin?.isOutput()) { + this.destinationPin = pin; + } + this.sourcePin = pin; + } + + startDragging() { + this.dragging = true; + } + + finishDragging() { + this.dragging = false; + } + + removeMessage() { + this.linkMessageIcon = b; + this.linkMessageText = b; + } + + setMessageConvertType() { + this.linkMessageIcon = "ueb-icon-conver-type"; + this.linkMessageText = `Convert ${this.sourcePin.pinType} to ${this.destinationPin.pinType}.`; + } + + setMessageCorrect() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = b; + } + + setMessageReplace() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = b; + } + + setMessageDirectionsIncompatible() { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = y`Directions are not compatbile.`; + } + + setMessagePlaceNode() { + this.linkMessageIcon = "ueb-icon-place-node"; + this.linkMessageText = y`Place a new node.`; + } + + setMessageReplaceLink() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = y`Replace existing input connections.`; + } + + setMessageReplaceOutputLink() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = y`Replace existing output connections.`; + } + + setMessageSameNode() { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = y`Both are on the same node.`; + } + + setMEssagetypesIncompatible() { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = y`${this.sourcePin.pinType} is not compatible with ${this.destinationPin.pinType}.`; + } +} + /** * @typedef {import("../../Blueprint").default} Blueprint * @typedef {import("../../element/IDraggableElement").default} IDraggableElement @@ -5898,8 +5898,8 @@ class MouseMoveDraggable extends IMouseClickDrag { dragAction(location, offset) { this.target.setLocation(location[0] - this.clickedOffset[0], location[1] - this.clickedOffset[1]); } -} - +} + /** @typedef {import("../../Blueprint").default} Blueprint */ class MouseClickDrag extends MouseMoveDraggable { @@ -5949,8 +5949,8 @@ class MouseClickDrag extends MouseMoveDraggable { super.endDrag(); this.#onEndDrag?.(); } -} - +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../element/IDraggableElement").default} IDraggableElement @@ -6008,31 +6008,31 @@ class IDraggableTemplate extends ITemplate { let avgY = Math.max((dt + db) / 2, minMargin); this.blueprint.scrollDelta(dl - avgX, dt - avgY, true); } -} - -/** - * @typedef {import("../element/IDraggableElement").default} IDraggableElement - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -/** - * @template {IDraggableElement} T - * @extends {IDraggableTemplate} - */ -class IDraggablePositionedTemplate extends IDraggableTemplate { - - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - if (changedProperties.has("locationX")) { - this.element.style.left = `${this.element.locationX}px`; - } - if (changedProperties.has("locationY")) { - this.element.style.top = `${this.element.locationY}px`; - } - } -} - +} + +/** + * @typedef {import("../element/IDraggableElement").default} IDraggableElement + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +/** + * @template {IDraggableElement} T + * @extends {IDraggableTemplate} + */ +class IDraggablePositionedTemplate extends IDraggableTemplate { + + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + super.update(changedProperties); + if (changedProperties.has("locationX")) { + this.element.style.left = `${this.element.locationX}px`; + } + if (changedProperties.has("locationY")) { + this.element.style.top = `${this.element.locationY}px`; + } + } +} + /** * @typedef {import("../../Blueprint").default} Blueprint * @typedef {import("../../element/NodeElement").default} NodeElement @@ -6068,8 +6068,8 @@ class MouseMoveNodes extends MouseMoveDraggable { ); } } -} - +} + /** * @typedef {import("../element/NodeElement").default} NodeElement * @typedef {import("lit").PropertyValues} PropertyValues @@ -6100,8 +6100,8 @@ class ISelectableDraggableTemplate extends IDraggablePositionedTemplate { this.element.setSelected(true); } } -} - +} + /** * @typedef {import("../../element/NodeElement").default} NodeElement * @typedef {import("../../element/PinElement").default} PinElement @@ -6242,275 +6242,275 @@ class NodeTemplate extends ISelectableDraggableTemplate { } linksChanged() { } -} - -/** - * @typedef {import("../element/NodeElement").default} NodeElement - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -class IResizeableTemplate extends NodeTemplate { - - #THandler = document.createElement("div") - #RHandler = document.createElement("div") - #BHandler = document.createElement("div") - #LHandler = document.createElement("div") - #TRHandler = document.createElement("div") - #BRHandler = document.createElement("div") - #BLHandler = document.createElement("div") - #TLHandler = document.createElement("div") - - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - this.element.classList.add("ueb-resizeable"); - this.#THandler.classList.add("ueb-resizeable-top"); - this.#RHandler.classList.add("ueb-resizeable-right"); - this.#BHandler.classList.add("ueb-resizeable-bottom"); - this.#LHandler.classList.add("ueb-resizeable-left"); - this.#TRHandler.classList.add("ueb-resizeable-top-right"); - this.#BRHandler.classList.add("ueb-resizeable-bottom-right"); - this.#BLHandler.classList.add("ueb-resizeable-bottom-left"); - this.#TLHandler.classList.add("ueb-resizeable-top-left"); - } - - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - if (this.element.sizeX >= 0 && changedProperties.has("sizeX")) { - this.element.style.width = `${this.element.sizeX}px`; - } - if (this.element.sizeY >= 0 && changedProperties.has("sizeY")) { - this.element.style.height = `${this.element.sizeY}px`; - } - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.element.append( - this.#THandler, - this.#RHandler, - this.#BHandler, - this.#LHandler, - this.#TRHandler, - this.#BRHandler, - this.#BLHandler, - this.#TLHandler - ); - } - - createInputObjects() { - return [ - ...super.createInputObjects(), - new MouseClickDrag(this.#THandler, this.blueprint, { - onDrag: (location, movement) => { - movement[1] = location[1] - this.element.topBoundary(); - if (this.setSizeY(this.element.sizeY - movement[1])) { - this.element.addLocation(0, movement[1], false); - } - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#RHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.rightBoundary(); - this.setSizeX(this.element.sizeX + movement[0]); - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#BHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[1] = location[1] - this.element.bottomBoundary(); - this.setSizeY(this.element.sizeY + movement[1]); - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#LHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.leftBoundary(); - if (this.setSizeX(this.element.sizeX - movement[0])) { - this.element.addLocation(movement[0], 0, false); - } - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#TRHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.rightBoundary(); - movement[1] = location[1] - this.element.topBoundary(); - this.setSizeX(this.element.sizeX + movement[0]); - if (this.setSizeY(this.element.sizeY - movement[1])) { - this.element.addLocation(0, movement[1], false); - } - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#BRHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.rightBoundary(); - movement[1] = location[1] - this.element.bottomBoundary(); - this.setSizeX(this.element.sizeX + movement[0]); - this.setSizeY(this.element.sizeY + movement[1]); - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#BLHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.leftBoundary(); - movement[1] = location[1] - this.element.bottomBoundary(); - if (this.setSizeX(this.element.sizeX - movement[0])) { - this.element.addLocation(movement[0], 0, false); - } - this.setSizeY(this.element.sizeY + movement[1]); - }, - onEndDrag: () => this.endResize(), - }), - new MouseClickDrag(this.#TLHandler, this.blueprint, { - onDrag: (location, movement) => { - movement[0] = location[0] - this.element.leftBoundary(); - movement[1] = location[1] - this.element.topBoundary(); - if (this.setSizeX(this.element.sizeX - movement[0])) { - this.element.addLocation(movement[0], 0, false); - } - if (this.setSizeY(this.element.sizeY - movement[1])) { - this.element.addLocation(0, movement[1], false); - } - }, - onEndDrag: () => this.endResize(), - }), - ] - } - - /** @param {Number} value */ - setSizeX(value) { - this.element.setNodeWidth(value); - return true - } - - /** @param {Number} value */ - setSizeY(value) { - this.element.setNodeHeight(value); - return true - } - - endResize() { - } -} - -/** - * @typedef {import("../../element/NodeElement").default} NodeElement - * @typedef {import("../../element/PinElement").default} PinElement - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -class CommentNodeTemplate extends IResizeableTemplate { - - #color = LinearColorEntity.getWhite() - #selectableAreaHeight = 0 - - /** @param {NodeElement} element */ - initialize(element) { - if (element.entity.CommentColor) { - this.#color.setFromRGBANumber(element.entity.CommentColor.toNumber()); - this.#color.setFromHSVA( - this.#color.H.value, - this.#color.S.value, - Math.pow(this.#color.V.value, 0.45) * 0.67 - ); - } - element.classList.add("ueb-node-style-comment", "ueb-node-resizeable"); - element.sizeX = 25 * Configuration.gridSize; - element.sizeY = 6 * Configuration.gridSize; - super.initialize(element); // Keep it at the end because it calls this.getColor() where this.#color must be initialized - } - - getColor() { - return i$3`${Math.round(this.#color.R.value * 255)}, ${Math.round(this.#color.G.value * 255)}, ${Math.round(this.#color.B.value * 255)}` - } - - getDraggableElement() { - return this.element.querySelector(".ueb-node-top") - } - - render() { - return y` -
-
-
- ${this.element.entity.NodeComment} -
-
-
- ` - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - const bounding = this.getDraggableElement().getBoundingClientRect(); - this.#selectableAreaHeight = bounding.height; - } - - manageNodesBind() { - let nodes = this.blueprint.getNodes(); - for (let node of nodes) { - if ( - node.topBoundary() >= this.element.topBoundary() - && node.rightBoundary() <= this.element.rightBoundary() - && node.bottomBoundary() <= this.element.bottomBoundary() - && node.leftBoundary() >= this.element.leftBoundary() - ) { - node.bindToComment(this.element); - } else { - node.unbindFromComment(this.element); - } - } - } - - /** @param {Number} value */ - setSizeX(value) { - value = Math.round(value); - if (value >= Configuration.gridSet * Configuration.gridSize) { - this.element.setNodeWidth(value); - return true - } - return false - } - - /** @param {Number} value */ - setSizeY(value) { - value = Math.round(value); - if (value >= 3 * Configuration.gridSize) { - this.element.setNodeHeight(value); - return true - } - return false - } - - endResize() { - this.manageNodesBind(); - } - - topBoundary(justSelectableArea = false) { - return this.element.locationY - } - - rightBoundary(justSelectableArea = false) { - return this.element.locationX + this.element.sizeX - } - - bottomBoundary(justSelectableArea = false) { - return justSelectableArea - ? this.element.locationY + this.#selectableAreaHeight - : super.bottomBoundary() - } - - leftBoundary(justSelectableArea = false) { - return this.element.locationX - } -} - +} + +/** + * @typedef {import("../element/NodeElement").default} NodeElement + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +class IResizeableTemplate extends NodeTemplate { + + #THandler = document.createElement("div") + #RHandler = document.createElement("div") + #BHandler = document.createElement("div") + #LHandler = document.createElement("div") + #TRHandler = document.createElement("div") + #BRHandler = document.createElement("div") + #BLHandler = document.createElement("div") + #TLHandler = document.createElement("div") + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + this.element.classList.add("ueb-resizeable"); + this.#THandler.classList.add("ueb-resizeable-top"); + this.#RHandler.classList.add("ueb-resizeable-right"); + this.#BHandler.classList.add("ueb-resizeable-bottom"); + this.#LHandler.classList.add("ueb-resizeable-left"); + this.#TRHandler.classList.add("ueb-resizeable-top-right"); + this.#BRHandler.classList.add("ueb-resizeable-bottom-right"); + this.#BLHandler.classList.add("ueb-resizeable-bottom-left"); + this.#TLHandler.classList.add("ueb-resizeable-top-left"); + } + + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + super.update(changedProperties); + if (this.element.sizeX >= 0 && changedProperties.has("sizeX")) { + this.element.style.width = `${this.element.sizeX}px`; + } + if (this.element.sizeY >= 0 && changedProperties.has("sizeY")) { + this.element.style.height = `${this.element.sizeY}px`; + } + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.element.append( + this.#THandler, + this.#RHandler, + this.#BHandler, + this.#LHandler, + this.#TRHandler, + this.#BRHandler, + this.#BLHandler, + this.#TLHandler + ); + } + + createInputObjects() { + return [ + ...super.createInputObjects(), + new MouseClickDrag(this.#THandler, this.blueprint, { + onDrag: (location, movement) => { + movement[1] = location[1] - this.element.topBoundary(); + if (this.setSizeY(this.element.sizeY - movement[1])) { + this.element.addLocation(0, movement[1], false); + } + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#RHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.rightBoundary(); + this.setSizeX(this.element.sizeX + movement[0]); + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#BHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[1] = location[1] - this.element.bottomBoundary(); + this.setSizeY(this.element.sizeY + movement[1]); + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#LHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.leftBoundary(); + if (this.setSizeX(this.element.sizeX - movement[0])) { + this.element.addLocation(movement[0], 0, false); + } + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#TRHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.rightBoundary(); + movement[1] = location[1] - this.element.topBoundary(); + this.setSizeX(this.element.sizeX + movement[0]); + if (this.setSizeY(this.element.sizeY - movement[1])) { + this.element.addLocation(0, movement[1], false); + } + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#BRHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.rightBoundary(); + movement[1] = location[1] - this.element.bottomBoundary(); + this.setSizeX(this.element.sizeX + movement[0]); + this.setSizeY(this.element.sizeY + movement[1]); + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#BLHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.leftBoundary(); + movement[1] = location[1] - this.element.bottomBoundary(); + if (this.setSizeX(this.element.sizeX - movement[0])) { + this.element.addLocation(movement[0], 0, false); + } + this.setSizeY(this.element.sizeY + movement[1]); + }, + onEndDrag: () => this.endResize(), + }), + new MouseClickDrag(this.#TLHandler, this.blueprint, { + onDrag: (location, movement) => { + movement[0] = location[0] - this.element.leftBoundary(); + movement[1] = location[1] - this.element.topBoundary(); + if (this.setSizeX(this.element.sizeX - movement[0])) { + this.element.addLocation(movement[0], 0, false); + } + if (this.setSizeY(this.element.sizeY - movement[1])) { + this.element.addLocation(0, movement[1], false); + } + }, + onEndDrag: () => this.endResize(), + }), + ] + } + + /** @param {Number} value */ + setSizeX(value) { + this.element.setNodeWidth(value); + return true + } + + /** @param {Number} value */ + setSizeY(value) { + this.element.setNodeHeight(value); + return true + } + + endResize() { + } +} + +/** + * @typedef {import("../../element/NodeElement").default} NodeElement + * @typedef {import("../../element/PinElement").default} PinElement + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +class CommentNodeTemplate extends IResizeableTemplate { + + #color = LinearColorEntity.getWhite() + #selectableAreaHeight = 0 + + /** @param {NodeElement} element */ + initialize(element) { + if (element.entity.CommentColor) { + this.#color.setFromRGBANumber(element.entity.CommentColor.toNumber()); + this.#color.setFromHSVA( + this.#color.H.value, + this.#color.S.value, + Math.pow(this.#color.V.value, 0.45) * 0.67 + ); + } + element.classList.add("ueb-node-style-comment", "ueb-node-resizeable"); + element.sizeX = 25 * Configuration.gridSize; + element.sizeY = 6 * Configuration.gridSize; + super.initialize(element); // Keep it at the end because it calls this.getColor() where this.#color must be initialized + } + + getColor() { + return i$3`${Math.round(this.#color.R.value * 255)}, ${Math.round(this.#color.G.value * 255)}, ${Math.round(this.#color.B.value * 255)}` + } + + getDraggableElement() { + return this.element.querySelector(".ueb-node-top") + } + + render() { + return y` +
+
+
+ ${this.element.entity.NodeComment} +
+
+
+ ` + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + const bounding = this.getDraggableElement().getBoundingClientRect(); + this.#selectableAreaHeight = bounding.height; + } + + manageNodesBind() { + let nodes = this.blueprint.getNodes(); + for (let node of nodes) { + if ( + node.topBoundary() >= this.element.topBoundary() + && node.rightBoundary() <= this.element.rightBoundary() + && node.bottomBoundary() <= this.element.bottomBoundary() + && node.leftBoundary() >= this.element.leftBoundary() + ) { + node.bindToComment(this.element); + } else { + node.unbindFromComment(this.element); + } + } + } + + /** @param {Number} value */ + setSizeX(value) { + value = Math.round(value); + if (value >= Configuration.gridSet * Configuration.gridSize) { + this.element.setNodeWidth(value); + return true + } + return false + } + + /** @param {Number} value */ + setSizeY(value) { + value = Math.round(value); + if (value >= 3 * Configuration.gridSize) { + this.element.setNodeHeight(value); + return true + } + return false + } + + endResize() { + this.manageNodesBind(); + } + + topBoundary(justSelectableArea = false) { + return this.element.locationY + } + + rightBoundary(justSelectableArea = false) { + return this.element.locationX + this.element.sizeX + } + + bottomBoundary(justSelectableArea = false) { + return justSelectableArea + ? this.element.locationY + this.#selectableAreaHeight + : super.bottomBoundary() + } + + leftBoundary(justSelectableArea = false) { + return this.element.locationX + } +} + /** * @typedef {import("../../Blueprint").default} Blueprint * @typedef {import("../../element/LinkElement").default} LinkElement @@ -6644,80 +6644,80 @@ class MouseCreateLink extends IMouseClickDrag { this.link = null; this.#listenedPins = null; } -} - -/** - * @typedef {import("../../element/NodeElement").default} NodeElement - * @typedef {import("../../element/PinElement").PinElementConstructor} PinElementConstructor - */ - -class VariableManagementNodeTemplate extends NodeTemplate { - - #hasInput = false - #hasOutput = false - #displayName = "" - - static nodeStyleClasses = ["ueb-node-style-glass"] - - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - this.#displayName = this.element.getNodeDisplayName(); - } - - render() { - return y` -
-
- ${this.#displayName ? y` -
-
- - ${this.#displayName} - -
-
- ` : b} -
- ${this.#hasInput ? y` -
- ` : b} - ${this.#hasOutput ? y` -
- ` : b} -
-
-
- ` - } - - createPinElements() { - return this.element.getPinEntities() - .filter(v => !v.isHidden()) - .map(v => { - this.#hasInput ||= v.isInput(); - this.#hasOutput ||= v.isOutput(); - const result = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) - .newObject(v, undefined, this.element); - return result - }) - } -} - -/** @typedef {import("../../element/NodeElement").default} NodeElement */ - -class VariableConversionNodeTemplate extends VariableManagementNodeTemplate { - - static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-conversion"] -} - -/** @typedef {import("../../element/NodeElement").default} NodeElement */ - -class VariableOperationNodeTemplate extends VariableManagementNodeTemplate { - - static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-operation"] -} - +} + +/** + * @typedef {import("../../element/NodeElement").default} NodeElement + * @typedef {import("../../element/PinElement").PinElementConstructor} PinElementConstructor + */ + +class VariableManagementNodeTemplate extends NodeTemplate { + + #hasInput = false + #hasOutput = false + #displayName = "" + + static nodeStyleClasses = ["ueb-node-style-glass"] + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + this.#displayName = this.element.getNodeDisplayName(); + } + + render() { + return y` +
+
+ ${this.#displayName ? y` +
+
+ + ${this.#displayName} + +
+
+ ` : b} +
+ ${this.#hasInput ? y` +
+ ` : b} + ${this.#hasOutput ? y` +
+ ` : b} +
+
+
+ ` + } + + createPinElements() { + return this.element.getPinEntities() + .filter(v => !v.isHidden()) + .map(v => { + this.#hasInput ||= v.isInput(); + this.#hasOutput ||= v.isOutput(); + const result = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) + .newObject(v, undefined, this.element); + return result + }) + } +} + +/** @typedef {import("../../element/NodeElement").default} NodeElement */ + +class VariableConversionNodeTemplate extends VariableManagementNodeTemplate { + + static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-conversion"] +} + +/** @typedef {import("../../element/NodeElement").default} NodeElement */ + +class VariableOperationNodeTemplate extends VariableManagementNodeTemplate { + + static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-operation"] +} + /** * @typedef {import("../../input/IInput").default} IInput * @typedef {import("lit").PropertyValues} PropertyValues @@ -6838,28 +6838,28 @@ class PinTemplate extends ITemplate { getClickableElement() { return this.#wrapperElement ?? this.element } -} - -/** - * @template T - * @typedef {import("../../element/PinElement").default} PinElement - */ - -/** - * @template T - * @extends PinTemplate> - */ -class MinimalPinTemplate extends PinTemplate { - - render() { - return y` -
-
${this.renderIcon()}
-
- ` - } -} - +} + +/** + * @template T + * @typedef {import("../../element/PinElement").default} PinElement + */ + +/** + * @template T + * @extends PinTemplate> + */ +class MinimalPinTemplate extends PinTemplate { + + render() { + return y` +
+
${this.renderIcon()}
+
+ ` + } +} + /** * @typedef {import("../../element/PinElement").PinElementConstructor} PinElementConstructor * @typedef {import("lit").PropertyValues} PropertyValues @@ -6914,492 +6914,492 @@ class EventNodeTemplate extends NodeTemplate { .newObject(pinEntity, undefined, this.element) ) } -} - -/** - * @typedef {import("../element/IDraggableElement").DragEvent} DragEvent - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../template/ISelectableDraggableTemplate").default} ISelectableDraggableTemplate - */ - -/** - * @template {IEntity} T - * @template {ISelectableDraggableTemplate} U - * @extends {IDraggableElement} - */ -class ISelectableDraggableElement extends IDraggableElement { - - static properties = { - ...super.properties, - selected: { - type: Boolean, - attribute: "data-selected", - reflect: true, - converter: Utility.booleanConverter, - }, - } - - /** @param {DragEvent} e */ - dragHandler = e => this.addLocation(...e.detail.value) - - constructor() { - super(); - this.selected = false; - this.listeningDrag = false; - } - - setup() { - super.setup(); - this.setSelected(this.selected); - } - - cleanup() { - super.cleanup(); - this.blueprint.removeEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); - } - - setSelected(value = true) { - this.selected = value; - if (this.blueprint) { - if (this.selected) { - this.listeningDrag = true; - this.blueprint.addEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); - } else { - this.blueprint.removeEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); - this.listeningDrag = false; - } - } - } -} - -/** - * @typedef {import("../node/KnotNodeTemplate").default} KnotNodeTemplate - * @typedef {import("../../entity/PinEntity").default} KnotEntity - */ - -/** @extends MinimalPinTemplate */ -class KnotPinTemplate extends MinimalPinTemplate { - - render() { - return this.element.isOutput() ? super.render() : y`` - } - - getOppositePin() { - const nodeTemplate = /** @type {KnotNodeTemplate} */(this.element.nodeElement.template); - return this.element.isOutput() ? nodeTemplate.inputPin : nodeTemplate.outputPin - } - - getLinkLocation() { - const rect = ( - this.element.isInput() - ? /** @type {KnotNodeTemplate} */(this.element.nodeElement.template).outputPin.template - : this - ) - .iconElement.getBoundingClientRect(); - const boundingLocation = [this.element.isInput() ? rect.left : rect.right, (rect.top + rect.bottom) / 2]; - const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); - return this.blueprint.compensateTranslation(location[0], location[1]) - } -} - -/** - * @typedef {import("../../element/NodeElement").default} NodeElement - * @typedef {import("../../element/PinElement").default} PinElement - * @typedef {import("../../element/PinElement").PinElementConstructor} PinElementConstructor - */ - -class KnotNodeTemplate extends NodeTemplate { - - static #traversedPin = new Set() - - /** @type {Boolean?} */ - #chainDirection = null // The node is part of a chain connected to an input or output pin - - /** @type {PinElement} */ - #inputPin - get inputPin() { - return this.#inputPin - } - - /** @type {PinElement} */ - #outputPin - get outputPin() { - return this.#outputPin - } - - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - this.element.classList.add("ueb-node-style-minimal"); - } - - /** @param {PinElement} startingPin */ - findDirectionaPin(startingPin) { - if ( - startingPin.nodeElement.getType() !== Configuration.nodeType.knot - || KnotNodeTemplate.#traversedPin.has(startingPin) - ) { - KnotNodeTemplate.#traversedPin.clear(); - return true - } - KnotNodeTemplate.#traversedPin.add(startingPin); - for (let pin of startingPin.getLinks().map(l => this.blueprint.getPin(l))) { - if (this.findDirectionaPin(pin)) { - return true - } - } - return false - } - - render() { - return y` -
- ` - } - - setupPins() { - this.element.getPinElements().forEach( - p => /** @type {HTMLElement} */(this.element.querySelector(".ueb-node-border")).appendChild(p) - ); - } - - /** - * @param {NodeElement} node - * @returns {NodeListOf} - */ - getPinElements(node) { - return node.querySelectorAll("ueb-pin") - } - - createPinElements() { - const entities = this.element.getPinEntities().filter(v => !v.isHidden()); - const inputEntity = entities[entities[0].isInput() ? 0 : 1]; - const outputEntity = entities[entities[0].isOutput() ? 0 : 1]; - const pinElementConstructor = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")); - let result = [ - this.#inputPin = pinElementConstructor.newObject(inputEntity, new KnotPinTemplate(), this.element), - this.#outputPin = pinElementConstructor.newObject(outputEntity, new KnotPinTemplate(), this.element), - ]; - return result - } - - linksChanged() { - - } -} - -/** @typedef {import("../../element/NodeElement").default} NodeElement */ - -class VariableAccessNodeTemplate extends VariableManagementNodeTemplate { - - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - if (element.getType() === Configuration.nodeType.variableGet) { - this.element.classList.add("ueb-node-style-getter"); - } else if (element.getType() === Configuration.nodeType.variableSet) { - this.element.classList.add("ueb-node-style-setter"); - } - } - - setupPins() { - super.setupPins(); - let outputPin = this.element.getPinElements().find(p => !p.entity.isHidden() && !p.entity.isExecution()); - this.element.style.setProperty("--ueb-node-color", outputPin.getColor().cssText); - } -} - -/** - * @typedef {import("./IDraggableElement").DragEvent} DragEvent - * @typedef {import("./IElement").default} IElement - * @typedef {import("./PinElement").default} PinElement - * @typedef {typeof NodeElement} NodeElementConstructor - */ - -/** @extends {ISelectableDraggableElement} */ -class NodeElement extends ISelectableDraggableElement { - - static properties = { - ...ISelectableDraggableElement.properties, - typePath: { - type: String, - attribute: "data-type", - reflect: true, - }, - nodeName: { - type: String, - attribute: "data-name", - reflect: true, - }, - advancedPinDisplay: { - type: String, - attribute: "data-advanced-display", - converter: IdentifierEntity.attributeConverter, - reflect: true, - }, - enabledState: { - type: String, - attribute: "data-enabled-state", - reflect: true, - }, - nodeDisplayName: { - type: String, - attribute: false, - }, - pureFunction: { - type: Boolean, - converter: Utility.booleanConverter, - attribute: "data-pure-function", - reflect: true, - }, - } - static dragEventName = Configuration.nodeDragEventName - static dragGeneralEventName = Configuration.nodeDragGeneralEventName - - get blueprint() { - return super.blueprint - } - set blueprint(v) { - super.blueprint = v; - this.#pins.forEach(p => p.blueprint = v); - } - - /** @type {HTMLElement} */ - #nodeNameElement - get nodeNameElement() { - return this.#nodeNameElement - } - set nodeNameElement(value) { - this.#nodeNameElement = value; - } - - /** @type {PinElement[]} */ - #pins = [] - /** @type {NodeElement[]} */ - boundComments = [] - #commentDragged = false - /** @param {DragEvent} e */ - #commentDragHandler = e => { - // If selected, it will already drag, also must check if under nested comments, it must drag just once - if (!this.selected && !this.#commentDragged) { - this.#commentDragged = true; - this.addNextUpdatedCallbacks(() => this.#commentDragged = false); - this.addLocation(...e.detail.value); - } - } - - /** - * @param {ObjectEntity} nodeEntity - * @return {new () => NodeTemplate} - */ - static getTypeTemplate(nodeEntity) { - if ( - nodeEntity.getClass() === Configuration.nodeType.callFunction - || nodeEntity.getClass() === Configuration.nodeType.commutativeAssociativeBinaryOperator - || nodeEntity.getClass() === Configuration.nodeType.callArrayFunction - ) { - const memberParent = nodeEntity.FunctionReference.MemberParent?.path ?? ""; - if ( - memberParent === "/Script/Engine.KismetMathLibrary" - || memberParent === "/Script/Engine.KismetArrayLibrary" - ) { - if (nodeEntity.FunctionReference.MemberName?.startsWith("Conv_")) { - return VariableConversionNodeTemplate - } - if (nodeEntity.FunctionReference.MemberName?.startsWith("Percent_")) { - return VariableOperationNodeTemplate - } - switch (nodeEntity.FunctionReference.MemberName) { - case "Array_Add": - case "Array_Identical": - case "Abs": - case "Array_Add": - case "BMax": - case "BMin": - case "Exp": - case "FMax": - case "FMin": - case "Max": - case "MaxInt64": - case "Min": - case "MinInt64": - return VariableOperationNodeTemplate - } - } - if (memberParent === "/Script/Engine.BlueprintSetLibrary") { - return VariableOperationNodeTemplate - } - if (memberParent === "/Script/Engine.BlueprintMapLibrary") { - return VariableOperationNodeTemplate - } - } - switch (nodeEntity.getClass()) { - case Configuration.nodeType.comment: - return CommentNodeTemplate - case Configuration.nodeType.event: - case Configuration.nodeType.customEvent: - return EventNodeTemplate - case Configuration.nodeType.promotableOperator: - return VariableOperationNodeTemplate - case Configuration.nodeType.knot: return KnotNodeTemplate - case Configuration.nodeType.variableGet: return VariableAccessNodeTemplate - case Configuration.nodeType.variableSet: return VariableAccessNodeTemplate - } - if (nodeEntity.getDelegatePin()) { - return EventNodeTemplate - } - return NodeTemplate - } - - /** @param {String} str */ - static fromSerializedObject(str) { - str = str.trim(); - let entity = SerializerFactory.getSerializer(ObjectEntity).deserialize(str); - return NodeElement.newObject(/** @type {ObjectEntity} */(entity)) - } - - /** - * @param {ObjectEntity} entity - * @param {NodeTemplate} template - */ - static newObject(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { - const result = new NodeElement(); - result.initialize(entity, template); - return result - } - - initialize(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { - super.initialize(entity, template); - this.#pins = this.template.createPinElements(); - this.typePath = this.entity.getType(); - this.nodeName = this.entity.getObjectName(); - this.advancedPinDisplay = this.entity.AdvancedPinDisplay?.toString(); - this.enabledState = this.entity.EnabledState; - this.nodeDisplayName = this.getNodeDisplayName(); - this.pureFunction = this.entity.bIsPureFunc; - this.dragLinkObjects = []; - super.setLocation(this.entity.getNodePosX(), this.entity.getNodePosY()); - if (this.entity.NodeWidth && this.entity.NodeHeight) { - this.sizeX = this.entity.NodeWidth.value; - this.sizeY = this.entity.NodeHeight.value; - } else { - this.updateComplete.then(() => this.computeSizes()); - } - } - - getUpdateComplete() { - return Promise.all([ - super.getUpdateComplete(), - ...this.getPinElements().map(pin => pin.updateComplete) - ]).then(() => true) - } - - /** @param {NodeElement} commentNode */ - bindToComment(commentNode) { - if (commentNode != this && !this.boundComments.includes(commentNode)) { - commentNode.addEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); - this.boundComments.push(commentNode); - } - } - - /** @param {NodeElement} commentNode */ - unbindFromComment(commentNode) { - const commentIndex = this.boundComments.indexOf(commentNode); - if (commentIndex >= 0) { - commentNode.removeEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); - this.boundComments[commentIndex] = this.boundComments[this.boundComments.length - 1]; - this.boundComments.pop(); - } - } - - /** @param {NodeElement} commentNode */ - isInsideComment(commentNode) { - return this.topBoundary() >= commentNode.topBoundary() - && this.rightBoundary() <= commentNode.rightBoundary() - && this.bottomBoundary() <= commentNode.bottomBoundary() - && this.leftBoundary() >= commentNode.leftBoundary() - } - - getType() { - return this.entity.getType() - } - - getNodeName() { - return this.entity.getObjectName() - } - - getNodeDisplayName() { - return this.entity.nodeDisplayName() - } - - /** @param {Number} value */ - setNodeWidth(value) { - this.entity.setNodeWidth(value); - this.sizeX = value; - this.acknowledgeReflow(); - } - - /** @param {Number} value */ - setNodeHeight(value) { - this.entity.setNodeHeight(value); - this.sizeY = value; - this.acknowledgeReflow(); - } - - /** @param {IElement[]} nodesWhitelist */ - sanitizeLinks(nodesWhitelist = []) { - this.getPinElements().forEach(pin => pin.sanitizeLinks(nodesWhitelist)); - } - - /** @param {String} name */ - rename(name) { - if (this.entity.Name == name) { - return false - } - for (let sourcePinElement of this.getPinElements()) { - for (let targetPinReference of sourcePinElement.getLinks()) { - this.blueprint.getPin(targetPinReference).redirectLink(sourcePinElement, new PinReferenceEntity({ - objectName: name, - pinGuid: sourcePinElement.entity.PinId, - })); - } - } - this.entity.Name = name; - this.nodeName = this.entity.Name; - } - - getPinElements() { - return this.#pins - } - - /** @returns {PinEntity[]} */ - getPinEntities() { - return this.entity.CustomProperties.filter(v => v instanceof PinEntity) - } - - setLocation(x = 0, y = 0, acknowledge = true) { - this.entity.setNodePosX(x); - this.entity.setNodePosY(y); - super.setLocation(x, y, acknowledge); - } - - acknowledgeReflow() { - this.requestUpdate(); - this.updateComplete.then(() => this.computeSizes()); - let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName); - this.dispatchEvent(reflowEvent); - } - - setShowAdvancedPinDisplay(value) { - this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden"); - this.advancedPinDisplay = this.entity.AdvancedPinDisplay; - } - - toggleShowAdvancedPinDisplay() { - this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay?.toString() != "Shown"); - } -} - +} + +/** + * @typedef {import("../element/IDraggableElement").DragEvent} DragEvent + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../template/ISelectableDraggableTemplate").default} ISelectableDraggableTemplate + */ + +/** + * @template {IEntity} T + * @template {ISelectableDraggableTemplate} U + * @extends {IDraggableElement} + */ +class ISelectableDraggableElement extends IDraggableElement { + + static properties = { + ...super.properties, + selected: { + type: Boolean, + attribute: "data-selected", + reflect: true, + converter: Utility.booleanConverter, + }, + } + + /** @param {DragEvent} e */ + dragHandler = e => this.addLocation(...e.detail.value) + + constructor() { + super(); + this.selected = false; + this.listeningDrag = false; + } + + setup() { + super.setup(); + this.setSelected(this.selected); + } + + cleanup() { + super.cleanup(); + this.blueprint.removeEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); + } + + setSelected(value = true) { + this.selected = value; + if (this.blueprint) { + if (this.selected) { + this.listeningDrag = true; + this.blueprint.addEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); + } else { + this.blueprint.removeEventListener(Configuration.nodeDragGeneralEventName, this.dragHandler); + this.listeningDrag = false; + } + } + } +} + +/** + * @typedef {import("../node/KnotNodeTemplate").default} KnotNodeTemplate + * @typedef {import("../../entity/PinEntity").default} KnotEntity + */ + +/** @extends MinimalPinTemplate */ +class KnotPinTemplate extends MinimalPinTemplate { + + render() { + return this.element.isOutput() ? super.render() : y`` + } + + getOppositePin() { + const nodeTemplate = /** @type {KnotNodeTemplate} */(this.element.nodeElement.template); + return this.element.isOutput() ? nodeTemplate.inputPin : nodeTemplate.outputPin + } + + getLinkLocation() { + const rect = ( + this.element.isInput() + ? /** @type {KnotNodeTemplate} */(this.element.nodeElement.template).outputPin.template + : this + ) + .iconElement.getBoundingClientRect(); + const boundingLocation = [this.element.isInput() ? rect.left : rect.right, (rect.top + rect.bottom) / 2]; + const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); + return this.blueprint.compensateTranslation(location[0], location[1]) + } +} + +/** + * @typedef {import("../../element/NodeElement").default} NodeElement + * @typedef {import("../../element/PinElement").default} PinElement + * @typedef {import("../../element/PinElement").PinElementConstructor} PinElementConstructor + */ + +class KnotNodeTemplate extends NodeTemplate { + + static #traversedPin = new Set() + + /** @type {Boolean?} */ + #chainDirection = null // The node is part of a chain connected to an input or output pin + + /** @type {PinElement} */ + #inputPin + get inputPin() { + return this.#inputPin + } + + /** @type {PinElement} */ + #outputPin + get outputPin() { + return this.#outputPin + } + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + this.element.classList.add("ueb-node-style-minimal"); + } + + /** @param {PinElement} startingPin */ + findDirectionaPin(startingPin) { + if ( + startingPin.nodeElement.getType() !== Configuration.nodeType.knot + || KnotNodeTemplate.#traversedPin.has(startingPin) + ) { + KnotNodeTemplate.#traversedPin.clear(); + return true + } + KnotNodeTemplate.#traversedPin.add(startingPin); + for (let pin of startingPin.getLinks().map(l => this.blueprint.getPin(l))) { + if (this.findDirectionaPin(pin)) { + return true + } + } + return false + } + + render() { + return y` +
+ ` + } + + setupPins() { + this.element.getPinElements().forEach( + p => /** @type {HTMLElement} */(this.element.querySelector(".ueb-node-border")).appendChild(p) + ); + } + + /** + * @param {NodeElement} node + * @returns {NodeListOf} + */ + getPinElements(node) { + return node.querySelectorAll("ueb-pin") + } + + createPinElements() { + const entities = this.element.getPinEntities().filter(v => !v.isHidden()); + const inputEntity = entities[entities[0].isInput() ? 0 : 1]; + const outputEntity = entities[entities[0].isOutput() ? 0 : 1]; + const pinElementConstructor = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")); + let result = [ + this.#inputPin = pinElementConstructor.newObject(inputEntity, new KnotPinTemplate(), this.element), + this.#outputPin = pinElementConstructor.newObject(outputEntity, new KnotPinTemplate(), this.element), + ]; + return result + } + + linksChanged() { + + } +} + +/** @typedef {import("../../element/NodeElement").default} NodeElement */ + +class VariableAccessNodeTemplate extends VariableManagementNodeTemplate { + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + if (element.getType() === Configuration.nodeType.variableGet) { + this.element.classList.add("ueb-node-style-getter"); + } else if (element.getType() === Configuration.nodeType.variableSet) { + this.element.classList.add("ueb-node-style-setter"); + } + } + + setupPins() { + super.setupPins(); + let outputPin = this.element.getPinElements().find(p => !p.entity.isHidden() && !p.entity.isExecution()); + this.element.style.setProperty("--ueb-node-color", outputPin.getColor().cssText); + } +} + +/** + * @typedef {import("./IDraggableElement").DragEvent} DragEvent + * @typedef {import("./IElement").default} IElement + * @typedef {import("./PinElement").default} PinElement + * @typedef {typeof NodeElement} NodeElementConstructor + */ + +/** @extends {ISelectableDraggableElement} */ +class NodeElement extends ISelectableDraggableElement { + + static properties = { + ...ISelectableDraggableElement.properties, + typePath: { + type: String, + attribute: "data-type", + reflect: true, + }, + nodeName: { + type: String, + attribute: "data-name", + reflect: true, + }, + advancedPinDisplay: { + type: String, + attribute: "data-advanced-display", + converter: IdentifierEntity.attributeConverter, + reflect: true, + }, + enabledState: { + type: String, + attribute: "data-enabled-state", + reflect: true, + }, + nodeDisplayName: { + type: String, + attribute: false, + }, + pureFunction: { + type: Boolean, + converter: Utility.booleanConverter, + attribute: "data-pure-function", + reflect: true, + }, + } + static dragEventName = Configuration.nodeDragEventName + static dragGeneralEventName = Configuration.nodeDragGeneralEventName + + get blueprint() { + return super.blueprint + } + set blueprint(v) { + super.blueprint = v; + this.#pins.forEach(p => p.blueprint = v); + } + + /** @type {HTMLElement} */ + #nodeNameElement + get nodeNameElement() { + return this.#nodeNameElement + } + set nodeNameElement(value) { + this.#nodeNameElement = value; + } + + /** @type {PinElement[]} */ + #pins = [] + /** @type {NodeElement[]} */ + boundComments = [] + #commentDragged = false + /** @param {DragEvent} e */ + #commentDragHandler = e => { + // If selected, it will already drag, also must check if under nested comments, it must drag just once + if (!this.selected && !this.#commentDragged) { + this.#commentDragged = true; + this.addNextUpdatedCallbacks(() => this.#commentDragged = false); + this.addLocation(...e.detail.value); + } + } + + /** + * @param {ObjectEntity} nodeEntity + * @return {new () => NodeTemplate} + */ + static getTypeTemplate(nodeEntity) { + if ( + nodeEntity.getClass() === Configuration.nodeType.callFunction + || nodeEntity.getClass() === Configuration.nodeType.commutativeAssociativeBinaryOperator + || nodeEntity.getClass() === Configuration.nodeType.callArrayFunction + ) { + const memberParent = nodeEntity.FunctionReference.MemberParent?.path ?? ""; + if ( + memberParent === "/Script/Engine.KismetMathLibrary" + || memberParent === "/Script/Engine.KismetArrayLibrary" + ) { + if (nodeEntity.FunctionReference.MemberName?.startsWith("Conv_")) { + return VariableConversionNodeTemplate + } + if (nodeEntity.FunctionReference.MemberName?.startsWith("Percent_")) { + return VariableOperationNodeTemplate + } + switch (nodeEntity.FunctionReference.MemberName) { + case "Array_Add": + case "Array_Identical": + case "Abs": + case "Array_Add": + case "BMax": + case "BMin": + case "Exp": + case "FMax": + case "FMin": + case "Max": + case "MaxInt64": + case "Min": + case "MinInt64": + return VariableOperationNodeTemplate + } + } + if (memberParent === "/Script/Engine.BlueprintSetLibrary") { + return VariableOperationNodeTemplate + } + if (memberParent === "/Script/Engine.BlueprintMapLibrary") { + return VariableOperationNodeTemplate + } + } + switch (nodeEntity.getClass()) { + case Configuration.nodeType.comment: + return CommentNodeTemplate + case Configuration.nodeType.event: + case Configuration.nodeType.customEvent: + return EventNodeTemplate + case Configuration.nodeType.promotableOperator: + return VariableOperationNodeTemplate + case Configuration.nodeType.knot: return KnotNodeTemplate + case Configuration.nodeType.variableGet: return VariableAccessNodeTemplate + case Configuration.nodeType.variableSet: return VariableAccessNodeTemplate + } + if (nodeEntity.getDelegatePin()) { + return EventNodeTemplate + } + return NodeTemplate + } + + /** @param {String} str */ + static fromSerializedObject(str) { + str = str.trim(); + let entity = SerializerFactory.getSerializer(ObjectEntity).deserialize(str); + return NodeElement.newObject(/** @type {ObjectEntity} */(entity)) + } + + /** + * @param {ObjectEntity} entity + * @param {NodeTemplate} template + */ + static newObject(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { + const result = new NodeElement(); + result.initialize(entity, template); + return result + } + + initialize(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { + super.initialize(entity, template); + this.#pins = this.template.createPinElements(); + this.typePath = this.entity.getType(); + this.nodeName = this.entity.getObjectName(); + this.advancedPinDisplay = this.entity.AdvancedPinDisplay?.toString(); + this.enabledState = this.entity.EnabledState; + this.nodeDisplayName = this.getNodeDisplayName(); + this.pureFunction = this.entity.bIsPureFunc; + this.dragLinkObjects = []; + super.setLocation(this.entity.getNodePosX(), this.entity.getNodePosY()); + if (this.entity.NodeWidth && this.entity.NodeHeight) { + this.sizeX = this.entity.NodeWidth.value; + this.sizeY = this.entity.NodeHeight.value; + } else { + this.updateComplete.then(() => this.computeSizes()); + } + } + + getUpdateComplete() { + return Promise.all([ + super.getUpdateComplete(), + ...this.getPinElements().map(pin => pin.updateComplete) + ]).then(() => true) + } + + /** @param {NodeElement} commentNode */ + bindToComment(commentNode) { + if (commentNode != this && !this.boundComments.includes(commentNode)) { + commentNode.addEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); + this.boundComments.push(commentNode); + } + } + + /** @param {NodeElement} commentNode */ + unbindFromComment(commentNode) { + const commentIndex = this.boundComments.indexOf(commentNode); + if (commentIndex >= 0) { + commentNode.removeEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); + this.boundComments[commentIndex] = this.boundComments[this.boundComments.length - 1]; + this.boundComments.pop(); + } + } + + /** @param {NodeElement} commentNode */ + isInsideComment(commentNode) { + return this.topBoundary() >= commentNode.topBoundary() + && this.rightBoundary() <= commentNode.rightBoundary() + && this.bottomBoundary() <= commentNode.bottomBoundary() + && this.leftBoundary() >= commentNode.leftBoundary() + } + + getType() { + return this.entity.getType() + } + + getNodeName() { + return this.entity.getObjectName() + } + + getNodeDisplayName() { + return this.entity.nodeDisplayName() + } + + /** @param {Number} value */ + setNodeWidth(value) { + this.entity.setNodeWidth(value); + this.sizeX = value; + this.acknowledgeReflow(); + } + + /** @param {Number} value */ + setNodeHeight(value) { + this.entity.setNodeHeight(value); + this.sizeY = value; + this.acknowledgeReflow(); + } + + /** @param {IElement[]} nodesWhitelist */ + sanitizeLinks(nodesWhitelist = []) { + this.getPinElements().forEach(pin => pin.sanitizeLinks(nodesWhitelist)); + } + + /** @param {String} name */ + rename(name) { + if (this.entity.Name == name) { + return false + } + for (let sourcePinElement of this.getPinElements()) { + for (let targetPinReference of sourcePinElement.getLinks()) { + this.blueprint.getPin(targetPinReference).redirectLink(sourcePinElement, new PinReferenceEntity({ + objectName: name, + pinGuid: sourcePinElement.entity.PinId, + })); + } + } + this.entity.Name = name; + this.nodeName = this.entity.Name; + } + + getPinElements() { + return this.#pins + } + + /** @returns {PinEntity[]} */ + getPinEntities() { + return this.entity.CustomProperties.filter(v => v instanceof PinEntity) + } + + setLocation(x = 0, y = 0, acknowledge = true) { + this.entity.setNodePosX(x); + this.entity.setNodePosY(y); + super.setLocation(x, y, acknowledge); + } + + acknowledgeReflow() { + this.requestUpdate(); + this.updateComplete.then(() => this.computeSizes()); + let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName); + this.dispatchEvent(reflowEvent); + } + + setShowAdvancedPinDisplay(value) { + this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden"); + this.advancedPinDisplay = this.entity.AdvancedPinDisplay; + } + + toggleShowAdvancedPinDisplay() { + this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay?.toString() != "Shown"); + } +} + /** * @typedef {import("./element/PinElement").default} PinElement * @typedef {import("./entity/GuidEntity").default} GuidEntity @@ -7856,65 +7856,65 @@ class Blueprint extends IElement { } } -customElements.define("ueb-blueprint", Blueprint); - -/** - * @typedef {import("../element/IDraggableElement").default} IDraggableElement - * @typedef {import("lit").PropertyValues} PropertyValues - */ - -/** - * @template {IDraggableElement} T - * @extends {IDraggableTemplate} - */ -class IDraggableControlTemplate extends IDraggableTemplate { - - /** @type {(x: Number, y: Number) => void} */ - #locationChangeCallback - get locationChangeCallback() { - return this.#locationChangeCallback - } - set locationChangeCallback(callback) { - this.#locationChangeCallback = callback; - } - - movementSpace - movementSpaceSize = [0, 0] - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.movementSpace = this.element.parentElement; - } - - setup() { - super.setup(); - const bounding = this.movementSpace.getBoundingClientRect(); - this.movementSpaceSize = [bounding.width, bounding.height]; - } - - createDraggableObject() { - return new MouseMoveDraggable(this.element, this.blueprint, { - draggableElement: this.movementSpace, - ignoreTranslateCompensate: true, - moveEverywhere: true, - movementSpace: this.movementSpace, - repositionOnClick: true, - stepSize: 1, - }) - } - - /** - * @param {Number} x - * @param {Number} y - * @returns {[Number, Number]} - */ - adjustLocation(x, y) { - this.locationChangeCallback?.(x, y); - return [x, y] - } -} - +customElements.define("ueb-blueprint", Blueprint); + +/** + * @typedef {import("../element/IDraggableElement").default} IDraggableElement + * @typedef {import("lit").PropertyValues} PropertyValues + */ + +/** + * @template {IDraggableElement} T + * @extends {IDraggableTemplate} + */ +class IDraggableControlTemplate extends IDraggableTemplate { + + /** @type {(x: Number, y: Number) => void} */ + #locationChangeCallback + get locationChangeCallback() { + return this.#locationChangeCallback + } + set locationChangeCallback(callback) { + this.#locationChangeCallback = callback; + } + + movementSpace + movementSpaceSize = [0, 0] + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.movementSpace = this.element.parentElement; + } + + setup() { + super.setup(); + const bounding = this.movementSpace.getBoundingClientRect(); + this.movementSpaceSize = [bounding.width, bounding.height]; + } + + createDraggableObject() { + return new MouseMoveDraggable(this.element, this.blueprint, { + draggableElement: this.movementSpace, + ignoreTranslateCompensate: true, + moveEverywhere: true, + movementSpace: this.movementSpace, + repositionOnClick: true, + stepSize: 1, + }) + } + + /** + * @param {Number} x + * @param {Number} y + * @returns {[Number, Number]} + */ + adjustLocation(x, y) { + this.locationChangeCallback?.(x, y); + return [x, y] + } +} + /** @typedef {import("../element/ColorHandlerElement").default} ColorHandlerElement */ /** @extends {IDraggableControlTemplate} */ @@ -7936,55 +7936,55 @@ class ColorHandlerTemplate extends IDraggableControlTemplate { y = Math.round(-y + radius); return [x, y] } -} - -/** - * @typedef {import("../element/WindowElement").default} WindowElement - * @typedef {import("../entity/IEntity").default} IEntity - * @typedef {import("../template/IDraggableControlTemplate").default} IDraggableControlTemplate - */ - -/** - * @template {IEntity} T - * @template {IDraggableControlTemplate} U - * @extends {IDraggableElement} - */ -class IDraggableControlElement extends IDraggableElement { - - /** @type {WindowElement} */ - windowElement - - setup() { - super.setup(); - this.windowElement = this.closest("ueb-window"); - } - - /** - * @param {Number} x - * @param {Number} y - */ - setLocation(x, y) { - super.setLocation(...this.template.adjustLocation(x, y)); - } -} - -/** @extends {IDraggableControlElement} */ -class ColorHandlerElement extends IDraggableControlElement { - - constructor() { - super(); - super.initialize({}, new ColorHandlerTemplate()); - } - - static newObject() { - return new ColorHandlerElement() - } - - initialize() { - // Initialized in the constructor, this method does nothing - } -} - +} + +/** + * @typedef {import("../element/WindowElement").default} WindowElement + * @typedef {import("../entity/IEntity").default} IEntity + * @typedef {import("../template/IDraggableControlTemplate").default} IDraggableControlTemplate + */ + +/** + * @template {IEntity} T + * @template {IDraggableControlTemplate} U + * @extends {IDraggableElement} + */ +class IDraggableControlElement extends IDraggableElement { + + /** @type {WindowElement} */ + windowElement + + setup() { + super.setup(); + this.windowElement = this.closest("ueb-window"); + } + + /** + * @param {Number} x + * @param {Number} y + */ + setLocation(x, y) { + super.setLocation(...this.template.adjustLocation(x, y)); + } +} + +/** @extends {IDraggableControlElement} */ +class ColorHandlerElement extends IDraggableControlElement { + + constructor() { + super(); + super.initialize({}, new ColorHandlerTemplate()); + } + + static newObject() { + return new ColorHandlerElement() + } + + initialize() { + // Initialized in the constructor, this method does nothing + } +} + /** @typedef {import("../element/ColorHandlerElement").default} ColorHandlerElement */ /** @extends {IDraggableControlTemplate} */ @@ -8001,416 +8001,416 @@ class ColorSliderTemplate extends IDraggableControlTemplate { this.locationChangeCallback?.(x / this.movementSpaceSize[0], 1 - y / this.movementSpaceSize[1]); return [x, y] } -} - -/** @extends {IDraggableControlElement} */ -class ColorSliderElement extends IDraggableControlElement { - - constructor() { - super(); - super.initialize({}, new ColorSliderTemplate()); - } - - static newObject() { - return new ColorSliderElement() - } - - initialize() { - // Initialized in the constructor, this method does nothing - } -} - -/** @typedef {import ("../../element/InputElement").default} InputElement */ - -/** @extends {ITemplate} */ -class InputTemplate extends ITemplate { - - #focusHandler = () => { - this.blueprint.acknowledgeEditText(true); - if (this.element.selectOnFocus) { - getSelection().selectAllChildren(this.element); - } - } - - #focusoutHandler = () => { - this.blueprint.acknowledgeEditText(false); - getSelection().removeAllRanges(); // Deselect eventually selected text inside the input - } - - /** @param {InputEvent} e */ - #inputSingleLineHandler = e => - /** @type {HTMLElement} */(e.target).querySelectorAll("br").forEach(br => br.remove()) - - /** @param {KeyboardEvent} e */ - #onKeydownBlurOnEnterHandler = e => { - if (e.code == "Enter" && !e.shiftKey) { - /** @type {HTMLElement} */(e.target).blur(); - } - } - - /** @param {InputElement} element */ - initialize(element) { - super.initialize(element); - this.element.classList.add("ueb-pin-input-content"); - this.element.setAttribute("role", "textbox"); - this.element.contentEditable = "true"; - } - - setup() { - super.setup(); - this.element.addEventListener("focus", this.#focusHandler); - this.element.addEventListener("focusout", this.#focusoutHandler); - if (this.element.singleLine) { - this.element.addEventListener("input", this.#inputSingleLineHandler); - } - if (this.element.blurOnEnter) { - this.element.addEventListener("keydown", this.#onKeydownBlurOnEnterHandler); - } - } - - cleanup() { - super.cleanup(); - this.element.removeEventListener("focus", this.#focusHandler); - this.element.removeEventListener("focusout", this.#focusoutHandler); - this.element.removeEventListener("input", this.#inputSingleLineHandler); - this.element.removeEventListener("keydown", this.#onKeydownBlurOnEnterHandler); - } -} - -class InputElement extends IElement { - - static properties = { - ...super.properties, - singleLine: { - type: Boolean, - attribute: "data-single-line", - converter: Utility.booleanConverter, - reflect: true, - }, - selectOnFocus: { - type: Boolean, - attribute: "data-select-focus", - converter: Utility.booleanConverter, - reflect: true, - }, - blurOnEnter: { - type: Boolean, - attribute: "data-blur-enter", - converter: Utility.booleanConverter, - reflect: true, - }, - } - - constructor() { - super(); - this.singleLine = false; - this.selectOnFocus = true; - this.blurOnEnter = true; - super.initialize({}, new InputTemplate()); - } - - static newObject() { - return new InputElement() - } - - initialize() { - // Initialized in the constructor, this method does nothing - } -} - -/** - * @typedef {import("../../element/IDraggableElement").default} IDraggableElement - */ - -/** -* @template {IDraggableElement} T -* @extends {IMouseClickDrag} -*/ -class MouseIgnore extends IMouseClickDrag { - - constructor(target, blueprint, options = {}) { - options.consumeEvent = true; - super(target, blueprint, options); - } -} - -/** @typedef {import("lit").PropertyValues} PropertyValues */ - -/** @extends PinTemplate */ -class BoolPinTemplate extends PinTemplate { - - /** @type {HTMLInputElement?} */ - #input - - #onChangeHandler = _ => this.element.setDefaultValue(this.#input.checked) - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.#input = this.element.querySelector(".ueb-pin-input"); - } - - setup() { - super.setup(); - this.#input?.addEventListener("change", this.#onChangeHandler); - } - - cleanup() { - super.cleanup(); - this.#input?.removeEventListener("change", this.#onChangeHandler); - } - - createInputObjects() { - return [ - ...super.createInputObjects(), - new MouseIgnore(this.#input, this.blueprint), - ] - } - - renderInput() { - return y` - - ` - } -} - -/** @typedef {import("../../element/PinElement").default} PinElement */ - -class ExecPinTemplate extends PinTemplate { - - renderIcon() { - return SVGIcon.execPin - } - - renderName() { - let pinName = this.element.entity.PinName; - if (this.element.entity.PinFriendlyName) { - pinName = this.element.entity.PinFriendlyName.toString(); - } else if (pinName === "execute" || pinName === "then") { - return y`` - } - return y`${Utility.formatStringName(pinName)}` - } -} - -/** @typedef {import("lit").PropertyValues} PropertyValues */ - -/** - * @template T - * @extends PinTemplate - */ -class IInputPinTemplate extends PinTemplate { - - static singleLineInput = false - static selectOnFocus = true - - /** @type {HTMLElement[]} */ - #inputContentElements - get inputContentElements() { - return this.#inputContentElements - } - - /** @param {String} value */ - static stringFromInputToUE(value) { - return value - .replace(/(?=\n\s*)\n$/, "") // Remove trailing double newline - .replaceAll("\n", "\\r\n") // Replace newline with \r\n (default newline in UE) - } - - /** @param {String} value */ - static stringFromUEToInput(value) { - return value - .replaceAll(/(?:\r|(?<=(?:^|[^\\])(?:\\\\)*)\\r)(?=\n)/g, "") // Remove \r leftover from \r\n - .replace(/(?<=\n\s*)$/, "\n") // Put back trailing double newline - } - - #onFocusOutHandler = () => this.setInputs(this.getInputs(), true) - /** @param {InputEvent} event */ - #onInputCheckWrapHandler = event => this.#updateWrapClass(/** @type {HTMLElement} */(event.target)) - - /** @param {HTMLElement} inputElement*/ - #updateWrapClass(inputElement) { - const width = this.blueprint.scaleCorrect(inputElement.getBoundingClientRect().width) + this.nameWidth; - const inputWrapped = this.element.classList.contains("ueb-pin-input-wrap"); - if (!inputWrapped && width > Configuration.pinInputWrapWidth) { - this.element.classList.add("ueb-pin-input-wrap"); - } else if (inputWrapped && width <= Configuration.pinInputWrapWidth) { - this.element.classList.remove("ueb-pin-input-wrap"); - } - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.#inputContentElements = /** @type {HTMLElement[]} */([...this.element.querySelectorAll("ueb-input")]); - if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) { - this.nameWidth = this.blueprint.scaleCorrect( - this.element.querySelector(".ueb-pin-name").getBoundingClientRect().width - ); - this.inputContentElements.forEach(inputElement => this.#updateWrapClass(inputElement)); - } - } - - setup() { - super.setup(); - this.#inputContentElements.forEach(element => { - element.addEventListener("focusout", this.#onFocusOutHandler); - if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) { - element.addEventListener("input", this.#onInputCheckWrapHandler); - } - }); - } - - cleanup() { - super.cleanup(); - this.#inputContentElements.forEach(element => { - element.removeEventListener("focusout", this.#onFocusOutHandler); - element.removeEventListener("input", this.#onInputCheckWrapHandler); - }); - } - - createInputObjects() { - return [ - ...super.createInputObjects(), - ...this.#inputContentElements.map(elem => new MouseIgnore(elem, this.blueprint)), - ] - } - - getInput() { - return this.getInputs().reduce((acc, cur) => acc + cur, "") - } - - getInputs() { - return this.#inputContentElements.map(element => - // Faster than innerText which causes reflow - Utility.clearHTMLWhitespace(element.innerHTML) - ) - } - - /** @param {String[]} values */ - setInputs(values = [], updateDefaultValue = true) { - this.#inputContentElements.forEach(/** @type {typeof IInputPinTemplate } */(this.constructor).singleLineInput - ? (elem, i) => elem.innerText = values[i] - : (elem, i) => elem.innerText = values[i].replaceAll("\n", "") - ); - if (updateDefaultValue) { - this.setDefaultValue(values.map(v => IInputPinTemplate.stringFromInputToUE(v)), values); - } - this.element.addNextUpdatedCallbacks(() => this.element.nodeElement.acknowledgeReflow()); - } - - setDefaultValue(values = [], rawValues = values) { - this.element.setDefaultValue( - // @ts-expect-error - values.join("") - ); - } - - renderInput() { - const singleLine = /** @type {typeof IInputPinTemplate} */(this.constructor).singleLineInput; - const selectOnFocus = /** @type {typeof IInputPinTemplate} */(this.constructor).selectOnFocus; - return y` -
- - -
- ` - } -} - -/** - * @template T - * @extends IInputPinTemplate - */ -class INumericPinTemplate extends IInputPinTemplate { - - static singleLineInput = true - - /** @param {String[]} values */ - setInputs(values = [], updateDefaultValue = false) { - if (!values || values.length == 0) { - values = [this.getInput()]; - } - super.setInputs(values, false); - if (updateDefaultValue) { - let parsedValues = []; - for (const value of values) { - let num = parseFloat(value); - if (isNaN(num)) { - num = 0; - updateDefaultValue = false; - } - parsedValues.push(num); - } - this.setDefaultValue(parsedValues, values); - } - } - - /** - * @param {Number[]} values - * @param {String[]} rawValues - */ - setDefaultValue(values = [], rawValues) { - this.element.setDefaultValue(/** @type {T} */(values[0])); - } -} - -/** @typedef {import("../../entity/IntegerEntity").default} IntegerEntity */ - -/** @extends INumericPinTemplate */ -class IntPinTemplate extends INumericPinTemplate { - - setDefaultValue(values = [], rawValues = values) { - const integer = this.element.getDefaultValue(true); - integer.value = values[0]; - this.inputContentElements[0].innerText = this.element.getDefaultValue()?.toString(); // needed - this.element.requestUpdate(); - } - - renderInput() { - return y` -
- - -
- ` - } -} - -/** @typedef {import("../../entity/IntegerEntity").default} IntegerEntity */ - -class Int64PinTemplate extends IntPinTemplate { - - /** @param {String[]} values */ - setInputs(values = [], updateDefaultValue = false) { - if (!values || values.length == 0) { - values = [this.getInput()]; - } - super.setInputs(values, false); - if (updateDefaultValue) { - if (!values[0].match(/[\-\+]?[0-9]+/)) { - return - } - const parsedValues = [BigInt(values[0])]; - this.setDefaultValue(parsedValues, values); - } - } -} - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i$1{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i;}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}} - -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const i=e(class extends i$1{constructor(t$1){var e;if(super(t$1),t$1.type!==t.ATTRIBUTE||"style"!==t$1.name||(null===(e=t$1.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,r)=>{const s=t[r];return null==s?e:e+`${r=r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`}),"")}update(e,[r]){const{style:s}=e.element;if(void 0===this.vt){this.vt=new Set;for(const t in r)this.vt.add(t);return this.render(r)}this.vt.forEach((t=>{null==r[t]&&(this.vt.delete(t),t.includes("-")?s.removeProperty(t):s[t]="");}));for(const t in r){const e=r[t];null!=e&&(this.vt.add(t),t.includes("-")?s.setProperty(t,e):s[t]=e);}return x}}); - +} + +/** @extends {IDraggableControlElement} */ +class ColorSliderElement extends IDraggableControlElement { + + constructor() { + super(); + super.initialize({}, new ColorSliderTemplate()); + } + + static newObject() { + return new ColorSliderElement() + } + + initialize() { + // Initialized in the constructor, this method does nothing + } +} + +/** @typedef {import ("../../element/InputElement").default} InputElement */ + +/** @extends {ITemplate} */ +class InputTemplate extends ITemplate { + + #focusHandler = () => { + this.blueprint.acknowledgeEditText(true); + if (this.element.selectOnFocus) { + getSelection().selectAllChildren(this.element); + } + } + + #focusoutHandler = () => { + this.blueprint.acknowledgeEditText(false); + getSelection().removeAllRanges(); // Deselect eventually selected text inside the input + } + + /** @param {InputEvent} e */ + #inputSingleLineHandler = e => + /** @type {HTMLElement} */(e.target).querySelectorAll("br").forEach(br => br.remove()) + + /** @param {KeyboardEvent} e */ + #onKeydownBlurOnEnterHandler = e => { + if (e.code == "Enter" && !e.shiftKey) { + /** @type {HTMLElement} */(e.target).blur(); + } + } + + /** @param {InputElement} element */ + initialize(element) { + super.initialize(element); + this.element.classList.add("ueb-pin-input-content"); + this.element.setAttribute("role", "textbox"); + this.element.contentEditable = "true"; + } + + setup() { + super.setup(); + this.element.addEventListener("focus", this.#focusHandler); + this.element.addEventListener("focusout", this.#focusoutHandler); + if (this.element.singleLine) { + this.element.addEventListener("input", this.#inputSingleLineHandler); + } + if (this.element.blurOnEnter) { + this.element.addEventListener("keydown", this.#onKeydownBlurOnEnterHandler); + } + } + + cleanup() { + super.cleanup(); + this.element.removeEventListener("focus", this.#focusHandler); + this.element.removeEventListener("focusout", this.#focusoutHandler); + this.element.removeEventListener("input", this.#inputSingleLineHandler); + this.element.removeEventListener("keydown", this.#onKeydownBlurOnEnterHandler); + } +} + +class InputElement extends IElement { + + static properties = { + ...super.properties, + singleLine: { + type: Boolean, + attribute: "data-single-line", + converter: Utility.booleanConverter, + reflect: true, + }, + selectOnFocus: { + type: Boolean, + attribute: "data-select-focus", + converter: Utility.booleanConverter, + reflect: true, + }, + blurOnEnter: { + type: Boolean, + attribute: "data-blur-enter", + converter: Utility.booleanConverter, + reflect: true, + }, + } + + constructor() { + super(); + this.singleLine = false; + this.selectOnFocus = true; + this.blurOnEnter = true; + super.initialize({}, new InputTemplate()); + } + + static newObject() { + return new InputElement() + } + + initialize() { + // Initialized in the constructor, this method does nothing + } +} + +/** + * @typedef {import("../../element/IDraggableElement").default} IDraggableElement + */ + +/** +* @template {IDraggableElement} T +* @extends {IMouseClickDrag} +*/ +class MouseIgnore extends IMouseClickDrag { + + constructor(target, blueprint, options = {}) { + options.consumeEvent = true; + super(target, blueprint, options); + } +} + +/** @typedef {import("lit").PropertyValues} PropertyValues */ + +/** @extends PinTemplate */ +class BoolPinTemplate extends PinTemplate { + + /** @type {HTMLInputElement?} */ + #input + + #onChangeHandler = _ => this.element.setDefaultValue(this.#input.checked) + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.#input = this.element.querySelector(".ueb-pin-input"); + } + + setup() { + super.setup(); + this.#input?.addEventListener("change", this.#onChangeHandler); + } + + cleanup() { + super.cleanup(); + this.#input?.removeEventListener("change", this.#onChangeHandler); + } + + createInputObjects() { + return [ + ...super.createInputObjects(), + new MouseIgnore(this.#input, this.blueprint), + ] + } + + renderInput() { + return y` + + ` + } +} + +/** @typedef {import("../../element/PinElement").default} PinElement */ + +class ExecPinTemplate extends PinTemplate { + + renderIcon() { + return SVGIcon.execPin + } + + renderName() { + let pinName = this.element.entity.PinName; + if (this.element.entity.PinFriendlyName) { + pinName = this.element.entity.PinFriendlyName.toString(); + } else if (pinName === "execute" || pinName === "then") { + return y`` + } + return y`${Utility.formatStringName(pinName)}` + } +} + +/** @typedef {import("lit").PropertyValues} PropertyValues */ + +/** + * @template T + * @extends PinTemplate + */ +class IInputPinTemplate extends PinTemplate { + + static singleLineInput = false + static selectOnFocus = true + + /** @type {HTMLElement[]} */ + #inputContentElements + get inputContentElements() { + return this.#inputContentElements + } + + /** @param {String} value */ + static stringFromInputToUE(value) { + return value + .replace(/(?=\n\s*)\n$/, "") // Remove trailing double newline + .replaceAll("\n", "\\r\n") // Replace newline with \r\n (default newline in UE) + } + + /** @param {String} value */ + static stringFromUEToInput(value) { + return value + .replaceAll(/(?:\r|(?<=(?:^|[^\\])(?:\\\\)*)\\r)(?=\n)/g, "") // Remove \r leftover from \r\n + .replace(/(?<=\n\s*)$/, "\n") // Put back trailing double newline + } + + #onFocusOutHandler = () => this.setInputs(this.getInputs(), true) + /** @param {InputEvent} event */ + #onInputCheckWrapHandler = event => this.#updateWrapClass(/** @type {HTMLElement} */(event.target)) + + /** @param {HTMLElement} inputElement*/ + #updateWrapClass(inputElement) { + const width = this.blueprint.scaleCorrect(inputElement.getBoundingClientRect().width) + this.nameWidth; + const inputWrapped = this.element.classList.contains("ueb-pin-input-wrap"); + if (!inputWrapped && width > Configuration.pinInputWrapWidth) { + this.element.classList.add("ueb-pin-input-wrap"); + } else if (inputWrapped && width <= Configuration.pinInputWrapWidth) { + this.element.classList.remove("ueb-pin-input-wrap"); + } + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.#inputContentElements = /** @type {HTMLElement[]} */([...this.element.querySelectorAll("ueb-input")]); + if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) { + this.nameWidth = this.blueprint.scaleCorrect( + this.element.querySelector(".ueb-pin-name").getBoundingClientRect().width + ); + this.inputContentElements.forEach(inputElement => this.#updateWrapClass(inputElement)); + } + } + + setup() { + super.setup(); + this.#inputContentElements.forEach(element => { + element.addEventListener("focusout", this.#onFocusOutHandler); + if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) { + element.addEventListener("input", this.#onInputCheckWrapHandler); + } + }); + } + + cleanup() { + super.cleanup(); + this.#inputContentElements.forEach(element => { + element.removeEventListener("focusout", this.#onFocusOutHandler); + element.removeEventListener("input", this.#onInputCheckWrapHandler); + }); + } + + createInputObjects() { + return [ + ...super.createInputObjects(), + ...this.#inputContentElements.map(elem => new MouseIgnore(elem, this.blueprint)), + ] + } + + getInput() { + return this.getInputs().reduce((acc, cur) => acc + cur, "") + } + + getInputs() { + return this.#inputContentElements.map(element => + // Faster than innerText which causes reflow + Utility.clearHTMLWhitespace(element.innerHTML) + ) + } + + /** @param {String[]} values */ + setInputs(values = [], updateDefaultValue = true) { + this.#inputContentElements.forEach(/** @type {typeof IInputPinTemplate } */(this.constructor).singleLineInput + ? (elem, i) => elem.innerText = values[i] + : (elem, i) => elem.innerText = values[i].replaceAll("\n", "") + ); + if (updateDefaultValue) { + this.setDefaultValue(values.map(v => IInputPinTemplate.stringFromInputToUE(v)), values); + } + this.element.addNextUpdatedCallbacks(() => this.element.nodeElement.acknowledgeReflow()); + } + + setDefaultValue(values = [], rawValues = values) { + this.element.setDefaultValue( + // @ts-expect-error + values.join("") + ); + } + + renderInput() { + const singleLine = /** @type {typeof IInputPinTemplate} */(this.constructor).singleLineInput; + const selectOnFocus = /** @type {typeof IInputPinTemplate} */(this.constructor).selectOnFocus; + return y` +
+ + +
+ ` + } +} + +/** + * @template T + * @extends IInputPinTemplate + */ +class INumericPinTemplate extends IInputPinTemplate { + + static singleLineInput = true + + /** @param {String[]} values */ + setInputs(values = [], updateDefaultValue = false) { + if (!values || values.length == 0) { + values = [this.getInput()]; + } + super.setInputs(values, false); + if (updateDefaultValue) { + let parsedValues = []; + for (const value of values) { + let num = parseFloat(value); + if (isNaN(num)) { + num = 0; + updateDefaultValue = false; + } + parsedValues.push(num); + } + this.setDefaultValue(parsedValues, values); + } + } + + /** + * @param {Number[]} values + * @param {String[]} rawValues + */ + setDefaultValue(values = [], rawValues) { + this.element.setDefaultValue(/** @type {T} */(values[0])); + } +} + +/** @typedef {import("../../entity/IntegerEntity").default} IntegerEntity */ + +/** @extends INumericPinTemplate */ +class IntPinTemplate extends INumericPinTemplate { + + setDefaultValue(values = [], rawValues = values) { + const integer = this.element.getDefaultValue(true); + integer.value = values[0]; + this.inputContentElements[0].innerText = this.element.getDefaultValue()?.toString(); // needed + this.element.requestUpdate(); + } + + renderInput() { + return y` +
+ + +
+ ` + } +} + +/** @typedef {import("../../entity/IntegerEntity").default} IntegerEntity */ + +class Int64PinTemplate extends IntPinTemplate { + + /** @param {String[]} values */ + setInputs(values = [], updateDefaultValue = false) { + if (!values || values.length == 0) { + values = [this.getInput()]; + } + super.setInputs(values, false); + if (updateDefaultValue) { + if (!values[0].match(/[\-\+]?[0-9]+/)) { + return + } + const parsedValues = [BigInt(values[0])]; + this.setDefaultValue(parsedValues, values); + } + } +} + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e=t=>(...e)=>({_$litDirective$:t,values:e});class i$1{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i;}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}} + +/** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const i=e(class extends i$1{constructor(t$1){var e;if(super(t$1),t$1.type!==t.ATTRIBUTE||"style"!==t$1.name||(null===(e=t$1.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,r)=>{const s=t[r];return null==s?e:e+`${r=r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`}),"")}update(e,[r]){const{style:s}=e.element;if(void 0===this.vt){this.vt=new Set;for(const t in r)this.vt.add(t);return this.render(r)}this.vt.forEach((t=>{null==r[t]&&(this.vt.delete(t),t.includes("-")?s.removeProperty(t):s[t]="");}));for(const t in r){const e=r[t];null!=e&&(this.vt.add(t),t.includes("-")?s.setProperty(t,e):s[t]=e);}return x}}); + /** @typedef {import("../../element/WindowElement").default} WindowElement */ /** @extends {IDraggablePositionedTemplate} */ @@ -8483,8 +8483,8 @@ class WindowTemplate extends IDraggablePositionedTemplate { this.element.dispatchEvent(new CustomEvent(Configuration.windowCancelEventName)); this.element.remove(); } -} - +} + /** * @typedef {import("../../element/WindowElement").default} WindowElement * @typedef {import("lit").PropertyValues} PropertyValues @@ -8838,501 +8838,501 @@ class ColorPickerWindowTemplate extends WindowTemplate { renderWindowName() { return y`${Configuration.colorWindowName}` } -} - -/** - * @typedef {import("../../element/WindowElement").default} WindowElement - * @typedef {import("../../element/WindowElement").WindowElementConstructor} WindowElementConstructor - * @typedef {import("../../entity/LinearColorEntity").default} LinearColorEntity - */ - -/** @extends PinTemplate */ -class LinearColorPinTemplate extends PinTemplate { - - /** @type {WindowElement} */ - #window - - /** @param {MouseEvent} e */ - #launchColorPickerWindow = e => { - e.preventDefault(); - this.blueprint.setFocused(true); - /** @type {WindowElement} */ - this.#window = /** @type {WindowElementConstructor} */(ElementFactory.getConstructor("ueb-window")) - .newObject({ - type: new ColorPickerWindowTemplate(), - windowOptions: { - // The created window will use the following functions to get and set the color - getPinColor: () => this.element.defaultValue, - /** @param {LinearColorEntity} color */ - setPinColor: color => this.element.setDefaultValue(color), - }, - }); - this.blueprint.append(this.#window); - const windowApplyHandler = () => { - this.element.setDefaultValue( - /** @type {ColorPickerWindowTemplate} */(this.#window.template).color - ); - }; - const windowCloseHandler = () => { - this.#window.removeEventListener(Configuration.windowApplyEventName, windowApplyHandler); - this.#window.removeEventListener(Configuration.windowCloseEventName, windowCloseHandler); - this.#window = null; - }; - this.#window.addEventListener(Configuration.windowApplyEventName, windowApplyHandler); - this.#window.addEventListener(Configuration.windowCloseEventName, windowCloseHandler); - } - - renderInput() { - return y` - - - ` - } -} - -/** @typedef {import("../../element/PinElement").default} PinElement */ - -class NamePinTemplate extends IInputPinTemplate { - - static singleLineInput = true -} - -/** - * @template {Number} T - * @extends INumericPinTemplate - */ -class RealPinTemplate extends INumericPinTemplate { - - setDefaultValue(values = [], rawValues = values) { - this.element.setDefaultValue(values[0]); - } - - renderInput() { - return y` -
- - -
- ` - } -} - -class ReferencePinTemplate extends PinTemplate { - - renderIcon() { - return SVGIcon.referencePin - } -} - -/** @typedef {import("../../entity/RotatorEntity").default} Rotator */ - -/** @extends INumericPinTemplate */ -class RotatorPinTemplate extends INumericPinTemplate { - - #getR() { - return Utility.minDecimals(this.element.getDefaultValue()?.R ?? 0) - } - - #getP() { - return Utility.minDecimals(this.element.getDefaultValue()?.P ?? 0) - } - - #getY() { - return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) - } - - setDefaultValue(values = [], rawValues = values) { - const rotator = this.element.getDefaultValue(true); - if (!(rotator instanceof RotatorEntity)) { - throw new TypeError("Expected DefaultValue to be a RotatorEntity") - } - rotator.R = values[0]; // Roll - rotator.P = values[1]; // Pitch - rotator.Y = values[2]; // Yaw - this.element.requestUpdate("DefaultValue", rotator); - } - - renderInput() { - return y` -
- X -
- -
- Y -
- -
- Z -
- -
-
- ` - } -} - -/** @extends IInputPinTemplate */ -class StringPinTemplate extends IInputPinTemplate { -} - -/** - * @extends INumericPinTemplate - */ -class VectorInputPinTemplate extends INumericPinTemplate { - - #getX() { - return Utility.minDecimals(this.element.getDefaultValue()?.X ?? 0) - } - - #getY() { - return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) - } - - /** - * @param {Number[]} values - * @param {String[]} rawValues - */ - setDefaultValue(values, rawValues) { - const vector = this.element.getDefaultValue(true); - if (!(vector instanceof Vector2DEntity)) { - throw new TypeError("Expected DefaultValue to be a Vector2DEntity") - } - vector.X = values[0]; - vector.Y = values[1]; - this.element.requestUpdate("DefaultValue", vector); - } - - renderInput() { - return y` -
- X -
- -
- Y -
- -
-
- ` - } -} - -/** - * @extends INumericPinTemplate - */ -class VectorPinTemplate extends INumericPinTemplate { - - #getX() { - return Utility.minDecimals(this.element.getDefaultValue()?.X ?? 0) - } - - #getY() { - return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) - } - - #getZ() { - return Utility.minDecimals(this.element.getDefaultValue()?.Z ?? 0) - } - - /** - * @param {Number[]} values - * @param {String[]} rawValues - */ - setDefaultValue(values, rawValues) { - const vector = this.element.getDefaultValue(true); - if (!(vector instanceof VectorEntity)) { - throw new TypeError("Expected DefaultValue to be a VectorEntity") - } - vector.X = values[0]; - vector.Y = values[1]; - vector.Z = values[2]; - this.element.requestUpdate("DefaultValue", vector); - } - - renderInput() { - return y` -
- X -
- -
- Y -
- -
- Z -
- -
-
- ` - } -} - -/** - * @typedef {import("../entity/IEntity").AnyValue} AnyValue - * @typedef {import("./LinkElement").LinkElementConstructor} LinkElementConstructor - * @typedef {import("./NodeElement").default} NodeElement - * @typedef {import("lit").CSSResult} CSSResult - * @typedef {typeof PinElement} PinElementConstructor - */ - -/** - * @template {AnyValue} T - * @extends {IElement, PinTemplate>} - */ -class PinElement extends IElement { - - static #inputPinTemplates = { - "/Script/CoreUObject.LinearColor": LinearColorPinTemplate, - "/Script/CoreUObject.Rotator": RotatorPinTemplate, - "/Script/CoreUObject.Vector": VectorPinTemplate, - "/Script/CoreUObject.Vector2D": VectorInputPinTemplate, - "bool": BoolPinTemplate, - "byte": IntPinTemplate, - "int": IntPinTemplate, - "int64": Int64PinTemplate, - "MUTABLE_REFERENCE": ReferencePinTemplate, - "name": NamePinTemplate, - "real": RealPinTemplate, - "string": StringPinTemplate, - } - - static properties = { - pinId: { - type: GuidEntity, - converter: { - fromAttribute: (value, type) => value - ? ISerializer.grammar.Guid.parse(value).value - : null, - toAttribute: (value, type) => value?.toString(), - }, - attribute: "data-id", - reflect: true, - }, - pinType: { - type: String, - attribute: "data-type", - reflect: true, - }, - advancedView: { - type: String, - attribute: "data-advanced-view", - reflect: true, - }, - color: { - type: LinearColorEntity, - converter: { - fromAttribute: (value, type) => value - ? ISerializer.grammar.LinearColorFromAnyColor.parse(value).value - : null, - toAttribute: (value, type) => value ? Utility.printLinearColor(value) : null, - }, - attribute: "data-color", - reflect: true, - }, - defaultValue: { - type: String, - attribute: false, - }, - isLinked: { - type: Boolean, - converter: Utility.booleanConverter, - attribute: "data-linked", - reflect: true, - }, - pinDirection: { - type: String, - attribute: "data-direction", - reflect: true, - }, - } - - /** @type {NodeElement} */ - nodeElement - - /** - * @param {PinEntity} pinEntity - * @return {new () => PinTemplate} - */ - static getTypeTemplate(pinEntity) { - if (pinEntity.PinType.bIsReference && !pinEntity.PinType.bIsConst) { - return PinElement.#inputPinTemplates["MUTABLE_REFERENCE"] - } - if (pinEntity.getType() === "exec") { - return ExecPinTemplate - } - let result; - if (pinEntity.isInput()) { - result = PinElement.#inputPinTemplates[pinEntity.getType()]; - } - return result ?? PinTemplate - } - - static newObject( - entity = new PinEntity(), - template = new (PinElement.getTypeTemplate(entity))(), - nodeElement = undefined - ) { - const result = new PinElement(); - result.initialize(entity, template, nodeElement); - return result - } - - initialize( - entity = /** @type {PinEntity} */(new PinEntity()), - template = new (PinElement.getTypeTemplate(entity))(), - nodeElement = undefined - ) { - super.initialize(entity, template); - this.pinId = this.entity.PinId; - this.pinType = this.entity.getType(); - this.advancedView = this.entity.bAdvancedView; - this.defaultValue = this.entity.getDefaultValue(); - this.color = PinElement.properties.color.converter.fromAttribute(this.getColor().toString()); - this.isLinked = false; - this.pinDirection = entity.isInput() ? "input" : entity.isOutput() ? "output" : "hidden"; - this.nodeElement = /** @type {NodeElement} */(nodeElement); - } - - setup() { - super.setup(); - this.nodeElement = this.closest("ueb-node"); - } - - createPinReference() { - return new PinReferenceEntity({ - objectName: this.nodeElement.getNodeName(), - pinGuid: this.getPinId(), - }) - } - - /** @return {GuidEntity} */ - getPinId() { - return this.entity.PinId - } - - /** @returns {String} */ - getPinName() { - return this.entity.PinName - } - - getPinDisplayName() { - return this.entity.getDisplayName() - } - - /** @return {CSSResult} */ - getColor() { - return this.entity.pinColor() - } - - isInput() { - return this.entity.isInput() - } - - isOutput() { - return this.entity.isOutput() - } - - getLinkLocation() { - return this.template.getLinkLocation() - } - - getNodeElement() { - return this.nodeElement - } - - getLinks() { - return this.entity.LinkedTo ?? [] - } - - getDefaultValue(maybeCreate = false) { - return this.defaultValue = this.entity.getDefaultValue(maybeCreate) - } - - /** @param {T} value */ - setDefaultValue(value) { - this.entity.DefaultValue = value; - this.defaultValue = value; - } - - /** @param {IElement[]} nodesWhitelist */ - sanitizeLinks(nodesWhitelist = []) { - this.entity.LinkedTo = this.entity.LinkedTo?.filter(pinReference => { - let pin = this.blueprint.getPin(pinReference); - if (pin) { - if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) { - return false - } - let link = this.blueprint.getLink(this, pin); - if (!link) { - link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) - .newObject(this, pin); - this.blueprint.addGraphElement(link); - } - } - return pin - }); - this.isLinked = this.entity.isLinked(); - } - - /** @param {PinElement} targetPinElement */ - linkTo(targetPinElement) { - const pinReference = this.createPinReference(); - if ( - this.isLinked - && this.isOutput() - && (this.pinType === "exec" || targetPinElement.pinType === "exec") - && !this.getLinks().some(ref => pinReference.equals(ref))) { - this.unlinkFromAll(); - } - if (this.entity.linkTo(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) { - this.isLinked = this.entity.isLinked(); - this.nodeElement?.template.linksChanged(); - } - } - - /** @param {PinElement} targetPinElement */ - unlinkFrom(targetPinElement, removeLink = true) { - if (this.entity.unlinkFrom(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) { - this.isLinked = this.entity.isLinked(); - this.nodeElement?.template.linksChanged(); - if (removeLink) { - this.blueprint.getLink(this, targetPinElement)?.remove(); // Might be called after the link is removed - } - } - } - - unlinkFromAll() { - const isLinked = this.getLinks().length; - this.getLinks().map(ref => this.blueprint.getPin(ref)).forEach(pin => this.unlinkFrom(pin)); - if (isLinked) { - this.nodeElement?.template.linksChanged(); - } - } - - /** - * @param {PinElement} originalPinElement - * @param {PinReferenceEntity} newReference - */ - redirectLink(originalPinElement, newReference) { - const index = this.getLinks().findIndex(pinReference => - pinReference.objectName.toString() == originalPinElement.getNodeElement().getNodeName() - && pinReference.pinGuid.valueOf() == originalPinElement.entity.PinId.valueOf() - ); - if (index >= 0) { - this.entity.LinkedTo[index] = newReference; - return true - } - return false - } -} - +} + +/** + * @typedef {import("../../element/WindowElement").default} WindowElement + * @typedef {import("../../element/WindowElement").WindowElementConstructor} WindowElementConstructor + * @typedef {import("../../entity/LinearColorEntity").default} LinearColorEntity + */ + +/** @extends PinTemplate */ +class LinearColorPinTemplate extends PinTemplate { + + /** @type {WindowElement} */ + #window + + /** @param {MouseEvent} e */ + #launchColorPickerWindow = e => { + e.preventDefault(); + this.blueprint.setFocused(true); + /** @type {WindowElement} */ + this.#window = /** @type {WindowElementConstructor} */(ElementFactory.getConstructor("ueb-window")) + .newObject({ + type: new ColorPickerWindowTemplate(), + windowOptions: { + // The created window will use the following functions to get and set the color + getPinColor: () => this.element.defaultValue, + /** @param {LinearColorEntity} color */ + setPinColor: color => this.element.setDefaultValue(color), + }, + }); + this.blueprint.append(this.#window); + const windowApplyHandler = () => { + this.element.setDefaultValue( + /** @type {ColorPickerWindowTemplate} */(this.#window.template).color + ); + }; + const windowCloseHandler = () => { + this.#window.removeEventListener(Configuration.windowApplyEventName, windowApplyHandler); + this.#window.removeEventListener(Configuration.windowCloseEventName, windowCloseHandler); + this.#window = null; + }; + this.#window.addEventListener(Configuration.windowApplyEventName, windowApplyHandler); + this.#window.addEventListener(Configuration.windowCloseEventName, windowCloseHandler); + } + + renderInput() { + return y` + + + ` + } +} + +/** @typedef {import("../../element/PinElement").default} PinElement */ + +class NamePinTemplate extends IInputPinTemplate { + + static singleLineInput = true +} + +/** + * @template {Number} T + * @extends INumericPinTemplate + */ +class RealPinTemplate extends INumericPinTemplate { + + setDefaultValue(values = [], rawValues = values) { + this.element.setDefaultValue(values[0]); + } + + renderInput() { + return y` +
+ + +
+ ` + } +} + +class ReferencePinTemplate extends PinTemplate { + + renderIcon() { + return SVGIcon.referencePin + } +} + +/** @typedef {import("../../entity/RotatorEntity").default} Rotator */ + +/** @extends INumericPinTemplate */ +class RotatorPinTemplate extends INumericPinTemplate { + + #getR() { + return Utility.minDecimals(this.element.getDefaultValue()?.R ?? 0) + } + + #getP() { + return Utility.minDecimals(this.element.getDefaultValue()?.P ?? 0) + } + + #getY() { + return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) + } + + setDefaultValue(values = [], rawValues = values) { + const rotator = this.element.getDefaultValue(true); + if (!(rotator instanceof RotatorEntity)) { + throw new TypeError("Expected DefaultValue to be a RotatorEntity") + } + rotator.R = values[0]; // Roll + rotator.P = values[1]; // Pitch + rotator.Y = values[2]; // Yaw + this.element.requestUpdate("DefaultValue", rotator); + } + + renderInput() { + return y` +
+ X +
+ +
+ Y +
+ +
+ Z +
+ +
+
+ ` + } +} + +/** @extends IInputPinTemplate */ +class StringPinTemplate extends IInputPinTemplate { +} + +/** + * @extends INumericPinTemplate + */ +class VectorInputPinTemplate extends INumericPinTemplate { + + #getX() { + return Utility.minDecimals(this.element.getDefaultValue()?.X ?? 0) + } + + #getY() { + return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) + } + + /** + * @param {Number[]} values + * @param {String[]} rawValues + */ + setDefaultValue(values, rawValues) { + const vector = this.element.getDefaultValue(true); + if (!(vector instanceof Vector2DEntity)) { + throw new TypeError("Expected DefaultValue to be a Vector2DEntity") + } + vector.X = values[0]; + vector.Y = values[1]; + this.element.requestUpdate("DefaultValue", vector); + } + + renderInput() { + return y` +
+ X +
+ +
+ Y +
+ +
+
+ ` + } +} + +/** + * @extends INumericPinTemplate + */ +class VectorPinTemplate extends INumericPinTemplate { + + #getX() { + return Utility.minDecimals(this.element.getDefaultValue()?.X ?? 0) + } + + #getY() { + return Utility.minDecimals(this.element.getDefaultValue()?.Y ?? 0) + } + + #getZ() { + return Utility.minDecimals(this.element.getDefaultValue()?.Z ?? 0) + } + + /** + * @param {Number[]} values + * @param {String[]} rawValues + */ + setDefaultValue(values, rawValues) { + const vector = this.element.getDefaultValue(true); + if (!(vector instanceof VectorEntity)) { + throw new TypeError("Expected DefaultValue to be a VectorEntity") + } + vector.X = values[0]; + vector.Y = values[1]; + vector.Z = values[2]; + this.element.requestUpdate("DefaultValue", vector); + } + + renderInput() { + return y` +
+ X +
+ +
+ Y +
+ +
+ Z +
+ +
+
+ ` + } +} + +/** + * @typedef {import("../entity/IEntity").AnyValue} AnyValue + * @typedef {import("./LinkElement").LinkElementConstructor} LinkElementConstructor + * @typedef {import("./NodeElement").default} NodeElement + * @typedef {import("lit").CSSResult} CSSResult + * @typedef {typeof PinElement} PinElementConstructor + */ + +/** + * @template {AnyValue} T + * @extends {IElement, PinTemplate>} + */ +class PinElement extends IElement { + + static #inputPinTemplates = { + "/Script/CoreUObject.LinearColor": LinearColorPinTemplate, + "/Script/CoreUObject.Rotator": RotatorPinTemplate, + "/Script/CoreUObject.Vector": VectorPinTemplate, + "/Script/CoreUObject.Vector2D": VectorInputPinTemplate, + "bool": BoolPinTemplate, + "byte": IntPinTemplate, + "int": IntPinTemplate, + "int64": Int64PinTemplate, + "MUTABLE_REFERENCE": ReferencePinTemplate, + "name": NamePinTemplate, + "real": RealPinTemplate, + "string": StringPinTemplate, + } + + static properties = { + pinId: { + type: GuidEntity, + converter: { + fromAttribute: (value, type) => value + ? ISerializer.grammar.Guid.parse(value).value + : null, + toAttribute: (value, type) => value?.toString(), + }, + attribute: "data-id", + reflect: true, + }, + pinType: { + type: String, + attribute: "data-type", + reflect: true, + }, + advancedView: { + type: String, + attribute: "data-advanced-view", + reflect: true, + }, + color: { + type: LinearColorEntity, + converter: { + fromAttribute: (value, type) => value + ? ISerializer.grammar.LinearColorFromAnyColor.parse(value).value + : null, + toAttribute: (value, type) => value ? Utility.printLinearColor(value) : null, + }, + attribute: "data-color", + reflect: true, + }, + defaultValue: { + type: String, + attribute: false, + }, + isLinked: { + type: Boolean, + converter: Utility.booleanConverter, + attribute: "data-linked", + reflect: true, + }, + pinDirection: { + type: String, + attribute: "data-direction", + reflect: true, + }, + } + + /** @type {NodeElement} */ + nodeElement + + /** + * @param {PinEntity} pinEntity + * @return {new () => PinTemplate} + */ + static getTypeTemplate(pinEntity) { + if (pinEntity.PinType.bIsReference && !pinEntity.PinType.bIsConst) { + return PinElement.#inputPinTemplates["MUTABLE_REFERENCE"] + } + if (pinEntity.getType() === "exec") { + return ExecPinTemplate + } + let result; + if (pinEntity.isInput()) { + result = PinElement.#inputPinTemplates[pinEntity.getType()]; + } + return result ?? PinTemplate + } + + static newObject( + entity = new PinEntity(), + template = new (PinElement.getTypeTemplate(entity))(), + nodeElement = undefined + ) { + const result = new PinElement(); + result.initialize(entity, template, nodeElement); + return result + } + + initialize( + entity = /** @type {PinEntity} */(new PinEntity()), + template = new (PinElement.getTypeTemplate(entity))(), + nodeElement = undefined + ) { + super.initialize(entity, template); + this.pinId = this.entity.PinId; + this.pinType = this.entity.getType(); + this.advancedView = this.entity.bAdvancedView; + this.defaultValue = this.entity.getDefaultValue(); + this.color = PinElement.properties.color.converter.fromAttribute(this.getColor().toString()); + this.isLinked = false; + this.pinDirection = entity.isInput() ? "input" : entity.isOutput() ? "output" : "hidden"; + this.nodeElement = /** @type {NodeElement} */(nodeElement); + } + + setup() { + super.setup(); + this.nodeElement = this.closest("ueb-node"); + } + + createPinReference() { + return new PinReferenceEntity({ + objectName: this.nodeElement.getNodeName(), + pinGuid: this.getPinId(), + }) + } + + /** @return {GuidEntity} */ + getPinId() { + return this.entity.PinId + } + + /** @returns {String} */ + getPinName() { + return this.entity.PinName + } + + getPinDisplayName() { + return this.entity.getDisplayName() + } + + /** @return {CSSResult} */ + getColor() { + return this.entity.pinColor() + } + + isInput() { + return this.entity.isInput() + } + + isOutput() { + return this.entity.isOutput() + } + + getLinkLocation() { + return this.template.getLinkLocation() + } + + getNodeElement() { + return this.nodeElement + } + + getLinks() { + return this.entity.LinkedTo ?? [] + } + + getDefaultValue(maybeCreate = false) { + return this.defaultValue = this.entity.getDefaultValue(maybeCreate) + } + + /** @param {T} value */ + setDefaultValue(value) { + this.entity.DefaultValue = value; + this.defaultValue = value; + } + + /** @param {IElement[]} nodesWhitelist */ + sanitizeLinks(nodesWhitelist = []) { + this.entity.LinkedTo = this.entity.LinkedTo?.filter(pinReference => { + let pin = this.blueprint.getPin(pinReference); + if (pin) { + if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) { + return false + } + let link = this.blueprint.getLink(this, pin); + if (!link) { + link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) + .newObject(this, pin); + this.blueprint.addGraphElement(link); + } + } + return pin + }); + this.isLinked = this.entity.isLinked(); + } + + /** @param {PinElement} targetPinElement */ + linkTo(targetPinElement) { + const pinReference = this.createPinReference(); + if ( + this.isLinked + && this.isOutput() + && (this.pinType === "exec" || targetPinElement.pinType === "exec") + && !this.getLinks().some(ref => pinReference.equals(ref))) { + this.unlinkFromAll(); + } + if (this.entity.linkTo(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) { + this.isLinked = this.entity.isLinked(); + this.nodeElement?.template.linksChanged(); + } + } + + /** @param {PinElement} targetPinElement */ + unlinkFrom(targetPinElement, removeLink = true) { + if (this.entity.unlinkFrom(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) { + this.isLinked = this.entity.isLinked(); + this.nodeElement?.template.linksChanged(); + if (removeLink) { + this.blueprint.getLink(this, targetPinElement)?.remove(); // Might be called after the link is removed + } + } + } + + unlinkFromAll() { + const isLinked = this.getLinks().length; + this.getLinks().map(ref => this.blueprint.getPin(ref)).forEach(pin => this.unlinkFrom(pin)); + if (isLinked) { + this.nodeElement?.template.linksChanged(); + } + } + + /** + * @param {PinElement} originalPinElement + * @param {PinReferenceEntity} newReference + */ + redirectLink(originalPinElement, newReference) { + const index = this.getLinks().findIndex(pinReference => + pinReference.objectName.toString() == originalPinElement.getNodeElement().getNodeName() + && pinReference.pinGuid.valueOf() == originalPinElement.entity.PinId.valueOf() + ); + if (index >= 0) { + this.entity.LinkedTo[index] = newReference; + return true + } + return false + } +} + class OrderedIndexArray { /** @@ -9450,8 +9450,8 @@ class OrderedIndexArray { shiftRight(leftLimit, steps = 1) { this.array.set(this.array.subarray(leftLimit, -steps), leftLimit + steps); } -} - +} + /** * @typedef {import("../element/NodeElement").default} NodeElement * @typedef {typeof import("../Blueprint").default.nodeBoundariesSupplier} BoundariesFunction @@ -9610,141 +9610,141 @@ class FastSelectionModel { } this.finalPosition = finalPosition; } -} - +} + /** @typedef {import("../element/SelectorElement").default} SelectorElement */ /** @extends IFromToPositionedTemplate */ class SelectorTemplate extends IFromToPositionedTemplate { -} - -/** @typedef {import("../Blueprint").BlueprintConstructor} BlueprintConstructor */ - -/** @extends {IFromToPositionedElement} */ -class SelectorElement extends IFromToPositionedElement { - - /** @type {FastSelectionModel} */ - selectionModel = null - - constructor() { - super(); - super.initialize({}, new SelectorTemplate()); - } - - static newObject() { - return new SelectorElement() - } - - initialize() { - // Initialized in the constructor, this method does nothing - } - - /** @param {Number[]} initialPosition */ - beginSelect(initialPosition) { - const blueprintConstructor = /** @type {BlueprintConstructor} */(this.blueprint.constructor); - this.blueprint.selecting = true; - this.setBothLocations(initialPosition); - this.selectionModel = new FastSelectionModel( - initialPosition, - this.blueprint.getNodes(), - blueprintConstructor.nodeBoundariesSupplier, - blueprintConstructor.nodeSelectToggleFunction - ); - } - - /** @param {Number[]} finalPosition */ - selectTo(finalPosition) { - this.selectionModel.selectTo(finalPosition); - this.toX = finalPosition[0]; - this.toY = finalPosition[1]; - } - - endSelect() { - this.blueprint.selecting = false; - this.selectionModel = null; - this.fromX = 0; - this.fromY = 0; - this.toX = 0; - this.toY = 0; - } -} - -/** @typedef {typeof WindowElement} WindowElementConstructor */ - -/** - * @template {WindowTemplate} T - * @extends {IDraggableElement} - */ -class WindowElement extends IDraggableElement { - - static #typeTemplateMap = { - "window": WindowTemplate, - "color-picker": ColorPickerWindowTemplate, - } - - static properties = { - ...IDraggableElement.properties, - type: { - type: WindowTemplate, - attribute: "data-type", - reflect: true, - converter: { - fromAttribute: (value, type) => WindowElement.#typeTemplateMap[value], - toAttribute: (value, type) => - Object.entries(WindowElement.#typeTemplateMap).find(([k, v]) => value.constructor === v)?.[0], - }, - }, - } - - static newObject(entity = {}, template = entity.type ?? new WindowTemplate()) { - const result = new WindowElement(); - result.initialize(entity, template); - return result - } - - initialize(entity = {}, template = entity.type ?? new WindowTemplate()) { - entity.windowOptions ??= {}; - this.type = entity.type; - this.windowOptions = entity.windowOptions; - super.initialize(entity, template); - } - - computeSizes() { - const bounding = this.getBoundingClientRect(); - this.sizeX = bounding.width; - this.sizeY = bounding.height; - } - - cleanup() { - super.cleanup(); - this.acknowledgeClose(); - } - - acknowledgeClose() { - let deleteEvent = new CustomEvent(Configuration.windowCloseEventName); - this.dispatchEvent(deleteEvent); - } -} - -function defineElements() { - customElements.define("ueb-color-handler", ColorHandlerElement); - ElementFactory.registerElement("ueb-color-handler", ColorHandlerElement); - customElements.define("ueb-input", InputElement); - ElementFactory.registerElement("ueb-input", InputElement); - customElements.define("ueb-link", LinkElement); - ElementFactory.registerElement("ueb-link", LinkElement); - customElements.define("ueb-node", NodeElement); - ElementFactory.registerElement("ueb-node", NodeElement); - customElements.define("ueb-pin", PinElement); - ElementFactory.registerElement("ueb-pin", PinElement); - customElements.define("ueb-selector", SelectorElement); - ElementFactory.registerElement("ueb-selector", SelectorElement); - customElements.define("ueb-ui-slider", ColorSliderElement); - ElementFactory.registerElement("ueb-ui-slider", ColorSliderElement); - customElements.define("ueb-window", WindowElement); - ElementFactory.registerElement("ueb-window", WindowElement); -} - +} + +/** @typedef {import("../Blueprint").BlueprintConstructor} BlueprintConstructor */ + +/** @extends {IFromToPositionedElement} */ +class SelectorElement extends IFromToPositionedElement { + + /** @type {FastSelectionModel} */ + selectionModel = null + + constructor() { + super(); + super.initialize({}, new SelectorTemplate()); + } + + static newObject() { + return new SelectorElement() + } + + initialize() { + // Initialized in the constructor, this method does nothing + } + + /** @param {Number[]} initialPosition */ + beginSelect(initialPosition) { + const blueprintConstructor = /** @type {BlueprintConstructor} */(this.blueprint.constructor); + this.blueprint.selecting = true; + this.setBothLocations(initialPosition); + this.selectionModel = new FastSelectionModel( + initialPosition, + this.blueprint.getNodes(), + blueprintConstructor.nodeBoundariesSupplier, + blueprintConstructor.nodeSelectToggleFunction + ); + } + + /** @param {Number[]} finalPosition */ + selectTo(finalPosition) { + this.selectionModel.selectTo(finalPosition); + this.toX = finalPosition[0]; + this.toY = finalPosition[1]; + } + + endSelect() { + this.blueprint.selecting = false; + this.selectionModel = null; + this.fromX = 0; + this.fromY = 0; + this.toX = 0; + this.toY = 0; + } +} + +/** @typedef {typeof WindowElement} WindowElementConstructor */ + +/** + * @template {WindowTemplate} T + * @extends {IDraggableElement} + */ +class WindowElement extends IDraggableElement { + + static #typeTemplateMap = { + "window": WindowTemplate, + "color-picker": ColorPickerWindowTemplate, + } + + static properties = { + ...IDraggableElement.properties, + type: { + type: WindowTemplate, + attribute: "data-type", + reflect: true, + converter: { + fromAttribute: (value, type) => WindowElement.#typeTemplateMap[value], + toAttribute: (value, type) => + Object.entries(WindowElement.#typeTemplateMap).find(([k, v]) => value.constructor === v)?.[0], + }, + }, + } + + static newObject(entity = {}, template = entity.type ?? new WindowTemplate()) { + const result = new WindowElement(); + result.initialize(entity, template); + return result + } + + initialize(entity = {}, template = entity.type ?? new WindowTemplate()) { + entity.windowOptions ??= {}; + this.type = entity.type; + this.windowOptions = entity.windowOptions; + super.initialize(entity, template); + } + + computeSizes() { + const bounding = this.getBoundingClientRect(); + this.sizeX = bounding.width; + this.sizeY = bounding.height; + } + + cleanup() { + super.cleanup(); + this.acknowledgeClose(); + } + + acknowledgeClose() { + let deleteEvent = new CustomEvent(Configuration.windowCloseEventName); + this.dispatchEvent(deleteEvent); + } +} + +function defineElements() { + customElements.define("ueb-color-handler", ColorHandlerElement); + ElementFactory.registerElement("ueb-color-handler", ColorHandlerElement); + customElements.define("ueb-input", InputElement); + ElementFactory.registerElement("ueb-input", InputElement); + customElements.define("ueb-link", LinkElement); + ElementFactory.registerElement("ueb-link", LinkElement); + customElements.define("ueb-node", NodeElement); + ElementFactory.registerElement("ueb-node", NodeElement); + customElements.define("ueb-pin", PinElement); + ElementFactory.registerElement("ueb-pin", PinElement); + customElements.define("ueb-selector", SelectorElement); + ElementFactory.registerElement("ueb-selector", SelectorElement); + customElements.define("ueb-ui-slider", ColorSliderElement); + ElementFactory.registerElement("ueb-ui-slider", ColorSliderElement); + customElements.define("ueb-window", WindowElement); + ElementFactory.registerElement("ueb-window", WindowElement); +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -9790,8 +9790,8 @@ class GeneralSerializer extends ISerializer { let result = this.wrap(this.subWrite(entity, [], object, insideString), object); return result } -} - +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -9824,8 +9824,8 @@ class CustomSerializer extends GeneralSerializer { let result = this.#objectWriter(object, insideString); return result } -} - +} + /** * @typedef {import("../entity/IEntity").AnyValue} AnyValue * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor @@ -9851,8 +9851,8 @@ class ToStringSerializer extends GeneralSerializer { ? `"${Utility.escapeString(object.toString())}"` // String will have quotes if not inside a string already : Utility.escapeString(object.toString()) } -} - +} + /** * @typedef {import("../entity/IEntity").AnySimpleValue} AnySimpleValue * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -10078,9 +10078,9 @@ function initializeSerializerFactory() { VectorEntity, new GeneralSerializer(bracketsWrapped, VectorEntity) ); -} - +} + initializeSerializerFactory(); -defineElements(); - -export { Blueprint, Configuration, LinkElement, NodeElement, Utility }; +defineElements(); + +export { Blueprint, Configuration, LinkElement, NodeElement, Utility }; diff --git a/empty.html b/empty.html index a55346d..ef6e68c 100755 --- a/empty.html +++ b/empty.html @@ -3,7 +3,7 @@ - Unreal Engine Blueprint + UE Blueprint