diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index d1eff1a..0489297 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 @@ -296,93 +296,93 @@ 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("./IEntity").default} IEntity */ - -class ComputedType { - - #f - - /** @param {Function} f */ - constructor(f) { - this.#f = f; - } - - /** @param {IEntity} entity */ - compute(entity) { - return this.#f(entity) - } -} - +} + +/** @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("./IEntity").default} IEntity */ + +class ComputedType { + + #f + + /** @param {Function} f */ + constructor(f) { + this.#f = f; + } + + /** @param {IEntity} entity */ + compute(entity) { + return this.#f(entity) + } +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -420,27 +420,27 @@ class SerializerFactory { // @ts-expect-error return SerializerFactory.#serializers.get(entity) } -} - -/** @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").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 @@ -874,233 +874,233 @@ class Utility { }; requestAnimationFrame(doAnimation); } -} - -/** - * @typedef {(entity: IEntity) => AnyValue} ValueSupplier - * @typedef {IEntity | String | Number | BigInt | Boolean} AnySimpleValue - * @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue - * @typedef {{ - * [key: String]: AttributeInformation - * }} AttributeDeclarations - * @typedef {typeof IEntity} EntityConstructor - * @typedef {{ - * type?: AnyValueConstructor | AnyValueConstructor[] | UnionType | ComputedType, - * default?: 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 { - - static lookbehind = "" - /** @type {AttributeDeclarations} */ - static attributes = {} - static defaultAttribute = { - showDefault: true, - nullable: false, - ignored: false, - serialized: false, - expected: false, - } - - constructor(values = {}, suppressWarns = false) { - 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 - }; - } - const valuesNames = Object.keys(values); - const attributesNames = Object.keys(attributes); - const allAttributesNames = Utility.mergeArrays(attributesNames, valuesNames); - for (let attributeName of allAttributesNames) { - let value = values[attributeName]; - let attribute = /** @type {AttributeInformation} */(attributes[attributeName]); - - if (!suppressWarns) { - if (!(attributeName in attributes)) { - console.warn( - `UEBlueprint: Attribute ${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 ${attributeName} not ` - + "defined in the serialized data" - ); - } - } - - if (!attribute) { - // Remember attributeName can come from the values and be not defined in the attributes - // In that case just assign it and skip the rest - this[attributeName] = value; - continue - } - - let defaultValue = attribute.default; - let defaultType = attribute.type; - if (defaultType instanceof ComputedType) { - defaultType = defaultType.compute(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 => this[attributeName] = v - : v => { - Object.defineProperties(this, { - ["#" + 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 ${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) - )); - } - } - - /** @param {AttributeDeclarations} attributes */ - static cleanupAttributes(attributes, prefix = "") { - for (const attributeName in attributes) { - if (attributes[attributeName].constructor !== Object) { - attributes[attributeName] = { - default: attributes[attributeName], - }; - } - const attribute = /** @type {AttributeInformation} */(attributes[attributeName]); - if (attribute.type === undefined && !(attribute.default instanceof Function)) { - attribute.type = Utility.getType(attribute.default); - } - attributes[attributeName] = { - ...IEntity.defaultAttribute, - ...attribute, - }; - if (attribute.default === undefined) { - if (attribute.type === undefined) { - throw new Error( - `UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}` - + attributeName - ) - } - attribute[attributeName] = Utility.sanitize(undefined, attribute.type); - } - if (attribute.default === 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 {IEntity | String | Number | BigInt | Boolean} AnySimpleValue + * @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue + * @typedef {{ + * [key: String]: AttributeInformation + * }} AttributeDeclarations + * @typedef {typeof IEntity} EntityConstructor + * @typedef {{ + * type?: AnyValueConstructor | AnyValueConstructor[] | UnionType | ComputedType, + * default?: 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 { + + static lookbehind = "" + /** @type {AttributeDeclarations} */ + static attributes = {} + static defaultAttribute = { + showDefault: true, + nullable: false, + ignored: false, + serialized: false, + expected: false, + } + + constructor(values = {}, suppressWarns = false) { + 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 + }; + } + const valuesNames = Object.keys(values); + const attributesNames = Object.keys(attributes); + const allAttributesNames = Utility.mergeArrays(attributesNames, valuesNames); + for (let attributeName of allAttributesNames) { + let value = values[attributeName]; + let attribute = /** @type {AttributeInformation} */(attributes[attributeName]); + + if (!suppressWarns) { + if (!(attributeName in attributes)) { + console.warn( + `UEBlueprint: Attribute ${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 ${attributeName} not ` + + "defined in the serialized data" + ); + } + } + + if (!attribute) { + // Remember attributeName can come from the values and be not defined in the attributes + // In that case just assign it and skip the rest + this[attributeName] = value; + continue + } + + let defaultValue = attribute.default; + let defaultType = attribute.type; + if (defaultType instanceof ComputedType) { + defaultType = defaultType.compute(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 => this[attributeName] = v + : v => { + Object.defineProperties(this, { + ["#" + 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 ${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) + )); + } + } + + /** @param {AttributeDeclarations} attributes */ + static cleanupAttributes(attributes, prefix = "") { + for (const attributeName in attributes) { + if (attributes[attributeName].constructor !== Object) { + attributes[attributeName] = { + default: attributes[attributeName], + }; + } + const attribute = /** @type {AttributeInformation} */(attributes[attributeName]); + if (attribute.type === undefined && !(attribute.default instanceof Function)) { + attribute.type = Utility.getType(attribute.default); + } + attributes[attributeName] = { + ...IEntity.defaultAttribute, + ...attribute, + }; + if (attribute.default === undefined) { + if (attribute.type === undefined) { + throw new Error( + `UEBlueprint: Expected either "type" or "value" property in ${this.name} attribute ${prefix}` + + attributeName + ) + } + attribute[attributeName] = Utility.sanitize(undefined, attribute.type); + } + if (attribute.default === 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 = { @@ -1127,8 +1127,8 @@ class IntegerEntity extends IEntity { toString() { return this.value.toString() } -} - +} + class ByteEntity extends IntegerEntity { static attributes = { @@ -1146,57 +1146,57 @@ class ByteEntity extends IntegerEntity { constructor(values = 0) { super(values); } -} - -class SymbolEntity extends IEntity { - - static attributes = { - value: { - default: "", - }, - } - - 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: { + default: "", + }, + } + + 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 InvariantTextEntity extends IEntity { - - static lookbehind = "INVTEXT" - static attributes = { - value: { - default: "", - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.value; - } -} - +} + +class InvariantTextEntity extends IEntity { + + static lookbehind = "INVTEXT" + static attributes = { + value: { + default: "", + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.value; + } +} + class LocalizedTextEntity extends IEntity { static lookbehind = "NSLOCTEXT" @@ -1226,27 +1226,27 @@ class LocalizedTextEntity extends IEntity { toString() { return Utility.capitalFirstLetter(this.value) } -} - -class FormatTextEntity extends IEntity { - - static lookbehind = "LOCGEN_FORMAT_NAMED" - static attributes = { - value: { - type: [new UnionType(LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)], - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values); - /** @type {String} */ this.value; - } -} - +} + +class FormatTextEntity extends IEntity { + + static lookbehind = "LOCGEN_FORMAT_NAMED" + static attributes = { + value: { + type: [new UnionType(LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)], + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values); + /** @type {String} */ this.value; + } +} + class GuidEntity extends IEntity { static attributes = { @@ -1286,8 +1286,8 @@ class GuidEntity extends IEntity { toString() { return this.value } -} - +} + class ObjectReferenceEntity extends IEntity { static attributes = { @@ -1317,8 +1317,8 @@ class ObjectReferenceEntity extends IEntity { getName() { return this.path.match(/[^\.\/]+$/)?.[0] ?? "" } -} - +} + class FunctionReferenceEntity extends IEntity { static attributes = { @@ -1346,39 +1346,39 @@ class FunctionReferenceEntity extends IEntity { /** @type {String} */ this.MemberName; /** @type {GuidEntity} */ this.MemberGuid; } -} - -class IdentifierEntity extends IEntity { - - static attributes = { - value: { - default: "", - }, - } - - 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: { + default: "", + }, + } + + 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 = { @@ -1406,314 +1406,314 @@ class Integer64Entity extends IEntity { toString() { return this.value.toString() } -} - -class KeyBindingEntity extends IEntity { - - static attributes = { - ActionName: { - default: "", - }, - bShift: { - default: false, - }, - bCtrl: { - default: false, - }, - bAlt: { - default: false, - }, - bCmd: { - default: false, - }, - Key: { - type: IdentifierEntity, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values = {}) { - 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: { - default: 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 KeyBindingEntity extends IEntity { + + static attributes = { + ActionName: { + default: "", + }, + bShift: { + default: false, + }, + bCtrl: { + default: false, + }, + bAlt: { + default: false, + }, + bCmd: { + default: false, + }, + Key: { + type: IdentifierEntity, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values = {}) { + 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: { + default: 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 MacroGraphReferenceEntity extends IEntity { static attributes = { @@ -1743,16 +1743,16 @@ class MacroGraphReferenceEntity extends IEntity { const colonIndex = this.MacroGraph.path.search(":"); return this.MacroGraph.path.substring(colonIndex + 1) } -} - +} + class NaturalNumberEntity extends IntegerEntity { constructor(values = 0) { super(values); this.value = Math.round(Utility.clamp(this.value, 0)); } -} - +} + class PathSymbolEntity extends IEntity { static attributes = { @@ -1777,8 +1777,8 @@ class PathSymbolEntity extends IEntity { toString() { return this.value } -} - +} + class PinReferenceEntity extends IEntity { static attributes = { @@ -1799,147 +1799,147 @@ class PinReferenceEntity extends IEntity { /** @type {PathSymbolEntity} */ this.objectName; /** @type {GuidEntity} */ this.pinGuid; } -} - -class PinTypeEntity extends IEntity { - - static attributes = { - TerminalCategory: { - default: "", - showDefault: false, - }, - TerminalSubCategory: { - default: "", - showDefault: false, - }, - bTerminalIsConst: { - default: false, - showDefault: false, - }, - bTerminalIsWeakPointer: { - default: false, - showDefault: false, - }, - bTerminalIsUObjectWrapper: { - default: 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: { - default: 0, - }, - P: { - default: 0, - }, - Y: { - default: 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: { - default: 0, - expected: true, - }, - Y: { - default: 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: { - default: 0, - expected: true, - }, - Y: { - default: 0, - expected: true, - }, - Z: { - default: 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: { + default: "", + showDefault: false, + }, + TerminalSubCategory: { + default: "", + showDefault: false, + }, + bTerminalIsConst: { + default: false, + showDefault: false, + }, + bTerminalIsWeakPointer: { + default: false, + showDefault: false, + }, + bTerminalIsUObjectWrapper: { + default: 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: { + default: 0, + }, + P: { + default: 0, + }, + Y: { + default: 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: { + default: 0, + expected: true, + }, + Y: { + default: 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: { + default: 0, + expected: true, + }, + Y: { + default: 0, + expected: true, + }, + Z: { + default: 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 @@ -2228,308 +2228,308 @@ 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 doOnce = 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 doOnce = 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 = { @@ -2559,8 +2559,8 @@ class VariableReferenceEntity extends IEntity { /** @type {GuidEntity} */ this.GuidEntity; /** @type {Boolean} */ this.bSelfContext; } -} - +} + class ObjectEntity extends IEntity { static attributes = { @@ -3156,42 +3156,42 @@ 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: { - default: "", - showDefault: false, - ignore: true, - }, - } - - static { - this.cleanupAttributes(this.attributes); - } - - constructor(values) { - super(values, true); - /** @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: { + default: "", + showDefault: false, + ignore: true, + }, + } + + static { + this.cleanupAttributes(this.attributes); + } + + constructor(values) { + super(values, true); + /** @type {String} */ this.lookbehind; + } +} + /** * @typedef {import ("../entity/IEntity").AttributeInformation} AttributeInformation * @typedef {import ("../entity/IEntity").EntityConstructor} EntityConstructor @@ -3817,124 +3817,124 @@ class Grammar { this.linearColorRGBList, ) ) -} - -/** - * @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor - * @typedef {import("../entity/IEntity").AnyValue} AnyValue - * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor - */ - -/** @template {AnyValue} T */ -class ISerializer { - - /** @param {AnyValueConstructor} entityType */ - constructor( - entityType, - attributePrefix = "", - attributeSeparator = ",", - trailingSeparator = false, - attributeValueConjunctionSign = "=", - attributeKeyPrinter = k => k - ) { - 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} value */ - serialize(value, insideString = false) { - return this.write(value, insideString) - } - - /** - * @protected - * @param {String} value - * @returns {T} - */ - read(value) { - throw new Error("Not implemented") - } - - /** - * @protected - * @param {T} entity - * @param {Boolean} insideString - * @returns {String} - */ - write(entity, insideString) { - let result = ""; - const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {}; - const keys = Utility.mergeArrays( - Object.keys(attributes), - Object.keys(entity) - ); - for (const key of keys) { - const value = entity[key]; - if (value !== undefined && this.showProperty(entity, key)) { - const isSerialized = Utility.isSerialized(entity, key); - result += (result.length ? this.attributeSeparator : "") - + this.attributePrefix - + Utility.decodeKeyName(this.attributeKeyPrinter(key)) - + this.attributeValueConjunctionSign - + ( - isSerialized - ? `"${this.writeValue(entity, key, true)}"` - : this.writeValue(entity, key, insideString) - ); - } - } - if (this.trailingSeparator && result.length) { - // append separator at the end if asked and there was printed content - result += this.attributeSeparator; - } - return result - } - - /** - * @protected - * @param {String} key - * @param {Boolean} insideString - */ - writeValue(entity, key, insideString) { - const value = entity[key]; - 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( - entity[key], - insideString - ) - } - - showProperty(entity, key) { - const attributes = /** @type {EntityConstructor} */(this.entityType).attributes; - const attribute = attributes[key]; - const value = entity[key]; - if (attribute?.constructor === Object) { - if (attribute.ignored) { - return false - } - return !Utility.equals(attribute.value, value) || 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 { + + /** @param {AnyValueConstructor} entityType */ + constructor( + entityType, + attributePrefix = "", + attributeSeparator = ",", + trailingSeparator = false, + attributeValueConjunctionSign = "=", + attributeKeyPrinter = k => k + ) { + 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} value */ + serialize(value, insideString = false) { + return this.write(value, insideString) + } + + /** + * @protected + * @param {String} value + * @returns {T} + */ + read(value) { + throw new Error("Not implemented") + } + + /** + * @protected + * @param {T} entity + * @param {Boolean} insideString + * @returns {String} + */ + write(entity, insideString) { + let result = ""; + const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {}; + const keys = Utility.mergeArrays( + Object.keys(attributes), + Object.keys(entity) + ); + for (const key of keys) { + const value = entity[key]; + if (value !== undefined && this.showProperty(entity, key)) { + const isSerialized = Utility.isSerialized(entity, key); + result += (result.length ? this.attributeSeparator : "") + + this.attributePrefix + + Utility.decodeKeyName(this.attributeKeyPrinter(key)) + + this.attributeValueConjunctionSign + + ( + isSerialized + ? `"${this.writeValue(entity, key, true)}"` + : this.writeValue(entity, key, insideString) + ); + } + } + if (this.trailingSeparator && result.length) { + // append separator at the end if asked and there was printed content + result += this.attributeSeparator; + } + return result + } + + /** + * @protected + * @param {String} key + * @param {Boolean} insideString + */ + writeValue(entity, key, insideString) { + const value = entity[key]; + 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( + entity[key], + insideString + ) + } + + showProperty(entity, key) { + const attributes = /** @type {EntityConstructor} */(this.entityType).attributes; + const attribute = attributes[key]; + const value = entity[key]; + if (attribute?.constructor === Object) { + if (attribute.ignored) { + return false + } + return !Utility.equals(attribute.value, value) || attribute.showDefault + } + return true + } +} + class ObjectSerializer extends ISerializer { constructor() { @@ -3990,8 +3990,8 @@ class ObjectSerializer extends ISerializer { + "\nEnd Object\n"; return result } -} - +} + class Copy extends IInput { static #serializer = new ObjectSerializer() @@ -4026,187 +4026,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 (typeof v === "string") { - const parsed = Grammar.keyBindingEntity.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 (typeof v === "string") { + const parsed = Grammar.keyBindingEntity.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 { /** @@ -4222,75 +4222,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 @@ -4313,33 +4313,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 { @@ -4356,489 +4356,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() { @@ -4852,8 +4852,8 @@ class MouseScrollGraph extends IMouseClickDrag { endDrag() { this.blueprint.scrolling = false; } -} - +} + class MouseTracking extends IPointing { /** @type {IPointing} */ @@ -4916,32 +4916,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 { @@ -4994,8 +4994,8 @@ class Paste extends IInput { this.blueprint.addGraphElement(...nodes); return true } -} - +} + class Select extends IMouseClickDrag { constructor(target, blueprint, options = {}) { @@ -5023,8 +5023,8 @@ class Select extends IMouseClickDrag { this.blueprint.unselectAll(); } } -} - +} + class Unfocus extends IInput { /** @param {MouseEvent} e */ @@ -5054,8 +5054,8 @@ class Unfocus extends IInput { unlistenEvents() { document.removeEventListener("click", this.#clickHandler); } -} - +} + /** * @typedef {import("../Blueprint").default} Blueprint * @typedef {import("../element/PinElement").default} PinElement @@ -5282,75 +5282,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 @@ -5386,95 +5386,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 @@ -5659,313 +5659,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 @@ -6025,8 +6025,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 { @@ -6076,8 +6076,8 @@ class MouseClickDrag extends MouseMoveDraggable { super.endDrag(); this.#onEndDrag?.(); } -} - +} + /** * @typedef {import("../entity/IEntity").default} IEntity * @typedef {import("../element/IDraggableElement").default} IDraggableElement @@ -6135,31 +6135,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 @@ -6195,8 +6195,8 @@ class MouseMoveNodes extends MouseMoveDraggable { ); } } -} - +} + /** * @typedef {import("../element/NodeElement").default} NodeElement * @typedef {import("lit").PropertyValues} PropertyValues @@ -6227,8 +6227,8 @@ class ISelectableDraggableTemplate extends IDraggablePositionedTemplate { this.element.setSelected(true); } } -} - +} + /** * @typedef {import("../../element/NodeElement").default} NodeElement * @typedef {import("../../element/PinElement").default} PinElement @@ -6350,7 +6350,7 @@ class NodeTemplate extends ISelectableDraggableTemplate { .filter(v => !v.isHidden()) .map(pinEntity => { this.hasSubtitle = this.hasSubtitle - || pinEntity["PinName"] === "self" && pinEntity.getDisplayName() === "Target"; + || pinEntity.PinName === "self" && pinEntity.getDisplayName() === "Target"; let pinElement = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) .newObject(pinEntity, undefined, this.element); return pinElement @@ -6370,275 +6370,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 @@ -6772,80 +6772,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 @@ -6905,7 +6905,7 @@ class PinTemplate extends ITemplate { const content = y`
${this.isNameRendered ? this.renderName() : b} - ${this.element.isInput() && !this.element.entity["bDefaultValueIsIgnored"] ? this.renderInput() : y``} + ${this.element.isInput() && !this.element.entity.bDefaultValueIsIgnored ? this.renderInput() : y``}
`; return y` @@ -6966,28 +6966,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 @@ -7042,492 +7042,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 @@ -7984,65 +7984,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} */ @@ -8064,55 +8064,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} */ @@ -8129,416 +8129,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} */ @@ -8611,8 +8611,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 @@ -8966,505 +8966,505 @@ 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 T - * @typedef {import("parsimmon").Success} Success - */ - -/** - * @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 - ? /** @type {Success} */(Grammar.guidEntity.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 - ? /** @type {Success} */(Grammar.linearColorFromAnyFormat.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 T + * @typedef {import("parsimmon").Success} Success + */ + +/** + * @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 + ? /** @type {Success} */(Grammar.guidEntity.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 + ? /** @type {Success} */(Grammar.linearColorFromAnyFormat.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 { /** @@ -9582,8 +9582,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 @@ -9742,141 +9742,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 @@ -9921,8 +9921,8 @@ class GeneralSerializer extends ISerializer { let result = this.wrap(super.write(entity, insideString), entity); return result } -} - +} + /** * @typedef {import("../entity/IEntity").AnyValue} AnyValue * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor @@ -9954,8 +9954,8 @@ class CustomSerializer extends GeneralSerializer { let result = this.#objectWriter(entity, insideString); return result } -} - +} + /** * @typedef {import("../entity/IEntity").AnyValue} AnyValue * @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor @@ -9981,8 +9981,8 @@ class ToStringSerializer extends GeneralSerializer { ? `"${Utility.escapeString(entity.toString())}"` // String will have quotes if not inside a string already : Utility.escapeString(entity.toString()) } -} - +} + /** * @typedef {import("../entity/IEntity").AnySimpleValue} AnySimpleValue * @typedef {import("../entity/IEntity").AnyValue} AnyValue @@ -10208,9 +10208,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/js/template/node/NodeTemplate.js b/js/template/node/NodeTemplate.js index 7719845..4fb8415 100755 --- a/js/template/node/NodeTemplate.js +++ b/js/template/node/NodeTemplate.js @@ -125,7 +125,7 @@ export default class NodeTemplate extends ISelectableDraggableTemplate { .filter(v => !v.isHidden()) .map(pinEntity => { this.hasSubtitle = this.hasSubtitle - || pinEntity["PinName"] === "self" && pinEntity.getDisplayName() === "Target" + || pinEntity.PinName === "self" && pinEntity.getDisplayName() === "Target" let pinElement = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) .newObject(pinEntity, undefined, this.element) return pinElement diff --git a/js/template/pin/ExecPinTemplate.js b/js/template/pin/ExecPinTemplate.js index 7be713d..64737c8 100644 --- a/js/template/pin/ExecPinTemplate.js +++ b/js/template/pin/ExecPinTemplate.js @@ -12,9 +12,9 @@ export default class ExecPinTemplate extends PinTemplate { } renderName() { - let pinName = this.element.entity["PinName"] - if (this.element.entity["PinFriendlyName"]) { - pinName = this.element.entity["PinFriendlyName"].toString() + let pinName = this.element.entity.PinName + if (this.element.entity.PinFriendlyName) { + pinName = this.element.entity.PinFriendlyName.toString() } else if (pinName === "execute" || pinName === "then") { return html`` } diff --git a/js/template/pin/PinTemplate.js b/js/template/pin/PinTemplate.js index ed83ef5..2886b18 100755 --- a/js/template/pin/PinTemplate.js +++ b/js/template/pin/PinTemplate.js @@ -65,7 +65,7 @@ export default class PinTemplate extends ITemplate { const content = html`
${this.isNameRendered ? this.renderName() : nothing} - ${this.element.isInput() && !this.element.entity["bDefaultValueIsIgnored"] ? this.renderInput() : html``} + ${this.element.isInput() && !this.element.entity.bDefaultValueIsIgnored ? this.renderInput() : html``}
` return html`