diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index 90b6b700..00000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Playwright Tests -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - run: npx playwright test - - uses: actions/upload-artifact@v3 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a7fd378f..a7c5035b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "runem.lit-plugin" + "runem.lit-plugin", + "ms-playwright.playwright" ] } diff --git a/dist/css/ueb-style.css b/dist/css/ueb-style.css index afd710be..02bab341 100644 --- a/dist/css/ueb-style.css +++ b/dist/css/ueb-style.css @@ -309,9 +309,10 @@ ueb-node[data-selected=true] > .ueb-node-border { overflow: hidden; } -.ueb-node-style-operation .ueb-node-wrapper { +ueb-node.ueb-node-style-operation .ueb-node-wrapper { grid-template-rows: min-content auto auto min-content min-content; grid-template-columns: 50% 0% 1fr; + box-shadow: none; } .ueb-node-outputs ueb-pin[data-type=exec] .ueb-pin-wrapper { @@ -381,7 +382,7 @@ ueb-node.ueb-node-style-event .ueb-node-top { .ueb-node-name { display: flex; align-items: center; - background: radial-gradient(ellipse 100% 100% at 40% 50%, rgba(0, 0, 0, 0.5) 20%, transparent 50%); + background: radial-gradient(ellipse 100% 100% at 35% 55%, rgba(0, 0, 0, 0.5) 15%, transparent 50%); margin: -1px -15px; padding: 2px 15px; } @@ -554,6 +555,14 @@ ueb-node.ueb-node-style-operation .ueb-node-top { font-stretch: condensed; font-weight: bold; line-height: 100%; +} + +ueb-node.ueb-node-style-operation.ueb-node-style-metasound .ueb-node-top { + font-size: 333%; + font-stretch: expanded; +} + +ueb-node.ueb-node-style-operation.ueb-node-style-glass .ueb-node-top { z-index: -1; } @@ -567,6 +576,28 @@ ueb-node.ueb-node-style-operation .ueb-node-variadic { margin-left: -100px; } +ueb-node.ueb-node-style-metasound .ueb-node-wrapper { + border: 1px solid black; + box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.4) inset; + background: linear-gradient(to bottom, rgba(52, 55, 52, 0.9) 0, rgba(42, 44, 42, 0.9) 8px, rgba(31, 32, 31, 0.8) 16px, rgba(13, 14, 13, 0.9)); +} + +ueb-node.ueb-node-style-metasound .ueb-node-name-text, +ueb-node.ueb-node-style-metasound .ueb-node-name-symbol { + color: #8cc483; +} + +ueb-node.ueb-node-style-metasound.ueb-node-style-operation .ueb-node-name-text { + color: #7c7c7c; +} + +ueb-node.ueb-node-style-metasound .ueb-node-name { + margin: 0px; + padding: 1px 15px; + justify-content: center; + background: none; +} + ueb-node:not(.ueb-node-style-comment) { z-index: 10; } @@ -668,6 +699,10 @@ ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true][data-lin align-items: center; } +ueb-node.ueb-node-style-operation .ueb-pin-wrapper { + padding: 0; +} + ueb-node.ueb-node-style-minimal .ueb-pin-wrapper { min-height: 0; } diff --git a/dist/css/ueb-style.css.map b/dist/css/ueb-style.css.map index 6fa54c33..0757baff 100644 --- a/dist/css/ueb-style.css.map +++ b/dist/css/ueb-style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-knot.scss","../../scss/ueb-link.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-ui-controls.scss","../../scss/ueb-window.scss"],"names":[],"mappings":"AAAA;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;IACI;;EAGJ;IACI;;;AAIR;EACI;;;AAGJ;EACI;EACA;EACA;AACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAEI;EA0BJ,iBAEI;EAQJ;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAEI;EAmDJ,iBAEI;EAWJ,qBAEI;EAOJ;;;AAGJ;EACI;;;AAIJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AChQJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;ACvBJ;EACI;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;AAAA;AAAA;AAAA;EAIA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EAOA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;ACrFJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI,kBACI;EAIJ;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA,eACI;EAOJ;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA,YACI;EAEJ;EACA,YACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EAMA;EAMA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;EAGI;EACA;;;AAGJ;EACI;EACA;EACA;EACA,YACI;EAEJ;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAgBR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACvZJ;EACI;;;AAGJ;EACI;AAAA;AAAA;AAAA;AAAA;AAAA;EAMA;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EAII;EACA;;;AAIR;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACrQJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;AAEA;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;;AAIR;AAAA;EAEI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;AAEA;EACI;;;AAIR;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;;AAIR;AAAA;AAAA;EAGI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AAAA;AAAA;EAGI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;ACnKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;AAAA;EAEI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA,YACI;;;AAWR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;;AAIR;AAAA;EAEI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA","file":"ueb-style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-knot.scss","../../scss/ueb-link.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-ui-controls.scss","../../scss/ueb-window.scss"],"names":[],"mappings":"AAAA;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;IACI;;EAGJ;IACI;;;AAIR;EACI;;;AAGJ;EACI;EACA;EACA;AACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAEI;EA0BJ,iBAEI;EAQJ;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAEI;EAmDJ,iBAEI;EAWJ,qBAEI;EAOJ;;;AAGJ;EACI;;;AAIJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AChQJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;ACvBJ;EACI;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;AAAA;AAAA;AAAA;EAIA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EAOA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;ACrFJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI,kBACI;EAIJ;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA,eACI;EAOJ;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA,YACI;EAEJ;EACA,YACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EAMA;EAMA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;EAGI;EACA;;;AAGJ;EACI;EACA;EACA;EACA,YACI;EAEJ;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAOJ;AAAA;EAEI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAgBR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AC1bJ;EACI;;;AAGJ;EACI;AAAA;AAAA;AAAA;AAAA;AAAA;EAMA;EACA;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EAII;EACA;;;AAIR;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACzQJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;AAEA;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;;AAIR;AAAA;EAEI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;AAEA;EACI;;;AAIR;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;;AAIR;AAAA;AAAA;EAGI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AAAA;AAAA;EAGI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;ACnKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;AAAA;EAEI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA,YACI;;;AAWR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AAAA;EAEI;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;AAEA;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;;AAIR;AAAA;EAEI;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;EAEI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA","file":"ueb-style.css"} \ No newline at end of file diff --git a/dist/css/ueb-style.min.css b/dist/css/ueb-style.min.css index eaba03ef..ced9c269 100644 --- a/dist/css/ueb-style.min.css +++ b/dist/css/ueb-style.min.css @@ -1 +1 @@ -@font-face{font-family:"Roboto";font-weight:normal;src:url("../font/roboto-regular.woff2") format("woff2")}@font-face{font-family:"Roboto";font-stretch:condensed;src:url("../font/roboto-condensed-regular.woff2") format("woff2")}ueb-blueprint{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size);display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-size);color:#fff;user-select:none}ueb-blueprint svg{overflow:visible}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;background:rgba(0,0,0,.5);z-index:1}@keyframes ueb-zoom-animation{0%{color:#7f7f7f}100%{color:#2b2b2b}}.ueb-zoom-changed .ueb-viewport-zoom{animation:600ms ueb-zoom-animation}.ueb-viewport-zoom{margin-left:auto;padding:5px;color:#2b2b2b;font-size:154%;font-weight:bold;letter-spacing:-1px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);overflow:hidden;scrollbar-width:0}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + 2*var(--ueb-grid-expand))/var(--ueb-scale));height:calc((100% + 2*var(--ueb-grid-expand))/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;scale:var(--ueb-scale) var(--ueb-scale);transform-origin:0 0;overflow:hidden}ueb-blueprint[data-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-scrolling=false] .ueb-grid{cursor:default}ueb-blueprint.ueb-zoom--4{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2);--ueb-node-radius: 0 !important}ueb-blueprint.ueb-zoom--6{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}ueb-blueprint.ueb-zoom--11{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}.ueb-grid-content>div{width:0;height:0}.ueb-positioned,ueb-blueprint[data-selecting=true] ueb-selector{position:absolute}ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}.ueb-ellipsis-nowrap-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}ueb-node.ueb-node-style-minimal{box-shadow:none}ueb-node.ueb-node-style-minimal ueb-pin{margin:0;padding:0;min-height:0}ueb-node.ueb-node-style-minimal ueb-pin[data-direction=input]{display:none}ueb-node.ueb-node-style-minimal .ueb-node-border{padding:8px 16px;background:none !important}ueb-node.ueb-node-style-minimal .ueb-pin-wrapper{margin:0;padding:4px 6px}ueb-node.ueb-node-style-minimal[data-selected=true] .ueb-node-border{box-shadow:inset 0 0 0px 6px #d29e07}ueb-link{position:absolute;--ueb-link-color: rgb(var(--ueb-link-color-rgb));--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-reflected: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;margin-left:calc(var(--ueb-link-start)*-1px);min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link>svg{--ueb-y-reflected-coefficient: calc(2 * var(--ueb-y-reflected) - 1);position:absolute;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-reflected-coefficient) * var(--ueb-from-input-coefficient)));z-index:1}ueb-link .ueb-link-path{visibility:visible;stroke:var(--ueb-link-color);stroke-width:calc(1.5px/var(--ueb-scale));transition:stroke-width .8s}ueb-link .ueb-link-area{visibility:visible;stroke-width:20px}ueb-link[data-dragging=true] .ueb-link-path,.ueb-link-area:hover~.ueb-link-path{stroke-width:calc(6px/var(--ueb-scale))}ueb-link[data-dragging=true] .ueb-link-message{display:block;visibility:visible}.ueb-link-message{display:none;position:absolute;top:calc(100%*(1 - var(--ueb-y-reflected)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb-link-message-icon{display:inline-block;padding:4px;width:16px;height:16px;vertical-align:middle}.ueb-link-message-text{padding:4px;padding-left:0;vertical-align:middle}.ueb-link-message-icon svg{width:100%;height:100%}ueb-node{display:block;position:absolute;min-width:100px;border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.6);font-weight:lighter}ueb-node.ueb-node-style-minimal,ueb-node.ueb-node-style-comment{min-width:0}.ueb-zoom--2 ueb-node{box-shadow:none}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-zoom--2 .ueb-node-border{margin:0;padding:0}ueb-node[data-selected=true]>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y}.ueb-zoom--2 ueb-node[data-selected=true]>.ueb-node-border{background:none !important;outline:3px solid #ff8d00}.ueb-zoom--10 ueb-node[data-selected=true]>.ueb-node-border{outline-width:8px}.ueb-node-wrapper{display:grid;grid-template:"top top top" min-content "input center output" min-content "input center variadic" 1fr "development development development" min-content "expansion expansion expansion" min-content/min-content auto min-content;justify-items:space-between;padding:1px;box-sizing:border-box;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(14,16,10,.8);overflow:hidden}.ueb-node-style-operation .ueb-node-wrapper{grid-template-rows:min-content auto auto min-content min-content;grid-template-columns:50% 0% 1fr}.ueb-node-outputs ueb-pin[data-type=exec] .ueb-pin-wrapper{min-height:26px}ueb-node.ueb-node-style-operation.ueb-node-has-inputs .ueb-node-inputs{align-self:center;padding-right:40px}.ueb-node-style-operation:not(.ueb-node-is-variadic) .ueb-node-outputs{grid-row:2/4;align-self:center}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-draggable{cursor:move}.ueb-zoom--2 .ueb-node-wrapper{box-shadow:none;padding:0;background:#101010}.ueb-node-top{grid-area:top;color:#d0d0d0;font-size:103%;font-weight:bold;line-height:120%;white-space:nowrap}ueb-node.ueb-node-style-setter .ueb-node-top{max-height:5px}ueb-node.ueb-node-style-comment .ueb-node-top{white-space:normal}ueb-node.ueb-node-style-event .ueb-node-top{display:flex;justify-content:space-between;align-items:center;padding-right:2px}.ueb-node-style-default .ueb-node-top{padding:2px 20px 2px 8px;box-shadow:inset 5px 1px 5px -3px rgba(255,255,255,.2509803922),inset 0 1px 2px 0 rgba(255,255,255,.2509803922);border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(rgba(255, 255, 255, 0.1882352941) 1px, transparent 1px),linear-gradient(170deg, rgb(var(--ueb-node-color)) 0%, rgb(var(--ueb-node-color)) 50%, transparent 100%)}.ueb-zoom--2 .ueb-node-top{box-shadow:none}.ueb-zoom--2 .ueb-node-style-default .ueb-node-top{background:rgb(var(--ueb-node-color))}.ueb-node-name{display:flex;align-items:center;background:radial-gradient(ellipse 100% 100% at 40% 50%, rgba(0, 0, 0, 0.5) 20%, transparent 50%);margin:-1px -15px;padding:2px 15px}.ueb-node-style-setter .ueb-node-name{justify-content:center}.ueb-node-name:last-child{padding-right:26px}.ueb-zoom--2 .ueb-node-name{background:none}.ueb-zoom--8 .ueb-node-name{visibility:hidden}.ueb-node-name-symbol{align-self:flex-start;margin-right:5px;width:16px;height:16px;color:#74bff2}ueb-node[data-pure-function=true] .ueb-node-name-symbol{color:#aaeda0}.ueb-node-name-symbol path{vertical-align:middle}.ueb-node-subtitle-text{padding-top:1px;padding-right:5px;font-size:calc(.946154*var(--ueb-font-size));font-weight:100;font-style:italic;color:#a9b78f}.ueb-node-inputs{grid-area:input}ueb-node.ueb-node-has-inputs .ueb-node-inputs{padding-right:15px;padding-left:6px}.ueb-node-outputs{grid-area:output}.ueb-node-outputs,.ueb-node-variadic{padding-right:6px}.ueb-node-variadic{grid-area:variadic;margin-top:10px;margin-bottom:5px;align-self:start;justify-self:end;white-space:nowrap;cursor:pointer}.ueb-node-developmentonly{grid-area:development;margin-top:4px;padding:2px;background:repeating-linear-gradient(-45deg, transparent 0, #57590a 1px, #57590a 11px, transparent 12px, transparent 24px);background:repeating-linear-gradient(-45deg, transparent 0, #57590a 1px, #57590a 11px, transparent 12px, transparent 24px);text-align:center}.ueb-node-developmentonly-text{padding:0 10px;letter-spacing:.04em;text-shadow:1px 1px 1px #000;white-space:nowrap}.ueb-zoom--6 .ueb-node-developmentonly-text{visibility:hidden}.ueb-node-expansion{grid-area:expansion;display:none;text-align:center}ueb-node[data-advanced-display] .ueb-node-expansion{display:block}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-node-expansion:hover{background-color:#656765;cursor:pointer}.ueb-zoom--8 .ueb-node-expansion{visibility:hidden}.ueb-node-variadic>svg,.ueb-node-expansion>svg{width:16px;height:16px;vertical-align:middle}ueb-node[data-advanced-display=Shown] .ueb-node-expansion>svg{transform:scaleY(-1)}ueb-node.ueb-node-style-glass .ueb-node-wrapper,ueb-node.ueb-node-style-glass .ueb-node-border,ueb-node.ueb-node-style-glass{min-width:128px;border-radius:15px}ueb-node.ueb-node-style-glass .ueb-node-wrapper{border:1px solid #000;box-shadow:0 1px 0 0 rgba(255,255,255,.6) inset;padding:3px;background:linear-gradient(to bottom, rgba(255, 255, 255, 0.3) 0, rgba(255, 255, 255, 0.2) 4px, rgba(0, 0, 0, 0.3) 14px),linear-gradient(to right, transparent 10%, rgba(var(--ueb-node-color), 0.3) 50%, transparent 90%);background-size:100%,100% 28px;background-repeat:repeat,no-repeat}ueb-node.ueb-node-style-glass .ueb-node-name{padding-right:0;padding-left:0}ueb-node[data-selected=true] .ueb-node-border{box-shadow:inset 0 0 0px 6px #ce8700;background:none}ueb-node.ueb-node-style-glass .ueb-node-top,ueb-node.ueb-node-style-glass .ueb-node-name{margin:0;box-shadow:none;border-radius:0;background:none}ueb-node.ueb-node-style-glass .ueb-node-outputs:first-child{padding-left:12px}ueb-node.ueb-node-style-operation .ueb-node-top{grid-area:center;align-self:center;justify-self:center;padding:0;font-size:215%;font-stretch:condensed;font-weight:bold;line-height:100%;z-index:-1}ueb-node.ueb-node-style-operation .ueb-node-outputs{align-self:end}ueb-node.ueb-node-style-operation .ueb-node-variadic{align-self:start;margin-top:12px;margin-left:-100px}ueb-node:not(.ueb-node-style-comment){z-index:10}ueb-node.ueb-node-style-comment .ueb-node-border{height:100%}ueb-node.ueb-node-style-comment .ueb-node-wrapper{position:relative;padding:0;height:100%;border-radius:0;background:rgba(var(--ueb-node-color), 0.35);box-shadow:none}ueb-node.ueb-node-style-comment .ueb-node-wrapper::after{content:"";display:block;position:absolute;right:3px;bottom:3px;width:16px;height:16px;background:linear-gradient(135deg, transparent 50%, #fff 50%, #fff calc(50% + 2px), transparent calc(50% + 2px), transparent calc(50% + 3px), #fff calc(50% + 3px), #fff calc(50% + 5px), transparent calc(50% + 5px), transparent calc(50% + 7px), #fff calc(50% + 7px), #fff calc(50% + 9px), transparent calc(50% + 9px))}ueb-node.ueb-node-style-comment .ueb-node-top{position:relative;margin:3px;padding:3px 10px;box-shadow:none;border-radius:0;background:rgb(var(--ueb-node-color));color:#fff;font-size:185%;text-shadow:2px 1px 1px #444,0 0 2px #bbb;line-height:1.22;z-index:1}ueb-node.ueb-node-style-conversion .ueb-node-wrapper::after{content:"";display:block;grid-area:center;align-self:center;justify-self:center;margin:10px;width:6px;height:6px;border-radius:3px;background:#8f8f8f}ueb-blueprint{--ueb-pin-color-dim: #afafaf}ueb-pin{--ueb-pin-background: linear-gradient(90deg, rgba(var(--ueb-pin-color-rgb), 0.15), rgba(var(--ueb-pin-color-rgb), 0.8) 15%, rgba(var(--ueb-pin-color-rgb), 0.5) 60%, rgba(var(--ueb-pin-color-rgb), 0.35) 95%, transparent);--ueb-pin-color: rgb(var(--ueb-pin-color-rgb));display:block}.ueb-node-inputs ueb-pin,.ueb-node-outputs ueb-pin{margin:6px 0}ueb-pin.ueb-pin-input-wrap .ueb-pin-content{flex-direction:column;align-items:start}.ueb-node-top ueb-pin{min-height:0;margin-left:10px}.ueb-zoom--10 ueb-pin{visibility:hidden}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true][data-linked=false]{display:none}.ueb-pin-wrapper{display:inline-flex;box-sizing:border-box;padding:3px 6px;min-height:24px;align-items:center}ueb-node.ueb-node-style-minimal .ueb-pin-wrapper{min-height:0}.ueb-node-top .ueb-pin-wrapper{padding:2px 4px 2px 10px;min-height:0;vertical-align:bottom}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-pin-wrapper:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-zoom--8 .ueb-pin-wrapper:hover{background:none !important}.ueb-pin-content{display:flex;align-items:center}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon{color:var(--ueb-pin-color);text-align:left}ueb-pin[data-connectable=false] .ueb-pin-icon{visibility:hidden}.ueb-node-style-event ueb-pin[data-type=delegate] .ueb-pin-icon{width:11px;height:11px}.ueb-node-inputs .ueb-pin-icon{margin-right:6px}.ueb-node-outputs .ueb-pin-icon{margin-left:4px}.ueb-pin-icon>svg{display:block}ueb-pin[data-direction=output] .ueb-pin-reflect-output{transform:scaleX(-1)}ueb-pin[data-type=exec] .ueb-pin-icon{width:15px;height:15px;--ueb-pin-color: white}ueb-pin[data-linked=true] .ueb-pin-tofill{fill:currentColor}ueb-pin[data-linked=true] .ueb-pin-tostroke{stroke:#000}ueb-pin.ueb-node-variadic-default{position:relative;margin-top:14px}ueb-pin.ueb-node-variadic-default::before{content:"";display:block;position:absolute;width:100%;height:1px;top:-8px;left:6px;background:linear-gradient(90deg, transparent 0, #404240 6px, #404240 calc(100% - 6px), transparent 100%)}.ueb-zoom--6 .ueb-pin-content{visibility:hidden}ueb-node[data-type="/Script/BlueprintGraph.K2Node_VariableSet"] ueb-pin[data-direction=output] .ueb-pin-content{display:none}.ueb-pin-input-wrapper{display:flex;padding-left:8px}.ueb-pin-input{margin-left:5px;border:1px solid #a0a0a0;border-radius:3px;padding:0 4px 0 4px;color:#e0e0e0}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:rgba(255,255,255,.2745098039);outline:none}ueb-pin[data-linked=true] .ueb-pin-input,ueb-pin[data-linked=true] .ueb-pin-input-wrapper{display:none}ueb-pin[data-type=bool] .ueb-pin-input{appearance:none;padding:0;height:18px;width:18px;background-color:#0f0f0f;color:var(--ueb-pin-color)}ueb-pin[data-type=bool] .ueb-pin-input:checked{background-image:url('data:image/svg+xml,')}ueb-pin[data-type="/Script/CoreUObject.LinearColor"] .ueb-pin-input{padding:0;width:18px;height:18px;border-color:#505050;border-radius:0;background-color:var(--ueb-linear-color)}.ueb-pin-input-label~.ueb-pin-input{margin-left:2px}.ueb-pin-input-label{margin-left:10px;color:#777}.ueb-pin-input-label:first-child{margin-left:2px}.ueb-pin-input-content{display:block;outline:none;border:none;padding:0;min-width:10px;max-width:400px;max-height:16em;font-size:97%;white-space:nowrap;background:none;color:inherit;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:rgba(0,0,0,0);border-radius:10px;margin:4px}.ueb-pin-input-content:hover::-webkit-scrollbar-thumb{background:#575757}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-pin-input-content:hover{cursor:text}ueb-dropdown{display:block}ueb-dropdown.ueb-pin-input{margin-right:5px;padding-right:10px;background:#101010 !important;color:silver;border-color:#404040}ueb-dropdown.ueb-pin-input .ueb-pin-input-content{padding:6px;width:max(170px,var(--ueb-dropdown-width));max-width:400px;background:none;font-size:10px;letter-spacing:-0.2px;cursor:default !important}.ueb-button{min-width:60px;border:1px solid #0f0f0f;border-radius:4px;padding:2px 10px;background:#383838;text-align:center;cursor:pointer}.ueb-button:hover{background:#575757}.ueb-buttons{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}.ueb-vertical-slider>ueb-ui-slider{position:relative;padding-bottom:10px}.ueb-vertical-slider>ueb-ui-slider::before,.ueb-vertical-slider>ueb-ui-slider::after{content:"";display:block;position:absolute;width:0;height:0;border:5px solid rgba(0,0,0,0)}.ueb-vertical-slider>ueb-ui-slider::before{left:-4px;border-left-color:#e0e0e0}.ueb-vertical-slider>ueb-ui-slider::after{right:-4px;border-right-color:#e0e0e0}.ueb-horizontal-slider,.ueb-text-input{position:relative;border:1px solid #383838;border-radius:6px;background:#0f0f0f}.ueb-horizontal-slider{position:relative;padding:1px}.ueb-horizontal-slider:hover{cursor:ew-resize}ueb-window .ueb-pin-input-content{padding:1px 10px}.ueb-horizontal-slider>ueb-ui-slider{display:block;position:relative;height:20px;border-radius:5px;background:#383838}.ueb-horizontal-slider-text{position:absolute;padding:0 5px;line-height:20px;z-index:1}.ueb-toggle-control::before{content:"";display:inline-block;border-top:5px solid #e0e0e0;border-left:5px solid rgba(0,0,0,0);border-right:5px solid rgba(0,0,0,0);vertical-align:middle}.ueb-toggle-control:hover{cursor:pointer}.ueb-resizeable-top,.ueb-resizeable-top-right,.ueb-resizeable-top-left{position:absolute;top:0;height:10px}.ueb-resizeable-top{right:0;left:0;cursor:ns-resize}.ueb-resizeable-top-right{right:0;width:10px;cursor:nesw-resize}.ueb-resizeable-top-left{left:0;width:10px;cursor:nwse-resize}.ueb-resizeable-right{position:absolute;top:0;right:0;bottom:0;width:10px;cursor:ew-resize}.ueb-resizeable-bottom,.ueb-resizeable-bottom-right,.ueb-resizeable-bottom-left{position:absolute;bottom:-3px;height:10px;cursor:ns-resize}.ueb-resizeable-bottom{right:0;left:0}.ueb-resizeable-bottom-right{right:0;width:10px;cursor:nwse-resize}.ueb-resizeable-bottom-left{left:0;width:10px;cursor:nesw-resize}.ueb-resizeable-left{position:absolute;top:0;bottom:0;left:0;width:10px;cursor:ew-resize}ueb-window{display:block;position:absolute;border:2px solid #101010;top:0;left:0;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));background:#242424;box-shadow:0 0 2px 0 rgba(0,0,0,.6274509804);z-index:1000}.ueb-window-top{display:flex;flex-direction:row;align-items:center;padding:4px 8px;height:30px;background:#1a1a1a}.ueb-window-content{padding:10px;border:1px solid #303030}.ueb-window-name{flex-grow:1;padding-left:28px;text-align:center}.ueb-window-close{padding:8px;height:12px;width:12px;cursor:pointer}.ueb-color-picker-theme,.ueb-color-picker-srgb{display:inline-block;vertical-align:middle}.ueb-color-picker-main{display:grid;grid-template:1fr/auto min-content min-content min-content}.ueb-color-picker-wheel{position:relative;padding-top:100%;min-width:200px;border-radius:100%;background:radial-gradient(white 5%, transparent 85%),conic-gradient(from 90deg, #FF0000 0deg, #FFFF00 60deg, #00FF00 120deg, #00FFFF 180deg, #0000FF 240deg, #FF00FF 300deg, #FF0000 360deg)}ueb-color-handler{display:block;position:absolute;margin-top:-3px;margin-left:-3px;width:4px;height:4px;border:1px solid #000;border-radius:4px}.ueb-color-picker-wheel ueb-color-handler{left:var(--ueb-color-wheel-x);top:var(--ueb-color-wheel-y)}.ueb-color-picker-saturation,.ueb-color-picker-value{margin:0 6px;padding-bottom:10px;width:25px}.ueb-color-picker-saturation{margin-left:25px;background-image:linear-gradient(to bottom, transparent 10px, #FFFFFF 100%)}.ueb-color-picker-value{margin-right:25px;background-image:linear-gradient(to bottom, transparent 10px, #000000 100%)}ueb-ui-slider{display:block}.ueb-color-picker-saturation ueb-ui-slider{top:calc(100% - var(--ueb-color-s)*100%)}.ueb-color-picker-value ueb-ui-slider{top:calc(100% - var(--ueb-color-v)*100%)}.ueb-color-picker-preview{position:relative;align-self:flex-start}.ueb-color-picker-preview::before,.ueb-color-picker-preview::after{content:"";display:block;position:absolute;top:calc(50% - 1px);width:5px;border-top:2px solid #000}.ueb-color-picker-preview::after{right:0}.ueb-color-picker-preview-old,.ueb-color-picker-preview-new{width:100px;height:40px}.ueb-color-picker-preview-new{display:flex}.ueb-color-picker-preview-1,.ueb-color-picker-preview-2{width:50%}.ueb-color-picker-advanced{display:flex;column-gap:10px;padding-top:5px}.ueb-color-picker-advanced .ueb-color-picker-column{display:flex;flex-direction:column;justify-content:space-between;flex-grow:1;width:50%}.ueb-color-picker-advanced .ueb-color-picker-column>div{display:flex;align-items:center;margin-bottom:8px}.ueb-color-picker-advanced .ueb-color-picker-column>div>div{flex-grow:1}.ueb-color-picker-advanced .ueb-horizontal-slider{flex-grow:1}ueb-ui-slider{max-width:100%}.ueb-color-picker-r .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-r)*100%)}.ueb-color-picker-g .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-g)*100%)}.ueb-color-picker-b .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-b)*100%)}.ueb-color-picker-a .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-a)*100%)}.ueb-color-picker-h .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-h)*100%)}.ueb-color-picker-s .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-s)*100%)}.ueb-color-picker-v .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-v)*100%)}.ueb-color-picker-gradient{height:6px}.ueb-color-control{align-self:flex-end}.ueb-color-control-label{margin-right:5px;color:silver}.ueb-color-control .ueb-pin-input-content{width:8ch;line-height:20px;font-family:monospace;color:silver}/*# sourceMappingURL=ueb-style.min.css.map */ +@font-face{font-family:"Roboto";font-weight:normal;src:url("../font/roboto-regular.woff2") format("woff2")}@font-face{font-family:"Roboto";font-stretch:condensed;src:url("../font/roboto-condensed-regular.woff2") format("woff2")}ueb-blueprint{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size);display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-size);color:#fff;user-select:none}ueb-blueprint svg{overflow:visible}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;background:rgba(0,0,0,.5);z-index:1}@keyframes ueb-zoom-animation{0%{color:#7f7f7f}100%{color:#2b2b2b}}.ueb-zoom-changed .ueb-viewport-zoom{animation:600ms ueb-zoom-animation}.ueb-viewport-zoom{margin-left:auto;padding:5px;color:#2b2b2b;font-size:154%;font-weight:bold;letter-spacing:-1px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);overflow:hidden;scrollbar-width:0}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + 2*var(--ueb-grid-expand))/var(--ueb-scale));height:calc((100% + 2*var(--ueb-grid-expand))/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;scale:var(--ueb-scale) var(--ueb-scale);transform-origin:0 0;overflow:hidden}ueb-blueprint[data-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-scrolling=false] .ueb-grid{cursor:default}ueb-blueprint.ueb-zoom--4{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2);--ueb-node-radius: 0 !important}ueb-blueprint.ueb-zoom--6{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}ueb-blueprint.ueb-zoom--11{--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}.ueb-grid-content>div{width:0;height:0}.ueb-positioned,ueb-blueprint[data-selecting=true] ueb-selector{position:absolute}ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}.ueb-ellipsis-nowrap-text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}ueb-node.ueb-node-style-minimal{box-shadow:none}ueb-node.ueb-node-style-minimal ueb-pin{margin:0;padding:0;min-height:0}ueb-node.ueb-node-style-minimal ueb-pin[data-direction=input]{display:none}ueb-node.ueb-node-style-minimal .ueb-node-border{padding:8px 16px;background:none !important}ueb-node.ueb-node-style-minimal .ueb-pin-wrapper{margin:0;padding:4px 6px}ueb-node.ueb-node-style-minimal[data-selected=true] .ueb-node-border{box-shadow:inset 0 0 0px 6px #d29e07}ueb-link{position:absolute;--ueb-link-color: rgb(var(--ueb-link-color-rgb));--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-reflected: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;margin-left:calc(var(--ueb-link-start)*-1px);min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link>svg{--ueb-y-reflected-coefficient: calc(2 * var(--ueb-y-reflected) - 1);position:absolute;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-reflected-coefficient) * var(--ueb-from-input-coefficient)));z-index:1}ueb-link .ueb-link-path{visibility:visible;stroke:var(--ueb-link-color);stroke-width:calc(1.5px/var(--ueb-scale));transition:stroke-width .8s}ueb-link .ueb-link-area{visibility:visible;stroke-width:20px}ueb-link[data-dragging=true] .ueb-link-path,.ueb-link-area:hover~.ueb-link-path{stroke-width:calc(6px/var(--ueb-scale))}ueb-link[data-dragging=true] .ueb-link-message{display:block;visibility:visible}.ueb-link-message{display:none;position:absolute;top:calc(100%*(1 - var(--ueb-y-reflected)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb-link-message-icon{display:inline-block;padding:4px;width:16px;height:16px;vertical-align:middle}.ueb-link-message-text{padding:4px;padding-left:0;vertical-align:middle}.ueb-link-message-icon svg{width:100%;height:100%}ueb-node{display:block;position:absolute;min-width:100px;border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.6);font-weight:lighter}ueb-node.ueb-node-style-minimal,ueb-node.ueb-node-style-comment{min-width:0}.ueb-zoom--2 ueb-node{box-shadow:none}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-zoom--2 .ueb-node-border{margin:0;padding:0}ueb-node[data-selected=true]>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y}.ueb-zoom--2 ueb-node[data-selected=true]>.ueb-node-border{background:none !important;outline:3px solid #ff8d00}.ueb-zoom--10 ueb-node[data-selected=true]>.ueb-node-border{outline-width:8px}.ueb-node-wrapper{display:grid;grid-template:"top top top" min-content "input center output" min-content "input center variadic" 1fr "development development development" min-content "expansion expansion expansion" min-content/min-content auto min-content;justify-items:space-between;padding:1px;box-sizing:border-box;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(14,16,10,.8);overflow:hidden}ueb-node.ueb-node-style-operation .ueb-node-wrapper{grid-template-rows:min-content auto auto min-content min-content;grid-template-columns:50% 0% 1fr;box-shadow:none}.ueb-node-outputs ueb-pin[data-type=exec] .ueb-pin-wrapper{min-height:26px}ueb-node.ueb-node-style-operation.ueb-node-has-inputs .ueb-node-inputs{align-self:center;padding-right:40px}.ueb-node-style-operation:not(.ueb-node-is-variadic) .ueb-node-outputs{grid-row:2/4;align-self:center}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-draggable{cursor:move}.ueb-zoom--2 .ueb-node-wrapper{box-shadow:none;padding:0;background:#101010}.ueb-node-top{grid-area:top;color:#d0d0d0;font-size:103%;font-weight:bold;line-height:120%;white-space:nowrap}ueb-node.ueb-node-style-setter .ueb-node-top{max-height:5px}ueb-node.ueb-node-style-comment .ueb-node-top{white-space:normal}ueb-node.ueb-node-style-event .ueb-node-top{display:flex;justify-content:space-between;align-items:center;padding-right:2px}.ueb-node-style-default .ueb-node-top{padding:2px 20px 2px 8px;box-shadow:inset 5px 1px 5px -3px rgba(255,255,255,.2509803922),inset 0 1px 2px 0 rgba(255,255,255,.2509803922);border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(rgba(255, 255, 255, 0.1882352941) 1px, transparent 1px),linear-gradient(170deg, rgb(var(--ueb-node-color)) 0%, rgb(var(--ueb-node-color)) 50%, transparent 100%)}.ueb-zoom--2 .ueb-node-top{box-shadow:none}.ueb-zoom--2 .ueb-node-style-default .ueb-node-top{background:rgb(var(--ueb-node-color))}.ueb-node-name{display:flex;align-items:center;background:radial-gradient(ellipse 100% 100% at 35% 55%, rgba(0, 0, 0, 0.5) 15%, transparent 50%);margin:-1px -15px;padding:2px 15px}.ueb-node-style-setter .ueb-node-name{justify-content:center}.ueb-node-name:last-child{padding-right:26px}.ueb-zoom--2 .ueb-node-name{background:none}.ueb-zoom--8 .ueb-node-name{visibility:hidden}.ueb-node-name-symbol{align-self:flex-start;margin-right:5px;width:16px;height:16px;color:#74bff2}ueb-node[data-pure-function=true] .ueb-node-name-symbol{color:#aaeda0}.ueb-node-name-symbol path{vertical-align:middle}.ueb-node-subtitle-text{padding-top:1px;padding-right:5px;font-size:calc(.946154*var(--ueb-font-size));font-weight:100;font-style:italic;color:#a9b78f}.ueb-node-inputs{grid-area:input}ueb-node.ueb-node-has-inputs .ueb-node-inputs{padding-right:15px;padding-left:6px}.ueb-node-outputs{grid-area:output}.ueb-node-outputs,.ueb-node-variadic{padding-right:6px}.ueb-node-variadic{grid-area:variadic;margin-top:10px;margin-bottom:5px;align-self:start;justify-self:end;white-space:nowrap;cursor:pointer}.ueb-node-developmentonly{grid-area:development;margin-top:4px;padding:2px;background:repeating-linear-gradient(-45deg, transparent 0, #57590a 1px, #57590a 11px, transparent 12px, transparent 24px);background:repeating-linear-gradient(-45deg, transparent 0, #57590a 1px, #57590a 11px, transparent 12px, transparent 24px);text-align:center}.ueb-node-developmentonly-text{padding:0 10px;letter-spacing:.04em;text-shadow:1px 1px 1px #000;white-space:nowrap}.ueb-zoom--6 .ueb-node-developmentonly-text{visibility:hidden}.ueb-node-expansion{grid-area:expansion;display:none;text-align:center}ueb-node[data-advanced-display] .ueb-node-expansion{display:block}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-node-expansion:hover{background-color:#656765;cursor:pointer}.ueb-zoom--8 .ueb-node-expansion{visibility:hidden}.ueb-node-variadic>svg,.ueb-node-expansion>svg{width:16px;height:16px;vertical-align:middle}ueb-node[data-advanced-display=Shown] .ueb-node-expansion>svg{transform:scaleY(-1)}ueb-node.ueb-node-style-glass .ueb-node-wrapper,ueb-node.ueb-node-style-glass .ueb-node-border,ueb-node.ueb-node-style-glass{min-width:128px;border-radius:15px}ueb-node.ueb-node-style-glass .ueb-node-wrapper{border:1px solid #000;box-shadow:0 1px 0 0 rgba(255,255,255,.6) inset;padding:3px;background:linear-gradient(to bottom, rgba(255, 255, 255, 0.3) 0, rgba(255, 255, 255, 0.2) 4px, rgba(0, 0, 0, 0.3) 14px),linear-gradient(to right, transparent 10%, rgba(var(--ueb-node-color), 0.3) 50%, transparent 90%);background-size:100%,100% 28px;background-repeat:repeat,no-repeat}ueb-node.ueb-node-style-glass .ueb-node-name{padding-right:0;padding-left:0}ueb-node[data-selected=true] .ueb-node-border{box-shadow:inset 0 0 0px 6px #ce8700;background:none}ueb-node.ueb-node-style-glass .ueb-node-top,ueb-node.ueb-node-style-glass .ueb-node-name{margin:0;box-shadow:none;border-radius:0;background:none}ueb-node.ueb-node-style-glass .ueb-node-outputs:first-child{padding-left:12px}ueb-node.ueb-node-style-operation .ueb-node-top{grid-area:center;align-self:center;justify-self:center;padding:0;font-size:215%;font-stretch:condensed;font-weight:bold;line-height:100%}ueb-node.ueb-node-style-operation.ueb-node-style-metasound .ueb-node-top{font-size:333%;font-stretch:expanded}ueb-node.ueb-node-style-operation.ueb-node-style-glass .ueb-node-top{z-index:-1}ueb-node.ueb-node-style-operation .ueb-node-outputs{align-self:end}ueb-node.ueb-node-style-operation .ueb-node-variadic{align-self:start;margin-top:12px;margin-left:-100px}ueb-node.ueb-node-style-metasound .ueb-node-wrapper{border:1px solid #000;box-shadow:0 1px 0 0 rgba(255,255,255,.4) inset;background:linear-gradient(to bottom, rgba(52, 55, 52, 0.9) 0, rgba(42, 44, 42, 0.9) 8px, rgba(31, 32, 31, 0.8) 16px, rgba(13, 14, 13, 0.9))}ueb-node.ueb-node-style-metasound .ueb-node-name-text,ueb-node.ueb-node-style-metasound .ueb-node-name-symbol{color:#8cc483}ueb-node.ueb-node-style-metasound.ueb-node-style-operation .ueb-node-name-text{color:#7c7c7c}ueb-node.ueb-node-style-metasound .ueb-node-name{margin:0px;padding:1px 15px;justify-content:center;background:none}ueb-node:not(.ueb-node-style-comment){z-index:10}ueb-node.ueb-node-style-comment .ueb-node-border{height:100%}ueb-node.ueb-node-style-comment .ueb-node-wrapper{position:relative;padding:0;height:100%;border-radius:0;background:rgba(var(--ueb-node-color), 0.35);box-shadow:none}ueb-node.ueb-node-style-comment .ueb-node-wrapper::after{content:"";display:block;position:absolute;right:3px;bottom:3px;width:16px;height:16px;background:linear-gradient(135deg, transparent 50%, #fff 50%, #fff calc(50% + 2px), transparent calc(50% + 2px), transparent calc(50% + 3px), #fff calc(50% + 3px), #fff calc(50% + 5px), transparent calc(50% + 5px), transparent calc(50% + 7px), #fff calc(50% + 7px), #fff calc(50% + 9px), transparent calc(50% + 9px))}ueb-node.ueb-node-style-comment .ueb-node-top{position:relative;margin:3px;padding:3px 10px;box-shadow:none;border-radius:0;background:rgb(var(--ueb-node-color));color:#fff;font-size:185%;text-shadow:2px 1px 1px #444,0 0 2px #bbb;line-height:1.22;z-index:1}ueb-node.ueb-node-style-conversion .ueb-node-wrapper::after{content:"";display:block;grid-area:center;align-self:center;justify-self:center;margin:10px;width:6px;height:6px;border-radius:3px;background:#8f8f8f}ueb-blueprint{--ueb-pin-color-dim: #afafaf}ueb-pin{--ueb-pin-background: linear-gradient(90deg, rgba(var(--ueb-pin-color-rgb), 0.15), rgba(var(--ueb-pin-color-rgb), 0.8) 15%, rgba(var(--ueb-pin-color-rgb), 0.5) 60%, rgba(var(--ueb-pin-color-rgb), 0.35) 95%, transparent);--ueb-pin-color: rgb(var(--ueb-pin-color-rgb));display:block}.ueb-node-inputs ueb-pin,.ueb-node-outputs ueb-pin{margin:6px 0}ueb-pin.ueb-pin-input-wrap .ueb-pin-content{flex-direction:column;align-items:start}.ueb-node-top ueb-pin{min-height:0;margin-left:10px}.ueb-zoom--10 ueb-pin{visibility:hidden}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true][data-linked=false]{display:none}.ueb-pin-wrapper{display:inline-flex;box-sizing:border-box;padding:3px 6px;min-height:24px;align-items:center}ueb-node.ueb-node-style-operation .ueb-pin-wrapper{padding:0}ueb-node.ueb-node-style-minimal .ueb-pin-wrapper{min-height:0}.ueb-node-top .ueb-pin-wrapper{padding:2px 4px 2px 10px;min-height:0;vertical-align:bottom}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-pin-wrapper:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-zoom--8 .ueb-pin-wrapper:hover{background:none !important}.ueb-pin-content{display:flex;align-items:center}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon{color:var(--ueb-pin-color);text-align:left}ueb-pin[data-connectable=false] .ueb-pin-icon{visibility:hidden}.ueb-node-style-event ueb-pin[data-type=delegate] .ueb-pin-icon{width:11px;height:11px}.ueb-node-inputs .ueb-pin-icon{margin-right:6px}.ueb-node-outputs .ueb-pin-icon{margin-left:4px}.ueb-pin-icon>svg{display:block}ueb-pin[data-direction=output] .ueb-pin-reflect-output{transform:scaleX(-1)}ueb-pin[data-type=exec] .ueb-pin-icon{width:15px;height:15px;--ueb-pin-color: white}ueb-pin[data-linked=true] .ueb-pin-tofill{fill:currentColor}ueb-pin[data-linked=true] .ueb-pin-tostroke{stroke:#000}ueb-pin.ueb-node-variadic-default{position:relative;margin-top:14px}ueb-pin.ueb-node-variadic-default::before{content:"";display:block;position:absolute;width:100%;height:1px;top:-8px;left:6px;background:linear-gradient(90deg, transparent 0, #404240 6px, #404240 calc(100% - 6px), transparent 100%)}.ueb-zoom--6 .ueb-pin-content{visibility:hidden}ueb-node[data-type="/Script/BlueprintGraph.K2Node_VariableSet"] ueb-pin[data-direction=output] .ueb-pin-content{display:none}.ueb-pin-input-wrapper{display:flex;padding-left:8px}.ueb-pin-input{margin-left:5px;border:1px solid #a0a0a0;border-radius:3px;padding:0 4px 0 4px;color:#e0e0e0}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:rgba(255,255,255,.2745098039);outline:none}ueb-pin[data-linked=true] .ueb-pin-input,ueb-pin[data-linked=true] .ueb-pin-input-wrapper{display:none}ueb-pin[data-type=bool] .ueb-pin-input{appearance:none;padding:0;height:18px;width:18px;background-color:#0f0f0f;color:var(--ueb-pin-color)}ueb-pin[data-type=bool] .ueb-pin-input:checked{background-image:url('data:image/svg+xml,')}ueb-pin[data-type="/Script/CoreUObject.LinearColor"] .ueb-pin-input{padding:0;width:18px;height:18px;border-color:#505050;border-radius:0;background-color:var(--ueb-linear-color)}.ueb-pin-input-label~.ueb-pin-input{margin-left:2px}.ueb-pin-input-label{margin-left:10px;color:#777}.ueb-pin-input-label:first-child{margin-left:2px}.ueb-pin-input-content{display:block;outline:none;border:none;padding:0;min-width:10px;max-width:400px;max-height:16em;font-size:97%;white-space:nowrap;background:none;color:inherit;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:rgba(0,0,0,0);border-radius:10px;margin:4px}.ueb-pin-input-content:hover::-webkit-scrollbar-thumb{background:#575757}ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-pin-input-content:hover{cursor:text}ueb-dropdown{display:block}ueb-dropdown.ueb-pin-input{margin-right:5px;padding-right:10px;background:#101010 !important;color:silver;border-color:#404040}ueb-dropdown.ueb-pin-input .ueb-pin-input-content{padding:6px;width:max(170px,var(--ueb-dropdown-width));max-width:400px;background:none;font-size:10px;letter-spacing:-0.2px;cursor:default !important}.ueb-button{min-width:60px;border:1px solid #0f0f0f;border-radius:4px;padding:2px 10px;background:#383838;text-align:center;cursor:pointer}.ueb-button:hover{background:#575757}.ueb-buttons{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}.ueb-vertical-slider>ueb-ui-slider{position:relative;padding-bottom:10px}.ueb-vertical-slider>ueb-ui-slider::before,.ueb-vertical-slider>ueb-ui-slider::after{content:"";display:block;position:absolute;width:0;height:0;border:5px solid rgba(0,0,0,0)}.ueb-vertical-slider>ueb-ui-slider::before{left:-4px;border-left-color:#e0e0e0}.ueb-vertical-slider>ueb-ui-slider::after{right:-4px;border-right-color:#e0e0e0}.ueb-horizontal-slider,.ueb-text-input{position:relative;border:1px solid #383838;border-radius:6px;background:#0f0f0f}.ueb-horizontal-slider{position:relative;padding:1px}.ueb-horizontal-slider:hover{cursor:ew-resize}ueb-window .ueb-pin-input-content{padding:1px 10px}.ueb-horizontal-slider>ueb-ui-slider{display:block;position:relative;height:20px;border-radius:5px;background:#383838}.ueb-horizontal-slider-text{position:absolute;padding:0 5px;line-height:20px;z-index:1}.ueb-toggle-control::before{content:"";display:inline-block;border-top:5px solid #e0e0e0;border-left:5px solid rgba(0,0,0,0);border-right:5px solid rgba(0,0,0,0);vertical-align:middle}.ueb-toggle-control:hover{cursor:pointer}.ueb-resizeable-top,.ueb-resizeable-top-right,.ueb-resizeable-top-left{position:absolute;top:0;height:10px}.ueb-resizeable-top{right:0;left:0;cursor:ns-resize}.ueb-resizeable-top-right{right:0;width:10px;cursor:nesw-resize}.ueb-resizeable-top-left{left:0;width:10px;cursor:nwse-resize}.ueb-resizeable-right{position:absolute;top:0;right:0;bottom:0;width:10px;cursor:ew-resize}.ueb-resizeable-bottom,.ueb-resizeable-bottom-right,.ueb-resizeable-bottom-left{position:absolute;bottom:-3px;height:10px;cursor:ns-resize}.ueb-resizeable-bottom{right:0;left:0}.ueb-resizeable-bottom-right{right:0;width:10px;cursor:nwse-resize}.ueb-resizeable-bottom-left{left:0;width:10px;cursor:nesw-resize}.ueb-resizeable-left{position:absolute;top:0;bottom:0;left:0;width:10px;cursor:ew-resize}ueb-window{display:block;position:absolute;border:2px solid #101010;top:0;left:0;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));background:#242424;box-shadow:0 0 2px 0 rgba(0,0,0,.6274509804);z-index:1000}.ueb-window-top{display:flex;flex-direction:row;align-items:center;padding:4px 8px;height:30px;background:#1a1a1a}.ueb-window-content{padding:10px;border:1px solid #303030}.ueb-window-name{flex-grow:1;padding-left:28px;text-align:center}.ueb-window-close{padding:8px;height:12px;width:12px;cursor:pointer}.ueb-color-picker-theme,.ueb-color-picker-srgb{display:inline-block;vertical-align:middle}.ueb-color-picker-main{display:grid;grid-template:1fr/auto min-content min-content min-content}.ueb-color-picker-wheel{position:relative;padding-top:100%;min-width:200px;border-radius:100%;background:radial-gradient(white 5%, transparent 85%),conic-gradient(from 90deg, #FF0000 0deg, #FFFF00 60deg, #00FF00 120deg, #00FFFF 180deg, #0000FF 240deg, #FF00FF 300deg, #FF0000 360deg)}ueb-color-handler{display:block;position:absolute;margin-top:-3px;margin-left:-3px;width:4px;height:4px;border:1px solid #000;border-radius:4px}.ueb-color-picker-wheel ueb-color-handler{left:var(--ueb-color-wheel-x);top:var(--ueb-color-wheel-y)}.ueb-color-picker-saturation,.ueb-color-picker-value{margin:0 6px;padding-bottom:10px;width:25px}.ueb-color-picker-saturation{margin-left:25px;background-image:linear-gradient(to bottom, transparent 10px, #FFFFFF 100%)}.ueb-color-picker-value{margin-right:25px;background-image:linear-gradient(to bottom, transparent 10px, #000000 100%)}ueb-ui-slider{display:block}.ueb-color-picker-saturation ueb-ui-slider{top:calc(100% - var(--ueb-color-s)*100%)}.ueb-color-picker-value ueb-ui-slider{top:calc(100% - var(--ueb-color-v)*100%)}.ueb-color-picker-preview{position:relative;align-self:flex-start}.ueb-color-picker-preview::before,.ueb-color-picker-preview::after{content:"";display:block;position:absolute;top:calc(50% - 1px);width:5px;border-top:2px solid #000}.ueb-color-picker-preview::after{right:0}.ueb-color-picker-preview-old,.ueb-color-picker-preview-new{width:100px;height:40px}.ueb-color-picker-preview-new{display:flex}.ueb-color-picker-preview-1,.ueb-color-picker-preview-2{width:50%}.ueb-color-picker-advanced{display:flex;column-gap:10px;padding-top:5px}.ueb-color-picker-advanced .ueb-color-picker-column{display:flex;flex-direction:column;justify-content:space-between;flex-grow:1;width:50%}.ueb-color-picker-advanced .ueb-color-picker-column>div{display:flex;align-items:center;margin-bottom:8px}.ueb-color-picker-advanced .ueb-color-picker-column>div>div{flex-grow:1}.ueb-color-picker-advanced .ueb-horizontal-slider{flex-grow:1}ueb-ui-slider{max-width:100%}.ueb-color-picker-r .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-r)*100%)}.ueb-color-picker-g .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-g)*100%)}.ueb-color-picker-b .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-b)*100%)}.ueb-color-picker-a .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-a)*100%)}.ueb-color-picker-h .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-h)*100%)}.ueb-color-picker-s .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-s)*100%)}.ueb-color-picker-v .ueb-horizontal-slider>ueb-ui-slider{width:calc(var(--ueb-color-v)*100%)}.ueb-color-picker-gradient{height:6px}.ueb-color-control{align-self:flex-end}.ueb-color-control-label{margin-right:5px;color:silver}.ueb-color-control .ueb-pin-input-content{width:8ch;line-height:20px;font-family:monospace;color:silver}/*# sourceMappingURL=ueb-style.min.css.map */ diff --git a/dist/css/ueb-style.min.css.map b/dist/css/ueb-style.min.css.map index 88418268..16e466f6 100644 --- a/dist/css/ueb-style.min.css.map +++ b/dist/css/ueb-style.min.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-knot.scss","../../scss/ueb-link.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-ui-controls.scss","../../scss/ueb-window.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,mBACA,wDAGJ,WACI,qBACA,uBACA,kEAGJ,cACI,eACA,6CACA,cACA,kBACA,8EACA,+BACA,WACA,iBAGJ,kBACI,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,0BACA,UAGJ,8BACI,GACI,cAGJ,KACI,eAIR,qCACI,mCAGJ,mBACI,iBACA,YACA,cAEA,eACA,iBACA,oBAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,UACI,kFACA,kBACA,eACA,gBACA,+DACA,gEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,wCACA,qBACA,gBAGJ,6CACI,gBAGJ,8CACI,eAGJ,0BACI,uDACA,gCAGJ,0BACI,uDAGJ,2BACI,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,sBACI,QACA,SAGJ,gEACI,kBAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBAGJ,0BACI,mBACA,uBACA,gBChQJ,gCACI,gBAGJ,wCACI,SACA,UACA,aAGJ,8DACI,aAGJ,iDACI,iBACA,2BAGJ,iDACI,SACA,gBAGJ,qEACI,qCCvBJ,SACI,kBACA,iDACA,kEAEA,wEACA,cACA,6CACA,8CAKA,kBAGJ,aACI,oEACA,kBACA,WACA,YACA,eACA,+FACA,UAGJ,wBACI,mBACA,6BACA,0CACA,4BAGJ,wBACI,mBACA,kBAGJ,gFAEI,wCAGJ,+CACI,cACA,mBAGJ,kBACI,aACA,kBACA,mDACA,qHAOA,sBACA,kBACA,4EACA,+BACA,mBACA,gBAGJ,uBACI,qBACA,YACA,WACA,YACA,sBAGJ,uBACI,YACA,eACA,sBAGJ,2BACI,WACA,YCrFJ,SACI,cACA,kBACA,gBACA,qCACA,uDACA,oBAGJ,gEAEI,YAGJ,sBACI,gBAGJ,iBACI,YACA,YACA,+CAGJ,8BACI,SACA,UAGJ,8CACI,iBACI,kNAIJ,oDACA,0CACA,sDAGJ,2DACI,2BACA,0BAGJ,4DACI,kBAGJ,kBACI,aACA,cACI,mNAOJ,4BACA,YACA,sBACA,gCACA,qCACA,6BACA,gBAGJ,4CACI,iEACA,iCAGJ,2DACI,gBAGJ,uEACI,kBACA,mBAGJ,uEACI,aACA,kBAGJ,yEACI,YAGJ,+BACI,gBACA,UACA,mBAGJ,cACI,cACA,cAEA,eACA,iBACA,iBACA,mBAGJ,6CACI,eAGJ,8CACI,mBAGJ,4CACI,aACA,8BACA,mBACA,kBAGJ,sCACI,yBACA,WACI,qGAEJ,gEACA,WACI,iLAIR,2BACI,gBAGJ,mDACI,sCAGJ,eACI,aACA,mBACA,kGACA,kBACA,iBAGJ,sCACI,uBAGJ,0BACI,mBAGJ,4BACI,gBAGJ,4BACI,kBAGJ,sBACI,sBACA,iBACA,WACA,YACA,cAGJ,wDACI,cAGJ,2BACI,sBAGJ,wBACI,gBACA,kBAEA,6CACA,gBACA,kBACA,cAGJ,iBACI,gBAGJ,8CACI,mBACA,iBAGJ,kBACI,iBAGJ,qCAEI,kBAGJ,mBACI,mBACA,gBACA,kBACA,iBACA,iBACA,mBACA,eAGJ,0BACI,sBACA,eACA,YACA,2HAMA,2HAMA,kBAGJ,+BACI,eACA,qBACA,6BACA,mBAGJ,4CACI,kBAGJ,oBACI,oBACA,aACA,kBAGJ,oDACI,cAGJ,oFACI,yBACA,eAGJ,iCACI,kBAGJ,+CAEI,WACA,YACA,sBAGJ,8DACI,qBAGJ,6HAGI,gBACA,mBAGJ,gDACI,sBACA,gDACA,YACA,WACI,gNAEJ,+BACA,mCAGJ,6CACI,gBACA,eAGJ,8CACI,qCACA,gBAGJ,yFAEI,SACA,gBACA,gBACA,gBAGJ,4DACI,kBAGJ,gDACI,iBACA,kBACA,oBACA,UAEA,eACA,uBACA,iBACA,iBACA,WAGJ,oDACI,eAGJ,qDACI,iBACA,gBACA,mBAGJ,sCACI,WAGJ,iDACI,YAGJ,kDACI,kBACA,UACA,YACA,gBACA,6CACA,gBAEA,yDACI,WACA,cACA,kBACA,UACA,WACA,WACA,YACA,6TAgBR,8CACI,kBACA,WACA,iBACA,gBACA,gBACA,sCACA,WAEA,eACA,0CACA,iBACA,UAGJ,4DACI,WACA,cACA,iBACA,kBACA,oBACA,YACA,UACA,WACA,kBACA,mBCvZJ,cACI,6BAGJ,QACI,4NAMA,+CACA,cAGJ,mDAEI,aAGJ,4CACI,sBACA,kBAGJ,sBACI,aACA,iBAGJ,sBACI,kBAGJ,2FACI,aAGJ,iBACI,oBACA,sBACA,gBACA,gBACA,mBAGJ,iDACI,aAGJ,+BACI,yBACA,aACA,sBAGJ,iFACI,qCACA,iBAGJ,oCACI,2BAGJ,iBACI,aACA,mBAGJ,0BACI,iBAGJ,cACI,2BACA,gBAGJ,8CACI,kBAGJ,gEACI,WACA,YAGJ,+BACI,iBAGJ,gCACI,gBAGJ,kBACI,cAGJ,uDACI,qBAGJ,sCACI,WACA,YACA,uBAGJ,0CACI,kBAGJ,4CACI,YAGJ,kCACI,kBACA,gBAEA,0CACI,WACA,cACA,kBACA,WACA,WACA,SACA,SACA,0GAQR,8BACI,kBAGJ,gHACI,aAGJ,uBACI,aACA,iBAGJ,eACI,gBACA,yBACA,kBACA,oBACA,cAEA,4FAII,yCACA,aAIR,0FAEI,aAGJ,uCACI,gBACA,UACA,YACA,WACA,yBACA,2BAGJ,+CACI,6OAGJ,oEACI,UACA,WACA,YACA,qBACA,gBACA,yCAGJ,oCACI,gBAGJ,qBACI,iBACA,WAGJ,iCACI,gBAGJ,uBACI,cACA,aACA,YACA,UACA,eACA,gBACA,gBAEA,cACA,mBACA,gBACA,cACA,cAEA,0CACI,WACA,YAGJ,gDACI,yBACA,mBACA,WAGJ,sDACI,mBAIR,uFACI,YAGJ,aACI,cAGJ,2BACI,iBACA,mBACA,8BACA,aACA,qBAGJ,kDACI,YACA,2CACA,gBACA,gBACA,eACA,sBACA,0BCrQJ,YACI,eACA,yBACA,kBACA,iBACA,mBACA,kBACA,eAEA,kBACI,mBAIR,aACI,aACA,yBACA,SACA,gBAGJ,mCACI,kBACA,oBAEA,qFAEI,WACA,cACA,kBACA,QACA,SACA,+BAGJ,2CACI,UACA,0BAGJ,0CACI,WACA,2BAIR,uCAEI,kBACA,yBACA,kBACA,mBAGJ,uBACI,kBACA,YAEA,6BACI,iBAIR,kCACI,iBAGJ,qCACI,cACA,kBACA,YACA,kBACA,mBAGJ,4BACI,kBACA,cACA,iBACA,UAKA,4BACI,WACA,qBACA,6BACA,oCACA,qCACA,sBAGJ,0BACI,eAIR,uEAGI,kBACA,MACA,YAGJ,oBACI,QACA,OACA,iBAGJ,0BACI,QACA,WACA,mBAGJ,yBACI,OACA,WACA,mBAGJ,sBACI,kBACA,MACA,QACA,SACA,WACA,iBAGJ,gFAGI,kBACA,YACA,YACA,iBAGJ,uBACI,QACA,OAGJ,6BACI,QACA,WACA,mBAGJ,4BACI,OACA,WACA,mBAGJ,qBACI,kBACA,MACA,SACA,OACA,WACA,iBCnKJ,WACI,cACA,kBACA,yBACA,MACA,OACA,sGACA,mBACA,6CACA,aAGJ,gBACI,aACA,mBACA,mBACA,gBACA,YACA,mBAGJ,oBACI,aACA,yBAGJ,iBACI,YACA,kBACA,kBAGJ,kBACI,YACA,YACA,WACA,eAGJ,+CAEI,qBACA,sBAGJ,uBACI,aACA,2DAGJ,wBACI,kBACA,iBACA,gBACA,mBACA,WACI,mLAWR,kBACI,cACA,kBACA,gBACA,iBACA,UACA,WACA,sBACA,kBAGJ,0CACI,8BACA,6BAGJ,qDAEI,aACA,oBACA,WAGJ,6BACI,iBACA,4EAGJ,wBACI,kBACA,4EAGJ,cACI,cAGJ,2CACI,yCAGJ,sCACI,yCAGJ,0BACI,kBACA,sBAEA,mEAEI,WACA,cACA,kBACA,oBACA,UACA,0BAGJ,iCACI,QAIR,4DAEI,YACA,YAGJ,8BACI,aAGJ,wDAEI,UAGJ,2BACI,aACA,gBACA,gBAGJ,oDACI,aACA,sBACA,8BACA,YACA,UAGJ,wDACI,aACA,mBACA,kBAEA,4DACI,YAIR,kDACI,YAGJ,cACI,eAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,2BACI,WAGJ,mBACI,oBAGJ,yBACI,iBACA,aAGJ,0CACI,UACA,iBACA,sBACA","file":"ueb-style.min.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-knot.scss","../../scss/ueb-link.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-ui-controls.scss","../../scss/ueb-window.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,mBACA,wDAGJ,WACI,qBACA,uBACA,kEAGJ,cACI,eACA,6CACA,cACA,kBACA,8EACA,+BACA,WACA,iBAGJ,kBACI,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,0BACA,UAGJ,8BACI,GACI,cAGJ,KACI,eAIR,qCACI,mCAGJ,mBACI,iBACA,YACA,cAEA,eACA,iBACA,oBAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,UACI,kFACA,kBACA,eACA,gBACA,+DACA,gEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,wCACA,qBACA,gBAGJ,6CACI,gBAGJ,8CACI,eAGJ,0BACI,uDACA,gCAGJ,0BACI,uDAGJ,2BACI,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,sBACI,QACA,SAGJ,gEACI,kBAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBAGJ,0BACI,mBACA,uBACA,gBChQJ,gCACI,gBAGJ,wCACI,SACA,UACA,aAGJ,8DACI,aAGJ,iDACI,iBACA,2BAGJ,iDACI,SACA,gBAGJ,qEACI,qCCvBJ,SACI,kBACA,iDACA,kEAEA,wEACA,cACA,6CACA,8CAKA,kBAGJ,aACI,oEACA,kBACA,WACA,YACA,eACA,+FACA,UAGJ,wBACI,mBACA,6BACA,0CACA,4BAGJ,wBACI,mBACA,kBAGJ,gFAEI,wCAGJ,+CACI,cACA,mBAGJ,kBACI,aACA,kBACA,mDACA,qHAOA,sBACA,kBACA,4EACA,+BACA,mBACA,gBAGJ,uBACI,qBACA,YACA,WACA,YACA,sBAGJ,uBACI,YACA,eACA,sBAGJ,2BACI,WACA,YCrFJ,SACI,cACA,kBACA,gBACA,qCACA,uDACA,oBAGJ,gEAEI,YAGJ,sBACI,gBAGJ,iBACI,YACA,YACA,+CAGJ,8BACI,SACA,UAGJ,8CACI,iBACI,kNAIJ,oDACA,0CACA,sDAGJ,2DACI,2BACA,0BAGJ,4DACI,kBAGJ,kBACI,aACA,cACI,mNAOJ,4BACA,YACA,sBACA,gCACA,qCACA,6BACA,gBAGJ,oDACI,iEACA,iCACA,gBAGJ,2DACI,gBAGJ,uEACI,kBACA,mBAGJ,uEACI,aACA,kBAGJ,yEACI,YAGJ,+BACI,gBACA,UACA,mBAGJ,cACI,cACA,cAEA,eACA,iBACA,iBACA,mBAGJ,6CACI,eAGJ,8CACI,mBAGJ,4CACI,aACA,8BACA,mBACA,kBAGJ,sCACI,yBACA,WACI,qGAEJ,gEACA,WACI,iLAIR,2BACI,gBAGJ,mDACI,sCAGJ,eACI,aACA,mBACA,kGACA,kBACA,iBAGJ,sCACI,uBAGJ,0BACI,mBAGJ,4BACI,gBAGJ,4BACI,kBAGJ,sBACI,sBACA,iBACA,WACA,YACA,cAGJ,wDACI,cAGJ,2BACI,sBAGJ,wBACI,gBACA,kBAEA,6CACA,gBACA,kBACA,cAGJ,iBACI,gBAGJ,8CACI,mBACA,iBAGJ,kBACI,iBAGJ,qCAEI,kBAGJ,mBACI,mBACA,gBACA,kBACA,iBACA,iBACA,mBACA,eAGJ,0BACI,sBACA,eACA,YACA,2HAMA,2HAMA,kBAGJ,+BACI,eACA,qBACA,6BACA,mBAGJ,4CACI,kBAGJ,oBACI,oBACA,aACA,kBAGJ,oDACI,cAGJ,oFACI,yBACA,eAGJ,iCACI,kBAGJ,+CAEI,WACA,YACA,sBAGJ,8DACI,qBAGJ,6HAGI,gBACA,mBAGJ,gDACI,sBACA,gDACA,YACA,WACI,gNAEJ,+BACA,mCAGJ,6CACI,gBACA,eAGJ,8CACI,qCACA,gBAGJ,yFAEI,SACA,gBACA,gBACA,gBAGJ,4DACI,kBAGJ,gDACI,iBACA,kBACA,oBACA,UAEA,eACA,uBACA,iBACA,iBAGJ,yEACI,eACA,sBAGJ,qEACI,WAGJ,oDACI,eAGJ,qDACI,iBACA,gBACA,mBAGJ,oDACI,sBACA,gDACA,6IAOJ,8GAEI,cAGJ,+EACI,cAGJ,iDACI,WACA,iBACA,uBACA,gBAGJ,sCACI,WAGJ,iDACI,YAGJ,kDACI,kBACA,UACA,YACA,gBACA,6CACA,gBAEA,yDACI,WACA,cACA,kBACA,UACA,WACA,WACA,YACA,6TAgBR,8CACI,kBACA,WACA,iBACA,gBACA,gBACA,sCACA,WAEA,eACA,0CACA,iBACA,UAGJ,4DACI,WACA,cACA,iBACA,kBACA,oBACA,YACA,UACA,WACA,kBACA,mBC1bJ,cACI,6BAGJ,QACI,4NAMA,+CACA,cAGJ,mDAEI,aAGJ,4CACI,sBACA,kBAGJ,sBACI,aACA,iBAGJ,sBACI,kBAGJ,2FACI,aAGJ,iBACI,oBACA,sBACA,gBACA,gBACA,mBAGJ,mDACI,UAGJ,iDACI,aAGJ,+BACI,yBACA,aACA,sBAGJ,iFACI,qCACA,iBAGJ,oCACI,2BAGJ,iBACI,aACA,mBAGJ,0BACI,iBAGJ,cACI,2BACA,gBAGJ,8CACI,kBAGJ,gEACI,WACA,YAGJ,+BACI,iBAGJ,gCACI,gBAGJ,kBACI,cAGJ,uDACI,qBAGJ,sCACI,WACA,YACA,uBAGJ,0CACI,kBAGJ,4CACI,YAGJ,kCACI,kBACA,gBAEA,0CACI,WACA,cACA,kBACA,WACA,WACA,SACA,SACA,0GAQR,8BACI,kBAGJ,gHACI,aAGJ,uBACI,aACA,iBAGJ,eACI,gBACA,yBACA,kBACA,oBACA,cAEA,4FAII,yCACA,aAIR,0FAEI,aAGJ,uCACI,gBACA,UACA,YACA,WACA,yBACA,2BAGJ,+CACI,6OAGJ,oEACI,UACA,WACA,YACA,qBACA,gBACA,yCAGJ,oCACI,gBAGJ,qBACI,iBACA,WAGJ,iCACI,gBAGJ,uBACI,cACA,aACA,YACA,UACA,eACA,gBACA,gBAEA,cACA,mBACA,gBACA,cACA,cAEA,0CACI,WACA,YAGJ,gDACI,yBACA,mBACA,WAGJ,sDACI,mBAIR,uFACI,YAGJ,aACI,cAGJ,2BACI,iBACA,mBACA,8BACA,aACA,qBAGJ,kDACI,YACA,2CACA,gBACA,gBACA,eACA,sBACA,0BCzQJ,YACI,eACA,yBACA,kBACA,iBACA,mBACA,kBACA,eAEA,kBACI,mBAIR,aACI,aACA,yBACA,SACA,gBAGJ,mCACI,kBACA,oBAEA,qFAEI,WACA,cACA,kBACA,QACA,SACA,+BAGJ,2CACI,UACA,0BAGJ,0CACI,WACA,2BAIR,uCAEI,kBACA,yBACA,kBACA,mBAGJ,uBACI,kBACA,YAEA,6BACI,iBAIR,kCACI,iBAGJ,qCACI,cACA,kBACA,YACA,kBACA,mBAGJ,4BACI,kBACA,cACA,iBACA,UAKA,4BACI,WACA,qBACA,6BACA,oCACA,qCACA,sBAGJ,0BACI,eAIR,uEAGI,kBACA,MACA,YAGJ,oBACI,QACA,OACA,iBAGJ,0BACI,QACA,WACA,mBAGJ,yBACI,OACA,WACA,mBAGJ,sBACI,kBACA,MACA,QACA,SACA,WACA,iBAGJ,gFAGI,kBACA,YACA,YACA,iBAGJ,uBACI,QACA,OAGJ,6BACI,QACA,WACA,mBAGJ,4BACI,OACA,WACA,mBAGJ,qBACI,kBACA,MACA,SACA,OACA,WACA,iBCnKJ,WACI,cACA,kBACA,yBACA,MACA,OACA,sGACA,mBACA,6CACA,aAGJ,gBACI,aACA,mBACA,mBACA,gBACA,YACA,mBAGJ,oBACI,aACA,yBAGJ,iBACI,YACA,kBACA,kBAGJ,kBACI,YACA,YACA,WACA,eAGJ,+CAEI,qBACA,sBAGJ,uBACI,aACA,2DAGJ,wBACI,kBACA,iBACA,gBACA,mBACA,WACI,mLAWR,kBACI,cACA,kBACA,gBACA,iBACA,UACA,WACA,sBACA,kBAGJ,0CACI,8BACA,6BAGJ,qDAEI,aACA,oBACA,WAGJ,6BACI,iBACA,4EAGJ,wBACI,kBACA,4EAGJ,cACI,cAGJ,2CACI,yCAGJ,sCACI,yCAGJ,0BACI,kBACA,sBAEA,mEAEI,WACA,cACA,kBACA,oBACA,UACA,0BAGJ,iCACI,QAIR,4DAEI,YACA,YAGJ,8BACI,aAGJ,wDAEI,UAGJ,2BACI,aACA,gBACA,gBAGJ,oDACI,aACA,sBACA,8BACA,YACA,UAGJ,wDACI,aACA,mBACA,kBAEA,4DACI,YAIR,kDACI,YAGJ,cACI,eAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,yDACI,oCAGJ,2BACI,WAGJ,mBACI,oBAGJ,yBACI,iBACA,aAGJ,0CACI,UACA,iBACA,sBACA","file":"ueb-style.min.css"} \ No newline at end of file diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index efbe18cf..cd9a00db 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -29,9 +29,11 @@ class Configuration { black: i$3`20, 20, 20`, blue: i$3`84, 122, 156`, darkBlue: i$3`32, 80, 128`, + darkerBlue: i$3`18, 18, 130`, darkTurquoise: i$3`19, 100, 137`, gray: i$3`150,150,150`, green: i$3`95, 129, 90`, + intenseGreen: i$3`42, 140, 42`, lime: i$3`150, 160, 30`, red: i$3`151, 33, 32`, turquoise: i$3`46, 104, 106`, @@ -140,6 +142,7 @@ class Configuration { edGraph: "/Script/Engine.EdGraph", eDrawDebugTrace: "/Script/Engine.EDrawDebugTrace", eMaterialSamplerType: "/Script/Engine.EMaterialSamplerType", + eNiagara_Float4Channel: "/Niagara/Enums/ENiagara_Float4Channel.ENiagara_Float4Channel", enum: "/Script/CoreUObject.Enum", enumLiteral: "/Script/BlueprintGraph.K2Node_EnumLiteral", eSamplerSourceMode: "/Script/Engine.ESamplerSourceMode", @@ -191,7 +194,17 @@ class Configuration { materialExpressionTextureSample: "/Script/Engine.MaterialExpressionTextureSample", materialGraphNode: "/Script/UnrealEd.MaterialGraphNode", materialGraphNodeComment: "/Script/UnrealEd.MaterialGraphNode_Comment", + metasoundEditorGraphExternalNode: "/Script/MetasoundEditor.MetasoundEditorGraphExternalNode", multiGate: "/Script/BlueprintGraph.K2Node_MultiGate", + niagaraBool: "/Script/Niagara.NiagaraBool", + niagaraClipboardContent: "/Script/NiagaraEditor.NiagaraClipboardContent", + niagaraDataInterfaceVolumeTexture: "/Script/Niagara.NiagaraDataInterfaceVolumeTexture", + niagaraFloat: "/Script/Niagara.NiagaraFloat", + niagaraMatrix: "/Script/Niagara.NiagaraMatrix", + niagaraNodeFunctionCall: "/Script/NiagaraEditor.NiagaraNodeFunctionCall", + niagaraNodeOp: "/Script/NiagaraEditor.NiagaraNodeOp", + niagaraNumeric: "/Script/Niagara.NiagaraNumeric", + niagaraPosition: "/Script/Niagara.NiagaraPosition", pawn: "/Script/Engine.Pawn", pcgEditorGraphNode: "/Script/PCGEditor.PCGEditorGraphNode", pcgEditorGraphNodeInput: "/Script/PCGEditor.PCGEditorGraphNodeInput", @@ -199,9 +212,11 @@ class Configuration { pcgHiGenGridSizeSettings: "/Script/PCG.PCGHiGenGridSizeSettings", pcgSubgraphSettings: "/Script/PCG.PCGSubgraphSettings", promotableOperator: "/Script/BlueprintGraph.K2Node_PromotableOperator", + quat4f: "/Script/CoreUObject.Quat4f", reverseForEachLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop", rotator: "/Script/CoreUObject.Rotator", select: "/Script/BlueprintGraph.K2Node_Select", + self: "/Script/BlueprintGraph.K2Node_Self", slateBlueprintLibrary: "/Script/UMG.SlateBlueprintLibrary", spawnActorFromClass: "/Script/BlueprintGraph.K2Node_SpawnActorFromClass", switchEnum: "/Script/BlueprintGraph.K2Node_SwitchEnum", @@ -217,44 +232,10 @@ class Configuration { variableSet: "/Script/BlueprintGraph.K2Node_VariableSet", vector: "/Script/CoreUObject.Vector", vector2D: "/Script/CoreUObject.Vector2D", + vector3f: "/Script/CoreUObject.Vector3f", + vector4f: "/Script/CoreUObject.Vector4f", whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop", } - static pinColor = { - [this.paths.rotator]: i$3`157, 177, 251`, - [this.paths.transform]: i$3`227, 103, 0`, - [this.paths.vector]: i$3`251, 198, 34`, - "Any": i$3`132, 132, 132`, - "Any[]": i$3`132, 132, 132`, - "blue": i$3`0, 0, 255`, - "bool": i$3`147, 0, 0`, - "byte": i$3`0, 109, 99`, - "class": i$3`88, 0, 186`, - "default": i$3`255, 255, 255`, - "delegate": i$3`255, 56, 56`, - "enum": i$3`0, 109, 99`, - "exec": i$3`240, 240, 240`, - "green": i$3`0, 255, 0`, - "int": i$3`31, 224, 172`, - "int64": i$3`169, 223, 172`, - "interface": i$3`238, 252, 168`, - "name": i$3`201, 128, 251`, - "object": i$3`0, 167, 240`, - "Param": i$3`255, 166, 39`, - "Param[]": i$3`255, 166, 39`, - "Point": i$3`63, 137, 255`, - "Point[]": i$3`63, 137, 255`, - "real": i$3`54, 208, 0`, - "red": i$3`255, 0, 0`, - "string": i$3`251, 0, 209`, - "struct": i$3`0, 88, 201`, - "Surface": i$3`69, 196, 126`, - "Surface[]": i$3`69, 196, 126`, - "text": i$3`226, 121, 167`, - "Volume": i$3`230, 69, 188`, - "Volume[]": i$3`230, 69, 188`, - "wildcard": i$3`128, 120, 120`, - } - static pinColorMaterial = i$3`120, 120, 120` static pinInputWrapWidth = 143 // px static removeEventName = "ueb-element-delete" static scale = { @@ -327,6 +308,12 @@ class Configuration { "Virtual Linear Color", "Virtual Linear Grayscal", ], + [this.paths.eNiagara_Float4Channel]: [ + ["NewEnumerator0", "R"], + ["NewEnumerator1", "G"], + ["NewEnumerator2", "B"], + ["NewEnumerator3", "A"], + ], [this.paths.eSamplerSourceMode]: ["From texture asset", "Shared: Wrap", "Shared: Clamp", "Hidden"], [this.paths.eSpawnActorCollisionHandlingMethod]: [ ["Undefined", "Default"], @@ -352,7 +339,6 @@ class Configuration { "Alt", "Meta", ] - /** @type {["R", "G", "B", "A"]} */ static rgba = ["R", "G", "B", "A"] static Keys = { /* UE name: JS name */ @@ -447,14 +433,6 @@ class Configuration { } } -class Shortcuts { - static deleteNodes = "Delete" - static duplicateNodes = "(bCtrl=True,Key=D)" - static enableLinkDelete = "LeftAlt" - static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 1:1 zoom - static selectAllNodes = "(bCtrl=True,Key=A)" -} - class ComputedType { #f @@ -482,6 +460,7 @@ class ComputedType { * inlined?: Boolean, * quoted?: Boolean, * silent?: Boolean, + * uninitialized?: Boolean, * predicate?: (value: T) => Boolean, * }} AttributeInfoSource */ @@ -499,6 +478,7 @@ class AttributeInfo { inlined: false, // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123) quoted: false, // Key is serialized with quotes silent: false, // Do not serialize if default + uninitialized: false, // Do not initialize with default } /** @param {AttributeInfoSource} source */ @@ -512,6 +492,7 @@ class AttributeInfo { this.inlined = source.inlined; this.quoted = source.quoted; this.silent = source.silent; + this.uninitialized = source.uninitialized; this.predicate = source.predicate; if (this.type === Array && this.default instanceof Array && this.default.length > 0) { this.type = this.default @@ -926,8 +907,9 @@ class Utility { * @template T * @param {Array} a * @param {Array} b + * @param {(l: T, r: T) => Boolean} predicate */ - static mergeArrays(a = [], b = []) { + static mergeArrays(a = [], b = [], predicate = (l, r) => l == r) { let result = []; a = [...a]; b = [...b]; @@ -935,7 +917,7 @@ class Utility { while (true) { for (let j = 0; j < b.length; ++j) { for (let i = 0; i < a.length; ++i) { - if (a[i] == b[j]) { + if (predicate(a[i], b[j])) { // Found an element in common in the two arrays result.push( // Take and append all the elements skipped from a @@ -956,6 +938,13 @@ class Utility { return [...(new Set(result.concat(...a, ...b)))] } + /** @param {String} value */ + static escapeNewlines(value) { + return value + .replaceAll("\n", "\\n") // Replace newline with \n + .replaceAll("\t", "\\t") // Replace tab with \t + } + /** @param {String} value */ static escapeString(value) { return value @@ -1015,8 +1004,10 @@ class Utility { } /** @param {String} pathValue */ - static getNameFromPath(pathValue) { - return pathValue.match(/[^\.\/]+$/)?.[0] ?? "" + static getNameFromPath(pathValue, dropCounter = false) { + // From end to the first "/" or "." + const regex = dropCounter ? /([^\.\/]+?)(?:_\d+)$/ : /([^\.\/]+)$/; + return pathValue.match(regex)?.[1] ?? "" } /** @param {LinearColorEntity} value */ @@ -1119,79 +1110,213 @@ class Utility { } /** - * @typedef {{ - * consumeEvent?: Boolean, - * listenOnFocus?: Boolean, - * unlistenOnTextEdit?: Boolean, - * }} Options + * @template {IEntity} EntityT + * @template {ITemplate} TemplateT */ - -/** @template {Element} T */ -class IInput { - - /** @type {T} */ - #target - get target() { - return this.#target - } +class IElement extends s { /** @type {Blueprint} */ #blueprint get blueprint() { return this.#blueprint } + set blueprint(v) { + this.#blueprint = v; + } - consumeEvent + /** @type {EntityT} */ + #entity + get entity() { + return this.#entity + } + set entity(entity) { + this.#entity = entity; + } - /** @type {Object} */ - options + /** @type {TemplateT} */ + #template + get template() { + return this.#template + } + isInitialized = false + isSetup = false - listenHandler = () => this.listenEvents() - unlistenHandler = () => this.unlistenEvents() + /** @type {IInput[]} */ + inputObjects = [] /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Options} options + * @param {EntityT} entity + * @param {TemplateT} template */ - constructor(target, blueprint, options = {}) { - options.consumeEvent ??= false; - options.listenOnFocus ??= false; - options.unlistenOnTextEdit ??= false; - this.#target = target; - this.#blueprint = blueprint; - this.consumeEvent = options.consumeEvent; - this.options = options; + 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; } - 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); + connectedCallback() { + super.connectedCallback(); + this.blueprint = /** @type {Blueprint} */(this.closest("ueb-blueprint")); + if (this.isInitialized) { + this.requestUpdate(); + this.updateComplete.then(() => this.setup()); } - if (this.blueprint.focused) { - this.listenEvents(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (this.isSetup) { + this.updateComplete.then(() => this.cleanup()); } + this.acknowledgeDelete(); + } + + createRenderRoot() { + return this + } + + setup() { + this.template.setup(); + this.isSetup = true; } 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); + this.template.cleanup(); + this.isSetup = false; } - /* Subclasses will probabily override the following methods */ - listenEvents() { + /** @param {PropertyValues} changedProperties */ + willUpdate(changedProperties) { + super.willUpdate(changedProperties); + this.template.willUpdate(changedProperties); } - unlistenEvents() { + /** @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); + } + + acknowledgeDelete() { + let deleteEvent = new CustomEvent(Configuration.removeEventName); + this.dispatchEvent(deleteEvent); + } + + /** @param {IElement} element */ + isSameGraph(element) { + return this.blueprint && this.blueprint == element?.blueprint + } +} + +/** + * @template {IEntity} EntityT + * @template {ITemplate} TemplateT + * @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 {Coordinates} 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; + } +} + +class Shortcuts { + static deleteNodes = "Delete" + static duplicateNodes = "(bCtrl=True,Key=D)" + static enableLinkDelete = "LeftAlt" + static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 1:1 zoom + static selectAllNodes = "(bCtrl=True,Key=A)" +} + +class ElementFactory { + + /** @type {Map>} */ + static #elementConstructors = new Map() + + /** + * @param {String} tagName + * @param {AnyConstructor} entityConstructor + */ + static registerElement(tagName, entityConstructor) { + ElementFactory.#elementConstructors.set(tagName, entityConstructor); + } + + /** @param {String} tagName */ + static getConstructor(tagName) { + return ElementFactory.#elementConstructors.get(tagName) } } @@ -1240,7 +1365,7 @@ class Reply { static makePathNode(parser, index = 0, previous = null) { return /** @type {PathNode} */({ parent: previous, - parser, + current: parser, index, }) } @@ -1254,26 +1379,37 @@ class Parser { /** @type {(new (...args: any) => Parser) & typeof Parser} */ Self - /** - * @param {String} target - * @param {String} value - */ - static appendBeforeHighlight(target, value) { - if (target.endsWith(Parser.highlight)) { - target = target.replace(/(?=(?:\n|^).+$)/, value); - } else { - target += value; - } - return target + /** @param {String} value */ + static frame(value, label = "", indentation = "") { + label = value ? "[ " + label + " ]" : ""; + let rows = value.split("\n"); + const width = Math.max(...rows.map(r => r.length)); + const rightPadding = width < label.length ? " ".repeat(label.length - width) : ""; + for (let i = 0; i < rows.length; ++i) { + rows[i] = + indentation + + "| " + + rows[i] + + " ".repeat(width - rows[i].length) + + rightPadding + + " |"; + } + if (label.length < width) { + label = label + "─".repeat(width - label.length); + } + const rowA = "┌─" + label + "─┐"; + const rowB = indentation + "└─" + "─".repeat(label.length) + "─┘"; + rows = [rowA, ...rows, rowB]; + return rows.join("\n") } - /** @param {String} value */ - static lastRowLength(value, firstRowPadding = 0) { - // This regex always matches and group 2 (content of the last row) is always there - const match = value.match(/(?:\n|(^))([^\n]*)$/); - // Group 1 tells wheter or not it matched the first row (last row is also first row) - const additional = match[1] !== undefined ? firstRowPadding : 0; - return match[2].length + additional + /** + * @param {PathNode} path + * @param {Number} index + * @returns {PathNode} + */ + makePath(path, index) { + return { current: this, parent: path, index } } /** @@ -1284,48 +1420,22 @@ class Parser { if (context.highlighted instanceof Parser) { return context.highlighted === this } - if (!context.highlighted || !path) { + if (!context.highlighted || !path?.current) { return false } - let a, prevA, b, prevB; - loop: + let a, b; for ( a = path, b = /** @type {PathNode} */(context.highlighted); - a && b; - prevA = a, a = a.parent, - prevB = b, b = b.parent + a.current && b.current; + a = a.parent, + b = b.parent ) { - if (a.parser !== b.parser || a.index !== b.index) { - if (!prevA || !prevB) { - return false // Starting nodes did not match - } - // Try to speculatevely walk the path in reverse to find matching nodes - let nextA; - let nextB; - for ( - nextA = a, nextB = b; - nextA || nextB; - nextA = nextA?.parent, nextB = nextB?.parent - ) { - const aMatches = nextA?.parser === prevA.parser; - const bMatches = nextB?.parser === prevB.parser; - if (aMatches || bMatches) { - if (aMatches) { - prevA = nextA; - } - if (bMatches) { - prevB = nextB; - } - a = prevA; - b = prevB; - continue loop - } - } + if (a.current !== b.current || a.index !== b.index) { return false } } - return true + return !a.current && !b.current } /** @param {PathNode?} path */ @@ -1334,7 +1444,7 @@ class Parser { return false } for (path = path.parent; path != null; path = path.parent) { - if (path.parser === this) { + if (path.current === this) { return true } } @@ -1345,28 +1455,37 @@ class Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index * @returns {Result>} */ - parse(context, position, path) { + parse(context, position, path, index) { return null } /** @param {PathNode} path */ - toString(context = Reply.makeContext(null, ""), indent = 0, path = null) { + toString(context = Reply.makeContext(null, ""), indentation = "", path = null, index = 0) { + path = this.makePath(path, index); if (this.isVisited(path)) { - return "<...>" // Recursive parser + return "<...>" + } + const isVisited = this.isVisited(path); + const isHighlighted = this.isHighlighted(context, path); + let result = isVisited ? "<...>" : this.doToString(context, isHighlighted ? "" : indentation, path, index); + if (isHighlighted) { + /** @type {String[]} */ + result = Parser.frame(result, Parser.highlight, indentation); } - const result = this.doToString(context, indent, path); return result } /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { + doToString(context, indentation, path, index) { return `${this.constructor.name} does not implement toString()` } } @@ -1389,8 +1508,10 @@ class StringParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); const end = position + this.#value.length; const value = context.input.substring(position, end); const result = this.#value === value @@ -1402,18 +1523,12 @@ class StringParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const inlined = this.value.replaceAll("\n", "\\n"); - let result = !this.value.match(/^[a-zA-Z]$/) - ? `"${inlined.replaceAll('"', '\\"')}"` - : inlined; - if (this.isHighlighted(context, path)) { - result += "\n" + Parser.indentation.repeat(indent) + "^".repeat(result.length) + " " + Parser.highlight; - } - return result + doToString(context, indentation, path, index) { + return `"${this.value.replaceAll("\n", "\\n").replaceAll('"', '\\"')}"` } } @@ -1425,35 +1540,28 @@ class SuccessParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); return Reply.makeSuccess(position, "", path, 0) } /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { + doToString(context, indentation, path, index) { return "" - + (this.isHighlighted(context, path) - ? `\n${Parser.indentation.repeat(indent)}^^^^^^^^^ ${Parser.highlight}` - : "" - ) } } /** @template {Parser[]} T */ class AlternativeParser extends Parser { - static highlightRegexp = new RegExp( - // Matches the beginning of a row containing Parser.highlight only when after the first row of an alternative - String.raw`(?<=[^\S\n]*\| .*\n)^(?=[^\S\n]*\^+ ${Parser.highlight}(?:\n|$))`, - "m" - ) - #parsers get parsers() { return this.#parsers @@ -1469,15 +1577,13 @@ class AlternativeParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); const result = Reply.makeSuccess(0, /** @type {ParserValue} */("")); for (let i = 0; i < this.#parsers.length; ++i) { - const outcome = this.#parsers[i].parse( - context, - position, - { parent: path, parser: this.#parsers[i], index: i } - ); + const outcome = this.#parsers[i].parse(context, position, path, i); if (outcome.bestPosition > result.bestPosition) { result.bestParser = outcome.bestParser; result.bestPosition = outcome.bestPosition; @@ -1496,33 +1602,31 @@ class AlternativeParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const indentation = Parser.indentation.repeat(indent); - const deeperIndentation = Parser.indentation.repeat(indent + 1); + doToString(context, indentation, path, index) { + // Short syntax for optional parser if (this.#parsers.length === 2 && this.#parsers[1] instanceof SuccessParser) { - let result = this.#parsers[0].toString( - context, - indent, - { parent: path, parser: this.#parsers[0], index: 0 } - ); + let result = this.#parsers[0].toString(context, indentation, path, 0); if (!(this.#parsers[0] instanceof StringParser)) { result = "<" + result + ">"; } result += "?"; return result } - let serialized = this.#parsers - .map((parser, index) => parser.toString(context, indent + 1, { parent: path, parser, index })) - .join("\n" + deeperIndentation + "| "); - if (context.highlighted) { - serialized = serialized.replace(AlternativeParser.highlightRegexp, " "); - } + const deeperIndentation = indentation + Parser.indentation; let result = "ALT<\n" - + (this.isHighlighted(context, path) ? `${indentation}^^^ ${Parser.highlight}\n` : "") - + deeperIndentation + serialized + + deeperIndentation + + this.#parsers + .map((parser, i) => parser.toString( + context, + deeperIndentation + " ".repeat(i === 0 ? 0 : Parser.indentation.length - 2), + path, + i, + )) + .join("\n" + deeperIndentation + "| ") + "\n" + indentation + ">"; return result } @@ -1555,10 +1659,12 @@ class ChainedParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index * @returns {Result>>>} */ - parse(context, position, path) { - const outcome = this.#parser.parse(context, position, { parent: path, parser: this.#parser, index: 0 }); + parse(context, position, path, index) { + path = this.makePath(path, index); + const outcome = this.#parser.parse(context, position, path, 0); if (!outcome.status) { // @ts-expect-error return outcome @@ -1577,26 +1683,12 @@ class ChainedParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const serialized = "chained"; - let result = this.#parser.toString(context, indent, { parent: path, parser: this.#parser, index: 0 }); - if (this.isHighlighted(context, path)) { - result += - " => " - + serialized - + "\n" - // Group 1 is the portion between the last newline and end or the whole text - + Parser.indentation.repeat(indent) - + " ".repeat(result.match(/(?:\n|^)([^\n]+)$/)?.[1].length + 4) - + "^".repeat(serialized.length) - + " " - + Parser.highlight; - } else { - result = Parser.appendBeforeHighlight(result, " => " + serialized); - } + doToString(context, indentation, path, index) { + const result = this.#parser.toString(context, indentation, path, 0) + " => chained"; return result } } @@ -1609,23 +1701,73 @@ class FailureParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { return Reply.makeFailure() } /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const result = "" + ( - this.isHighlighted(context, path) - ? `\n${Parser.indentation.repeat(indent)}^^^^^^^^^ ${Parser.highlight}` - : "" - ); + doToString(context, indentation, path, index) { + return "" + } +} + +/** @template {Parser} T */ +class Label extends Parser { + + #parser + get parser() { + return this.#parser + } + + #label = "" + + /** + * @param {T} parser + * @param {String} label + */ + constructor(parser, label) { + super(); + this.#parser = parser; + this.#label = label; + } + + /** + * @param {PathNode} path + * @param {Number} index + */ + makePath(path, index) { + return path // Label does not alter the path + } + + /** + * @param {Context} context + * @param {Number} position + * @param {PathNode} path + * @param {Number} index + */ + parse(context, position, path, index) { + this.parse = this.#parser.parse.bind(this.#parser); + return this.parse(context, position, path, index) + } + + /** + * @protected + * @param {Context} context + * @param {String} indentation + * @param {PathNode} path + * @param {Number} index + */ + doToString(context, indentation, path, index) { + let result = this.#parser.toString(context, "", path, index); + result = Parser.frame(result, this.#label, indentation); return result } } @@ -1644,6 +1786,27 @@ class LazyParser extends Parser { this.#parser = parser; } + /** + * @param {PathNode} path + * @param {Number} index + */ + makePath(path, index) { + return path + } + + /** + * @param {Context} context + * @param {PathNode} path + */ + isHighlighted(context, path) { + if (super.isHighlighted(context, path)) { + // If LazyParser is highlighted, then highlight its child + const childrenPath = { parent: path, parser: this.#resolvedPraser, index: 0 }; + context.highlighted = context.highlighted instanceof Parser ? this.#resolvedPraser : childrenPath; + } + return false + } + resolve() { if (!this.#resolvedPraser) { this.#resolvedPraser = this.#parser().getParser(); @@ -1655,24 +1818,25 @@ class LazyParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { this.resolve(); - return this.#resolvedPraser.parse(context, position, { parent: path, parser: this.#resolvedPraser, index: 0 }) + this.parse = this.#resolvedPraser.parse.bind(this.#resolvedPraser); + return this.parse(context, position, path, index) } /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const childrenPath = { parent: path, parser: this.#resolvedPraser, index: 0 }; - if (this.isHighlighted(context, path)) { - context.highlighted = context.highlighted instanceof Parser ? this.#resolvedPraser : childrenPath; - } - return this.resolve().toString(context, indent, childrenPath) + doToString(context, indentation, path, index) { + this.resolve(); + this.doToString = this.#resolvedPraser.toString.bind(this.#resolvedPraser); + return this.doToString(context, indentation, path, index) } } @@ -1714,9 +1878,11 @@ class Lookahead extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { - let result = this.#parser.parse(context, position, { parent: path, parser: this.#parser, index: 0 }); + parse(context, position, path, index) { + path = this.makePath(path, index); + let result = this.#parser.parse(context, position, path, 0); result = result.status == (this.#type === Lookahead.Type.POSITIVE_AHEAD) ? Reply.makeSuccess(position, "", path, position) : Reply.makeFailure(); @@ -1726,26 +1892,12 @@ class Lookahead extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - let result = "(" - + this.#type - + this.#parser.toString(context, indent, { parent: path, parser: this.#parser, index: 0 }) - + ")"; - if (this.isHighlighted(context, path)) { - result = result.replace( - /(\n)|$/, - "\n" - + Parser.indentation.repeat(indent) - + "^".repeat(this.#type.length + 1) - + " " - + Parser.highlight - + "$1" - ); - } - return result + doToString(context, indentation, path, index) { + return "(" + this.#type + this.#parser.toString(context, indentation, path, 0) + ")" } } @@ -1796,8 +1948,10 @@ class RegExpParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); const match = this.#anchoredRegexp.exec(context.input.substring(position)); if (match) { position += match[0].length; @@ -1811,20 +1965,16 @@ class RegExpParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { + doToString(context, indentation, path, index) { let result = "/" + this.#regexp.source + "/"; - const shortname = Object - .entries(RegExpParser.common) - .find(([k, v]) => v.source === this.#regexp.source)?.[0]; + const shortname = Object.entries(RegExpParser.common).find(([k, v]) => v.source === this.#regexp.source)?.[0]; if (shortname) { result = "P." + shortname; } - if (this.isHighlighted(context, path)) { - result += "\n" + Parser.indentation.repeat(indent) + "^".repeat(result.length) + " " + Parser.highlight; - } return result } } @@ -1855,14 +2005,29 @@ class MapParser extends Parser { this.#mapper = mapper; } + /** + * @param {Context} context + * @param {PathNode} path + */ + isHighlighted(context, path) { + if (super.isHighlighted(context, path)) { + // If MapParser is highlighted, then highlight its child + const childrenPath = { parent: path, parser: this.#parser, index: 0 }; + context.highlighted = context.highlighted instanceof Parser ? this.#parser : childrenPath; + } + return false + } + /** * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index * @returns {Result

} */ - parse(context, position, path) { - const result = this.#parser.parse(context, position, { parent: path, parser: this.#parser, index: 0 }); + parse(context, position, path, index) { + path = this.makePath(path, index); + const result = this.#parser.parse(context, position, path, 0); if (result.status) { result.value = this.#mapper(result.value); } @@ -1872,15 +2037,12 @@ class MapParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const childrenPath = { parent: path, parser: this.#parser, index: 0 }; - if (this.isHighlighted(context, path)) { - context.highlighted = context.highlighted instanceof Parser ? this.#parser : childrenPath; - } - let result = this.#parser.toString(context, indent, childrenPath); + doToString(context, indentation, path, index) { + let result = this.#parser.toString(context, indentation, path, 0); if (this.#parser instanceof RegExpParser) { if (Object.values(RegExpParser.common).includes(this.#parser.regexp)) { if ( @@ -1896,8 +2058,7 @@ class MapParser extends Parser { if (serializedMapper.length > 60 || serializedMapper.includes("\n")) { serializedMapper = "(...) => { ... }"; } - serializedMapper = ` -> map<${serializedMapper}>`; - result = Parser.appendBeforeHighlight(result, serializedMapper); + result += ` -> map<${serializedMapper}>`; return result } } @@ -1945,16 +2106,14 @@ class SequenceParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); const value = /** @type {ParserValue} */(new Array(this.#parsers.length)); const result = Reply.makeSuccess(position, value); for (let i = 0; i < this.#parsers.length; ++i) { - const outcome = this.#parsers[i].parse( - context, - result.position, - { parent: path, parser: this.#parsers[i], index: i } - ); + const outcome = this.#parsers[i].parse(context, result.position, path, i); if (outcome.bestPosition > result.bestPosition) { result.bestParser = outcome.bestParser; result.bestPosition = outcome.bestPosition; @@ -1973,17 +2132,17 @@ class SequenceParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - const indentation = Parser.indentation.repeat(indent); - const deeperIndentation = Parser.indentation.repeat(indent + 1); + doToString(context, indentation, path, index) { + const deeperIndentation = indentation + Parser.indentation; const result = "SEQ<\n" - + (this.isHighlighted(context, path) ? `${indentation}^^^ ${Parser.highlight}\n` : "") + + deeperIndentation + this.#parsers - .map((parser, index) => deeperIndentation + parser.toString(context, indent + 1, { parent: path, parser, index })) - .join("\n") + .map((parser, index) => parser.toString(context, deeperIndentation, path, index)) + .join("\n" + deeperIndentation) + "\n" + indentation + ">"; return result } @@ -2022,16 +2181,14 @@ class TimesParser extends Parser { * @param {Context} context * @param {Number} position * @param {PathNode} path + * @param {Number} index */ - parse(context, position, path) { + parse(context, position, path, index) { + path = this.makePath(path, index); const value = /** @type {ParserValue[]} */([]); const result = Reply.makeSuccess(position, value, path); for (let i = 0; i < this.#max; ++i) { - const outcome = this.#parser.parse( - context, - result.position, - { parent: path, parser: this.#parser, index: 0 } - ); + const outcome = this.#parser.parse(context, result.position, path, 0); if (outcome.bestPosition > result.bestPosition) { result.bestParser = outcome.bestParser; result.bestPosition = outcome.bestPosition; @@ -2052,11 +2209,12 @@ class TimesParser extends Parser { /** * @protected * @param {Context} context - * @param {Number} indent + * @param {String} indentation * @param {PathNode} path + * @param {Number} index */ - doToString(context, indent, path) { - let result = this.parser.toString(context, indent, { parent: path, parser: this.parser, index: 0 }); + doToString(context, indentation, path, index) { + let result = this.parser.toString(context, indentation, path, 0); const serialized = this.#min === 0 && this.#max === 1 ? "?" : this.#min === 0 && this.#max === Number.POSITIVE_INFINITY ? "*" @@ -2065,17 +2223,7 @@ class TimesParser extends Parser { + this.#min + (this.#min !== this.#max ? "," + this.#max : "") + "}"; - if (this.isHighlighted(context, path)) { - result += - serialized - + "\n" - + " ".repeat(Parser.lastRowLength(result, Parser.indentation.length * indent)) - + "^".repeat(serialized.length) - + " " - + Parser.highlight; - } else { - result = Parser.appendBeforeHighlight(result, serialized); - } + result += serialized; return result } } @@ -2160,6 +2308,34 @@ class Parsernostrum { this.#parser = parser; } + /** @param {PathNode} path */ + static #simplifyPath(path) { + /** @type {PathNode[]} */ + const array = []; + while (path) { + array.push(path); + path = path.parent; + } + array.reverse(); + /** @type {Map} */ + let visited = new Map(); + for (let i = 1; i < array.length; ++i) { + const existing = visited.get(array[i].current); + if (existing !== undefined) { + if (array[i + 1]) { + array[i + 1].parent = array[existing]; + } + visited = new Map([...visited.entries()].filter(([parser, index]) => index <= existing || index > i)); + visited.set(array[i].current, existing); + array.splice(existing + 1, i - existing); + i = existing; + } else { + visited.set(array[i].current, i); + } + } + return array[array.length - 1] + } + getParser() { return this.#parser } @@ -2169,7 +2345,7 @@ class Parsernostrum { * @returns {Result>} */ run(input) { - const result = this.#parser.parse(Reply.makeContext(this, input), 0, Reply.makePathNode(this.#parser)); + const result = this.#parser.parse(Reply.makeContext(this, input), 0, Reply.makePathNode(), 0); if (result.position !== input.length) { result.status = false; } @@ -2180,56 +2356,63 @@ class Parsernostrum { * @param {String} input * @throws {Error} when the parser fails to match */ - parse(input) { + parse(input, printParser = true) { const result = this.run(input); - if (!result.status) { - const chunkLength = 60; - const chunkRange = /** @type {[Number, Number]} */( - [Math.ceil(chunkLength / 2), Math.floor(chunkLength / 2)] - ); - const position = Parsernostrum.lineColumnFromOffset(input, result.bestPosition); - let bestPosition = result.bestPosition; - const inlineInput = input.replaceAll( - /^(\s)+|\s{6,}|\s*?\n\s*/g, - (m, startingSpace, offset) => { - let replaced = startingSpace ? "..." : " ... "; - if (offset <= result.bestPosition) { - if (result.bestPosition < offset + m.length) { - bestPosition -= result.bestPosition - offset; - } else { - bestPosition -= m.length - replaced.length; - } + if (result.status) { + return result.value + } + const chunkLength = 60; + const chunkRange = /** @type {[Number, Number]} */( + [Math.ceil(chunkLength / 2), Math.floor(chunkLength / 2)] + ); + const position = Parsernostrum.lineColumnFromOffset(input, result.bestPosition); + let bestPosition = result.bestPosition; + const inlineInput = input.replaceAll( + /^(\s)+|\s{6,}|\s*?\n\s*/g, + (m, startingSpace, offset) => { + let replaced = startingSpace ? "..." : " ... "; + if (offset <= result.bestPosition) { + if (result.bestPosition < offset + m.length) { + bestPosition -= result.bestPosition - offset; + } else { + bestPosition -= m.length - replaced.length; } - return replaced } - ); - const string = inlineInput.substring(0, chunkLength).trimEnd(); - const leadingWhitespaceLength = Math.min( - input.substring(result.bestPosition - chunkRange[0]).match(/^\s*/)[0].length, - chunkRange[0] - 1, - ); - let offset = Math.min(bestPosition, chunkRange[0] - leadingWhitespaceLength); - chunkRange[0] = Math.max(0, bestPosition - chunkRange[0]) + leadingWhitespaceLength; - chunkRange[1] = Math.min(input.length, chunkRange[0] + chunkLength); - let segment = inlineInput.substring(...chunkRange); - if (chunkRange[0] > 0) { - segment = "..." + segment; - offset += 3; - } - if (chunkRange[1] < inlineInput.length - 1) { - segment = segment + "..."; + return replaced } - throw new Error( - `Could not parse: ${string}\n\n` - + `Input: ${segment}\n` - + " " + " ".repeat(offset) - + `^ From here (line: ${position.line}, column: ${position.column}, offset: ${result.bestPosition})${result.bestPosition === input.length ? ", end of string" : ""}\n\n` + ); + const string = inlineInput.substring(0, chunkLength).trimEnd(); + const leadingWhitespaceLength = Math.min( + input.substring(result.bestPosition - chunkRange[0]).match(/^\s*/)[0].length, + chunkRange[0] - 1, + ); + let offset = Math.min(bestPosition, chunkRange[0] - leadingWhitespaceLength); + chunkRange[0] = Math.max(0, bestPosition - chunkRange[0]) + leadingWhitespaceLength; + chunkRange[1] = Math.min(input.length, chunkRange[0] + chunkLength); + let segment = inlineInput.substring(...chunkRange); + if (chunkRange[0] > 0) { + segment = "..." + segment; + offset += 3; + } + if (chunkRange[1] < inlineInput.length - 1) { + segment = segment + "..."; + } + const bestParser = this.toString(Parser.indentation, true, Parsernostrum.#simplifyPath(result.bestParser)); + throw new Error( + `Could not parse: ${string}\n\n` + + `Input: ${segment}\n` + + " " + " ".repeat(offset) + + `^ From here (line: ${position.line}, ` + + `column: ${position.column}, ` + + `offset: ${result.bestPosition})${result.bestPosition === input.length ? ", end of string" : ""}\n` + + (printParser + ? "\n" + (result.bestParser ? "Last valid parser matched:" : "No parser matched:") - + this.toString(1, true, result.bestParser) + + bestParser + "\n" + : "" ) - } - return result.value + ) } // Parsers @@ -2376,657 +2559,302 @@ class Parsernostrum { return this.map(Parsernostrum.#joiner) } + label(value = "") { + return new Parsernostrum(new Label(this.#parser, value)) + } + /** @param {Parsernostrum | Parser | PathNode} highlight */ - toString(indent = 0, newline = false, highlight = null) { + toString(indentation = "", newline = false, highlight = null) { if (highlight instanceof Parsernostrum) { highlight = highlight.getParser(); } const context = Reply.makeContext(this, ""); context.highlighted = highlight; - return (newline ? "\n" + Parser.indentation.repeat(indent) : "") - + this.#parser.toString(context, indent, Reply.makePathNode(this.#parser)) + const path = Reply.makePathNode(); + return (newline ? "\n" + indentation : "") + this.#parser.toString(context, indentation, path) } } -class SVGIcon { - - static arrayPin = x` - - - - - - - - - - - - ` - - static branchNode = x` - - - - - - - ` - - static breakStruct = x` - - - - - - ` - - static cast = x` - - - - - - - - - ` - - static close = x` - - - - - ` +class Serializable { - static convert = x` - - - - - - - ` + static grammar = this.createGrammar() - static correct = x` - - - - ` + /** @protected */ + static createGrammar() { + return /** @type {Parsernostrum} */(Parsernostrum.failure()) + } +} - static delegate = x` - - - - ` +class SerializerFactory { + + static #serializers = new Map() + + /** + * @template {AttributeConstructor} T + * @param {T} type + * @param {Serializer} object + */ + static registerSerializer(type, object) { + SerializerFactory.#serializers.set(type, object); + } + + /** + * @template {AttributeConstructor} T + * @param {T} type + * @returns {Serializer} + */ + static getSerializer(type) { + return SerializerFactory.#serializers.get(type) + } +} - static doN = x` - - - - - ` +/** @abstract */ +class IEntity extends Serializable { - static doOnce = x` - - - - - - ` + /** @type {{ [attribute: String]: AttributeInfo }} */ + static attributes = { + attributes: new AttributeInfo({ + ignored: true, + }), + lookbehind: new AttributeInfo({ + default: /** @type {String | Union} */(""), + ignored: true, + uninitialized: true, + }), + } - static enum = x` - - - - - ` + /** @type {String[]} */ + #_keys + get _keys() { + return this.#_keys + } + set _keys(keys) { + this.#_keys = keys; + } - static event = x` - - - - - - - ` - - static execPin = x` - - - - ` - - static expandIcon = x` - - - - ` - - static flipflop = x` - - - - - - ` + constructor(values = {}, suppressWarns = false) { + super(); + const Self = /** @type {typeof IEntity} */(this.constructor); + /** @type {AttributeDeclarations?} */ this.attributes; + /** @type {String} */ this.lookbehind; + const valuesKeys = Object.keys(values); + const attributesKeys = values.attributes + ? Utility.mergeArrays(Object.keys(values.attributes), Object.keys(Self.attributes)) + : Object.keys(Self.attributes); + const allAttributesKeys = Utility.mergeArrays(valuesKeys, attributesKeys); + for (const key of allAttributesKeys) { + let value = values[key]; + if (!suppressWarns && !(key in values)) { + if (!(key in Self.attributes) && !key.startsWith(Configuration.subObjectAttributeNamePrefix)) { + const typeName = value instanceof Array ? `[${value[0]?.constructor.name}]` : value.constructor.name; + console.warn( + `UEBlueprint: Attribute ${key} (of type ${typeName}) in the serialized data is not defined in ${Self.name}.attributes` + ); + } + } + if (!(key in Self.attributes)) { + // 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[key] = value; + continue + } + Self.attributes.lookbehind; + const predicate = AttributeInfo.getAttribute(values, key, "predicate", Self); + const assignAttribute = !predicate + ? v => this[key] = v + : v => { + Object.defineProperties(this, { + ["#" + key]: { + writable: true, + enumerable: false, + }, + [key]: { + enumerable: true, + get() { + return this["#" + key] + }, + set(v) { + if (!predicate(v)) { + console.warn( + `UEBlueprint: Tried to assign attribute ${key} to ${Self.name} not satisfying the predicate` + ); + return + } + this["#" + key] = v; + } + }, + }); + this[key] = v; + }; - static forEachLoop = x` - - - - - - - - - ` + let defaultValue = AttributeInfo.getAttribute(values, key, "default", Self); + if (defaultValue instanceof Function) { + defaultValue = defaultValue(this); + } + let defaultType = AttributeInfo.getAttribute(values, key, "type", Self); + if (defaultType instanceof ComputedType) { + defaultType = defaultType.compute(this); + } + if (defaultType instanceof Array) { + defaultType = Array; + } + if (defaultType === undefined) { + defaultType = Utility.getType(defaultValue); + } - static functionSymbol = x` - - - - ` + if (value !== undefined) { + // Remember value can still be null + if ( + value?.constructor === String + && AttributeInfo.getAttribute(values, key, "serialized", Self) + && defaultType !== String + ) { + try { + value = SerializerFactory + .getSerializer(defaultType) + .read(/** @type {String} */(value)); + } catch (e) { + assignAttribute(value); + continue + } + } + assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor} */(defaultType))); + continue // We have a value, need nothing more + } + if (defaultValue !== undefined && !AttributeInfo.getAttribute(values, key, "uninitialized", Self)) { + assignAttribute(defaultValue); + } + } + } - static gamepad = x` - - - - ` + /** @param {AttributeTypeDescription} attributeType */ + static defaultValueProviderFromType(attributeType) { + if (attributeType === Boolean) { + return false + } else if (attributeType === Number) { + return 0 + } else if (attributeType === BigInt) { + return 0n + } else if (attributeType === String) { + return "" + } else if (attributeType === Array || attributeType instanceof Array) { + return () => [] + } else if (attributeType instanceof Union) { + return this.defaultValueProviderFromType(attributeType.values[0]) + } else if (attributeType instanceof MirroredEntity) { + return () => new MirroredEntity(attributeType.type, attributeType.getter) + } else if (attributeType instanceof ComputedType) { + return undefined + } else { + return () => new /** @type {AnyConstructor} */(attributeType)() + } + } - static genericPin = x` - - - - - ` + /** + * @template {new (...args: any) => any} C + * @param {C} type + * @returns {value is InstanceType} + */ + static isValueOfType(value, type) { + return value != null && (value instanceof type || value.constructor === type) + } - static keyboard = x` - - - - ` + static defineAttributes(object, attributes) { + Object.defineProperty(object, "attributes", { + writable: true, + configurable: false, + }); + object.attributes = attributes; + } - static loop = x` - - - - - - - - - - - - ` - - static macro = x` - - - - ` + }, + }); + } + } - static mapPin = x` - - - - - - - - - ` + getLookbehind() { + let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default"); + lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind; + return lookbehind + } - static makeArray = x` - - - - - - - - - - - - - - ` + unexpectedKeys() { + return Object.keys(this).length - Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length + } - static makeMap = x` - - - - - - - - - - - ` - - static makeSet = x` - - - - - - - - - ` - - static makeStruct = x` - - - - - - ` - - static mouse = x` - - - - - ` - - static node = x` - - - - - - ` - - static operationPin = x` - - - - - ` - - static pcgStackPin = x` - - - - - - - - - ` - - static pcgPin = x` - - - - - - - ` - - static pcgParamPin = x` - - - - - ` - - static pcgSpatialPin = x` - - - - - ` - - static plusCircle = x` - - - - ` - - static questionMark = x` - - - - - ` - - static referencePin = x` - - - - ` - - static reject = x` - - - - - ` - - static setPin = x` - - - - - - - ` - - static select = x` - - - - - - - - - ` - - static sequence = x` - - - - - - - - - - ` - - static sound = x` - - - - - - ` - - static spawnActor = x` - - - - - - - - ` - - static switch = x` - - - - - - - - - ` - - static timer = x` - - - - - ` - - static touchpad = x` - - - - - ` -} - -class Serializable { - - static grammar = this.createGrammar() - - /** @protected */ - static createGrammar() { - return /** @type {Parsernostrum} */(Parsernostrum.failure()) + /** @param {IEntity} other */ + equals(other) { + const thisKeys = Object.keys(this); + const otherKeys = Object.keys(other); + 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 SerializerFactory { +class Grammar { - static #serializers = new Map() + static separatedBy = (source, separator, min = 1) => + new RegExp( + source + "(?:" + separator + source + ")" + + (min === 1 ? "*" : min === 2 ? "+" : `{${min},}`) + ) - /** - * @template {AttributeConstructor} T - * @param {T} type - * @param {Serializer} object - */ - static registerSerializer(type, object) { - SerializerFactory.#serializers.set(type, object); - } - - /** - * @template {AttributeConstructor} T - * @param {T} type - * @returns {Serializer} - */ - static getSerializer(type) { - return SerializerFactory.#serializers.get(type) - } -} - -/** @abstract */ -class IEntity extends Serializable { - - /** @type {{ [attribute: String]: AttributeInfo }} */ - static attributes = { - attributes: new AttributeInfo({ - ignored: true, - }), - lookbehind: new AttributeInfo({ - default: /** @type {String | Union} */(""), - ignored: true, - }), - } - - constructor(values = {}, suppressWarns = false) { - super(); - const Self = /** @type {typeof IEntity} */(this.constructor); - /** @type {AttributeDeclarations?} */ this.attributes; - /** @type {String} */ this.lookbehind; - const valuesKeys = Object.keys(values); - const attributesKeys = values.attributes - ? Utility.mergeArrays(Object.keys(values.attributes), Object.keys(Self.attributes)) - : Object.keys(Self.attributes); - const allAttributesKeys = Utility.mergeArrays(valuesKeys, attributesKeys); - for (const key of allAttributesKeys) { - let value = values[key]; - if (!suppressWarns && !(key in values)) { - if (!(key in Self.attributes) && !key.startsWith(Configuration.subObjectAttributeNamePrefix)) { - const typeName = value instanceof Array ? `[${value[0]?.constructor.name}]` : value.constructor.name; - console.warn( - `UEBlueprint: Attribute ${key} (of type ${typeName}) in the serialized data is not defined in ${Self.name}.attributes` - ); - } - } - if (!(key in Self.attributes)) { - // 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[key] = value; - continue - } - Self.attributes.lookbehind; - const predicate = AttributeInfo.getAttribute(values, key, "predicate", Self); - const assignAttribute = !predicate - ? v => this[key] = v - : v => { - Object.defineProperties(this, { - ["#" + key]: { - writable: true, - enumerable: false, - }, - [key]: { - enumerable: true, - get() { - return this["#" + key] - }, - set(v) { - if (!predicate(v)) { - console.warn( - `UEBlueprint: Tried to assign attribute ${key} to ${Self.name} not satisfying the predicate` - ); - return - } - this["#" + key] = v; - } - }, - }); - this[key] = v; - }; - - let defaultValue = AttributeInfo.getAttribute(values, key, "default", Self); - if (defaultValue instanceof Function) { - defaultValue = defaultValue(this); - } - let defaultType = AttributeInfo.getAttribute(values, key, "type", Self); - if (defaultType instanceof ComputedType) { - defaultType = defaultType.compute(this); - } - if (defaultType instanceof Array) { - defaultType = Array; - } - if (defaultType === undefined) { - defaultType = Utility.getType(defaultValue); - } - - if (value !== undefined) { - // Remember value can still be null - if ( - value?.constructor === String - && AttributeInfo.getAttribute(values, key, "serialized", Self) - && defaultType !== String - ) { - try { - value = SerializerFactory - .getSerializer(defaultType) - .read(/** @type {String} */(value)); - } catch (e) { - assignAttribute(value); - continue - } - } - assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor} */(defaultType))); - continue // We have a value, need nothing more - } - if (defaultValue !== undefined) { - assignAttribute(defaultValue); - } - } - } - - /** @param {AttributeTypeDescription} attributeType */ - static defaultValueProviderFromType(attributeType) { - if (attributeType === Boolean) { - return false - } else if (attributeType === Number) { - return 0 - } else if (attributeType === BigInt) { - return 0n - } else if (attributeType === String) { - return "" - } else if (attributeType === Array || attributeType instanceof Array) { - return () => [] - } else if (attributeType instanceof Union) { - return this.defaultValueProviderFromType(attributeType.values[0]) - } else if (attributeType instanceof MirroredEntity) { - return () => new MirroredEntity(attributeType.type, attributeType.getter) - } else if (attributeType instanceof ComputedType) { - return undefined - } else { - return () => new /** @type {AnyConstructor} */(attributeType)() - } - } - - /** - * @template {new (...args: any) => any} C - * @param {C} type - * @returns {value is InstanceType} - */ - static isValueOfType(value, type) { - return value != null && (value instanceof type || value.constructor === type) - } - - static defineAttributes(object, attributes) { - Object.defineProperty(object, "attributes", { - writable: true, - configurable: false, - }); - object.attributes = attributes; - } - - getLookbehind() { - let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default"); - lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind; - return lookbehind - } - - 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(other); - 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 Grammar { - - static separatedBy = (source, separator, min = 1) => - new RegExp( - source + "(?:" + separator + source + ")" - + (min === 1 ? "*" : min === 2 ? "+" : `{${min},}`) - ) - - static Regex = class { - static HexDigit = /[0-9a-fA-F]/ - static InsideString = /(?:[^"\\]|\\.)*/ - static InsideSingleQuotedString = /(?:[^'\\]|\\.)*/ - static Integer = /[\-\+]?\d+(?!\d|\.)/ - static Number = /[-\+]?(?:\d*\.)?\d+(?!\d|\.)/ - static RealUnit = /\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/ // A number between 0 and 1 included - static Word = Grammar.separatedBy("[a-zA-Z]", "_") - static Symbol = /[a-zA-Z_]\w*/ - static DotSeparatedSymbols = Grammar.separatedBy(this.Symbol.source, "\\.") - static PathFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:]") - static PathSpaceFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:\\ ]") - static Path = new RegExp(`(?:\\/${this.PathFragment.source}){2,}`) // Multiple (2+) /PathFragment + static Regex = class { + static HexDigit = /[0-9a-fA-F]/ + static InsideString = /(?:[^"\\]|\\.)*/ + static InsideSingleQuotedString = /(?:[^'\\]|\\.)*/ + static Integer = /[\-\+]?\d+(?!\d|\.)/ + static Number = /[-\+]?(?:\d*\.)?\d+(?!\d|\.)/ + static RealUnit = /\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/ // A number between 0 and 1 included + static Word = Grammar.separatedBy("[a-zA-Z]", "_") + static Symbol = /[a-zA-Z_]\w*/ + static DotSeparatedSymbols = Grammar.separatedBy(this.Symbol.source, "\\.") + static MultipleWordsSymbols = Grammar.separatedBy(this.Symbol.source, "(?:\\.|\\ +)") + static PathFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:]") + static PathSpaceFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:\\ ]") + static Path = new RegExp(`(?:\\/${this.PathFragment.source}){2,}`) // Multiple (2+) /PathFragment } /* --- Primitive --- */ @@ -3056,7 +2884,7 @@ class Grammar { static symbol = Parsernostrum.reg(Grammar.Regex.Symbol) static symbolQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.Symbol.source + ')"'), 1) static attributeName = Parsernostrum.reg(Grammar.Regex.DotSeparatedSymbols) - static attributeNameQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.DotSeparatedSymbols.source + ')"'), 1) + static attributeNameQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.InsideString.source + ')"'), 1) static guid = Parsernostrum.reg(new RegExp(`${Grammar.Regex.HexDigit.source}{32}`)) static commaSeparation = Parsernostrum.reg(/\s*,\s*(?!\))/) static commaOrSpaceSeparation = Parsernostrum.reg(/\s*,\s*(?!\))|\s+/) @@ -3068,11 +2896,12 @@ class Grammar { /** * @template T * @param {AttributeInfo} attribute + * @param {Parsernostrum} defaultGrammar * @returns {Parsernostrum} */ static grammarFor(attribute, type = attribute?.type, defaultGrammar = this.unknownValue) { let result = defaultGrammar; - if (type instanceof Array) { + if (type === Array || type instanceof Array) { if (attribute?.inlined) { return this.grammarFor(undefined, type[0]) } @@ -3122,7 +2951,7 @@ class Grammar { if (result == this.unknownValue) { result = this.string; } else { - result = Parsernostrum.seq(Parsernostrum.str('"'), result, Parsernostrum.str('"')); + result = Parsernostrum.seq(Parsernostrum.str('"'), result, Parsernostrum.str('"')).map(([_0, value, _2]) => value); } } if (attribute.nullable) { @@ -3231,255 +3060,11 @@ class Grammar { }) } - /* --- Entity --- */ - + /** @type {Parsernostrum} */ static unknownValue // Defined in initializeSerializerFactor to avoid circular include } -var crypto; -if (typeof window === "undefined") { - import('crypto').then(mod => crypto = mod.default).catch(); -} else { - crypto = window.crypto; -} - -class GuidEntity extends IEntity { - - static attributes = { - ...super.attributes, - value: AttributeInfo.createValue(""), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.guid.map(v => new this(v)) - } - - static generateGuid(random = true) { - let values = new Uint32Array(4); - if (random === true) { - crypto.getRandomValues(values); - } - let guid = ""; - values.forEach(n => { - guid += ("0".repeat(8) + n.toString(16).toUpperCase()).slice(-8); - }); - return new GuidEntity({ value: guid }) - } - - constructor(values) { - if (!values) { - values = GuidEntity.generateGuid().value; - } - if (values.constructor !== Object) { - values = { - value: values, - }; - } - super(values); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } -} - -class ObjectReferenceEntity extends IEntity { - - static #quoteSymbols = [ - [`'"`, Grammar.Regex.InsideString.source], - [`'`, Grammar.Regex.InsideSingleQuotedString.source], - [`"`, Grammar.Regex.InsideString.source] - ] - static attributes = { - ...super.attributes, - type: new AttributeInfo({ - default: "", - serialized: true, - }), - path: new AttributeInfo({ - default: "", - serialized: true, - }), - delim: new AttributeInfo({ - ignored: true, - }), - } - static quoted = Parsernostrum.regArray(new RegExp( - this.#quoteSymbols.map(([delim, parser]) => - delim + "(" + parser + ")" + delim.split("").reverse().join("")).join("|") - )).map(([_0, a, b, c]) => a ?? b ?? c) - static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source - static typeReference = Parsernostrum.reg( - new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source) - ) - static fullReferenceGrammar = Parsernostrum.regArray( - new RegExp( - "(" + this.typeReference.getParser().regexp.source + ")" - + /\s*/.source - + "(?:" + this.quoted.getParser().parser.regexp.source + ")" - ) - ).map(([_0, type, ...path]) => new this({ - type, - path: path.find(v => v), - delim: this.#quoteSymbols[path.findIndex(v => v)]?.[0] ?? "", - })) - static fullReferenceSerializedGrammar = Parsernostrum.regArray( - new RegExp( - "(" + this.typeReference.getParser().regexp.source + ")" - + /\s*/.source - + `'(` + Grammar.Regex.InsideSingleQuotedString.source + `)'` - ) - ).map(([_0, type, ...path]) => new this({ - type, - path: path.find(v => v), - delim: "'", - })) - static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "" })) - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.alt( - Parsernostrum.seq( - Parsernostrum.str('"'), - Parsernostrum.alt( - this.fullReferenceSerializedGrammar, - this.typeReferenceGrammar, - ), - Parsernostrum.str('"'), - ).map(([_0, objectReference, _1]) => objectReference), - this.fullReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)), - this.typeReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)), - ) - } - - constructor(values = {}) { - if (values.constructor === String) { - values = { - path: values - }; - } - super(values); - /** @type {String} */ this.type; - /** @type {String} */ this.path; - /** @type {String} */ this.delim; - } - - static createNoneInstance() { - return new ObjectReferenceEntity({ type: "None", path: "" }) - } - - getName() { - return Utility.getNameFromPath(this.path.replace(/_C$/, "")) - } - - toString() { - return this.type + (this.path ? (this.delim + this.path + this.delim.split("").reverse().join("")) : "") - } -} - -class FunctionReferenceEntity extends IEntity { - - static attributes = { - ...super.attributes, - MemberParent: AttributeInfo.createType(ObjectReferenceEntity), - MemberName: AttributeInfo.createType(String), - MemberGuid: AttributeInfo.createType(GuidEntity), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.createEntityGrammar(this) - } - - constructor(values) { - super(values); - /** @type {ObjectReferenceEntity} */ this.MemberParent; - /** @type {String} */ this.MemberName; - /** @type {GuidEntity} */ this.MemberGuid; - } -} - -class IdentifierEntity extends IEntity { - - static attributes = { - ...super.attributes, - value: AttributeInfo.createValue(""), - } - static attributeConverter = { - fromAttribute: (value, type) => new IdentifierEntity(value), - toAttribute: (value, type) => value.toString() - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.symbol.map(v => new this(v)) - } - - constructor(values) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - super(values); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } -} - -class IntegerEntity extends IEntity { - - static attributes = { - ...super.attributes, - value: new AttributeInfo({ - default: 0, - predicate: v => v % 1 == 0 && v > 1 << 31 && v < -(1 << 31), - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.numberInteger.map(v => new this(v)) - } - - /** @param {Number | Object} values */ - constructor(values = 0) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - values.value = Math.floor(values.value); - if (values.value === -0) { - values.value = 0; - } - super(values); - /** @type {Number} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value.toString() - } -} - -class ColorChannelEntity extends IEntity { +class ColorChannelEntity extends IEntity { static attributes = { ...super.attributes, @@ -3835,947 +3420,1118 @@ class LinearColorEntity extends IEntity { } } -class MacroGraphReferenceEntity extends IEntity { - - static attributes = { - ...super.attributes, - MacroGraph: new AttributeInfo({ - type: ObjectReferenceEntity, - default: () => new ObjectReferenceEntity(), - }), - GraphBlueprint: new AttributeInfo({ - type: ObjectReferenceEntity, - default: () => new ObjectReferenceEntity(), - }), - GraphGuid: new AttributeInfo({ - type: GuidEntity, - default: () => new GuidEntity(), - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.createEntityGrammar(this) - } - - constructor(values) { - super(values); - /** @type {ObjectReferenceEntity} */ this.MacroGraph; - /** @type {ObjectReferenceEntity} */ this.GraphBlueprint; - /** @type {GuidEntity} */ this.GuidEntity; - } - - getMacroName() { - const colonIndex = this.MacroGraph.path.search(":"); - return this.MacroGraph.path.substring(colonIndex + 1) - } -} - -class ByteEntity extends IntegerEntity { - - static attributes = { - ...super.attributes, - value: new AttributeInfo({ - ...super.attributes.value, - predicate: v => v % 1 == 0 && v >= 0 && v < 1 << 8, - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.numberByte.map(v => new this(v)) - } - - constructor(values = 0) { - super(values); - } +/** @param {ObjectEntity} entity */ +function nodeColor(entity) { + switch (entity.getType()) { + case Configuration.paths.materialExpressionConstant2Vector: + case Configuration.paths.materialExpressionConstant3Vector: + case Configuration.paths.materialExpressionConstant4Vector: + return Configuration.nodeColors.yellow + case Configuration.paths.makeStruct: + return Configuration.nodeColors.darkBlue + case Configuration.paths.materialExpressionMaterialFunctionCall: + return Configuration.nodeColors.blue + case Configuration.paths.materialExpressionFunctionInput: + return Configuration.nodeColors.red + case Configuration.paths.materialExpressionTextureSample: + return Configuration.nodeColors.darkTurquoise + case Configuration.paths.materialExpressionTextureCoordinate: + return Configuration.nodeColors.red + case Configuration.paths.pcgEditorGraphNodeInput: + case Configuration.paths.pcgEditorGraphNodeOutput: + return Configuration.nodeColors.red + } + switch (entity.getClass()) { + case Configuration.paths.callFunction: + return entity.bIsPureFunc + ? Configuration.nodeColors.green + : Configuration.nodeColors.blue + case Configuration.paths.niagaraNodeFunctionCall: + return Configuration.nodeColors.darkerBlue + case Configuration.paths.dynamicCast: + return Configuration.nodeColors.turquoise + case Configuration.paths.inputDebugKey: + case Configuration.paths.inputKey: + return Configuration.nodeColors.red + case Configuration.paths.createDelegate: + case Configuration.paths.enumLiteral: + case Configuration.paths.makeArray: + case Configuration.paths.makeMap: + case Configuration.paths.materialGraphNode: + case Configuration.paths.select: + return Configuration.nodeColors.green + case Configuration.paths.executionSequence: + case Configuration.paths.ifThenElse: + case Configuration.paths.macro: + case Configuration.paths.multiGate: + return Configuration.nodeColors.gray + case Configuration.paths.functionEntry: + case Configuration.paths.functionResult: + return Configuration.nodeColors.violet + case Configuration.paths.timeline: + return Configuration.nodeColors.yellow + } + if (entity.switchTarget()) { + return Configuration.nodeColors.lime + } + if (entity.isEvent()) { + return Configuration.nodeColors.red + } + if (entity.isComment()) { + return (entity.CommentColor ? entity.CommentColor : LinearColorEntity.getWhite()) + .toDimmedColor() + .toCSSRGBValues() + } + const pcgSubobject = entity.getPcgSubobject(); + if (pcgSubobject) { + if (pcgSubobject.NodeTitleColor) { + return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues() + } + switch (entity.PCGNode?.getName(true)) { + case "Branch": + case "Select": + return Configuration.nodeColors.intenseGreen + } + } + if (entity.bIsPureFunc) { + return Configuration.nodeColors.green + } + return Configuration.nodeColors.blue } -class SymbolEntity extends IEntity { +class SVGIcon { - static attributes = { - ...super.attributes, - value: AttributeInfo.createValue(""), - } - static grammar = this.createGrammar() + static arrayPin = x` + + + + + + + + + + + + ` - static createGrammar() { - return Grammar.symbol.map(v => new this(v)) - } + static branchNode = x` + + + + + + + ` - /** @param {String | Object} values */ - constructor(values) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - super(values); - /** @type {String} */ this.value; - } + static breakStruct = x` + + + + + + ` - valueOf() { - return this.value - } + static cast = x` + + + + + + + + + ` - toString() { - return this.value - } -} + static close = x` + + + + + ` -class EnumEntity extends SymbolEntity { - - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.symbol.map(v => new this(v)) - } -} + static convert = x` + + + + + + + ` -class EnumDisplayValueEntity extends EnumEntity { - - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.reg(Grammar.Regex.InsideString).map(v => new this(v)) - } -} + static correct = x` + + + + ` -class InvariantTextEntity extends IEntity { + static delegate = x` + + + + ` - static attributes = { - ...super.attributes, - value: AttributeInfo.createValue(""), - lookbehind: new AttributeInfo({ - ...super.attributes.lookbehind, - default: "INVTEXT", - }), - } - static grammar = this.createGrammar() + static doN = x` + + + + + ` - static createGrammar() { - return Parsernostrum.alt( - Parsernostrum.seq( - Parsernostrum.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)), - Grammar.grammarFor(this.attributes.value), - Parsernostrum.reg(/\s*\)/) - ) - .map(([_0, value, _2]) => value), - Parsernostrum.reg(new RegExp(this.attributes.lookbehind.default)) // InvariantTextEntity can not have arguments - .map(() => "") - ).map(value => new this(value)) - } + static doOnce = x` + + + + + + ` - constructor(values) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - super(values); - /** @type {String} */ this.value; - } -} + static enum = x` + + + + + ` -class LocalizedTextEntity extends IEntity { - - static attributes = { - ...super.attributes, - namespace: AttributeInfo.createValue(""), - key: AttributeInfo.createValue(""), - value: AttributeInfo.createValue(""), - lookbehind: new AttributeInfo({ - ...super.attributes.lookbehind, - default: "NSLOCTEXT", - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.regArray(new RegExp( - String.raw`${this.attributes.lookbehind.default}\s*\(` - + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,` - + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,` - + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*` - + String.raw`(?:,\s+)?` - + String.raw`\)`, - "m" - )).map(matchResult => new this({ - namespace: Utility.unescapeString(matchResult[1]), - key: Utility.unescapeString(matchResult[2]), - value: Utility.unescapeString(matchResult[3]), - })) - } - - constructor(values) { - super(values); - /** @type {String} */ this.namespace; - /** @type {String} */ this.key; - /** @type {String} */ this.value; - } - - toString() { - return Utility.capitalFirstLetter(this.value) - } -} + static event = x` + + + + + + + ` -class FormatTextEntity extends IEntity { + static execPin = x` + + + + ` - static attributes = { - ...super.attributes, - value: new AttributeInfo({ - type: [new Union(String, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)], - default: [], - }), - lookbehind: /** @type {AttributeInfo>} */(new AttributeInfo({ - ...super.attributes.lookbehind, - default: new Union("LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"), - })), - } - static grammar = this.createGrammar() + static expandIcon = x` + + + + ` - static createGrammar() { - return Parsernostrum.seq( - Parsernostrum.reg( - // Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/ - new RegExp(`(${this.attributes.lookbehind.default.values.reduce((acc, cur) => acc + "|" + cur)})\\s*`), - 1 - ), - Grammar.grammarFor(this.attributes.value) - ) - .map(([lookbehind, values]) => { - const result = new this({ - value: values, - lookbehind, - }); - return result - }) - } + static flipflop = x` + + + + + + ` - constructor(values) { - super(values); - /** @type {(String | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} */ this.value; - } + static forEachLoop = x` + + + + + + + + + ` - toString() { - const pattern = this.value?.[0]?.toString(); // The pattern is always the first element of the array - if (!pattern) { - return "" - } - const values = this.value.slice(1).map(v => v.toString()); - return this.lookbehind == "LOCGEN_FORMAT_NAMED" - ? pattern.replaceAll(/\{([a-zA-Z]\w*)\}/g, (substring, arg) => { - const argLocation = values.indexOf(arg) + 1; - return argLocation > 0 && argLocation < values.length - ? values[argLocation] - : substring - }) - : this.lookbehind == "LOCGEN_FORMAT_ORDERED" - ? pattern.replaceAll(/\{(\d+)\}/g, (substring, arg) => { - const argValue = Number(arg); - return argValue < values.length - ? values[argValue] - : substring - }) - : "" - } -} + static functionSymbol = x` + + + + ` -class Integer64Entity extends IEntity { - - static attributes = { - ...super.attributes, - value: new AttributeInfo({ - default: 0n, - predicate: v => v >= -(1n << 63n) && v < 1n << 63n, - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.numberBigInteger.map(v => new this(v)) - } - - /** @param {BigInt | Number | Object} values */ - constructor(values = 0) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - if (values.value === -0) { - values.value = 0n; - } - super(values); - /** @type {BigInt} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value.toString() - } -} - -class PathSymbolEntity extends IEntity { - - static attributes = { - ...super.attributes, - value: new AttributeInfo({ - default: "", - }), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.symbol.map(v => new this(v)) - } - - constructor(values) { - if (values.constructor !== Object) { - values = { - value: values, - }; - } - super(values); - /** @type {String} */ this.value; - } - - valueOf() { - return this.value - } - - toString() { - return this.value - } -} + static gamepad = x` + + + + ` -class PinReferenceEntity extends IEntity { - - static attributes = { - ...super.attributes, - objectName: AttributeInfo.createType(PathSymbolEntity), - pinGuid: AttributeInfo.createType(GuidEntity), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.seq( - PathSymbolEntity.createGrammar(), - Parsernostrum.whitespace, - GuidEntity.createGrammar() - ).map( - ([objectName, _1, pinGuid]) => new this({ - objectName: objectName, - pinGuid: pinGuid, - }) - ) - } - - constructor(values) { - super(values); - /** @type {PathSymbolEntity} */ this.objectName; - /** @type {GuidEntity} */ this.pinGuid; - } -} + static genericPin = x` + + + + + ` -class PinTypeEntity extends IEntity { - - static attributes = { - ...super.attributes, - PinCategory: AttributeInfo.createValue(""), - PinSubCategory: AttributeInfo.createValue(""), - PinSubCategoryObject: new AttributeInfo({ - type: ObjectReferenceEntity, - default: () => ObjectReferenceEntity.createNoneInstance(), - }), - PinSubCategoryMemberReference: new AttributeInfo({ - type: FunctionReferenceEntity, - default: null, - }), - PinValueType: new AttributeInfo({ - type: PinTypeEntity, - default: null, - }), - ContainerType: AttributeInfo.createType(PathSymbolEntity), - bIsReference: AttributeInfo.createValue(false), - bIsConst: AttributeInfo.createValue(false), - bIsWeakPointer: AttributeInfo.createValue(false), - bIsUObjectWrapper: AttributeInfo.createValue(false), - bSerializeAsSinglePrecisionFloat: AttributeInfo.createValue(false), - } - static grammar = this.createGrammar() - - static createGrammar() { - return Grammar.createEntityGrammar(this) - } - - constructor(values = {}, suppressWarns = false) { - super(values, suppressWarns); - /** @type {String} */ this.PinCategory; - /** @type {String} */ this.PinSubCategory; - /** @type {ObjectReferenceEntity} */ this.PinSubCategoryObject; - /** @type {FunctionReferenceEntity} */ this.PinSubCategoryMemberReference; - /** @type {PinTypeEntity} */ this.PinValueType; - /** @type {PathSymbolEntity} */ this.ContainerType; - /** @type {Boolean} */ this.bIsReference; - /** @type {Boolean} */ this.bIsConst; - /** @type {Boolean} */ this.bIsWeakPointer; - /** @type {Boolean} */ this.bIsUObjectWrapper; - /** @type {Boolean} */ this.bIsUObjectWrapper; - /** @type {Boolean} */ this.bSerializeAsSinglePrecisionFloat; - } - - /** @param {PinTypeEntity} other */ - copyTypeFrom(other) { - this.PinCategory = other.PinCategory; - this.PinSubCategory = other.PinSubCategory; - this.PinSubCategoryObject = other.PinSubCategoryObject; - this.PinSubCategoryMemberReference = other.PinSubCategoryMemberReference; - this.PinValueType = other.PinValueType; - this.ContainerType = other.ContainerType; - this.bIsReference = other.bIsReference; - this.bIsConst = other.bIsConst; - this.bIsWeakPointer = other.bIsWeakPointer; - this.bIsUObjectWrapper = other.bIsUObjectWrapper; - this.bSerializeAsSinglePrecisionFloat = other.bSerializeAsSinglePrecisionFloat; - } -} + static keyboard = x` + + + + ` -class Vector2DEntity extends IEntity { + static loop = x` + + + + + + + + + + + + ` - static attributes = { - ...super.attributes, - X: new AttributeInfo({ - default: 0, - expected: true, - }), - Y: new AttributeInfo({ - default: 0, - expected: true, - }), - } - static grammar = this.createGrammar() + static macro = x` + + + + ` - static createGrammar() { - return Grammar.createEntityGrammar(this, false) - } + static mapPin = x` + + + + + + + + + ` - constructor(values) { - super(values); - /** @type {Number} */ this.X; - /** @type {Number} */ this.Y; - } + static makeArray = x` + + + + + + + + + + + + + + ` - /** @returns {[Number, Number]} */ - toArray() { - return [this.X, this.Y] - } -} + static makeMap = x` + + + + + + + + + + + ` -class RBSerializationVector2DEntity extends Vector2DEntity { + static makeSet = x` + + + + + + + + + ` - static grammar = this.createGrammar() + static makeStruct = x` + + + + + + ` - static createGrammar() { - return Parsernostrum.alt( - Parsernostrum.regArray(new RegExp( - /X\s*=\s*/.source + "(?" + Parsernostrum.number.getParser().parser.regexp.source + ")" - + "\\s+" - + /Y\s*=\s*/.source + "(?" + Parsernostrum.number.getParser().parser.regexp.source + ")" - )).map(({ groups: { x, y } }) => new this({ - X: Number(x), - Y: Number(y), - })), - Vector2DEntity.createGrammar() - ) - } -} + static metasoundFunction = x` + + + + ` -class RotatorEntity extends IEntity { + static mouse = x` + + + + + ` - static attributes = { - ...super.attributes, - R: new AttributeInfo({ - default: 0, - expected: true, - }), - P: new AttributeInfo({ - default: 0, - expected: true, - }), - Y: new AttributeInfo({ - default: 0, - expected: true, - }), - } - static grammar = this.createGrammar() + static node = x` + + + + + + ` - static createGrammar() { - return Grammar.createEntityGrammar(this, false) - } + static operationPin = x` + + + + + ` - constructor(values) { - super(values); - /** @type {Number} */ this.R; - /** @type {Number} */ this.P; - /** @type {Number} */ this.Y; - } + static pcgStackPin = x` + + + + + + + + + ` - getRoll() { - return this.R - } + static pcgPin = x` + + + + + + + ` - getPitch() { - return this.P - } + static pcgParamPin = x` + + + + + ` - getYaw() { - return this.Y - } -} + static pcgSpatialPin = x` + + + + + ` -class SimpleSerializationRotatorEntity extends RotatorEntity { + static plusCircle = x` + + + + ` - static grammar = this.createGrammar() + static questionMark = x` + + + + + ` - static createGrammar() { - const number = Parsernostrum.number.getParser().parser.regexp.source; - return Parsernostrum.alt( - Parsernostrum.regArray(new RegExp( - "(" + number + ")" - + "\\s*,\\s*" - + "(" + number + ")" - + "\\s*,\\s*" - + "(" + number + ")" - )).map(([_, p, y, r]) => new this({ - R: Number(r), - P: Number(p), - Y: Number(y), - })), - RotatorEntity.createGrammar() - ) - } -} + static referencePin = x` + + + + ` -class SimpleSerializationVector2DEntity extends Vector2DEntity { + static reject = x` + + + + + ` - static grammar = this.createGrammar() + static setPin = x` + + + + + + + ` - static createGrammar() { - const number = Parsernostrum.number.getParser().parser.regexp.source; - return Parsernostrum.alt( - Parsernostrum.regArray(new RegExp( - "(" + number + ")" - + "\\s*,\\s*" - + "(" + number + ")" - )).map(([_, x, y]) => new this({ - X: Number(x), - Y: Number(y), - })), - Vector2DEntity.createGrammar() - ) - } -} + static select = x` + + + + + + + + + ` -class VectorEntity extends IEntity { + static sequence = x` + + + + + + + + + + ` - static attributes = { - ...super.attributes, - X: new AttributeInfo({ - default: 0, - expected: true, - }), - Y: new AttributeInfo({ - default: 0, - expected: true, - }), - Z: new AttributeInfo({ - default: 0, - expected: true, - }), - } - static grammar = this.createGrammar() + static sound = x` + + + + + + ` - static createGrammar() { - return Grammar.createEntityGrammar(VectorEntity, false) - } + static spawnActor = x` + + + + + + + + ` - constructor(values) { - super(values); - /** @type {Number} */ this.X; - /** @type {Number} */ this.Y; - /** @type {Number} */ this.Z; - } + static switch = x` + + + + + + + + + ` - /** @returns {[Number, Number, Number]} */ - toArray() { - return [this.X, this.Y, this.Z] - } + static timer = x` + + + + + ` + + static touchpad = x` + + + + + ` } -class SimpleSerializationVectorEntity extends VectorEntity { +const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/; +const keyNameValue = { + "A_AccentGrave": "à", + "Add": "Num +", + "C_Cedille": "ç", + "Decimal": "Num .", + "Divide": "Num /", + "E_AccentAigu": "é", + "E_AccentGrave": "è", + "F1": "F1", // Otherwise F and number will be separated + "F10": "F10", + "F11": "F11", + "F12": "F12", + "F2": "F2", + "F3": "F3", + "F4": "F4", + "F5": "F5", + "F6": "F6", + "F7": "F7", + "F8": "F8", + "F9": "F9", + "Gamepad_Special_Left_X": "Touchpad Button X Axis", + "Gamepad_Special_Left_Y": "Touchpad Button Y Axis", + "Mouse2D": "Mouse XY 2D-Axis", + "Multiply": "Num *", + "Section": "§", + "Subtract": "Num -", + "Tilde": "`", +}; + +function keyName(value) { + /** @type {String} */ + let result = keyNameValue[value]; + if (result) { + return result + } + result = Utility.numberFromText(value)?.toString(); + if (result) { + return result + } + const match = value.match(/NumPad([a-zA-Z]+)/); + if (match) { + result = Utility.numberFromText(match[1]).toString(); + if (result) { + return "Num " + result + } + } +} - static grammar = this.createGrammar() +/** @param {ObjectEntity} entity */ +function nodeTitle(entity) { + let input; + switch (entity.getType()) { + case Configuration.paths.asyncAction: + if (entity.ProxyFactoryFunctionName) { + return Utility.formatStringName(entity.ProxyFactoryFunctionName) + } + case Configuration.paths.actorBoundEvent: + case Configuration.paths.componentBoundEvent: + return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})` + case Configuration.paths.callDelegate: + return `Call ${entity.DelegateReference?.MemberName ?? "None"}` + case Configuration.paths.createDelegate: + return "Create Event" + case Configuration.paths.customEvent: + if (entity.CustomFunctionName) { + return entity.CustomFunctionName + } + case Configuration.paths.dynamicCast: + if (!entity.TargetType) { + return "Bad cast node" // Target type not found + } + return `Cast To ${entity.TargetType?.getName()}` + case Configuration.paths.enumLiteral: + return `Literal enum ${entity.Enum?.getName()}` + case Configuration.paths.event: + return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}` + case Configuration.paths.executionSequence: + return "Sequence" + case Configuration.paths.forEachElementInEnum: + return `For Each ${entity.Enum?.getName()}` + case Configuration.paths.forEachLoopWithBreak: + return "For Each Loop with Break" + case Configuration.paths.functionEntry: + return entity.FunctionReference?.MemberName === "UserConstructionScript" + ? "Construction Script" + : entity.FunctionReference?.MemberName + case Configuration.paths.functionResult: + return "Return Node" + case Configuration.paths.ifThenElse: + return "Branch" + case Configuration.paths.makeStruct: + if (entity.StructType) { + return `Make ${entity.StructType.getName()}` + } + case Configuration.paths.materialExpressionComponentMask: { + const materialObject = entity.getMaterialSubobject(); + return `Mask ( ${Configuration.rgba + .filter(k => /** @type {MirroredEntity} */(materialObject[k]).get() === true) + .map(v => v + " ") + .join("")})` + } + case Configuration.paths.materialExpressionConstant: + input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]; + case Configuration.paths.materialExpressionConstant2Vector: + input ??= [ + entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue, + entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue, + ]; + case Configuration.paths.materialExpressionConstant3Vector: + if (!input) { + /** @type {VectorEntity} */ + const vector = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "Constant") + ?.DefaultValue; + input = [vector.X, vector.Y, vector.Z]; + } + case Configuration.paths.materialExpressionConstant4Vector: + if (!input) { + /** @type {LinearColorEntity} */ + const vector = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "Constant") + ?.DefaultValue; + input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf()); + } + if (input.length > 0) { + return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur) + } + break + case Configuration.paths.materialExpressionFunctionInput: { + const materialObject = entity.getMaterialSubobject(); + const inputName = materialObject?.InputName ?? "In"; + const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3"; + return `Input ${inputName} (${inputType})` + } + case Configuration.paths.materialExpressionLogarithm: + return "Ln" + case Configuration.paths.materialExpressionLogarithm10: + return "Log10" + case Configuration.paths.materialExpressionLogarithm2: + return "Log2" + case Configuration.paths.materialExpressionMaterialFunctionCall: + const materialFunction = entity.getMaterialSubobject()?.MaterialFunction; + if (materialFunction) { + return materialFunction.getName() + } + break + case Configuration.paths.materialExpressionSquareRoot: + return "Sqrt" + case Configuration.paths.metasoundEditorGraphExternalNode: { + const name = entity["ClassName"]?.["Name"]; + if (name) { + switch (name) { + case "Add": return "+" + default: return name + } + } + } + case Configuration.paths.pcgEditorGraphNodeInput: + return "Input" + case Configuration.paths.pcgEditorGraphNodeOutput: + return "Output" + case Configuration.paths.spawnActorFromClass: + let className = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "ReturnValue") + ?.PinType + ?.PinSubCategoryObject + ?.getName(); + if (className === "Actor") { + className = null; + } + return `SpawnActor ${Utility.formatStringName(className ?? "NONE")}` + case Configuration.paths.switchEnum: + return `Switch on ${entity.Enum?.getName() ?? "Enum"}` + case Configuration.paths.switchInteger: + return `Switch on Int` + case Configuration.paths.variableGet: + return "" + case Configuration.paths.variableSet: + return "SET" + } + let switchTarget = entity.switchTarget(); + if (switchTarget) { + if (switchTarget[0] !== "E") { + switchTarget = Utility.formatStringName(switchTarget); + } + return `Switch on ${switchTarget}` + } + if (entity.isComment()) { + return entity.NodeComment + } + const keyNameSymbol = entity.getHIDAttribute(); + if (keyNameSymbol) { + const name = keyNameSymbol.toString(); + let title = keyName(name) ?? Utility.formatStringName(name); + if (entity.getClass() === Configuration.paths.inputDebugKey) { + title = "Debug Key " + title; + } else if (entity.getClass() === Configuration.paths.getInputAxisKeyValue) { + title = "Get " + title; + } + return title + } + if (entity.getClass() === Configuration.paths.macro) { + return Utility.formatStringName(entity.MacroGraphReference?.getMacroName()) + } + if (entity.isMaterial() && entity.getMaterialSubobject()) { + let result = nodeTitle(entity.getMaterialSubobject()); + result = result.match(/Material Expression (.+)/)?.[1] ?? result; + return result + } + if (entity.isPcg() && entity.getPcgSubobject()) { + let pcgSubobject = entity.getPcgSubobject(); + let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : nodeTitle(pcgSubobject); + return result + } + const subgraphObject = entity.getSubgraphObject(); + if (subgraphObject) { + return subgraphObject.Graph.getName() + } + const settingsObject = entity.getSettingsObject(); + if (settingsObject) { + if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) { + return `Grid Size: ${( + settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00") + ?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0] + ) ?? "256"}` + } + if (settingsObject.BlueprintElementInstance) { + return Utility.formatStringName(settingsObject.BlueprintElementType.getName()) + } + if (settingsObject.Operation) { + const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/); + if (match) { + return Utility.formatStringName(match[1] + ": " + settingsObject.Operation) + } + } + const settingsSubgraphObject = settingsObject.getSubgraphObject(); + if (settingsSubgraphObject && settingsSubgraphObject.Graph) { + return settingsSubgraphObject.Graph.getName() + } + } + let memberName = entity.FunctionReference?.MemberName; + if (memberName) { + const memberParent = entity.FunctionReference.MemberParent?.path ?? ""; + switch (memberName) { + case "AddKey": + let result = memberParent.match(sequencerScriptingNameRegex); + if (result) { + return `Add Key (${Utility.formatStringName(result[1])})` + } + case "Concat_StrStr": + return "Append" + } + const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern); + if (memberNameTraceLineMatch) { + return "Line Trace" + + (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ") + + (memberNameTraceLineMatch[2] === "" + ? "By Channel" + : Utility.formatStringName(memberNameTraceLineMatch[2]) + ) + } + switch (memberParent) { + case Configuration.paths.blueprintGameplayTagLibrary: + case Configuration.paths.kismetMathLibrary: + case Configuration.paths.slateBlueprintLibrary: + case Configuration.paths.timeManagementBlueprintLibrary: + const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/); + if (leadingLetter) { + // Some functions start with B or F (Like FCeil, FMax, BMin) + memberName = leadingLetter[1]; + } + switch (memberName) { + case "Abs": return "ABS" + case "BooleanAND": return "AND" + case "BooleanNAND": return "NAND" + case "BooleanOR": return "OR" + case "Exp": return "e" + case "LineTraceSingle": return "Line Trace By Channel" + case "Max": return "MAX" + case "MaxInt64": return "MAX" + case "Min": return "MIN" + case "MinInt64": return "MIN" + case "Not_PreBool": return "NOT" + case "Sin": return "SIN" + case "Sqrt": return "SQRT" + case "Square": return "^2" + // Dot products not respecting MemberName pattern + case "CrossProduct2D": return "cross" + case "Vector4_CrossProduct3": return "cross3" + case "DotProduct2D": + case "Vector4_DotProduct": + return "dot" + case "Vector4_DotProduct3": return "dot3" + } + if (memberName.startsWith("Add_")) { + return "+" + } + if (memberName.startsWith("And_")) { + return "&" + } + if (memberName.startsWith("Conv_")) { + return "" // Conversion nodes do not have visible names + } + if (memberName.startsWith("Cross_")) { + return "cross" + } + if (memberName.startsWith("Divide_")) { + return String.fromCharCode(0x00f7) + } + if (memberName.startsWith("Dot_")) { + return "dot" + } + if (memberName.startsWith("EqualEqual_")) { + return "==" + } + if (memberName.startsWith("Greater_")) { + return ">" + } + if (memberName.startsWith("GreaterEqual_")) { + return ">=" + } + if (memberName.startsWith("Less_")) { + return "<" + } + if (memberName.startsWith("LessEqual_")) { + return "<=" + } + if (memberName.startsWith("Multiply_")) { + return String.fromCharCode(0x2a2f) + } + if (memberName.startsWith("Not_")) { + return "~" + } + if (memberName.startsWith("NotEqual_")) { + return "!=" + } + if (memberName.startsWith("Or_")) { + return "|" + } + if (memberName.startsWith("Percent_")) { + return "%" + } + if (memberName.startsWith("Subtract_")) { + return "-" + } + if (memberName.startsWith("Xor_")) { + return "^" + } + break + case Configuration.paths.blueprintSetLibrary: + { + const setOperationMatch = memberName.match(/Set_(\w+)/); + if (setOperationMatch) { + return Utility.formatStringName(setOperationMatch[1]).toUpperCase() + } + } + break + case Configuration.paths.blueprintMapLibrary: + { + const setOperationMatch = memberName.match(/Map_(\w+)/); + if (setOperationMatch) { + return Utility.formatStringName(setOperationMatch[1]).toUpperCase() + } + } + break + case Configuration.paths.kismetArrayLibrary: + { + const arrayOperationMath = memberName.match(/Array_(\w+)/); + if (arrayOperationMath) { + return arrayOperationMath[1].toUpperCase() + } + } + break + } + return Utility.formatStringName(memberName) + } + if (entity.OpName) { + switch (entity.OpName) { + case "Boolean::LogicAnd": return "Logic AND" + case "Boolean::LogicEq": return "==" + case "Boolean::LogicNEq": return "!=" + case "Boolean::LogicNot": return "Logic NOT" + case "Boolean::LogicOr": return "Logic OR" + case "Matrix::MatrixMultiply": return "Multiply (Matrix * Matrix)" + case "Matrix::MatrixVectorMultiply": return "Multiply (Matrix * Vector4)" + case "Numeric::Abs": return "Abs" + case "Numeric::Add": return "+" + case "Numeric::DistancePos": return "Distance" + case "Numeric::Mul": return String.fromCharCode(0x2a2f) + } + return Utility.formatStringName(entity.OpName).replaceAll("::", " ") + } + if (entity.FunctionDisplayName) { + return Utility.formatStringName(entity.FunctionDisplayName) + } + if (entity.ObjectRef) { + return entity.ObjectRef.getName() + } + return Utility.formatStringName(entity.getNameAndCounter()[0]) +} - static createGrammar() { - const number = Parsernostrum.number.getParser().parser.regexp.source; - return Parsernostrum.alt( - Parsernostrum.regArray(new RegExp( - "(" + number + ")" - + "\\s*,\\s*" - + "(" + number + ")" - + "\\s*,\\s*" - + "(" + number + ")" - )) - .map(([_0, x, y, z]) => new this({ - X: Number(x), - Y: Number(y), - Z: Number(z), - })), - VectorEntity.createGrammar() - ) +/** @param {ObjectEntity} entity */ +function nodeIcon(entity) { + if (entity.isMaterial() || entity.isPcg() || entity.isNiagara()) { + return null + } + switch (entity.getType()) { + case Configuration.paths.addDelegate: + case Configuration.paths.asyncAction: + case Configuration.paths.callDelegate: + case Configuration.paths.createDelegate: + case Configuration.paths.functionEntry: + case Configuration.paths.functionResult: + return SVGIcon.node + case Configuration.paths.customEvent: return SVGIcon.event + case Configuration.paths.doN: return SVGIcon.doN + case Configuration.paths.doOnce: return SVGIcon.doOnce + case Configuration.paths.dynamicCast: return SVGIcon.cast + case Configuration.paths.enumLiteral: return SVGIcon.enum + case Configuration.paths.event: return SVGIcon.event + case Configuration.paths.executionSequence: + case Configuration.paths.multiGate: + return SVGIcon.sequence + case Configuration.paths.flipflop: + return SVGIcon.flipflop + case Configuration.paths.forEachElementInEnum: + case Configuration.paths.forLoop: + case Configuration.paths.forLoopWithBreak: + case Configuration.paths.whileLoop: + return SVGIcon.loop + case Configuration.paths.forEachLoop: + case Configuration.paths.forEachLoopWithBreak: + return SVGIcon.forEachLoop + case Configuration.paths.ifThenElse: return SVGIcon.branchNode + case Configuration.paths.isValid: return SVGIcon.questionMark + case Configuration.paths.makeArray: return SVGIcon.makeArray + case Configuration.paths.makeMap: return SVGIcon.makeMap + case Configuration.paths.makeSet: return SVGIcon.makeSet + case Configuration.paths.makeStruct: return SVGIcon.makeStruct + case Configuration.paths.metasoundEditorGraphExternalNode: return SVGIcon.metasoundFunction + case Configuration.paths.select: return SVGIcon.select + case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor + case Configuration.paths.timeline: return SVGIcon.timer + } + if (entity.switchTarget()) { + return SVGIcon.switch + } + if (nodeTitle(entity).startsWith("Break")) { + return SVGIcon.breakStruct + } + if (entity.getClass() === Configuration.paths.macro) { + return SVGIcon.macro + } + const hidValue = entity.getHIDAttribute()?.toString(); + if (hidValue) { + if (hidValue.includes("Mouse")) { + return SVGIcon.mouse + } else if (hidValue.includes("Gamepad_Special")) { + return SVGIcon.keyboard // It is called Touchpad in UE + } else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) { + return SVGIcon.gamepad + } else if (hidValue.includes("Touch")) { + return SVGIcon.touchpad + } else { + return SVGIcon.keyboard + } + } + if (entity.getDelegatePin()) { + return SVGIcon.event } + if (entity.ObjectRef?.type === Configuration.paths.ambientSound) { + return SVGIcon.sound + } + return SVGIcon.functionSymbol } -/** @template {TerminalAttribute} T */ -class PinEntity extends IEntity { +var crypto; +if (typeof window === "undefined") { + import('crypto').then(mod => crypto = mod.default).catch(); +} else { + crypto = window.crypto; +} + +class GuidEntity extends IEntity { - static #typeEntityMap = { - [Configuration.paths.linearColor]: LinearColorEntity, - [Configuration.paths.rotator]: RotatorEntity, - [Configuration.paths.vector]: VectorEntity, - [Configuration.paths.vector2D]: Vector2DEntity, - "bool": Boolean, - "byte": ByteEntity, - "enum": EnumEntity, - "exec": String, - "int": IntegerEntity, - "int64": Integer64Entity, - "name": String, - "real": Number, - "string": String, - } - static #alternativeTypeEntityMap = { - "enum": EnumDisplayValueEntity, - "rg": RBSerializationVector2DEntity, - [Configuration.paths.rotator]: SimpleSerializationRotatorEntity, - [Configuration.paths.vector]: SimpleSerializationVectorEntity, - [Configuration.paths.vector2D]: SimpleSerializationVector2DEntity, - } static attributes = { ...super.attributes, - lookbehind: new AttributeInfo({ - default: "Pin", - ignored: true, - }), - objectEntity: new AttributeInfo({ - ignored: true, - }), - pinIndex: new AttributeInfo({ - type: Number, - ignored: true, - }), - PinId: new AttributeInfo({ - type: GuidEntity, - default: () => new GuidEntity() - }), - PinName: AttributeInfo.createValue(""), - PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, String)), - PinToolTip: AttributeInfo.createType(String), - Direction: AttributeInfo.createType(String), - PinType: new AttributeInfo({ - type: PinTypeEntity, - default: () => new PinTypeEntity(), - inlined: true, - }), - LinkedTo: AttributeInfo.createType([PinReferenceEntity]), - SubPins: AttributeInfo.createType([PinReferenceEntity]), - ParentPin: AttributeInfo.createType(PinReferenceEntity), - DefaultValue: new AttributeInfo({ - type: new ComputedType( - /** @param {PinEntity} pinEntity */ - pinEntity => pinEntity.getEntityType(true) ?? String - ), - serialized: true, - }), - AutogeneratedDefaultValue: AttributeInfo.createType(String), - DefaultObject: AttributeInfo.createType(ObjectReferenceEntity), - PersistentGuid: AttributeInfo.createType(GuidEntity), - bHidden: AttributeInfo.createValue(false), - bNotConnectable: AttributeInfo.createValue(false), - bDefaultValueIsReadOnly: AttributeInfo.createValue(false), - bDefaultValueIsIgnored: AttributeInfo.createValue(false), - bAdvancedView: AttributeInfo.createValue(false), - bOrphanedPin: AttributeInfo.createValue(false), + value: AttributeInfo.createValue(""), } static grammar = this.createGrammar() - #recomputesNodeTitleOnChange = false - set recomputesNodeTitleOnChange(value) { - this.#recomputesNodeTitleOnChange = value; - } - get recomputesNodeTitleOnChange() { - return this.#recomputesNodeTitleOnChange + static createGrammar() { + return Grammar.guid.map(v => new this(v)) } - static createGrammar() { - return Grammar.createEntityGrammar(this) + static generateGuid(random = true) { + let values = new Uint32Array(4); + if (random === true) { + crypto.getRandomValues(values); + } + let guid = ""; + values.forEach(n => { + guid += ("0".repeat(8) + n.toString(16).toUpperCase()).slice(-8); + }); + return new GuidEntity({ value: guid }) } - constructor(values = {}, suppressWarns = false) { - super(values, suppressWarns); - /** @type {ObjectEntity} */ this.objectEntity; - /** @type {Number} */ this.pinIndex; - /** @type {GuidEntity} */ this.PinId; - /** @type {String} */ this.PinName; - /** @type {LocalizedTextEntity | String} */ this.PinFriendlyName; - /** @type {String} */ this.PinToolTip; - /** @type {String} */ this.Direction; - /** @type {PinTypeEntity} */ this.PinType; - /** @type {PinReferenceEntity[]} */ this.LinkedTo; - /** @type {T} */ this.DefaultValue; - /** @type {String} */ this.AutogeneratedDefaultValue; - /** @type {ObjectReferenceEntity} */ this.DefaultObject; - /** @type {GuidEntity} */ this.PersistentGuid; - /** @type {Boolean} */ this.bHidden; - /** @type {Boolean} */ this.bNotConnectable; - /** @type {Boolean} */ this.bDefaultValueIsReadOnly; - /** @type {Boolean} */ this.bDefaultValueIsIgnored; - /** @type {Boolean} */ this.bAdvancedView; - /** @type {Boolean} */ this.bOrphanedPin; + constructor(values) { + if (!values) { + values = GuidEntity.generateGuid().value; + } + if (values.constructor !== Object) { + values = { + value: values, + }; + } + super(values); + /** @type {String} */ this.value; } - /** @param {ObjectEntity} objectEntity */ - static fromLegacyObject(objectEntity) { - return new PinEntity(objectEntity, true) + valueOf() { + return this.value } - getType() { - const category = this.PinType.PinCategory; - if (category === "struct" || category === "object") { - return this.PinType.PinSubCategoryObject.path - } - if (this.isEnum()) { - return "enum" - } - if (this.objectEntity?.isPcg()) { - const pcgSuboject = this.objectEntity.getPcgSubobject(); - const pinObjectReference = this.isInput() - ? pcgSuboject.InputPins?.[this.pinIndex] - : pcgSuboject.OutputPins?.[this.pinIndex]; - if (pinObjectReference) { - /** @type {ObjectEntity} */ - const pinObject = pcgSuboject[Configuration.subObjectAttributeNameFromReference(pinObjectReference, true)]; - let allowedTypes = pinObject.Properties?.AllowedTypes?.toString() ?? ""; - if (allowedTypes == "") { - allowedTypes = this.PinType.PinCategory ?? ""; - if (allowedTypes == "") { - allowedTypes = "Any"; - } - } - if (allowedTypes) { - if ( - pinObject.Properties.bAllowMultipleData !== false - && pinObject.Properties.bAllowMultipleConnections !== false - ) { - allowedTypes += "[]"; - } - return allowedTypes - } - } - } - if (category === "optional") { - switch (this.PinType.PinSubCategory) { - case "red": - return "real" - case "rg": - return "rg" - case "rgb": - return Configuration.paths.vector - case "rgba": - return Configuration.paths.linearColor - default: - return this.PinType.PinSubCategory - } - } - return category - } - - getEntityType(alternative = false) { - const typeString = this.getType(); - const entity = PinEntity.#typeEntityMap[typeString]; - const alternativeEntity = PinEntity.#alternativeTypeEntityMap[typeString]; - return alternative && alternativeEntity !== undefined - ? alternativeEntity - : entity - } - - pinDisplayName() { - let result = this.PinFriendlyName - ? this.PinFriendlyName.toString() - : Utility.formatStringName(this.PinName ?? ""); - let match; - if ( - this.PinToolTip - // Match up until the first \n excluded or last character - && (match = this.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/)) - ) { - if (match[1].toLowerCase() === result.toLowerCase()) { - return match[1] // In case they match, then keep the case of the PinToolTip - } - } - return result - } - - /** @param {PinEntity} other */ - copyTypeFrom(other) { - this.PinType.PinCategory = other.PinType.PinCategory; - this.PinType.PinSubCategory = other.PinType.PinSubCategory; - this.PinType.PinSubCategoryObject = other.PinType.PinSubCategoryObject; - this.PinType.PinSubCategoryMemberReference = other.PinType.PinSubCategoryMemberReference; - this.PinType.PinValueType = other.PinType.PinValueType; - this.PinType.ContainerType = other.PinType.ContainerType; - this.PinType.bIsReference = other.PinType.bIsReference; - this.PinType.bIsConst = other.PinType.bIsConst; - this.PinType.bIsWeakPointer = other.PinType.bIsWeakPointer; - this.PinType.bIsUObjectWrapper = other.PinType.bIsUObjectWrapper; - this.PinType.bSerializeAsSinglePrecisionFloat = other.PinType.bSerializeAsSinglePrecisionFloat; - } - - getDefaultValue(maybeCreate = false) { - if (this.DefaultValue === undefined && maybeCreate) { - // @ts-expect-error - this.DefaultValue = new (this.getEntityType(true))(); - } - return this.DefaultValue - } - - isEnum() { - const type = this.PinType.PinSubCategoryObject.type; - return type === Configuration.paths.enum - || type === Configuration.paths.userDefinedEnum - || type.toLowerCase() === "enum" - } - - isExecution() { - return this.PinType.PinCategory === "exec" - } - - isHidden() { - return this.bHidden - } - - isInput() { - return !this.bHidden && this.Direction != "EGPD_Output" - } - - isOutput() { - return !this.bHidden && this.Direction == "EGPD_Output" + toString() { + return this.value } +} + +class ObjectReferenceEntity extends IEntity { - isLinked() { - return this.LinkedTo?.length > 0 ?? false + static attributes = { + ...super.attributes, + type: new AttributeInfo({ + default: "", + serialized: true, + }), + path: new AttributeInfo({ + default: "", + serialized: true, + }), + _full: new AttributeInfo({ + ignored: true, + }), } + static quoted = Parsernostrum.regArray(new RegExp( + `'"(${Grammar.Regex.InsideString.source})"'` + + "|" + + `'(${Grammar.Regex.InsideSingleQuotedString.source})'` + )).map(([_0, a, b]) => a ?? b) + static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source + static typeReference = Parsernostrum.reg( + new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source) + ) + static fullReferenceGrammar = Parsernostrum.regArray( + new RegExp( + "(" + this.typeReference.getParser().regexp.source + ")" + + "(?:" + this.quoted.getParser().parser.regexp.source + ")" + ) + ).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full })) + static fullReferenceSerializedGrammar = Parsernostrum.regArray( + new RegExp( + "(" + this.typeReference.getParser().regexp.source + ")" + + `'(` + Grammar.Regex.InsideSingleQuotedString.source + `)'` + ) + ).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full })) + static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "", _full: v })) + static grammar = this.createGrammar() - /** - * @param {String} targetObjectName - * @param {PinEntity} targetPinEntity - * @returns true if it was not already linked to the tarket - */ - linkTo(targetObjectName, targetPinEntity) { - const linkFound = this.LinkedTo?.some(pinReferenceEntity => - pinReferenceEntity.objectName.toString() == targetObjectName - && pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf() - ); - if (!linkFound) { - (this.LinkedTo ??= []).push(new PinReferenceEntity({ - objectName: targetObjectName, - pinGuid: targetPinEntity.PinId, - })); - return true + constructor(values = {}) { + if (values.constructor === String) { + values = { + path: values + }; } - return false // Already linked - } - - /** - * @param {String} targetObjectName - * @param {PinEntity} targetPinEntity - * @returns true if it was linked to the target - */ - unlinkFrom(targetObjectName, targetPinEntity) { - const indexElement = this.LinkedTo?.findIndex(pinReferenceEntity => { - return pinReferenceEntity.objectName.toString() == targetObjectName - && pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf() - }); - if (indexElement >= 0) { - this.LinkedTo.splice(indexElement, 1); - if (this.LinkedTo.length === 0 && PinEntity.attributes.LinkedTo.default === undefined) { - this.LinkedTo = undefined; - } - return true + super(values); + if (!values._full || values._full.length === 0) { + this._full = `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`; } - return false + /** @type {String} */ this.type; + /** @type {String} */ this.path; } - getSubCategory() { - return this.PinType.PinSubCategoryObject.path + static createGrammar() { + return Parsernostrum.alt( + Parsernostrum.seq( + Parsernostrum.str('"'), + Parsernostrum.alt( + this.fullReferenceSerializedGrammar, + this.typeReferenceGrammar, + ), + Parsernostrum.str('"'), + ).map(([_0, objectReference, _1]) => (objectReference._full = `"${objectReference._full}"`, objectReference)), + this.fullReferenceGrammar, + this.typeReferenceGrammar, + ) } - /** @return {CSSResult} */ - pinColor() { - if (this.PinType.PinCategory == "mask") { - const result = Configuration.pinColor[this.PinType.PinSubCategory]; - if (result) { - return result - } - } else if (this.PinType.PinCategory == "optional") { - return Configuration.pinColorMaterial - } - return Configuration.pinColor[this.getType()] - ?? Configuration.pinColor[this.PinType.PinCategory.toLowerCase()] - ?? Configuration.pinColor["default"] + static createNoneInstance() { + return new ObjectReferenceEntity({ type: "None", path: "" }) } -} - -class UnknownPinEntity extends PinEntity { - static grammar = this.createGrammar() - - static createGrammar() { - return Parsernostrum.seq( - Parsernostrum.reg( - new RegExp(`(${Grammar.Regex.Symbol.source})\\s*\\(\\s*`), - 1 - ), - Grammar.createAttributeGrammar(this).sepBy(Grammar.commaSeparation), - Parsernostrum.reg(/\s*(?:,\s*)?\)/) - ).map(([lookbehind, attributes, _2]) => { - lookbehind ??= ""; - let values = {}; - if (lookbehind.length) { - values.lookbehind = lookbehind; - } - attributes.forEach(attributeSetter => attributeSetter(values)); - return new this(values) - }) + getName(dropCounter = false) { + return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter) } - constructor(values = {}) { - super(values, true); + toString() { + return this._full } } -class VariableReferenceEntity extends IEntity { +class FunctionReferenceEntity extends IEntity { static attributes = { ...super.attributes, - MemberScope: AttributeInfo.createType(String), - MemberName: AttributeInfo.createValue(""), + MemberParent: AttributeInfo.createType(ObjectReferenceEntity), + MemberName: AttributeInfo.createType(String), MemberGuid: AttributeInfo.createType(GuidEntity), - bSelfContext: AttributeInfo.createType(Boolean), } static grammar = this.createGrammar() @@ -4785,2662 +4541,2464 @@ class VariableReferenceEntity extends IEntity { constructor(values) { super(values); + /** @type {ObjectReferenceEntity} */ this.MemberParent; /** @type {String} */ this.MemberName; - /** @type {GuidEntity} */ this.GuidEntity; - /** @type {Boolean} */ this.bSelfContext; + /** @type {GuidEntity} */ this.MemberGuid; } } -class ObjectEntity extends IEntity { - - static #keyName = { - "A_AccentGrave": "à", - "Add": "Num +", - "C_Cedille": "ç", - "Decimal": "Num .", - "Divide": "Num /", - "E_AccentAigu": "é", - "E_AccentGrave": "è", - "F1": "F1", // Otherwise F and number will be separated - "F10": "F10", - "F11": "F11", - "F12": "F12", - "F2": "F2", - "F3": "F3", - "F4": "F4", - "F5": "F5", - "F6": "F6", - "F7": "F7", - "F8": "F8", - "F9": "F9", - "Gamepad_Special_Left_X": "Touchpad Button X Axis", - "Gamepad_Special_Left_Y": "Touchpad Button Y Axis", - "Mouse2D": "Mouse XY 2D-Axis", - "Multiply": "Num *", - "Section": "§", - "Subtract": "Num -", - "Tilde": "`", - } - static attributes = { +class IdentifierEntity extends IEntity { + + static attributes = { + ...super.attributes, + value: AttributeInfo.createValue(""), + } + static attributeConverter = { + fromAttribute: (value, type) => new IdentifierEntity(value), + toAttribute: (value, type) => value.toString() + } + static grammar = this.createGrammar() + + static createGrammar() { + return Grammar.symbol.map(v => new this(v)) + } + + constructor(values) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + super(values); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } +} + +class IntegerEntity extends IEntity { + + static attributes = { ...super.attributes, - Class: AttributeInfo.createType(ObjectReferenceEntity), - Name: AttributeInfo.createType(String), - Archetype: AttributeInfo.createType(ObjectReferenceEntity), - ExportPath: AttributeInfo.createType(ObjectReferenceEntity), - R: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - G: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - B: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - A: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - ObjectRef: AttributeInfo.createType(ObjectReferenceEntity), - BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity), - BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity), - PinTags: new AttributeInfo({ - type: [null], - inlined: true, - }), - PinNames: new AttributeInfo({ - type: [String], - inlined: true, - }), - AxisKey: AttributeInfo.createType(SymbolEntity), - InputAxisKey: AttributeInfo.createType(SymbolEntity), - InputName: AttributeInfo.createType(String), - InputType: AttributeInfo.createType(SymbolEntity), - NumAdditionalInputs: AttributeInfo.createType(Number), - bIsPureFunc: AttributeInfo.createType(Boolean), - bIsConstFunc: AttributeInfo.createType(Boolean), - bIsCaseSensitive: AttributeInfo.createType(Boolean), - VariableReference: AttributeInfo.createType(VariableReferenceEntity), - SelfContextInfo: AttributeInfo.createType(SymbolEntity), - DelegatePropertyName: AttributeInfo.createType(String), - DelegateOwnerClass: AttributeInfo.createType(ObjectReferenceEntity), - ComponentPropertyName: AttributeInfo.createType(String), - EventReference: AttributeInfo.createType(FunctionReferenceEntity), - FunctionReference: AttributeInfo.createType(FunctionReferenceEntity), - CustomFunctionName: AttributeInfo.createType(String), - TargetType: AttributeInfo.createType(ObjectReferenceEntity), - MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity), - Enum: AttributeInfo.createType(ObjectReferenceEntity), - EnumEntries: new AttributeInfo({ - type: [String], - inlined: true, - }), - InputKey: AttributeInfo.createType(SymbolEntity), - MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity), - bOverrideFunction: AttributeInfo.createType(Boolean), - bInternalEvent: AttributeInfo.createType(Boolean), - bConsumeInput: AttributeInfo.createType(Boolean), - bExecuteWhenPaused: AttributeInfo.createType(Boolean), - bOverrideParentBinding: AttributeInfo.createType(Boolean), - bControl: AttributeInfo.createType(Boolean), - bAlt: AttributeInfo.createType(Boolean), - bShift: AttributeInfo.createType(Boolean), - bCommand: AttributeInfo.createType(Boolean), - CommentColor: AttributeInfo.createType(LinearColorEntity), - bCommentBubbleVisible_InDetailsPanel: AttributeInfo.createType(Boolean), - bColorCommentBubble: AttributeInfo.createType(Boolean), - ProxyFactoryFunctionName: AttributeInfo.createType(String), - ProxyFactoryClass: AttributeInfo.createType(ObjectReferenceEntity), - ProxyClass: AttributeInfo.createType(ObjectReferenceEntity), - StructType: AttributeInfo.createType(ObjectReferenceEntity), - MaterialExpression: AttributeInfo.createType(ObjectReferenceEntity), - MaterialExpressionComment: AttributeInfo.createType(ObjectReferenceEntity), - MoveMode: AttributeInfo.createType(SymbolEntity), - TimelineName: AttributeInfo.createType(String), - TimelineGuid: AttributeInfo.createType(GuidEntity), - SizeX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - SizeY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - Text: AttributeInfo.createType(new MirroredEntity(String)), - MaterialExpressionEditorX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - MaterialExpressionEditorY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - NodeTitle: AttributeInfo.createType(String), - NodeTitleColor: AttributeInfo.createType(LinearColorEntity), - PositionX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - PositionY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), - SettingsInterface: AttributeInfo.createType(ObjectReferenceEntity), - PCGNode: AttributeInfo.createType(ObjectReferenceEntity), - HiGenGridSize: AttributeInfo.createType(SymbolEntity), - Operation: AttributeInfo.createType(SymbolEntity), - NodePosX: AttributeInfo.createType(IntegerEntity), - NodePosY: AttributeInfo.createType(IntegerEntity), - NodeHeight: AttributeInfo.createType(IntegerEntity), - NodeWidth: AttributeInfo.createType(IntegerEntity), - Graph: AttributeInfo.createType(ObjectReferenceEntity), - SubgraphInstance: AttributeInfo.createType(String), - InputPins: new AttributeInfo({ - type: [ObjectReferenceEntity], - inlined: true, - }), - OutputPins: new AttributeInfo({ - type: [ObjectReferenceEntity], - inlined: true, + value: new AttributeInfo({ + default: 0, + predicate: v => v % 1 == 0 && v > 1 << 31 && v < -(1 << 31), }), - bExposeToLibrary: AttributeInfo.createType(Boolean), - bCanRenameNode: AttributeInfo.createType(Boolean), - bCommentBubblePinned: AttributeInfo.createType(Boolean), - bCommentBubbleVisible: AttributeInfo.createType(Boolean), - NodeComment: AttributeInfo.createType(String), - AdvancedPinDisplay: AttributeInfo.createType(IdentifierEntity), - DelegateReference: AttributeInfo.createType(VariableReferenceEntity), - EnabledState: AttributeInfo.createType(IdentifierEntity), - NodeGuid: AttributeInfo.createType(GuidEntity), - ErrorType: AttributeInfo.createType(IntegerEntity), - ErrorMsg: AttributeInfo.createType(String), - Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)), - CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]), } - static nameRegex = /^(\w+?)(?:_(\d+))?$/ - static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/ - static customPropertyGrammar = Parsernostrum.seq( - Parsernostrum.reg(/CustomProperties\s+/), - Grammar.grammarFor( - undefined, - this.attributes.CustomProperties.type[0] - ), - ).map(([_0, pin]) => values => { - if (!values.CustomProperties) { - values.CustomProperties = []; - } - values.CustomProperties.push(pin); - }) - static inlinedArrayEntryGrammar = Parsernostrum.seq( - Parsernostrum.alt( - Grammar.symbolQuoted.map(v => [v, true]), - Grammar.symbol.map(v => [v, false]), - ), - Parsernostrum.reg( - new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`), - 1 - ).map(Number) - ) - .chain( - /** @param {[[String, Boolean], Number]} param */ - ([[symbol, quoted], index]) => - Grammar.grammarFor(this.attributes[symbol]) - .map(currentValue => - values => { - (values[symbol] ??= [])[index] = currentValue; - Utility.objectSet(values, ["attributes", symbol, "quoted"], quoted); - if (!this.attributes[symbol]?.inlined) { - if (!values.attributes) { - IEntity.defineAttributes(values, {}); - } - Utility.objectSet(values, ["attributes", symbol, "type"], [currentValue.constructor]); - Utility.objectSet(values, ["attributes", symbol, "inlined"], true); - } - } - ) - ) static grammar = this.createGrammar() - static createSubObjectGrammar() { - return Parsernostrum.lazy(() => this.grammar) - .map(object => - values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object - ) - } - static createGrammar() { - return Parsernostrum.seq( - Parsernostrum.reg(/Begin\s+Object/), - Parsernostrum.seq( - Parsernostrum.whitespace, - Parsernostrum.alt( - this.customPropertyGrammar, - Grammar.createAttributeGrammar(this), - Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) => - Utility.objectSet(obj, ["attributes", ...k, "quoted"], true) - ), - this.inlinedArrayEntryGrammar, - this.createSubObjectGrammar() - ) - ) - .map(([_0, entry]) => entry) - .many(), - Parsernostrum.reg(/\s+End\s+Object/), - ) - .map(([_0, attributes, _2]) => { - const values = {}; - attributes.forEach(attributeSetter => attributeSetter(values)); - return new this(values) - }) + return Parsernostrum.numberInteger.map(v => new this(v)) } - /** @param {String} value */ - static keyName(value) { - /** @type {String} */ - let result = ObjectEntity.#keyName[value]; - if (result) { - return result - } - result = Utility.numberFromText(value)?.toString(); - if (result) { - return result - } - const match = value.match(/NumPad([a-zA-Z]+)/); - if (match) { - result = Utility.numberFromText(match[1]).toString(); - if (result) { - return "Num " + result - } + /** @param {Number | Object} values */ + constructor(values = 0) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + values.value = Math.floor(values.value); + if (values.value === -0) { + values.value = 0; } + super(values); + /** @type {Number} */ this.value; } - static getMultipleObjectsGrammar() { - return Parsernostrum.seq( - Parsernostrum.whitespaceOpt, - this.createGrammar(), - Parsernostrum.seq( - Parsernostrum.whitespace, - this.createGrammar(), - ) - .map(([_0, object]) => object) - .many(), - Parsernostrum.whitespaceOpt - ) - .map(([_0, first, remaining, _4]) => [first, ...remaining]) + valueOf() { + return this.value } - /** @type {String} */ - #class + toString() { + return this.value.toString() + } +} + +class MacroGraphReferenceEntity extends IEntity { - constructor(values = {}, suppressWarns = false) { - if ("NodePosX" in values !== "NodePosY" in values) { - const entries = Object.entries(values); - const [key, position] = "NodePosX" in values - ? ["NodePosY", Object.keys(values).indexOf("NodePosX") + 1] - : ["NodePosX", Object.keys(values).indexOf("NodePosY")]; - const entry = [key, new (AttributeInfo.getAttribute(values, key, "type", ObjectEntity))()]; - entries.splice(position, 0, entry); - values = Object.fromEntries(entries); - } - super(values, suppressWarns); - - // Attributes not assigned a strong type in attributes because the names are too generic - /** @type {Number | MirroredEntity} */ this.R; - /** @type {Number | MirroredEntity} */ this.G; - /** @type {Number | MirroredEntity} */ this.B; - /** @type {Number | MirroredEntity} */ this.A; - - // Attributes - /** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties; - /** @type {Boolean} */ this.bIsPureFunc; - /** @type {FunctionReferenceEntity} */ this.ComponentPropertyName; - /** @type {FunctionReferenceEntity} */ this.EventReference; - /** @type {FunctionReferenceEntity} */ this.FunctionReference; - /** @type {IdentifierEntity} */ this.AdvancedPinDisplay; - /** @type {IdentifierEntity} */ this.EnabledState; - /** @type {IntegerEntity} */ this.NodeHeight; - /** @type {IntegerEntity} */ this.NodePosX; - /** @type {IntegerEntity} */ this.NodePosY; - /** @type {IntegerEntity} */ this.NodeWidth; - /** @type {LinearColorEntity} */ this.CommentColor; - /** @type {LinearColorEntity} */ this.NodeTitleColor; - /** @type {MacroGraphReferenceEntity} */ this.MacroGraphReference; - /** @type {MirroredEntity} */ this.MaterialExpressionEditorX; - /** @type {MirroredEntity} */ this.MaterialExpressionEditorY; - /** @type {MirroredEntity} */ this.SizeX; - /** @type {MirroredEntity} */ this.SizeY; - /** @type {MirroredEntity} */ this.Text; - /** @type {MirroredEntity} */ this.PositionX; - /** @type {MirroredEntity} */ this.PositionY; - /** @type {MirroredEntity} */ this.Node; - /** @type {null[]} */ this.PinTags; - /** @type {Number} */ this.NumAdditionalInputs; - /** @type {ObjectReferenceEntity[]} */ this.InputPins; - /** @type {ObjectReferenceEntity[]} */ this.OutputPins; - /** @type {ObjectReferenceEntity} */ this.Archetype; - /** @type {ObjectReferenceEntity} */ this.BlueprintElementInstance; - /** @type {ObjectReferenceEntity} */ this.BlueprintElementType; - /** @type {ObjectReferenceEntity} */ this.Class; - /** @type {ObjectReferenceEntity} */ this.Enum; - /** @type {ObjectReferenceEntity} */ this.ExportPath; - /** @type {ObjectReferenceEntity} */ this.Graph; - /** @type {ObjectReferenceEntity} */ this.MaterialExpression; - /** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment; - /** @type {ObjectReferenceEntity} */ this.MaterialFunction; - /** @type {ObjectReferenceEntity} */ this.ObjectRef; - /** @type {ObjectReferenceEntity} */ this.PCGNode; - /** @type {ObjectReferenceEntity} */ this.SettingsInterface; - /** @type {ObjectReferenceEntity} */ this.StructType; - /** @type {ObjectReferenceEntity} */ this.TargetType; - /** @type {String[]} */ this.EnumEntries; - /** @type {String[]} */ this.PinNames; - /** @type {String} */ this.CustomFunctionName; - /** @type {String} */ this.DelegatePropertyName; - /** @type {String} */ this.InputName; - /** @type {String} */ this.Name; - /** @type {String} */ this.NodeComment; - /** @type {String} */ this.NodeTitle; - /** @type {String} */ this.Operation; - /** @type {String} */ this.ProxyFactoryFunctionName; - /** @type {String} */ this.SubgraphInstance; - /** @type {String} */ this.Text; - /** @type {SymbolEntity} */ this.AxisKey; - /** @type {SymbolEntity} */ this.HiGenGridSize; - /** @type {SymbolEntity} */ this.InputAxisKey; - /** @type {SymbolEntity} */ this.InputKey; - /** @type {SymbolEntity} */ this.InputType; - /** @type {VariableReferenceEntity} */ this.DelegateReference; - /** @type {VariableReferenceEntity} */ this.VariableReference; - - // Legacy nodes pins - if (this["Pins"] instanceof Array) { - this["Pins"].forEach( - /** @param {ObjectReferenceEntity} objectReference */ - objectReference => { - const pinObject = this[Configuration.subObjectAttributeNameFromReference(objectReference, true)]; - if (pinObject) { - const pinEntity = PinEntity.fromLegacyObject(pinObject); - pinEntity.LinkedTo = []; - this.getCustomproperties(true).push(pinEntity); - Utility.objectSet(this, ["attributes", "CustomProperties", "ignored"], true); - } - } - ); - } - /** @type {ObjectEntity} */ - const materialSubobject = this.getMaterialSubobject(); - if (materialSubobject) { - const obj = materialSubobject; - obj.SizeX !== undefined && (obj.SizeX.getter = () => this.NodeWidth); - obj.SizeY && (obj.SizeY.getter = () => this.NodeHeight); - obj.Text && (obj.Text.getter = () => this.NodeComment); - obj.MaterialExpressionEditorX && (obj.MaterialExpressionEditorX.getter = () => this.NodePosX); - obj.MaterialExpressionEditorY && (obj.MaterialExpressionEditorY.getter = () => this.NodePosY); - if (this.getType() === Configuration.paths.materialExpressionComponentMask) { - // The following attributes are too generic therefore not assigned a MirroredEntity - const rgbaPins = Configuration.rgba.map(pinName => - this.getPinEntities().find(pin => pin.PinName === pinName && (pin.recomputesNodeTitleOnChange = true)) - ); - obj.R = new MirroredEntity(Boolean, () => rgbaPins[0].DefaultValue); - obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue); - obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue); - obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue); - } - } - /** @type {ObjectEntity} */ - const pcgObject = this.getPcgSubobject(); - if (pcgObject) { - pcgObject.PositionX && (pcgObject.PositionX.getter = () => this.NodePosX); - pcgObject.PositionY && (pcgObject.PositionY.getter = () => this.NodePosY); - pcgObject.getSubobjects() - .forEach( - /** @param {ObjectEntity} obj */ - obj => { - if (obj.Node !== undefined) { - const nodeRef = obj.Node.get(); - if ( - nodeRef.type === this.PCGNode.type - && nodeRef.path === `${this.Name}.${this.PCGNode.path}` - ) { - obj.Node.getter = () => new ObjectReferenceEntity({ - type: this.PCGNode.type, - path: `${this.Name}.${this.PCGNode.path}`, - }); - } - } - } - ); - - } - let inputIndex = 0; - let outputIndex = 0; - this.CustomProperties?.forEach((pinEntity, i) => { - pinEntity.objectEntity = this; - pinEntity.pinIndex = pinEntity.isInput() - ? inputIndex++ - : pinEntity.isOutput() - ? outputIndex++ - : i; - }); + static attributes = { + ...super.attributes, + MacroGraph: new AttributeInfo({ + type: ObjectReferenceEntity, + default: () => new ObjectReferenceEntity(), + }), + GraphBlueprint: new AttributeInfo({ + type: ObjectReferenceEntity, + default: () => new ObjectReferenceEntity(), + }), + GraphGuid: new AttributeInfo({ + type: GuidEntity, + default: () => new GuidEntity(), + }), } + static grammar = this.createGrammar() - getClass() { - if (!this.#class) { - this.#class = (this.Class?.path ? this.Class.path : this.Class?.type) - ?? (this.ExportPath?.path ? this.ExportPath.path : this.ExportPath?.type) - ?? ""; - if (this.#class && !this.#class.startsWith("/")) { - // Old path names did not start with /Script or /Engine, check tests/resources/LegacyNodes.js - let path = Object.values(Configuration.paths).find(path => path.endsWith("." + this.#class)); - if (path) { - this.#class = path; - } - } - } - return this.#class + static createGrammar() { + return Grammar.createEntityGrammar(this) } - getType() { - let classValue = this.getClass(); - if (this.MacroGraphReference?.MacroGraph?.path) { - return this.MacroGraphReference.MacroGraph.path - } - if (this.MaterialExpression) { - return this.MaterialExpression.type - } - return classValue + constructor(values) { + super(values); + /** @type {ObjectReferenceEntity} */ this.MacroGraph; + /** @type {ObjectReferenceEntity} */ this.GraphBlueprint; + /** @type {GuidEntity} */ this.GuidEntity; } - getObjectName(dropCounter = false) { - if (dropCounter) { - return this.getNameAndCounter()[0] - } - return this.Name + getMacroName() { + const colonIndex = this.MacroGraph.path.search(":"); + return this.MacroGraph.path.substring(colonIndex + 1) } +} + +const colors = { + [Configuration.paths.niagaraBool]: i$3`146, 0, 0`, + [Configuration.paths.niagaraDataInterfaceVolumeTexture]: i$3`0, 168, 242`, + [Configuration.paths.niagaraFloat]: i$3`160, 250, 68`, + [Configuration.paths.niagaraMatrix]: i$3`0, 88, 200`, + [Configuration.paths.niagaraNumeric]: i$3`0, 88, 200`, + [Configuration.paths.niagaraPosition]: i$3`251, 146, 251`, + [Configuration.paths.quat4f]: i$3`0, 88, 200`, + [Configuration.paths.rotator]: i$3`157, 177, 251`, + [Configuration.paths.transform]: i$3`227, 103, 0`, + [Configuration.paths.vector]: i$3`251, 198, 34`, + [Configuration.paths.vector3f]: i$3`250, 200, 36`, + [Configuration.paths.vector4f]: i$3`0, 88, 200`, + "Any": i$3`132, 132, 132`, + "Any[]": i$3`132, 132, 132`, + "audio": i$3`252, 148, 252`, + "blue": i$3`0, 0, 255`, + "bool": i$3`146, 0, 0`, + "byte": i$3`0, 109, 99`, + "class": i$3`88, 0, 186`, + "default": i$3`255, 255, 255`, + "delegate": i$3`255, 56, 56`, + "enum": i$3`0, 109, 99`, + "exec": i$3`240, 240, 240`, + "float": i$3`160, 252, 70`, + "green": i$3`0, 255, 0`, + "int": i$3`31, 224, 172`, + "int32": i$3`30, 224, 172`, + "int64": i$3`169, 223, 172`, + "interface": i$3`238, 252, 168`, + "name": i$3`201, 128, 251`, + "object": i$3`0, 168, 242`, + "Param": i$3`255, 166, 39`, + "Param[]": i$3`255, 166, 39`, + "Point": i$3`63, 137, 255`, + "Point[]": i$3`63, 137, 255`, + "real": i$3`54, 208, 0`, + "red": i$3`255, 0, 0`, + "string": i$3`251, 0, 208`, + "struct": i$3`0, 88, 200`, + "Surface": i$3`69, 196, 126`, + "Surface[]": i$3`69, 196, 126`, + "text": i$3`226, 121, 167`, + "time": i$3`148, 252, 252`, + "Volume": i$3`230, 69, 188`, + "Volume[]": i$3`230, 69, 188`, + "wildcard": i$3`128, 120, 120`, +}; + +const pinColorMaterial = i$3`120, 120, 120`; + +/** @param {PinEntity} entity */ +function pinColor(entity) { + if (entity.PinType.PinCategory == "mask") { + const result = colors[entity.PinType.PinSubCategory]; + if (result) { + return result + } + } else if (entity.PinType.PinCategory == "optional") { + return pinColorMaterial + } + return colors[entity.getType()] + ?? colors[entity.PinType.PinCategory.toLowerCase()] + ?? colors["default"] +} + +/** @param {PinEntity} entity */ +function pinTitle(entity) { + let result = entity.PinFriendlyName + ? entity.PinFriendlyName.toString() + : Utility.formatStringName(entity.PinName ?? ""); + let match; + if ( + entity.PinToolTip + // Match up until the first \n excluded or last character + && (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/)) + ) { + if (match[1].toLowerCase() === result.toLowerCase()) { + return match[1] // In case they match, then keep the case of the PinToolTip + } + } + return result +} + +class ByteEntity extends IntegerEntity { - /** @returns {[String, Number]} */ - getNameAndCounter() { - const result = this.getObjectName(false).match(ObjectEntity.nameRegex); - let name = ""; - let counter = null; - if (result) { - if (result.length > 1) { - name = result[1]; - } - if (result.length > 2) { - counter = parseInt(result[2]); - } - return [name, counter] - } - return ["", 0] + static attributes = { + ...super.attributes, + value: new AttributeInfo({ + ...super.attributes.value, + predicate: v => v % 1 == 0 && v >= 0 && v < 1 << 8, + }), } + static grammar = this.createGrammar() - getCounter() { - return this.getNameAndCounter()[1] + static createGrammar() { + return Parsernostrum.numberByte.map(v => new this(v)) } - getNodeWidth() { - return this.NodeWidth - ?? this.isComment() ? Configuration.defaultCommentWidth : undefined + constructor(values = 0) { + super(values); } +} + +class SymbolEntity extends IEntity { + + static attributes = { + ...super.attributes, + value: AttributeInfo.createValue(""), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Grammar.symbol.map(v => new this(v)) + } + + /** @param {String | Object} values */ + constructor(values) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + super(values); + /** @type {String} */ this.value; + } + + valueOf() { + return this.value + } + + toString() { + return this.value + } +} + +class EnumEntity extends SymbolEntity { - /** @param {Number} value */ - setNodeWidth(value) { - if (!this.NodeWidth) { - this.NodeWidth = new IntegerEntity(); - } - this.NodeWidth.value = value; - } + static grammar = this.createGrammar() - getNodeHeight() { - return this.NodeHeight - ?? this.isComment() ? Configuration.defaultCommentHeight : undefined + static createGrammar() { + return Grammar.symbol.map(v => new this(v)) } +} + +class EnumDisplayValueEntity extends EnumEntity { - /** @param {Number} value */ - setNodeHeight(value) { - if (!this.NodeHeight) { - this.NodeHeight = new IntegerEntity(); - } - this.NodeHeight.value = value; - } + static grammar = this.createGrammar() - getNodePosX() { - return this.NodePosX?.value ?? 0 + static createGrammar() { + return Parsernostrum.reg(Grammar.Regex.InsideString).map(v => new this(v)) } +} + +class InvariantTextEntity extends IEntity { + + static attributes = { + ...super.attributes, + value: AttributeInfo.createValue(""), + lookbehind: new AttributeInfo({ + ...super.attributes.lookbehind, + default: "INVTEXT", + }), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Parsernostrum.alt( + Parsernostrum.seq( + Parsernostrum.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)), + Grammar.grammarFor(this.attributes.value), + Parsernostrum.reg(/\s*\)/) + ) + .map(([_0, value, _2]) => value), + Parsernostrum.reg(new RegExp(this.attributes.lookbehind.default)) // InvariantTextEntity can not have arguments + .map(() => "") + ).map(value => new this(value)) + } + + constructor(values) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + super(values); + /** @type {String} */ this.value; + } + + toString() { + return this.value + } +} + +class LocalizedTextEntity extends IEntity { - /** @param {Number} value */ - setNodePosX(value) { - if (!this.NodePosX) { - this.NodePosX = new IntegerEntity(); - } - this.NodePosX.value = Math.round(value); + static attributes = { + ...super.attributes, + namespace: AttributeInfo.createValue(""), + key: AttributeInfo.createValue(""), + value: AttributeInfo.createValue(""), + lookbehind: new AttributeInfo({ + ...super.attributes.lookbehind, + default: "NSLOCTEXT", + }), } + static grammar = this.createGrammar() - getNodePosY() { - return this.NodePosY?.value ?? 0 + static createGrammar() { + return Parsernostrum.regArray(new RegExp( + String.raw`${this.attributes.lookbehind.default}\s*\(` + + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,` + + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,` + + String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*` + + String.raw`(?:,\s+)?` + + String.raw`\)`, + "m" + )).map(matchResult => new this({ + namespace: Utility.unescapeString(matchResult[1]), + key: Utility.unescapeString(matchResult[2]), + value: Utility.unescapeString(matchResult[3]), + })) } - /** @param {Number} value */ - setNodePosY(value) { - if (!this.NodePosY) { - this.NodePosY = new IntegerEntity(); - } - this.NodePosY.value = Math.round(value); + constructor(values) { + super(values); + /** @type {String} */ this.namespace; + /** @type {String} */ this.key; + /** @type {String} */ this.value; } - getCustomproperties(canCreate = false) { - if (canCreate && !this.CustomProperties) { - this.CustomProperties = []; - } - return this.CustomProperties ?? [] + toString() { + return Utility.capitalFirstLetter(this.value) } +} + +class FormatTextEntity extends IEntity { + + static attributes = { + ...super.attributes, + value: new AttributeInfo({ + type: [new Union(String, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)], + default: [], + }), + lookbehind: /** @type {AttributeInfo>} */(new AttributeInfo({ + ...super.attributes.lookbehind, + default: new Union("LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"), + })), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Parsernostrum.seq( + Parsernostrum.reg( + // Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/ + new RegExp(`(${this.attributes.lookbehind.default.values.reduce((acc, cur) => acc + "|" + cur)})\\s*`), + 1 + ), + Grammar.grammarFor(this.attributes.value) + ) + .map(([lookbehind, values]) => { + const result = new this({ + value: values, + lookbehind, + }); + return result + }) + } + + constructor(values) { + super(values); + /** @type {(String | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} */ this.value; + } + + toString() { + const pattern = this.value?.[0]?.toString(); // The pattern is always the first element of the array + if (!pattern) { + return "" + } + const values = this.value.slice(1).map(v => v.toString()); + return this.lookbehind == "LOCGEN_FORMAT_NAMED" + ? pattern.replaceAll(/\{([a-zA-Z]\w*)\}/g, (substring, arg) => { + const argLocation = values.indexOf(arg) + 1; + return argLocation > 0 && argLocation < values.length + ? values[argLocation] + : substring + }) + : this.lookbehind == "LOCGEN_FORMAT_ORDERED" + ? pattern.replaceAll(/\{(\d+)\}/g, (substring, arg) => { + const argValue = Number(arg); + return argValue < values.length + ? values[argValue] + : substring + }) + : "" + } +} + +class Integer64Entity extends IEntity { - /** @returns {PinEntity[]} */ - getPinEntities() { - return this.getCustomproperties().filter(v => v.constructor === PinEntity) + static attributes = { + ...super.attributes, + value: new AttributeInfo({ + default: 0n, + predicate: v => v >= -(1n << 63n) && v < 1n << 63n, + }), } + static grammar = this.createGrammar() - /** @returns {ObjectEntity[]} */ - getSubobjects() { - return Object.keys(this) - .filter(k => k.startsWith(Configuration.subObjectAttributeNamePrefix)) - .flatMap(k => [this[k], .../** @type {ObjectEntity} */(this[k]).getSubobjects()]) + static createGrammar() { + return Parsernostrum.numberBigInteger.map(v => new this(v)) } - switchTarget() { - const switchMatch = this.getClass().match(Configuration.switchTargetPattern); - if (switchMatch) { - return switchMatch[1] + /** @param {BigInt | Number | Object} values */ + constructor(values = 0) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + if (values.value === -0) { + values.value = 0n; } + super(values); + /** @type {BigInt} */ this.value; } - isEvent() { - switch (this.getClass()) { - case Configuration.paths.actorBoundEvent: - case Configuration.paths.componentBoundEvent: - case Configuration.paths.customEvent: - case Configuration.paths.event: - case Configuration.paths.inputAxisKeyEvent: - case Configuration.paths.inputVectorAxisEvent: - return true - } - return false + valueOf() { + return this.value } - isComment() { - switch (this.getClass()) { - case Configuration.paths.comment: - case Configuration.paths.materialGraphNodeComment: - return true - } - return false + toString() { + return this.value.toString() } +} + +class PathSymbolEntity extends IEntity { - isMaterial() { + static attributes = { + ...super.attributes, + value: new AttributeInfo({ + default: "", + }), + } + static grammar = this.createGrammar() - return this.getClass() === Configuration.paths.materialGraphNode - // return [ - // Configuration.paths.materialExpressionConstant, - // Configuration.paths.materialExpressionConstant2Vector, - // Configuration.paths.materialExpressionConstant3Vector, - // Configuration.paths.materialExpressionConstant4Vector, - // Configuration.paths.materialExpressionLogarithm, - // Configuration.paths.materialExpressionLogarithm10, - // Configuration.paths.materialExpressionLogarithm2, - // Configuration.paths.materialExpressionMaterialFunctionCall, - // Configuration.paths.materialExpressionSquareRoot, - // Configuration.paths.materialExpressionTextureCoordinate, - // Configuration.paths.materialExpressionTextureSample, - // Configuration.paths.materialGraphNode, - // Configuration.paths.materialGraphNodeComment, - // ] - // .includes(this.getClass()) - } - - /** @return {ObjectEntity} */ - getMaterialSubobject() { - const expression = this.MaterialExpression ?? this.MaterialExpressionComment; - return expression - ? this[Configuration.subObjectAttributeNameFromReference(expression, true)] - : null - } - - isPcg() { - return this.getClass() === Configuration.paths.pcgEditorGraphNode - || this.getPcgSubobject() + static createGrammar() { + return Grammar.symbol.map(v => new this(v)) } - /** @return {ObjectEntity} */ - getPcgSubobject() { - const node = this.PCGNode; - return node - ? this[Configuration.subObjectAttributeNameFromReference(node, true)] - : null + constructor(values) { + if (values.constructor !== Object) { + values = { + value: values, + }; + } + super(values); + /** @type {String} */ this.value; } - /** @return {ObjectEntity} */ - getSettingsObject() { - const settings = this.SettingsInterface; - return settings - ? this[Configuration.subObjectAttributeNameFromReference(settings, true)] - : null + valueOf() { + return this.value } - /** @return {ObjectEntity} */ - getSubgraphObject() { - const node = this.SubgraphInstance; - return node - ? this[Configuration.subObjectAttributeNameFromName(node)] - : null + toString() { + return this.value } +} + +class PinReferenceEntity extends IEntity { - isDevelopmentOnly() { - const nodeClass = this.getClass(); - return this.EnabledState?.toString() === "DevelopmentOnly" - || nodeClass.includes("Debug", Math.max(0, nodeClass.lastIndexOf("."))) + static attributes = { + ...super.attributes, + objectName: AttributeInfo.createType(PathSymbolEntity), + pinGuid: AttributeInfo.createType(GuidEntity), } + static grammar = this.createGrammar() - getHIDAttribute() { - return this.InputKey ?? this.AxisKey ?? this.InputAxisKey + static createGrammar() { + return Parsernostrum.seq( + PathSymbolEntity.grammar, + Parsernostrum.whitespace, + GuidEntity.grammar + ).map( + ([objectName, _1, pinGuid]) => new this({ + objectName: objectName, + pinGuid: pinGuid, + }) + ) } - getDelegatePin() { - return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate") + constructor(values) { + super(values); + /** @type {PathSymbolEntity} */ this.objectName; + /** @type {GuidEntity} */ this.pinGuid; } +} + +class PinTypeEntity extends IEntity { - /** @returns {String} */ - nodeDisplayName() { - let input; - switch (this.getType()) { - case Configuration.paths.asyncAction: - if (this.ProxyFactoryFunctionName) { - return Utility.formatStringName(this.ProxyFactoryFunctionName) - } - case Configuration.paths.actorBoundEvent: - case Configuration.paths.componentBoundEvent: - return `${Utility.formatStringName(this.DelegatePropertyName)} (${this.ComponentPropertyName ?? "Unknown"})` - case Configuration.paths.callDelegate: - return `Call ${this.DelegateReference?.MemberName ?? "None"}` - case Configuration.paths.createDelegate: - return "Create Event" - case Configuration.paths.customEvent: - if (this.CustomFunctionName) { - return this.CustomFunctionName - } - case Configuration.paths.dynamicCast: - if (!this.TargetType) { - return "Bad cast node" // Target type not found - } - return `Cast To ${this.TargetType?.getName()}` - case Configuration.paths.enumLiteral: - return `Literal enum ${this.Enum?.getName()}` - case Configuration.paths.event: - return `Event ${(this.EventReference?.MemberName ?? "").replace(/^Receive/, "")}` - case Configuration.paths.executionSequence: - return "Sequence" - case Configuration.paths.forEachElementInEnum: - return `For Each ${this.Enum?.getName()}` - case Configuration.paths.forEachLoopWithBreak: - return "For Each Loop with Break" - case Configuration.paths.functionEntry: - return this.FunctionReference?.MemberName === "UserConstructionScript" - ? "Construction Script" - : this.FunctionReference?.MemberName - case Configuration.paths.functionResult: - return "Return Node" - case Configuration.paths.ifThenElse: - return "Branch" - case Configuration.paths.makeStruct: - if (this.StructType) { - return `Make ${this.StructType.getName()}` - } - case Configuration.paths.materialExpressionComponentMask: { - const materialObject = this.getMaterialSubobject(); - return `Mask ( ${Configuration.rgba - .filter(k => /** @type {MirroredEntity} */(materialObject[k]).get() === true) - .map(v => v + " ") - .join("")})` - } - case Configuration.paths.materialExpressionConstant: - input ??= [this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]; - case Configuration.paths.materialExpressionConstant2Vector: - input ??= [ - this.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue, - this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue, - ]; - case Configuration.paths.materialExpressionConstant3Vector: - if (!input) { - /** @type {VectorEntity} */ - const vector = this.getCustomproperties() - .find(pinEntity => pinEntity.PinName == "Constant") - ?.DefaultValue; - input = [vector.X, vector.Y, vector.Z]; - } - case Configuration.paths.materialExpressionConstant4Vector: - if (!input) { - /** @type {LinearColorEntity} */ - const vector = this.getCustomproperties() - .find(pinEntity => pinEntity.PinName == "Constant") - ?.DefaultValue; - input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf()); - } - if (input.length > 0) { - return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur) - } - break - case Configuration.paths.materialExpressionFunctionInput: { - const materialObject = this.getMaterialSubobject(); - const inputName = materialObject?.InputName ?? "In"; - const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3"; - return `Input ${inputName} (${inputType})` - } - case Configuration.paths.materialExpressionLogarithm: - return "Ln" - case Configuration.paths.materialExpressionLogarithm10: - return "Log10" - case Configuration.paths.materialExpressionLogarithm2: - return "Log2" - case Configuration.paths.materialExpressionMaterialFunctionCall: - const materialFunction = this.getMaterialSubobject()?.MaterialFunction; - if (materialFunction) { - return materialFunction.getName() - } - break - case Configuration.paths.materialExpressionSquareRoot: - return "Sqrt" - case Configuration.paths.pcgEditorGraphNodeInput: - return "Input" - case Configuration.paths.pcgEditorGraphNodeOutput: - return "Output" - case Configuration.paths.spawnActorFromClass: - return `SpawnActor ${Utility.formatStringName( - this.getCustomproperties().find(pinEntity => pinEntity.getType() == "class")?.DefaultObject?.getName() - ?? "NONE" - )}` - case Configuration.paths.switchEnum: - return `Switch on ${this.Enum?.getName() ?? "Enum"}` - case Configuration.paths.switchInteger: - return `Switch on Int` - case Configuration.paths.variableGet: - return "" - case Configuration.paths.variableSet: - return "SET" - } - let switchTarget = this.switchTarget(); - if (switchTarget) { - if (switchTarget[0] !== "E") { - switchTarget = Utility.formatStringName(switchTarget); - } - return `Switch on ${switchTarget}` - } - if (this.isComment()) { - return this.NodeComment - } - const keyNameSymbol = this.getHIDAttribute(); - if (keyNameSymbol) { - const keyName = keyNameSymbol.toString(); - let title = ObjectEntity.keyName(keyName) ?? Utility.formatStringName(keyName); - if (this.getClass() === Configuration.paths.inputDebugKey) { - title = "Debug Key " + title; - } else if (this.getClass() === Configuration.paths.getInputAxisKeyValue) { - title = "Get " + title; - } - return title - } - if (this.getClass() === Configuration.paths.macro) { - return Utility.formatStringName(this.MacroGraphReference?.getMacroName()) - } - if (this.isMaterial() && this.getMaterialSubobject()) { - let result = this.getMaterialSubobject().nodeDisplayName(); - result = result.match(/Material Expression (.+)/)?.[1] ?? result; - return result - } - if (this.isPcg() && this.getPcgSubobject()) { - let pcgSubobject = this.getPcgSubobject(); - let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : pcgSubobject.nodeDisplayName(); - return result - } - const subgraphObject = this.getSubgraphObject(); - if (subgraphObject) { - return subgraphObject.Graph.getName() - } - const settingsObject = this.getSettingsObject(); - if (settingsObject) { - if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) { - return `Grid Size: ${( - settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00") - ?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0] - ) ?? "256"}` - } - if (settingsObject.BlueprintElementInstance) { - return Utility.formatStringName(settingsObject.BlueprintElementType.getName()) - } - if (settingsObject.Operation) { - const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/); - if (match) { - return Utility.formatStringName(match[1] + ": " + settingsObject.Operation) - } - } - const settingsSubgraphObject = settingsObject.getSubgraphObject(); - if (settingsSubgraphObject && settingsSubgraphObject.Graph) { - return settingsSubgraphObject.Graph.getName() - } - } - let memberName = this.FunctionReference?.MemberName; - if (memberName) { - const memberParent = this.FunctionReference.MemberParent?.path ?? ""; - switch (memberName) { - case "AddKey": - let result = memberParent.match(ObjectEntity.sequencerScriptingNameRegex); - if (result) { - return `Add Key (${Utility.formatStringName(result[1])})` - } - case "Concat_StrStr": - return "Append" - } - const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern); - if (memberNameTraceLineMatch) { - return "Line Trace" - + (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ") - + (memberNameTraceLineMatch[2] === "" - ? "By Channel" - : Utility.formatStringName(memberNameTraceLineMatch[2]) - ) - } - switch (memberParent) { - case Configuration.paths.blueprintGameplayTagLibrary: - case Configuration.paths.kismetMathLibrary: - case Configuration.paths.slateBlueprintLibrary: - case Configuration.paths.timeManagementBlueprintLibrary: - const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/); - if (leadingLetter) { - // Some functions start with B or F (Like FCeil, FMax, BMin) - memberName = leadingLetter[1]; - } - switch (memberName) { - case "Abs": return "ABS" - case "BooleanAND": return "AND" - case "BooleanNAND": return "NAND" - case "BooleanOR": return "OR" - case "Exp": return "e" - case "LineTraceSingle": return "Line Trace By Channel" - case "Max": return "MAX" - case "MaxInt64": return "MAX" - case "Min": return "MIN" - case "MinInt64": return "MIN" - case "Not_PreBool": return "NOT" - case "Sin": return "SIN" - case "Sqrt": return "SQRT" - case "Square": return "^2" - // Dot products not respecting MemberName pattern - case "CrossProduct2D": return "cross" - case "Vector4_CrossProduct3": return "cross3" - case "DotProduct2D": - case "Vector4_DotProduct": - return "dot" - case "Vector4_DotProduct3": return "dot3" - } - if (memberName.startsWith("Add_")) { - return "+" - } - if (memberName.startsWith("And_")) { - return "&" - } - if (memberName.startsWith("Conv_")) { - return "" // Conversion nodes do not have visible names - } - if (memberName.startsWith("Cross_")) { - return "cross" - } - if (memberName.startsWith("Divide_")) { - return String.fromCharCode(0x00f7) - } - if (memberName.startsWith("Dot_")) { - return "dot" - } - if (memberName.startsWith("EqualEqual_")) { - return "==" - } - if (memberName.startsWith("Greater_")) { - return ">" - } - if (memberName.startsWith("GreaterEqual_")) { - return ">=" - } - if (memberName.startsWith("Less_")) { - return "<" - } - if (memberName.startsWith("LessEqual_")) { - return "<=" - } - if (memberName.startsWith("Multiply_")) { - return String.fromCharCode(0x2a2f) - } - if (memberName.startsWith("Not_")) { - return "~" - } - if (memberName.startsWith("NotEqual_")) { - return "!=" - } - if (memberName.startsWith("Or_")) { - return "|" - } - if (memberName.startsWith("Percent_")) { - return "%" - } - if (memberName.startsWith("Subtract_")) { - return "-" - } - if (memberName.startsWith("Xor_")) { - return "^" - } - break - case Configuration.paths.blueprintSetLibrary: - { - const setOperationMatch = memberName.match(/Set_(\w+)/); - if (setOperationMatch) { - return Utility.formatStringName(setOperationMatch[1]).toUpperCase() - } - } - break - case Configuration.paths.blueprintMapLibrary: - { - const setOperationMatch = memberName.match(/Map_(\w+)/); - if (setOperationMatch) { - return Utility.formatStringName(setOperationMatch[1]).toUpperCase() - } - } - break - case Configuration.paths.kismetArrayLibrary: - { - const arrayOperationMath = memberName.match(/Array_(\w+)/); - if (arrayOperationMath) { - return arrayOperationMath[1].toUpperCase() - } - } - break - } - return Utility.formatStringName(memberName) - } - if (this.ObjectRef) { - return this.ObjectRef.getName() - } - return Utility.formatStringName(this.getNameAndCounter()[0]) + static attributes = { + ...super.attributes, + PinCategory: AttributeInfo.createValue(""), + PinSubCategory: AttributeInfo.createValue(""), + PinSubCategoryObject: new AttributeInfo({ + type: ObjectReferenceEntity, + default: () => ObjectReferenceEntity.createNoneInstance(), + }), + PinSubCategoryMemberReference: new AttributeInfo({ + type: FunctionReferenceEntity, + default: null, + }), + PinValueType: new AttributeInfo({ + type: PinTypeEntity, + default: null, + }), + ContainerType: AttributeInfo.createType(PathSymbolEntity), + bIsReference: AttributeInfo.createValue(false), + bIsConst: AttributeInfo.createValue(false), + bIsWeakPointer: AttributeInfo.createValue(false), + bIsUObjectWrapper: AttributeInfo.createValue(false), + bSerializeAsSinglePrecisionFloat: AttributeInfo.createValue(false), } + static grammar = this.createGrammar() - nodeColor() { - switch (this.getType()) { - case Configuration.paths.materialExpressionConstant2Vector: - case Configuration.paths.materialExpressionConstant3Vector: - case Configuration.paths.materialExpressionConstant4Vector: - return Configuration.nodeColors.yellow - case Configuration.paths.makeStruct: - return Configuration.nodeColors.darkBlue - case Configuration.paths.materialExpressionMaterialFunctionCall: - return Configuration.nodeColors.blue - case Configuration.paths.materialExpressionFunctionInput: - return Configuration.nodeColors.red - case Configuration.paths.materialExpressionTextureSample: - return Configuration.nodeColors.darkTurquoise - case Configuration.paths.materialExpressionTextureCoordinate: - return Configuration.nodeColors.red - case Configuration.paths.pcgEditorGraphNodeInput: - case Configuration.paths.pcgEditorGraphNodeOutput: - return Configuration.nodeColors.red - } - switch (this.getClass()) { - case Configuration.paths.callFunction: - return this.bIsPureFunc - ? Configuration.nodeColors.green - : Configuration.nodeColors.blue - case Configuration.paths.dynamicCast: - return Configuration.nodeColors.turquoise - case Configuration.paths.inputDebugKey: - case Configuration.paths.inputKey: - return Configuration.nodeColors.red - case Configuration.paths.createDelegate: - case Configuration.paths.enumLiteral: - case Configuration.paths.makeArray: - case Configuration.paths.makeMap: - case Configuration.paths.materialGraphNode: - case Configuration.paths.select: - return Configuration.nodeColors.green - case Configuration.paths.executionSequence: - case Configuration.paths.ifThenElse: - case Configuration.paths.macro: - case Configuration.paths.multiGate: - return Configuration.nodeColors.gray - case Configuration.paths.functionEntry: - case Configuration.paths.functionResult: - return Configuration.nodeColors.violet - case Configuration.paths.timeline: - return Configuration.nodeColors.yellow - } - if (this.switchTarget()) { - return Configuration.nodeColors.lime - } - if (this.isEvent()) { - return Configuration.nodeColors.red - } - if (this.isComment()) { - return (this.CommentColor ? this.CommentColor : LinearColorEntity.getWhite()) - .toDimmedColor() - .toCSSRGBValues() - } - const pcgSubobject = this.getPcgSubobject(); - if (pcgSubobject && pcgSubobject.NodeTitleColor) { - return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues() - } - if (this.bIsPureFunc) { - return Configuration.nodeColors.green - } - return Configuration.nodeColors.blue + static createGrammar() { + return Grammar.createEntityGrammar(this) } - nodeIcon() { - if (this.isMaterial() || this.isPcg()) { - return null - } - switch (this.getType()) { - case Configuration.paths.addDelegate: - case Configuration.paths.asyncAction: - case Configuration.paths.callDelegate: - case Configuration.paths.createDelegate: - case Configuration.paths.functionEntry: - case Configuration.paths.functionResult: - return SVGIcon.node - case Configuration.paths.customEvent: return SVGIcon.event - case Configuration.paths.doN: return SVGIcon.doN - case Configuration.paths.doOnce: return SVGIcon.doOnce - case Configuration.paths.dynamicCast: return SVGIcon.cast - case Configuration.paths.enumLiteral: return SVGIcon.enum - case Configuration.paths.event: return SVGIcon.event - case Configuration.paths.executionSequence: - case Configuration.paths.multiGate: - return SVGIcon.sequence - case Configuration.paths.flipflop: - return SVGIcon.flipflop - case Configuration.paths.forEachElementInEnum: - case Configuration.paths.forLoop: - case Configuration.paths.forLoopWithBreak: - case Configuration.paths.whileLoop: - return SVGIcon.loop - case Configuration.paths.forEachLoop: - case Configuration.paths.forEachLoopWithBreak: - return SVGIcon.forEachLoop - case Configuration.paths.ifThenElse: return SVGIcon.branchNode - case Configuration.paths.isValid: return SVGIcon.questionMark - case Configuration.paths.makeArray: return SVGIcon.makeArray - case Configuration.paths.makeMap: return SVGIcon.makeMap - case Configuration.paths.makeSet: return SVGIcon.makeSet - case Configuration.paths.makeStruct: return SVGIcon.makeStruct - case Configuration.paths.select: return SVGIcon.select - case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor - case Configuration.paths.timeline: return SVGIcon.timer - } - if (this.switchTarget()) { - return SVGIcon.switch - } - if (this.nodeDisplayName().startsWith("Break")) { - return SVGIcon.breakStruct - } - if (this.getClass() === Configuration.paths.macro) { - return SVGIcon.macro - } - const hidValue = this.getHIDAttribute()?.toString(); - if (hidValue) { - if (hidValue.includes("Mouse")) { - return SVGIcon.mouse - } else if (hidValue.includes("Gamepad_Special")) { - return SVGIcon.keyboard // This is called Touchpad in UE - } else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) { - return SVGIcon.gamepad - } else if (hidValue.includes("Touch")) { - return SVGIcon.touchpad - } else { - return SVGIcon.keyboard - } - } - if (this.getDelegatePin()) { - return SVGIcon.event - } - if (this.ObjectRef?.type === Configuration.paths.ambientSound) { - return SVGIcon.sound - } - return SVGIcon.functionSymbol + constructor(values = {}, suppressWarns = false) { + super(values, suppressWarns); + /** @type {String} */ this.PinCategory; + /** @type {String} */ this.PinSubCategory; + /** @type {ObjectReferenceEntity} */ this.PinSubCategoryObject; + /** @type {FunctionReferenceEntity} */ this.PinSubCategoryMemberReference; + /** @type {PinTypeEntity} */ this.PinValueType; + /** @type {PathSymbolEntity} */ this.ContainerType; + /** @type {Boolean} */ this.bIsReference; + /** @type {Boolean} */ this.bIsConst; + /** @type {Boolean} */ this.bIsWeakPointer; + /** @type {Boolean} */ this.bIsUObjectWrapper; + /** @type {Boolean} */ this.bIsUObjectWrapper; + /** @type {Boolean} */ this.bSerializeAsSinglePrecisionFloat; } - additionalPinInserter() { - /** @type {() => PinEntity[]} */ - let pinEntities; - /** @type {(pinEntity: PinEntity) => Number} */ - let pinIndexFromEntity; - /** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number) => String} */ - let pinNameFromIndex; - switch (this.getType()) { - case Configuration.paths.commutativeAssociativeBinaryOperator: - case Configuration.paths.promotableOperator: - switch (this.FunctionReference?.MemberName) { - default: - if ( - !this.FunctionReference?.MemberName?.startsWith("Add_") - && !this.FunctionReference?.MemberName?.startsWith("Subtract_") - && !this.FunctionReference?.MemberName?.startsWith("Multiply_") - && !this.FunctionReference?.MemberName?.startsWith("Divide_") - ) { - break - } - case "And_Int64Int64": - case "And_IntInt": - case "BMax": - case "BMin": - case "BooleanAND": - case "BooleanNAND": - case "BooleanOR": - case "Concat_StrStr": - case "FMax": - case "FMin": - case "Max": - case "MaxInt64": - case "Min": - case "MinInt64": - case "Or_Int64Int64": - case "Or_IntInt": - - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isInput()); - pinIndexFromEntity ??= pinEntity => - pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0); - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1); - this.NumAdditionalInputs = pinEntities().length - 1; - return result - }; - break - } - break - case Configuration.paths.multiGate: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()); - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]); - pinNameFromIndex ??= (index, min = -1, max = -1) => - `Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`; - break - case Configuration.paths.switchInteger: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()); - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1]); - pinNameFromIndex ??= (index, min = -1, max = -1) => (index < 0 ? max + 1 : index).toString(); - break - case Configuration.paths.switchGameplayTag: - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`; - this.PinNames ??= []; - this.PinNames.push(result); - delete this.PinTags[this.PinTags.length - 1]; - this.PinTags[this.PinTags.length] = null; - return result - }; - case Configuration.paths.switchName: - case Configuration.paths.switchString: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()); - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]); - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`; - this.PinNames ??= []; - this.PinNames.push(result); - return result - }; - break - } - if (pinEntities) { - return () => { - let min = Number.MAX_SAFE_INTEGER; - let max = Number.MIN_SAFE_INTEGER; - let values = []; - const modelPin = pinEntities().reduce( - (acc, cur) => { - const value = pinIndexFromEntity(cur); - if (!isNaN(value)) { - values.push(value); - min = Math.min(value, min); - if (value > max) { - max = value; - return cur - } - } else if (acc === undefined) { - return cur - } - return acc - }, - undefined - ); - if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) { - min = undefined; - max = undefined; - } - if (!modelPin) { - return null - } - values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1); - let prev = values[0]; - let index = values.findIndex( - // Search for a gap - value => { - const result = value - prev > 1; - prev = value; - return result - } - ); - const newPin = new PinEntity(modelPin); - newPin.PinId = GuidEntity.generateGuid(); - newPin.PinName = pinNameFromIndex(index, min, max); - newPin.PinToolTip = undefined; - this.getCustomproperties(true).push(newPin); - return newPin - } - } + /** @param {PinTypeEntity} other */ + copyTypeFrom(other) { + this.PinCategory = other.PinCategory; + this.PinSubCategory = other.PinSubCategory; + this.PinSubCategoryObject = other.PinSubCategoryObject; + this.PinSubCategoryMemberReference = other.PinSubCategoryMemberReference; + this.PinValueType = other.PinValueType; + this.ContainerType = other.ContainerType; + this.bIsReference = other.bIsReference; + this.bIsConst = other.bIsConst; + this.bIsWeakPointer = other.bIsWeakPointer; + this.bIsUObjectWrapper = other.bIsUObjectWrapper; + this.bSerializeAsSinglePrecisionFloat = other.bSerializeAsSinglePrecisionFloat; } } -/** @template {AttributeConstructor} T */ -class Serializer { - - /** @type {(v: String) => String} */ - static same = v => v +class Vector2DEntity extends IEntity { - /** @type {(entity: Attribute, serialized: String) => String} */ - static notWrapped = (entity, serialized) => serialized + static attributes = { + ...super.attributes, + X: new AttributeInfo({ + default: 0, + expected: true, + }), + Y: new AttributeInfo({ + default: 0, + expected: true, + }), + } + static grammar = this.createGrammar() - /** @type {(entity: Attribute, serialized: String) => String} */ - static bracketsWrapped = (entity, serialized) => `(${serialized})` + static createGrammar() { + return Grammar.createEntityGrammar(this, false) + } - /** @param {T} entityType */ - constructor( - entityType, - /** @type {(entity: ConstructedType, serialized: String) => String} */ - wrap = (entity, serialized) => serialized, - attributeSeparator = ",", - trailingSeparator = false, - attributeValueConjunctionSign = "=", - attributeKeyPrinter = Serializer.same - ) { - this.entityType = entityType; - this.wrap = wrap; - this.attributeSeparator = attributeSeparator; - this.trailingSeparator = trailingSeparator; - this.attributeValueConjunctionSign = attributeValueConjunctionSign; - this.attributeKeyPrinter = attributeKeyPrinter; + constructor(values) { + super(values); + /** @type {Number} */ this.X; + /** @type {Number} */ this.Y; } - /** - * @param {String} value - * @returns {ConstructedType} - */ - read(value) { - return this.doRead(value.trim()) + /** @returns {[Number, Number]} */ + toArray() { + return [this.X, this.Y] } +} - /** @param {ConstructedType} value */ - write(value, insideString = false) { - return this.doWrite(value, insideString) +class RBSerializationVector2DEntity extends Vector2DEntity { + + static grammar = this.createGrammar() + + static createGrammar() { + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + /X\s*=\s*/.source + "(?" + Parsernostrum.number.getParser().parser.regexp.source + ")" + + "\\s+" + + /Y\s*=\s*/.source + "(?" + Parsernostrum.number.getParser().parser.regexp.source + ")" + )).map(({ groups: { x, y } }) => new this({ + X: Number(x), + Y: Number(y), + })), + Vector2DEntity.grammar + ) } +} - /** - * @param {String} value - * @returns {ConstructedType} - */ - doRead(value) { - let grammar = Grammar.grammarFor(undefined, this.entityType); - const parseResult = grammar.run(value); - if (!parseResult.status) { - throw new Error( - this.entityType - ? `Error when trying to parse the entity ${this.entityType.prototype.constructor.name}` - : "Error when trying to parse null" - ) - } - return parseResult.value +class RotatorEntity extends IEntity { + + static attributes = { + ...super.attributes, + R: new AttributeInfo({ + default: 0, + expected: true, + }), + P: new AttributeInfo({ + default: 0, + expected: true, + }), + Y: new AttributeInfo({ + default: 0, + expected: true, + }), } + static grammar = this.createGrammar() - /** - * @param {ConstructedType} entity - * @param {Boolean} insideString - * @returns {String} - */ - doWrite( - entity, - insideString = false, - indentation = "", - wrap = this.wrap, - attributeSeparator = this.attributeSeparator, - trailingSeparator = this.trailingSeparator, - attributeValueConjunctionSign = this.attributeValueConjunctionSign, - attributeKeyPrinter = this.attributeKeyPrinter - ) { - let result = ""; - const keys = Object.keys(entity); - let first = true; - for (const key of keys) { - const value = entity[key]; - if (value !== undefined && this.showProperty(entity, key)) { - let keyValue = entity instanceof Array ? `(${key})` : key; - if (AttributeInfo.getAttribute(entity, key, "quoted")) { - keyValue = `"${keyValue}"`; - } - const isSerialized = AttributeInfo.getAttribute(entity, key, "serialized"); - if (first) { - first = false; - } else { - result += attributeSeparator; - } - if (AttributeInfo.getAttribute(entity, key, "inlined")) { - result += this.doWrite( - value, - insideString, - indentation, - Serializer.notWrapped, - attributeSeparator, - false, - attributeValueConjunctionSign, - AttributeInfo.getAttribute(entity, key, "type") instanceof Array - ? k => attributeKeyPrinter(`${keyValue}${k}`) - : k => attributeKeyPrinter(`${keyValue}.${k}`) - ); - continue - } - const keyPrinted = attributeKeyPrinter(keyValue); - const indentationPrinted = attributeSeparator.includes("\n") ? indentation : ""; - result += ( - keyPrinted.length - ? (indentationPrinted + keyPrinted + this.attributeValueConjunctionSign) - : "" - ) - + ( - isSerialized - ? `"${this.doWriteValue(value, true, indentation)}"` - : this.doWriteValue(value, insideString, indentation) - ); - } - } - if (trailingSeparator && result.length) { - // append separator at the end if asked and there was printed content - result += attributeSeparator; - } - return wrap(entity, result) + static createGrammar() { + return Grammar.createEntityGrammar(this, false) } - /** @param {Boolean} insideString */ - doWriteValue(value, insideString, indentation = "") { - const type = Utility.getType(value); - 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.doWrite(value, insideString, indentation) + constructor(values) { + super(values); + /** @type {Number} */ this.R; + /** @type {Number} */ this.P; + /** @type {Number} */ this.Y; } - /** - * @param {IEntity} entity - * @param {String} key - */ - showProperty(entity, key) { - if (entity instanceof IEntity) { - if ( - AttributeInfo.getAttribute(entity, key, "ignored") - || AttributeInfo.getAttribute(entity, key, "silent") && Utility.equals( - AttributeInfo.getAttribute(entity, key, "default"), - entity[key] - ) - ) { - return false - } - } - return true + getRoll() { + return this.R + } + + getPitch() { + return this.P + } + + getYaw() { + return this.Y } } -/** @extends Serializer */ -class ObjectSerializer extends Serializer { - - constructor(entityType = ObjectEntity) { - super(entityType, undefined, "\n", true, undefined, Serializer.same); - } - - showProperty(entity, key) { - switch (key) { - case "Class": - case "Name": - case "Archetype": - case "ExportPath": - case "CustomProperties": - // Serielized separately, check doWrite() - return false - } - return super.showProperty(entity, key) - } - - /** @param {ObjectEntity} value */ - write(value, insideString = false) { - return this.doWrite(value, insideString) + "\n" - } - - /** @param {String} value */ - doRead(value) { - return Grammar.grammarFor(undefined, this.entityType).parse(value) - } - - /** - * @param {String} value - * @returns {ObjectEntity[]} - */ - readMultiple(value) { - return ObjectEntity.getMultipleObjectsGrammar().parse(value) - } - - /** - * @param {ObjectEntity} entity - * @param {Boolean} insideString - * @returns {String} - */ - doWrite( - entity, - insideString, - indentation = "", - wrap = this.wrap, - attributeSeparator = this.attributeSeparator, - trailingSeparator = this.trailingSeparator, - attributeValueConjunctionSign = this.attributeValueConjunctionSign, - attributeKeyPrinter = this.attributeKeyPrinter, - ) { - const moreIndentation = indentation + Configuration.indentation; - if (!(entity instanceof ObjectEntity)) { - return super.doWrite( - entity, - insideString, - indentation, - wrap, - attributeSeparator, - trailingSeparator, - attributeValueConjunctionSign, - // @ts-expect-error - key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key) - ) - } - let result = indentation + "Begin Object" - + (entity.Class?.type || entity.Class?.path ? ` Class=${this.doWriteValue(entity.Class, insideString)}` : "") - + (entity.Name ? ` Name=${this.doWriteValue(entity.Name, insideString)}` : "") - + (entity.Archetype ? ` Archetype=${this.doWriteValue(entity.Archetype, insideString)}` : "") - + (entity.ExportPath?.type || entity.ExportPath?.path ? ` ExportPath=${this.doWriteValue(entity.ExportPath, insideString)}` : "") - + "\n" - + super.doWrite( - entity, - insideString, - moreIndentation, - wrap, - attributeSeparator, - true, - attributeValueConjunctionSign, - key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key) - ) - + (!AttributeInfo.getAttribute(entity, "CustomProperties", "ignored") - ? entity.getCustomproperties().map(pin => - moreIndentation - + attributeKeyPrinter("CustomProperties ") - + SerializerFactory.getSerializer(PinEntity).doWrite(pin, insideString) - + this.attributeSeparator - ).join("") - : "" - ) - + indentation + "End Object"; - return result - } -} +class SimpleSerializationRotatorEntity extends RotatorEntity { -/** - * @typedef {import("../IInput.js").Options & { - * listenOnFocus?: Boolean, - * unlistenOnTextEdit?: Boolean, - * }} Options - */ - -class Copy extends IInput { - - static #serializer = new ObjectSerializer() - - /** @type {(e: ClipboardEvent) => void} */ - #copyHandler - - constructor(target, blueprint, options = {}) { - options.listenOnFocus ??= true; - options.unlistenOnTextEdit ??= true; // No nodes copy if inside a text field, just text (default behavior) - super(target, blueprint, options); - let self = this; - this.#copyHandler = () => self.copied(); - } - - listenEvents() { - window.addEventListener("copy", this.#copyHandler); - } - - unlistenEvents() { - window.removeEventListener("copy", this.#copyHandler); - } - - getSerializedText() { - return this.blueprint - .getNodes(true) - .map(node => Copy.#serializer.write(node.entity, false)) - .join("") - } - - copied() { - const value = this.getSerializedText(); - navigator.clipboard.writeText(value); - return value - } -} + static grammar = this.createGrammar() -/** - * @typedef {import("../IInput.js").Options & { - * listenOnFocus?: Boolean, - * unlistenOnTextEdit?: Boolean, - * }} Options - */ - -class Cut extends IInput { - - static #serializer = new ObjectSerializer() - - /** @type {(e: ClipboardEvent) => void} */ - #cutHandler - - /** - * @param {Element} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.listenOnFocus ??= true; - options.unlistenOnTextEdit ??= true; // No nodes copy if inside a text field, just text (default behavior) - super(target, blueprint, options); - let self = this; - this.#cutHandler = () => self.cut(); - } - - listenEvents() { - window.addEventListener("cut", this.#cutHandler); - } - - unlistenEvents() { - window.removeEventListener("cut", this.#cutHandler); - } - - getSerializedText() { - return this.blueprint - .getNodes(true) - .map(node => Cut.#serializer.write(node.entity, false)) - .join("") - } - - cut() { - this.blueprint.template.getCopyInputObject().copied(); - this.blueprint.removeGraphElement(...this.blueprint.getNodes(true)); - } + static createGrammar() { + const number = Parsernostrum.number.getParser().parser.regexp.source; + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + )).map(([_, p, y, r]) => new this({ + R: Number(r), + P: Number(p), + Y: Number(y), + })), + RotatorEntity.grammar + ) + } } -class ElementFactory { - - /** @type {Map>} */ - static #elementConstructors = new Map() +class SimpleSerializationVector2DEntity extends Vector2DEntity { - /** - * @param {String} tagName - * @param {AnyConstructor} entityConstructor - */ - static registerElement(tagName, entityConstructor) { - ElementFactory.#elementConstructors.set(tagName, entityConstructor); - } + static grammar = this.createGrammar() - /** @param {String} tagName */ - static getConstructor(tagName) { - return ElementFactory.#elementConstructors.get(tagName) + static createGrammar() { + const number = Parsernostrum.number.getParser().parser.regexp.source; + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + )).map(([_, x, y]) => new this({ + X: Number(x), + Y: Number(y), + })), + Vector2DEntity.grammar + ) } } -/** - * @typedef {import("../IInput.js").Options & { - * listenOnFocus?: Boolean, - * unlistenOnTextEdit?: Boolean, - * }} Options - */ - -class Paste extends IInput { - - static #serializer = new ObjectSerializer() - - /** @type {(e: ClipboardEvent) => void} */ - #pasteHandle - - /** - * @param {Element} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.listenOnFocus ??= true; - options.unlistenOnTextEdit ??= true; // No nodes paste if inside a text field, just text (default behavior) - super(target, blueprint, options); - let self = this; - this.#pasteHandle = e => self.pasted(e.clipboardData.getData("Text")); - } - - listenEvents() { - window.addEventListener("paste", this.#pasteHandle); - } - - unlistenEvents() { - window.removeEventListener("paste", this.#pasteHandle); - } - - /** @param {String} value */ - pasted(value) { - let top = 0; - let left = 0; - let count = 0; - let nodes = Paste.#serializer.readMultiple(value).map(entity => { - let node = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) - .newObject(entity); - top += node.locationY; - left += node.locationX; - ++count; - return node - }); - top /= count; - left /= count; - if (nodes.length > 0) { - this.blueprint.unselectAll(); - } - let mousePosition = this.blueprint.mousePosition; - nodes.forEach(node => { - node.addLocation(mousePosition[0] - left, mousePosition[1] - top); - node.snapToGrid(); - node.setSelected(true); - }); - this.blueprint.addGraphElement(...nodes); - return true - } -} - -class KeyBindingEntity extends IEntity { +class Vector4DEntity extends IEntity { static attributes = { ...super.attributes, - ActionName: AttributeInfo.createValue(""), - bShift: AttributeInfo.createValue(false), - bCtrl: AttributeInfo.createValue(false), - bAlt: AttributeInfo.createValue(false), - bCmd: AttributeInfo.createValue(false), - Key: AttributeInfo.createType(IdentifierEntity), + X: new AttributeInfo({ + default: 0, + expected: true, + }), + Y: new AttributeInfo({ + default: 0, + expected: true, + }), + Z: new AttributeInfo({ + default: 0, + expected: true, + }), + W: new AttributeInfo({ + default: 0, + expected: true, + }), } static grammar = this.createGrammar() static createGrammar() { - return Parsernostrum.alt( - IdentifierEntity.grammar.map(identifier => new this({ - Key: identifier - })), - Grammar.createEntityGrammar(this) - ) + return Grammar.createEntityGrammar(Vector4DEntity, false) } - constructor(values = {}) { - super(values, true); - /** @type {String} */ this.ActionName; - /** @type {Boolean} */ this.bShift; - /** @type {Boolean} */ this.bCtrl; - /** @type {Boolean} */ this.bAlt; - /** @type {Boolean} */ this.bCmd; - /** @type {IdentifierEntity} */ this.Key; - } -} - -/** - * @typedef {import("../IInput.js").Options & { - * activationKeys?: String | KeyBindingEntity | (String | KeyBindingEntity)[], - * consumeEvent?: Boolean, - * listenOnFocus?: Boolean, - * unlistenOnTextEdit?: Boolean, - * }} Options - */ - -/** - * @template {Element} T - * @extends IInput - */ -class KeyboardShortcut extends IInput { - - static #ignoreEvent = - /** @param {KeyboardShortcut} self */ - self => { } - - /** @type {KeyBindingEntity[]} */ - #activationKeys - - pressedKey = "" - - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor( - target, - blueprint, - options = {}, - onKeyDown = KeyboardShortcut.#ignoreEvent, - onKeyUp = KeyboardShortcut.#ignoreEvent - ) { - options.activationKeys ??= []; - options.consumeEvent ??= true; - options.listenOnFocus ??= true; - options.unlistenOnTextEdit ??= true; // No shortcuts when inside of a text field - if (!(options.activationKeys instanceof Array)) { - options.activationKeys = [options.activationKeys]; - } - options.activationKeys = options.activationKeys.map(v => { - if (v instanceof KeyBindingEntity) { - return v - } - if (v.constructor === String) { - const parsed = KeyBindingEntity.grammar.run(v); - if (parsed.status) { - return parsed.value - } - } - throw new Error("Unexpected key value") - }); - - super(target, blueprint, options); - this.onKeyDown = onKeyDown; - this.onKeyUp = onKeyUp; - - 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 ( - self.#activationKeys.some(keyEntry => - wantsShift(keyEntry) == e.shiftKey - && wantsCtrl(keyEntry) == e.ctrlKey - && wantsAlt(keyEntry) == e.altKey - && Configuration.Keys[keyEntry.Key.value] == e.code - ) - ) { - if (this.consumeEvent) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - this.pressedKey = e.code; - self.fire(); - document.removeEventListener("keydown", self.keyDownHandler); - document.addEventListener("keyup", self.keyUpHandler); - } - }; - - /** @param {KeyboardEvent} e */ - this.keyUpHandler = e => { - if ( - self.#activationKeys.some(keyEntry => - keyEntry.bShift && e.key == "Shift" - || keyEntry.bCtrl && e.key == "Control" - || keyEntry.bAlt && e.key == "Alt" - || keyEntry.bCmd && e.key == "Meta" - || Configuration.Keys[keyEntry.Key.value] == e.code - ) - ) { - if (this.consumeEvent) { - e.stopImmediatePropagation(); - } - self.unfire(); - this.pressedKey = ""; - document.removeEventListener("keyup", this.keyUpHandler); - document.addEventListener("keydown", this.keyDownHandler); - } - }; - } - - listenEvents() { - document.addEventListener("keydown", this.keyDownHandler); + constructor(values) { + super(values); + /** @type {Number} */ this.X; + /** @type {Number} */ this.Y; + /** @type {Number} */ this.Z; + /** @type {Number} */ this.W; } - unlistenEvents() { - document.removeEventListener("keydown", this.keyDownHandler); + /** @returns {[Number, Number, Number, Number]} */ + toArray() { + return [this.X, this.Y, this.Z, this.W] } +} - /* Subclasses can override */ +class SimpleSerializationVector4DEntity extends Vector4DEntity { - fire() { - this.onKeyDown(this); - } + static grammar = this.createGrammar() - unfire() { - this.onKeyUp(this); + static createGrammar() { + const number = Parsernostrum.number.getParser().parser.regexp.source; + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + )) + .map(([_0, x, y, z, w]) => new this({ + X: Number(x), + Y: Number(y), + Z: Number(z), + W: Number(w), + })), + Vector4DEntity.grammar + ) } } -/** - * @typedef {import("../IInput.js").Options & { - * ignoreTranslateCompensate?: Boolean, - * ignoreScale?: Boolean, - * movementSpace?: HTMLElement, - * enablerKey?: KeyboardShortcut, - * }} Options - */ - -/** - * @template {Element} T - * @extends {IInput} - */ -class IPointing extends IInput { +class VectorEntity extends IEntity { - #location = /** @type {Coordinates} */([0, 0]) - get location() { - return this.#location + static attributes = { + ...super.attributes, + X: new AttributeInfo({ + default: 0, + expected: true, + }), + Y: new AttributeInfo({ + default: 0, + expected: true, + }), + Z: new AttributeInfo({ + default: 0, + expected: true, + }), } + static grammar = this.createGrammar() - /** @type {KeyboardShortcut?} */ - #enablerKey - get enablerKey() { - return this.#enablerKey - } - #enablerActivated = true - get enablerActivated() { - return this.#enablerActivated + static createGrammar() { + return Grammar.createEntityGrammar(VectorEntity, false) } - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - 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; - if (options.enablerKey) { - this.#enablerKey = options.enablerKey; - this.#enablerKey.onKeyDown = () => this.#enablerActivated = true; - this.#enablerKey.onKeyUp = () => this.#enablerActivated = false; - this.#enablerKey.consumeEvent = false; - this.#enablerKey.listenEvents(); - this.#enablerActivated = false; - } + constructor(values) { + super(values); + /** @type {Number} */ this.X; + /** @type {Number} */ this.Y; + /** @type {Number} */ this.Z; } - /** @param {MouseEvent} mouseEvent */ - setLocationFromEvent(mouseEvent) { - let location = Utility.convertLocation( - [mouseEvent.clientX, mouseEvent.clientY], - this.movementSpace, - this.options.ignoreScale - ); - location = this.options.ignoreTranslateCompensate - ? location - : this.blueprint.compensateTranslation(location[0], location[1]); - this.#location = [...location]; - return this.#location + /** @returns {[Number, Number, Number]} */ + toArray() { + return [this.X, this.Y, this.Z] } } -/** - * @typedef {import("./IPointing.js").Options & { - * listenOnFocus?: Boolean, - * strictTarget?: Boolean, - * }} Options - */ - -class MouseWheel extends IPointing { - - /** @param {MouseWheel} self */ - static #ignoreEvent = self => { } +class SimpleSerializationVectorEntity extends VectorEntity { - #variation = 0 - get variation() { - return this.#variation - } + static grammar = this.createGrammar() - /** @param {WheelEvent} e */ - #mouseWheelHandler = e => { - if (this.enablerKey && !this.enablerActivated) { - return - } - e.preventDefault(); - this.#variation = e.deltaY; - this.setLocationFromEvent(e); - this.wheel(); - } - - /** @param {WheelEvent} e */ - #mouseParentWheelHandler = e => e.preventDefault() - - /** - * @param {HTMLElement} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor( - target, - blueprint, - options = {}, - onWheel = MouseWheel.#ignoreEvent, - ) { - options.listenOnFocus = true; - options.strictTarget ??= false; - super(target, blueprint, options); - this.strictTarget = options.strictTarget; - this.onWheel = onWheel; - } - - 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 can override */ - wheel() { - this.onWheel(this); + static createGrammar() { + const number = Parsernostrum.number.getParser().parser.regexp.source; + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + )) + .map(([_0, x, y, z]) => new this({ + X: Number(x), + Y: Number(y), + Z: Number(z), + })), + VectorEntity.grammar + ) } } -class Zoom extends MouseWheel { +/** @template {TerminalAttribute} T */ +class PinEntity extends IEntity { - #accumulatedVariation = 0 + static #typeEntityMap = { + [Configuration.paths.linearColor]: LinearColorEntity, + [Configuration.paths.rotator]: RotatorEntity, + [Configuration.paths.vector]: VectorEntity, + [Configuration.paths.vector2D]: Vector2DEntity, + [Configuration.paths.vector4f]: Vector4DEntity, + "bool": Boolean, + "byte": ByteEntity, + "enum": EnumEntity, + "exec": String, + "int": IntegerEntity, + "int64": Integer64Entity, + "name": String, + "real": Number, + "string": String, + } + static #alternativeTypeEntityMap = { + "enum": EnumDisplayValueEntity, + "rg": RBSerializationVector2DEntity, + [Configuration.paths.rotator]: SimpleSerializationRotatorEntity, + [Configuration.paths.vector]: SimpleSerializationVectorEntity, + [Configuration.paths.vector2D]: SimpleSerializationVector2DEntity, + [Configuration.paths.vector3f]: SimpleSerializationVectorEntity, + [Configuration.paths.vector4f]: SimpleSerializationVector4DEntity, + } + static attributes = { + ...super.attributes, + lookbehind: new AttributeInfo({ + default: "Pin", + ignored: true, + }), + objectEntity: new AttributeInfo({ + ignored: true, + }), + pinIndex: new AttributeInfo({ + type: Number, + ignored: true, + }), + PinId: new AttributeInfo({ + type: GuidEntity, + default: () => new GuidEntity() + }), + PinName: AttributeInfo.createValue(""), + PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, InvariantTextEntity, String)), + PinToolTip: AttributeInfo.createType(String), + Direction: AttributeInfo.createType(String), + PinType: new AttributeInfo({ + type: PinTypeEntity, + default: () => new PinTypeEntity(), + inlined: true, + }), + LinkedTo: AttributeInfo.createType([PinReferenceEntity]), + SubPins: AttributeInfo.createType([PinReferenceEntity]), + ParentPin: AttributeInfo.createType(PinReferenceEntity), + DefaultValue: new AttributeInfo({ + type: new ComputedType( + /** @param {PinEntity} pinEntity */ + pinEntity => pinEntity.getEntityType(true) ?? String + ), + serialized: true, + }), + AutogeneratedDefaultValue: AttributeInfo.createType(String), + DefaultObject: AttributeInfo.createType(ObjectReferenceEntity), + PersistentGuid: AttributeInfo.createType(GuidEntity), + bHidden: AttributeInfo.createValue(false), + bNotConnectable: AttributeInfo.createValue(false), + bDefaultValueIsReadOnly: AttributeInfo.createValue(false), + bDefaultValueIsIgnored: AttributeInfo.createValue(false), + bAdvancedView: AttributeInfo.createValue(false), + bOrphanedPin: AttributeInfo.createValue(false), + } + static grammar = this.createGrammar() - #enableZoonIn = false - get enableZoonIn() { - return this.#enableZoonIn + #recomputesNodeTitleOnChange = false + set recomputesNodeTitleOnChange(value) { + this.#recomputesNodeTitleOnChange = value; } - set enableZoonIn(value) { - if (value == this.#enableZoonIn) { - return - } - this.#enableZoonIn = value; + get recomputesNodeTitleOnChange() { + return this.#recomputesNodeTitleOnChange } - wheel() { - this.#accumulatedVariation += -this.variation; - if (Math.abs(this.#accumulatedVariation) < Configuration.mouseWheelZoomThreshold) { - return + static createGrammar() { + return Grammar.createEntityGrammar(this) + } + + constructor(values = {}, suppressWarns = false) { + super(values, suppressWarns); + /** @type {ObjectEntity} */ this.objectEntity; + /** @type {Number} */ this.pinIndex; + /** @type {GuidEntity} */ this.PinId; + /** @type {String} */ this.PinName; + /** @type {LocalizedTextEntity | String} */ this.PinFriendlyName; + /** @type {String} */ this.PinToolTip; + /** @type {String} */ this.Direction; + /** @type {PinTypeEntity} */ this.PinType; + /** @type {PinReferenceEntity[]} */ this.LinkedTo; + /** @type {T} */ this.DefaultValue; + /** @type {String} */ this.AutogeneratedDefaultValue; + /** @type {ObjectReferenceEntity} */ this.DefaultObject; + /** @type {GuidEntity} */ this.PersistentGuid; + /** @type {Boolean} */ this.bHidden; + /** @type {Boolean} */ this.bNotConnectable; + /** @type {Boolean} */ this.bDefaultValueIsReadOnly; + /** @type {Boolean} */ this.bDefaultValueIsIgnored; + /** @type {Boolean} */ this.bAdvancedView; + /** @type {Boolean} */ this.bOrphanedPin; + } + + /** @param {ObjectEntity} objectEntity */ + static fromLegacyObject(objectEntity) { + return new PinEntity(objectEntity, true) + } + + getType() { + const category = this.PinType.PinCategory.toLocaleLowerCase(); + if (category === "struct" || category === "class" || category === "object" || category === "type") { + return this.PinType.PinSubCategoryObject.path } - let zoomLevel = this.blueprint.getZoom(); - if (!this.enableZoonIn && zoomLevel == 0 && this.#accumulatedVariation > 0) { - return + if (this.isEnum()) { + return "enum" } - zoomLevel += Math.sign(this.#accumulatedVariation); - this.blueprint.setZoom(zoomLevel, this.location); - this.#accumulatedVariation = 0; + if (this.objectEntity?.isPcg()) { + const pcgSuboject = this.objectEntity.getPcgSubobject(); + const pinObjectReference = this.isInput() + ? pcgSuboject.InputPins?.[this.pinIndex] + : pcgSuboject.OutputPins?.[this.pinIndex]; + if (pinObjectReference) { + /** @type {ObjectEntity} */ + const pinObject = pcgSuboject[Configuration.subObjectAttributeNameFromReference(pinObjectReference, true)]; + let allowedTypes = pinObject.Properties?.AllowedTypes?.toString() ?? ""; + if (allowedTypes == "") { + allowedTypes = this.PinType.PinCategory ?? ""; + if (allowedTypes == "") { + allowedTypes = "Any"; + } + } + if (allowedTypes) { + if ( + pinObject.Properties.bAllowMultipleData !== false + && pinObject.Properties.bAllowMultipleConnections !== false + ) { + allowedTypes += "[]"; + } + return allowedTypes + } + } + } + if (category === "optional") { + switch (this.PinType.PinSubCategory) { + case "red": + return "real" + case "rg": + return "rg" + case "rgb": + return Configuration.paths.vector + case "rgba": + return Configuration.paths.linearColor + default: + return this.PinType.PinSubCategory + } + } + return category + } + + getEntityType(alternative = false) { + const typeString = this.getType(); + const entity = PinEntity.#typeEntityMap[typeString]; + const alternativeEntity = PinEntity.#alternativeTypeEntityMap[typeString]; + return alternative && alternativeEntity !== undefined + ? alternativeEntity + : entity + } + + pinTitle() { + return pinTitle(this) + } + + /** @param {PinEntity} other */ + copyTypeFrom(other) { + this.PinType.PinCategory = other.PinType.PinCategory; + this.PinType.PinSubCategory = other.PinType.PinSubCategory; + this.PinType.PinSubCategoryObject = other.PinType.PinSubCategoryObject; + this.PinType.PinSubCategoryMemberReference = other.PinType.PinSubCategoryMemberReference; + this.PinType.PinValueType = other.PinType.PinValueType; + this.PinType.ContainerType = other.PinType.ContainerType; + this.PinType.bIsReference = other.PinType.bIsReference; + this.PinType.bIsConst = other.PinType.bIsConst; + this.PinType.bIsWeakPointer = other.PinType.bIsWeakPointer; + this.PinType.bIsUObjectWrapper = other.PinType.bIsUObjectWrapper; + this.PinType.bSerializeAsSinglePrecisionFloat = other.PinType.bSerializeAsSinglePrecisionFloat; + } + + getDefaultValue(maybeCreate = false) { + if (this.DefaultValue === undefined && maybeCreate) { + // @ts-expect-error + this.DefaultValue = new (this.getEntityType(true))(); + } + return this.DefaultValue + } + + isEnum() { + const type = this.PinType.PinSubCategoryObject.type; + return type === Configuration.paths.enum + || type === Configuration.paths.userDefinedEnum + || type.toLowerCase() === "enum" + } + + isExecution() { + return this.PinType.PinCategory === "exec" + } + + isHidden() { + return this.bHidden + } + + isInput() { + return !this.bHidden && this.Direction != "EGPD_Output" + } + + isOutput() { + return !this.bHidden && this.Direction == "EGPD_Output" + } + + isLinked() { + return this.LinkedTo?.length > 0 ?? false + } + + /** + * @param {String} targetObjectName + * @param {PinEntity} targetPinEntity + * @returns true if it was not already linked to the tarket + */ + linkTo(targetObjectName, targetPinEntity) { + const linkFound = this.LinkedTo?.some(pinReferenceEntity => + pinReferenceEntity.objectName.toString() == targetObjectName + && pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf() + ); + if (!linkFound) { + (this.LinkedTo ??= []).push(new PinReferenceEntity({ + objectName: targetObjectName, + pinGuid: targetPinEntity.PinId, + })); + return true + } + return false // Already linked + } + + /** + * @param {String} targetObjectName + * @param {PinEntity} targetPinEntity + * @returns true if it was linked to the target + */ + unlinkFrom(targetObjectName, targetPinEntity) { + const indexElement = this.LinkedTo?.findIndex(pinReferenceEntity => { + return pinReferenceEntity.objectName.toString() == targetObjectName + && pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf() + }); + if (indexElement >= 0) { + this.LinkedTo.splice(indexElement, 1); + if (this.LinkedTo.length === 0 && PinEntity.attributes.LinkedTo.default === undefined) { + this.LinkedTo = undefined; + } + return true + } + return false + } + + getSubCategory() { + return this.PinType.PinSubCategoryObject.path + } + + pinColor() { + return pinColor(this) } } -/** - * @typedef {import("./KeyboardShortcut.js").Options & { - * activationKeys?: String | KeyBindingEntity | (String | KeyBindingEntity)[], - * }} Options - */ - -class KeyboardEnableZoom extends KeyboardShortcut { - - /** @type {Zoom} */ - #zoomInputObject - - /** - * @param {HTMLElement} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.activationKeys = Shortcuts.enableZoomIn; - super(target, blueprint, options); - } - - fire() { - this.#zoomInputObject = this.blueprint.template.getZoomInputObject(); - this.#zoomInputObject.enableZoonIn = true; - } - - unfire() { - this.#zoomInputObject.enableZoonIn = false; - } -} - -/** - * @template {IEntity} EntityT - * @template {ITemplate} TemplateT - */ -class IElement extends s { +class ScriptVariableEntity extends IEntity { - /** @type {Blueprint} */ - #blueprint - get blueprint() { - return this.#blueprint - } - set blueprint(v) { - this.#blueprint = v; + static attributes = { + ...super.attributes, + ScriptVariable: AttributeInfo.createType(ObjectReferenceEntity), + OriginalChangeId: AttributeInfo.createType(GuidEntity), } + static grammar = this.createGrammar() - /** @type {EntityT} */ - #entity - get entity() { - return this.#entity - } - set entity(entity) { - this.#entity = entity; + static createGrammar() { + return Grammar.createEntityGrammar(this) } - /** @type {TemplateT} */ - #template - get template() { - return this.#template + constructor(values = {}, suppressWarns = false) { + super(values, suppressWarns); + /** @type {ObjectReferenceEntity} */ this.ScriptVariable; + /** @type {GuidEntity} */ this.OriginalChangeId; } +} - isInitialized = false - isSetup = false - - /** @type {IInput[]} */ - inputObjects = [] - - /** - * @param {EntityT} entity - * @param {TemplateT} 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; - } +class UnknownPinEntity extends PinEntity { + + static grammar = this.createGrammar() + + static createGrammar() { + return Parsernostrum.seq( + Parsernostrum.reg( + new RegExp(`(${Grammar.Regex.Symbol.source})\\s*\\(\\s*`), + 1 + ), + Grammar.createAttributeGrammar(this).sepBy(Grammar.commaSeparation), + Parsernostrum.reg(/\s*(?:,\s*)?\)/) + ).map(([lookbehind, attributes, _2]) => { + lookbehind ??= ""; + let values = {}; + if (lookbehind.length) { + values.lookbehind = lookbehind; + } + attributes.forEach(attributeSetter => attributeSetter(values)); + return new this(values) + }) + } + + constructor(values = {}) { + super(values, true); + } +} - connectedCallback() { - super.connectedCallback(); - this.blueprint = /** @type {Blueprint} */(this.closest("ueb-blueprint")); - if (this.isInitialized) { - this.requestUpdate(); - this.updateComplete.then(() => this.setup()); - } - } +class VariableReferenceEntity extends IEntity { + + static attributes = { + ...super.attributes, + MemberScope: AttributeInfo.createType(String), + MemberName: AttributeInfo.createValue(""), + MemberGuid: AttributeInfo.createType(GuidEntity), + bSelfContext: AttributeInfo.createType(Boolean), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Grammar.createEntityGrammar(this) + } + + constructor(values) { + super(values); + /** @type {String} */ this.MemberName; + /** @type {GuidEntity} */ this.GuidEntity; + /** @type {Boolean} */ this.bSelfContext; + } +} - disconnectedCallback() { - super.disconnectedCallback(); - if (this.isSetup) { - this.updateComplete.then(() => this.cleanup()); +/** @param {PinEntity} pinEntity */ +const indexFromUpperCaseLetterName = pinEntity => + pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0); + +/** @param {ObjectEntity} entity */ +function nodeVariadic(entity) { + /** @type {() => PinEntity[]} */ + let pinEntities; + /** @type {(pinEntity: PinEntity) => Number} */ + let pinIndexFromEntity; + /** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number, newPin: PinEntity) => String} */ + let pinNameFromIndex; + const type = entity.getType(); + let name; + switch (type) { + case Configuration.paths.commutativeAssociativeBinaryOperator: + case Configuration.paths.promotableOperator: + name = entity.FunctionReference?.MemberName; + switch (name) { + default: + if ( + !name?.startsWith("Add_") + && !name?.startsWith("Subtract_") + && !name?.startsWith("Multiply_") + && !name?.startsWith("Divide_") + ) { + break + } + case "And_Int64Int64": + case "And_IntInt": + case "BMax": + case "BMin": + case "BooleanAND": + case "BooleanNAND": + case "BooleanOR": + case "Concat_StrStr": + case "FMax": + case "FMin": + case "Max": + case "MaxInt64": + case "Min": + case "MinInt64": + case "Or_Int64Int64": + case "Or_IntInt": + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput()); + pinIndexFromEntity ??= indexFromUpperCaseLetterName; + pinNameFromIndex ??= (index, min = -1, max = -1) => { + const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1); + entity.NumAdditionalInputs = pinEntities().length - 1; + return result + }; + break + } + break + case Configuration.paths.multiGate: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()); + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]); + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => + `Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`; + break + // case Configuration.paths.niagaraNodeOp: + // pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput()) + // pinIndexFromEntity ??= indexFromUpperCaseLetterName + // pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + // const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1) + // entity.AddedPins ??= [] + // entity.AddedPins.push(newPin) + // return result + // } + // break + case Configuration.paths.switchInteger: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()); + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1]); + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString(); + break + case Configuration.paths.switchGameplayTag: + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`; + entity.PinNames ??= []; + entity.PinNames.push(result); + delete entity.PinTags[entity.PinTags.length - 1]; + entity.PinTags[entity.PinTags.length] = null; + return result + }; + case Configuration.paths.switchName: + case Configuration.paths.switchString: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()); + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]); + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`; + entity.PinNames ??= []; + entity.PinNames.push(result); + return result + }; + break + } + if (pinEntities) { + return () => { + let min = Number.MAX_SAFE_INTEGER; + let max = Number.MIN_SAFE_INTEGER; + let values = []; + const modelPin = pinEntities().reduce( + (acc, cur) => { + const value = pinIndexFromEntity(cur); + if (!isNaN(value)) { + values.push(value); + min = Math.min(value, min); + if (value > max) { + max = value; + return cur + } + } else if (acc === undefined) { + return cur + } + return acc + }, + undefined + ); + if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) { + min = undefined; + max = undefined; + } + if (!modelPin) { + return null + } + values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1); + let prev = values[0]; + let index = values.findIndex( + // Search for a gap + value => { + const result = value - prev > 1; + prev = value; + return result + } + ); + const newPin = new PinEntity(modelPin); + newPin.PinId = GuidEntity.generateGuid(); + newPin.PinName = pinNameFromIndex(index, min, max, newPin); + newPin.PinToolTip = undefined; + entity.getCustomproperties(true).push(newPin); + return newPin } - this.acknowledgeDelete(); - } - - createRenderRoot() { - return this - } - - 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); +class ObjectEntity extends IEntity { + + static attributes = { + ...super.attributes, + isExported: new AttributeInfo({ + type: Boolean, + ignored: true, + }), + Class: AttributeInfo.createType(ObjectReferenceEntity), + Name: AttributeInfo.createType(String), + Archetype: AttributeInfo.createType(ObjectReferenceEntity), + ExportPath: AttributeInfo.createType(ObjectReferenceEntity), + ObjectRef: AttributeInfo.createType(ObjectReferenceEntity), + BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity), + BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity), + PinTags: new AttributeInfo({ + type: [null], + inlined: true, + }), + PinNames: new AttributeInfo({ + type: [String], + inlined: true, + }), + AxisKey: AttributeInfo.createType(SymbolEntity), + InputAxisKey: AttributeInfo.createType(SymbolEntity), + InputName: AttributeInfo.createType(String), + InputType: AttributeInfo.createType(SymbolEntity), + NumAdditionalInputs: AttributeInfo.createType(Number), + bIsPureFunc: AttributeInfo.createType(Boolean), + bIsConstFunc: AttributeInfo.createType(Boolean), + bIsCaseSensitive: AttributeInfo.createType(Boolean), + VariableReference: AttributeInfo.createType(VariableReferenceEntity), + SelfContextInfo: AttributeInfo.createType(SymbolEntity), + DelegatePropertyName: AttributeInfo.createType(String), + DelegateOwnerClass: AttributeInfo.createType(ObjectReferenceEntity), + ComponentPropertyName: AttributeInfo.createType(String), + EventReference: AttributeInfo.createType(FunctionReferenceEntity), + FunctionReference: AttributeInfo.createType(FunctionReferenceEntity), + FunctionScript: AttributeInfo.createType(ObjectReferenceEntity), + CustomFunctionName: AttributeInfo.createType(String), + TargetType: AttributeInfo.createType(ObjectReferenceEntity), + MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity), + Enum: AttributeInfo.createType(ObjectReferenceEntity), + EnumEntries: new AttributeInfo({ + type: [String], + inlined: true, + }), + InputKey: AttributeInfo.createType(SymbolEntity), + OpName: AttributeInfo.createType(String), + CachedChangeId: AttributeInfo.createType(GuidEntity), + FunctionDisplayName: AttributeInfo.createType(String), + AddedPins: new AttributeInfo({ + type: [UnknownPinEntity], + default: () => [], + inlined: true, + silent: true, + }), + ChangeId: AttributeInfo.createType(GuidEntity), + MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity), + bOverrideFunction: AttributeInfo.createType(Boolean), + bInternalEvent: AttributeInfo.createType(Boolean), + bConsumeInput: AttributeInfo.createType(Boolean), + bExecuteWhenPaused: AttributeInfo.createType(Boolean), + bOverrideParentBinding: AttributeInfo.createType(Boolean), + bControl: AttributeInfo.createType(Boolean), + bAlt: AttributeInfo.createType(Boolean), + bShift: AttributeInfo.createType(Boolean), + bCommand: AttributeInfo.createType(Boolean), + CommentColor: AttributeInfo.createType(LinearColorEntity), + bCommentBubbleVisible_InDetailsPanel: AttributeInfo.createType(Boolean), + bColorCommentBubble: AttributeInfo.createType(Boolean), + ProxyFactoryFunctionName: AttributeInfo.createType(String), + ProxyFactoryClass: AttributeInfo.createType(ObjectReferenceEntity), + ProxyClass: AttributeInfo.createType(ObjectReferenceEntity), + StructType: AttributeInfo.createType(ObjectReferenceEntity), + MaterialExpression: AttributeInfo.createType(ObjectReferenceEntity), + MaterialExpressionComment: AttributeInfo.createType(ObjectReferenceEntity), + MoveMode: AttributeInfo.createType(SymbolEntity), + TimelineName: AttributeInfo.createType(String), + TimelineGuid: AttributeInfo.createType(GuidEntity), + SizeX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + SizeY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + Text: AttributeInfo.createType(new MirroredEntity(String)), + MaterialExpressionEditorX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + MaterialExpressionEditorY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + NodeTitle: AttributeInfo.createType(String), + NodeTitleColor: AttributeInfo.createType(LinearColorEntity), + PositionX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + PositionY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)), + SettingsInterface: AttributeInfo.createType(ObjectReferenceEntity), + PCGNode: AttributeInfo.createType(ObjectReferenceEntity), + HiGenGridSize: AttributeInfo.createType(SymbolEntity), + Operation: AttributeInfo.createType(SymbolEntity), + NodePosX: AttributeInfo.createType(IntegerEntity), + NodePosY: AttributeInfo.createType(IntegerEntity), + NodeHeight: AttributeInfo.createType(IntegerEntity), + NodeWidth: AttributeInfo.createType(IntegerEntity), + Graph: AttributeInfo.createType(ObjectReferenceEntity), + SubgraphInstance: AttributeInfo.createType(String), + InputPins: new AttributeInfo({ + type: [ObjectReferenceEntity], + inlined: true, + }), + OutputPins: new AttributeInfo({ + type: [ObjectReferenceEntity], + inlined: true, + }), + bExposeToLibrary: AttributeInfo.createType(Boolean), + bCanRenameNode: AttributeInfo.createType(Boolean), + bCommentBubblePinned: AttributeInfo.createType(Boolean), + bCommentBubbleVisible: AttributeInfo.createType(Boolean), + NodeComment: AttributeInfo.createType(String), + AdvancedPinDisplay: AttributeInfo.createType(IdentifierEntity), + DelegateReference: AttributeInfo.createType(VariableReferenceEntity), + EnabledState: AttributeInfo.createType(IdentifierEntity), + NodeGuid: AttributeInfo.createType(GuidEntity), + ErrorType: AttributeInfo.createType(IntegerEntity), + ErrorMsg: AttributeInfo.createType(String), + ScriptVariables: new AttributeInfo({ + type: [ScriptVariableEntity], + inlined: true, + }), + Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)), + ExportedNodes: AttributeInfo.createType(String), + CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]), + } + static nameRegex = /^(\w+?)(?:_(\d+))?$/ + static customPropertyGrammar = Parsernostrum.seq( + Parsernostrum.reg(/CustomProperties\s+/), + Grammar.grammarFor( + undefined, + this.attributes.CustomProperties.type[0] + ), + ).map(([_0, pin]) => values => { + if (!values.CustomProperties) { + values.CustomProperties = []; + } + values.CustomProperties.push(pin); + }) + static inlinedArrayEntryGrammar = Parsernostrum.seq( + Parsernostrum.alt( + Grammar.symbolQuoted.map(v => [v, true]), + Grammar.symbol.map(v => [v, false]), + ), + Parsernostrum.reg( + new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`), + 1 + ).map(Number) + ) + .chain( + /** @param {[[String, Boolean], Number]} param */ + ([[symbol, quoted], index]) => + Grammar.grammarFor(this.attributes[symbol]) + .map(currentValue => + values => { + (values[symbol] ??= [])[index] = currentValue; + Utility.objectSet(values, ["attributes", symbol, "quoted"], quoted); + if (!this.attributes[symbol]?.inlined) { + if (!values.attributes) { + IEntity.defineAttributes(values, {}); + } + Utility.objectSet(values, ["attributes", symbol, "type"], [currentValue.constructor]); + Utility.objectSet(values, ["attributes", symbol, "inlined"], true); + } + } + ) + ) + static grammar = this.createGrammar() + + static createSubObjectGrammar() { + return Parsernostrum.lazy(() => this.grammar) + .map(object => + values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object + ) + } + + static createGrammar() { + return Parsernostrum.seq( + Parsernostrum.reg(/Begin +Object/), + Parsernostrum.seq( + Parsernostrum.whitespace, + Parsernostrum.alt( + this.createSubObjectGrammar(), + this.customPropertyGrammar, + Grammar.createAttributeGrammar(this, Parsernostrum.reg(Grammar.Regex.MultipleWordsSymbols)), + Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) => + Utility.objectSet(obj, ["attributes", ...k, "quoted"], true) + ), + this.inlinedArrayEntryGrammar, + ) + ) + .map(([_0, entry]) => entry) + .many(), + Parsernostrum.reg(/\s+End +Object/), + ) + .map(([_0, attributes, _2]) => { + const values = {}; + attributes.forEach(attributeSetter => attributeSetter(values)); + return new this(values) + }) + } + + static getMultipleObjectsGrammar() { + return Parsernostrum.seq( + Parsernostrum.whitespaceOpt, + this.grammar, + Parsernostrum.seq( + Parsernostrum.whitespace, + this.grammar, + ) + .map(([_0, object]) => object) + .many(), + Parsernostrum.whitespaceOpt + ) + .map(([_0, first, remaining, _4]) => [first, ...remaining]) + } + + /** @type {String} */ + #class + + constructor(values = {}, suppressWarns = false) { + if ("NodePosX" in values !== "NodePosY" in values) { + const entries = Object.entries(values); + const [key, position] = "NodePosX" in values + ? ["NodePosY", Object.keys(values).indexOf("NodePosX") + 1] + : ["NodePosX", Object.keys(values).indexOf("NodePosY")]; + const entry = [key, new (AttributeInfo.getAttribute(values, key, "type", ObjectEntity))()]; + entries.splice(position, 0, entry); + values = Object.fromEntries(entries); + } + super(values, suppressWarns); + + // Attributes not assigned a strong type in attributes because the names are too generic + /** @type {Number | MirroredEntity} */ this.R; + /** @type {Number | MirroredEntity} */ this.G; + /** @type {Number | MirroredEntity} */ this.B; + /** @type {Number | MirroredEntity} */ this.A; + + // Attributes + /** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties; + /** @type {Boolean} */ this.bIsPureFunc; + /** @type {Boolean} */ this.isExported; + /** @type {FunctionReferenceEntity} */ this.ComponentPropertyName; + /** @type {FunctionReferenceEntity} */ this.EventReference; + /** @type {FunctionReferenceEntity} */ this.FunctionReference; + /** @type {IdentifierEntity} */ this.AdvancedPinDisplay; + /** @type {IdentifierEntity} */ this.EnabledState; + /** @type {IntegerEntity} */ this.NodeHeight; + /** @type {IntegerEntity} */ this.NodePosX; + /** @type {IntegerEntity} */ this.NodePosY; + /** @type {IntegerEntity} */ this.NodeWidth; + /** @type {LinearColorEntity} */ this.CommentColor; + /** @type {LinearColorEntity} */ this.NodeTitleColor; + /** @type {MacroGraphReferenceEntity} */ this.MacroGraphReference; + /** @type {MirroredEntity} */ this.MaterialExpressionEditorX; + /** @type {MirroredEntity} */ this.MaterialExpressionEditorY; + /** @type {MirroredEntity} */ this.SizeX; + /** @type {MirroredEntity} */ this.SizeY; + /** @type {MirroredEntity} */ this.Text; + /** @type {MirroredEntity} */ this.PositionX; + /** @type {MirroredEntity} */ this.PositionY; + /** @type {MirroredEntity} */ this.Node; + /** @type {null[]} */ this.PinTags; + /** @type {Number} */ this.NumAdditionalInputs; + /** @type {ObjectReferenceEntity[]} */ this.InputPins; + /** @type {ObjectReferenceEntity[]} */ this.OutputPins; + /** @type {ObjectReferenceEntity} */ this.Archetype; + /** @type {ObjectReferenceEntity} */ this.BlueprintElementInstance; + /** @type {ObjectReferenceEntity} */ this.BlueprintElementType; + /** @type {ObjectReferenceEntity} */ this.Class; + /** @type {ObjectReferenceEntity} */ this.Enum; + /** @type {ObjectReferenceEntity} */ this.ExportPath; + /** @type {ObjectReferenceEntity} */ this.FunctionScript; + /** @type {ObjectReferenceEntity} */ this.Graph; + /** @type {ObjectReferenceEntity} */ this.MaterialExpression; + /** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment; + /** @type {ObjectReferenceEntity} */ this.MaterialFunction; + /** @type {ObjectReferenceEntity} */ this.ObjectRef; + /** @type {ObjectReferenceEntity} */ this.PCGNode; + /** @type {ObjectReferenceEntity} */ this.SettingsInterface; + /** @type {ObjectReferenceEntity} */ this.StructType; + /** @type {ObjectReferenceEntity} */ this.TargetType; + /** @type {ScriptVariableEntity[]} */ this.ScriptVariables; + /** @type {String[]} */ this.EnumEntries; + /** @type {String[]} */ this.PinNames; + /** @type {String} */ this.CustomFunctionName; + /** @type {String} */ this.DelegatePropertyName; + /** @type {String} */ this.ExportedNodes; + /** @type {String} */ this.FunctionDisplayName; + /** @type {String} */ this.InputName; + /** @type {String} */ this.Name; + /** @type {String} */ this.NodeComment; + /** @type {String} */ this.NodeTitle; + /** @type {String} */ this.Operation; + /** @type {String} */ this.OpName; + /** @type {String} */ this.ProxyFactoryFunctionName; + /** @type {String} */ this.SubgraphInstance; + /** @type {String} */ this.Text; + /** @type {SymbolEntity} */ this.AxisKey; + /** @type {SymbolEntity} */ this.HiGenGridSize; + /** @type {SymbolEntity} */ this.InputAxisKey; + /** @type {SymbolEntity} */ this.InputKey; + /** @type {SymbolEntity} */ this.InputType; + /** @type {UnknownPinEntity[]} */ this.AddedPins; + /** @type {VariableReferenceEntity} */ this.DelegateReference; + /** @type {VariableReferenceEntity} */ this.VariableReference; + + // Legacy nodes pins + if (this["Pins"] instanceof Array) { + this["Pins"].forEach( + /** @param {ObjectReferenceEntity} objectReference */ + objectReference => { + const pinObject = this[Configuration.subObjectAttributeNameFromReference(objectReference, true)]; + if (pinObject) { + const pinEntity = PinEntity.fromLegacyObject(pinObject); + pinEntity.LinkedTo = []; + this.getCustomproperties(true).push(pinEntity); + Utility.objectSet(this, ["attributes", "CustomProperties", "ignored"], true); + } + } + ); + } + /** @type {ObjectEntity} */ + const materialSubobject = this.getMaterialSubobject(); + if (materialSubobject) { + const obj = materialSubobject; + obj.SizeX !== undefined && (obj.SizeX.getter = () => this.NodeWidth); + obj.SizeY && (obj.SizeY.getter = () => this.NodeHeight); + obj.Text && (obj.Text.getter = () => this.NodeComment); + obj.MaterialExpressionEditorX && (obj.MaterialExpressionEditorX.getter = () => this.NodePosX); + obj.MaterialExpressionEditorY && (obj.MaterialExpressionEditorY.getter = () => this.NodePosY); + if (this.getType() === Configuration.paths.materialExpressionComponentMask) { + // The following attributes are too generic therefore not assigned a MirroredEntity + const rgbaPins = Configuration.rgba.map(pinName => + this.getPinEntities().find(pin => pin.PinName === pinName && (pin.recomputesNodeTitleOnChange = true)) + ); + obj.R = new MirroredEntity(Boolean, () => rgbaPins[0].DefaultValue); + obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue); + obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue); + obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue); + Utility.objectSet(obj, ["attributes", "R", "default"], false); + Utility.objectSet(obj, ["attributes", "R", "silent"], true); + Utility.objectSet(obj, ["attributes", "G", "default"], false); + Utility.objectSet(obj, ["attributes", "G", "silent"], true); + Utility.objectSet(obj, ["attributes", "B", "default"], false); + Utility.objectSet(obj, ["attributes", "B", "silent"], true); + Utility.objectSet(obj, ["attributes", "A", "default"], false); + Utility.objectSet(obj, ["attributes", "A", "silent"], true); + obj._keys = [...Configuration.rgba, ...Object.keys(obj).filter(k => !Configuration.rgba.includes(k))]; + } + } + /** @type {ObjectEntity} */ + const pcgObject = this.getPcgSubobject(); + if (pcgObject) { + pcgObject.PositionX && (pcgObject.PositionX.getter = () => this.NodePosX); + pcgObject.PositionY && (pcgObject.PositionY.getter = () => this.NodePosY); + pcgObject.getSubobjects().forEach( + /** @param {ObjectEntity} obj */ + obj => { + if (obj.Node !== undefined) { + const nodeRef = obj.Node.get(); + if ( + nodeRef.type === this.PCGNode.type + && nodeRef.path === `${this.Name}.${this.PCGNode.path}` + ) { + obj.Node.getter = () => new ObjectReferenceEntity({ + type: this.PCGNode.type, + path: `${this.Name}.${this.PCGNode.path}`, + }); + } + } + } + ); + + } + let inputIndex = 0; + let outputIndex = 0; + this.CustomProperties?.forEach((pinEntity, i) => { + pinEntity.objectEntity = this; + pinEntity.pinIndex = pinEntity.isInput() + ? inputIndex++ + : pinEntity.isOutput() + ? outputIndex++ + : i; + }); + } + + getClass() { + if (!this.#class) { + this.#class = (this.Class?.path ? this.Class.path : this.Class?.type) + ?? (this.ExportPath?.path ? this.ExportPath.path : this.ExportPath?.type) + ?? ""; + if (this.#class && !this.#class.startsWith("/")) { + // Old path names did not start with /Script or /Engine, check tests/resources/LegacyNodes.js + let path = Object.values(Configuration.paths).find(path => path.endsWith("." + this.#class)); + if (path) { + this.#class = path; + } + } + } + return this.#class + } + + getType() { + let classValue = this.getClass(); + if (this.MacroGraphReference?.MacroGraph?.path) { + return this.MacroGraphReference.MacroGraph.path + } + if (this.MaterialExpression) { + return this.MaterialExpression.type + } + return classValue + } + + getObjectName(dropCounter = false) { + if (dropCounter) { + return this.getNameAndCounter()[0] + } + return this.Name + } + + /** @returns {[String, Number]} */ + getNameAndCounter() { + const result = this.getObjectName().match(ObjectEntity.nameRegex); + return result + ? [result[1] ?? "", parseInt(result[2] ?? "0")] + : ["", 0] + } + + getCounter() { + return this.getNameAndCounter()[1] + } + + getNodeWidth() { + return this.NodeWidth + ?? this.isComment() ? Configuration.defaultCommentWidth : undefined + } + + /** @param {Number} value */ + setNodeWidth(value) { + if (!this.NodeWidth) { + this.NodeWidth = new IntegerEntity(); + } + this.NodeWidth.value = value; + } + + getNodeHeight() { + return this.NodeHeight + ?? this.isComment() ? Configuration.defaultCommentHeight : undefined + } + + /** @param {Number} value */ + setNodeHeight(value) { + if (!this.NodeHeight) { + this.NodeHeight = new IntegerEntity(); + } + this.NodeHeight.value = value; + } + + getNodePosX() { + return this.NodePosX?.value ?? 0 + } + + /** @param {Number} value */ + setNodePosX(value) { + if (!this.NodePosX) { + this.NodePosX = new IntegerEntity(); + } + this.NodePosX.value = Math.round(value); + } + + getNodePosY() { + return this.NodePosY?.value ?? 0 + } + + /** @param {Number} value */ + setNodePosY(value) { + if (!this.NodePosY) { + this.NodePosY = new IntegerEntity(); + } + this.NodePosY.value = Math.round(value); + } + + getCustomproperties(canCreate = false) { + if (canCreate && !this.CustomProperties) { + this.CustomProperties = []; + } + return this.CustomProperties ?? [] + } + + /** @returns {PinEntity[]} */ + getPinEntities() { + return this.getCustomproperties().filter(v => v.constructor === PinEntity) + } + + /** @returns {ObjectEntity[]} */ + getSubobjects() { + return Object.keys(this) + .filter(k => k.startsWith(Configuration.subObjectAttributeNamePrefix)) + .flatMap(k => [this[k], .../** @type {ObjectEntity} */(this[k]).getSubobjects()]) + } + + switchTarget() { + const switchMatch = this.getClass().match(Configuration.switchTargetPattern); + if (switchMatch) { + return switchMatch[1] + } + } + + isEvent() { + switch (this.getClass()) { + case Configuration.paths.actorBoundEvent: + case Configuration.paths.componentBoundEvent: + case Configuration.paths.customEvent: + case Configuration.paths.event: + case Configuration.paths.inputAxisKeyEvent: + case Configuration.paths.inputVectorAxisEvent: + return true + } + return false + } + + isComment() { + switch (this.getClass()) { + case Configuration.paths.comment: + case Configuration.paths.materialGraphNodeComment: + return true + } + return false + } + + isMaterial() { + + return this.getClass() === Configuration.paths.materialGraphNode + // return [ + // Configuration.paths.materialExpressionConstant, + // Configuration.paths.materialExpressionConstant2Vector, + // Configuration.paths.materialExpressionConstant3Vector, + // Configuration.paths.materialExpressionConstant4Vector, + // Configuration.paths.materialExpressionLogarithm, + // Configuration.paths.materialExpressionLogarithm10, + // Configuration.paths.materialExpressionLogarithm2, + // Configuration.paths.materialExpressionMaterialFunctionCall, + // Configuration.paths.materialExpressionSquareRoot, + // Configuration.paths.materialExpressionTextureCoordinate, + // Configuration.paths.materialExpressionTextureSample, + // Configuration.paths.materialGraphNode, + // Configuration.paths.materialGraphNodeComment, + // ] + // .includes(this.getClass()) + } + + /** @return {ObjectEntity} */ + getMaterialSubobject() { + const expression = this.MaterialExpression ?? this.MaterialExpressionComment; + return expression + ? this[Configuration.subObjectAttributeNameFromReference(expression, true)] + : null + } + + isPcg() { + return this.getClass() === Configuration.paths.pcgEditorGraphNode + || this.getPcgSubobject() + } + + isNiagara() { + return this.Class && (this.Class.type ? this.Class.type : this.Class.path)?.startsWith("/Script/NiagaraEditor.") + } + + /** @return {ObjectEntity} */ + getPcgSubobject() { + const node = this.PCGNode; + return node + ? this[Configuration.subObjectAttributeNameFromReference(node, true)] + : null + } + + /** @return {ObjectEntity} */ + getSettingsObject() { + const settings = this.SettingsInterface; + return settings + ? this[Configuration.subObjectAttributeNameFromReference(settings, true)] + : null + } + + /** @return {ObjectEntity} */ + getSubgraphObject() { + const name = this.SubgraphInstance; + return name + ? this[Configuration.subObjectAttributeNameFromName(name)] + : null + } + + isDevelopmentOnly() { + const nodeClass = this.getClass(); + return this.EnabledState?.toString() === "DevelopmentOnly" + || nodeClass.includes("Debug", Math.max(0, nodeClass.lastIndexOf("."))) + } + + getHIDAttribute() { + return this.InputKey ?? this.AxisKey ?? this.InputAxisKey + } + + getDelegatePin() { + return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate") + } + + nodeColor() { + return nodeColor(this) + } + + nodeIcon() { + return nodeIcon(this) + } + + additionalPinInserter() { + return nodeVariadic(this) + } +} + +class KnotEntity extends ObjectEntity { + + /** + * @param {Object} values + * @param {PinEntity} pinReferenceForType + */ + constructor(values = {}, pinReferenceForType = undefined) { + values.Class = new ObjectReferenceEntity(Configuration.paths.knot); + values.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); + } + values["CustomProperties"] = [inputPinEntity, outputPinEntity]; + super(values, true); } +} - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - this.template.update(changedProperties); +/** + * @typedef {{ + * consumeEvent?: Boolean, + * listenOnFocus?: Boolean, + * unlistenOnTextEdit?: Boolean, + * }} Options + */ + +/** @template {Element} T */ +class IInput { + + /** @type {T} */ + #target + get target() { + return this.#target } - render() { - return this.template.render() + /** @type {Blueprint} */ + #blueprint + get blueprint() { + return this.#blueprint } - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.template.firstUpdated(changedProperties); - this.template.inputSetup(); + consumeEvent + + /** @type {Object} */ + options + + + listenHandler = () => this.listenEvents() + unlistenHandler = () => this.unlistenEvents() + + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.consumeEvent ??= false; + options.listenOnFocus ??= false; + options.unlistenOnTextEdit ??= false; + this.#target = target; + this.#blueprint = blueprint; + this.consumeEvent = options.consumeEvent; + this.options = options; } - /** @param {PropertyValues} changedProperties */ - updated(changedProperties) { - super.updated(changedProperties); - this.template.updated(changedProperties); + 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(); + } } - acknowledgeDelete() { - let deleteEvent = new CustomEvent(Configuration.removeEventName); - this.dispatchEvent(deleteEvent); + 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); } - /** @param {IElement} element */ - isSameGraph(element) { - return this.blueprint && this.blueprint == element?.blueprint + /* Subclasses will probabily override the following methods */ + listenEvents() { + } + + unlistenEvents() { } } -/** - * @template {IEntity} T - * @template {IDraggableTemplate} U - * @extends {IElement} - */ -class IDraggableElement extends IElement { +class KeyBindingEntity extends IEntity { - 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 attributes = { + ...super.attributes, + ActionName: AttributeInfo.createValue(""), + bShift: AttributeInfo.createValue(false), + bCtrl: AttributeInfo.createValue(false), + bAlt: AttributeInfo.createValue(false), + bCmd: AttributeInfo.createValue(false), + Key: AttributeInfo.createType(IdentifierEntity), } - static dragEventName = Configuration.dragEventName - static dragGeneralEventName = Configuration.dragGeneralEventName + static grammar = this.createGrammar() - constructor() { - super(); - this.locationX = 0; - this.locationY = 0; - this.sizeX = 0; - this.sizeY = 0; + static createGrammar() { + return Parsernostrum.alt( + IdentifierEntity.grammar.map(identifier => new this({ + Key: identifier + })), + Grammar.createEntityGrammar(this) + ) } - computeSizes() { - const bounding = this.getBoundingClientRect(); - this.sizeX = this.blueprint.scaleCorrect(bounding.width); - this.sizeY = this.blueprint.scaleCorrect(bounding.height); + constructor(values = {}) { + super(values, true); + /** @type {String} */ this.ActionName; + /** @type {Boolean} */ this.bShift; + /** @type {Boolean} */ this.bCtrl; + /** @type {Boolean} */ this.bAlt; + /** @type {Boolean} */ this.bCmd; + /** @type {IdentifierEntity} */ this.Key; } +} - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.computeSizes(); - } +/** + * @typedef {import("../IInput.js").Options & { + * activationKeys?: String | KeyBindingEntity | (String | KeyBindingEntity)[], + * consumeEvent?: Boolean, + * listenOnFocus?: Boolean, + * unlistenOnTextEdit?: Boolean, + * }} Options + */ + +/** + * @template {Element} T + * @extends IInput + */ +class KeyboardShortcut extends IInput { + + static #ignoreEvent = + /** @param {KeyboardShortcut} self */ + self => { } + + /** @type {KeyBindingEntity[]} */ + #activationKeys + + pressedKey = "" /** - * @param {Number} x - * @param {Number} y + * @param {T} target + * @param {Blueprint} blueprint + * @param {Options} options */ - 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); + constructor( + target, + blueprint, + options = {}, + onKeyDown = KeyboardShortcut.#ignoreEvent, + onKeyUp = KeyboardShortcut.#ignoreEvent + ) { + options.activationKeys ??= []; + options.consumeEvent ??= true; + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; // No shortcuts when inside of a text field + if (!(options.activationKeys instanceof Array)) { + options.activationKeys = [options.activationKeys]; } - } + options.activationKeys = options.activationKeys.map(v => { + if (v instanceof KeyBindingEntity) { + return v + } + if (v.constructor === String) { + const parsed = KeyBindingEntity.grammar.run(v); + if (parsed.status) { + return parsed.value + } + } + throw new Error("Unexpected key value") + }); - /** - * @param {Number} x - * @param {Number} y - */ - addLocation(x, y, acknowledge = true) { - this.setLocation(this.locationX + x, this.locationY + y, acknowledge); - } + super(target, blueprint, options); + this.onKeyDown = onKeyDown; + this.onKeyUp = onKeyUp; - /** @param {Coordinates} value */ - acknowledgeDrag(value) { - const dragEvent = new CustomEvent( - /** @type {typeof IDraggableElement} */(this.constructor).dragGeneralEventName, - { - detail: { - value: value - }, - bubbles: true, - cancelable: true + 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 ( + self.#activationKeys.some(keyEntry => + wantsShift(keyEntry) == e.shiftKey + && wantsCtrl(keyEntry) == e.ctrlKey + && wantsAlt(keyEntry) == e.altKey + && Configuration.Keys[keyEntry.Key.value] == e.code + ) + ) { + if (this.consumeEvent) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + this.pressedKey = e.code; + self.fire(); + document.removeEventListener("keydown", self.keyDownHandler); + document.addEventListener("keyup", self.keyUpHandler); } - ); - 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]); - } + /** @param {KeyboardEvent} e */ + this.keyUpHandler = e => { + if ( + self.#activationKeys.some(keyEntry => + keyEntry.bShift && e.key == "Shift" + || keyEntry.bCtrl && e.key == "Control" + || keyEntry.bAlt && e.key == "Alt" + || keyEntry.bCmd && e.key == "Meta" + || Configuration.Keys[keyEntry.Key.value] == e.code + ) + ) { + if (this.consumeEvent) { + e.stopImmediatePropagation(); + } + self.unfire(); + this.pressedKey = ""; + document.removeEventListener("keyup", this.keyUpHandler); + document.addEventListener("keydown", this.keyDownHandler); + } + }; } - topBoundary(justSelectableArea = false) { - return this.template.topBoundary(justSelectableArea) + listenEvents() { + document.addEventListener("keydown", this.keyDownHandler); } - rightBoundary(justSelectableArea = false) { - return this.template.rightBoundary(justSelectableArea) + unlistenEvents() { + document.removeEventListener("keydown", this.keyDownHandler); } - bottomBoundary(justSelectableArea = false) { - return this.template.bottomBoundary(justSelectableArea) + /* Subclasses can override */ + + fire() { + this.onKeyDown(this); } - leftBoundary(justSelectableArea = false) { - return this.template.leftBoundary(justSelectableArea) + unfire() { + this.onKeyUp(this); } } /** - * @typedef {import("./IPointing.js").Options & { - * clickButton?: Number, - * consumeEvent?: Boolean, - * draggableElement?: HTMLElement, - * exitAnyButton?: Boolean, - * moveEverywhere?: Boolean, + * @typedef {import("../IInput.js").Options & { + * ignoreTranslateCompensate?: Boolean, + * ignoreScale?: Boolean, * movementSpace?: HTMLElement, - * repositionOnClick?: Boolean, - * scrollGraphEdge?: Boolean, - * strictTarget?: Boolean, - * stepSize?: Number, + * enablerKey?: KeyboardShortcut, * }} Options */ /** - * @template {IElement} T - * @extends {IPointing} + * @template {Element} T + * @extends {IInput} */ -class IMouseClickDrag extends IPointing { +class IPointing extends IInput { - /** @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.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - // Attach the listeners - this.#movementListenedElement.addEventListener("mousemove", this.#mouseStartedMovingHandler); - document.addEventListener("mouseup", this.#mouseUpHandler); - this.setLocationFromEvent(e); - this.clickedPosition[0] = this.location[0]; - this.clickedPosition[1] = this.location[1]; - this.blueprint.mousePosition[0] = this.location[0]; - this.blueprint.mousePosition[1] = this.location[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 - } + #location = /** @type {Coordinates} */([0, 0]) + get location() { + return this.#location } - /** @param {MouseEvent} e */ - #mouseStartedMovingHandler = e => { - if (this.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; - this.setLocationFromEvent(e); - // Do actual actions - this.lastLocation = Utility.snapToGrid(this.clickedPosition[0], this.clickedPosition[1], this.stepSize); - this.startDrag(this.location); - this.started = true; - this.#mouseMoveHandler(e); + /** @type {KeyboardShortcut?} */ + #enablerKey + get enablerKey() { + return this.#enablerKey + } + #enablerActivated = true + get enablerActivated() { + return this.#enablerActivated } - /** @param {MouseEvent} e */ - #mouseMoveHandler = e => { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - const location = this.setLocationFromEvent(e); - const movement = [e.movementX, e.movementY]; - this.dragTo(location, movement); - if (this.#trackingMouse) { - this.blueprint.mousePosition = location; + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + 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; + if (options.enablerKey) { + this.#enablerKey = options.enablerKey; + this.#enablerKey.onKeyDown = () => this.#enablerActivated = true; + this.#enablerKey.onKeyUp = () => this.#enablerActivated = false; + this.#enablerKey.consumeEvent = false; + this.#enablerKey.listenEvents(); + this.#enablerActivated = false; } - 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} mouseEvent */ + setLocationFromEvent(mouseEvent) { + let location = Utility.convertLocation( + [mouseEvent.clientX, mouseEvent.clientY], + this.movementSpace, + this.options.ignoreScale + ); + location = this.options.ignoreTranslateCompensate + ? location + : this.blueprint.compensateTranslation(location[0], location[1]); + this.#location = [...location]; + return this.#location + } +} + +/** + * @typedef {import("./IMouseClickDrag.js").Options & { +* }} Options +*/ + +/** + * @template {Element} T + * @extends {IPointing} + */ +class MouseClick extends IPointing { + + static #ignoreEvent = + /** @param {MouseClick} self */ + self => { } + + /** @param {MouseEvent} e */ + #mouseDownHandler = e => { + this.blueprint.setFocused(true); + if (this.enablerKey && !this.enablerActivated) { + return + } + switch (e.button) { + case this.options.clickButton: + // Either doesn't matter or consider the click only when clicking on the target, not descandants + if (!this.options.strictTarget || e.target === e.currentTarget) { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + // Attach the listeners + document.addEventListener("mouseup", this.#mouseUpHandler); + this.setLocationFromEvent(e); + this.clickedPosition[0] = this.location[0]; + this.clickedPosition[1] = this.location[1]; + this.blueprint.mousePosition[0] = this.location[0]; + this.blueprint.mousePosition[1] = this.location[1]; + this.clicked(this.clickedPosition); + } + break + default: + if (!this.options.exitAnyButton) { + this.#mouseUpHandler(e); + } + break } } - /** @param {MouseEvent} e */ + /** @param {MouseEvent} e */ #mouseUpHandler = e => { if (!this.options.exitAnyButton || e.button == this.options.clickButton) { if (this.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 - get draggableElement() { - return this.#draggableElement - } - - clickedOffset = /** @type {Coordinates} */([0, 0]) - clickedPosition = /** @type {Coordinates} */([0, 0]) - lastLocation = /** @type {Coordinates} */([0, 0]) - started = false - stepSize = 1 + clickedPosition = [0, 0] /** * @param {T} target * @param {Blueprint} blueprint * @param {Options} options */ - constructor(target, blueprint, options = {}) { + constructor( + target, + blueprint, + options = {}, + onClick = MouseClick.#ignoreEvent, + onUnclick = MouseClick.#ignoreEvent, + ) { options.clickButton ??= Configuration.mouseClickButton; 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 = Number(options.stepSize ?? Configuration.gridSize); - this.#movementListenedElement = this.options.moveEverywhere ? document.documentElement : this.movementSpace; - this.#draggableElement = /** @type {HTMLElement} */(this.options.draggableElement); - + this.onClick = onClick; + this.onUnclick = onUnclick; this.listenEvents(); } listenEvents() { - super.listenEvents(); - this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler); + this.target.addEventListener("mousedown", this.#mouseDownHandler); if (this.options.clickButton === Configuration.mouseRightClickButton) { - this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()); + this.target.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 - }) + this.target.removeEventListener("mousedown", this.#mouseDownHandler); } /* Subclasses will override the following methods */ clicked(location) { - } - - startDrag(location) { - } - - dragTo(location, offset) { - } - - endDrag() { + this.onClick(this); } unclicked(location) { + this.onUnclick(this); } } -class MouseScrollGraph extends IMouseClickDrag { - - startDrag() { - this.blueprint.scrolling = true; - } - - /** - * @param {Coordinates} location - * @param {Coordinates} movement - */ - dragTo(location, movement) { - this.blueprint.scrollDelta(-movement[0], -movement[1]); - } - - endDrag() { - this.blueprint.scrolling = false; - } -} +/** + * @typedef {import("./IPointing.js").Options & { + * consumeEvent?: Boolean, + * strictTarget?: Boolean, +* }} Options +*/ -/** - * @typedef {import("./IPointing.js").Options & { - * listenOnFocus?: Boolean, - * }} Options - */ - -class MouseTracking extends IPointing { - - /** @type {IPointing} */ - #mouseTracker = null - - /** @param {MouseEvent} e */ - #mousemoveHandler = e => { - e.preventDefault(); - this.setLocationFromEvent(e); - this.blueprint.mousePosition = [...this.location]; - } - - /** @param {CustomEvent} e */ - #trackingMouseStolenHandler = e => { - if (!this.#mouseTracker) { - e.preventDefault(); - this.#mouseTracker = e.detail.tracker; - this.unlistenMouseMove(); - } - } - - /** @param {CustomEvent} e */ - #trackingMouseGaveBackHandler = e => { - if (this.#mouseTracker == e.detail.tracker) { - e.preventDefault(); - this.#mouseTracker = null; - this.listenMouseMove(); - } - } - - /** - * @param {Element} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.listenOnFocus = true; - super(target, blueprint, options); - } - - listenMouseMove() { - this.target.addEventListener("mousemove", this.#mousemoveHandler); - } - - unlistenMouseMove() { - this.target.removeEventListener("mousemove", this.#mousemoveHandler); - } - - listenEvents() { - this.listenMouseMove(); - this.blueprint.addEventListener( - Configuration.trackingMouseEventName.begin, - /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); - this.blueprint.addEventListener( - Configuration.trackingMouseEventName.end, - /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler)); - } - - unlistenEvents() { - this.unlistenMouseMove(); - this.blueprint.removeEventListener( - Configuration.trackingMouseEventName.begin, - /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); - this.blueprint.removeEventListener( - Configuration.trackingMouseEventName.end, - /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler) - ); - } -} +/** + * @template {HTMLElement} T + * @extends {IPointing} + */ +class MouseDbClick extends IPointing { -/** - * @typedef {import("./IMouseClickDrag.js").Options & { - * scrollGraphEdge?: Boolean, - * }} Options - */ - -class Select extends IMouseClickDrag { - - constructor(target, blueprint, options = {}) { - options.scrollGraphEdge ??= true; - super(target, blueprint, options); - this.selectorElement = this.blueprint.template.selectorElement; - } - - startDrag() { - this.selectorElement.beginSelect(this.clickedPosition); - } - - /** - * @param {Coordinates} location - * @param {Coordinates} movement - */ - dragTo(location, movement) { - this.selectorElement.selectTo(location); - } - - endDrag() { - if (this.started) { - this.selectorElement.endSelect(); - } - } - - unclicked() { - if (!this.started) { - this.blueprint.unselectAll(); - } - } -} + /** @param {Coordinates} location */ + static ignoreDbClick = location => { } + + /** @param {MouseEvent} e */ + #mouseDbClickHandler = e => { + if (!this.options.strictTarget || e.target === e.currentTarget) { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + this.clickedPosition = this.setLocationFromEvent(e); + this.blueprint.mousePosition = [...this.clickedPosition]; + this.dbclicked(this.clickedPosition); + } + } -/** - * @typedef {import("../IInput.js").Options & { - * listenOnFocus?: Boolean, - * }} Options - */ - -class Unfocus extends IInput { - - /** @param {MouseEvent} e */ - #clickHandler = e => this.clickedSomewhere(/** @type {HTMLElement} */(e.target)) - - /** - * @param {HTMLElement} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.listenOnFocus = true; - super(target, blueprint, options); - if (this.blueprint.focus) { - document.addEventListener("click", this.#clickHandler); - } - } - - /** @param {HTMLElement} target */ - clickedSomewhere(target) { - // If target is outside the blueprint grid - if (!target.closest("ueb-blueprint")) { - this.blueprint.setFocused(false); - } - } - - listenEvents() { - document.addEventListener("click", this.#clickHandler); - } - - unlistenEvents() { - document.removeEventListener("click", this.#clickHandler); - } + #onDbClick + get onDbClick() { + return this.#onDbClick + } + set onDbClick(value) { + this.#onDbClick = value; + } + + clickedPosition = /** @type {Coordinates} */([0, 0]) + + /** + * @param {T} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + 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 */ + /** @param {Coordinates} location */ + dbclicked(location) { + this.onDbClick(location); + } } /** @template {IElement} ElementT */ @@ -7501,417 +7059,686 @@ class ITemplate { } } -/** @extends ITemplate */ -class BlueprintTemplate extends ITemplate { - - static styleVariables = { - "--ueb-font-size": `${Configuration.fontSize}`, - "--ueb-grid-axis-line-color": `${Configuration.gridAxisLineColor}`, - "--ueb-grid-expand": `${Configuration.expandGridSize}px`, - "--ueb-grid-line-color": `${Configuration.gridLineColor}`, - "--ueb-grid-line-width": `${Configuration.gridLineWidth}px`, - "--ueb-grid-set-line-color": `${Configuration.gridSetLineColor}`, - "--ueb-grid-set": `${Configuration.gridSet}`, - "--ueb-grid-size": `${Configuration.gridSize}px`, - "--ueb-link-min-width": `${Configuration.linkMinWidth}`, - "--ueb-node-radius": `${Configuration.nodeRadius}px`, - } - - #resizeObserver = new ResizeObserver(entries => { - const size = entries.find(entry => entry.target === this.viewportElement)?.devicePixelContentBoxSize?.[0]; - if (size) { - this.viewportSize[0] = size.inlineSize; - this.viewportSize[1] = size.blockSize; - } - }) - - /** @type {Copy} */ - #copyInputObject - - /** @type {Paste} */ - #pasteInputObject - - /** @type {Zoom} */ - #zoomInputObject - - /** @type {HTMLElement} */ headerElement - /** @type {HTMLElement} */ overlayElement - /** @type {HTMLElement} */ viewportElement - /** @type {SelectorElement} */ selectorElement - /** @type {HTMLElement} */ gridElement - /** @type {HTMLElement} */ linksContainerElement - /** @type {HTMLElement} */ nodesContainerElement - viewportSize = [0, 0] - - #setViewportSize() { - - } - - /** @param {Blueprint} element */ - initialize(element) { - super.initialize(element); - this.element.style.cssText = Object.entries(BlueprintTemplate.styleVariables) - .map(([k, v]) => `${k}:${v};`).join(""); - const htmlTemplate = /** @type {HTMLTemplateElement} */( - this.element.querySelector(":scope > template") - )?.content.textContent; - if (htmlTemplate) { - this.element.requestUpdate(); - this.element.updateComplete.then(() => { - this.blueprint.mousePosition = [ - Math.round(this.viewportSize[0] / 2), - Math.round(this.viewportSize[1] / 2), - ]; - this.getPasteInputObject().pasted(htmlTemplate); - this.blueprint.unselectAll(); - }); - } - } - - setup() { - super.setup(); - this.#resizeObserver.observe(this.viewportElement, { - box: "device-pixel-content-box", - }); - const bounding = this.viewportElement.getBoundingClientRect(); - this.viewportSize[0] = bounding.width; - this.viewportSize[1] = bounding.height; - if (this.blueprint.nodes.length > 0) { - this.blueprint.requestUpdate(); - this.blueprint.updateComplete.then(() => this.centerContentInViewport()); - } - } - - cleanup() { - super.cleanup(); - this.#resizeObserver.unobserve(this.viewportElement); - } - - createInputObjects() { - const gridElement = this.element.getGridDOMElement(); - this.#copyInputObject = new Copy(gridElement, this.blueprint); - this.#pasteInputObject = new Paste(gridElement, this.blueprint); - this.#zoomInputObject = new Zoom(gridElement, this.blueprint); - return [ - ...super.createInputObjects(), - this.#copyInputObject, - this.#pasteInputObject, - this.#zoomInputObject, - new Cut(gridElement, this.blueprint), - new KeyboardShortcut(gridElement, this.blueprint, { - activationKeys: Shortcuts.duplicateNodes - }, () => - this.blueprint.template.getPasteInputObject().pasted( - this.blueprint.template.getCopyInputObject().copied() - ) - ), - new KeyboardShortcut(gridElement, this.blueprint, { - activationKeys: Shortcuts.deleteNodes - }, () => this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))), - new KeyboardShortcut(gridElement, this.blueprint, { - activationKeys: Shortcuts.selectAllNodes - }, () => this.blueprint.selectAll()), - new Select(gridElement, this.blueprint, { - clickButton: Configuration.mouseClickButton, - exitAnyButton: true, - moveEverywhere: true, - }), - new MouseScrollGraph(gridElement, this.blueprint, { - clickButton: Configuration.mouseRightClickButton, - exitAnyButton: false, - moveEverywhere: true, - }), - new Unfocus(gridElement, this.blueprint), - new MouseTracking(gridElement, this.blueprint), - new KeyboardEnableZoom(gridElement, this.blueprint), - ] - } - - render() { - return x` -

-
- Zoom ${this.blueprint.zoom == 0 ? "1:1" : (this.blueprint.zoom > 0 ? "+" : "") + this.blueprint.zoom} -
-
-
-
-
-
-
-
- -
-
-
- ` - } - - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.headerElement = this.blueprint.querySelector('.ueb-viewport-header'); - this.overlayElement = this.blueprint.querySelector('.ueb-viewport-overlay'); - this.viewportElement = this.blueprint.querySelector('.ueb-viewport-body'); - this.selectorElement = this.blueprint.querySelector('ueb-selector'); - this.gridElement = this.viewportElement.querySelector(".ueb-grid"); - this.linksContainerElement = this.blueprint.querySelector("[data-links]"); - this.linksContainerElement.append(...this.blueprint.getLinks()); - this.nodesContainerElement = this.blueprint.querySelector("[data-nodes]"); - this.nodesContainerElement.append(...this.blueprint.getNodes()); - this.viewportElement.scroll(Configuration.expandGridSize, Configuration.expandGridSize); - } - - /** @param {PropertyValues} changedProperties */ - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - if (this.headerElement && changedProperties.has("zoom")) { - this.headerElement.classList.add("ueb-zoom-changed"); - this.headerElement.addEventListener( - "animationend", - () => this.headerElement.classList.remove("ueb-zoom-changed") - ); - } - } +/** + * @template {IFromToPositionedElement} T + * @extends {ITemplate} + */ +class IFromToPositionedTemplate extends ITemplate { /** @param {PropertyValues} changedProperties */ - updated(changedProperties) { - super.updated(changedProperties); - if (changedProperties.has("scrollX") || changedProperties.has("scrollY")) { - this.viewportElement.scroll(this.blueprint.scrollX, this.blueprint.scrollY); + update(changedProperties) { + super.update(changedProperties); + const [fromX, fromY, toX, toY] = [ + Math.round(this.element.fromX), + Math.round(this.element.fromY), + Math.round(this.element.toX), + Math.round(this.element.toY), + ]; + const [left, top, width, height] = [ + Math.min(fromX, toX), + Math.min(fromY, toY), + Math.abs(fromX - toX), + Math.abs(fromY - toY), + ]; + if (changedProperties.has("fromX") || changedProperties.has("toX")) { + this.element.style.left = `${left}px`; + this.element.style.width = `${width}px`; } - if (changedProperties.has("zoom")) { - this.blueprint.style.setProperty("--ueb-scale", this.blueprint.getScale()); - const previousZoom = changedProperties.get("zoom"); - const minZoom = Math.min(previousZoom, this.blueprint.zoom); - const maxZoom = Math.max(previousZoom, this.blueprint.zoom); - const classes = Utility.range(minZoom, maxZoom); - const getClassName = v => `ueb-zoom-${v}`; - if (previousZoom < this.blueprint.zoom) { - this.blueprint.classList.remove(...classes.filter(v => v < 0).map(getClassName)); - this.blueprint.classList.add(...classes.filter(v => v > 0).map(getClassName)); - } else { - this.blueprint.classList.remove(...classes.filter(v => v > 0).map(getClassName)); - this.blueprint.classList.add(...classes.filter(v => v < 0).map(getClassName)); - } + if (changedProperties.has("fromY") || changedProperties.has("toY")) { + this.element.style.top = `${top}px`; + this.element.style.height = `${height}px`; } } +} + +/** @extends {IFromToPositionedTemplate} */ +class LinkTemplate extends IFromToPositionedTemplate { - getCommentNodes(justSelected = false) { - return this.blueprint.querySelectorAll( - `ueb-node[data-type="${Configuration.paths.comment}"]${justSelected ? '[data-selected="true"]' : ''}` - + `, ueb-node[data-type="${Configuration.paths.materialGraphNodeComment}"]${justSelected ? '[data-selected="true"]' : ''}` - ) - } - - /** @param {PinReferenceEntity} pinReference */ - getPin(pinReference) { - return /** @type {PinElement} */(this.blueprint.querySelector( - `ueb-node[data-title="${pinReference.objectName}"] ueb-pin[data-id="${pinReference.pinGuid}"]` - )) - } - - getCopyInputObject() { - return this.#copyInputObject - } - - getPasteInputObject() { - return this.#pasteInputObject - } - - getZoomInputObject() { - return this.#zoomInputObject + /** + * Returns a function providing the inverse multiplication y = a / x + q. The value of a and q are calculated using + * the derivative of that function y' = -a / x^2 at the point p (x = p[0] and y = p[1]). This means + * y'(p[0]) = m => -a / p[0]^2 = m => a = -m * p[0]^2. Now, in order to determine q we can use the starting + * function: p[1] = a / p[0] + q => q = p[1] - a / p[0] + * @param {Number} m slope + * @param {Coordinates} p reference point + */ + static decreasingValue(m, p) { + const a = -m * p[0] ** 2; + const q = p[1] - a / p[0]; + /** @param {Number} x */ + return x => a / x + q } /** - * @param {Number} x - * @param {Number} y + * Returns a function providing a clamped line passing through two points. It is clamped after and before the + * points. It is easier explained with the following ascii draw. + * b ______ + * / + * / + * / + * ______/ a */ - isPointVisible(x, y) { - return false + static clampedLine(a, b) { + if (a[0] > b[0]) { + const temp = a; + a = b; + b = temp; + } + const m = (b[1] - a[1]) / (b[0] - a[0]); + const q = a[1] - m * a[0]; + return x => x < a[0] + ? a[1] + : x > b[0] + ? b[1] + : m * x + q } - gridTopVisibilityBoundary() { - return this.blueprint.scaleCorrect(this.blueprint.scrollY) - this.blueprint.translateY - } + static c1DecreasingValue = LinkTemplate.decreasingValue(-0.15, [100, 15]) - gridRightVisibilityBoundary() { - return this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0]) - } + static c2DecreasingValue = LinkTemplate.decreasingValue(-0.05, [500, 130]) - gridBottomVisibilityBoundary() { - return this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1]) + static c2Clamped = LinkTemplate.clampedLine([0, 80], [200, 40]) + + #uniqueId = `ueb-id-${Math.floor(Math.random() * 1E12)}` + + /** @param {Coordinates} location */ + #createKnot = location => { + const knotEntity = new KnotEntity({}, this.element.source.entity); + const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) + .newObject(knotEntity); + knot.setLocation(...this.blueprint.snapToGrid(...location)); + const knotTemplate = /** @type {KnotNodeTemplate} */(knot.template); + this.blueprint.addGraphElement(knot); // Important: keep it before changing existing links + const inputPin = this.element.getInputPin(); + const outputPin = this.element.getOutputPin(); + this.element.source = null; + this.element.destination = null; + const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) + .newObject(outputPin, knotTemplate.inputPin); + this.blueprint.addGraphElement(link); + this.element.source = knotTemplate.outputPin; + this.element.destination = inputPin; } - gridLeftVisibilityBoundary() { - return this.blueprint.scaleCorrect(this.blueprint.scrollX) - this.blueprint.translateX + createInputObjects() { + /** @type {HTMLElement} */ + const linkArea = this.element.querySelector(".ueb-link-area"); + return [ + ...super.createInputObjects(), + new MouseDbClick( + linkArea, + this.blueprint, + undefined, + /** @param {Coordinates} location */ + location => { + location[0] += Configuration.knotOffset[0]; + location[1] += Configuration.knotOffset[1]; + location = Utility.snapToGrid(location[0], location[1], Configuration.gridSize); + this.#createKnot(location); + }, + ), + new MouseClick( + linkArea, + this.blueprint, + { + enablerKey: new KeyboardShortcut(this.blueprint, this.blueprint, { + activationKeys: Shortcuts.enableLinkDelete, + }) + }, + () => this.blueprint.removeGraphElement(this.element), + ), + ] } - centerViewport(x = 0, y = 0, smooth = true) { - const centerX = this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0] / 2); - const centerY = this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1] / 2); - this.blueprint.scrollDelta( - this.blueprint.scaleCorrectReverse(x - centerX), - this.blueprint.scaleCorrectReverse(y - centerY), - smooth - ); + /** @param {PropertyValues} changedProperties */ + willUpdate(changedProperties) { + super.willUpdate(changedProperties); + const sourcePin = this.element.source; + const destinationPin = this.element.destination; + if (changedProperties.has("fromX") || changedProperties.has("toX")) { + const from = this.element.fromX; + const to = this.element.toX; + const isSourceAKnot = sourcePin?.nodeElement.getType() == Configuration.paths.knot; + const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.paths.knot; + if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) { + if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) { + this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin; + } else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) { + this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin; + } + } + if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) { + if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) { + this.element.destination = + /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin; + } else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) { + this.element.destination = + /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin; + } + } + } + const dx = Math.max(Math.abs(this.element.fromX - this.element.toX), 1); + const dy = Math.max(Math.abs(this.element.fromY - this.element.toY), 1); + const width = Math.max(dx, Configuration.linkMinWidth); + // const height = Math.max(Math.abs(link.fromY - link.toY), 1) + const fillRatio = dx / width; + const xInverted = this.element.originatesFromInput + ? this.element.fromX < this.element.toX + : this.element.toX < this.element.fromX; + this.element.startPixels = dx < width // If under minimum width + ? (width - dx) / 2 // Start from half the empty space + : 0; // Otherwise start from the beginning + this.element.startPercentage = xInverted ? this.element.startPixels + fillRatio * 100 : this.element.startPixels; + const c1 = + this.element.startPercentage + + (xInverted + ? LinkTemplate.c1DecreasingValue(width) + : 10 + ) + * fillRatio; + const aspectRatio = dy / Math.max(30, dx); + const c2 = + LinkTemplate.c2Clamped(dx) + * Utility.sigmoidPositive(fillRatio * 1.2 + aspectRatio * 0.5, 1.5, 1.8) + + this.element.startPercentage; + this.element.svgPathD = Configuration.linkRightSVGPath(this.element.startPercentage, c1, c2); } - centerContentInViewport(smooth = true) { - let avgX = 0; - let avgY = 0; - let minX = Number.MAX_SAFE_INTEGER; - let maxX = Number.MIN_SAFE_INTEGER; - let minY = Number.MAX_SAFE_INTEGER; - let maxY = Number.MIN_SAFE_INTEGER; - const nodes = this.blueprint.getNodes(); - for (const node of nodes) { - avgX += node.leftBoundary() + node.rightBoundary(); - avgY += node.topBoundary() + node.bottomBoundary(); - minX = Math.min(minX, node.leftBoundary()); - maxX = Math.max(maxX, node.rightBoundary()); - minY = Math.min(minY, node.topBoundary()); - maxY = Math.max(maxY, node.bottomBoundary()); + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + super.update(changedProperties); + if (changedProperties.has("originatesFromInput")) { + this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0"); } - avgX = Math.round(maxX - minX <= this.viewportSize[0] - ? (maxX + minX) / 2 - : avgX / (2 * nodes.length) - ); - avgY = Math.round(maxY - minY <= this.viewportSize[1] - ? (maxY + minY) / 2 - : avgY / (2 * nodes.length) - ); - this.centerViewport(avgX, avgY, smooth); + const referencePin = this.element.source ?? this.element.destination; + if (referencePin) { + this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color)); + } + this.element.style.setProperty("--ueb-y-reflected", `${this.element.fromY > this.element.toY ? 1 : 0}`); + this.element.style.setProperty("--ueb-start-percentage", `${Math.round(this.element.startPercentage)}%`); + this.element.style.setProperty("--ueb-link-start", `${Math.round(this.element.startPixels)}`); + } + + render() { + return x` + + + + + + ${this.element.linkMessageIcon || this.element.linkMessageText ? x` + + ` : A} + ` } } +/** @extends {IFromToPositionedElement} */ +class LinkElement extends IFromToPositionedElement { + + static properties = { + ...super.properties, + 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} */ + #source + get source() { + return this.#source + } + set source(pin) { + this.#setPin(pin, false); + } + + /** @type {PinElement} */ + #destination + get destination() { + return this.#destination + } + set destination(pin) { + this.#setPin(pin, true); + } + + #nodeDeleteHandler = () => this.remove() + /** @param {UEBDragEvent} e */ + #nodeDragSourceHandler = e => this.addSourceLocation(...e.detail.value) + /** @param {UEBDragEvent} e */ + #nodeDragDestinatonHandler = e => this.addDestinationLocation(...e.detail.value) + #nodeReflowSourceHandler = e => this.setSourceLocation() + #nodeReflowDestinatonHandler = e => this.setDestinationLocation() + + /** @type {TemplateResult | nothing} */ + linkMessageIcon = A + /** @type {TemplateResult | nothing} */ + linkMessageText = A + + /** @type {SVGPathElement} */ + pathElement + + constructor() { + super(); + 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 + */ + // @ts-expect-error + initialize(source, destination) { + super.initialize({}, new LinkTemplate()); + if (source) { + this.source = source; + if (!destination) { + this.toX = this.fromX; + this.toY = this.fromY; + } + } + if (destination) { + this.destination = destination; + if (!source) { + this.fromX = this.toX; + this.fromY = this.toY; + } + } + } + + /** + * @param {PinElement} pin + * @param {Boolean} isDestinationPin + */ + #setPin(pin, isDestinationPin) { + const getCurrentPin = () => isDestinationPin ? this.destination : this.source; + 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.#destination = pin + : this.#source = 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.source.isInput()); + this.#linkPins(); + } + } + + #linkPins() { + if (this.source && this.destination) { + this.source.linkTo(this.destination); + this.destination.linkTo(this.source); + } + } + + #unlinkPins() { + if (this.source && this.destination) { + this.source.unlinkFrom(this.destination, false); + this.destination.unlinkFrom(this.source, false); + } + } + + cleanup() { + super.cleanup(); + this.#unlinkPins(); + this.source = null; + this.destination = null; + } + + /** @param {Coordinates} location */ + setSourceLocation(location = null, canPostpone = true) { + if (location == null) { + const self = this; + if (canPostpone && (!this.hasUpdated || !this.source.hasUpdated)) { + Promise.all([this.updateComplete, this.source.updateComplete]) + .then(() => self.setSourceLocation(null, false)); + return + } + location = this.source.template.getLinkLocation(); + } + const [x, y] = location; + this.fromX = x; + this.fromY = y; + } + + /** @param {Coordinates} location */ + setDestinationLocation(location = null, canPostpone = true) { + if (location == null) { + const self = this; + if (canPostpone && (!this.hasUpdated || !this.destination.hasUpdated)) { + Promise.all([this.updateComplete, this.destination.updateComplete]) + .then(() => self.setDestinationLocation(null, false)); + return + } + location = this.destination.template.getLinkLocation(); + } + this.toX = location[0]; + this.toY = location[1]; + } + + getInputPin() { + if (this.source?.isInput()) { + return this.source + } + return this.destination + } + + /** @param {PinElement} pin */ + setInputPin(pin) { + if (this.source?.isInput()) { + this.source = pin; + } + this.destination = pin; + } + + getOutputPin() { + if (this.destination?.isOutput()) { + return this.destination + } + return this.source + } + + /** @param {PinElement} pin */ + setOutputPin(pin) { + if (this.destination?.isOutput()) { + this.destination = pin; + } + this.source = pin; + } + + startDragging() { + this.dragging = true; + } + + finishDragging() { + this.dragging = false; + } + + removeMessage() { + this.linkMessageIcon = A; + this.linkMessageText = A; + } + + setMessageConvertType() { + this.linkMessageIcon = SVGIcon.convert; + this.linkMessageText = x`Convert ${this.source.pinType} to ${this.destination.pinType}.`; + } + + setMessageCorrect() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = A; + } + + setMessageReplace() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = A; + } + + setMessageDirectionsIncompatible() { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = x`Directions are not compatbile.`; + } + + setMessagePlaceNode() { + this.linkMessageIcon = A; + this.linkMessageText = x`Place a new node.`; + } + + setMessageReplaceLink() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = x`Replace existing input connections.`; + } + + setMessageReplaceOutputLink() { + this.linkMessageIcon = SVGIcon.correct; + this.linkMessageText = x`Replace existing output connections.`; + } + + setMessageSameNode() { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = x`Both are on the same node.`; + } + + /** + * @param {PinElement} a + * @param {PinElement} b + */ + setMessageTypesIncompatible(a, b) { + this.linkMessageIcon = SVGIcon.reject; + this.linkMessageText = + x`${Utility.capitalFirstLetter(a.pinType)} is not compatible with ${Utility.capitalFirstLetter(b.pinType)}.`; + } +} + /** - * @template {IEntity} EntityT - * @template {ITemplate} TemplateT - * @extends {IElement} + * @template {IEntity} T + * @template {IDraggableTemplate} U + * @extends {IElement} */ -class IFromToPositionedElement extends IElement { +class IDraggableElement extends IElement { static properties = { ...super.properties, - fromX: { + locationX: { type: Number, attribute: false, }, - fromY: { + locationY: { type: Number, attribute: false, }, - toX: { + sizeX: { type: Number, attribute: false, }, - toY: { + sizeY: { type: Number, attribute: false, }, } + static dragEventName = Configuration.dragEventName + static dragGeneralEventName = Configuration.dragGeneralEventName constructor() { super(); - this.fromX = 0; - this.fromY = 0; - this.toX = 0; - this.toY = 0; + this.locationX = 0; + this.locationY = 0; + this.sizeX = 0; + this.sizeY = 0; } - /** @param {Coordinates} param0 */ - setBothLocations([x, y]) { - this.fromX = x; - this.fromY = y; - this.toX = x; - this.toY = y; + 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 */ - addSourceLocation(x, y) { - this.fromX += x; - this.fromY += 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 */ - addDestinationLocation(x, y) { - this.toX += x; - this.toY += y; + addLocation(x, y, acknowledge = true) { + this.setLocation(this.locationX + x, this.locationY + y, acknowledge); } -} - -class KnotEntity extends ObjectEntity { - /** - * @param {Object} values - * @param {PinEntity} pinReferenceForType - */ - constructor(values = {}, pinReferenceForType = undefined) { - values.Class = new ObjectReferenceEntity(Configuration.paths.knot); - values.Name = "K2Node_Knot"; - const inputPinEntity = new PinEntity( - { PinName: "InputPin" }, - true - ); - const outputPinEntity = new PinEntity( + /** @param {Coordinates} value */ + acknowledgeDrag(value) { + const dragEvent = new CustomEvent( + /** @type {typeof IDraggableElement} */(this.constructor).dragGeneralEventName, { - PinName: "OutputPin", - Direction: "EGPD_Output", - }, - true + detail: { + value: value + }, + bubbles: true, + cancelable: true + } ); - if (pinReferenceForType) { - inputPinEntity.copyTypeFrom(pinReferenceForType); - outputPinEntity.copyTypeFrom(pinReferenceForType); + 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]); } - values["CustomProperties"] = [inputPinEntity, outputPinEntity]; - super(values, true); + } + + 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("./IMouseClickDrag.js").Options & { -* }} Options -*/ + * @typedef {import("./IPointing.js").Options & { + * clickButton?: Number, + * consumeEvent?: Boolean, + * draggableElement?: HTMLElement, + * exitAnyButton?: Boolean, + * moveEverywhere?: Boolean, + * movementSpace?: HTMLElement, + * repositionOnClick?: Boolean, + * scrollGraphEdge?: Boolean, + * strictTarget?: Boolean, + * stepSize?: Number, + * }} Options + */ /** - * @template {Element} T + * @template {IElement} T * @extends {IPointing} */ -class MouseClick extends IPointing { - - static #ignoreEvent = - /** @param {MouseClick} self */ - self => { } +class IMouseClickDrag extends IPointing { - /** @param {MouseEvent} e */ + /** @param {MouseEvent} e */ #mouseDownHandler = e => { this.blueprint.setFocused(true); - if (this.enablerKey && !this.enablerActivated) { - return - } switch (e.button) { case this.options.clickButton: - // Either doesn't matter or consider the click only when clicking on the target, not descandants - if (!this.options.strictTarget || e.target === e.currentTarget) { + // 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.consumeEvent) { e.stopImmediatePropagation(); // Captured, don't call anyone else } // Attach the listeners + this.#movementListenedElement.addEventListener("mousemove", this.#mouseStartedMovingHandler); document.addEventListener("mouseup", this.#mouseUpHandler); this.setLocationFromEvent(e); this.clickedPosition[0] = this.location[0]; this.clickedPosition[1] = this.location[1]; this.blueprint.mousePosition[0] = this.location[0]; this.blueprint.mousePosition[1] = this.location[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 @@ -7923,1031 +7750,1285 @@ class MouseClick extends IPointing { } } - /** @param {MouseEvent} e */ + /** @param {MouseEvent} e */ + #mouseStartedMovingHandler = e => { + if (this.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; + this.setLocationFromEvent(e); + // Do actual actions + this.lastLocation = Utility.snapToGrid(this.clickedPosition[0], this.clickedPosition[1], this.stepSize); + this.startDrag(this.location); + this.started = true; + this.#mouseMoveHandler(e); + } + + /** @param {MouseEvent} e */ + #mouseMoveHandler = e => { + if (this.consumeEvent) { + e.stopImmediatePropagation(); // Captured, don't call anyone else + } + const location = this.setLocationFromEvent(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.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; } } - clickedPosition = [0, 0] + #trackingMouse = false + #movementListenedElement + #draggableElement + get draggableElement() { + return this.#draggableElement + } + + clickedOffset = /** @type {Coordinates} */([0, 0]) + clickedPosition = /** @type {Coordinates} */([0, 0]) + lastLocation = /** @type {Coordinates} */([0, 0]) + started = false + stepSize = 1 /** * @param {T} target * @param {Blueprint} blueprint * @param {Options} options */ - constructor( - target, - blueprint, - options = {}, - onClick = MouseClick.#ignoreEvent, - onUnclick = MouseClick.#ignoreEvent, - ) { + constructor(target, blueprint, options = {}) { options.clickButton ??= Configuration.mouseClickButton; 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.onClick = onClick; - this.onUnclick = onUnclick; + this.stepSize = Number(options.stepSize ?? Configuration.gridSize); + this.#movementListenedElement = this.options.moveEverywhere ? document.documentElement : this.movementSpace; + this.#draggableElement = /** @type {HTMLElement} */(this.options.draggableElement); + this.listenEvents(); } listenEvents() { - this.target.addEventListener("mousedown", this.#mouseDownHandler); + super.listenEvents(); + this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler); if (this.options.clickButton === Configuration.mouseRightClickButton) { - this.target.addEventListener("contextmenu", e => e.preventDefault()); + this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()); } } unlistenEvents() { - this.target.removeEventListener("mousedown", this.#mouseDownHandler); + 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) { - this.onClick(this); + } + + startDrag(location) { + } + + dragTo(location, offset) { + } + + endDrag() { } unclicked(location) { - this.onUnclick(this); } } -/** - * @typedef {import("./IPointing.js").Options & { - * consumeEvent?: Boolean, - * strictTarget?: Boolean, -* }} Options -*/ +/** @typedef {import("./IMouseClickDrag.js").Options} Options */ + +/** + * @template {IDraggableElement} T + * @extends {IMouseClickDrag} + */ +class MouseMoveDraggable extends IMouseClickDrag { + + /** @param {Coordinates} location */ + clicked(location) { + if (this.options.repositionOnClick) { + this.target.setLocation(...(this.stepSize > 1 + ? Utility.snapToGrid(location[0], location[1], this.stepSize) + : location + )); + this.clickedOffset = [0, 0]; + } + } + + /** + * @param {Coordinates} location + * @param {Coordinates} offset + */ + dragTo(location, offset) { + const targetLocation = [ + this.target.locationX ?? this.lastLocation[0], + this.target.locationY ?? this.lastLocation[1], + ]; + const [adjustedLocation, adjustedTargetLocation] = this.stepSize > 1 + ? [ + Utility.snapToGrid(location[0], location[1], this.stepSize), + Utility.snapToGrid(targetLocation[0], targetLocation[1], this.stepSize) + ] + : [location, targetLocation]; + offset = [ + adjustedLocation[0] - this.lastLocation[0], + adjustedLocation[1] - this.lastLocation[1], + ]; + if (offset[0] == 0 && offset[1] == 0) { + return + } + // Make sure it snaps on the grid + offset[0] += adjustedTargetLocation[0] - targetLocation[0]; + offset[1] += adjustedTargetLocation[1] - targetLocation[1]; + this.dragAction(adjustedLocation, offset); + // Reassign the position of mouse + this.lastLocation = adjustedLocation; + } + + /** + * @param {Coordinates} location + * @param {Coordinates} offset + */ + dragAction(location, offset) { + this.target.setLocation(location[0] - this.clickedOffset[0], location[1] - this.clickedOffset[1]); + } +} + +/** + * @typedef {import("./MouseMoveDraggable.js").Options & { + * onClicked?: () => void, + * onStartDrag?: () => void, + * onDrag?: (location: Coordinates, movement: Coordinates) => void, + * onEndDrag?: () => void, +* }} Options +*/ + +class MouseClickDrag extends MouseMoveDraggable { + + #onClicked + #onStartDrag + #onDrag + #onEndDrag + + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + super(target, blueprint, options); + if (options.onClicked) { + this.#onClicked = options.onClicked; + } + if (options.onStartDrag) { + this.#onStartDrag = options.onStartDrag; + } + if (options.onDrag) { + this.#onDrag = options.onDrag; + } + if (options.onEndDrag) { + this.#onEndDrag = options.onEndDrag; + } + } + + /** @param {Coordinates} location */ + clicked(location) { + super.clicked(location); + this.#onClicked?.(); + } + + startDrag() { + super.startDrag(); + this.#onStartDrag?.(); + } + + /** + * @param {Coordinates} location + * @param {Coordinates} movement + */ + dragAction(location, movement) { + this.#onDrag?.(location, movement); + } + + endDrag() { + super.endDrag(); + this.#onEndDrag?.(); + } +} + +/** @typedef {import("./IMouseClickDrag.js").Options} Options */ + +/** @extends {MouseMoveDraggable} */ +class MouseMoveNodes extends MouseMoveDraggable { + + /** + * @param {NodeElement} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + super(target, blueprint, options); + this.draggableElement.classList.add("ueb-draggable"); + } + + startDrag() { + if (!this.target.selected) { + this.blueprint.unselectAll(); + this.target.setSelected(true); + } + } + + dragAction(location, offset) { + this.target.acknowledgeDrag(offset); + } + + unclicked() { + if (!this.started) { + this.blueprint.unselectAll(); + this.target.setSelected(true); + } else { + this.blueprint.getNodes(true).forEach(node => + node.boundComments + .filter(comment => !node.isInsideComment(comment)) + .forEach(comment => node.unbindFromComment(comment)) + ); + this.blueprint.getCommentNodes().forEach(comment => + /** @type {CommentNodeTemplate} */(comment.template).manageNodesBind() + ); + } + } +} + +/** + * @template {IDraggableElement} T + * @extends {ITemplate} + */ +class IDraggableTemplate extends ITemplate { + + /** @returns {HTMLElement} */ + getDraggableElement() { + return this.element + } + + createDraggableObject() { + const draggableElement = this.getDraggableElement(); + return new MouseMoveDraggable(this.element, this.blueprint, { draggableElement }) + } + + createInputObjects() { + return [ + ...super.createInputObjects(), + this.createDraggableObject(), + new KeyboardShortcut( + this.element, + this.blueprint, + { + activationKeys: [ + Configuration.Keys.ArrowUp, + Configuration.Keys.ArrowRight, + Configuration.Keys.ArrowDown, + Configuration.Keys.ArrowLeft, + ] + }, + self => self.target.acknowledgeDrag([ + self.pressedKey === Configuration.Keys.ArrowLeft + ? -Configuration.gridSize + : self.pressedKey === Configuration.Keys.ArrowRight + ? Configuration.gridSize + : 0, + self.pressedKey === Configuration.Keys.ArrowUp + ? -Configuration.gridSize + : self.pressedKey === Configuration.Keys.ArrowDown + ? Configuration.gridSize + : 0, + ]) + ) + ] + } + + topBoundary(justSelectableArea = false) { + return this.element.locationY + } + + rightBoundary(justSelectableArea = false) { + return this.element.locationX + this.element.sizeX + } + + bottomBoundary(justSelectableArea = false) { + return this.element.locationY + this.element.sizeY + } + + leftBoundary(justSelectableArea = false) { + return this.element.locationX + } + + centerInViewport() { + const minMargin = Math.min( + this.blueprint.template.viewportSize[0] / 10, + this.blueprint.template.viewportSize[1] / 10 + ); + const dl = this.leftBoundary() - this.blueprint.template.gridLeftVisibilityBoundary(); + const dr = this.blueprint.template.gridRightVisibilityBoundary() - this.rightBoundary(); + let avgX = Math.max((dl + dr) / 2, minMargin); + const dt = this.topBoundary() - this.blueprint.template.gridTopVisibilityBoundary(); + const db = this.blueprint.template.gridBottomVisibilityBoundary() - this.bottomBoundary(); + let avgY = Math.max((dt + db) / 2, minMargin); + this.blueprint.scrollDelta(dl - avgX, dt - avgY, true); + } +} /** - * @template {HTMLElement} T - * @extends {IPointing} + * @template {IDraggableElement} T + * @extends {IDraggableTemplate} */ -class MouseDbClick extends IPointing { - - /** @param {Coordinates} location */ - static ignoreDbClick = location => { } +class IDraggablePositionedTemplate extends IDraggableTemplate { - /** @param {MouseEvent} e */ - #mouseDbClickHandler = e => { - if (!this.options.strictTarget || e.target === e.currentTarget) { - if (this.consumeEvent) { - e.stopImmediatePropagation(); // Captured, don't call anyone else - } - this.clickedPosition = this.setLocationFromEvent(e); - this.blueprint.mousePosition = [...this.clickedPosition]; - this.dbclicked(this.clickedPosition); + /** @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`; } - } - - #onDbClick - get onDbClick() { - return this.#onDbClick - } - set onDbClick(value) { - this.#onDbClick = value; - } - - clickedPosition = /** @type {Coordinates} */([0, 0]) - - /** - * @param {T} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - 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 */ - /** @param {Coordinates} location */ - dbclicked(location) { - this.onDbClick(location); } } /** - * @template {IFromToPositionedElement} T - * @extends {ITemplate} + * @template {NodeElement} T + * @extends {IDraggablePositionedTemplate} */ -class IFromToPositionedTemplate extends ITemplate { +class ISelectableDraggableTemplate extends IDraggablePositionedTemplate { + + /** @returns {HTMLElement} */ + getDraggableElement() { + return this.element + } + + createDraggableObject() { + return /** @type {MouseMoveDraggable} */(new MouseMoveNodes(this.element, this.blueprint, { + draggableElement: this.getDraggableElement(), + scrollGraphEdge: true, + })) + } /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - const [fromX, fromY, toX, toY] = [ - Math.round(this.element.fromX), - Math.round(this.element.fromY), - Math.round(this.element.toX), - Math.round(this.element.toY), - ]; - const [left, top, width, height] = [ - Math.min(fromX, toX), - Math.min(fromY, toY), - Math.abs(fromX - toX), - Math.abs(fromY - toY), - ]; - if (changedProperties.has("fromX") || changedProperties.has("toX")) { - this.element.style.left = `${left}px`; - this.element.style.width = `${width}px`; - } - if (changedProperties.has("fromY") || changedProperties.has("toY")) { - this.element.style.top = `${top}px`; - this.element.style.height = `${height}px`; + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + if (this.element.selected && !this.element.listeningDrag) { + this.element.setSelected(true); } } } -/** @extends {IFromToPositionedTemplate} */ -class LinkTemplate extends IFromToPositionedTemplate { +/** @extends {ISelectableDraggableTemplate} */ +class NodeTemplate extends ISelectableDraggableTemplate { - /** - * Returns a function providing the inverse multiplication y = a / x + q. The value of a and q are calculated using - * the derivative of that function y' = -a / x^2 at the point p (x = p[0] and y = p[1]). This means - * y'(p[0]) = m => -a / p[0]^2 = m => a = -m * p[0]^2. Now, in order to determine q we can use the starting - * function: p[1] = a / p[0] + q => q = p[1] - a / p[0] - * @param {Number} m slope - * @param {Coordinates} p reference point - */ - static decreasingValue(m, p) { - const a = -m * p[0] ** 2; - const q = p[1] - a / p[0]; - /** @param {Number} x */ - return x => a / x + q + static nodeStyleClasses = ["ueb-node-style-default"] + + #hasSubtitle = false + + /** @type {() => PinEntity} */ + pinInserter + + /** @type {HTMLElement} */ + inputContainer + + /** @type {HTMLElement} */ + outputContainer + + /** @type {PinElement} */ + pinElement + + addPinHandler = () => { + const pin = this.pinInserter?.(); + if (pin) { + if (this.defaultPin && this.defaultPin.isInput() === pin.isInput()) { + this.defaultPin.before(this.createPinElement(pin)); + } else { + (pin.isInput() ? this.inputContainer : this.outputContainer).appendChild(this.createPinElement(pin)); + } + this.element.acknowledgeReflow(); + } + } + + toggleAdvancedDisplayHandler = () => { + this.element.toggleShowAdvancedPinDisplay(); + this.element.requestUpdate(); + this.element.updateComplete.then(() => this.element.acknowledgeReflow()); + } + + /** @param {PinEntity} pinEntity */ + createPinElement(pinEntity) { + const pinElement = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) + .newObject(pinEntity, undefined, this.element); + if (this.pinInserter && !this.defaultPin && pinElement.getPinName() === "Default") { + this.defaultPin = pinElement; + this.defaultPin.classList.add("ueb-node-variadic-default"); + } + return pinElement } - /** - * Returns a function providing a clamped line passing through two points. It is clamped after and before the - * points. It is easier explained with the following ascii draw. - * b ______ - * / - * / - * / - * ______/ a - */ - static clampedLine(a, b) { - if (a[0] > b[0]) { - const temp = a; - a = b; - b = temp; + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + this.element.classList.add(.../** @type {typeof NodeTemplate} */(this.constructor).nodeStyleClasses); + this.element.style.setProperty("--ueb-node-color", this.getColor().cssText); + this.pinInserter = this.element.entity.additionalPinInserter(); + if (this.pinInserter) { + this.element.classList.add("ueb-node-is-variadic"); } - const m = (b[1] - a[1]) / (b[0] - a[0]); - const q = a[1] - m * a[0]; - return x => x < a[0] - ? a[1] - : x > b[0] - ? b[1] - : m * x + q } - static c1DecreasingValue = LinkTemplate.decreasingValue(-0.15, [100, 15]) - - static c2DecreasingValue = LinkTemplate.decreasingValue(-0.05, [500, 130]) + getColor() { + return this.element.entity.nodeColor() + } - static c2Clamped = LinkTemplate.clampedLine([0, 80], [200, 40]) + render() { + return x` +
+
+
${this.renderTop()}
+
+
+ ${this.pinInserter ? x` +
+ Add pin ${SVGIcon.plusCircle} +
+ `: A} + ${this.element.entity.isDevelopmentOnly() ? x` +
+ Development Only +
+ ` : A} + ${this.element.advancedPinDisplay ? x` +
+ ${SVGIcon.expandIcon} +
+ ` : A} +
+
+ ` + } - #uniqueId = `ueb-id-${Math.floor(Math.random() * 1E12)}` + renderNodeIcon() { + return this.element.entity.nodeIcon() + } - /** @param {Coordinates} location */ - #createKnot = location => { - const knotEntity = new KnotEntity({}, this.element.source.entity); - const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) - .newObject(knotEntity); - knot.setLocation(...this.blueprint.snapToGrid(...location)); - const knotTemplate = /** @type {KnotNodeTemplate} */(knot.template); - this.blueprint.addGraphElement(knot); // Important: keep it before changing existing links - const inputPin = this.element.getInputPin(); - const outputPin = this.element.getOutputPin(); - this.element.source = null; - this.element.destination = null; - const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) - .newObject(outputPin, knotTemplate.inputPin); - this.blueprint.addGraphElement(link); - this.element.source = knotTemplate.outputPin; - this.element.destination = inputPin; + renderNodeName() { + return this.element.nodeDisplayName } - createInputObjects() { - /** @type {HTMLElement} */ - const linkArea = this.element.querySelector(".ueb-link-area"); - return [ - ...super.createInputObjects(), - new MouseDbClick( - linkArea, - this.blueprint, - undefined, - /** @param {Coordinates} location */ - location => { - location[0] += Configuration.knotOffset[0]; - location[1] += Configuration.knotOffset[1]; - location = Utility.snapToGrid(location[0], location[1], Configuration.gridSize); - this.#createKnot(location); - }, - ), - new MouseClick( - linkArea, - this.blueprint, - { - enablerKey: new KeyboardShortcut(this.blueprint, this.blueprint, { - activationKeys: Shortcuts.enableLinkDelete, - }) - }, - () => this.blueprint.removeGraphElement(this.element), - ), - ] + renderTop() { + const icon = this.renderNodeIcon(); + const name = this.renderNodeName(); + return x` +
+ ${icon ? x` +
${icon}
+ ` : A} + ${name ? x` +
+ ${name} + ${this.#hasSubtitle && this.getTargetType().length > 0 ? x` +
+ Target is ${Utility.formatStringName(this.getTargetType())} +
+ `: A} +
+ ` : A} +
+ ` } /** @param {PropertyValues} changedProperties */ - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - const sourcePin = this.element.source; - const destinationPin = this.element.destination; - if (changedProperties.has("fromX") || changedProperties.has("toX")) { - const from = this.element.fromX; - const to = this.element.toX; - const isSourceAKnot = sourcePin?.nodeElement.getType() == Configuration.paths.knot; - const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.paths.knot; - if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) { - if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) { - this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin; - } else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) { - this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin; - } + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.inputContainer = this.element.querySelector(".ueb-node-inputs"); + this.outputContainer = this.element.querySelector(".ueb-node-outputs"); + this.setupPins(); + this.element.updateComplete.then(() => this.element.acknowledgeReflow()); + } + + setupPins() { + this.element.nodeNameElement = /** @type {HTMLElement} */(this.element.querySelector(".ueb-node-name-text")); + let hasInput = false; + let hasOutput = false; + for (const p of this.element.getPinElements()) { + if (p === this.defaultPin) { + continue } - if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) { - if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) { - this.element.destination = - /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin; - } else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) { - this.element.destination = - /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin; - } + if (p.isInput()) { + this.inputContainer.appendChild(p); + hasInput = true; + } else if (p.isOutput()) { + this.outputContainer.appendChild(p); + hasOutput = true; } } - const dx = Math.max(Math.abs(this.element.fromX - this.element.toX), 1); - const dy = Math.max(Math.abs(this.element.fromY - this.element.toY), 1); - const width = Math.max(dx, Configuration.linkMinWidth); - // const height = Math.max(Math.abs(link.fromY - link.toY), 1) - const fillRatio = dx / width; - const xInverted = this.element.originatesFromInput - ? this.element.fromX < this.element.toX - : this.element.toX < this.element.fromX; - this.element.startPixels = dx < width // If under minimum width - ? (width - dx) / 2 // Start from half the empty space - : 0; // Otherwise start from the beginning - this.element.startPercentage = xInverted ? this.element.startPixels + fillRatio * 100 : this.element.startPixels; - const c1 = - this.element.startPercentage - + (xInverted - ? LinkTemplate.c1DecreasingValue(width) - : 10 - ) - * fillRatio; - const aspectRatio = dy / Math.max(30, dx); - const c2 = - LinkTemplate.c2Clamped(dx) - * Utility.sigmoidPositive(fillRatio * 1.2 + aspectRatio * 0.5, 1.5, 1.8) - + this.element.startPercentage; - this.element.svgPathD = Configuration.linkRightSVGPath(this.element.startPercentage, c1, c2); - } - - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - if (changedProperties.has("originatesFromInput")) { - this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0"); + if (this.defaultPin) { + (this.defaultPin.isInput() ? this.inputContainer : this.outputContainer).appendChild(this.defaultPin); } - const referencePin = this.element.source ?? this.element.destination; - if (referencePin) { - this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color)); + if (hasInput) { + this.element.classList.add("ueb-node-has-inputs"); + } + if (hasOutput) { + this.element.classList.add("ueb-node-has-outputs"); } - this.element.style.setProperty("--ueb-y-reflected", `${this.element.fromY > this.element.toY ? 1 : 0}`); - this.element.style.setProperty("--ueb-start-percentage", `${Math.round(this.element.startPercentage)}%`); - this.element.style.setProperty("--ueb-link-start", `${Math.round(this.element.startPixels)}`); } - render() { - return x` - - - - - - ${this.element.linkMessageIcon || this.element.linkMessageText ? x` - - ` : A} - ` + createPinElements() { + return this.element.getPinEntities() + .filter(v => !v.isHidden()) + .map(pinEntity => { + this.#hasSubtitle = this.#hasSubtitle + || pinEntity.PinName === "self" && pinEntity.pinTitle() === "Target"; + return this.createPinElement(pinEntity) + }) + } + + getTargetType() { + return this.element.entity.FunctionReference?.MemberParent?.getName() ?? "Untitled" + } + + /** + * @param {NodeElement} node + * @returns {NodeListOf} + */ + getPinElements(node) { + return node.querySelectorAll("ueb-pin") } + + linksChanged() { } } -/** @extends {IFromToPositionedElement} */ -class LinkElement extends IFromToPositionedElement { - - static properties = { - ...super.properties, - 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} */ - #source - get source() { - return this.#source - } - set source(pin) { - this.#setPin(pin, false); - } - - /** @type {PinElement} */ - #destination - get destination() { - return this.#destination - } - set destination(pin) { - this.#setPin(pin, true); - } - - #nodeDeleteHandler = () => this.remove() - /** @param {UEBDragEvent} e */ - #nodeDragSourceHandler = e => this.addSourceLocation(...e.detail.value) - /** @param {UEBDragEvent} e */ - #nodeDragDestinatonHandler = e => this.addDestinationLocation(...e.detail.value) - #nodeReflowSourceHandler = e => this.setSourceLocation() - #nodeReflowDestinatonHandler = e => this.setDestinationLocation() - - /** @type {TemplateResult | nothing} */ - linkMessageIcon = A - /** @type {TemplateResult | nothing} */ - linkMessageText = A - - /** @type {SVGPathElement} */ - pathElement - - constructor() { - super(); - 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 - */ - // @ts-expect-error - initialize(source, destination) { - super.initialize({}, new LinkTemplate()); - if (source) { - this.source = source; - if (!destination) { - this.toX = this.fromX; - this.toY = this.fromY; - } - } - if (destination) { - this.destination = destination; - if (!source) { - this.fromX = this.toX; - this.fromY = this.toY; - } - } - } - - /** - * @param {PinElement} pin - * @param {Boolean} isDestinationPin - */ - #setPin(pin, isDestinationPin) { - const getCurrentPin = () => isDestinationPin ? this.destination : this.source; - 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.#destination = pin - : this.#source = 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.source.isInput()); - this.#linkPins(); - } - } - - #linkPins() { - if (this.source && this.destination) { - this.source.linkTo(this.destination); - this.destination.linkTo(this.source); - } - } - - #unlinkPins() { - if (this.source && this.destination) { - this.source.unlinkFrom(this.destination, false); - this.destination.unlinkFrom(this.source, false); - } - } - - cleanup() { - super.cleanup(); - this.#unlinkPins(); - this.source = null; - this.destination = null; - } - - /** @param {Coordinates} location */ - setSourceLocation(location = null, canPostpone = true) { - if (location == null) { - const self = this; - if (canPostpone && (!this.hasUpdated || !this.source.hasUpdated)) { - Promise.all([this.updateComplete, this.source.updateComplete]) - .then(() => self.setSourceLocation(null, false)); - return - } - location = this.source.template.getLinkLocation(); - } - const [x, y] = location; - this.fromX = x; - this.fromY = y; - } - - /** @param {Coordinates} location */ - setDestinationLocation(location = null, canPostpone = true) { - if (location == null) { - const self = this; - if (canPostpone && (!this.hasUpdated || !this.destination.hasUpdated)) { - Promise.all([this.updateComplete, this.destination.updateComplete]) - .then(() => self.setDestinationLocation(null, false)); - return - } - location = this.destination.template.getLinkLocation(); - } - this.toX = location[0]; - this.toY = location[1]; - } +class IResizeableTemplate extends NodeTemplate { - getInputPin() { - if (this.source?.isInput()) { - return this.source - } - return this.destination - } + #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 {PinElement} pin */ - setInputPin(pin) { - if (this.source?.isInput()) { - this.source = pin; - } - this.destination = pin; + /** @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"); } - getOutputPin() { - if (this.destination?.isOutput()) { - return this.destination + /** @param {PropertyValues} changedProperties */ + update(changedProperties) { + super.update(changedProperties); + if (this.element.sizeX >= 0 && changedProperties.has("sizeX")) { + this.element.style.width = `${this.element.sizeX}px`; } - return this.source - } - - /** @param {PinElement} pin */ - setOutputPin(pin) { - if (this.destination?.isOutput()) { - this.destination = pin; + if (this.element.sizeY >= 0 && changedProperties.has("sizeY")) { + this.element.style.height = `${this.element.sizeY}px`; } - this.source = pin; } - startDragging() { - this.dragging = true; + /** @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 + ); } - finishDragging() { - this.dragging = false; + 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(), + }), + ] } - removeMessage() { - this.linkMessageIcon = A; - this.linkMessageText = A; + /** @param {Number} value */ + setSizeX(value) { + this.element.setNodeWidth(value); + return true } - setMessageConvertType() { - this.linkMessageIcon = SVGIcon.convert; - this.linkMessageText = x`Convert ${this.source.pinType} to ${this.destination.pinType}.`; + /** @param {Number} value */ + setSizeY(value) { + this.element.setNodeHeight(value); + return true } - setMessageCorrect() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = A; + endResize() { } +} - setMessageReplace() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = A; - } +class CommentNodeTemplate extends IResizeableTemplate { - setMessageDirectionsIncompatible() { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = x`Directions are not compatbile.`; + #selectableAreaHeight = 0 + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + 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 } - setMessagePlaceNode() { - this.linkMessageIcon = A; - this.linkMessageText = x`Place a new node.`; + /** @returns {HTMLElement} */ + getDraggableElement() { + return this.element.querySelector(".ueb-node-top") } - setMessageReplaceLink() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = x`Replace existing input connections.`; + render() { + return x` +
+
+
+
+
+
+ ` } - setMessageReplaceOutputLink() { - this.linkMessageIcon = SVGIcon.correct; - this.linkMessageText = x`Replace existing output connections.`; + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + const bounding = this.getDraggableElement().getBoundingClientRect(); + this.#selectableAreaHeight = bounding.height; } - setMessageSameNode() { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = x`Both are on the same node.`; + 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 {PinElement} a - * @param {PinElement} b - */ - setMessageTypesIncompatible(a, b) { - this.linkMessageIcon = SVGIcon.reject; - this.linkMessageText = - x`${Utility.capitalFirstLetter(a.pinType)} is not compatible with ${Utility.capitalFirstLetter(b.pinType)}.`; + /** @param {Number} value */ + setSizeX(value) { + value = Math.round(value); + if (value >= 2 * Configuration.gridSize) { + this.element.setNodeWidth(value); + return true + } + return false } -} -/** @typedef {import("./IMouseClickDrag.js").Options} Options */ - -/** - * @template {IDraggableElement} T - * @extends {IMouseClickDrag} - */ -class MouseMoveDraggable extends IMouseClickDrag { - - /** @param {Coordinates} location */ - clicked(location) { - if (this.options.repositionOnClick) { - this.target.setLocation(...(this.stepSize > 1 - ? Utility.snapToGrid(location[0], location[1], this.stepSize) - : location - )); - this.clickedOffset = [0, 0]; - } - } - - /** - * @param {Coordinates} location - * @param {Coordinates} offset - */ - dragTo(location, offset) { - const targetLocation = [ - this.target.locationX ?? this.lastLocation[0], - this.target.locationY ?? this.lastLocation[1], - ]; - const [adjustedLocation, adjustedTargetLocation] = this.stepSize > 1 - ? [ - Utility.snapToGrid(location[0], location[1], this.stepSize), - Utility.snapToGrid(targetLocation[0], targetLocation[1], this.stepSize) - ] - : [location, targetLocation]; - offset = [ - adjustedLocation[0] - this.lastLocation[0], - adjustedLocation[1] - this.lastLocation[1], - ]; - if (offset[0] == 0 && offset[1] == 0) { - return - } - // Make sure it snaps on the grid - offset[0] += adjustedTargetLocation[0] - targetLocation[0]; - offset[1] += adjustedTargetLocation[1] - targetLocation[1]; - this.dragAction(adjustedLocation, offset); - // Reassign the position of mouse - this.lastLocation = adjustedLocation; - } - - /** - * @param {Coordinates} location - * @param {Coordinates} offset - */ - dragAction(location, offset) { - this.target.setLocation(location[0] - this.clickedOffset[0], location[1] - this.clickedOffset[1]); - } + /** @param {Number} value */ + setSizeY(value) { + value = Math.round(value); + if (value >= 2 * 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("./MouseMoveDraggable.js").Options & { - * onClicked?: () => void, - * onStartDrag?: () => void, - * onDrag?: (location: Coordinates, movement: Coordinates) => void, - * onEndDrag?: () => void, + * @typedef {import("./IMouseClickDrag.js").Options & { + * scrollGraphEdge?: Boolean, * }} Options */ -class MouseClickDrag extends MouseMoveDraggable { +/** @extends IMouseClickDrag */ +class MouseCreateLink extends IMouseClickDrag { - #onClicked - #onStartDrag - #onDrag - #onEndDrag + /** @type {NodeListOf} */ + #listenedPins - /** - * @param {HTMLElement} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - super(target, blueprint, options); - if (options.onClicked) { - this.#onClicked = options.onClicked; - } - if (options.onStartDrag) { - this.#onStartDrag = options.onStartDrag; - } - if (options.onDrag) { - this.#onDrag = options.onDrag; - } - if (options.onEndDrag) { - this.#onEndDrag = options.onEndDrag; - } - } + /** @type {PinElement} */ + #knotPin = null - /** @param {Coordinates} location */ - clicked(location) { - super.clicked(location); - this.#onClicked?.(); + /** @param {MouseEvent} e */ + #mouseenterHandler = e => { + if (!this.enteredPin) { + this.linkValid = false; + this.enteredPin = /** @type {PinElement} */(e.target); + const a = this.link.source ?? this.target; // Remember target might have change + const b = this.enteredPin; + const outputPin = a.isOutput() ? a : b; + if ( + a.nodeElement.getType() === Configuration.paths.knot + || b.nodeElement.getType() === Configuration.paths.knot + ) { + // A knot can be linked to any pin, it doesn't matter the type or input/output direction + this.link.setMessageCorrect(); + this.linkValid = true; + } else if (a.getNodeElement() === b.getNodeElement()) { + this.link.setMessageSameNode(); + } else if (a.isOutput() === b.isOutput()) { + this.link.setMessageDirectionsIncompatible(); + } else if (this.blueprint.getLinks(a, b).length) { + this.link.setMessageReplaceLink(); + this.linkValid = true; + } else if (outputPin.entity.getType() === "exec" && outputPin.isLinked) { + this.link.setMessageReplaceOutputLink(); + this.linkValid = true; + } else if ( + (a.entity.PinType.PinCategory != "object" || b.entity.PinType.PinCategory != "object") + && a.pinType != b.pinType + ) { + this.link.setMessageTypesIncompatible(a, b); + this.linkValid = false; + } else { + this.link.setMessageCorrect(); + this.linkValid = true; + } + } } - startDrag() { - super.startDrag(); - this.#onStartDrag?.(); + /** @param {MouseEvent} e */ + #mouseleaveHandler = e => { + if (this.enteredPin == e.target) { + this.enteredPin = null; + this.linkValid = false; + this.link?.setMessagePlaceNode(); + } } - /** - * @param {Coordinates} location - * @param {Coordinates} movement - */ - dragAction(location, movement) { - this.#onDrag?.(location, movement); - } + /** @type {LinkElement?} */ + link - endDrag() { - super.endDrag(); - this.#onEndDrag?.(); - } -} - -/** @typedef {import("./IMouseClickDrag.js").Options} Options */ + /** @type {PinElement?} */ + enteredPin -/** @extends {MouseMoveDraggable} */ -class MouseMoveNodes extends MouseMoveDraggable { + linkValid = false /** - * @param {NodeElement} target + * @param {PinElement} target * @param {Blueprint} blueprint * @param {Options} options */ constructor(target, blueprint, options = {}) { + options.scrollGraphEdge ??= true; super(target, blueprint, options); - this.draggableElement.classList.add("ueb-draggable"); - } - - startDrag() { - if (!this.target.selected) { - this.blueprint.unselectAll(); - this.target.setSelected(true); - } - } - - dragAction(location, offset) { - this.target.acknowledgeDrag(offset); } - unclicked() { - if (!this.started) { - this.blueprint.unselectAll(); - this.target.setSelected(true); - } else { - this.blueprint.getNodes(true).forEach(node => - node.boundComments - .filter(comment => !node.isInsideComment(comment)) - .forEach(comment => node.unbindFromComment(comment)) - ); - this.blueprint.getCommentNodes().forEach(comment => - /** @type {CommentNodeTemplate} */(comment.template).manageNodesBind() - ); + startDrag(location) { + if (this.target.nodeElement.getType() == Configuration.paths.knot) { + this.#knotPin = this.target; } + /** @type {LinkElement} */ + this.link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) + .newObject(this.target, null); + this.blueprint.template.linksContainerElement.prepend(this.link); + this.link.setMessagePlaceNode(); + this.#listenedPins = this.blueprint.querySelectorAll("ueb-pin"); + this.#listenedPins.forEach(pin => { + if (pin != this.target) { + pin.addEventListener("mouseenter", this.#mouseenterHandler); + pin.addEventListener("mouseleave", this.#mouseleaveHandler); + } + }); + this.link.startDragging(); + this.link.setDestinationLocation(location); + } + + dragTo(location, movement) { + this.link.setDestinationLocation(location); + } + + endDrag() { + this.#listenedPins.forEach(pin => { + pin.removeEventListener("mouseenter", this.#mouseenterHandler); + pin.removeEventListener("mouseleave", this.#mouseleaveHandler); + }); + this.#listenedPins = null; + if (this.enteredPin && this.linkValid) { + if (this.#knotPin) { + const otherPin = this.#knotPin !== this.link.source ? this.link.source : this.enteredPin; + // Knot pin direction correction + if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) { + const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin(); + if (this.#knotPin === this.link.source) { + this.link.source = oppositePin; + } else { + this.enteredPin = oppositePin; + } + } + } else if (this.enteredPin.nodeElement.getType() === Configuration.paths.knot) { + this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin(); + } + if (!this.link.source.getLinks().find(ref => ref.equals(this.enteredPin.createPinReference()))) { + this.blueprint.addGraphElement(this.link); + this.link.destination = this.enteredPin; + } else { + this.link.remove(); + } + } else { + this.link.remove(); + } + this.enteredPin = null; + this.link.removeMessage(); + this.link.finishDragging(); + this.link = null; } } -/** - * @template {IDraggableElement} T - * @extends {ITemplate} - */ -class IDraggableTemplate extends ITemplate { - - /** @returns {HTMLElement} */ - getDraggableElement() { - return this.element - } - - createDraggableObject() { - const draggableElement = this.getDraggableElement(); - return new MouseMoveDraggable(this.element, this.blueprint, { draggableElement }) - } - - createInputObjects() { - return [ - ...super.createInputObjects(), - this.createDraggableObject(), - new KeyboardShortcut( - this.element, - this.blueprint, - { - activationKeys: [ - Configuration.Keys.ArrowUp, - Configuration.Keys.ArrowRight, - Configuration.Keys.ArrowDown, - Configuration.Keys.ArrowLeft, - ] - }, - self => self.target.acknowledgeDrag([ - self.pressedKey === Configuration.Keys.ArrowLeft - ? -Configuration.gridSize - : self.pressedKey === Configuration.Keys.ArrowRight - ? Configuration.gridSize - : 0, - self.pressedKey === Configuration.Keys.ArrowUp - ? -Configuration.gridSize - : self.pressedKey === Configuration.Keys.ArrowDown - ? Configuration.gridSize - : 0, - ]) - ) - ] - } - - topBoundary(justSelectableArea = false) { - return this.element.locationY - } - - rightBoundary(justSelectableArea = false) { - return this.element.locationX + this.element.sizeX - } - - bottomBoundary(justSelectableArea = false) { - return this.element.locationY + this.element.sizeY - } - - leftBoundary(justSelectableArea = false) { - return this.element.locationX - } - - centerInViewport() { - const minMargin = Math.min( - this.blueprint.template.viewportSize[0] / 10, - this.blueprint.template.viewportSize[1] / 10 - ); - const dl = this.leftBoundary() - this.blueprint.template.gridLeftVisibilityBoundary(); - const dr = this.blueprint.template.gridRightVisibilityBoundary() - this.rightBoundary(); - let avgX = Math.max((dl + dr) / 2, minMargin); - const dt = this.topBoundary() - this.blueprint.template.gridTopVisibilityBoundary(); - const db = this.blueprint.template.gridBottomVisibilityBoundary() - this.bottomBoundary(); - let avgY = Math.max((dt + db) / 2, minMargin); - this.blueprint.scrollDelta(dl - avgX, dt - avgY, true); - } -} - -/** - * @template {IDraggableElement} T - * @extends {IDraggableTemplate} - */ -class IDraggablePositionedTemplate extends IDraggableTemplate { +class VariableManagementNodeTemplate extends NodeTemplate { - /** @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`; - } + #hasInput = false + #hasOutput = false + displayName = "" + + static nodeStyleClasses = ["ueb-node-style-glass"] + + /** @param {NodeElement} element */ + initialize(element) { + super.initialize(element); + this.displayName = this.element.nodeDisplayName; + } + + render() { + return x` +
+
+ ${this.displayName ? x` +
+
+ + ${this.displayName} + +
+
+ ` : A} + ${this.#hasInput ? x` +
+ ` : A} + ${this.#hasOutput ? x` +
+ ` : A} + ${this.pinInserter ? x` +
+ Add pin ${SVGIcon.plusCircle} +
+ `: A} +
+
+ ` + } + + 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 + }) } } +class MetasoundOperationTemplate extends VariableManagementNodeTemplate { + static nodeStyleClasses = ["ueb-node-style-metasound", "ueb-node-style-operation"] +} + +class VariableConversionNodeTemplate extends VariableManagementNodeTemplate { + + static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-conversion"] +} + +class VariableOperationNodeTemplate extends VariableManagementNodeTemplate { + + static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-operation"] +} + /** - * @template {NodeElement} T - * @extends {IDraggablePositionedTemplate} + * @template {TerminalAttribute} T + * @typedef {import("../../element/PinElement.js").default} PinElement */ -class ISelectableDraggableTemplate extends IDraggablePositionedTemplate { - /** @returns {HTMLElement} */ - getDraggableElement() { - return this.element - } +/** + * @template {TerminalAttribute} T + * @extends ITemplate> + */ +class PinTemplate extends ITemplate { - createDraggableObject() { - return /** @type {MouseMoveDraggable} */(new MouseMoveNodes(this.element, this.blueprint, { - draggableElement: this.getDraggableElement(), - scrollGraphEdge: true, - })) - } + static canWrapInput = true - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - if (this.element.selected && !this.element.listeningDrag) { - this.element.setSelected(true); - } + /** @type {HTMLElement} */ + #iconElement + get iconElement() { + return this.#iconElement } -} - -/** @extends {ISelectableDraggableTemplate} */ -class NodeTemplate extends ISelectableDraggableTemplate { - static nodeStyleClasses = ["ueb-node-style-default"] + /** @type {HTMLElement} */ + #wrapperElement + get wrapperElement() { + return this.#wrapperElement + } - #hasSubtitle = false + isNameRendered = true - /** @type {() => PinEntity} */ - pinInserter + /** @param {PinElement} element */ + initialize(element) { + super.initialize(element); + if (this.element.nodeElement) { + const nodeTemplate = this.element.nodeElement.template; + this.isNameRendered = !( + nodeTemplate instanceof VariableConversionNodeTemplate + || nodeTemplate instanceof VariableOperationNodeTemplate + || nodeTemplate instanceof MetasoundOperationTemplate + ); + } + } - /** @type {HTMLElement} */ - inputContainer + setup() { + super.setup(); + this.element.nodeElement = this.element.closest("ueb-node"); + const nodeTemplate = this.element.nodeElement.template; + if ( + nodeTemplate instanceof VariableConversionNodeTemplate + || nodeTemplate instanceof VariableOperationNodeTemplate + ) { + this.isNameRendered = false; + this.element.requestUpdate(); + } + } - /** @type {HTMLElement} */ - outputContainer + /** @returns {IInput[]} */ + createInputObjects() { + return [ + new MouseCreateLink(this.element, this.blueprint, { + moveEverywhere: true, + draggableElement: this.getClickableElement(), + }) + ] + } - /** @type {PinElement} */ - pinElement + render() { + const icon = x`
${this.renderIcon()}
`; + const content = x` +
+ ${this.isNameRendered ? this.renderName() : A} + ${this.isInputRendered() ? this.renderInput() : x``} +
+ `; + return x` +
+ ${this.element.isInput() ? x`${icon}${content}` : x`${content}${icon}`} +
+ ` + } - addPinHandler = () => { - const pin = this.pinInserter?.(); - if (pin) { - if (this.defaultPin && this.defaultPin.isInput() === pin.isInput()) { - this.defaultPin.before(this.createPinElement(pin)); - } else { - (pin.isInput() ? this.inputContainer : this.outputContainer).appendChild(this.createPinElement(pin)); + renderIcon() { + if (this.element.nodeElement.entity.isPcg()) { + switch (this.element.entity.getType()) { + case "Any": + return SVGIcon.pcgPin + case "Param": + case "Param[]": + return SVGIcon.pcgParamPin + case "Spatial": + case "Spatial[]": + return SVGIcon.pcgSpatialPin + case "Any[]": + case "Point[]": + case "Surface[]": + case "Volume[]": + if (this.element.isOutput()) { + return SVGIcon.pcgPin + } + case "Point": + case "Surface": + case "Volume": + return SVGIcon.pcgStackPin } - this.element.acknowledgeReflow(); } + switch (this.element.entity.PinType?.ContainerType?.toString()) { + case "Array": return SVGIcon.arrayPin + case "Set": return SVGIcon.setPin + case "Map": return SVGIcon.mapPin + } + if (this.element.entity.PinType?.PinCategory?.toLocaleLowerCase() === "delegate") { + return SVGIcon.delegate + } + if (this.element.nodeElement?.template instanceof VariableOperationNodeTemplate) { + return SVGIcon.operationPin + } + return SVGIcon.genericPin + } + + renderName() { + let name = this.element.getPinDisplayName(); + const nodeElement = this.element.nodeElement; + const pinName = this.element.getPinName(); + if ( + nodeElement.getType() == Configuration.paths.makeStruct + && pinName == nodeElement.entity.StructType.getName() + ) { + name = pinName; + } + return x` + ${name} + ` } - toggleAdvancedDisplayHandler = () => { - this.element.toggleShowAdvancedPinDisplay(); - this.element.requestUpdate(); - this.element.updateComplete.then(() => this.element.acknowledgeReflow()); + isInputRendered() { + return this.element.isInput() + && !this.element.entity.bDefaultValueIsIgnored + && !this.element.entity.PinType.bIsReference } - /** @param {PinEntity} pinEntity */ - createPinElement(pinEntity) { - const pinElement = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) - .newObject(pinEntity, undefined, this.element); - if (this.pinInserter && !this.defaultPin && pinElement.getPinName() === "Default") { - this.defaultPin = pinElement; - this.defaultPin.classList.add("ueb-node-variadic-default"); - } - return pinElement + renderInput() { + return x`` } - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - this.element.classList.add(.../** @type {typeof NodeTemplate} */(this.constructor).nodeStyleClasses); - this.element.style.setProperty("--ueb-node-color", this.getColor().cssText); - this.pinInserter = this.element.entity.additionalPinInserter(); - if (this.pinInserter) { - this.element.classList.add("ueb-node-is-variadic"); + /** @param {PropertyValues} changedProperties */ + updated(changedProperties) { + super.updated(changedProperties); + if (this.element.isInput() && changedProperties.has("isLinked")) { + // When connected, an input may drop its input fields which means the node has to reflow + const node = this.element.nodeElement; + this.element.requestUpdate(); + this.element.updateComplete.then(() => node.acknowledgeReflow()); } } - getColor() { - return this.element.entity.nodeColor() + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.element.style.setProperty("--ueb-pin-color-rgb", this.element.entity.pinColor().cssText); + this.#iconElement = this.element.querySelector(".ueb-pin-icon svg") ?? this.element; + this.#wrapperElement = this.element.querySelector(".ueb-pin-wrapper"); } - render() { - return x` -
-
-
${this.renderTop()}
-
-
- ${this.pinInserter ? x` -
- Add pin ${SVGIcon.plusCircle} -
- `: A} - ${this.element.entity.isDevelopmentOnly() ? x` -
- Development Only -
- ` : A} - ${this.element.advancedPinDisplay ? x` -
- ${SVGIcon.expandIcon} -
- ` : A} -
-
- ` + getLinkLocation() { + const rect = this.iconElement.getBoundingClientRect(); + const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]; + const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); + return this.blueprint.compensateTranslation(location[0], location[1]) } - renderNodeIcon() { - return this.element.entity.nodeIcon() + getClickableElement() { + return this.#wrapperElement ?? this.element } +} + +/** + * @template {TerminalAttribute} T + * @extends PinTemplate + */ +class MinimalPinTemplate extends PinTemplate { + + render() { + return x` +
+
${this.renderIcon()}
+
+ ` + } +} + +class EventNodeTemplate extends NodeTemplate { - renderNodeName() { - return this.element.nodeDisplayName + static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-event"] + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.element.querySelector(".ueb-node-top").appendChild(this.createDelegatePinElement()); } renderTop() { const icon = this.renderNodeIcon(); const name = this.renderNodeName(); + const customEvent = this.element.getType() === Configuration.paths.customEvent + && (this.element.entity.CustomFunctionName || this.element.entity.FunctionReference.MemberParent); return x`
${icon ? x` @@ -8956,9 +9037,9 @@ class NodeTemplate extends ISelectableDraggableTemplate { ${name ? x`
${name} - ${this.#hasSubtitle && this.getTargetType().length > 0 ? x` + ${customEvent ? x`
- Target is ${Utility.formatStringName(this.getTargetType())} + Custom Event
`: A}
@@ -8967,1252 +9048,1634 @@ class NodeTemplate extends ISelectableDraggableTemplate { ` } - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.inputContainer = this.element.querySelector(".ueb-node-inputs"); - this.outputContainer = this.element.querySelector(".ueb-node-outputs"); - this.setupPins(); - this.element.updateComplete.then(() => this.element.acknowledgeReflow()); - } - - setupPins() { - this.element.nodeNameElement = /** @type {HTMLElement} */(this.element.querySelector(".ueb-node-name-text")); - let hasInput = false; - let hasOutput = false; - for (const p of this.element.getPinElements()) { - if (p === this.defaultPin) { - continue - } - if (p.isInput()) { - this.inputContainer.appendChild(p); - hasInput = true; - } else if (p.isOutput()) { - this.outputContainer.appendChild(p); - hasOutput = true; - } - } - if (this.defaultPin) { - (this.defaultPin.isInput() ? this.inputContainer : this.outputContainer).appendChild(this.defaultPin); - } - if (hasInput) { - this.element.classList.add("ueb-node-has-inputs"); - } - if (hasOutput) { - this.element.classList.add("ueb-node-has-outputs"); - } + createDelegatePinElement() { + const pin = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")).newObject( + this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory === "delegate"), + new MinimalPinTemplate(), + this.element + ); + pin.template.isNameRendered = false; + return pin } createPinElements() { return this.element.getPinEntities() - .filter(v => !v.isHidden()) - .map(pinEntity => { - this.#hasSubtitle = this.#hasSubtitle - || pinEntity.PinName === "self" && pinEntity.pinDisplayName() === "Target"; - return this.createPinElement(pinEntity) - }) - } - - getTargetType() { - return this.element.entity.FunctionReference?.MemberParent?.getName() ?? "Untitled" - } - - /** - * @param {NodeElement} node - * @returns {NodeListOf} - */ - getPinElements(node) { - return node.querySelectorAll("ueb-pin") + .filter(v => !v.isHidden() && v.PinType.PinCategory !== "delegate") + .map(pinEntity => /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) + .newObject(pinEntity, undefined, this.element) + ) } - - linksChanged() { } } -class IResizeableTemplate extends NodeTemplate { +/** @extends MinimalPinTemplate */ +class KnotPinTemplate extends MinimalPinTemplate { + + render() { + return this.element.isOutput() ? super.render() : x`` + } + + 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(); + /** @type {Coordinates} */ + const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]; + const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); + return this.blueprint.compensateTranslation(location[0], location[1]) + } +} + +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.paths.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 x` +
+ ` + } + + 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() { + + } +} + +class MetasoundNodeTemplate 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") + static nodeStyleClasses = ["ueb-node-style-metasound"] +} + +class VariableAccessNodeTemplate extends VariableManagementNodeTemplate { /** @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"); + const type = element.getType(); + if ( + type === Configuration.paths.variableGet + || type === Configuration.paths.self + ) { + this.element.classList.add("ueb-node-style-getter"); + this.displayName = ""; + } else if (type === Configuration.paths.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); + } +} + +/** + * @param {ObjectEntity} nodeEntity + * @return {new () => NodeTemplate} + */ +function nodeTemplateClass(nodeEntity) { + if ( + nodeEntity.getClass() === Configuration.paths.callFunction + || nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator + || nodeEntity.getClass() === Configuration.paths.callArrayFunction + ) { + const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""; + const memberName = nodeEntity.FunctionReference?.MemberName; + if ( + memberName && ( + memberParent === Configuration.paths.kismetMathLibrary + || memberParent === Configuration.paths.kismetArrayLibrary + )) { + if (memberName.startsWith("Conv_")) { + return VariableConversionNodeTemplate + } + if ( + memberName.startsWith("Add_") + || memberName.startsWith("And_") + || memberName.startsWith("Boolean") // Boolean logic operations + || memberName.startsWith("Cross_") + || memberName.startsWith("Dot_") + || memberName.startsWith("Not_") + || memberName.startsWith("Or_") + || memberName.startsWith("Percent_") + || memberName.startsWith("Xor_") + ) { + return VariableOperationNodeTemplate + } + switch (memberName) { + case "Abs": + case "Array_Add": + case "Array_AddUnique": + case "Array_Identical": + case "BMax": + case "BMin": + case "CrossProduct2D": + case "DotProduct2D": + case "Exp": + case "FMax": + case "FMin": + case "GetPI": + case "Max": + case "MaxInt64": + case "Min": + case "MinInt64": + case "Sqrt": + case "Square": + case "Vector4_CrossProduct3": + case "Vector4_DotProduct": + case "Vector4_DotProduct3": + // Trigonometry + case "Acos": + case "Asin": + case "Cos": + case "DegAcos": + case "DegCos": + case "DegSin": + case "DegTan": + case "Sin": + case "Tan": + return VariableOperationNodeTemplate + } + } + if (memberParent === Configuration.paths.blueprintSetLibrary) { + return VariableOperationNodeTemplate + } + if (memberParent === Configuration.paths.blueprintMapLibrary) { + return VariableOperationNodeTemplate + } + } + switch (nodeEntity.getClass()) { + case Configuration.paths.comment: + case Configuration.paths.materialGraphNodeComment: + return CommentNodeTemplate + case Configuration.paths.createDelegate: + return NodeTemplate + case Configuration.paths.metasoundEditorGraphExternalNode: + if (nodeEntity["ClassName"]?.["Name"] == "Add") { + return MetasoundOperationTemplate + } + return MetasoundNodeTemplate + case Configuration.paths.niagaraNodeOp: + if ([ + "Boolean::LogicEq", + "Boolean::LogicNEq", + "Numeric::Abs", + "Numeric::Add", + "Numeric::Mul", + ].includes(nodeEntity.OpName)) { + return VariableOperationNodeTemplate + } + break + case Configuration.paths.promotableOperator: + return VariableOperationNodeTemplate + case Configuration.paths.knot: + return KnotNodeTemplate + case Configuration.paths.literal: + case Configuration.paths.self: + case Configuration.paths.variableGet: + case Configuration.paths.variableSet: + return VariableAccessNodeTemplate + } + if (nodeEntity.isEvent()) { + return EventNodeTemplate + } + return NodeTemplate +} + +/** + * @template {IEntity} EntityT + * @template {ISelectableDraggableTemplate} TemplateT + * @extends {IDraggableElement} + */ +class ISelectableDraggableElement extends IDraggableElement { + + static properties = { + ...super.properties, + selected: { + type: Boolean, + attribute: "data-selected", + reflect: true, + converter: Utility.booleanConverter, + }, + } + + /** @param {UEBDragEvent} 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; + } + } + } +} + +/** @extends {ISelectableDraggableElement} */ +class NodeElement extends ISelectableDraggableElement { + + static properties = { + ...ISelectableDraggableElement.properties, + typePath: { + type: String, + attribute: "data-type", + reflect: true, + }, + nodeTitle: { + type: String, + attribute: "data-title", + 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 {UEBDragEvent} 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.requestUpdate(); + this.updateComplete.then(() => this.#commentDragged = false); + this.addLocation(...e.detail.value); + } + } + + /** @param {String} str */ + static fromSerializedObject(str) { + str = str.trim(); + let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); + return NodeElement.newObject(/** @type {ObjectEntity} */(entity)) + } + + /** + * @param {ObjectEntity} entity + * @param {NodeTemplate} template + */ + static newObject(entity = new ObjectEntity(), template = new (nodeTemplateClass(entity))()) { + const result = new NodeElement(); + result.initialize(entity, template); + return result + } + + #redirectLinksAfterRename(name) { + 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, + })); + } + } + } + + initialize(entity = new ObjectEntity(), template = new (nodeTemplateClass(entity))()) { + this.typePath = entity.getType(); + this.nodeTitle = entity.getObjectName(); + this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString(); + this.enabledState = entity.EnabledState; + this.nodeDisplayName = nodeTitle(entity); + this.pureFunction = entity.bIsPureFunc; + this.dragLinkObjects = []; + super.initialize(entity, template); + this.#pins = this.template.createPinElements(); + 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()); + } + entity.listenAttribute("Name", name => { + this.nodeTitle = entity.Name; + this.nodeDisplayName = nodeTitle(entity); + this.#redirectLinksAfterRename(name); + }); } - /** @param {PropertyValues} changedProperties */ - update(changedProperties) { - super.update(changedProperties); - if (this.element.sizeX >= 0 && changedProperties.has("sizeX")) { - this.element.style.width = `${this.element.sizeX}px`; + async getUpdateComplete() { + let result = await super.getUpdateComplete(); + for (const pin of this.getPinElements()) { + result &&= await pin.updateComplete; } - if (this.element.sizeY >= 0 && changedProperties.has("sizeY")) { - this.element.style.height = `${this.element.sizeY}px`; + return result + } + + /** @param {NodeElement} commentNode */ + bindToComment(commentNode) { + if (commentNode != this && !this.boundComments.includes(commentNode)) { + commentNode.addEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); + this.boundComments.push(commentNode); } } - /** @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 - ); + /** @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(); + } } - 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 {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() + } + + computeNodeDisplayName() { + this.nodeDisplayName = nodeTitle(this.entity); } /** @param {Number} value */ - setSizeX(value) { - this.element.setNodeWidth(value); - return true + setNodeWidth(value) { + this.entity.setNodeWidth(value); + this.sizeX = value; + this.acknowledgeReflow(); } /** @param {Number} value */ - setSizeY(value) { - this.element.setNodeHeight(value); - return true + setNodeHeight(value) { + this.entity.setNodeHeight(value); + this.sizeY = value; + this.acknowledgeReflow(); } - endResize() { + /** @param {IElement[]} nodesWhitelist */ + sanitizeLinks(nodesWhitelist = []) { + this.getPinElements().forEach(pin => pin.sanitizeLinks(nodesWhitelist)); } -} -class CommentNodeTemplate extends IResizeableTemplate { + getPinElements() { + return this.#pins + } - #selectableAreaHeight = 0 + /** @returns {PinEntity[]} */ + getPinEntities() { + return this.entity.getPinEntities() + } - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - 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 + setLocation(x = 0, y = 0, acknowledge = true) { + this.entity.setNodePosX(x); + this.entity.setNodePosY(y); + super.setLocation(x, y, acknowledge); } - /** @returns {HTMLElement} */ - getDraggableElement() { - return this.element.querySelector(".ueb-node-top") + acknowledgeReflow() { + this.requestUpdate(); + this.updateComplete.then(() => this.computeSizes()); + let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName); + this.dispatchEvent(reflowEvent); } - render() { - return x` -
-
-
-
-
-
- ` + setShowAdvancedPinDisplay(value) { + this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden"); + this.advancedPinDisplay = this.entity.AdvancedPinDisplay; } - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - const bounding = this.getDraggableElement().getBoundingClientRect(); - this.#selectableAreaHeight = bounding.height; + toggleShowAdvancedPinDisplay() { + this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay?.toString() != "Shown"); } +} - 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); - } - } +class BlueprintEntity extends ObjectEntity { + + /** @type {Map} */ + #objectEntitiesNameCounter = new Map() + + /** @type {ObjectEntity[]}" */ + #objectEntities = [] + get objectEntities() { + return this.#objectEntities } - /** @param {Number} value */ - setSizeX(value) { - value = Math.round(value); - if (value >= 2 * Configuration.gridSize) { - this.element.setNodeWidth(value); + /** @param {ObjectEntity} entity */ + getHomonymObjectEntity(entity) { + const name = entity.getObjectName(false); + return this.#objectEntities.find(entity => entity.getObjectName() == name) + } + + /** @param {String} name */ + takeFreeName(name) { + name = name.replace(/_\d+$/, ""); + const counter = (this.#objectEntitiesNameCounter.get(name) ?? -1) + 1; + this.#objectEntitiesNameCounter.set(name, counter); + return Configuration.nodeTitle(name, counter) + } + + /** @param {ObjectEntity} entity */ + addObjectEntity(entity) { + if (!this.#objectEntities.includes(entity)) { + this.#objectEntities.push(entity); + const [name, counter] = entity.getNameAndCounter(); + this.#objectEntitiesNameCounter.set( + name, + Math.max((this.#objectEntitiesNameCounter.get(name) ?? 0), counter) + ); return true } return false } - /** @param {Number} value */ - setSizeY(value) { - value = Math.round(value); - if (value >= 2 * Configuration.gridSize) { - this.element.setNodeHeight(value); + /** @param {ObjectEntity} entity */ + removeObjectEntity(entity) { + const index = this.#objectEntities.indexOf(entity); + if (index >= 0) { + const last = this.#objectEntities.pop(); + if (index < this.#objectEntities.length) { + this.#objectEntities[index] = last; + } return true } return false } - endResize() { - this.manageNodesBind(); - } + /** @param {ObjectEntity} entity */ + mergeWith(entity) { + if (!entity.ScriptVariables || entity.ScriptVariables.length === 0) { + return this + } + if (!this.ScriptVariables || this.ScriptVariables.length === 0) { + this.ScriptVariables = entity.ScriptVariables; + } + let scriptVariables = Utility.mergeArrays( + this.ScriptVariables, + entity.ScriptVariables, + (l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value + ); + if (scriptVariables.length === this.ScriptVariables.length) { + return this + } + const entries = scriptVariables.concat(scriptVariables).map((v, i) => { + const name = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, i >= scriptVariables.length); + return [ + name, + this[name] ?? entity[name] + ] + }); + entries.push( + ...Object.entries(this).filter(([k, v]) => + !k.startsWith(Configuration.subObjectAttributeNamePrefix) + && k !== "ExportedNodes" + ) + ); + return new BlueprintEntity(Object.fromEntries(entries)) + } +} + +/** @template {AttributeConstructor} T */ +class Serializer { + + /** @type {(v: String) => String} */ + static same = v => v + + /** @type {(entity: Attribute, serialized: String) => String} */ + static notWrapped = (entity, serialized) => serialized - topBoundary(justSelectableArea = false) { - return this.element.locationY - } + /** @type {(entity: Attribute, serialized: String) => String} */ + static bracketsWrapped = (entity, serialized) => `(${serialized})` - rightBoundary(justSelectableArea = false) { - return this.element.locationX + this.element.sizeX + /** @param {T} entityType */ + constructor( + entityType, + /** @type {(entity: ConstructedType, serialized: String) => String} */ + wrap = (entity, serialized) => serialized, + attributeSeparator = ",", + trailingSeparator = false, + attributeValueConjunctionSign = "=", + attributeKeyPrinter = Serializer.same + ) { + this.entityType = entityType; + this.wrap = wrap; + this.attributeSeparator = attributeSeparator; + this.trailingSeparator = trailingSeparator; + this.attributeValueConjunctionSign = attributeValueConjunctionSign; + this.attributeKeyPrinter = attributeKeyPrinter; } - bottomBoundary(justSelectableArea = false) { - return justSelectableArea - ? this.element.locationY + this.#selectableAreaHeight - : super.bottomBoundary() + /** + * @param {String} value + * @returns {ConstructedType} + */ + read(value) { + return this.doRead(value.trim()) } - leftBoundary(justSelectableArea = false) { - return this.element.locationX + /** @param {ConstructedType} value */ + write(value, insideString = false) { + return this.doWrite(value, insideString) } -} - -/** - * @typedef {import("./IMouseClickDrag.js").Options & { - * scrollGraphEdge?: Boolean, -* }} Options -*/ - -/** @extends IMouseClickDrag */ -class MouseCreateLink extends IMouseClickDrag { - - /** @type {NodeListOf} */ - #listenedPins - - /** @type {PinElement} */ - #knotPin = null - - /** @param {MouseEvent} e */ - #mouseenterHandler = e => { - if (!this.enteredPin) { - this.linkValid = false; - this.enteredPin = /** @type {PinElement} */(e.target); - const a = this.link.source ?? this.target; // Remember target might have change - const b = this.enteredPin; - const outputPin = a.isOutput() ? a : b; - if ( - a.nodeElement.getType() === Configuration.paths.knot - || b.nodeElement.getType() === Configuration.paths.knot - ) { - // A knot can be linked to any pin, it doesn't matter the type or input/output direction - this.link.setMessageCorrect(); - this.linkValid = true; - } else if (a.getNodeElement() === b.getNodeElement()) { - this.link.setMessageSameNode(); - } else if (a.isOutput() === b.isOutput()) { - this.link.setMessageDirectionsIncompatible(); - } else if (this.blueprint.getLinks(a, b).length) { - this.link.setMessageReplaceLink(); - this.linkValid = true; - } else if (outputPin.entity.getType() === "exec" && outputPin.isLinked) { - this.link.setMessageReplaceOutputLink(); - this.linkValid = true; - } else if ( - (a.entity.PinType.PinCategory != "object" || b.entity.PinType.PinCategory != "object") - && a.pinType != b.pinType - ) { - this.link.setMessageTypesIncompatible(a, b); - this.linkValid = false; - } else { - this.link.setMessageCorrect(); - this.linkValid = true; - } - } - } - - /** @param {MouseEvent} e */ - #mouseleaveHandler = e => { - if (this.enteredPin == e.target) { - this.enteredPin = null; - this.linkValid = false; - this.link?.setMessagePlaceNode(); - } - } - - /** @type {LinkElement?} */ - link - - /** @type {PinElement?} */ - enteredPin - - linkValid = false - - /** - * @param {PinElement} target - * @param {Blueprint} blueprint - * @param {Options} options - */ - constructor(target, blueprint, options = {}) { - options.scrollGraphEdge ??= true; - super(target, blueprint, options); - } - - startDrag(location) { - if (this.target.nodeElement.getType() == Configuration.paths.knot) { - this.#knotPin = this.target; - } - /** @type {LinkElement} */ - this.link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) - .newObject(this.target, null); - this.blueprint.template.linksContainerElement.prepend(this.link); - this.link.setMessagePlaceNode(); - this.#listenedPins = this.blueprint.querySelectorAll("ueb-pin"); - this.#listenedPins.forEach(pin => { - if (pin != this.target) { - pin.addEventListener("mouseenter", this.#mouseenterHandler); - pin.addEventListener("mouseleave", this.#mouseleaveHandler); - } - }); - this.link.startDragging(); - this.link.setDestinationLocation(location); - } - - dragTo(location, movement) { - this.link.setDestinationLocation(location); - } - - endDrag() { - this.#listenedPins.forEach(pin => { - pin.removeEventListener("mouseenter", this.#mouseenterHandler); - pin.removeEventListener("mouseleave", this.#mouseleaveHandler); - }); - this.#listenedPins = null; - if (this.enteredPin && this.linkValid) { - if (this.#knotPin) { - const otherPin = this.#knotPin !== this.link.source ? this.link.source : this.enteredPin; - // Knot pin direction correction - if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) { - const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin(); - if (this.#knotPin === this.link.source) { - this.link.source = oppositePin; - } else { - this.enteredPin = oppositePin; - } - } - } else if (this.enteredPin.nodeElement.getType() === Configuration.paths.knot) { - this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin(); - } - if (!this.link.source.getLinks().find(ref => ref.equals(this.enteredPin.createPinReference()))) { - this.blueprint.addGraphElement(this.link); - this.link.destination = this.enteredPin; - } else { - this.link.remove(); - } - } else { - this.link.remove(); - } - this.enteredPin = null; - this.link.removeMessage(); - this.link.finishDragging(); - this.link = null; - } -} - -class VariableManagementNodeTemplate extends NodeTemplate { - - #hasInput = false - #hasOutput = false - #displayName = "" - static nodeStyleClasses = ["ueb-node-style-glass"] + /** + * @param {String} value + * @returns {ConstructedType} + */ + doRead(value) { + let grammar = Grammar.grammarFor(undefined, this.entityType); + const parseResult = grammar.run(value); + if (!parseResult.status) { + throw new Error( + this.entityType + ? `Error when trying to parse the entity ${this.entityType.prototype.constructor.name}` + : "Error when trying to parse null" + ) + } + return parseResult.value + } - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - this.#displayName = this.element.nodeDisplayName; + /** + * @param {ConstructedType} entity + * @param {Boolean} insideString + * @returns {String} + */ + doWrite( + entity, + insideString = false, + indentation = "", + wrap = this.wrap, + attributeSeparator = this.attributeSeparator, + trailingSeparator = this.trailingSeparator, + attributeValueConjunctionSign = this.attributeValueConjunctionSign, + attributeKeyPrinter = this.attributeKeyPrinter + ) { + let result = ""; + const keys = entity._keys ?? Object.keys(entity); + let first = true; + for (const key of keys) { + const value = entity[key]; + if (value !== undefined && this.showProperty(entity, key)) { + let keyValue = entity instanceof Array ? `(${key})` : key; + if (AttributeInfo.getAttribute(entity, key, "quoted")) { + keyValue = `"${keyValue}"`; + } + const isSerialized = AttributeInfo.getAttribute(entity, key, "serialized"); + if (first) { + first = false; + } else { + result += attributeSeparator; + } + if (AttributeInfo.getAttribute(entity, key, "inlined")) { + result += this.doWrite( + value, + insideString, + indentation, + Serializer.notWrapped, + attributeSeparator, + false, + attributeValueConjunctionSign, + AttributeInfo.getAttribute(entity, key, "type") instanceof Array + ? k => attributeKeyPrinter(`${keyValue}${k}`) + : k => attributeKeyPrinter(`${keyValue}.${k}`) + ); + continue + } + const keyPrinted = attributeKeyPrinter(keyValue); + const indentationPrinted = attributeSeparator.includes("\n") ? indentation : ""; + result += ( + keyPrinted.length + ? (indentationPrinted + keyPrinted + this.attributeValueConjunctionSign) + : "" + ) + + ( + isSerialized + ? `"${this.doWriteValue(value, true, indentation)}"` + : this.doWriteValue(value, insideString, indentation) + ); + } + } + if (trailingSeparator && result.length) { + // append separator at the end if asked and there was printed content + result += attributeSeparator; + } + return wrap(entity, result) } - render() { - return x` -
-
- ${this.#displayName ? x` -
-
- - ${this.#displayName} - -
-
- ` : A} - ${this.#hasInput ? x` -
- ` : A} - ${this.#hasOutput ? x` -
- ` : A} - ${this.pinInserter ? x` -
- Add pin ${SVGIcon.plusCircle} -
- `: A} -
-
- ` + /** @param {Boolean} insideString */ + doWriteValue(value, insideString, indentation = "") { + const type = Utility.getType(value); + 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.doWrite(value, insideString, indentation) } - 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 - }) + /** + * @param {IEntity} entity + * @param {String} key + */ + showProperty(entity, key) { + if (entity instanceof IEntity) { + if (AttributeInfo.getAttribute(entity, key, "ignored")) { + return false + } + if (AttributeInfo.getAttribute(entity, key, "silent")) { + let defaultValue = AttributeInfo.getAttribute(entity, key, "default"); + if (defaultValue instanceof Function) { + defaultValue = defaultValue(entity); + } + if (Utility.equals(entity[key], defaultValue)) { + return false + } + } + } + return true } } -class VariableConversionNodeTemplate extends VariableManagementNodeTemplate { - - static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-conversion"] -} - -class VariableOperationNodeTemplate extends VariableManagementNodeTemplate { - - static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-operation"] -} - -/** - * @template {TerminalAttribute} T - * @typedef {import("../../element/PinElement.js").default} PinElement - */ - -/** - * @template {TerminalAttribute} T - * @extends ITemplate> - */ -class PinTemplate extends ITemplate { - - static canWrapInput = true - - /** @type {HTMLElement} */ - #iconElement - get iconElement() { - return this.#iconElement - } - - /** @type {HTMLElement} */ - #wrapperElement - get wrapperElement() { - return this.#wrapperElement - } - - isNameRendered = true - - /** @param {PinElement} element */ - initialize(element) { - super.initialize(element); - if (this.element.nodeElement) { - const nodeTemplate = this.element.nodeElement.template; - this.isNameRendered = !( - nodeTemplate instanceof VariableConversionNodeTemplate - || nodeTemplate instanceof VariableOperationNodeTemplate - ); - } - } - - setup() { - super.setup(); - this.element.nodeElement = this.element.closest("ueb-node"); - const nodeTemplate = this.element.nodeElement.template; - if ( - nodeTemplate instanceof VariableConversionNodeTemplate - || nodeTemplate instanceof VariableOperationNodeTemplate - ) { - this.isNameRendered = false; - this.element.requestUpdate(); - } - } - - /** @returns {IInput[]} */ - createInputObjects() { - return [ - new MouseCreateLink(this.element, this.blueprint, { - moveEverywhere: true, - draggableElement: this.getClickableElement(), - }) - ] - } - - render() { - const icon = x`
${this.renderIcon()}
`; - const content = x` -
- ${this.isNameRendered ? this.renderName() : A} - ${this.isInputRendered() ? this.renderInput() : x``} -
- `; - return x` -
- ${this.element.isInput() ? x`${icon}${content}` : x`${content}${icon}`} -
- ` - } +/** @extends Serializer */ +class ObjectSerializer extends Serializer { - renderIcon() { - if (this.element.nodeElement.entity.isPcg()) { - switch (this.element.entity.getType()) { - case "Any": - return SVGIcon.pcgPin - case "Param": - case "Param[]": - return SVGIcon.pcgParamPin - case "Spatial": - case "Spatial[]": - return SVGIcon.pcgSpatialPin - case "Any[]": - case "Point[]": - case "Surface[]": - case "Volume[]": - if (this.element.isOutput()) { - return SVGIcon.pcgPin - } - case "Point": - case "Surface": - case "Volume": - return SVGIcon.pcgStackPin - } - } - switch (this.element.entity.PinType?.ContainerType?.toString()) { - case "Array": return SVGIcon.arrayPin - case "Set": return SVGIcon.setPin - case "Map": return SVGIcon.mapPin - } - if (this.element.entity.PinType?.PinCategory?.toLocaleLowerCase() === "delegate") { - return SVGIcon.delegate - } - if (this.element.nodeElement?.template instanceof VariableOperationNodeTemplate) { - return SVGIcon.operationPin - } - return SVGIcon.genericPin + constructor(entityType = ObjectEntity) { + super(entityType, undefined, "\n", true, undefined, Serializer.same); } - renderName() { - let name = this.element.getPinDisplayName(); - const nodeElement = this.element.nodeElement; - const pinName = this.element.getPinName(); - if ( - nodeElement.getType() == Configuration.paths.makeStruct - && pinName == nodeElement.entity.StructType.getName() - ) { - name = pinName; + showProperty(entity, key) { + switch (key) { + case "Class": + case "Name": + case "Archetype": + case "ExportPath": + case "CustomProperties": + // Serielized separately, check doWrite() + return false } - return x` - ${name} - ` - } - - isInputRendered() { - return this.element.isInput() - && !this.element.entity.bDefaultValueIsIgnored - && !this.element.entity.PinType.bIsReference - } - - renderInput() { - return x`` + return super.showProperty(entity, key) } - /** @param {PropertyValues} changedProperties */ - updated(changedProperties) { - super.updated(changedProperties); - if (this.element.isInput() && changedProperties.has("isLinked")) { - // When connected, an input may drop its input fields which means the node has to reflow - const node = this.element.nodeElement; - this.element.requestUpdate(); - this.element.updateComplete.then(() => node.acknowledgeReflow()); - } + /** @param {ObjectEntity} value */ + write(value, insideString = false) { + return this.doWrite(value, insideString) + "\n" } - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.element.style.setProperty("--ueb-pin-color-rgb", this.element.entity.pinColor().cssText); - this.#iconElement = this.element.querySelector(".ueb-pin-icon svg") ?? this.element; - this.#wrapperElement = this.element.querySelector(".ueb-pin-wrapper"); + /** @param {String} value */ + doRead(value) { + return Grammar.grammarFor(undefined, this.entityType).parse(value) } - getLinkLocation() { - const rect = this.iconElement.getBoundingClientRect(); - const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]; - const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); - return this.blueprint.compensateTranslation(location[0], location[1]) + /** + * @param {String} value + * @returns {ObjectEntity[]} + */ + readMultiple(value) { + return ObjectEntity.getMultipleObjectsGrammar().parse(value) } - getClickableElement() { - return this.#wrapperElement ?? this.element + /** + * @param {ObjectEntity} entity + * @param {Boolean} insideString + * @returns {String} + */ + doWrite( + entity, + insideString, + indentation = "", + wrap = this.wrap, + attributeSeparator = this.attributeSeparator, + trailingSeparator = this.trailingSeparator, + attributeValueConjunctionSign = this.attributeValueConjunctionSign, + attributeKeyPrinter = this.attributeKeyPrinter, + ) { + const moreIndentation = indentation + Configuration.indentation; + if (!(entity instanceof ObjectEntity)) { + return super.doWrite( + entity, + insideString, + indentation, + wrap, + attributeSeparator, + trailingSeparator, + attributeValueConjunctionSign, + // @ts-expect-error + key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key) + ) + } + let result = indentation + "Begin Object" + + (entity.Class?.type || entity.Class?.path ? ` Class=${this.doWriteValue(entity.Class, insideString)}` : "") + + (entity.Name ? ` Name=${this.doWriteValue(entity.Name, insideString)}` : "") + + (entity.Archetype ? ` Archetype=${this.doWriteValue(entity.Archetype, insideString)}` : "") + + (entity.ExportPath?.type || entity.ExportPath?.path ? ` ExportPath=${this.doWriteValue(entity.ExportPath, insideString)}` : "") + + "\n" + + super.doWrite( + entity, + insideString, + moreIndentation, + wrap, + attributeSeparator, + true, + attributeValueConjunctionSign, + key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key) + ) + + (!AttributeInfo.getAttribute(entity, "CustomProperties", "ignored") + ? entity.getCustomproperties().map(pin => + moreIndentation + + attributeKeyPrinter("CustomProperties ") + + SerializerFactory.getSerializer(PinEntity).doWrite(pin, insideString) + + this.attributeSeparator + ).join("") + : "" + ) + + indentation + "End Object"; + return result } } -/** - * @template {TerminalAttribute} T - * @extends PinTemplate - */ -class MinimalPinTemplate extends PinTemplate { - - render() { - return x` -
-
${this.renderIcon()}
-
- ` - } -} - -class EventNodeTemplate extends NodeTemplate { +/** + * @typedef {import("../IInput.js").Options & { + * listenOnFocus?: Boolean, + * unlistenOnTextEdit?: Boolean, + * }} Options + */ - static nodeStyleClasses = [...super.nodeStyleClasses, "ueb-node-style-event"] +class Copy extends IInput { - /** @param {PropertyValues} changedProperties */ - firstUpdated(changedProperties) { - super.firstUpdated(changedProperties); - this.element.querySelector(".ueb-node-top").appendChild(this.createDelegatePinElement()); - } + static #serializer = new ObjectSerializer() - renderTop() { - const icon = this.renderNodeIcon(); - const name = this.renderNodeName(); - const customEvent = this.element.getType() === Configuration.paths.customEvent - && (this.element.entity.CustomFunctionName || this.element.entity.FunctionReference.MemberParent); - return x` -
- ${icon ? x` -
${icon}
- ` : A} - ${name ? x` -
- ${name} - ${customEvent ? x` -
- Custom Event -
- `: A} -
- ` : A} -
- ` + /** @type {(e: ClipboardEvent) => void} */ + #copyHandler + + constructor(target, blueprint, options = {}) { + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; // No nodes copy if inside a text field, just text (default behavior) + super(target, blueprint, options); + let self = this; + this.#copyHandler = () => self.copied(); } - createDelegatePinElement() { - const pin = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")).newObject( - this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory === "delegate"), - new MinimalPinTemplate(), - this.element - ); - pin.template.isNameRendered = false; - return pin + listenEvents() { + window.addEventListener("copy", this.#copyHandler); } - createPinElements() { - return this.element.getPinEntities() - .filter(v => !v.isHidden() && v.PinType.PinCategory !== "delegate") - .map(pinEntity => /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")) - .newObject(pinEntity, undefined, this.element) - ) + unlistenEvents() { + window.removeEventListener("copy", this.#copyHandler); + } + + getSerializedText() { + const allNodes = this.blueprint.getNodes(true).map(n => n.entity); + const exported = allNodes.filter(n => n.isExported).map(n => Copy.#serializer.write(n, false)); + const result = allNodes.filter(n => !n.isExported).map(n => Copy.#serializer.write(n, false)); + if (exported.length) { + this.blueprint.entity.ExportedNodes = btoa(exported.join("")); + result.splice(0, 0, Copy.#serializer.write(this.blueprint.entity, false)); + delete this.blueprint.entity.ExportedNodes; + } + return result.join("") + } + + copied() { + const value = this.getSerializedText(); + navigator.clipboard.writeText(value); + return value } } -/** @extends MinimalPinTemplate */ -class KnotPinTemplate extends MinimalPinTemplate { - - render() { - return this.element.isOutput() ? super.render() : x`` - } - - 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(); - /** @type {Coordinates} */ - const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]; - const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement); - return this.blueprint.compensateTranslation(location[0], location[1]) - } -} - -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.paths.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 x` -
- ` - } - - 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() { - - } -} - -class VariableAccessNodeTemplate extends VariableManagementNodeTemplate { - - /** @param {NodeElement} element */ - initialize(element) { - super.initialize(element); - if (element.getType() === Configuration.paths.variableGet) { - this.element.classList.add("ueb-node-style-getter"); - } else if (element.getType() === Configuration.paths.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("../IInput.js").Options & { + * listenOnFocus?: Boolean, + * unlistenOnTextEdit?: Boolean, + * }} Options + */ + +class Cut extends IInput { + + static #serializer = new ObjectSerializer() + + /** @type {(e: ClipboardEvent) => void} */ + #cutHandler + + /** + * @param {Element} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; // No nodes copy if inside a text field, just text (default behavior) + super(target, blueprint, options); + let self = this; + this.#cutHandler = () => self.cut(); + } + + listenEvents() { + window.addEventListener("cut", this.#cutHandler); + } + + unlistenEvents() { + window.removeEventListener("cut", this.#cutHandler); + } + + getSerializedText() { + return this.blueprint + .getNodes(true) + .map(node => Cut.#serializer.write(node.entity, false)) + .join("") + } + + cut() { + this.blueprint.template.getCopyInputObject().copied(); + this.blueprint.removeGraphElement(...this.blueprint.getNodes(true)); + } } -/** - * @template {IEntity} EntityT - * @template {ISelectableDraggableTemplate} TemplateT - * @extends {IDraggableElement} - */ -class ISelectableDraggableElement extends IDraggableElement { - - static properties = { - ...super.properties, - selected: { - type: Boolean, - attribute: "data-selected", - reflect: true, - converter: Utility.booleanConverter, - }, - } - - /** @param {UEBDragEvent} 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("../IInput.js").Options & { + * listenOnFocus?: Boolean, + * unlistenOnTextEdit?: Boolean, + * }} Options + */ + +class Paste extends IInput { + + static #serializer = new ObjectSerializer() + + /** @type {(e: ClipboardEvent) => void} */ + #pasteHandle + + /** + * @param {Element} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; // No nodes paste if inside a text field, just text (default behavior) + super(target, blueprint, options); + let self = this; + this.#pasteHandle = e => self.pasted(e.clipboardData.getData("Text")); + } + + listenEvents() { + window.addEventListener("paste", this.#pasteHandle); + } + + unlistenEvents() { + window.removeEventListener("paste", this.#pasteHandle); + } + + /** @param {String} value */ + pasted(value) { + let top = 0; + let left = 0; + let count = 0; + let nodes = Paste.#serializer.readMultiple(value).map(entity => { + let node = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) + .newObject(entity); + top += node.locationY; + left += node.locationX; + ++count; + return node + }); + top /= count; + left /= count; + if (nodes.length > 0) { + this.blueprint.unselectAll(); + } + let mousePosition = this.blueprint.mousePosition; + nodes.forEach(node => { + node.addLocation(mousePosition[0] - left, mousePosition[1] - top); + node.snapToGrid(); + node.setSelected(true); + }); + this.blueprint.addGraphElement(...nodes); + return nodes + } } -/** @extends {ISelectableDraggableElement} */ -class NodeElement extends ISelectableDraggableElement { - - static properties = { - ...ISelectableDraggableElement.properties, - typePath: { - type: String, - attribute: "data-type", - reflect: true, - }, - nodeTitle: { - type: String, - attribute: "data-title", - 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 {UEBDragEvent} 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.requestUpdate(); - this.updateComplete.then(() => this.#commentDragged = false); - this.addLocation(...e.detail.value); - } - } +/** + * @typedef {import("./IPointing.js").Options & { + * listenOnFocus?: Boolean, + * strictTarget?: Boolean, + * }} Options + */ - /** - * @param {ObjectEntity} nodeEntity - * @return {new () => NodeTemplate} - */ - static getTypeTemplate(nodeEntity) { - if ( - nodeEntity.getClass() === Configuration.paths.callFunction - || nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator - || nodeEntity.getClass() === Configuration.paths.callArrayFunction - ) { - const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""; - const memberName = nodeEntity.FunctionReference?.MemberName; - if ( - memberName && ( - memberParent === Configuration.paths.kismetMathLibrary - || memberParent === Configuration.paths.kismetArrayLibrary - )) { - if (memberName.startsWith("Conv_")) { - return VariableConversionNodeTemplate - } - if ( - memberName.startsWith("And_") - || memberName.startsWith("Boolean") // Boolean logic operations - || memberName.startsWith("Cross_") - || memberName.startsWith("Dot_") - || memberName.startsWith("Not_") - || memberName.startsWith("Or_") - || memberName.startsWith("Percent_") - || memberName.startsWith("Xor_") - ) { - return VariableOperationNodeTemplate - } - switch (memberName) { - case "Abs": - case "Array_Add": - case "Array_AddUnique": - case "Array_Identical": - case "BMax": - case "BMin": - case "CrossProduct2D": - case "DotProduct2D": - case "Exp": - case "FMax": - case "FMin": - case "GetPI": - case "Max": - case "MaxInt64": - case "Min": - case "MinInt64": - case "Sqrt": - case "Square": - case "Vector4_CrossProduct3": - case "Vector4_DotProduct": - case "Vector4_DotProduct3": - // Trigonometry - case "Acos": - case "Asin": - case "Cos": - case "DegAcos": - case "DegCos": - case "DegSin": - case "DegTan": - case "Sin": - case "Tan": - return VariableOperationNodeTemplate - } - } - if (memberParent === Configuration.paths.blueprintSetLibrary) { - return VariableOperationNodeTemplate - } - if (memberParent === Configuration.paths.blueprintMapLibrary) { - return VariableOperationNodeTemplate - } - } - switch (nodeEntity.getClass()) { - case Configuration.paths.comment: - case Configuration.paths.materialGraphNodeComment: - return CommentNodeTemplate - case Configuration.paths.createDelegate: - return NodeTemplate - case Configuration.paths.promotableOperator: - return VariableOperationNodeTemplate - case Configuration.paths.knot: - return KnotNodeTemplate - case Configuration.paths.literal: - case Configuration.paths.variableGet: - case Configuration.paths.variableSet: - return VariableAccessNodeTemplate - } - if (nodeEntity.isEvent()) { - return EventNodeTemplate - } - return NodeTemplate - } +class MouseWheel extends IPointing { - /** @param {String} str */ - static fromSerializedObject(str) { - str = str.trim(); - let entity = SerializerFactory.getSerializer(ObjectEntity).read(str); - return NodeElement.newObject(/** @type {ObjectEntity} */(entity)) - } + /** @param {MouseWheel} self */ + static #ignoreEvent = self => { } - /** - * @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 + #variation = 0 + get variation() { + return this.#variation } - initialize(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { - this.typePath = entity.getType(); - this.nodeTitle = entity.getObjectName(); - this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString(); - this.enabledState = entity.EnabledState; - this.nodeDisplayName = entity.nodeDisplayName(); - this.pureFunction = entity.bIsPureFunc; - this.dragLinkObjects = []; - super.initialize(entity, template); - this.#pins = this.template.createPinElements(); - 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()); + /** @param {WheelEvent} e */ + #mouseWheelHandler = e => { + if (this.enablerKey && !this.enablerActivated) { + return } + e.preventDefault(); + this.#variation = e.deltaY; + this.setLocationFromEvent(e); + this.wheel(); } - async getUpdateComplete() { - let result = await super.getUpdateComplete(); - for (const pin of this.getPinElements()) { - result &&= await pin.updateComplete; - } - return result - } + /** @param {WheelEvent} e */ + #mouseParentWheelHandler = e => e.preventDefault() - /** @param {NodeElement} commentNode */ - bindToComment(commentNode) { - if (commentNode != this && !this.boundComments.includes(commentNode)) { - commentNode.addEventListener(Configuration.nodeDragEventName, this.#commentDragHandler); - this.boundComments.push(commentNode); - } + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor( + target, + blueprint, + options = {}, + onWheel = MouseWheel.#ignoreEvent, + ) { + options.listenOnFocus = true; + options.strictTarget ??= false; + super(target, blueprint, options); + this.strictTarget = options.strictTarget; + this.onWheel = onWheel; } - /** @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(); - } + listenEvents() { + this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler); } - /** @param {NodeElement} commentNode */ - isInsideComment(commentNode) { - return this.topBoundary() >= commentNode.topBoundary() - && this.rightBoundary() <= commentNode.rightBoundary() - && this.bottomBoundary() <= commentNode.bottomBoundary() - && this.leftBoundary() >= commentNode.leftBoundary() + unlistenEvents() { + this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false); + this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler); } - getType() { - return this.entity.getType() + /* Subclasses can override */ + wheel() { + this.onWheel(this); } +} - getNodeName() { - return this.entity.getObjectName() - } +class Zoom extends MouseWheel { + + #accumulatedVariation = 0 + + #enableZoonIn = false + get enableZoonIn() { + return this.#enableZoonIn + } + set enableZoonIn(value) { + if (value == this.#enableZoonIn) { + return + } + this.#enableZoonIn = value; + } + + wheel() { + this.#accumulatedVariation += -this.variation; + if (Math.abs(this.#accumulatedVariation) < Configuration.mouseWheelZoomThreshold) { + return + } + let zoomLevel = this.blueprint.getZoom(); + if (!this.enableZoonIn && zoomLevel == 0 && this.#accumulatedVariation > 0) { + return + } + zoomLevel += Math.sign(this.#accumulatedVariation); + this.blueprint.setZoom(zoomLevel, this.location); + this.#accumulatedVariation = 0; + } +} - computeNodeDisplayName() { - this.nodeDisplayName = this.entity.nodeDisplayName(); - } +/** + * @typedef {import("./KeyboardShortcut.js").Options & { + * activationKeys?: String | KeyBindingEntity | (String | KeyBindingEntity)[], + * }} Options + */ - /** @param {Number} value */ - setNodeWidth(value) { - this.entity.setNodeWidth(value); - this.sizeX = value; - this.acknowledgeReflow(); - } +class KeyboardEnableZoom extends KeyboardShortcut { - /** @param {Number} value */ - setNodeHeight(value) { - this.entity.setNodeHeight(value); - this.sizeY = value; - this.acknowledgeReflow(); - } + /** @type {Zoom} */ + #zoomInputObject - /** @param {IElement[]} nodesWhitelist */ - sanitizeLinks(nodesWhitelist = []) { - this.getPinElements().forEach(pin => pin.sanitizeLinks(nodesWhitelist)); + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.activationKeys = Shortcuts.enableZoomIn; + super(target, blueprint, options); } - /** @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.nodeTitle = this.entity.Name; + fire() { + this.#zoomInputObject = this.blueprint.template.getZoomInputObject(); + this.#zoomInputObject.enableZoonIn = true; } - getPinElements() { - return this.#pins + unfire() { + this.#zoomInputObject.enableZoonIn = false; } +} - /** @returns {PinEntity[]} */ - getPinEntities() { - return this.entity.getPinEntities() - } +class MouseScrollGraph extends IMouseClickDrag { + + startDrag() { + this.blueprint.scrolling = true; + } + + /** + * @param {Coordinates} location + * @param {Coordinates} movement + */ + dragTo(location, movement) { + this.blueprint.scrollDelta(-movement[0], -movement[1]); + } + + endDrag() { + this.blueprint.scrolling = false; + } +} - setLocation(x = 0, y = 0, acknowledge = true) { - this.entity.setNodePosX(x); - this.entity.setNodePosY(y); - super.setLocation(x, y, acknowledge); - } +/** + * @typedef {import("./IPointing.js").Options & { + * listenOnFocus?: Boolean, + * }} Options + */ + +class MouseTracking extends IPointing { + + /** @type {IPointing} */ + #mouseTracker = null + + /** @param {MouseEvent} e */ + #mousemoveHandler = e => { + e.preventDefault(); + this.setLocationFromEvent(e); + this.blueprint.mousePosition = [...this.location]; + } + + /** @param {CustomEvent} e */ + #trackingMouseStolenHandler = e => { + if (!this.#mouseTracker) { + e.preventDefault(); + this.#mouseTracker = e.detail.tracker; + this.unlistenMouseMove(); + } + } + + /** @param {CustomEvent} e */ + #trackingMouseGaveBackHandler = e => { + if (this.#mouseTracker == e.detail.tracker) { + e.preventDefault(); + this.#mouseTracker = null; + this.listenMouseMove(); + } + } + + /** + * @param {Element} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.listenOnFocus = true; + super(target, blueprint, options); + } + + listenMouseMove() { + this.target.addEventListener("mousemove", this.#mousemoveHandler); + } + + unlistenMouseMove() { + this.target.removeEventListener("mousemove", this.#mousemoveHandler); + } + + listenEvents() { + this.listenMouseMove(); + this.blueprint.addEventListener( + Configuration.trackingMouseEventName.begin, + /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); + this.blueprint.addEventListener( + Configuration.trackingMouseEventName.end, + /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler)); + } + + unlistenEvents() { + this.unlistenMouseMove(); + this.blueprint.removeEventListener( + Configuration.trackingMouseEventName.begin, + /** @type {(e: Event) => any} */(this.#trackingMouseStolenHandler)); + this.blueprint.removeEventListener( + Configuration.trackingMouseEventName.end, + /** @type {(e: Event) => any} */(this.#trackingMouseGaveBackHandler) + ); + } +} - acknowledgeReflow() { - this.requestUpdate(); - this.updateComplete.then(() => this.computeSizes()); - let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName); - this.dispatchEvent(reflowEvent); - } +/** + * @typedef {import("./IMouseClickDrag.js").Options & { + * scrollGraphEdge?: Boolean, + * }} Options + */ + +class Select extends IMouseClickDrag { + + constructor(target, blueprint, options = {}) { + options.scrollGraphEdge ??= true; + super(target, blueprint, options); + this.selectorElement = this.blueprint.template.selectorElement; + } + + startDrag() { + this.selectorElement.beginSelect(this.clickedPosition); + } + + /** + * @param {Coordinates} location + * @param {Coordinates} movement + */ + dragTo(location, movement) { + this.selectorElement.selectTo(location); + } + + endDrag() { + if (this.started) { + this.selectorElement.endSelect(); + } + } + + unclicked() { + if (!this.started) { + this.blueprint.unselectAll(); + } + } +} - setShowAdvancedPinDisplay(value) { - this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden"); - this.advancedPinDisplay = this.entity.AdvancedPinDisplay; - } +/** + * @typedef {import("../IInput.js").Options & { + * listenOnFocus?: Boolean, + * }} Options + */ + +class Unfocus extends IInput { + + /** @param {MouseEvent} e */ + #clickHandler = e => this.clickedSomewhere(/** @type {HTMLElement} */(e.target)) + + /** + * @param {HTMLElement} target + * @param {Blueprint} blueprint + * @param {Options} options + */ + constructor(target, blueprint, options = {}) { + options.listenOnFocus = true; + super(target, blueprint, options); + if (this.blueprint.focus) { + document.addEventListener("click", this.#clickHandler); + } + } + + /** @param {HTMLElement} target */ + clickedSomewhere(target) { + // If target is outside the blueprint grid + if (!target.closest("ueb-blueprint")) { + this.blueprint.setFocused(false); + } + } + + listenEvents() { + document.addEventListener("click", this.#clickHandler); + } + + unlistenEvents() { + document.removeEventListener("click", this.#clickHandler); + } +} - toggleShowAdvancedPinDisplay() { - this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay?.toString() != "Shown"); - } +/** @extends ITemplate */ +class BlueprintTemplate extends ITemplate { + + static styleVariables = { + "--ueb-font-size": `${Configuration.fontSize}`, + "--ueb-grid-axis-line-color": `${Configuration.gridAxisLineColor}`, + "--ueb-grid-expand": `${Configuration.expandGridSize}px`, + "--ueb-grid-line-color": `${Configuration.gridLineColor}`, + "--ueb-grid-line-width": `${Configuration.gridLineWidth}px`, + "--ueb-grid-set-line-color": `${Configuration.gridSetLineColor}`, + "--ueb-grid-set": `${Configuration.gridSet}`, + "--ueb-grid-size": `${Configuration.gridSize}px`, + "--ueb-link-min-width": `${Configuration.linkMinWidth}`, + "--ueb-node-radius": `${Configuration.nodeRadius}px`, + } + + #resizeObserver = new ResizeObserver(entries => { + const size = entries.find(entry => entry.target === this.viewportElement)?.devicePixelContentBoxSize?.[0]; + if (size) { + this.viewportSize[0] = size.inlineSize; + this.viewportSize[1] = size.blockSize; + } + }) + + /** @type {Copy} */ + #copyInputObject + + /** @type {Paste} */ + #pasteInputObject + + /** @type {Zoom} */ + #zoomInputObject + + /** @type {HTMLElement} */ headerElement + /** @type {HTMLElement} */ overlayElement + /** @type {HTMLElement} */ viewportElement + /** @type {SelectorElement} */ selectorElement + /** @type {HTMLElement} */ gridElement + /** @type {HTMLElement} */ linksContainerElement + /** @type {HTMLElement} */ nodesContainerElement + viewportSize = [0, 0] + + /** @param {Blueprint} element */ + initialize(element) { + super.initialize(element); + this.element.style.cssText = Object.entries(BlueprintTemplate.styleVariables) + .map(([k, v]) => `${k}:${v};`).join(""); + const htmlTemplate = /** @type {HTMLTemplateElement} */( + this.element.querySelector(":scope > template") + )?.content.textContent; + if (htmlTemplate) { + this.element.requestUpdate(); + this.element.updateComplete.then(() => { + this.blueprint.mousePosition = [ + Math.round(this.viewportSize[0] / 2), + Math.round(this.viewportSize[1] / 2), + ]; + this.getPasteInputObject().pasted(htmlTemplate); + this.blueprint.unselectAll(); + }); + } + } + + setup() { + super.setup(); + this.#resizeObserver.observe(this.viewportElement, { + box: "device-pixel-content-box", + }); + const bounding = this.viewportElement.getBoundingClientRect(); + this.viewportSize[0] = bounding.width; + this.viewportSize[1] = bounding.height; + if (this.blueprint.nodes.length > 0) { + this.blueprint.requestUpdate(); + this.blueprint.updateComplete.then(() => this.centerContentInViewport()); + } + } + + cleanup() { + super.cleanup(); + this.#resizeObserver.unobserve(this.viewportElement); + } + + createInputObjects() { + const gridElement = this.element.getGridDOMElement(); + this.#copyInputObject = new Copy(gridElement, this.blueprint); + this.#pasteInputObject = new Paste(gridElement, this.blueprint); + this.#zoomInputObject = new Zoom(gridElement, this.blueprint); + return [ + ...super.createInputObjects(), + this.#copyInputObject, + this.#pasteInputObject, + this.#zoomInputObject, + new Cut(gridElement, this.blueprint), + new KeyboardShortcut(gridElement, this.blueprint, { + activationKeys: Shortcuts.duplicateNodes + }, () => + this.blueprint.template.getPasteInputObject().pasted( + this.blueprint.template.getCopyInputObject().copied() + ) + ), + new KeyboardShortcut(gridElement, this.blueprint, { + activationKeys: Shortcuts.deleteNodes + }, () => this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))), + new KeyboardShortcut(gridElement, this.blueprint, { + activationKeys: Shortcuts.selectAllNodes + }, () => this.blueprint.selectAll()), + new Select(gridElement, this.blueprint, { + clickButton: Configuration.mouseClickButton, + exitAnyButton: true, + moveEverywhere: true, + }), + new MouseScrollGraph(gridElement, this.blueprint, { + clickButton: Configuration.mouseRightClickButton, + exitAnyButton: false, + moveEverywhere: true, + }), + new Unfocus(gridElement, this.blueprint), + new MouseTracking(gridElement, this.blueprint), + new KeyboardEnableZoom(gridElement, this.blueprint), + ] + } + + render() { + return x` +
+
+ Zoom ${this.blueprint.zoom == 0 ? "1:1" : (this.blueprint.zoom > 0 ? "+" : "") + this.blueprint.zoom} +
+
+
+
+
+
+
+
+ +
+
+
+ ` + } + + /** @param {PropertyValues} changedProperties */ + firstUpdated(changedProperties) { + super.firstUpdated(changedProperties); + this.headerElement = this.blueprint.querySelector('.ueb-viewport-header'); + this.overlayElement = this.blueprint.querySelector('.ueb-viewport-overlay'); + this.viewportElement = this.blueprint.querySelector('.ueb-viewport-body'); + this.selectorElement = this.blueprint.querySelector('ueb-selector'); + this.gridElement = this.viewportElement.querySelector(".ueb-grid"); + this.linksContainerElement = this.blueprint.querySelector("[data-links]"); + this.linksContainerElement.append(...this.blueprint.getLinks()); + this.nodesContainerElement = this.blueprint.querySelector("[data-nodes]"); + this.nodesContainerElement.append(...this.blueprint.getNodes()); + this.viewportElement.scroll(Configuration.expandGridSize, Configuration.expandGridSize); + } + + /** @param {PropertyValues} changedProperties */ + willUpdate(changedProperties) { + super.willUpdate(changedProperties); + if (this.headerElement && changedProperties.has("zoom")) { + this.headerElement.classList.add("ueb-zoom-changed"); + this.headerElement.addEventListener( + "animationend", + () => this.headerElement.classList.remove("ueb-zoom-changed") + ); + } + } + + /** @param {PropertyValues} changedProperties */ + updated(changedProperties) { + super.updated(changedProperties); + if (changedProperties.has("scrollX") || changedProperties.has("scrollY")) { + this.viewportElement.scroll(this.blueprint.scrollX, this.blueprint.scrollY); + } + if (changedProperties.has("zoom")) { + this.blueprint.style.setProperty("--ueb-scale", this.blueprint.getScale()); + const previousZoom = changedProperties.get("zoom"); + const minZoom = Math.min(previousZoom, this.blueprint.zoom); + const maxZoom = Math.max(previousZoom, this.blueprint.zoom); + const classes = Utility.range(minZoom, maxZoom); + const getClassName = v => `ueb-zoom-${v}`; + if (previousZoom < this.blueprint.zoom) { + this.blueprint.classList.remove(...classes.filter(v => v < 0).map(getClassName)); + this.blueprint.classList.add(...classes.filter(v => v > 0).map(getClassName)); + } else { + this.blueprint.classList.remove(...classes.filter(v => v > 0).map(getClassName)); + this.blueprint.classList.add(...classes.filter(v => v < 0).map(getClassName)); + } + } + } + + getCommentNodes(justSelected = false) { + return this.blueprint.querySelectorAll( + `ueb-node[data-type="${Configuration.paths.comment}"]${justSelected ? '[data-selected="true"]' : ''}` + + `, ueb-node[data-type="${Configuration.paths.materialGraphNodeComment}"]${justSelected ? '[data-selected="true"]' : ''}` + ) + } + + /** @param {PinReferenceEntity} pinReference */ + getPin(pinReference) { + return /** @type {PinElement} */(this.blueprint.querySelector( + `ueb-node[data-title="${pinReference.objectName}"] ueb-pin[data-id="${pinReference.pinGuid}"]` + )) + } + + getCopyInputObject() { + return this.#copyInputObject + } + + getPasteInputObject() { + return this.#pasteInputObject + } + + getZoomInputObject() { + return this.#zoomInputObject + } + + /** + * @param {Number} x + * @param {Number} y + */ + isPointVisible(x, y) { + return false + } + + gridTopVisibilityBoundary() { + return this.blueprint.scaleCorrect(this.blueprint.scrollY) - this.blueprint.translateY + } + + gridRightVisibilityBoundary() { + return this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0]) + } + + gridBottomVisibilityBoundary() { + return this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1]) + } + + gridLeftVisibilityBoundary() { + return this.blueprint.scaleCorrect(this.blueprint.scrollX) - this.blueprint.translateX + } + + centerViewport(x = 0, y = 0, smooth = true) { + const centerX = this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0] / 2); + const centerY = this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1] / 2); + this.blueprint.scrollDelta( + this.blueprint.scaleCorrectReverse(x - centerX), + this.blueprint.scaleCorrectReverse(y - centerY), + smooth + ); + } + + centerContentInViewport(smooth = true) { + let avgX = 0; + let avgY = 0; + let minX = Number.MAX_SAFE_INTEGER; + let maxX = Number.MIN_SAFE_INTEGER; + let minY = Number.MAX_SAFE_INTEGER; + let maxY = Number.MIN_SAFE_INTEGER; + const nodes = this.blueprint.getNodes(); + for (const node of nodes) { + avgX += node.leftBoundary() + node.rightBoundary(); + avgY += node.topBoundary() + node.bottomBoundary(); + minX = Math.min(minX, node.leftBoundary()); + maxX = Math.max(maxX, node.rightBoundary()); + minY = Math.min(minY, node.topBoundary()); + maxY = Math.max(maxY, node.bottomBoundary()); + } + avgX = Math.round(maxX - minX <= this.viewportSize[0] + ? (maxX + minX) / 2 + : avgX / (2 * nodes.length) + ); + avgY = Math.round(maxY - minY <= this.viewportSize[1] + ? (maxY + minY) / 2 + : avgY / (2 * nodes.length) + ); + this.centerViewport(avgX, avgY, smooth); + } } -/** @extends {IElement} */ +/** @extends {IElement} */ class Blueprint extends IElement { static properties = { @@ -10279,8 +10742,6 @@ class Blueprint extends IElement { node.setSelected(selected); } - /** @type {Map} */ - #nodeNameCounter = new Map() #xScrollingAnimationId = 0 #yScrollingAnimationId = 0 /** @type {NodeElement[]}" */ @@ -10303,7 +10764,7 @@ class Blueprint extends IElement { this.scrollY = Configuration.expandGridSize; this.translateX = Configuration.expandGridSize; this.translateY = Configuration.expandGridSize; - super.initialize({}, new BlueprintTemplate()); + super.initialize(new BlueprintEntity(), new BlueprintTemplate()); } initialize() { @@ -10597,10 +11058,10 @@ class Blueprint extends IElement { const removeEventHandler = event => { const target = event.currentTarget; target.removeEventListener(Configuration.removeEventName, removeEventHandler); - const graphElementsArray = target instanceof NodeElement - ? this.nodes + const [graphElementsArray, entity] = target instanceof NodeElement + ? [this.nodes, target.entity] : target instanceof LinkElement - ? this.links + ? [this.links] : null; // @ts-expect-error const index = graphElementsArray?.indexOf(target); @@ -10610,24 +11071,27 @@ class Blueprint extends IElement { graphElementsArray[index] = last; } } + if (entity) { + this.entity.removeObjectEntity(entity); + } }; for (const element of graphElements) { element.blueprint = this; if (element instanceof NodeElement && !this.nodes.includes(element)) { + if (element.getType() == Configuration.paths.niagaraClipboardContent) { + this.entity = this.entity.mergeWith(element.entity); + const additionalSerialization = atob(element.entity.ExportedNodes); + this.template.getPasteInputObject().pasted(additionalSerialization) + .forEach(node => node.entity.isExported = true); + continue + } const name = element.entity.getObjectName(); - const homonymNode = this.nodes.find(node => node.entity.getObjectName() == name); - if (homonymNode) { - // Inserted node keeps tha name and the homonym nodes is renamed - let name = homonymNode.entity.getObjectName(true); - this.#nodeNameCounter[name] = this.#nodeNameCounter[name] ?? -1; - do { - ++this.#nodeNameCounter[name]; - } while (this.nodes.find(node => - node.entity.getObjectName() == Configuration.nodeTitle(name, this.#nodeNameCounter[name]) - )) - homonymNode.rename(Configuration.nodeTitle(name, this.#nodeNameCounter[name])); + const homonym = this.entity.getHomonymObjectEntity(element.entity); + if (homonym) { + homonym.Name = this.entity.takeFreeName(name); } this.nodes.push(element); + this.entity.addObjectEntity(element.entity); element.addEventListener(Configuration.removeEventName, removeEventHandler); this.template.nodesContainerElement?.appendChild(element); } else if (element instanceof LinkElement && !this.links.includes(element)) { @@ -11942,7 +12406,7 @@ class StringPinTemplate extends IInputPinTemplate { /** * @extends INumericPinTemplate */ -class VectorInputPinTemplate extends INumericPinTemplate { +class Vector2DPinTemplate extends INumericPinTemplate { #getX() { return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0) @@ -11982,6 +12446,65 @@ class VectorInputPinTemplate extends INumericPinTemplate { } } +/** @extends INumericPinTemplate */ +class Vector4DPinTemplate extends INumericPinTemplate { + + #getX() { + return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0) + } + + #getY() { + return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0) + } + + #getZ() { + return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0) + } + + #getW() { + return Utility.printNumber(this.element.getDefaultValue()?.W ?? 0) + } + + /** + * @param {Number[]} values + * @param {String[]} rawValues + */ + setDefaultValue(values, rawValues) { + const vector = this.element.getDefaultValue(true); + if (!(vector instanceof Vector4DEntity)) { + throw new TypeError("Expected DefaultValue to be a Vector4DEntity") + } + vector.X = values[0]; + vector.Y = values[1]; + vector.Z = values[2]; + vector.W = values[3]; + this.element.requestUpdate("DefaultValue", vector); + } + + renderInput() { + return x` +
+ X +
+ +
+ Y +
+ +
+ Z +
+ +
+ W +
+ +
+
+ ` + } +} + /** @extends INumericPinTemplate */ class VectorPinTemplate extends INumericPinTemplate { @@ -12032,29 +12555,47 @@ class VectorPinTemplate extends INumericPinTemplate { } } +const inputPinTemplates = { + [Configuration.paths.linearColor]: LinearColorPinTemplate, + [Configuration.paths.niagaraBool]: BoolPinTemplate, + [Configuration.paths.niagaraPosition]: VectorPinTemplate, + [Configuration.paths.rotator]: RotatorPinTemplate, + [Configuration.paths.vector]: VectorPinTemplate, + [Configuration.paths.vector2D]: Vector2DPinTemplate, + [Configuration.paths.vector3f]: VectorPinTemplate, + [Configuration.paths.vector4f]: Vector4DPinTemplate, + "bool": BoolPinTemplate, + "byte": IntPinTemplate, + "enum": EnumPinTemplate, + "int": IntPinTemplate, + "int64": Int64PinTemplate, + "MUTABLE_REFERENCE": ReferencePinTemplate, + "name": NamePinTemplate, + "rg": Vector2DPinTemplate, + "real": RealPinTemplate, + "string": StringPinTemplate, +}; + +/** @param {PinEntity} entity */ +function pinTemplate(entity) { + if (entity.PinType.ContainerType?.toString() === "Array") { + return PinTemplate + } + if (entity.PinType.bIsReference && !entity.PinType.bIsConst) { + return inputPinTemplates["MUTABLE_REFERENCE"] + } + if (entity.getType() === "exec") { + return ExecPinTemplate + } + return (entity.isInput() ? inputPinTemplates[entity.getType()] : PinTemplate) ?? PinTemplate +} + /** * @template {TerminalAttribute} T * @extends {IElement, PinTemplate>} */ class PinElement extends IElement { - static #inputPinTemplates = { - "bool": BoolPinTemplate, - "byte": IntPinTemplate, - "enum": EnumPinTemplate, - "int": IntPinTemplate, - "int64": Int64PinTemplate, - "MUTABLE_REFERENCE": ReferencePinTemplate, - "name": NamePinTemplate, - "rg": VectorInputPinTemplate, - "real": RealPinTemplate, - "string": StringPinTemplate, - [Configuration.paths.linearColor]: LinearColorPinTemplate, - [Configuration.paths.rotator]: RotatorPinTemplate, - [Configuration.paths.vector]: VectorPinTemplate, - [Configuration.paths.vector2D]: VectorInputPinTemplate, - } - static properties = { pinId: { type: GuidEntity, @@ -12114,23 +12655,9 @@ class PinElement extends IElement { /** @type {NodeElement} */ nodeElement - /** @param {PinEntity} pinEntity */ - static getTypeTemplate(pinEntity) { - if (pinEntity.PinType.ContainerType?.toString() === "Array") { - return PinTemplate - } - if (pinEntity.PinType.bIsReference && !pinEntity.PinType.bIsConst) { - return PinElement.#inputPinTemplates["MUTABLE_REFERENCE"] - } - if (pinEntity.getType() === "exec") { - return ExecPinTemplate - } - return (pinEntity.isInput() ? PinElement.#inputPinTemplates[pinEntity.getType()] : PinTemplate) ?? PinTemplate - } - static newObject( entity = new PinEntity(), - template = /** @type {PinTemplate} */(new (PinElement.getTypeTemplate(entity))()), + template = /** @type {PinTemplate} */(new (pinTemplate(entity))()), nodeElement = undefined ) { const result = new PinElement(); @@ -12140,7 +12667,7 @@ class PinElement extends IElement { initialize( entity = /** @type {PinEntity} */(new PinEntity()), - template = /** @type {PinTemplate} */(new (PinElement.getTypeTemplate(entity))()), + template = /** @type {PinTemplate} */(new (pinTemplate(entity))()), nodeElement = undefined ) { this.nodeElement = nodeElement; @@ -12177,7 +12704,7 @@ class PinElement extends IElement { } getPinDisplayName() { - return this.entity.pinDisplayName() + return this.entity.pinTitle() } /** @return {CSSResult} */ @@ -12790,8 +13317,11 @@ class CustomSerializer extends Serializer { class ToStringSerializer extends Serializer { /** @param {T} entityType */ - constructor(entityType) { + constructor(entityType, escape = true) { super(entityType); + if (escape) { + this.wrap = (entity, serialized) => Utility.escapeString(serialized); + } } /** @@ -12799,9 +13329,10 @@ class ToStringSerializer extends Serializer { * @param {Boolean} insideString */ doWrite(entity, insideString, indentation = "") { + return !insideString && entity.constructor === String - ? `"${Utility.escapeString(entity.toString())}"` // String will have quotes if not inside a string already - : Utility.escapeString(entity.toString()) + ? `"${this.wrap(entity, entity.toString())}"` // String will have quotes if not inside a string already + : this.wrap(entity, entity.toString()) } } @@ -12809,24 +13340,26 @@ Grammar.unknownValue = Parsernostrum.alt( // Remember to keep the order, otherwise parsing might fail Grammar.boolean, - GuidEntity.createGrammar(), + GuidEntity.grammar, Parsernostrum.str("None").map(() => new ObjectReferenceEntity({ type: "None" })), Grammar.null, Grammar.number, ObjectReferenceEntity.fullReferenceGrammar, Grammar.string, - LocalizedTextEntity.createGrammar(), - InvariantTextEntity.createGrammar(), - FormatTextEntity.createGrammar(), - PinReferenceEntity.createGrammar(), - VectorEntity.createGrammar(), - RotatorEntity.createGrammar(), - LinearColorEntity.createGrammar(), - Vector2DEntity.createGrammar(), - UnknownKeysEntity.createGrammar(), - SymbolEntity.createGrammar(), + LocalizedTextEntity.grammar, + InvariantTextEntity.grammar, + FormatTextEntity.grammar, + PinReferenceEntity.grammar, + Vector4DEntity.grammar, + VectorEntity.grammar, + RotatorEntity.grammar, + LinearColorEntity.grammar, + Vector2DEntity.grammar, + UnknownKeysEntity.grammar, + SymbolEntity.grammar, Grammar.grammarFor(undefined, [PinReferenceEntity]), Grammar.grammarFor(undefined, [new Union(Number, String, SymbolEntity)]), + Parsernostrum.lazy(() => Grammar.grammarFor(undefined, [undefined])), ); function initializeSerializerFactory() { @@ -12844,10 +13377,8 @@ function initializeSerializerFactory() { new CustomSerializer( (array, insideString) => `(${array - .map(v => - SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString) + "," - ) - .join("") + .map(v => SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString)) + .join(",") })`, Array ) @@ -12858,6 +13389,11 @@ function initializeSerializerFactory() { new ToStringSerializer(BigInt) ); + SerializerFactory.registerSerializer( + BlueprintEntity, + new ObjectSerializer(BlueprintEntity), + ); + SerializerFactory.registerSerializer( Boolean, new CustomSerializer( @@ -12977,22 +13513,7 @@ function initializeSerializerFactory() { SerializerFactory.registerSerializer( ObjectReferenceEntity, - new CustomSerializer( - objectReference => { - let type = objectReference.type ?? ""; - let path = objectReference.path ?? ""; - let delim = objectReference.delim ?? ""; - if (type && path && Utility.isSerialized(objectReference, "path")) { - path = delim + path + delim.split("").reverse().join(""); - } - let result = type + path; - if (Utility.isSerialized(objectReference, "type")) { - result = `"${result}"`; - } - return result - }, - ObjectReferenceEntity - ) + new ToStringSerializer(ObjectReferenceEntity, false) ); SerializerFactory.registerSerializer( @@ -13002,7 +13523,7 @@ function initializeSerializerFactory() { SerializerFactory.registerSerializer( PinEntity, - new Serializer(PinEntity, (entity, v) => `${entity.getLookbehind()} (${v})`, ",", false) + new Serializer(PinEntity, (entity, v) => `${entity.getLookbehind()} (${v})`, ",", true) ); SerializerFactory.registerSerializer( @@ -13033,6 +13554,11 @@ function initializeSerializerFactory() { new Serializer(RotatorEntity, Serializer.bracketsWrapped) ); + SerializerFactory.registerSerializer( + ScriptVariableEntity, + new Serializer(ScriptVariableEntity, Serializer.bracketsWrapped) + ); + SerializerFactory.registerSerializer( String, new CustomSerializer( @@ -13067,6 +13593,14 @@ function initializeSerializerFactory() { ) ); + SerializerFactory.registerSerializer( + SimpleSerializationVector4DEntity, + new CustomSerializer( + (value, insideString) => `${value.X}, ${value.Y}, ${value.Z}, ${value.W}`, + SimpleSerializationVector4DEntity + ) + ); + SerializerFactory.registerSerializer( SymbolEntity, new ToStringSerializer(SymbolEntity) @@ -13091,6 +13625,11 @@ function initializeSerializerFactory() { VectorEntity, new Serializer(VectorEntity, Serializer.bracketsWrapped) ); + + SerializerFactory.registerSerializer( + Vector4DEntity, + new Serializer(Vector4DEntity, Serializer.bracketsWrapped) + ); } initializeSerializerFactory(); diff --git a/dist/ueblueprint.min.js b/dist/ueblueprint.min.js index e37498af..9b65e11f 100644 --- a/dist/ueblueprint.min.js +++ b/dist/ueblueprint.min.js @@ -3,31 +3,31 @@ * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -const e=window,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;class r{constructor(e,t,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=s.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&s.set(i,e))}return e}toString(){return this.cssText}}const n=(e,...t)=>{const s=1===e.length?e[0]:t.reduce(((t,i,s)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[s+1]),e[0]);return new r(s,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e +const e=window,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),r=new WeakMap;class s{constructor(e,t,r){if(this._$cssResult$=!0,r!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=r.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&r.set(i,e))}return e}toString(){return this.cssText}}const n=(e,...t)=>{const r=1===e.length?e[0]:t.reduce(((t,i,r)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[r+1]),e[0]);return new s(r,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new s("string"==typeof e?e:e+"",void 0,i))(t)})(e):e /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */;var o;const l=window,c=l.trustedTypes,u=c?c.emptyScript:"",h=l.reactiveElementPolyfillSupport,p={toAttribute(e,t){switch(t){case Boolean:e=e?u:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},d=(e,t)=>t!==e&&(t==t||e==e),m={attribute:!0,type:String,converter:p,reflect:!1,hasChanged:d},g="finalized";class b extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(e){var t;this.finalize(),(null!==(t=this.h)&&void 0!==t?t:this.h=[]).push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,i)=>{const s=this._$Ep(i,t);void 0!==s&&(this._$Ev.set(s,i),e.push(s))})),e}static createProperty(e,t=m){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const i="symbol"==typeof e?Symbol():"__"+e,s=this.getPropertyDescriptor(e,i,t);void 0!==s&&Object.defineProperty(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(s){const r=this[e];this[t]=s,this.requestUpdate(e,r,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||m}static finalize(){if(this.hasOwnProperty(g))return!1;this[g]=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),void 0!==e.h&&(this.h=[...e.h]),this.elementProperties=new Map(e.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const i of t)this.createProperty(i,e[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(a(e))}else void 0!==e&&t.push(a(e));return t}static _$Ep(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}_$Eu(){var e;this._$E_=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(e=this.constructor.h)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,i;(null!==(t=this._$ES)&&void 0!==t?t:this._$ES=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(i=e.hostConnected)||void 0===i||i.call(e))}removeController(e){var t;null===(t=this._$ES)||void 0===t||t.splice(this._$ES.indexOf(e)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Ei.set(t,this[t]),delete this[t])}))}createRenderRoot(){var i;const s=null!==(i=this.shadowRoot)&&void 0!==i?i:this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{t?i.adoptedStyleSheets=s.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):s.forEach((t=>{const s=document.createElement("style"),r=e.litNonce;void 0!==r&&s.setAttribute("nonce",r),s.textContent=t.cssText,i.appendChild(s)}))})(s,this.constructor.elementStyles),s}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$EO(e,t,i=m){var s;const r=this.constructor._$Ep(e,i);if(void 0!==r&&!0===i.reflect){const n=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:p).toAttribute(t,i.type);this._$El=e,null==n?this.removeAttribute(r):this.setAttribute(r,n),this._$El=null}}_$AK(e,t){var i;const s=this.constructor,r=s._$Ev.get(e);if(void 0!==r&&this._$El!==r){const e=s.getPropertyOptions(r),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==(null===(i=e.converter)||void 0===i?void 0:i.fromAttribute)?e.converter:p;this._$El=r,this[r]=n.fromAttribute(t,e.type),this._$El=null}}requestUpdate(e,t,i){let s=!0;void 0!==e&&(((i=i||this.constructor.getPropertyOptions(e)).hasChanged||d)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===i.reflect&&this._$El!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((e,t)=>this[t]=e)),this._$Ei=void 0);let t=!1;const i=this._$AL;try{t=this.shouldUpdate(i),t?(this.willUpdate(i),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(i)):this._$Ek()}catch(e){throw t=!1,this._$Ek(),e}t&&this._$AE(i)}willUpdate(e){}_$AE(e){var t;null===(t=this._$ES)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$EO(t,this[t],e))),this._$EC=void 0),this._$Ek()}updated(e){}firstUpdated(e){}} + */;var o;const l=window,c=l.trustedTypes,u=c?c.emptyScript:"",h=l.reactiveElementPolyfillSupport,p={toAttribute(e,t){switch(t){case Boolean:e=e?u:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},d=(e,t)=>t!==e&&(t==t||e==e),m={attribute:!0,type:String,converter:p,reflect:!1,hasChanged:d},g="finalized";class b extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(e){var t;this.finalize(),(null!==(t=this.h)&&void 0!==t?t:this.h=[]).push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,i)=>{const r=this._$Ep(i,t);void 0!==r&&(this._$Ev.set(r,i),e.push(r))})),e}static createProperty(e,t=m){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const i="symbol"==typeof e?Symbol():"__"+e,r=this.getPropertyDescriptor(e,i,t);void 0!==r&&Object.defineProperty(this.prototype,e,r)}}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(r){const s=this[e];this[t]=r,this.requestUpdate(e,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||m}static finalize(){if(this.hasOwnProperty(g))return!1;this[g]=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),void 0!==e.h&&(this.h=[...e.h]),this.elementProperties=new Map(e.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const i of t)this.createProperty(i,e[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(a(e))}else void 0!==e&&t.push(a(e));return t}static _$Ep(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}_$Eu(){var e;this._$E_=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(e=this.constructor.h)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,i;(null!==(t=this._$ES)&&void 0!==t?t:this._$ES=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(i=e.hostConnected)||void 0===i||i.call(e))}removeController(e){var t;null===(t=this._$ES)||void 0===t||t.splice(this._$ES.indexOf(e)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Ei.set(t,this[t]),delete this[t])}))}createRenderRoot(){var i;const r=null!==(i=this.shadowRoot)&&void 0!==i?i:this.attachShadow(this.constructor.shadowRootOptions);return((i,r)=>{t?i.adoptedStyleSheets=r.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):r.forEach((t=>{const r=document.createElement("style"),s=e.litNonce;void 0!==s&&r.setAttribute("nonce",s),r.textContent=t.cssText,i.appendChild(r)}))})(r,this.constructor.elementStyles),r}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$EO(e,t,i=m){var r;const s=this.constructor._$Ep(e,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==(null===(r=i.converter)||void 0===r?void 0:r.toAttribute)?i.converter:p).toAttribute(t,i.type);this._$El=e,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$El=null}}_$AK(e,t){var i;const r=this.constructor,s=r._$Ev.get(e);if(void 0!==s&&this._$El!==s){const e=r.getPropertyOptions(s),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==(null===(i=e.converter)||void 0===i?void 0:i.fromAttribute)?e.converter:p;this._$El=s,this[s]=n.fromAttribute(t,e.type),this._$El=null}}requestUpdate(e,t,i){let r=!0;void 0!==e&&(((i=i||this.constructor.getPropertyOptions(e)).hasChanged||d)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===i.reflect&&this._$El!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,i))):r=!1),!this.isUpdatePending&&r&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((e,t)=>this[t]=e)),this._$Ei=void 0);let t=!1;const i=this._$AL;try{t=this.shouldUpdate(i),t?(this.willUpdate(i),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(i)):this._$Ek()}catch(e){throw t=!1,this._$Ek(),e}t&&this._$AE(i)}willUpdate(e){}_$AE(e){var t;null===(t=this._$ES)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$EO(t,this[t],e))),this._$EC=void 0),this._$Ek()}updated(e){}firstUpdated(e){}} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -var v;b[g]=!0,b.elementProperties=new Map,b.elementStyles=[],b.shadowRootOptions={mode:"open"},null==h||h({ReactiveElement:b}),(null!==(o=l.reactiveElementVersions)&&void 0!==o?o:l.reactiveElementVersions=[]).push("1.6.3");const y=window,f=y.trustedTypes,w=f?f.createPolicy("lit-html",{createHTML:e=>e}):void 0,S="$lit$",E=`lit$${(Math.random()+"").slice(9)}$`,C="?"+E,x=`<${C}>`,P=document,N=()=>P.createComment(""),k=e=>null===e||"object"!=typeof e&&"function"!=typeof e,T=Array.isArray,A="[ \t\n\f\r]",L=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,M=/-->/g,I=/>/g,B=RegExp(`>|${A}(?:([^\\s"'>=/]+)(${A}*=${A}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),$=/'/g,H=/"/g,V=/^(?:script|style|textarea|title)$/i,G=(e=>(t,...i)=>({_$litType$:e,strings:t,values:i}))(1),D=Symbol.for("lit-noChange"),O=Symbol.for("lit-nothing"),R=new WeakMap,z=P.createTreeWalker(P,129,null,!1);function _(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==w?w.createHTML(t):t}const F=(e,t)=>{const i=e.length-1,s=[];let r,n=2===t?"":"",a=L;for(let t=0;t"===l[0]?(a=null!=r?r:L,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,o=l[1],a=void 0===l[3]?B:'"'===l[3]?H:$):a===H||a===$?a=B:a===M||a===I?a=L:(a=B,r=void 0);const h=a===B&&e[t+1].startsWith("/>")?" ":"";n+=a===L?i+x:c>=0?(s.push(o),i.slice(0,c)+S+i.slice(c)+E+h):i+E+(-2===c?(s.push(void 0),t):h)}return[_(e,n+(e[i]||"")+(2===t?"":"")),s]};class j{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let r=0,n=0;const a=e.length-1,o=this.parts,[l,c]=F(e,t);if(this.el=j.createElement(l,i),z.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(s=z.nextNode())&&o.length0){s.textContent=f?f.emptyScript:"";for(let i=0;iT(e)||"function"==typeof(null==e?void 0:e[Symbol.iterator]))(e)?this.T(e):this._(e)}k(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}$(e){this._$AH!==e&&(this._$AR(),this._$AH=this.k(e))}_(e){this._$AH!==O&&k(this._$AH)?this._$AA.nextSibling.data=e:this.$(P.createTextNode(e)),this._$AH=e}g(e){var t;const{values:i,_$litType$:s}=e,r="number"==typeof s?this._$AC(e):(void 0===s.el&&(s.el=j.createElement(_(s.h,s.h[0]),this.options)),s);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===r)this._$AH.v(i);else{const e=new W(r,this),t=e.u(this.options);e.v(i),this.$(t),this._$AH=e}}_$AC(e){let t=R.get(e.strings);return void 0===t&&R.set(e.strings,t=new j(e)),t}T(e){T(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const r of e)s===t.length?t.push(i=new K(this.k(N()),this.k(N()),this,this.options)):i=t[s],i._$AI(r),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=O}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,i,s){const r=this.strings;let n=!1;if(void 0===r)e=U(this,e,t,0),n=!k(e)||e!==this._$AH&&e!==D,n&&(this._$AH=e);else{const s=e;let a,o;for(e=r[0],a=0;ae}):void 0,S="$lit$",E=`lit$${(Math.random()+"").slice(9)}$`,C="?"+E,N=`<${C}>`,x=document,P=()=>x.createComment(""),k=e=>null===e||"object"!=typeof e&&"function"!=typeof e,T=Array.isArray,A="[ \t\n\f\r]",L=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,M=/-->/g,I=/>/g,B=RegExp(`>|${A}(?:([^\\s"'>=/]+)(${A}*=${A}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),V=/'/g,$=/"/g,O=/^(?:script|style|textarea|title)$/i,H=(e=>(t,...i)=>({_$litType$:e,strings:t,values:i}))(1),D=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),R=new WeakMap,_=x.createTreeWalker(x,129,null,!1);function z(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==w?w.createHTML(t):t}const F=(e,t)=>{const i=e.length-1,r=[];let s,n=2===t?"":"",a=L;for(let t=0;t"===l[0]?(a=null!=s?s:L,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,o=l[1],a=void 0===l[3]?B:'"'===l[3]?$:V):a===$||a===V?a=B:a===M||a===I?a=L:(a=B,s=void 0);const h=a===B&&e[t+1].startsWith("/>")?" ":"";n+=a===L?i+N:c>=0?(r.push(o),i.slice(0,c)+S+i.slice(c)+E+h):i+E+(-2===c?(r.push(void 0),t):h)}return[z(e,n+(e[i]||"")+(2===t?"":"")),r]};class j{constructor({strings:e,_$litType$:t},i){let r;this.parts=[];let s=0,n=0;const a=e.length-1,o=this.parts,[l,c]=F(e,t);if(this.el=j.createElement(l,i),_.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(r=_.nextNode())&&o.length0){r.textContent=f?f.emptyScript:"";for(let i=0;iT(e)||"function"==typeof(null==e?void 0:e[Symbol.iterator]))(e)?this.T(e):this._(e)}k(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}$(e){this._$AH!==e&&(this._$AR(),this._$AH=this.k(e))}_(e){this._$AH!==G&&k(this._$AH)?this._$AA.nextSibling.data=e:this.$(x.createTextNode(e)),this._$AH=e}g(e){var t;const{values:i,_$litType$:r}=e,s="number"==typeof r?this._$AC(e):(void 0===r.el&&(r.el=j.createElement(z(r.h,r.h[0]),this.options)),r);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===s)this._$AH.v(i);else{const e=new U(s,this),t=e.u(this.options);e.v(i),this.$(t),this._$AH=e}}_$AC(e){let t=R.get(e.strings);return void 0===t&&R.set(e.strings,t=new j(e)),t}T(e){T(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,r=0;for(const s of e)r===t.length?t.push(i=new K(this.k(P()),this.k(P()),this,this.options)):i=t[r],i._$AI(s),r++;r2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,i,r){const s=this.strings;let n=!1;if(void 0===s)e=W(this,e,t,0),n=!k(e)||e!==this._$AH&&e!==D,n&&(this._$AH=e);else{const r=e;let a,o;for(e=s[0],a=0;a{var s,r;const n=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:t;let a=n._$litPart$;if(void 0===a){const e=null!==(r=null==i?void 0:i.renderBefore)&&void 0!==r?r:null;n._$litPart$=a=new K(t.insertBefore(N(),e),e,void 0,null!=i?i:{})}return a._$AI(e),a})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!1)}render(){return D}}se.finalized=!0,se._$litElement$=!0,null===(te=globalThis.litElementHydrateSupport)||void 0===te||te.call(globalThis,{LitElement:se});const re=globalThis.litElementPolyfillSupport;null==re||re({LitElement:se}),(null!==(ie=globalThis.litElementVersions)&&void 0!==ie?ie:globalThis.litElementVersions=[]).push("3.3.3");class ne{static nodeColors={black:n``,blue:n``,darkBlue:n``,darkTurquoise:n``,gray:n``,green:n``,lime:n``,red:n``,turquoise:n``,violet:n``,yellow:n``};static alphaPattern="repeating-conic-gradient(#7c8184 0% 25%, #c2c3c4 0% 50%) 50% / 10px 10px";static colorDragEventName="ueb-color-drag";static colorPickEventName="ueb-color-pick";static colorWindowEventName="ueb-color-window";static colorWindowName="Color Picker";static defaultCommentHeight=96;static defaultCommentWidth=400;static distanceThreshold=5;static dragEventName="ueb-drag";static dragGeneralEventName="ueb-drag-general";static edgeScrollThreshold=50;static editTextEventName={begin:"ueb-edit-text-begin",end:"ueb-edit-text-end"};static expandGridSize=400;static focusEventName={begin:"blueprint-focus",end:"blueprint-unfocus"};static fontSize=n``;static gridAxisLineColor=n``;static gridExpandThreshold=.25;static gridLineColor=n``;static gridLineWidth=1;static gridSet=8;static gridSetLineColor=n``;static gridShrinkThreshold=4;static gridSize=16;static hexColorRegex=/^\s*#(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})([0-9a-fA-F]{2})?|#(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])\s*$/;static indentation=" ";static keysSeparator=/[\.\(\)]/;static knotOffset=[-ne.gridSize,-.5*ne.gridSize];static lineTracePattern=/LineTrace(Single|Multi)(\w*)/;static linkCurveHeight=15;static linkCurveWidth=80;static linkMinWidth=100;static nameRegexSpaceReplacement=new RegExp("^K2(?:[Nn]ode)?_|(?<=[a-z])(?=[A-Z0-9])|(?<=[A-Z])(?{let s=100-e;return`M ${e} 0 C ${t.toFixed(3)} 0, ${i.toFixed(3)} 0, 50 50 S ${(s-t+e).toFixed(3)} 100, ${s.toFixed(3)} 100`};static maxZoom=7;static minZoom=-12;static mouseClickButton=0;static mouseRightClickButton=2;static mouseWheelZoomThreshold=80;static nodeDragEventName="ueb-node-drag";static nodeDragGeneralEventName="ueb-node-drag-general";static nodeTitle=(e,t)=>`${e}_${t}`;static nodeRadius=8;static nodeReflowEventName="ueb-node-reflow";static paths={actorBoundEvent:"/Script/BlueprintGraph.K2Node_ActorBoundEvent",addDelegate:"/Script/BlueprintGraph.K2Node_AddDelegate",ambientSound:"/Script/Engine.AmbientSound",asyncAction:"/Script/BlueprintGraph.K2Node_AsyncAction",blueprint:"/Script/Engine.Blueprint",blueprintGameplayTagLibrary:"/Script/GameplayTags.BlueprintGameplayTagLibrary",blueprintMapLibrary:"/Script/Engine.BlueprintMapLibrary",blueprintSetLibrary:"/Script/Engine.BlueprintSetLibrary",callArrayFunction:"/Script/BlueprintGraph.K2Node_CallArrayFunction",callDelegate:"/Script/BlueprintGraph.K2Node_CallDelegate",callFunction:"/Script/BlueprintGraph.K2Node_CallFunction",comment:"/Script/UnrealEd.EdGraphNode_Comment",commutativeAssociativeBinaryOperator:"/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator",componentBoundEvent:"/Script/BlueprintGraph.K2Node_ComponentBoundEvent",createDelegate:"/Script/BlueprintGraph.K2Node_CreateDelegate",customEvent:"/Script/BlueprintGraph.K2Node_CustomEvent",doN:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:Do N",doOnce:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:DoOnce",dynamicCast:"/Script/BlueprintGraph.K2Node_DynamicCast",eAttachmentRule:"/Script/Engine.EAttachmentRule",edGraph:"/Script/Engine.EdGraph",eDrawDebugTrace:"/Script/Engine.EDrawDebugTrace",eMaterialSamplerType:"/Script/Engine.EMaterialSamplerType",enum:"/Script/CoreUObject.Enum",enumLiteral:"/Script/BlueprintGraph.K2Node_EnumLiteral",eSamplerSourceMode:"/Script/Engine.ESamplerSourceMode",eSearchCase:"/Script/CoreUObject.ESearchCase",eSearchDir:"/Script/CoreUObject.ESearchDir",eSpawnActorCollisionHandlingMethod:"/Script/Engine.ESpawnActorCollisionHandlingMethod",eTextureMipValueMode:"/Script/Engine.ETextureMipValueMode",eTraceTypeQuery:"/Script/Engine.ETraceTypeQuery",event:"/Script/BlueprintGraph.K2Node_Event",executionSequence:"/Script/BlueprintGraph.K2Node_ExecutionSequence",flipflop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:FlipFlop",forEachElementInEnum:"/Script/BlueprintGraph.K2Node_ForEachElementInEnum",forEachLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForEachLoop",forEachLoopWithBreak:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForEachLoopWithBreak",forLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForLoop",forLoopWithBreak:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForLoopWithBreak",functionEntry:"/Script/BlueprintGraph.K2Node_FunctionEntry",functionResult:"/Script/BlueprintGraph.K2Node_FunctionResult",gameplayTag:"/Script/GameplayTags.GameplayTag",getInputAxisKeyValue:"/Script/BlueprintGraph.K2Node_GetInputAxisKeyValue",ifThenElse:"/Script/BlueprintGraph.K2Node_IfThenElse",inputAxisKeyEvent:"/Script/BlueprintGraph.K2Node_InputAxisKeyEvent",inputDebugKey:"/Script/InputBlueprintNodes.K2Node_InputDebugKey",inputKey:"/Script/BlueprintGraph.K2Node_InputKey",inputVectorAxisEvent:"/Script/BlueprintGraph.K2Node_InputVectorAxisEvent",isValid:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:IsValid",kismetArrayLibrary:"/Script/Engine.KismetArrayLibrary",kismetMathLibrary:"/Script/Engine.KismetMathLibrary",knot:"/Script/BlueprintGraph.K2Node_Knot",linearColor:"/Script/CoreUObject.LinearColor",literal:"/Script/BlueprintGraph.K2Node_Literal",macro:"/Script/BlueprintGraph.K2Node_MacroInstance",makeArray:"/Script/BlueprintGraph.K2Node_MakeArray",makeMap:"/Script/BlueprintGraph.K2Node_MakeMap",makeSet:"/Script/BlueprintGraph.K2Node_MakeSet",makeStruct:"/Script/BlueprintGraph.K2Node_MakeStruct",materialExpressionComponentMask:"/Script/Engine.MaterialExpressionComponentMask",materialExpressionConstant:"/Script/Engine.MaterialExpressionConstant",materialExpressionConstant2Vector:"/Script/Engine.MaterialExpressionConstant2Vector",materialExpressionConstant3Vector:"/Script/Engine.MaterialExpressionConstant3Vector",materialExpressionConstant4Vector:"/Script/Engine.MaterialExpressionConstant4Vector",materialExpressionFunctionInput:"/Script/Engine.MaterialExpressionFunctionInput",materialExpressionLogarithm:"/Script/InterchangeImport.MaterialExpressionLogarithm",materialExpressionLogarithm10:"/Script/Engine.MaterialExpressionLogarithm10",materialExpressionLogarithm2:"/Script/Engine.MaterialExpressionLogarithm2",materialExpressionMaterialFunctionCall:"/Script/Engine.MaterialExpressionMaterialFunctionCall",materialExpressionSquareRoot:"/Script/Engine.MaterialExpressionSquareRoot",materialExpressionTextureCoordinate:"/Script/Engine.MaterialExpressionTextureCoordinate",materialExpressionTextureSample:"/Script/Engine.MaterialExpressionTextureSample",materialGraphNode:"/Script/UnrealEd.MaterialGraphNode",materialGraphNodeComment:"/Script/UnrealEd.MaterialGraphNode_Comment",multiGate:"/Script/BlueprintGraph.K2Node_MultiGate",pawn:"/Script/Engine.Pawn",pcgEditorGraphNode:"/Script/PCGEditor.PCGEditorGraphNode",pcgEditorGraphNodeInput:"/Script/PCGEditor.PCGEditorGraphNodeInput",pcgEditorGraphNodeOutput:"/Script/PCGEditor.PCGEditorGraphNodeOutput",pcgHiGenGridSizeSettings:"/Script/PCG.PCGHiGenGridSizeSettings",pcgSubgraphSettings:"/Script/PCG.PCGSubgraphSettings",promotableOperator:"/Script/BlueprintGraph.K2Node_PromotableOperator",reverseForEachLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop",rotator:"/Script/CoreUObject.Rotator",select:"/Script/BlueprintGraph.K2Node_Select",slateBlueprintLibrary:"/Script/UMG.SlateBlueprintLibrary",spawnActorFromClass:"/Script/BlueprintGraph.K2Node_SpawnActorFromClass",switchEnum:"/Script/BlueprintGraph.K2Node_SwitchEnum",switchGameplayTag:"/Script/GameplayTagsEditor.GameplayTagsK2Node_SwitchGameplayTag",switchInteger:"/Script/BlueprintGraph.K2Node_SwitchInteger",switchName:"/Script/BlueprintGraph.K2Node_SwitchName",switchString:"/Script/BlueprintGraph.K2Node_SwitchString",timeline:"/Script/BlueprintGraph.K2Node_Timeline",timeManagementBlueprintLibrary:"/Script/TimeManagement.TimeManagementBlueprintLibrary",transform:"/Script/CoreUObject.Transform",userDefinedEnum:"/Script/Engine.UserDefinedEnum",variableGet:"/Script/BlueprintGraph.K2Node_VariableGet",variableSet:"/Script/BlueprintGraph.K2Node_VariableSet",vector:"/Script/CoreUObject.Vector",vector2D:"/Script/CoreUObject.Vector2D",whileLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop"};static pinColor={[this.paths.rotator]:n``,[this.paths.transform]:n``,[this.paths.vector]:n``,Any:n``,"Any[]":n``,blue:n``,bool:n``,byte:n``,class:n``,default:n``,delegate:n``,enum:n``,exec:n``,green:n``,int:n``,int64:n``,interface:n``,name:n``,object:n``,Param:n``,"Param[]":n``,Point:n``,"Point[]":n``,real:n``,red:n``,string:n``,struct:n``,Surface:n``,"Surface[]":n``,text:n``,Volume:n``,"Volume[]":n``,wildcard:n``};static pinColorMaterial=n``;static pinInputWrapWidth=143;static removeEventName="ueb-element-delete";static scale={[-12]:.133333,[-11]:.166666,[-10]:.2,[-9]:.233333,[-8]:.266666,[-7]:.3,[-6]:.333333,[-5]:.375,[-4]:.5,[-3]:.675,[-2]:.75,[-1]:.875,0:1,1:1.25,2:1.375,3:1.5,4:1.675,5:1.75,6:1.875,7:2};static smoothScrollTime=1e3;static stringEscapedCharacters=/["\\]/g;static subObjectAttributeNamePrefix="#SubObject";static subObjectAttributeNameFromEntity=(e,t=!1)=>this.subObjectAttributeNamePrefix+(!t&&e.Class?`_${e.Class.type}`:"")+"_"+e.Name;static subObjectAttributeNameFromReference=(e,t=!1)=>this.subObjectAttributeNamePrefix+(t?"":"_"+e.type)+"_"+e.path;static subObjectAttributeNameFromName=e=>this.subObjectAttributeNamePrefix+"_"+e;static switchTargetPattern=/\/Script\/[\w\.\/\:]+K2Node_Switch([A-Z]\w+)+/;static trackingMouseEventName={begin:"ueb-tracking-mouse-begin",end:"ueb-tracking-mouse-end"};static unescapedBackslash=/(?<=(?:[^\\]|^)(?:\\\\)*)\\(?!\\)/;static windowApplyEventName="ueb-window-apply";static windowApplyButtonText="OK";static windowCancelEventName="ueb-window-cancel";static windowCancelButtonText="Cancel";static windowCloseEventName="ueb-window-close";static CommonEnums={[this.paths.eAttachmentRule]:["KeepRelative","KeepWorld","SnapToTarget"],[this.paths.eMaterialSamplerType]:["Color","Grayscale","Alpha","Normal","Masks","Distance Field Font","Linear Color","Linear Grayscale","Data","External","Virtual Color","Virtual Grayscale","Virtual Alpha","Virtual Normal","Virtual Mask","Virtual Linear Color","Virtual Linear Grayscal"],[this.paths.eSamplerSourceMode]:["From texture asset","Shared: Wrap","Shared: Clamp","Hidden"],[this.paths.eSpawnActorCollisionHandlingMethod]:[["Undefined","Default"],["AlwaysSpawn","Always Spawn, Ignore Collisions"],["AdjustIfPossibleButAlwaysSpawn","Try To Adjust Location, But Always Spawn"],["AdjustIfPossibleButDontSpawnIfColliding","Try To Adjust Location, Don't Spawn If Still Colliding"],["DontSpawnIfColliding","Do Not Spawn"]],[this.paths.eSearchCase]:["CaseSensitive","IgnoreCase"],[this.paths.eSearchDir]:["FromStart","FromEnd"],[this.paths.eDrawDebugTrace]:["None","ForOneFrame","ForDuration","Persistent"],[this.paths.eTextureMipValueMode]:["None (use computed mip level)","MipLevel (absolute, 0 is full resolution)","MipBias (relative to the computed mip level)","Derivative (explicit derivative to compute mip level)"],[this.paths.eTraceTypeQuery]:[["TraceTypeQuery1","Visibility"],["TraceTypeQuery2","Camera"]]};static ModifierKeys=["Ctrl","Shift","Alt","Meta"];static rgba=["R","G","B","A"];static Keys={Backspace:"Backspace",Tab:"Tab",LeftControl:"ControlLeft",RightControl:"ControlRight",LeftShift:"ShiftLeft",RightShift:"ShiftRight",LeftAlt:"AltLeft",RightAlt:"AltRight",Enter:"Enter",Pause:"Pause",CapsLock:"CapsLock",Escape:"Escape",Space:"Space",PageUp:"PageUp",PageDown:"PageDown",End:"End",Home:"Home",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",PrintScreen:"PrintScreen",Insert:"Insert",Delete:"Delete",Zero:"Digit0",One:"Digit1",Two:"Digit2",Three:"Digit3",Four:"Digit4",Five:"Digit5",Six:"Digit6",Seven:"Digit7",Eight:"Digit8",Nine:"Digit9",A:"KeyA",B:"KeyB",C:"KeyC",D:"KeyD",E:"KeyE",F:"KeyF",G:"KeyG",H:"KeyH",I:"KeyI",K:"KeyK",L:"KeyL",M:"KeyM",N:"KeyN",O:"KeyO",P:"KeyP",Q:"KeyQ",R:"KeyR",S:"KeyS",T:"KeyT",U:"KeyU",V:"KeyV",W:"KeyW",X:"KeyX",Y:"KeyY",Z:"KeyZ",NumPadZero:"Numpad0",NumPadOne:"Numpad1",NumPadTwo:"Numpad2",NumPadThree:"Numpad3",NumPadFour:"Numpad4",NumPadFive:"Numpad5",NumPadSix:"Numpad6",NumPadSeven:"Numpad7",NumPadEight:"Numpad8",NumPadNine:"Numpad9",Multiply:"NumpadMultiply",Add:"NumpadAdd",Subtract:"NumpadSubtract",Decimal:"NumpadDecimal",Divide:"NumpadDivide",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12",NumLock:"NumLock",ScrollLock:"ScrollLock"}}class ae{static deleteNodes="Delete";static duplicateNodes="(bCtrl=True,Key=D)";static enableLinkDelete="LeftAlt";static enableZoomIn=["LeftControl","RightControl"];static selectAllNodes="(bCtrl=True,Key=A)"}class oe{#e;constructor(e){this.#e=e}compute(e){return this.#e(e)}}class le{static#t={nullable:!1,ignored:!1,serialized:!1,expected:!1,inlined:!1,quoted:!1,silent:!1};constructor(e){this.type=e.type??e.default?.constructor,this.default=e.default,this.nullable=e.nullable??null===e.default,this.ignored=e.ignored,this.serialized=e.serialized,this.expected=e.expected,this.inlined=e.inlined,this.quoted=e.quoted,this.silent=e.silent,this.predicate=e.predicate,this.type===Array&&this.default instanceof Array&&this.default.length>0&&(this.type=this.default.map((e=>e.constructor)).reduce(((e,t)=>(e.includes(t)||e.push(t),e)),[]))}static createType(e){return new le({type:e})}static createValue(e){return new le({default:e})}static hasAttribute(e,t,i,s=e.constructor){const r=e,n=r.attributes[t]?.[i];return n??s?.attributes?.[t]?.[i]??le.#t[i]}static getAttribute(e,t,i,s=e.constructor){let r=e.attributes?.[t]?.[i];return void 0!==r?r:(r=(s?.attributes)?.[t]?.[i],void 0!==r?r:(r=le.#t[i],void 0!==r?r:void 0))}get(e){return this[e]??le.#t[e]}}class ce{static attributes={type:new le({ignored:!0}),getter:new le({ignored:!0})};constructor(e,t=null){this.type=e,this.getter=t}get(){return this.getter()}getTargetType(){const e=this.type;return e instanceof ce?e.getTargetType():e}}class ue{#i;get values(){return this.#i}constructor(...e){this.#i=e}}class he{static booleanConverter={fromAttribute:(e,t)=>{},toAttribute:(e,t)=>!0===e?"true":!1===e?"false":""};static sigmoid(e,t=1.7){return 1/(1+e/(1-e)**-t)}static sigmoidPositive(e,t=3.7,i=1.1){return 1-Math.exp(-((e/i)**t))}static clamp(e,t=-1/0,i=1/0){return Math.min(Math.max(e,t),i)}static getScale(e){const t=e.blueprint?.getScale()??getComputedStyle(e).getPropertyValue("--ueb-scale");return""!=t?parseFloat(t):1}static minDecimals(e,t=1,i=1e-8){const s=e*10**t;return Math.abs(s%1)>i?e.toString():e.toFixed(t)}static numberFromText(e=""){switch(e=e.toLowerCase()){case"zero":return 0;case"one":return 1;case"two":return 2;case"three":return 3;case"four":return 4;case"five":return 5;case"six":return 6;case"seven":return 7;case"eight":return 8;case"nine":return 9}}static roundDecimals(e,t=1){const i=10**t;return Math.round(e*i)/i}static printNumber(e){return e==Number.POSITIVE_INFINITY?"inf":e==Number.NEGATIVE_INFINITY?"-inf":he.minDecimals(e)}static printExponential(e){if(e==Number.POSITIVE_INFINITY)return"inf";if(e==Number.NEGATIVE_INFINITY)return"-inf";const t=Math.round(e);if(t>=1e3){const i=Math.floor(Math.log10(t));return`${Math.round(e/10**(i-2))/100}e+${i<10?"0":""}${i}`}return 0==Math.floor(e)?e.toString():this.roundDecimals(e,Math.max(0,3-Math.floor(e).toString().length)).toString()}static approximatelyEqual(e,t,i=1e-8){return!(Math.abs(e-t)>i)}static convertLocation(e,t,i=!1){const s=i?1:1/he.getScale(t),r=t.getBoundingClientRect();return[Math.round((e[0]-r.x)*s),Math.round((e[1]-r.y)*s)]}static isSerialized(e,t){return e.attributes?.[t]?.serialized??e.constructor.attributes?.[t]?.serialized??!1}static objectGet(e,t,i=void 0){if(void 0!==e){if(!(t instanceof Array))throw new TypeError("UEBlueprint: Expected keys to be an array");return 0!=t.length&&t[0]in e&&void 0!==e[t[0]]?1==t.length?e[t[0]]:he.objectGet(e[t[0]],t.slice(1),i):i}}static objectSet(e,t,i,s=Object){if(!(t instanceof Array))throw new TypeError("Expected keys to be an array.");if(1==t.length){if(t[0]in e||void 0===e[t[0]])return e[t[0]]=i,!0}else if(t.length>0)return e[t[0]]instanceof Object||(e[t[0]]=new s),he.objectSet(e[t[0]],t.slice(1),i,s);return!1}static equals(e,t){for(;e instanceof ce;)e=e.get();for(;t instanceof ce;)t=t.get();return e?.equals&&t?.equals?e.equals(t):(e=he.sanitize(e),t=he.sanitize(t),e?.constructor===BigInt&&t?.constructor===Number?t=BigInt(t):e?.constructor===Number&&t?.constructor===BigInt&&(e=BigInt(e)),e===t||e instanceof Array&&t instanceof Array&&(e.length===t.length&&e.every(((e,i)=>he.equals(e,t[i])))))}static getType(e){return null===e?null:e?.constructor===Object&&e?.type instanceof Function?e.type:e?.constructor}static isValueOfType(e,t,i=!1){return t instanceof ce&&(t=t.getTargetType()),i&&null===e||e instanceof t||e?.constructor===t}static sanitize(e,t=e?.constructor){if(t instanceof Array&&(t=t[0]),t instanceof oe)return e;if(t instanceof ue){let i=t.values.find((t=>he.isValueOfType(e,t,!1)));i||(i=t.values[0]),t=i}return t instanceof ce?e instanceof ce?e:he.sanitize(e,t.getTargetType()):(t&&!he.isValueOfType(e,t,!0)&&(e=t===BigInt?BigInt(e):new t(e)),(e instanceof Boolean||e instanceof Number||e instanceof String)&&(e=e.valueOf()),e)}static snapToGrid(e,t,i){return 1===i?[e,t]:[i*Math.floor(e/i),i*Math.floor(t/i)]}static mergeArrays(e=[],t=[]){let i=[];e=[...e],t=[...t];e:for(;;){for(let s=0;s|
/g,"\n").replaceAll(/(\)/g,"")}static encodeHTMLWhitespace(e){return e.replaceAll(" "," ")}static capitalFirstLetter(e){return 0===e.length?e:e.charAt(0).toUpperCase()+e.slice(1)}static formatStringName(e=""){return e.replace(/^\s*b(?=[A-Z])/,"").replaceAll(ne.nameRegexSpaceReplacement," ").trim().split(" ").map((e=>he.capitalFirstLetter(e))).join(" ")}static getIdFromReference(e){return e.replace(/(?:.+\.)?([^\.]+)$/,"$1").replaceAll(/(?<=[a-z\d])(?=[A-Z])|(?<=[a-zA-Z])(?=\d)|(?<=[A-Z]{2})(?=[A-Z][a-z])/g,"-").toLowerCase()}static getNameFromPath(e){return e.match(/[^\.\/]+$/)?.[0]??""}static printLinearColor(e){return`${Math.round(255*e.R.valueOf())}, ${Math.round(255*e.G.valueOf())}, ${Math.round(255*e.B.valueOf())}`}static getPolarCoordinates(e,t,i=!1){let s=Math.atan2(t,e);return i&&s<0&&(s=2*Math.PI+s),[Math.sqrt(e*e+t*t),s]}static getCartesianCoordinates(e,t){return[e*Math.cos(t),e*Math.sin(t)]}static range(e=0,t=0,i=(t>=e?1:-1)){return Array.from({length:Math.ceil((t-e)/i)},((t,s)=>e+s*i))}static getFirstWordOrder(e){return new RegExp(/\s*/.source+e.join(/[^\n]+\n\s*/.source)+/\s*/.source)}static paste(e,t){const i=new ClipboardEvent("paste",{bubbles:!0,cancelable:!0,clipboardData:new DataTransfer});i.clipboardData.setData("text",t),e.dispatchEvent(i)}static async copy(e){const t=new ClipboardEvent("copy",{bubbles:!0,cancelable:!0,clipboardData:new DataTransfer});e.dispatchEvent(t)}static animate(e,t,i,s,r=(e=>{}),n=(e=>{const t=e**3.5;return t/(t+(1-e)**3.5)})){let a;const o=l=>{void 0===a&&(a=l);let c=(l-a)/i;he.approximatelyEqual(c,1)||c>1?c=1:r(requestAnimationFrame(o));const u=e+(t-e)*n(c);s(u)};r(requestAnimationFrame(o))}}class pe{#s;get target(){return this.#s}#r;get blueprint(){return this.#r}consumeEvent;options;listenHandler=()=>this.listenEvents();unlistenHandler=()=>this.unlistenEvents();constructor(e,t,i={}){i.consumeEvent??=!1,i.listenOnFocus??=!1,i.unlistenOnTextEdit??=!1,this.#s=e,this.#r=t,this.consumeEvent=i.consumeEvent,this.options=i}setup(){this.options.listenOnFocus&&(this.blueprint.addEventListener(ne.focusEventName.begin,this.listenHandler),this.blueprint.addEventListener(ne.focusEventName.end,this.unlistenHandler)),this.options.unlistenOnTextEdit&&(this.blueprint.addEventListener(ne.editTextEventName.begin,this.unlistenHandler),this.blueprint.addEventListener(ne.editTextEventName.end,this.listenHandler)),this.blueprint.focused&&this.listenEvents()}cleanup(){this.unlistenEvents(),this.blueprint.removeEventListener(ne.focusEventName.begin,this.listenHandler),this.blueprint.removeEventListener(ne.focusEventName.end,this.unlistenHandler),this.blueprint.removeEventListener(ne.editTextEventName.begin,this.unlistenHandler),this.blueprint.removeEventListener(ne.editTextEventName.end,this.listenHandler)}listenEvents(){}unlistenEvents(){}}class de{static makeSuccess(e,t,i=null,s=0){return{status:!0,value:t,position:e,bestParser:i,bestPosition:s}}static makeFailure(e=0,t=null,i=0){return{status:!1,value:null,position:e,bestParser:t,bestPosition:i}}static makeContext(e=null,t=""){return{parsernostrum:e,input:t,highlighted:null}}static makePathNode(e,t=0,i=null){return{parent:i,parser:e,index:t}}}class me{static indentation=" ";static highlight="Last valid parser";Self;static appendBeforeHighlight(e,t){return e.endsWith(me.highlight)?e=e.replace(/(?=(?:\n|^).+$)/,t):e+=t,e}static lastRowLength(e,t=0){const i=e.match(/(?:\n|(^))([^\n]*)$/),s=void 0!==i[1]?t:0;return i[2].length+s}isHighlighted(e,t){if(e.highlighted instanceof me)return e.highlighted===this;if(!e.highlighted||!t)return!1;let i,s,r,n;e:for(i=t,r=e.highlighted;i&&r;s=i,i=i.parent,n=r,r=r.parent)if(i.parser!==r.parser||i.index!==r.index){if(!s||!n)return!1;let e,t;for(e=i,t=r;e||t;e=e?.parent,t=t?.parent){const a=e?.parser===s.parser,o=t?.parser===n.parser;if(a||o){a&&(s=e),o&&(n=t),i=s,r=n;continue e}}return!1}return!0}isVisited(e){if(!e)return!1;for(e=e.parent;null!=e;e=e.parent)if(e.parser===this)return!0;return!1}parse(e,t,i){return null}toString(e=de.makeContext(null,""),t=0,i=null){if(this.isVisited(i))return"<...>";return this.doToString(e,t,i)}doToString(e,t,i){return`${this.constructor.name} does not implement toString()`}}class ge extends me{#n;get value(){return this.#n}constructor(e){super(),this.#n=e}parse(e,t,i){const s=t+this.#n.length,r=e.input.substring(t,s);return this.#n===r?de.makeSuccess(s,this.#n,i,s):de.makeFailure()}doToString(e,t,i){const s=this.value.replaceAll("\n","\\n");let r=this.value.match(/^[a-zA-Z]$/)?s:`"${s.replaceAll('"','\\"')}"`;return this.isHighlighted(e,i)&&(r+="\n"+me.indentation.repeat(t)+"^".repeat(r.length)+" "+me.highlight),r}}class be extends me{static instance=new be;parse(e,t,i){return de.makeSuccess(t,"",i,0)}doToString(e,t,i){return""+(this.isHighlighted(e,i)?`\n${me.indentation.repeat(t)}^^^^^^^^^ ${me.highlight}`:"")}}class ve extends me{static highlightRegexp=new RegExp(String.raw`(?<=[^\S\n]*\| .*\n)^(?=[^\S\n]*\^+ ${me.highlight}(?:\n|$))`,"m");#a;get parsers(){return this.#a}constructor(...e){super(),this.#a=e}parse(e,t,i){const s=de.makeSuccess(0,"");for(let r=0;rs.bestPosition&&(s.bestParser=n.bestParser,s.bestPosition=n.bestPosition),n.status)return s.value=n.value,s.position=n.position,s}return s.status=!1,s.value=null,s}doToString(e,t,i){const s=me.indentation.repeat(t),r=me.indentation.repeat(t+1);if(2===this.#a.length&&this.#a[1]instanceof be){let s=this.#a[0].toString(e,t,{parent:i,parser:this.#a[0],index:0});return this.#a[0]instanceof ge||(s="<"+s+">"),s+="?",s}let n=this.#a.map(((s,r)=>s.toString(e,t+1,{parent:i,parser:s,index:r}))).join("\n"+r+"| ");return e.highlighted&&(n=n.replace(ve.highlightRegexp," ")),"ALT<\n"+(this.isHighlighted(e,i)?`${s}^^^ ${me.highlight}\n`:"")+r+n+"\n"+s+">"}}class ye extends me{#o;get parser(){return this.#o}#l;constructor(e,t){super(),this.#o=e,this.#l=t}parse(e,t,i){const s=this.#o.parse(e,t,{parent:i,parser:this.#o,index:0});if(!s.status)return s;const r=this.#l(s.value,e.input,s.position).getParser().parse(e,s.position);return s.bestPosition>r.bestPosition&&(r.bestParser=s.bestParser,r.bestPosition=s.bestPosition),r}doToString(e,t,i){const s="chained";let r=this.#o.toString(e,t,{parent:i,parser:this.#o,index:0});return this.isHighlighted(e,i)?r+=" => "+s+"\n"+me.indentation.repeat(t)+" ".repeat(r.match(/(?:\n|^)([^\n]+)$/)?.[1].length+4)+"^".repeat(12)+" "+me.highlight:r=me.appendBeforeHighlight(r," => "+s),r}}class fe extends me{static instance=new fe;parse(e,t,i){return de.makeFailure()}doToString(e,t,i){return""+(this.isHighlighted(e,i)?`\n${me.indentation.repeat(t)}^^^^^^^^^ ${me.highlight}`:"")}}class we extends me{#o;#c;constructor(e){super(),this.#o=e}resolve(){return this.#c||(this.#c=this.#o().getParser()),this.#c}parse(e,t,i){return this.resolve(),this.#c.parse(e,t,{parent:i,parser:this.#c,index:0})}doToString(e,t,i){const s={parent:i,parser:this.#c,index:0};return this.isHighlighted(e,i)&&(e.highlighted=e.highlighted instanceof me?this.#c:s),this.resolve().toString(e,t,s)}}class Se extends me{#o;get parser(){return this.#o}#u;get type(){return this.#u}static Type={NEGATIVE_AHEAD:"?!",NEGATIVE_BEHIND:"?String.raw`[^${e}\\]*(?:\\.[^${e}\\]*)*`;static#g=/[-\+]?(?:\d*\.)?\d+/;static common={number:new RegExp(this.#g.source+String.raw`(?!\.)`),numberInteger:/[\-\+]?\d+(?!\.\d)/,numberNatural:/\d+/,numberExponential:new RegExp(this.#g.source+String.raw`(?:[eE][\+\-]?\d+)?(?!\.)`),numberUnit:/\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/,numberByte:/0*(?:25[0-5]|2[0-4]\d|1?\d?\d)(?!\d|\.)/,whitespace:/\s+/,whitespaceOpt:/\s*/,whitespaceInline:/[^\S\n]+/,whitespaceInlineOpt:/[^\S\n]*/,whitespaceMultiline:/\s*?\n\s*/,doubleQuotedString:new RegExp(`"(${this.#m('"')})"`),singleQuotedString:new RegExp(`'(${this.#m("'")})'`),backtickQuotedString:new RegExp("`("+this.#m("`")+")`")};constructor(e,t){super(),this.#h=e,this.#p=new RegExp(`^(?:${e.source})`,e.flags),this.#d=t}parse(e,t,i){const s=this.#p.exec(e.input.substring(t));s&&(t+=s[0].length);return s?de.makeSuccess(t,this.#d(s),i,t):de.makeFailure()}doToString(e,t,i){let s="/"+this.#h.source+"/";const r=Object.entries(Ee.common).find((([e,t])=>t.source===this.#h.source))?.[0];return r&&(s="P."+r),this.isHighlighted(e,i)&&(s+="\n"+me.indentation.repeat(t)+"^".repeat(s.length)+" "+me.highlight),s}}class Ce extends me{#o;get parser(){return this.#o}#b;get mapper(){return this.#b}constructor(e,t){super(),this.#o=e,this.#b=t}parse(e,t,i){const s=this.#o.parse(e,t,{parent:i,parser:this.#o,index:0});return s.status&&(s.value=this.#b(s.value)),s}doToString(e,t,i){const s={parent:i,parser:this.#o,index:0};this.isHighlighted(e,i)&&(e.highlighted=e.highlighted instanceof me?this.#o:s);let r=this.#o.toString(e,t,s);if(this.#o instanceof Ee&&Object.values(Ee.common).includes(this.#o.regexp))return this.#o.regexp===Ee.common.numberInteger&&this.#b===BigInt?"P.numberBigInteger":r;let n=this.#b.toString();return(n.length>60||n.includes("\n"))&&(n="(...) => { ... }"),n=` -> map<${n}>`,r=me.appendBeforeHighlight(r,n),r}}class xe extends Ee{static#b=e=>e;constructor(e){super(e,xe.#b)}}class Pe extends Ee{constructor(e,t=0){super(e,(e=>e[t]))}}class Ne extends me{#a;get parsers(){return this.#a}constructor(...e){super(),this.#a=e}parse(e,t,i){const s=new Array(this.#a.length),r=de.makeSuccess(t,s);for(let t=0;tr.bestPosition&&(r.bestParser=s.bestParser,r.bestPosition=s.bestPosition),!s.status){r.status=!1,r.value=null;break}r.value[t]=s.value,r.position=s.position}return r}doToString(e,t,i){const s=me.indentation.repeat(t),r=me.indentation.repeat(t+1);return"SEQ<\n"+(this.isHighlighted(e,i)?`${s}^^^ ${me.highlight}\n`:"")+this.#a.map(((s,n)=>r+s.toString(e,t+1,{parent:i,parser:s,index:n}))).join("\n")+"\n"+s+">"}}class ke extends me{#o;get parser(){return this.#o}#v;get min(){return this.#v}#y;get max(){return this.#y}constructor(e,t=0,i=Number.POSITIVE_INFINITY){if(super(),t>i)throw new Error("Min is greater than max");this.#o=e,this.#v=t,this.#y=i}parse(e,t,i){const s=de.makeSuccess(t,[],i);for(let t=0;ts.bestPosition&&(s.bestParser=r.bestParser,s.bestPosition=r.bestPosition),!r.status){te;static#w=([e,t])=>t;static#S=([e,t])=>[e,...t];static#E=e=>e instanceof Array?e.join(""):e;static#m=e=>String.raw`[^${e}\\]*(?:\\.[^${e}\\]*)*`;static number=this.reg(Ee.common.number).map(Number);static numberInteger=this.reg(Ee.common.numberInteger).map(Number);static numberBigInteger=this.reg(this.numberInteger.getParser().parser.regexp).map(BigInt);static numberNatural=this.reg(Ee.common.numberNatural).map(Number);static numberExponential=this.reg(Ee.common.numberExponential).map(Number);static numberUnit=this.reg(Ee.common.numberUnit).map(Number);static numberByte=this.reg(Ee.common.numberByte).map(Number);static whitespace=this.reg(Ee.common.whitespace);static whitespaceOpt=this.reg(Ee.common.whitespaceOpt);static whitespaceInline=this.reg(Ee.common.whitespaceInline);static whitespaceInlineOpt=this.reg(Ee.common.whitespaceInlineOpt);static whitespaceMultiline=this.reg(Ee.common.whitespaceMultiline);static doubleQuotedString=this.reg(Ee.common.doubleQuotedString,1);static singleQuotedString=this.reg(Ee.common.singleQuotedString,1);static backtickQuotedString=this.reg(Ee.common.backtickQuotedString,1);constructor(e,t=!1){this.#o=e}getParser(){return this.#o}run(e){const t=this.#o.parse(de.makeContext(this,e),0,de.makePathNode(this.#o));return t.position!==e.length&&(t.status=!1),t}parse(e){const t=this.run(e);if(!t.status){const i=60,s=[Math.ceil(i/2),Math.floor(i/2)],r=Te.lineColumnFromOffset(e,t.bestPosition);let n=t.bestPosition;const a=e.replaceAll(/^(\s)+|\s{6,}|\s*?\n\s*/g,((e,i,s)=>{let r=i?"...":" ... ";return s<=t.bestPosition&&(t.bestPosition0&&(u="..."+u,c+=3),s[1]e.getParser()))));return t}static alt(...e){return new this(new ve(...e.map((e=>e.getParser()))))}static lookahead(e){return new this(new Se(e.getParser(),Se.Type.POSITIVE_AHEAD))}static lazy(e){return new this(new we(e))}times(e,t=e){return new Te(new ke(this.#o,e,t))}many(){return this.times(0,Number.POSITIVE_INFINITY)}atLeast(e){return this.times(e,Number.POSITIVE_INFINITY)}atMost(e){return this.times(0,e)}opt(){return Te.alt(this,Te.success())}sepBy(e,t=!1){return Te.seq(this,Te.seq(e,this).map(Te.#w).many()).map(Te.#S)}skipSpace(){return Te.seq(this,Te.whitespaceOpt).map(Te.#f)}map(e){return new Te(new Ce(this.#o,e))}chain(e){return new Te(new ye(this.#o,e))}assert(e){return this.chain(((t,i,s)=>e(t,i,s)?Te.success().map((()=>t)):Te.failure()))}join(e=""){return this.map(Te.#E)}toString(e=0,t=!1,i=null){i instanceof Te&&(i=i.getParser());const s=de.makeContext(this,"");return s.highlighted=i,(t?"\n"+me.indentation.repeat(e):"")+this.#o.toString(s,e,de.makePathNode(this.#o))}}class Ae{static arrayPin=G``;static branchNode=G``;static breakStruct=G``;static cast=G``;static close=G``;static convert=G``;static correct=G``;static delegate=G``;static doN=G``;static doOnce=G``;static enum=G``;static event=G``;static execPin=G``;static expandIcon=G``;static flipflop=G``;static forEachLoop=G``;static functionSymbol=G``;static gamepad=G``;static genericPin=G``;static keyboard=G``;static loop=G``;static macro=G``;static mapPin=G``;static makeArray=G``;static makeMap=G``;static makeSet=G``;static makeStruct=G``;static mouse=G``;static node=G``;static operationPin=G``;static pcgStackPin=G``;static pcgPin=G``;static pcgParamPin=G``;static pcgSpatialPin=G``;static plusCircle=G``;static questionMark=G``;static referencePin=G``;static reject=G``;static setPin=G``;static select=G``;static sequence=G``;static sound=G``;static spawnActor=G``;static switch=G``;static timer=G``;static touchpad=G``}class Le{static grammar=this.createGrammar();static createGrammar(){return Te.failure()}}class Me{static#C=new Map;static registerSerializer(e,t){Me.#C.set(e,t)}static getSerializer(e){return Me.#C.get(e)}}class Ie extends Le{static attributes={attributes:new le({ignored:!0}),lookbehind:new le({default:"",ignored:!0})};constructor(e={},t=!1){super();const i=this.constructor;this.attributes,this.lookbehind;const s=Object.keys(e),r=e.attributes?he.mergeArrays(Object.keys(e.attributes),Object.keys(i.attributes)):Object.keys(i.attributes),n=he.mergeArrays(s,r);for(const s of n){let r=e[s];if(!(t||s in e||s in i.attributes||s.startsWith(ne.subObjectAttributeNamePrefix))){const e=r instanceof Array?`[${r[0]?.constructor.name}]`:r.constructor.name;console.warn(`UEBlueprint: Attribute ${s} (of type ${e}) in the serialized data is not defined in ${i.name}.attributes`)}if(!(s in i.attributes)){this[s]=r;continue}i.attributes.lookbehind;const n=le.getAttribute(e,s,"predicate",i),a=n?e=>{Object.defineProperties(this,{["#"+s]:{writable:!0,enumerable:!1},[s]:{enumerable:!0,get(){return this["#"+s]},set(e){n(e)?this["#"+s]=e:console.warn(`UEBlueprint: Tried to assign attribute ${s} to ${i.name} not satisfying the predicate`)}}}),this[s]=e}:e=>this[s]=e;let o=le.getAttribute(e,s,"default",i);o instanceof Function&&(o=o(this));let l=le.getAttribute(e,s,"type",i);if(l instanceof oe&&(l=l.compute(this)),l instanceof Array&&(l=Array),void 0===l&&(l=he.getType(o)),void 0===r)void 0!==o&&a(o);else{if(r?.constructor===String&&le.getAttribute(e,s,"serialized",i)&&l!==String)try{r=Me.getSerializer(l).read(r)}catch(e){a(r);continue}a(he.sanitize(r,l))}}}static defaultValueProviderFromType(e){return e!==Boolean&&(e===Number?0:e===BigInt?0n:e===String?"":e===Array||e instanceof Array?()=>[]:e instanceof ue?this.defaultValueProviderFromType(e.values[0]):e instanceof ce?()=>new ce(e.type,e.getter):e instanceof oe?void 0:()=>new e)}static isValueOfType(e,t){return null!=e&&(e instanceof t||e.constructor===t)}static defineAttributes(e,t){Object.defineProperty(e,"attributes",{writable:!0,configurable:!1}),e.attributes=t}getLookbehind(){let e=this.lookbehind??le.getAttribute(this,"lookbehind","default");return e=e instanceof ue?e.values[0]:e,e}unexpectedKeys(){return Object.keys(this).length-Object.keys(this.constructor.attributes).length}equals(e){const t=Object.keys(this),i=Object.keys(e);if(t.length!=i.length)return!1;for(const i of t){if(this[i]instanceof Ie&&!this[i].equals(e[i]))return!1;if(!he.equals(this[i],e[i]))return!1}return!0}}class Be{static separatedBy=(e,t,i=1)=>new RegExp(e+"(?:"+t+e+")"+(1===i?"*":2===i?"+":`{${i},}`));static Regex=class{static HexDigit=/[0-9a-fA-F]/;static InsideString=/(?:[^"\\]|\\.)*/;static InsideSingleQuotedString=/(?:[^'\\]|\\.)*/;static Integer=/[\-\+]?\d+(?!\d|\.)/;static Number=/[-\+]?(?:\d*\.)?\d+(?!\d|\.)/;static RealUnit=/\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/;static Word=Be.separatedBy("[a-zA-Z]","_");static Symbol=/[a-zA-Z_]\w*/;static DotSeparatedSymbols=Be.separatedBy(this.Symbol.source,"\\.");static PathFragment=Be.separatedBy(this.Symbol.source,"[\\.:]");static PathSpaceFragment=Be.separatedBy(this.Symbol.source,"[\\.:\\ ]");static Path=new RegExp(`(?:\\/${this.PathFragment.source}){2,}`)};static null=Te.reg(/\(\s*\)/).map((()=>null));static true=Te.reg(/true/i).map((()=>!0));static false=Te.reg(/false/i).map((()=>!1));static boolean=Te.regArray(/(true)|false/i).map((e=>!!e[1]));static number=Te.regArray(new RegExp(`(${Te.number.getParser().parser.regexp.source})|(\\+?inf)|(-inf)`)).map((([e,t,i,s])=>t?Number(t):i?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY));static bigInt=Te.reg(new RegExp(Te.number.getParser().parser.regexp.source)).map(BigInt).map((e=>void 0!==e[2]?Number.POSITIVE_INFINITY:void 0!==e[3]?Number.NEGATIVE_INFINITY:Number(e[1])));static naturalNumber=Te.lazy((()=>Te.reg(/\d+/).map(Number)));static string=Te.doubleQuotedString.map((e=>he.unescapeString(e)));static colorValue=Te.numberByte;static word=Te.reg(Be.Regex.Word);static symbol=Te.reg(Be.Regex.Symbol);static symbolQuoted=Te.reg(new RegExp('"('+Be.Regex.Symbol.source+')"'),1);static attributeName=Te.reg(Be.Regex.DotSeparatedSymbols);static attributeNameQuoted=Te.reg(new RegExp('"('+Be.Regex.DotSeparatedSymbols.source+')"'),1);static guid=Te.reg(new RegExp(`${Be.Regex.HexDigit.source}{32}`));static commaSeparation=Te.reg(/\s*,\s*(?!\))/);static commaOrSpaceSeparation=Te.reg(/\s*,\s*(?!\))|\s+/);static equalSeparation=Te.reg(/\s*=\s*/);static hexColorChannel=Te.reg(new RegExp(Be.Regex.HexDigit.source+"{2}"));static grammarFor(e,t=e?.type,i=this.unknownValue){let s=i;if(t instanceof Array){if(e?.inlined)return this.grammarFor(void 0,t[0]);s=Te.seq(Te.reg(/\(\s*/),this.grammarFor(void 0,t[0]).sepBy(this.commaSeparation).opt(),Te.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>t instanceof Array?t:[]))}else if(t instanceof ue)s=t.values.map((e=>this.grammarFor(void 0,e))).reduce(((e,t)=>t&&t!==this.unknownValue&&e!==this.unknownValue?Te.alt(e,t):this.unknownValue));else{if(t instanceof ce)return this.grammarFor(void 0,t.getTargetType()).map((e=>new ce(t.type,(()=>e))));if(e?.constructor===Object)s=this.grammarFor(void 0,t);else switch(t){case Boolean:s=this.boolean;break;case null:s=this.null;break;case Number:s=this.number;break;case BigInt:s=this.bigInt;break;case String:s=this.string;break;default:t?.prototype instanceof Le&&(s=t.grammar)}}return e&&(e.serialized&&t.constructor!==String&&(s=s==this.unknownValue?this.string:Te.seq(Te.str('"'),s,Te.str('"'))),e.nullable&&(s=Te.alt(s,this.null))),s}static getAttribute(e,t){let i,s;if(e instanceof ue)for(let s of e.values)if(i=this.getAttribute(s,t))return i;return e instanceof Ie.constructor?(i=e.attributes[t[0]],s=i?.type):e instanceof Array&&(i=e[t[0]],s=i),t.length>1?this.getAttribute(s,t.slice(1)):i}static createAttributeGrammar(e,t=this.attributeName,i=this.equalSeparation,s=((e,t,i)=>{})){return Te.seq(t,i).chain((([t,i])=>{const r=t.split(ne.keysSeparator),n=this.getAttribute(e,r);return this.grammarFor(n).map((e=>t=>{s(t,r,e),he.objectSet(t,r,e)}))}))}static createEntityGrammar(e,t=!0,i=this.commaSeparation){const s=e.attributes.lookbehind.default;return Te.seq(Te.reg(s instanceof ue?new RegExp(`(${s.values.reduce(((e,t)=>e+"|"+t))})\\s*\\(\\s*`):s.constructor==String&&s.length>0?new RegExp(`(${s})\\s*\\(\\s*`):/()\(\s*/,1),this.createAttributeGrammar(e).sepBy(i),Te.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{let s={};return t.forEach((e=>e(s))),e.length&&(s.lookbehind=e),s})).chain((i=>{let s=Object.keys(i);if(Object.keys(e.attributes).filter((t=>e.attributes[t].expected)).find((e=>!s.includes(e)&&e)))return Te.failure();const r=Object.keys(i).filter((t=>!(t in e.attributes))).length;return!t&&r>0?Te.failure():Te.success().map((()=>new e(i)))}))}static unknownValue}var $e;"undefined"==typeof window?import("crypto").then((e=>$e=e.default)).catch():$e=window.crypto;class He extends Ie{static attributes={...super.attributes,value:le.createValue("")};static grammar=this.createGrammar();static createGrammar(){return Be.guid.map((e=>new this(e)))}static generateGuid(e=!0){let t=new Uint32Array(4);!0===e&&$e.getRandomValues(t);let i="";return t.forEach((e=>{i+=("0".repeat(8)+e.toString(16).toUpperCase()).slice(-8)})),new He({value:i})}constructor(e){e||(e=He.generateGuid().value),e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Ve extends Ie{static#x=[["'\"",Be.Regex.InsideString.source],["'",Be.Regex.InsideSingleQuotedString.source],['"',Be.Regex.InsideString.source]];static attributes={...super.attributes,type:new le({default:"",serialized:!0}),path:new le({default:"",serialized:!0}),delim:new le({ignored:!0})};static quoted=Te.regArray(new RegExp(this.#x.map((([e,t])=>e+"("+t+")"+e.split("").reverse().join(""))).join("|"))).map((([e,t,i,s])=>t??i??s));static path=this.quoted.getParser().parser.regexp.source+"|"+Be.Regex.Path.source;static typeReference=Te.reg(new RegExp(Be.Regex.Path.source+"|"+Be.symbol.getParser().regexp.source));static fullReferenceGrammar=Te.regArray(new RegExp("("+this.typeReference.getParser().regexp.source+")"+/\s*/.source+"(?:"+this.quoted.getParser().parser.regexp.source+")")).map((([e,t,...i])=>new this({type:t,path:i.find((e=>e)),delim:this.#x[i.findIndex((e=>e))]?.[0]??""})));static fullReferenceSerializedGrammar=Te.regArray(new RegExp("("+this.typeReference.getParser().regexp.source+")"+/\s*/.source+"'("+Be.Regex.InsideSingleQuotedString.source+")'")).map((([e,t,...i])=>new this({type:t,path:i.find((e=>e)),delim:"'"})));static typeReferenceGrammar=this.typeReference.map((e=>new this({type:e,path:""})));static grammar=this.createGrammar();static createGrammar(){return Te.alt(Te.seq(Te.str('"'),Te.alt(this.fullReferenceSerializedGrammar,this.typeReferenceGrammar),Te.str('"')).map((([e,t,i])=>t)),this.fullReferenceGrammar.map((e=>(he.objectSet(e,["attributes","type","serialized"],!1),e))),this.typeReferenceGrammar.map((e=>(he.objectSet(e,["attributes","type","serialized"],!1),e))))}constructor(e={}){e.constructor===String&&(e={path:e}),super(e),this.type,this.path,this.delim}static createNoneInstance(){return new Ve({type:"None",path:""})}getName(){return he.getNameFromPath(this.path.replace(/_C$/,""))}toString(){return this.type+(this.path?this.delim+this.path+this.delim.split("").reverse().join(""):"")}}class Ge extends Ie{static attributes={...super.attributes,MemberParent:le.createType(Ve),MemberName:le.createType(String),MemberGuid:le.createType(He)};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this)}constructor(e){super(e),this.MemberParent,this.MemberName,this.MemberGuid}}class De extends Ie{static attributes={...super.attributes,value:le.createValue("")};static attributeConverter={fromAttribute:(e,t)=>new De(e),toAttribute:(e,t)=>e.toString()};static grammar=this.createGrammar();static createGrammar(){return Be.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Oe extends Ie{static attributes={...super.attributes,value:new le({default:0,predicate:e=>e%1==0&&e>1<<31&&e<-(1<<31)})};static grammar=this.createGrammar();static createGrammar(){return Te.numberInteger.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),e.value=Math.floor(e.value),-0===e.value&&(e.value=0),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toString()}}class Re extends Ie{static attributes={...super.attributes,value:le.createValue(0)};static grammar=this.createGrammar();static createGrammar(){return Te.number.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toFixed(6)}}class ze extends Ie{static attributes={...super.attributes,R:new le({type:Re,default:()=>new Re,expected:!0}),G:new le({type:Re,default:()=>new Re,expected:!0}),B:new le({type:Re,default:()=>new Re,expected:!0}),A:new le({type:Re,default:()=>new Re(1)}),H:new le({type:Re,default:()=>new Re,ignored:!0}),S:new le({type:Re,default:()=>new Re,ignored:!0}),V:new le({type:Re,default:()=>new Re,ignored:!0})};static grammar=this.createGrammar();static linearToSRGB(e){return e<=0?0:e>=1?1:e<.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055}static sRGBtoLinear(e){return e<=0?0:e>=1?1:e<.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}static getWhite(){return new ze({R:1,G:1,B:1})}static createGrammar(){return Be.createEntityGrammar(this,!1)}static getLinearColorFromHexGrammar(){return Te.regArray(new RegExp("#("+Be.Regex.HexDigit.source+"{2})("+Be.Regex.HexDigit.source+"{2})("+Be.Regex.HexDigit.source+"{2})("+Be.Regex.HexDigit.source+"{2})?")).map((([e,t,i,s,r])=>new this({R:parseInt(t,16)/255,G:parseInt(i,16)/255,B:parseInt(s,16)/255,A:parseInt(r??"FF",16)/255})))}static getLinearColorRGBListGrammar(){return Te.seq(Te.numberByte,Be.commaSeparation,Te.numberByte,Be.commaSeparation,Te.numberByte).map((([e,t,i,s,r])=>new this({R:e/255,G:i/255,B:r/255,A:1})))}static getLinearColorRGBGrammar(){return Te.seq(Te.reg(/rgb\s*\(\s*/),this.getLinearColorRGBListGrammar(),Te.reg(/\s*\)/)).map((([e,t,i])=>t))}static getLinearColorRGBAGrammar(){return Te.seq(Te.reg(/rgba\s*\(\s*/),this.getLinearColorRGBListGrammar(),Te.reg(/\s*\)/)).map((([e,t,i])=>t))}static getLinearColorFromAnyFormat(){return Te.alt(this.getLinearColorFromHexGrammar(),this.getLinearColorRGBAGrammar(),this.getLinearColorRGBGrammar(),this.getLinearColorRGBListGrammar())}constructor(e){e instanceof Array&&(e={R:e[0]??0,G:e[1]??0,B:e[2]??0,A:e[3]??1}),super(e),this.R,this.G,this.B,this.A,this.H,this.S,this.V,this.#P()}#P(){const e=this.R.value,t=this.G.value,i=this.B.value;if(he.approximatelyEqual(e,t)&&he.approximatelyEqual(e,i)&&he.approximatelyEqual(t,i))return this.S.value=0,void(this.V.value=e);const s=Math.max(e,t,i),r=Math.min(e,t,i),n=s-r;let a;switch(s){case r:a=0;break;case e:a=(t-i)/n+(te.toString(16).toUpperCase().padStart(2,"0"))).join("")}toSRGBAString(){return this.toSRGBA().map((e=>e.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(255*this.R.value)<<24)+(Math.round(255*this.G.value)<<16)+(Math.round(255*this.B.value)<<8)+Math.round(255*this.A.value)}setFromRGBANumber(e){this.A.value=(255&e)/255,this.B.value=(e>>8&255)/255,this.G.value=(e>>16&255)/255,this.R.value=(e>>24&255)/255,this.#P()}setFromSRGBANumber(e){this.A.value=(255&e)/255,this.B.value=ze.sRGBtoLinear((e>>8&255)/255),this.G.value=ze.sRGBtoLinear((e>>16&255)/255),this.R.value=ze.sRGBtoLinear((e>>24&255)/255),this.#P()}toArray(){return[this.R.value,this.G.value,this.B.value,this.A.value]}toString(){return he.printLinearColor(this)}}class _e extends Ie{static attributes={...super.attributes,MacroGraph:new le({type:Ve,default:()=>new Ve}),GraphBlueprint:new le({type:Ve,default:()=>new Ve}),GraphGuid:new le({type:He,default:()=>new He})};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this)}constructor(e){super(e),this.MacroGraph,this.GraphBlueprint,this.GuidEntity}getMacroName(){const e=this.MacroGraph.path.search(":");return this.MacroGraph.path.substring(e+1)}}class Fe extends Oe{static attributes={...super.attributes,value:new le({...super.attributes.value,predicate:e=>e%1==0&&e>=0&&e<256})};static grammar=this.createGrammar();static createGrammar(){return Te.numberByte.map((e=>new this(e)))}constructor(e=0){super(e)}}class je extends Ie{static attributes={...super.attributes,value:le.createValue("")};static grammar=this.createGrammar();static createGrammar(){return Be.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Ue extends je{static grammar=this.createGrammar();static createGrammar(){return Be.symbol.map((e=>new this(e)))}}class We extends Ue{static grammar=this.createGrammar();static createGrammar(){return Te.reg(Be.Regex.InsideString).map((e=>new this(e)))}}class Ke extends Ie{static attributes={...super.attributes,value:le.createValue(""),lookbehind:new le({...super.attributes.lookbehind,default:"INVTEXT"})};static grammar=this.createGrammar();static createGrammar(){return Te.alt(Te.seq(Te.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)),Be.grammarFor(this.attributes.value),Te.reg(/\s*\)/)).map((([e,t,i])=>t)),Te.reg(new RegExp(this.attributes.lookbehind.default)).map((()=>""))).map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}}class Ye extends Ie{static attributes={...super.attributes,namespace:le.createValue(""),key:le.createValue(""),value:le.createValue(""),lookbehind:new le({...super.attributes.lookbehind,default:"NSLOCTEXT"})};static grammar=this.createGrammar();static createGrammar(){return Te.regArray(new RegExp(String.raw`${this.attributes.lookbehind.default}\s*\(`+String.raw`\s*"(${Be.Regex.InsideString.source})"\s*,`+String.raw`\s*"(${Be.Regex.InsideString.source})"\s*,`+String.raw`\s*"(${Be.Regex.InsideString.source})"\s*`+String.raw`(?:,\s+)?`+String.raw`\)`,"m")).map((e=>new this({namespace:he.unescapeString(e[1]),key:he.unescapeString(e[2]),value:he.unescapeString(e[3])})))}constructor(e){super(e),this.namespace,this.key,this.value}toString(){return he.capitalFirstLetter(this.value)}}class Xe extends Ie{static attributes={...super.attributes,value:new le({type:[new ue(String,Ye,Ke,Xe)],default:[]}),lookbehind:new le({...super.attributes.lookbehind,default:new ue("LOCGEN_FORMAT_NAMED","LOCGEN_FORMAT_ORDERED")})};static grammar=this.createGrammar();static createGrammar(){return Te.seq(Te.reg(new RegExp(`(${this.attributes.lookbehind.default.values.reduce(((e,t)=>e+"|"+t))})\\s*`),1),Be.grammarFor(this.attributes.value)).map((([e,t])=>new this({value:t,lookbehind:e})))}constructor(e){super(e),this.value}toString(){const e=this.value?.[0]?.toString();if(!e)return"";const t=this.value.slice(1).map((e=>e.toString()));return"LOCGEN_FORMAT_NAMED"==this.lookbehind?e.replaceAll(/\{([a-zA-Z]\w*)\}/g,((e,i)=>{const s=t.indexOf(i)+1;return s>0&&s{const s=Number(i);return se>=-(1n<<63n)&&e<1n<<63n})};static grammar=this.createGrammar();static createGrammar(){return Te.numberBigInteger.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),-0===e.value&&(e.value=0n),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toString()}}class qe extends Ie{static attributes={...super.attributes,value:new le({default:""})};static grammar=this.createGrammar();static createGrammar(){return Be.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Qe extends Ie{static attributes={...super.attributes,objectName:le.createType(qe),pinGuid:le.createType(He)};static grammar=this.createGrammar();static createGrammar(){return Te.seq(qe.createGrammar(),Te.whitespace,He.createGrammar()).map((([e,t,i])=>new this({objectName:e,pinGuid:i})))}constructor(e){super(e),this.objectName,this.pinGuid}}class Je extends Ie{static attributes={...super.attributes,PinCategory:le.createValue(""),PinSubCategory:le.createValue(""),PinSubCategoryObject:new le({type:Ve,default:()=>Ve.createNoneInstance()}),PinSubCategoryMemberReference:new le({type:Ge,default:null}),PinValueType:new le({type:Je,default:null}),ContainerType:le.createType(qe),bIsReference:le.createValue(!1),bIsConst:le.createValue(!1),bIsWeakPointer:le.createValue(!1),bIsUObjectWrapper:le.createValue(!1),bSerializeAsSinglePrecisionFloat:le.createValue(!1)};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this)}constructor(e={},t=!1){super(e,t),this.PinCategory,this.PinSubCategory,this.PinSubCategoryObject,this.PinSubCategoryMemberReference,this.PinValueType,this.ContainerType,this.bIsReference,this.bIsConst,this.bIsWeakPointer,this.bIsUObjectWrapper,this.bIsUObjectWrapper,this.bSerializeAsSinglePrecisionFloat}copyTypeFrom(e){this.PinCategory=e.PinCategory,this.PinSubCategory=e.PinSubCategory,this.PinSubCategoryObject=e.PinSubCategoryObject,this.PinSubCategoryMemberReference=e.PinSubCategoryMemberReference,this.PinValueType=e.PinValueType,this.ContainerType=e.ContainerType,this.bIsReference=e.bIsReference,this.bIsConst=e.bIsConst,this.bIsWeakPointer=e.bIsWeakPointer,this.bIsUObjectWrapper=e.bIsUObjectWrapper,this.bSerializeAsSinglePrecisionFloat=e.bSerializeAsSinglePrecisionFloat}}class et extends Ie{static attributes={...super.attributes,X:new le({default:0,expected:!0}),Y:new le({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this,!1)}constructor(e){super(e),this.X,this.Y}toArray(){return[this.X,this.Y]}}class tt extends et{static grammar=this.createGrammar();static createGrammar(){return Te.alt(Te.regArray(new RegExp(/X\s*=\s*/.source+"(?"+Te.number.getParser().parser.regexp.source+")\\s+"+/Y\s*=\s*/.source+"(?"+Te.number.getParser().parser.regexp.source+")")).map((({groups:{x:e,y:t}})=>new this({X:Number(e),Y:Number(t)}))),et.createGrammar())}}class it extends Ie{static attributes={...super.attributes,R:new le({default:0,expected:!0}),P:new le({default:0,expected:!0}),Y:new le({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this,!1)}constructor(e){super(e),this.R,this.P,this.Y}getRoll(){return this.R}getPitch(){return this.P}getYaw(){return this.Y}}class st extends it{static grammar=this.createGrammar();static createGrammar(){const e=Te.number.getParser().parser.regexp.source;return Te.alt(Te.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")")).map((([e,t,i,s])=>new this({R:Number(s),P:Number(t),Y:Number(i)}))),it.createGrammar())}}class rt extends et{static grammar=this.createGrammar();static createGrammar(){const e=Te.number.getParser().parser.regexp.source;return Te.alt(Te.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")")).map((([e,t,i])=>new this({X:Number(t),Y:Number(i)}))),et.createGrammar())}}class nt extends Ie{static attributes={...super.attributes,X:new le({default:0,expected:!0}),Y:new le({default:0,expected:!0}),Z:new le({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(nt,!1)}constructor(e){super(e),this.X,this.Y,this.Z}toArray(){return[this.X,this.Y,this.Z]}}class at extends nt{static grammar=this.createGrammar();static createGrammar(){const e=Te.number.getParser().parser.regexp.source;return Te.alt(Te.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")")).map((([e,t,i,s])=>new this({X:Number(t),Y:Number(i),Z:Number(s)}))),nt.createGrammar())}}class ot extends Ie{static#N={[ne.paths.linearColor]:ze,[ne.paths.rotator]:it,[ne.paths.vector]:nt,[ne.paths.vector2D]:et,bool:Boolean,byte:Fe,enum:Ue,exec:String,int:Oe,int64:Ze,name:String,real:Number,string:String};static#k={enum:We,rg:tt,[ne.paths.rotator]:st,[ne.paths.vector]:at,[ne.paths.vector2D]:rt};static attributes={...super.attributes,lookbehind:new le({default:"Pin",ignored:!0}),objectEntity:new le({ignored:!0}),pinIndex:new le({type:Number,ignored:!0}),PinId:new le({type:He,default:()=>new He}),PinName:le.createValue(""),PinFriendlyName:le.createType(new ue(Ye,Xe,String)),PinToolTip:le.createType(String),Direction:le.createType(String),PinType:new le({type:Je,default:()=>new Je,inlined:!0}),LinkedTo:le.createType([Qe]),SubPins:le.createType([Qe]),ParentPin:le.createType(Qe),DefaultValue:new le({type:new oe((e=>e.getEntityType(!0)??String)),serialized:!0}),AutogeneratedDefaultValue:le.createType(String),DefaultObject:le.createType(Ve),PersistentGuid:le.createType(He),bHidden:le.createValue(!1),bNotConnectable:le.createValue(!1),bDefaultValueIsReadOnly:le.createValue(!1),bDefaultValueIsIgnored:le.createValue(!1),bAdvancedView:le.createValue(!1),bOrphanedPin:le.createValue(!1)};static grammar=this.createGrammar();#T=!1;set recomputesNodeTitleOnChange(e){this.#T=e}get recomputesNodeTitleOnChange(){return this.#T}static createGrammar(){return Be.createEntityGrammar(this)}constructor(e={},t=!1){super(e,t),this.objectEntity,this.pinIndex,this.PinId,this.PinName,this.PinFriendlyName,this.PinToolTip,this.Direction,this.PinType,this.LinkedTo,this.DefaultValue,this.AutogeneratedDefaultValue,this.DefaultObject,this.PersistentGuid,this.bHidden,this.bNotConnectable,this.bDefaultValueIsReadOnly,this.bDefaultValueIsIgnored,this.bAdvancedView,this.bOrphanedPin}static fromLegacyObject(e){return new ot(e,!0)}getType(){const e=this.PinType.PinCategory;if("struct"===e||"object"===e)return this.PinType.PinSubCategoryObject.path;if(this.isEnum())return"enum";if(this.objectEntity?.isPcg()){const e=this.objectEntity.getPcgSubobject(),t=this.isInput()?e.InputPins?.[this.pinIndex]:e.OutputPins?.[this.pinIndex];if(t){const i=e[ne.subObjectAttributeNameFromReference(t,!0)];let s=i.Properties?.AllowedTypes?.toString()??"";if(""==s&&(s=this.PinType.PinCategory??"",""==s&&(s="Any")),s)return!1!==i.Properties.bAllowMultipleData&&!1!==i.Properties.bAllowMultipleConnections&&(s+="[]"),s}}if("optional"===e)switch(this.PinType.PinSubCategory){case"red":return"real";case"rg":return"rg";case"rgb":return ne.paths.vector;case"rgba":return ne.paths.linearColor;default:return this.PinType.PinSubCategory}return e}getEntityType(e=!1){const t=this.getType(),i=ot.#N[t],s=ot.#k[t];return e&&void 0!==s?s:i}pinDisplayName(){let e,t=this.PinFriendlyName?this.PinFriendlyName.toString():he.formatStringName(this.PinName??"");return this.PinToolTip&&(e=this.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))&&e[1].toLowerCase()===t.toLowerCase()?e[1]:t}copyTypeFrom(e){this.PinType.PinCategory=e.PinType.PinCategory,this.PinType.PinSubCategory=e.PinType.PinSubCategory,this.PinType.PinSubCategoryObject=e.PinType.PinSubCategoryObject,this.PinType.PinSubCategoryMemberReference=e.PinType.PinSubCategoryMemberReference,this.PinType.PinValueType=e.PinType.PinValueType,this.PinType.ContainerType=e.PinType.ContainerType,this.PinType.bIsReference=e.PinType.bIsReference,this.PinType.bIsConst=e.PinType.bIsConst,this.PinType.bIsWeakPointer=e.PinType.bIsWeakPointer,this.PinType.bIsUObjectWrapper=e.PinType.bIsUObjectWrapper,this.PinType.bSerializeAsSinglePrecisionFloat=e.PinType.bSerializeAsSinglePrecisionFloat}getDefaultValue(e=!1){return void 0===this.DefaultValue&&e&&(this.DefaultValue=new(this.getEntityType(!0))),this.DefaultValue}isEnum(){const e=this.PinType.PinSubCategoryObject.type;return e===ne.paths.enum||e===ne.paths.userDefinedEnum||"enum"===e.toLowerCase()}isExecution(){return"exec"===this.PinType.PinCategory}isHidden(){return this.bHidden}isInput(){return!this.bHidden&&"EGPD_Output"!=this.Direction}isOutput(){return!this.bHidden&&"EGPD_Output"==this.Direction}isLinked(){return this.LinkedTo?.length>0??!1}linkTo(e,t){const i=this.LinkedTo?.some((i=>i.objectName.toString()==e&&i.pinGuid.valueOf()==t.PinId.valueOf()));return!i&&((this.LinkedTo??=[]).push(new Qe({objectName:e,pinGuid:t.PinId})),!0)}unlinkFrom(e,t){const i=this.LinkedTo?.findIndex((i=>i.objectName.toString()==e&&i.pinGuid.valueOf()==t.PinId.valueOf()));return i>=0&&(this.LinkedTo.splice(i,1),0===this.LinkedTo.length&&void 0===ot.attributes.LinkedTo.default&&(this.LinkedTo=void 0),!0)}getSubCategory(){return this.PinType.PinSubCategoryObject.path}pinColor(){if("mask"==this.PinType.PinCategory){const e=ne.pinColor[this.PinType.PinSubCategory];if(e)return e}else if("optional"==this.PinType.PinCategory)return ne.pinColorMaterial;return ne.pinColor[this.getType()]??ne.pinColor[this.PinType.PinCategory.toLowerCase()]??ne.pinColor.default}}class lt extends ot{static grammar=this.createGrammar();static createGrammar(){return Te.seq(Te.reg(new RegExp(`(${Be.Regex.Symbol.source})\\s*\\(\\s*`),1),Be.createAttributeGrammar(this).sepBy(Be.commaSeparation),Te.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{e??="";let s={};return e.length&&(s.lookbehind=e),t.forEach((e=>e(s))),new this(s)}))}constructor(e={}){super(e,!0)}}class ct extends Ie{static attributes={...super.attributes,MemberScope:le.createType(String),MemberName:le.createValue(""),MemberGuid:le.createType(He),bSelfContext:le.createType(Boolean)};static grammar=this.createGrammar();static createGrammar(){return Be.createEntityGrammar(this)}constructor(e){super(e),this.MemberName,this.GuidEntity,this.bSelfContext}}class ut extends Ie{static#A={A_AccentGrave:"à",Add:"Num +",C_Cedille:"ç",Decimal:"Num .",Divide:"Num /",E_AccentAigu:"é",E_AccentGrave:"è",F1:"F1",F10:"F10",F11:"F11",F12:"F12",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",Gamepad_Special_Left_X:"Touchpad Button X Axis",Gamepad_Special_Left_Y:"Touchpad Button Y Axis",Mouse2D:"Mouse XY 2D-Axis",Multiply:"Num *",Section:"§",Subtract:"Num -",Tilde:"`"};static attributes={...super.attributes,Class:le.createType(Ve),Name:le.createType(String),Archetype:le.createType(Ve),ExportPath:le.createType(Ve),R:new le({type:new ue(Boolean,Number),default:!1,silent:!0}),G:new le({type:new ue(Boolean,Number),default:!1,silent:!0}),B:new le({type:new ue(Boolean,Number),default:!1,silent:!0}),A:new le({type:new ue(Boolean,Number),default:!1,silent:!0}),ObjectRef:le.createType(Ve),BlueprintElementType:le.createType(Ve),BlueprintElementInstance:le.createType(Ve),PinTags:new le({type:[null],inlined:!0}),PinNames:new le({type:[String],inlined:!0}),AxisKey:le.createType(je),InputAxisKey:le.createType(je),InputName:le.createType(String),InputType:le.createType(je),NumAdditionalInputs:le.createType(Number),bIsPureFunc:le.createType(Boolean),bIsConstFunc:le.createType(Boolean),bIsCaseSensitive:le.createType(Boolean),VariableReference:le.createType(ct),SelfContextInfo:le.createType(je),DelegatePropertyName:le.createType(String),DelegateOwnerClass:le.createType(Ve),ComponentPropertyName:le.createType(String),EventReference:le.createType(Ge),FunctionReference:le.createType(Ge),CustomFunctionName:le.createType(String),TargetType:le.createType(Ve),MacroGraphReference:le.createType(_e),Enum:le.createType(Ve),EnumEntries:new le({type:[String],inlined:!0}),InputKey:le.createType(je),MaterialFunction:le.createType(Ve),bOverrideFunction:le.createType(Boolean),bInternalEvent:le.createType(Boolean),bConsumeInput:le.createType(Boolean),bExecuteWhenPaused:le.createType(Boolean),bOverrideParentBinding:le.createType(Boolean),bControl:le.createType(Boolean),bAlt:le.createType(Boolean),bShift:le.createType(Boolean),bCommand:le.createType(Boolean),CommentColor:le.createType(ze),bCommentBubbleVisible_InDetailsPanel:le.createType(Boolean),bColorCommentBubble:le.createType(Boolean),ProxyFactoryFunctionName:le.createType(String),ProxyFactoryClass:le.createType(Ve),ProxyClass:le.createType(Ve),StructType:le.createType(Ve),MaterialExpression:le.createType(Ve),MaterialExpressionComment:le.createType(Ve),MoveMode:le.createType(je),TimelineName:le.createType(String),TimelineGuid:le.createType(He),SizeX:le.createType(new ce(Oe)),SizeY:le.createType(new ce(Oe)),Text:le.createType(new ce(String)),MaterialExpressionEditorX:le.createType(new ce(Oe)),MaterialExpressionEditorY:le.createType(new ce(Oe)),NodeTitle:le.createType(String),NodeTitleColor:le.createType(ze),PositionX:le.createType(new ce(Oe)),PositionY:le.createType(new ce(Oe)),SettingsInterface:le.createType(Ve),PCGNode:le.createType(Ve),HiGenGridSize:le.createType(je),Operation:le.createType(je),NodePosX:le.createType(Oe),NodePosY:le.createType(Oe),NodeHeight:le.createType(Oe),NodeWidth:le.createType(Oe),Graph:le.createType(Ve),SubgraphInstance:le.createType(String),InputPins:new le({type:[Ve],inlined:!0}),OutputPins:new le({type:[Ve],inlined:!0}),bExposeToLibrary:le.createType(Boolean),bCanRenameNode:le.createType(Boolean),bCommentBubblePinned:le.createType(Boolean),bCommentBubbleVisible:le.createType(Boolean),NodeComment:le.createType(String),AdvancedPinDisplay:le.createType(De),DelegateReference:le.createType(ct),EnabledState:le.createType(De),NodeGuid:le.createType(He),ErrorType:le.createType(Oe),ErrorMsg:le.createType(String),Node:le.createType(new ce(Ve)),CustomProperties:le.createType([new ue(ot,lt)])};static nameRegex=/^(\w+?)(?:_(\d+))?$/;static sequencerScriptingNameRegex=/\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/;static customPropertyGrammar=Te.seq(Te.reg(/CustomProperties\s+/),Be.grammarFor(void 0,this.attributes.CustomProperties.type[0])).map((([e,t])=>e=>{e.CustomProperties||(e.CustomProperties=[]),e.CustomProperties.push(t)}));static inlinedArrayEntryGrammar=Te.seq(Te.alt(Be.symbolQuoted.map((e=>[e,!0])),Be.symbol.map((e=>[e,!1]))),Te.reg(new RegExp("\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*"),1).map(Number)).chain((([[e,t],i])=>Be.grammarFor(this.attributes[e]).map((s=>r=>{(r[e]??=[])[i]=s,he.objectSet(r,["attributes",e,"quoted"],t),this.attributes[e]?.inlined||(r.attributes||Ie.defineAttributes(r,{}),he.objectSet(r,["attributes",e,"type"],[s.constructor]),he.objectSet(r,["attributes",e,"inlined"],!0))}))));static grammar=this.createGrammar();static createSubObjectGrammar(){return Te.lazy((()=>this.grammar)).map((e=>t=>t[ne.subObjectAttributeNameFromEntity(e)]=e))}static createGrammar(){return Te.seq(Te.reg(/Begin\s+Object/),Te.seq(Te.whitespace,Te.alt(this.customPropertyGrammar,Be.createAttributeGrammar(this),Be.createAttributeGrammar(this,Be.attributeNameQuoted,void 0,((e,t,i)=>he.objectSet(e,["attributes",...t,"quoted"],!0))),this.inlinedArrayEntryGrammar,this.createSubObjectGrammar())).map((([e,t])=>t)).many(),Te.reg(/\s+End\s+Object/)).map((([e,t,i])=>{const s={};return t.forEach((e=>e(s))),new this(s)}))}static keyName(e){let t=ut.#A[e];if(t)return t;if(t=he.numberFromText(e)?.toString(),t)return t;const i=e.match(/NumPad([a-zA-Z]+)/);return i&&(t=he.numberFromText(i[1]).toString(),t)?"Num "+t:void 0}static getMultipleObjectsGrammar(){return Te.seq(Te.whitespaceOpt,this.createGrammar(),Te.seq(Te.whitespace,this.createGrammar()).map((([e,t])=>t)).many(),Te.whitespaceOpt).map((([e,t,i,s])=>[t,...i]))}#L;constructor(e={},t=!1){if("NodePosX"in e!="NodePosY"in e){const t=Object.entries(e),[i,s]="NodePosX"in e?["NodePosY",Object.keys(e).indexOf("NodePosX")+1]:["NodePosX",Object.keys(e).indexOf("NodePosY")],r=[i,new(le.getAttribute(e,i,"type",ut))];t.splice(s,0,r),e=Object.fromEntries(t)}super(e,t),this.R,this.G,this.B,this.A,this.CustomProperties,this.bIsPureFunc,this.ComponentPropertyName,this.EventReference,this.FunctionReference,this.AdvancedPinDisplay,this.EnabledState,this.NodeHeight,this.NodePosX,this.NodePosY,this.NodeWidth,this.CommentColor,this.NodeTitleColor,this.MacroGraphReference,this.MaterialExpressionEditorX,this.MaterialExpressionEditorY,this.SizeX,this.SizeY,this.Text,this.PositionX,this.PositionY,this.Node,this.PinTags,this.NumAdditionalInputs,this.InputPins,this.OutputPins,this.Archetype,this.BlueprintElementInstance,this.BlueprintElementType,this.Class,this.Enum,this.ExportPath,this.Graph,this.MaterialExpression,this.MaterialExpressionComment,this.MaterialFunction,this.ObjectRef,this.PCGNode,this.SettingsInterface,this.StructType,this.TargetType,this.EnumEntries,this.PinNames,this.CustomFunctionName,this.DelegatePropertyName,this.InputName,this.Name,this.NodeComment,this.NodeTitle,this.Operation,this.ProxyFactoryFunctionName,this.SubgraphInstance,this.Text,this.AxisKey,this.HiGenGridSize,this.InputAxisKey,this.InputKey,this.InputType,this.DelegateReference,this.VariableReference,this.Pins instanceof Array&&this.Pins.forEach((e=>{const t=this[ne.subObjectAttributeNameFromReference(e,!0)];if(t){const e=ot.fromLegacyObject(t);e.LinkedTo=[],this.getCustomproperties(!0).push(e),he.objectSet(this,["attributes","CustomProperties","ignored"],!0)}}));const i=this.getMaterialSubobject();if(i){const e=i;if(void 0!==e.SizeX&&(e.SizeX.getter=()=>this.NodeWidth),e.SizeY&&(e.SizeY.getter=()=>this.NodeHeight),e.Text&&(e.Text.getter=()=>this.NodeComment),e.MaterialExpressionEditorX&&(e.MaterialExpressionEditorX.getter=()=>this.NodePosX),e.MaterialExpressionEditorY&&(e.MaterialExpressionEditorY.getter=()=>this.NodePosY),this.getType()===ne.paths.materialExpressionComponentMask){const t=ne.rgba.map((e=>this.getPinEntities().find((t=>t.PinName===e&&(t.recomputesNodeTitleOnChange=!0)))));e.R=new ce(Boolean,(()=>t[0].DefaultValue)),e.G=new ce(Boolean,(()=>t[1].DefaultValue)),e.B=new ce(Boolean,(()=>t[2].DefaultValue)),e.A=new ce(Boolean,(()=>t[3].DefaultValue))}}const s=this.getPcgSubobject();s&&(s.PositionX&&(s.PositionX.getter=()=>this.NodePosX),s.PositionY&&(s.PositionY.getter=()=>this.NodePosY),s.getSubobjects().forEach((e=>{if(void 0!==e.Node){const t=e.Node.get();t.type===this.PCGNode.type&&t.path===`${this.Name}.${this.PCGNode.path}`&&(e.Node.getter=()=>new Ve({type:this.PCGNode.type,path:`${this.Name}.${this.PCGNode.path}`}))}})));let r=0,n=0;this.CustomProperties?.forEach(((e,t)=>{e.objectEntity=this,e.pinIndex=e.isInput()?r++:e.isOutput()?n++:t}))}getClass(){if(!this.#L&&(this.#L=(this.Class?.path?this.Class.path:this.Class?.type)??(this.ExportPath?.path?this.ExportPath.path:this.ExportPath?.type)??"",this.#L&&!this.#L.startsWith("/"))){let e=Object.values(ne.paths).find((e=>e.endsWith("."+this.#L)));e&&(this.#L=e)}return this.#L}getType(){let e=this.getClass();return this.MacroGraphReference?.MacroGraph?.path?this.MacroGraphReference.MacroGraph.path:this.MaterialExpression?this.MaterialExpression.type:e}getObjectName(e=!1){return e?this.getNameAndCounter()[0]:this.Name}getNameAndCounter(){const e=this.getObjectName(!1).match(ut.nameRegex);let t="",i=null;return e?(e.length>1&&(t=e[1]),e.length>2&&(i=parseInt(e[2])),[t,i]):["",0]}getCounter(){return this.getNameAndCounter()[1]}getNodeWidth(){return this.NodeWidth??this.isComment()?ne.defaultCommentWidth:void 0}setNodeWidth(e){this.NodeWidth||(this.NodeWidth=new Oe),this.NodeWidth.value=e}getNodeHeight(){return this.NodeHeight??this.isComment()?ne.defaultCommentHeight:void 0}setNodeHeight(e){this.NodeHeight||(this.NodeHeight=new Oe),this.NodeHeight.value=e}getNodePosX(){return this.NodePosX?.value??0}setNodePosX(e){this.NodePosX||(this.NodePosX=new Oe),this.NodePosX.value=Math.round(e)}getNodePosY(){return this.NodePosY?.value??0}setNodePosY(e){this.NodePosY||(this.NodePosY=new Oe),this.NodePosY.value=Math.round(e)}getCustomproperties(e=!1){return e&&!this.CustomProperties&&(this.CustomProperties=[]),this.CustomProperties??[]}getPinEntities(){return this.getCustomproperties().filter((e=>e.constructor===ot))}getSubobjects(){return Object.keys(this).filter((e=>e.startsWith(ne.subObjectAttributeNamePrefix))).flatMap((e=>[this[e],...this[e].getSubobjects()]))}switchTarget(){const e=this.getClass().match(ne.switchTargetPattern);if(e)return e[1]}isEvent(){switch(this.getClass()){case ne.paths.actorBoundEvent:case ne.paths.componentBoundEvent:case ne.paths.customEvent:case ne.paths.event:case ne.paths.inputAxisKeyEvent:case ne.paths.inputVectorAxisEvent:return!0}return!1}isComment(){switch(this.getClass()){case ne.paths.comment:case ne.paths.materialGraphNodeComment:return!0}return!1}isMaterial(){return this.getClass()===ne.paths.materialGraphNode}getMaterialSubobject(){const e=this.MaterialExpression??this.MaterialExpressionComment;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}isPcg(){return this.getClass()===ne.paths.pcgEditorGraphNode||this.getPcgSubobject()}getPcgSubobject(){const e=this.PCGNode;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}getSettingsObject(){const e=this.SettingsInterface;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}getSubgraphObject(){const e=this.SubgraphInstance;return e?this[ne.subObjectAttributeNameFromName(e)]:null}isDevelopmentOnly(){const e=this.getClass();return"DevelopmentOnly"===this.EnabledState?.toString()||e.includes("Debug",Math.max(0,e.lastIndexOf(".")))}getHIDAttribute(){return this.InputKey??this.AxisKey??this.InputAxisKey}getDelegatePin(){return this.getCustomproperties().find((e=>"delegate"===e.PinType.PinCategory))}nodeDisplayName(){let e;switch(this.getType()){case ne.paths.asyncAction:if(this.ProxyFactoryFunctionName)return he.formatStringName(this.ProxyFactoryFunctionName);case ne.paths.actorBoundEvent:case ne.paths.componentBoundEvent:return`${he.formatStringName(this.DelegatePropertyName)} (${this.ComponentPropertyName??"Unknown"})`;case ne.paths.callDelegate:return`Call ${this.DelegateReference?.MemberName??"None"}`;case ne.paths.createDelegate:return"Create Event";case ne.paths.customEvent:if(this.CustomFunctionName)return this.CustomFunctionName;case ne.paths.dynamicCast:return this.TargetType?`Cast To ${this.TargetType?.getName()}`:"Bad cast node";case ne.paths.enumLiteral:return`Literal enum ${this.Enum?.getName()}`;case ne.paths.event:return`Event ${(this.EventReference?.MemberName??"").replace(/^Receive/,"")}`;case ne.paths.executionSequence:return"Sequence";case ne.paths.forEachElementInEnum:return`For Each ${this.Enum?.getName()}`;case ne.paths.forEachLoopWithBreak:return"For Each Loop with Break";case ne.paths.functionEntry:return"UserConstructionScript"===this.FunctionReference?.MemberName?"Construction Script":this.FunctionReference?.MemberName;case ne.paths.functionResult:return"Return Node";case ne.paths.ifThenElse:return"Branch";case ne.paths.makeStruct:if(this.StructType)return`Make ${this.StructType.getName()}`;case ne.paths.materialExpressionComponentMask:{const e=this.getMaterialSubobject();return`Mask ( ${ne.rgba.filter((t=>!0===e[t].get())).map((e=>e+" ")).join("")})`}case ne.paths.materialExpressionConstant:e??=[this.getCustomproperties().find((e=>"Value"==e.PinName))?.DefaultValue];case ne.paths.materialExpressionConstant2Vector:e??=[this.getCustomproperties().find((e=>"X"==e.PinName))?.DefaultValue,this.getCustomproperties().find((e=>"Y"==e.PinName))?.DefaultValue];case ne.paths.materialExpressionConstant3Vector:if(!e){const t=this.getCustomproperties().find((e=>"Constant"==e.PinName))?.DefaultValue;e=[t.X,t.Y,t.Z]}case ne.paths.materialExpressionConstant4Vector:if(!e){const t=this.getCustomproperties().find((e=>"Constant"==e.PinName))?.DefaultValue;e=[t.R,t.G,t.B,t.A].map((e=>e.valueOf()))}if(e.length>0)return e.map((e=>he.printExponential(e))).reduce(((e,t)=>e+","+t));break;case ne.paths.materialExpressionFunctionInput:{const e=this.getMaterialSubobject();return`Input ${e?.InputName??"In"} (${e?.InputType?.value.match(/^.+?_(\w+)$/)?.[1]??"Vector3"})`}case ne.paths.materialExpressionLogarithm:return"Ln";case ne.paths.materialExpressionLogarithm10:return"Log10";case ne.paths.materialExpressionLogarithm2:return"Log2";case ne.paths.materialExpressionMaterialFunctionCall:const t=this.getMaterialSubobject()?.MaterialFunction;if(t)return t.getName();break;case ne.paths.materialExpressionSquareRoot:return"Sqrt";case ne.paths.pcgEditorGraphNodeInput:return"Input";case ne.paths.pcgEditorGraphNodeOutput:return"Output";case ne.paths.spawnActorFromClass:return`SpawnActor ${he.formatStringName(this.getCustomproperties().find((e=>"class"==e.getType()))?.DefaultObject?.getName()??"NONE")}`;case ne.paths.switchEnum:return`Switch on ${this.Enum?.getName()??"Enum"}`;case ne.paths.switchInteger:return"Switch on Int";case ne.paths.variableGet:return"";case ne.paths.variableSet:return"SET"}let t=this.switchTarget();if(t)return"E"!==t[0]&&(t=he.formatStringName(t)),`Switch on ${t}`;if(this.isComment())return this.NodeComment;const i=this.getHIDAttribute();if(i){const e=i.toString();let t=ut.keyName(e)??he.formatStringName(e);return this.getClass()===ne.paths.inputDebugKey?t="Debug Key "+t:this.getClass()===ne.paths.getInputAxisKeyValue&&(t="Get "+t),t}if(this.getClass()===ne.paths.macro)return he.formatStringName(this.MacroGraphReference?.getMacroName());if(this.isMaterial()&&this.getMaterialSubobject()){let e=this.getMaterialSubobject().nodeDisplayName();return e=e.match(/Material Expression (.+)/)?.[1]??e,e}if(this.isPcg()&&this.getPcgSubobject()){let e=this.getPcgSubobject();return e.NodeTitle?e.NodeTitle:e.nodeDisplayName()}const s=this.getSubgraphObject();if(s)return s.Graph.getName();const r=this.getSettingsObject();if(r){if(r.ExportPath.type===ne.paths.pcgHiGenGridSizeSettings)return`Grid Size: ${r.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")??r.HiGenGridSize?.toString().match(/^\w+$/)?.[0]??"256"}`;if(r.BlueprintElementInstance)return he.formatStringName(r.BlueprintElementType.getName());if(r.Operation){const e=r.Name.match(/PCGMetadata(\w+)Settings_\d+/);if(e)return he.formatStringName(e[1]+": "+r.Operation)}const e=r.getSubgraphObject();if(e&&e.Graph)return e.Graph.getName()}let n=this.FunctionReference?.MemberName;if(n){const e=this.FunctionReference.MemberParent?.path??"";switch(n){case"AddKey":let t=e.match(ut.sequencerScriptingNameRegex);if(t)return`Add Key (${he.formatStringName(t[1])})`;case"Concat_StrStr":return"Append"}const t=n.match(ne.lineTracePattern);if(t)return"Line Trace"+("Multi"===t[1]?" Multi ":" ")+(""===t[2]?"By Channel":he.formatStringName(t[2]));switch(e){case ne.paths.blueprintGameplayTagLibrary:case ne.paths.kismetMathLibrary:case ne.paths.slateBlueprintLibrary:case ne.paths.timeManagementBlueprintLibrary:const e=n.match(/[BF]([A-Z]\w+)/);switch(e&&(n=e[1]),n){case"Abs":return"ABS";case"BooleanAND":return"AND";case"BooleanNAND":return"NAND";case"BooleanOR":return"OR";case"Exp":return"e";case"LineTraceSingle":return"Line Trace By Channel";case"Max":case"MaxInt64":return"MAX";case"Min":case"MinInt64":return"MIN";case"Not_PreBool":return"NOT";case"Sin":return"SIN";case"Sqrt":return"SQRT";case"Square":return"^2";case"CrossProduct2D":return"cross";case"Vector4_CrossProduct3":return"cross3";case"DotProduct2D":case"Vector4_DotProduct":return"dot";case"Vector4_DotProduct3":return"dot3"}if(n.startsWith("Add_"))return"+";if(n.startsWith("And_"))return"&";if(n.startsWith("Conv_"))return"";if(n.startsWith("Cross_"))return"cross";if(n.startsWith("Divide_"))return String.fromCharCode(247);if(n.startsWith("Dot_"))return"dot";if(n.startsWith("EqualEqual_"))return"==";if(n.startsWith("Greater_"))return">";if(n.startsWith("GreaterEqual_"))return">=";if(n.startsWith("Less_"))return"<";if(n.startsWith("LessEqual_"))return"<=";if(n.startsWith("Multiply_"))return String.fromCharCode(10799);if(n.startsWith("Not_"))return"~";if(n.startsWith("NotEqual_"))return"!=";if(n.startsWith("Or_"))return"|";if(n.startsWith("Percent_"))return"%";if(n.startsWith("Subtract_"))return"-";if(n.startsWith("Xor_"))return"^";break;case ne.paths.blueprintSetLibrary:{const e=n.match(/Set_(\w+)/);if(e)return he.formatStringName(e[1]).toUpperCase()}break;case ne.paths.blueprintMapLibrary:{const e=n.match(/Map_(\w+)/);if(e)return he.formatStringName(e[1]).toUpperCase()}break;case ne.paths.kismetArrayLibrary:{const e=n.match(/Array_(\w+)/);if(e)return e[1].toUpperCase()}}return he.formatStringName(n)}return this.ObjectRef?this.ObjectRef.getName():he.formatStringName(this.getNameAndCounter()[0])}nodeColor(){switch(this.getType()){case ne.paths.materialExpressionConstant2Vector:case ne.paths.materialExpressionConstant3Vector:case ne.paths.materialExpressionConstant4Vector:return ne.nodeColors.yellow;case ne.paths.makeStruct:return ne.nodeColors.darkBlue;case ne.paths.materialExpressionMaterialFunctionCall:return ne.nodeColors.blue;case ne.paths.materialExpressionFunctionInput:return ne.nodeColors.red;case ne.paths.materialExpressionTextureSample:return ne.nodeColors.darkTurquoise;case ne.paths.materialExpressionTextureCoordinate:case ne.paths.pcgEditorGraphNodeInput:case ne.paths.pcgEditorGraphNodeOutput:return ne.nodeColors.red}switch(this.getClass()){case ne.paths.callFunction:return this.bIsPureFunc?ne.nodeColors.green:ne.nodeColors.blue;case ne.paths.dynamicCast:return ne.nodeColors.turquoise;case ne.paths.inputDebugKey:case ne.paths.inputKey:return ne.nodeColors.red;case ne.paths.createDelegate:case ne.paths.enumLiteral:case ne.paths.makeArray:case ne.paths.makeMap:case ne.paths.materialGraphNode:case ne.paths.select:return ne.nodeColors.green;case ne.paths.executionSequence:case ne.paths.ifThenElse:case ne.paths.macro:case ne.paths.multiGate:return ne.nodeColors.gray;case ne.paths.functionEntry:case ne.paths.functionResult:return ne.nodeColors.violet;case ne.paths.timeline:return ne.nodeColors.yellow}if(this.switchTarget())return ne.nodeColors.lime;if(this.isEvent())return ne.nodeColors.red;if(this.isComment())return(this.CommentColor?this.CommentColor:ze.getWhite()).toDimmedColor().toCSSRGBValues();const e=this.getPcgSubobject();return e&&e.NodeTitleColor?e.NodeTitleColor.toDimmedColor(.1).toCSSRGBValues():this.bIsPureFunc?ne.nodeColors.green:ne.nodeColors.blue}nodeIcon(){if(this.isMaterial()||this.isPcg())return null;switch(this.getType()){case ne.paths.addDelegate:case ne.paths.asyncAction:case ne.paths.callDelegate:case ne.paths.createDelegate:case ne.paths.functionEntry:case ne.paths.functionResult:return Ae.node;case ne.paths.customEvent:return Ae.event;case ne.paths.doN:return Ae.doN;case ne.paths.doOnce:return Ae.doOnce;case ne.paths.dynamicCast:return Ae.cast;case ne.paths.enumLiteral:return Ae.enum;case ne.paths.event:return Ae.event;case ne.paths.executionSequence:case ne.paths.multiGate:return Ae.sequence;case ne.paths.flipflop:return Ae.flipflop;case ne.paths.forEachElementInEnum:case ne.paths.forLoop:case ne.paths.forLoopWithBreak:case ne.paths.whileLoop:return Ae.loop;case ne.paths.forEachLoop:case ne.paths.forEachLoopWithBreak:return Ae.forEachLoop;case ne.paths.ifThenElse:return Ae.branchNode;case ne.paths.isValid:return Ae.questionMark;case ne.paths.makeArray:return Ae.makeArray;case ne.paths.makeMap:return Ae.makeMap;case ne.paths.makeSet:return Ae.makeSet;case ne.paths.makeStruct:return Ae.makeStruct;case ne.paths.select:return Ae.select;case ne.paths.spawnActorFromClass:return Ae.spawnActor;case ne.paths.timeline:return Ae.timer}if(this.switchTarget())return Ae.switch;if(this.nodeDisplayName().startsWith("Break"))return Ae.breakStruct;if(this.getClass()===ne.paths.macro)return Ae.macro;const e=this.getHIDAttribute()?.toString();return e?e.includes("Mouse")?Ae.mouse:e.includes("Gamepad_Special")?Ae.keyboard:e.includes("Gamepad")||e.includes("Steam")?Ae.gamepad:e.includes("Touch")?Ae.touchpad:Ae.keyboard:this.getDelegatePin()?Ae.event:this.ObjectRef?.type===ne.paths.ambientSound?Ae.sound:Ae.functionSymbol}additionalPinInserter(){let e,t,i;switch(this.getType()){case ne.paths.commutativeAssociativeBinaryOperator:case ne.paths.promotableOperator:switch(this.FunctionReference?.MemberName){default:if(!(this.FunctionReference?.MemberName?.startsWith("Add_")||this.FunctionReference?.MemberName?.startsWith("Subtract_")||this.FunctionReference?.MemberName?.startsWith("Multiply_")||this.FunctionReference?.MemberName?.startsWith("Divide_")))break;case"And_Int64Int64":case"And_IntInt":case"BMax":case"BMin":case"BooleanAND":case"BooleanNAND":case"BooleanOR":case"Concat_StrStr":case"FMax":case"FMin":case"Max":case"MaxInt64":case"Min":case"MinInt64":case"Or_Int64Int64":case"Or_IntInt":e??=()=>this.getPinEntities().filter((e=>e.isInput())),t??=e=>e.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0)-"A".charCodeAt(0),i??=(t,i=-1,s=-1)=>{const r=String.fromCharCode(t>=0?t:s+"A".charCodeAt(0)+1);return this.NumAdditionalInputs=e().length-1,r}}break;case ne.paths.multiGate:e??=()=>this.getPinEntities().filter((e=>e.isOutput())),t??=e=>Number(e.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]),i??=(e,t=-1,i=-1)=>`Out ${e>=0?e:t>0?"Out 0":i+1}`;break;case ne.paths.switchInteger:e??=()=>this.getPinEntities().filter((e=>e.isOutput())),t??=e=>Number(e.PinName.match(/^\s*(\d+)\s*$/)?.[1]),i??=(e,t=-1,i=-1)=>(e<0?i+1:e).toString();break;case ne.paths.switchGameplayTag:i??=(e,t=-1,i=-1)=>{const s=`Case_${e>=0?e:t>0?"0":i+1}`;return this.PinNames??=[],this.PinNames.push(s),delete this.PinTags[this.PinTags.length-1],this.PinTags[this.PinTags.length]=null,s};case ne.paths.switchName:case ne.paths.switchString:e??=()=>this.getPinEntities().filter((e=>e.isOutput())),t??=e=>Number(e.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]),i??=(e,t=-1,i=-1)=>{const s=`Case_${e>=0?e:t>0?"0":i+1}`;return this.PinNames??=[],this.PinNames.push(s),s}}if(e)return()=>{let s=Number.MAX_SAFE_INTEGER,r=Number.MIN_SAFE_INTEGER,n=[];const a=e().reduce(((e,i)=>{const a=t(i);if(isNaN(a)){if(void 0===e)return i}else if(n.push(a),s=Math.min(a,s),a>r)return r=a,i;return e}),void 0);if(s!==Number.MAX_SAFE_INTEGER&&r!==Number.MIN_SAFE_INTEGER||(s=void 0,r=void 0),!a)return null;n.sort(((e,t)=>e{const t=e-o>1;return o=e,t}));const c=new ot(a);return c.PinId=He.generateGuid(),c.PinName=i(l,s,r),c.PinToolTip=void 0,this.getCustomproperties(!0).push(c),c}}}class ht{static same=e=>e;static notWrapped=(e,t)=>t;static bracketsWrapped=(e,t)=>`(${t})`;constructor(e,t=((e,t)=>t),i=",",s=!1,r="=",n=ht.same){this.entityType=e,this.wrap=t,this.attributeSeparator=i,this.trailingSeparator=s,this.attributeValueConjunctionSign=r,this.attributeKeyPrinter=n}read(e){return this.doRead(e.trim())}write(e,t=!1){return this.doWrite(e,t)}doRead(e){const t=Be.grammarFor(void 0,this.entityType).run(e);if(!t.status)throw new Error(this.entityType?`Error when trying to parse the entity ${this.entityType.prototype.constructor.name}`:"Error when trying to parse null");return t.value}doWrite(e,t=!1,i="",s=this.wrap,r=this.attributeSeparator,n=this.trailingSeparator,a=this.attributeValueConjunctionSign,o=this.attributeKeyPrinter){let l="";const c=Object.keys(e);let u=!0;for(const s of c){const n=e[s];if(void 0!==n&&this.showProperty(e,s)){let c=e instanceof Array?`(${s})`:s;le.getAttribute(e,s,"quoted")&&(c=`"${c}"`);const h=le.getAttribute(e,s,"serialized");if(u?u=!1:l+=r,le.getAttribute(e,s,"inlined")){l+=this.doWrite(n,t,i,ht.notWrapped,r,!1,a,le.getAttribute(e,s,"type")instanceof Array?e=>o(`${c}${e}`):e=>o(`${c}.${e}`));continue}const p=o(c),d=r.includes("\n")?i:"";l+=(p.length?d+p+this.attributeValueConjunctionSign:"")+(h?`"${this.doWriteValue(n,!0,i)}"`:this.doWriteValue(n,t,i))}}return n&&l.length&&(l+=r),s(e,l)}doWriteValue(e,t,i=""){const s=he.getType(e),r=Me.getSerializer(s);if(!r)throw new Error(`Unknown value type "${s.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`);return r.doWrite(e,t,i)}showProperty(e,t){return!(e instanceof Ie&&(le.getAttribute(e,t,"ignored")||le.getAttribute(e,t,"silent")&&he.equals(le.getAttribute(e,t,"default"),e[t])))}}class pt extends ht{constructor(e=ut){super(e,void 0,"\n",!0,void 0,ht.same)}showProperty(e,t){switch(t){case"Class":case"Name":case"Archetype":case"ExportPath":case"CustomProperties":return!1}return super.showProperty(e,t)}write(e,t=!1){return this.doWrite(e,t)+"\n"}doRead(e){return Be.grammarFor(void 0,this.entityType).parse(e)}readMultiple(e){return ut.getMultipleObjectsGrammar().parse(e)}doWrite(e,t,i="",s=this.wrap,r=this.attributeSeparator,n=this.trailingSeparator,a=this.attributeValueConjunctionSign,o=this.attributeKeyPrinter){const l=i+ne.indentation;if(!(e instanceof ut))return super.doWrite(e,t,i,s,r,n,a,(t=>e[t]instanceof ut?"":o(t)));return i+"Begin Object"+(e.Class?.type||e.Class?.path?` Class=${this.doWriteValue(e.Class,t)}`:"")+(e.Name?` Name=${this.doWriteValue(e.Name,t)}`:"")+(e.Archetype?` Archetype=${this.doWriteValue(e.Archetype,t)}`:"")+(e.ExportPath?.type||e.ExportPath?.path?` ExportPath=${this.doWriteValue(e.ExportPath,t)}`:"")+"\n"+super.doWrite(e,t,l,s,r,!0,a,(t=>e[t]instanceof ut?"":o(t)))+(le.getAttribute(e,"CustomProperties","ignored")?"":e.getCustomproperties().map((e=>l+o("CustomProperties ")+Me.getSerializer(ot).doWrite(e,t)+this.attributeSeparator)).join(""))+i+"End Object"}}class dt extends pe{static#M=new pt;#I;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let s=this;this.#I=()=>s.copied()}listenEvents(){window.addEventListener("copy",this.#I)}unlistenEvents(){window.removeEventListener("copy",this.#I)}getSerializedText(){return this.blueprint.getNodes(!0).map((e=>dt.#M.write(e.entity,!1))).join("")}copied(){const e=this.getSerializedText();return navigator.clipboard.writeText(e),e}}class mt extends pe{static#M=new pt;#B;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let s=this;this.#B=()=>s.cut()}listenEvents(){window.addEventListener("cut",this.#B)}unlistenEvents(){window.removeEventListener("cut",this.#B)}getSerializedText(){return this.blueprint.getNodes(!0).map((e=>mt.#M.write(e.entity,!1))).join("")}cut(){this.blueprint.template.getCopyInputObject().copied(),this.blueprint.removeGraphElement(...this.blueprint.getNodes(!0))}}class gt{static#$=new Map;static registerElement(e,t){gt.#$.set(e,t)}static getConstructor(e){return gt.#$.get(e)}}class bt extends pe{static#M=new pt;#H;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let s=this;this.#H=e=>s.pasted(e.clipboardData.getData("Text"))}listenEvents(){window.addEventListener("paste",this.#H)}unlistenEvents(){window.removeEventListener("paste",this.#H)}pasted(e){let t=0,i=0,s=0,r=bt.#M.readMultiple(e).map((e=>{let r=gt.getConstructor("ueb-node").newObject(e);return t+=r.locationY,i+=r.locationX,++s,r}));t/=s,i/=s,r.length>0&&this.blueprint.unselectAll();let n=this.blueprint.mousePosition;return r.forEach((e=>{e.addLocation(n[0]-i,n[1]-t),e.snapToGrid(),e.setSelected(!0)})),this.blueprint.addGraphElement(...r),!0}}class vt extends Ie{static attributes={...super.attributes,ActionName:le.createValue(""),bShift:le.createValue(!1),bCtrl:le.createValue(!1),bAlt:le.createValue(!1),bCmd:le.createValue(!1),Key:le.createType(De)};static grammar=this.createGrammar();static createGrammar(){return Te.alt(De.grammar.map((e=>new this({Key:e}))),Be.createEntityGrammar(this))}constructor(e={}){super(e,!0),this.ActionName,this.bShift,this.bCtrl,this.bAlt,this.bCmd,this.Key}}class yt extends pe{static#V=e=>{};#G;pressedKey="";constructor(e,t,i={},s=yt.#V,r=yt.#V){i.activationKeys??=[],i.consumeEvent??=!0,i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,i.activationKeys instanceof Array||(i.activationKeys=[i.activationKeys]),i.activationKeys=i.activationKeys.map((e=>{if(e instanceof vt)return e;if(e.constructor===String){const t=vt.grammar.run(e);if(t.status)return t.value}throw new Error("Unexpected key value")})),super(e,t,i),this.onKeyDown=s,this.onKeyUp=r,this.#G=this.options.activationKeys??[];let n=this;this.keyDownHandler=e=>{n.#G.some((t=>(e=>e.bShift||"LeftShift"==e.Key||"RightShift"==e.Key)(t)==e.shiftKey&&(e=>e.bCtrl||"LeftControl"==e.Key||"RightControl"==e.Key)(t)==e.ctrlKey&&(e=>e.bAlt||"LeftAlt"==e.Key||"RightAlt"==e.Key)(t)==e.altKey&&ne.Keys[t.Key.value]==e.code))&&(this.consumeEvent&&(e.preventDefault(),e.stopImmediatePropagation()),this.pressedKey=e.code,n.fire(),document.removeEventListener("keydown",n.keyDownHandler),document.addEventListener("keyup",n.keyUpHandler))},this.keyUpHandler=e=>{n.#G.some((t=>t.bShift&&"Shift"==e.key||t.bCtrl&&"Control"==e.key||t.bAlt&&"Alt"==e.key||t.bCmd&&"Meta"==e.key||ne.Keys[t.Key.value]==e.code))&&(this.consumeEvent&&e.stopImmediatePropagation(),n.unfire(),this.pressedKey="",document.removeEventListener("keyup",this.keyUpHandler),document.addEventListener("keydown",this.keyDownHandler))}}listenEvents(){document.addEventListener("keydown",this.keyDownHandler)}unlistenEvents(){document.removeEventListener("keydown",this.keyDownHandler)}fire(){this.onKeyDown(this)}unfire(){this.onKeyUp(this)}}class ft extends pe{#D=[0,0];get location(){return this.#D}#O;get enablerKey(){return this.#O}#R=!0;get enablerActivated(){return this.#R}constructor(e,t,i={}){i.ignoreTranslateCompensate??=!1,i.ignoreScale??=!1,i.movementSpace??=t.getGridDOMElement()??document.documentElement,super(e,t,i),this.movementSpace=i.movementSpace,i.enablerKey&&(this.#O=i.enablerKey,this.#O.onKeyDown=()=>this.#R=!0,this.#O.onKeyUp=()=>this.#R=!1,this.#O.consumeEvent=!1,this.#O.listenEvents(),this.#R=!1)}setLocationFromEvent(e){let t=he.convertLocation([e.clientX,e.clientY],this.movementSpace,this.options.ignoreScale);return t=this.options.ignoreTranslateCompensate?t:this.blueprint.compensateTranslation(t[0],t[1]),this.#D=[...t],this.#D}}class wt extends ft{static#V=e=>{};#z=0;get variation(){return this.#z}#_=e=>{this.enablerKey&&!this.enablerActivated||(e.preventDefault(),this.#z=e.deltaY,this.setLocationFromEvent(e),this.wheel())};#F=e=>e.preventDefault();constructor(e,t,i={},s=wt.#V){i.listenOnFocus=!0,i.strictTarget??=!1,super(e,t,i),this.strictTarget=i.strictTarget,this.onWheel=s}listenEvents(){this.movementSpace.addEventListener("wheel",this.#_,!1),this.movementSpace.parentElement?.addEventListener("wheel",this.#F)}unlistenEvents(){this.movementSpace.removeEventListener("wheel",this.#_,!1),this.movementSpace.parentElement?.removeEventListener("wheel",this.#F)}wheel(){this.onWheel(this)}}class St extends wt{#j=0;#U=!1;get enableZoonIn(){return this.#U}set enableZoonIn(e){e!=this.#U&&(this.#U=e)}wheel(){if(this.#j+=-this.variation,Math.abs(this.#j)0||(e+=Math.sign(this.#j),this.blueprint.setZoom(e,this.location),this.#j=0)}}class Et extends yt{#W;constructor(e,t,i={}){i.activationKeys=ae.enableZoomIn,super(e,t,i)}fire(){this.#W=this.blueprint.template.getZoomInputObject(),this.#W.enableZoonIn=!0}unfire(){this.#W.enableZoonIn=!1}}class Ct extends se{#r;get blueprint(){return this.#r}set blueprint(e){this.#r=e}#K;get entity(){return this.#K}set entity(e){this.#K=e}#Y;get template(){return this.#Y}isInitialized=!1;isSetup=!1;inputObjects=[];initialize(e,t){this.requestUpdate(),this.#K=e,this.#Y=t,this.#Y.initialize(this),this.isConnected&&this.updateComplete.then((()=>this.setup())),this.isInitialized=!0}connectedCallback(){super.connectedCallback(),this.blueprint=this.closest("ueb-blueprint"),this.isInitialized&&(this.requestUpdate(),this.updateComplete.then((()=>this.setup())))}disconnectedCallback(){super.disconnectedCallback(),this.isSetup&&this.updateComplete.then((()=>this.cleanup())),this.acknowledgeDelete()}createRenderRoot(){return this}setup(){this.template.setup(),this.isSetup=!0}cleanup(){this.template.cleanup(),this.isSetup=!1}willUpdate(e){super.willUpdate(e),this.template.willUpdate(e)}update(e){super.update(e),this.template.update(e)}render(){return this.template.render()}firstUpdated(e){super.firstUpdated(e),this.template.firstUpdated(e),this.template.inputSetup()}updated(e){super.updated(e),this.template.updated(e)}acknowledgeDelete(){let e=new CustomEvent(ne.removeEventName);this.dispatchEvent(e)}isSameGraph(e){return this.blueprint&&this.blueprint==e?.blueprint}}class xt extends Ct{static properties={...super.properties,locationX:{type:Number,attribute:!1},locationY:{type:Number,attribute:!1},sizeX:{type:Number,attribute:!1},sizeY:{type:Number,attribute:!1}};static dragEventName=ne.dragEventName;static dragGeneralEventName=ne.dragGeneralEventName;constructor(){super(),this.locationX=0,this.locationY=0,this.sizeX=0,this.sizeY=0}computeSizes(){const e=this.getBoundingClientRect();this.sizeX=this.blueprint.scaleCorrect(e.width),this.sizeY=this.blueprint.scaleCorrect(e.height)}firstUpdated(e){super.firstUpdated(e),this.computeSizes()}setLocation(e,t,i=!0){const s=e-this.locationX,r=t-this.locationY;if(this.locationX=e,this.locationY=t,this.blueprint&&i){const e=new CustomEvent(this.constructor.dragEventName,{detail:{value:[s,r]},bubbles:!1,cancelable:!0});this.dispatchEvent(e)}}addLocation(e,t,i=!0){this.setLocation(this.locationX+e,this.locationY+t,i)}acknowledgeDrag(e){const t=new CustomEvent(this.constructor.dragGeneralEventName,{detail:{value:e},bubbles:!0,cancelable:!0});this.dispatchEvent(t)}snapToGrid(){const e=he.snapToGrid(this.locationX,this.locationY,ne.gridSize);this.locationX==e[0]&&this.locationY==e[1]||this.setLocation(e[0],e[1])}topBoundary(e=!1){return this.template.topBoundary(e)}rightBoundary(e=!1){return this.template.rightBoundary(e)}bottomBoundary(e=!1){return this.template.bottomBoundary(e)}leftBoundary(e=!1){return this.template.leftBoundary(e)}}class Pt extends ft{#X=e=>{if(this.blueprint.setFocused(!0),e.button===this.options.clickButton)this.options.strictTarget&&e.target!=e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),this.#Z.addEventListener("mousemove",this.#q),document.addEventListener("mouseup",this.#Q),this.setLocationFromEvent(e),this.clickedPosition[0]=this.location[0],this.clickedPosition[1]=this.location[1],this.blueprint.mousePosition[0]=this.location[0],this.blueprint.mousePosition[1]=this.location[1],this.target instanceof xt&&(this.clickedOffset=[this.clickedPosition[0]-this.target.locationX,this.clickedPosition[1]-this.target.locationY]),this.clicked(this.clickedPosition));else this.options.exitAnyButton||this.#Q(e)};#q=e=>{this.consumeEvent&&e.stopImmediatePropagation(),this.#Z.removeEventListener("mousemove",this.#q),this.#Z.addEventListener("mousemove",this.#J);const t=this.getEvent(ne.trackingMouseEventName.begin);this.#ee=0==this.target.dispatchEvent(t),this.setLocationFromEvent(e),this.lastLocation=he.snapToGrid(this.clickedPosition[0],this.clickedPosition[1],this.stepSize),this.startDrag(this.location),this.started=!0,this.#J(e)};#J=e=>{this.consumeEvent&&e.stopImmediatePropagation();const t=this.setLocationFromEvent(e),i=[e.movementX,e.movementY];if(this.dragTo(t,i),this.#ee&&(this.blueprint.mousePosition=t),this.options.scrollGraphEdge){const e=Math.sqrt(i[0]*i[0]+i[1]*i[1]),s=this.blueprint.scaleCorrect(ne.edgeScrollThreshold),r=this.blueprint.template.gridLeftVisibilityBoundary()+s,n=this.blueprint.template.gridRightVisibilityBoundary()-s;let a=0;t[0]n&&(a=t[0]-n);const o=this.blueprint.template.gridTopVisibilityBoundary()+s,l=this.blueprint.template.gridBottomVisibilityBoundary()-s;let c=0;t[1]l&&(c=t[1]-l),a=he.clamp(this.blueprint.scaleCorrectReverse(a)**3*e*.6,-20,20),c=he.clamp(this.blueprint.scaleCorrectReverse(c)**3*e*.6,-20,20),this.blueprint.scrollDelta(a,c)}};#Q=e=>{if(!this.options.exitAnyButton||e.button==this.options.clickButton){if(this.consumeEvent&&e.stopImmediatePropagation(),this.#Z.removeEventListener("mousemove",this.#q),this.#Z.removeEventListener("mousemove",this.#J),document.removeEventListener("mouseup",this.#Q),this.started&&this.endDrag(),this.unclicked(),this.#ee){const e=this.getEvent(ne.trackingMouseEventName.end);this.target.dispatchEvent(e),this.#ee=!1}this.started=!1}};#ee=!1;#Z;#te;get draggableElement(){return this.#te}clickedOffset=[0,0];clickedPosition=[0,0];lastLocation=[0,0];started=!1;stepSize=1;constructor(e,t,i={}){i.clickButton??=ne.mouseClickButton,i.consumeEvent??=!0,i.draggableElement??=e,i.exitAnyButton??=!0,i.moveEverywhere??=!1,i.movementSpace??=t?.getGridDOMElement(),i.repositionOnClick??=!1,i.scrollGraphEdge??=!1,i.strictTarget??=!1,super(e,t,i),this.stepSize=Number(i.stepSize??ne.gridSize),this.#Z=this.options.moveEverywhere?document.documentElement:this.movementSpace,this.#te=this.options.draggableElement,this.listenEvents()}listenEvents(){super.listenEvents(),this.#te.addEventListener("mousedown",this.#X),this.options.clickButton===ne.mouseRightClickButton&&this.#te.addEventListener("contextmenu",(e=>e.preventDefault()))}unlistenEvents(){super.unlistenEvents(),this.#te.removeEventListener("mousedown",this.#X)}getEvent(e){return new CustomEvent(e,{detail:{tracker:this},bubbles:!0,cancelable:!0})}clicked(e){}startDrag(e){}dragTo(e,t){}endDrag(){}unclicked(e){}}class Nt extends Pt{startDrag(){this.blueprint.scrolling=!0}dragTo(e,t){this.blueprint.scrollDelta(-t[0],-t[1])}endDrag(){this.blueprint.scrolling=!1}}class kt extends ft{#ie=null;#se=e=>{e.preventDefault(),this.setLocationFromEvent(e),this.blueprint.mousePosition=[...this.location]};#re=e=>{this.#ie||(e.preventDefault(),this.#ie=e.detail.tracker,this.unlistenMouseMove())};#ne=e=>{this.#ie==e.detail.tracker&&(e.preventDefault(),this.#ie=null,this.listenMouseMove())};constructor(e,t,i={}){i.listenOnFocus=!0,super(e,t,i)}listenMouseMove(){this.target.addEventListener("mousemove",this.#se)}unlistenMouseMove(){this.target.removeEventListener("mousemove",this.#se)}listenEvents(){this.listenMouseMove(),this.blueprint.addEventListener(ne.trackingMouseEventName.begin,this.#re),this.blueprint.addEventListener(ne.trackingMouseEventName.end,this.#ne)}unlistenEvents(){this.unlistenMouseMove(),this.blueprint.removeEventListener(ne.trackingMouseEventName.begin,this.#re),this.blueprint.removeEventListener(ne.trackingMouseEventName.end,this.#ne)}}class Tt extends Pt{constructor(e,t,i={}){i.scrollGraphEdge??=!0,super(e,t,i),this.selectorElement=this.blueprint.template.selectorElement}startDrag(){this.selectorElement.beginSelect(this.clickedPosition)}dragTo(e,t){this.selectorElement.selectTo(e)}endDrag(){this.started&&this.selectorElement.endSelect()}unclicked(){this.started||this.blueprint.unselectAll()}}class At extends pe{#ae=e=>this.clickedSomewhere(e.target);constructor(e,t,i={}){i.listenOnFocus=!0,super(e,t,i),this.blueprint.focus&&document.addEventListener("click",this.#ae)}clickedSomewhere(e){e.closest("ueb-blueprint")||this.blueprint.setFocused(!1)}listenEvents(){document.addEventListener("click",this.#ae)}unlistenEvents(){document.removeEventListener("click",this.#ae)}}class Lt{element;get blueprint(){return this.element.blueprint}#oe=[];get inputObjects(){return this.#oe}initialize(e){this.element=e}createInputObjects(){return[]}setup(){this.#oe.forEach((e=>e.setup()))}cleanup(){this.#oe.forEach((e=>e.cleanup()))}willUpdate(e){}update(e){}render(){return G``}firstUpdated(e){}updated(e){}inputSetup(){this.#oe=this.createInputObjects()}}class Mt extends Lt{static styleVariables={"--ueb-font-size":`${ne.fontSize}`,"--ueb-grid-axis-line-color":`${ne.gridAxisLineColor}`,"--ueb-grid-expand":`${ne.expandGridSize}px`,"--ueb-grid-line-color":`${ne.gridLineColor}`,"--ueb-grid-line-width":`${ne.gridLineWidth}px`,"--ueb-grid-set-line-color":`${ne.gridSetLineColor}`,"--ueb-grid-set":`${ne.gridSet}`,"--ueb-grid-size":`${ne.gridSize}px`,"--ueb-link-min-width":`${ne.linkMinWidth}`,"--ueb-node-radius":`${ne.nodeRadius}px`};#le=new ResizeObserver((e=>{const t=e.find((e=>e.target===this.viewportElement))?.devicePixelContentBoxSize?.[0];t&&(this.viewportSize[0]=t.inlineSize,this.viewportSize[1]=t.blockSize)}));#ce;#ue;#W;headerElement;overlayElement;viewportElement;selectorElement;gridElement;linksContainerElement;nodesContainerElement;viewportSize=[0,0];#he(){}initialize(e){super.initialize(e),this.element.style.cssText=Object.entries(Mt.styleVariables).map((([e,t])=>`${e}:${t};`)).join("");const t=this.element.querySelector(":scope > template")?.content.textContent;t&&(this.element.requestUpdate(),this.element.updateComplete.then((()=>{this.blueprint.mousePosition=[Math.round(this.viewportSize[0]/2),Math.round(this.viewportSize[1]/2)],this.getPasteInputObject().pasted(t),this.blueprint.unselectAll()})))}setup(){super.setup(),this.#le.observe(this.viewportElement,{box:"device-pixel-content-box"});const e=this.viewportElement.getBoundingClientRect();this.viewportSize[0]=e.width,this.viewportSize[1]=e.height,this.blueprint.nodes.length>0&&(this.blueprint.requestUpdate(),this.blueprint.updateComplete.then((()=>this.centerContentInViewport())))}cleanup(){super.cleanup(),this.#le.unobserve(this.viewportElement)}createInputObjects(){const e=this.element.getGridDOMElement();return this.#ce=new dt(e,this.blueprint),this.#ue=new bt(e,this.blueprint),this.#W=new St(e,this.blueprint),[...super.createInputObjects(),this.#ce,this.#ue,this.#W,new mt(e,this.blueprint),new yt(e,this.blueprint,{activationKeys:ae.duplicateNodes},(()=>this.blueprint.template.getPasteInputObject().pasted(this.blueprint.template.getCopyInputObject().copied()))),new yt(e,this.blueprint,{activationKeys:ae.deleteNodes},(()=>this.blueprint.removeGraphElement(...this.blueprint.getNodes(!0)))),new yt(e,this.blueprint,{activationKeys:ae.selectAllNodes},(()=>this.blueprint.selectAll())),new Tt(e,this.blueprint,{clickButton:ne.mouseClickButton,exitAnyButton:!0,moveEverywhere:!0}),new Nt(e,this.blueprint,{clickButton:ne.mouseRightClickButton,exitAnyButton:!1,moveEverywhere:!0}),new At(e,this.blueprint),new kt(e,this.blueprint),new Et(e,this.blueprint)]}render(){return G`
Zoom ${0==this.blueprint.zoom?"1:1":(this.blueprint.zoom>0?"+":"")+this.blueprint.zoom}
`}firstUpdated(e){super.firstUpdated(e),this.headerElement=this.blueprint.querySelector(".ueb-viewport-header"),this.overlayElement=this.blueprint.querySelector(".ueb-viewport-overlay"),this.viewportElement=this.blueprint.querySelector(".ueb-viewport-body"),this.selectorElement=this.blueprint.querySelector("ueb-selector"),this.gridElement=this.viewportElement.querySelector(".ueb-grid"),this.linksContainerElement=this.blueprint.querySelector("[data-links]"),this.linksContainerElement.append(...this.blueprint.getLinks()),this.nodesContainerElement=this.blueprint.querySelector("[data-nodes]"),this.nodesContainerElement.append(...this.blueprint.getNodes()),this.viewportElement.scroll(ne.expandGridSize,ne.expandGridSize)}willUpdate(e){super.willUpdate(e),this.headerElement&&e.has("zoom")&&(this.headerElement.classList.add("ueb-zoom-changed"),this.headerElement.addEventListener("animationend",(()=>this.headerElement.classList.remove("ueb-zoom-changed"))))}updated(e){if(super.updated(e),(e.has("scrollX")||e.has("scrollY"))&&this.viewportElement.scroll(this.blueprint.scrollX,this.blueprint.scrollY),e.has("zoom")){this.blueprint.style.setProperty("--ueb-scale",this.blueprint.getScale());const t=e.get("zoom"),i=Math.min(t,this.blueprint.zoom),s=Math.max(t,this.blueprint.zoom),r=he.range(i,s),n=e=>`ueb-zoom-${e}`;te<0)).map(n)),this.blueprint.classList.add(...r.filter((e=>e>0)).map(n))):(this.blueprint.classList.remove(...r.filter((e=>e>0)).map(n)),this.blueprint.classList.add(...r.filter((e=>e<0)).map(n)))}}getCommentNodes(e=!1){return this.blueprint.querySelectorAll(`ueb-node[data-type="${ne.paths.comment}"]${e?'[data-selected="true"]':""}, ueb-node[data-type="${ne.paths.materialGraphNodeComment}"]${e?'[data-selected="true"]':""}`)}getPin(e){return this.blueprint.querySelector(`ueb-node[data-title="${e.objectName}"] ueb-pin[data-id="${e.pinGuid}"]`)}getCopyInputObject(){return this.#ce}getPasteInputObject(){return this.#ue}getZoomInputObject(){return this.#W}isPointVisible(e,t){return!1}gridTopVisibilityBoundary(){return this.blueprint.scaleCorrect(this.blueprint.scrollY)-this.blueprint.translateY}gridRightVisibilityBoundary(){return this.gridLeftVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[0])}gridBottomVisibilityBoundary(){return this.gridTopVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[1])}gridLeftVisibilityBoundary(){return this.blueprint.scaleCorrect(this.blueprint.scrollX)-this.blueprint.translateX}centerViewport(e=0,t=0,i=!0){const s=this.gridLeftVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[0]/2),r=this.gridTopVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[1]/2);this.blueprint.scrollDelta(this.blueprint.scaleCorrectReverse(e-s),this.blueprint.scaleCorrectReverse(t-r),i)}centerContentInViewport(e=!0){let t=0,i=0,s=Number.MAX_SAFE_INTEGER,r=Number.MIN_SAFE_INTEGER,n=Number.MAX_SAFE_INTEGER,a=Number.MIN_SAFE_INTEGER;const o=this.blueprint.getNodes();for(const e of o)t+=e.leftBoundary()+e.rightBoundary(),i+=e.topBoundary()+e.bottomBoundary(),s=Math.min(s,e.leftBoundary()),r=Math.max(r,e.rightBoundary()),n=Math.min(n,e.topBoundary()),a=Math.max(a,e.bottomBoundary());t=Math.round(r-s<=this.viewportSize[0]?(r+s)/2:t/(2*o.length)),i=Math.round(a-n<=this.viewportSize[1]?(a+n)/2:i/(2*o.length)),this.centerViewport(t,i,e)}}class It extends Ct{static properties={...super.properties,fromX:{type:Number,attribute:!1},fromY:{type:Number,attribute:!1},toX:{type:Number,attribute:!1},toY:{type:Number,attribute:!1}};constructor(){super(),this.fromX=0,this.fromY=0,this.toX=0,this.toY=0}setBothLocations([e,t]){this.fromX=e,this.fromY=t,this.toX=e,this.toY=t}addSourceLocation(e,t){this.fromX+=e,this.fromY+=t}addDestinationLocation(e,t){this.toX+=e,this.toY+=t}}class Bt extends ut{constructor(e={},t=void 0){e.Class=new Ve(ne.paths.knot),e.Name="K2Node_Knot";const i=new ot({PinName:"InputPin"},!0),s=new ot({PinName:"OutputPin",Direction:"EGPD_Output"},!0);t&&(i.copyTypeFrom(t),s.copyTypeFrom(t)),e.CustomProperties=[i,s],super(e,!0)}}class $t extends ft{static#V=e=>{};#X=e=>{if(this.blueprint.setFocused(!0),!this.enablerKey||this.enablerActivated)if(e.button===this.options.clickButton)this.options.strictTarget&&e.target!==e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),document.addEventListener("mouseup",this.#Q),this.setLocationFromEvent(e),this.clickedPosition[0]=this.location[0],this.clickedPosition[1]=this.location[1],this.blueprint.mousePosition[0]=this.location[0],this.blueprint.mousePosition[1]=this.location[1],this.clicked(this.clickedPosition));else this.options.exitAnyButton||this.#Q(e)};#Q=e=>{this.options.exitAnyButton&&e.button!=this.options.clickButton||(this.consumeEvent&&e.stopImmediatePropagation(),document.removeEventListener("mouseup",this.#Q),this.unclicked())};clickedPosition=[0,0];constructor(e,t,i={},s=$t.#V,r=$t.#V){i.clickButton??=ne.mouseClickButton,i.consumeEvent??=!0,i.exitAnyButton??=!0,i.strictTarget??=!1,super(e,t,i),this.onClick=s,this.onUnclick=r,this.listenEvents()}listenEvents(){this.target.addEventListener("mousedown",this.#X),this.options.clickButton===ne.mouseRightClickButton&&this.target.addEventListener("contextmenu",(e=>e.preventDefault()))}unlistenEvents(){this.target.removeEventListener("mousedown",this.#X)}clicked(e){this.onClick(this)}unclicked(e){this.onUnclick(this)}}class Ht extends ft{static ignoreDbClick=e=>{};#pe=e=>{this.options.strictTarget&&e.target!==e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),this.clickedPosition=this.setLocationFromEvent(e),this.blueprint.mousePosition=[...this.clickedPosition],this.dbclicked(this.clickedPosition))};#de;get onDbClick(){return this.#de}set onDbClick(e){this.#de=e}clickedPosition=[0,0];constructor(e,t,i={},s=Ht.ignoreDbClick){i.consumeEvent??=!0,i.strictTarget??=!1,super(e,t,i),this.#de=s,this.listenEvents()}listenEvents(){this.target.addEventListener("dblclick",this.#pe)}unlistenEvents(){this.target.removeEventListener("dblclick",this.#pe)}dbclicked(e){this.onDbClick(e)}}class Vt extends Lt{update(e){super.update(e);const[t,i,s,r]=[Math.round(this.element.fromX),Math.round(this.element.fromY),Math.round(this.element.toX),Math.round(this.element.toY)],[n,a,o,l]=[Math.min(t,s),Math.min(i,r),Math.abs(t-s),Math.abs(i-r)];(e.has("fromX")||e.has("toX"))&&(this.element.style.left=`${n}px`,this.element.style.width=`${o}px`),(e.has("fromY")||e.has("toY"))&&(this.element.style.top=`${a}px`,this.element.style.height=`${l}px`)}}class Gt extends Vt{static decreasingValue(e,t){const i=-e*t[0]**2,s=t[1]-i/t[0];return e=>i/e+s}static clampedLine(e,t){if(e[0]>t[0]){const i=e;e=t,t=i}const i=(t[1]-e[1])/(t[0]-e[0]),s=e[1]-i*e[0];return r=>rt[0]?t[1]:i*r+s}static c1DecreasingValue=Gt.decreasingValue(-.15,[100,15]);static c2DecreasingValue=Gt.decreasingValue(-.05,[500,130]);static c2Clamped=Gt.clampedLine([0,80],[200,40]);#me=`ueb-id-${Math.floor(1e12*Math.random())}`;#ge=e=>{const t=new Bt({},this.element.source.entity),i=gt.getConstructor("ueb-node").newObject(t);i.setLocation(...this.blueprint.snapToGrid(...e));const s=i.template;this.blueprint.addGraphElement(i);const r=this.element.getInputPin(),n=this.element.getOutputPin();this.element.source=null,this.element.destination=null;const a=gt.getConstructor("ueb-link").newObject(n,s.inputPin);this.blueprint.addGraphElement(a),this.element.source=s.outputPin,this.element.destination=r};createInputObjects(){const e=this.element.querySelector(".ueb-link-area");return[...super.createInputObjects(),new Ht(e,this.blueprint,void 0,(e=>{e[0]+=ne.knotOffset[0],e[1]+=ne.knotOffset[1],e=he.snapToGrid(e[0],e[1],ne.gridSize),this.#ge(e)})),new $t(e,this.blueprint,{enablerKey:new yt(this.blueprint,this.blueprint,{activationKeys:ae.enableLinkDelete})},(()=>this.blueprint.removeGraphElement(this.element)))]}willUpdate(e){super.willUpdate(e);const t=this.element.source,i=this.element.destination;if(e.has("fromX")||e.has("toX")){const e=this.element.fromX,s=this.element.toX,r=t?.nodeElement.getType()==ne.paths.knot,n=i?.nodeElement.getType()==ne.paths.knot;!r||i&&!n||(t?.isInput()&&s>e+ne.distanceThreshold?this.element.source=t.nodeElement.template.outputPin:t?.isOutput()&&se+ne.distanceThreshold&&(this.element.destination=i.nodeElement.template.inputPin))}const s=Math.max(Math.abs(this.element.fromX-this.element.toX),1),r=Math.max(Math.abs(this.element.fromY-this.element.toY),1),n=Math.max(s,ne.linkMinWidth),a=s/n,o=this.element.originatesFromInput?this.element.fromXthis.element.toY?1:0)),this.element.style.setProperty("--ueb-start-percentage",`${Math.round(this.element.startPercentage)}%`),this.element.style.setProperty("--ueb-link-start",`${Math.round(this.element.startPixels)}`)}render(){return G` ${this.element.linkMessageIcon||this.element.linkMessageText?G``:O}`}}class Dt extends It{static properties={...super.properties,dragging:{type:Boolean,attribute:"data-dragging",converter:he.booleanConverter,reflect:!0},originatesFromInput:{type:Boolean,attribute:!1},svgPathD:{type:String,attribute:!1},linkMessageIcon:{type:String,attribute:!1},linkMessageText:{type:String,attribute:!1}};#be;get source(){return this.#be}set source(e){this.#ve(e,!1)}#ye;get destination(){return this.#ye}set destination(e){this.#ve(e,!0)}#fe=()=>this.remove();#we=e=>this.addSourceLocation(...e.detail.value);#Se=e=>this.addDestinationLocation(...e.detail.value);#Ee=e=>this.setSourceLocation();#Ce=e=>this.setDestinationLocation();linkMessageIcon=O;linkMessageText=O;pathElement;constructor(){super(),this.dragging=!1,this.originatesFromInput=!1,this.startPercentage=0,this.svgPathD="",this.startPixels=0}static newObject(e,t){const i=new Dt;return i.initialize(e,t),i}initialize(e,t){super.initialize({},new Gt),e&&(this.source=e,t||(this.toX=this.fromX,this.toY=this.fromY)),t&&(this.destination=t,e||(this.fromX=this.toX,this.fromY=this.toY))}#ve(e,t){const i=()=>t?this.destination:this.source;if(i()!=e){if(i()){const e=i().getNodeElement();e.removeEventListener(ne.removeEventName,this.#fe),e.removeEventListener(ne.nodeDragEventName,t?this.#Se:this.#we),e.removeEventListener(ne.nodeReflowEventName,t?this.#Ce:this.#Ee),this.#xe()}if(t?this.#ye=e:this.#be=e,i()){const e=i().getNodeElement();e.addEventListener(ne.removeEventName,this.#fe),e.addEventListener(ne.nodeDragEventName,t?this.#Se:this.#we),e.addEventListener(ne.nodeReflowEventName,t?this.#Ce:this.#Ee),t?this.setDestinationLocation():(this.setSourceLocation(),this.originatesFromInput=this.source.isInput()),this.#Pe()}}}#Pe(){this.source&&this.destination&&(this.source.linkTo(this.destination),this.destination.linkTo(this.source))}#xe(){this.source&&this.destination&&(this.source.unlinkFrom(this.destination,!1),this.destination.unlinkFrom(this.source,!1))}cleanup(){super.cleanup(),this.#xe(),this.source=null,this.destination=null}setSourceLocation(e=null,t=!0){if(null==e){const i=this;if(t&&(!this.hasUpdated||!this.source.hasUpdated))return void Promise.all([this.updateComplete,this.source.updateComplete]).then((()=>i.setSourceLocation(null,!1)));e=this.source.template.getLinkLocation()}const[i,s]=e;this.fromX=i,this.fromY=s}setDestinationLocation(e=null,t=!0){if(null==e){const i=this;if(t&&(!this.hasUpdated||!this.destination.hasUpdated))return void Promise.all([this.updateComplete,this.destination.updateComplete]).then((()=>i.setDestinationLocation(null,!1)));e=this.destination.template.getLinkLocation()}this.toX=e[0],this.toY=e[1]}getInputPin(){return this.source?.isInput()?this.source:this.destination}setInputPin(e){this.source?.isInput()&&(this.source=e),this.destination=e}getOutputPin(){return this.destination?.isOutput()?this.destination:this.source}setOutputPin(e){this.destination?.isOutput()&&(this.destination=e),this.source=e}startDragging(){this.dragging=!0}finishDragging(){this.dragging=!1}removeMessage(){this.linkMessageIcon=O,this.linkMessageText=O}setMessageConvertType(){this.linkMessageIcon=Ae.convert,this.linkMessageText=G`Convert ${this.source.pinType} to ${this.destination.pinType}.`}setMessageCorrect(){this.linkMessageIcon=Ae.correct,this.linkMessageText=O}setMessageReplace(){this.linkMessageIcon=Ae.correct,this.linkMessageText=O}setMessageDirectionsIncompatible(){this.linkMessageIcon=Ae.reject,this.linkMessageText=G`Directions are not compatbile.`}setMessagePlaceNode(){this.linkMessageIcon=O,this.linkMessageText=G`Place a new node.`}setMessageReplaceLink(){this.linkMessageIcon=Ae.correct,this.linkMessageText=G`Replace existing input connections.`}setMessageReplaceOutputLink(){this.linkMessageIcon=Ae.correct,this.linkMessageText=G`Replace existing output connections.`}setMessageSameNode(){this.linkMessageIcon=Ae.reject,this.linkMessageText=G`Both are on the same node.`}setMessageTypesIncompatible(e,t){this.linkMessageIcon=Ae.reject,this.linkMessageText=G`${he.capitalFirstLetter(e.pinType)} is not compatible with ${he.capitalFirstLetter(t.pinType)}.`}}class Ot extends Pt{clicked(e){this.options.repositionOnClick&&(this.target.setLocation(...this.stepSize>1?he.snapToGrid(e[0],e[1],this.stepSize):e),this.clickedOffset=[0,0])}dragTo(e,t){const i=[this.target.locationX??this.lastLocation[0],this.target.locationY??this.lastLocation[1]],[s,r]=this.stepSize>1?[he.snapToGrid(e[0],e[1],this.stepSize),he.snapToGrid(i[0],i[1],this.stepSize)]:[e,i];0==(t=[s[0]-this.lastLocation[0],s[1]-this.lastLocation[1]])[0]&&0==t[1]||(t[0]+=r[0]-i[0],t[1]+=r[1]-i[1],this.dragAction(s,t),this.lastLocation=s)}dragAction(e,t){this.target.setLocation(e[0]-this.clickedOffset[0],e[1]-this.clickedOffset[1])}}class Rt extends Ot{#Ne;#ke;#Te;#Ae;constructor(e,t,i={}){super(e,t,i),i.onClicked&&(this.#Ne=i.onClicked),i.onStartDrag&&(this.#ke=i.onStartDrag),i.onDrag&&(this.#Te=i.onDrag),i.onEndDrag&&(this.#Ae=i.onEndDrag)}clicked(e){super.clicked(e),this.#Ne?.()}startDrag(){super.startDrag(),this.#ke?.()}dragAction(e,t){this.#Te?.(e,t)}endDrag(){super.endDrag(),this.#Ae?.()}}class zt extends Ot{constructor(e,t,i={}){super(e,t,i),this.draggableElement.classList.add("ueb-draggable")}startDrag(){this.target.selected||(this.blueprint.unselectAll(),this.target.setSelected(!0))}dragAction(e,t){this.target.acknowledgeDrag(t)}unclicked(){this.started?(this.blueprint.getNodes(!0).forEach((e=>e.boundComments.filter((t=>!e.isInsideComment(t))).forEach((t=>e.unbindFromComment(t))))),this.blueprint.getCommentNodes().forEach((e=>e.template.manageNodesBind()))):(this.blueprint.unselectAll(),this.target.setSelected(!0))}}class _t extends Lt{getDraggableElement(){return this.element}createDraggableObject(){const e=this.getDraggableElement();return new Ot(this.element,this.blueprint,{draggableElement:e})}createInputObjects(){return[...super.createInputObjects(),this.createDraggableObject(),new yt(this.element,this.blueprint,{activationKeys:[ne.Keys.ArrowUp,ne.Keys.ArrowRight,ne.Keys.ArrowDown,ne.Keys.ArrowLeft]},(e=>e.target.acknowledgeDrag([e.pressedKey===ne.Keys.ArrowLeft?-ne.gridSize:e.pressedKey===ne.Keys.ArrowRight?ne.gridSize:0,e.pressedKey===ne.Keys.ArrowUp?-ne.gridSize:e.pressedKey===ne.Keys.ArrowDown?ne.gridSize:0])))]}topBoundary(e=!1){return this.element.locationY}rightBoundary(e=!1){return this.element.locationX+this.element.sizeX}bottomBoundary(e=!1){return this.element.locationY+this.element.sizeY}leftBoundary(e=!1){return this.element.locationX}centerInViewport(){const e=Math.min(this.blueprint.template.viewportSize[0]/10,this.blueprint.template.viewportSize[1]/10),t=this.leftBoundary()-this.blueprint.template.gridLeftVisibilityBoundary(),i=this.blueprint.template.gridRightVisibilityBoundary()-this.rightBoundary();let s=Math.max((t+i)/2,e);const r=this.topBoundary()-this.blueprint.template.gridTopVisibilityBoundary(),n=this.blueprint.template.gridBottomVisibilityBoundary()-this.bottomBoundary();let a=Math.max((r+n)/2,e);this.blueprint.scrollDelta(t-s,r-a,!0)}}class Ft extends _t{update(e){super.update(e),e.has("locationX")&&(this.element.style.left=`${this.element.locationX}px`),e.has("locationY")&&(this.element.style.top=`${this.element.locationY}px`)}}class jt extends Ft{getDraggableElement(){return this.element}createDraggableObject(){return new zt(this.element,this.blueprint,{draggableElement:this.getDraggableElement(),scrollGraphEdge:!0})}firstUpdated(e){super.firstUpdated(e),this.element.selected&&!this.element.listeningDrag&&this.element.setSelected(!0)}}class Ut extends jt{static nodeStyleClasses=["ueb-node-style-default"];#Le=!1;pinInserter;inputContainer;outputContainer;pinElement;addPinHandler=()=>{const e=this.pinInserter?.();e&&(this.defaultPin&&this.defaultPin.isInput()===e.isInput()?this.defaultPin.before(this.createPinElement(e)):(e.isInput()?this.inputContainer:this.outputContainer).appendChild(this.createPinElement(e)),this.element.acknowledgeReflow())};toggleAdvancedDisplayHandler=()=>{this.element.toggleShowAdvancedPinDisplay(),this.element.requestUpdate(),this.element.updateComplete.then((()=>this.element.acknowledgeReflow()))};createPinElement(e){const t=gt.getConstructor("ueb-pin").newObject(e,void 0,this.element);return this.pinInserter&&!this.defaultPin&&"Default"===t.getPinName()&&(this.defaultPin=t,this.defaultPin.classList.add("ueb-node-variadic-default")),t}initialize(e){super.initialize(e),this.element.classList.add(...this.constructor.nodeStyleClasses),this.element.style.setProperty("--ueb-node-color",this.getColor().cssText),this.pinInserter=this.element.entity.additionalPinInserter(),this.pinInserter&&this.element.classList.add("ueb-node-is-variadic")}getColor(){return this.element.entity.nodeColor()}render(){return G`
${this.renderTop()}
${this.pinInserter?G`
Add pin ${Ae.plusCircle}
`:O} ${this.element.entity.isDevelopmentOnly()?G`
Development Only
`:O} ${this.element.advancedPinDisplay?G`
${Ae.expandIcon}
`:O}
`}renderNodeIcon(){return this.element.entity.nodeIcon()}renderNodeName(){return this.element.nodeDisplayName}renderTop(){const e=this.renderNodeIcon(),t=this.renderNodeName();return G`
${e?G`
${e}
`:O} ${t?G`
${t} ${this.#Le&&this.getTargetType().length>0?G`
Target is ${he.formatStringName(this.getTargetType())}
`:O}
`:O}
`}firstUpdated(e){super.firstUpdated(e),this.inputContainer=this.element.querySelector(".ueb-node-inputs"),this.outputContainer=this.element.querySelector(".ueb-node-outputs"),this.setupPins(),this.element.updateComplete.then((()=>this.element.acknowledgeReflow()))}setupPins(){this.element.nodeNameElement=this.element.querySelector(".ueb-node-name-text");let e=!1,t=!1;for(const i of this.element.getPinElements())i!==this.defaultPin&&(i.isInput()?(this.inputContainer.appendChild(i),e=!0):i.isOutput()&&(this.outputContainer.appendChild(i),t=!0));this.defaultPin&&(this.defaultPin.isInput()?this.inputContainer:this.outputContainer).appendChild(this.defaultPin),e&&this.element.classList.add("ueb-node-has-inputs"),t&&this.element.classList.add("ueb-node-has-outputs")}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden())).map((e=>(this.#Le=this.#Le||"self"===e.PinName&&"Target"===e.pinDisplayName(),this.createPinElement(e))))}getTargetType(){return this.element.entity.FunctionReference?.MemberParent?.getName()??"Untitled"}getPinElements(e){return e.querySelectorAll("ueb-pin")}linksChanged(){}}class Wt extends Ut{#Me=document.createElement("div");#Ie=document.createElement("div");#Be=document.createElement("div");#$e=document.createElement("div");#He=document.createElement("div");#Ve=document.createElement("div");#Ge=document.createElement("div");#De=document.createElement("div");initialize(e){super.initialize(e),this.element.classList.add("ueb-resizeable"),this.#Me.classList.add("ueb-resizeable-top"),this.#Ie.classList.add("ueb-resizeable-right"),this.#Be.classList.add("ueb-resizeable-bottom"),this.#$e.classList.add("ueb-resizeable-left"),this.#He.classList.add("ueb-resizeable-top-right"),this.#Ve.classList.add("ueb-resizeable-bottom-right"),this.#Ge.classList.add("ueb-resizeable-bottom-left"),this.#De.classList.add("ueb-resizeable-top-left")}update(e){super.update(e),this.element.sizeX>=0&&e.has("sizeX")&&(this.element.style.width=`${this.element.sizeX}px`),this.element.sizeY>=0&&e.has("sizeY")&&(this.element.style.height=`${this.element.sizeY}px`)}firstUpdated(e){super.firstUpdated(e),this.element.append(this.#Me,this.#Ie,this.#Be,this.#$e,this.#He,this.#Ve,this.#Ge,this.#De)}createInputObjects(){return[...super.createInputObjects(),new Rt(this.#Me,this.blueprint,{onDrag:(e,t)=>{t[1]=e[1]-this.element.topBoundary(),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()}),new Rt(this.#Ie,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),this.setSizeX(this.element.sizeX+t[0])},onEndDrag:()=>this.endResize()}),new Rt(this.#Be,this.blueprint,{onDrag:(e,t)=>{t[1]=e[1]-this.element.bottomBoundary(),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Rt(this.#$e,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1)},onEndDrag:()=>this.endResize()}),new Rt(this.#He,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),t[1]=e[1]-this.element.topBoundary(),this.setSizeX(this.element.sizeX+t[0]),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()}),new Rt(this.#Ve,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),t[1]=e[1]-this.element.bottomBoundary(),this.setSizeX(this.element.sizeX+t[0]),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Rt(this.#Ge,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),t[1]=e[1]-this.element.bottomBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Rt(this.#De,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),t[1]=e[1]-this.element.topBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()})]}setSizeX(e){return this.element.setNodeWidth(e),!0}setSizeY(e){return this.element.setNodeHeight(e),!0}endResize(){}}class Kt extends Wt{#Oe=0;initialize(e){super.initialize(e),e.classList.add("ueb-node-style-comment","ueb-node-resizeable"),e.sizeX=25*ne.gridSize,e.sizeY=6*ne.gridSize,super.initialize(e)}getDraggableElement(){return this.element.querySelector(".ueb-node-top")}render(){return G`
`}firstUpdated(e){super.firstUpdated(e);const t=this.getDraggableElement().getBoundingClientRect();this.#Oe=t.height}manageNodesBind(){let e=this.blueprint.getNodes();for(let t of e)t.topBoundary()>=this.element.topBoundary()&&t.rightBoundary()<=this.element.rightBoundary()&&t.bottomBoundary()<=this.element.bottomBoundary()&&t.leftBoundary()>=this.element.leftBoundary()?t.bindToComment(this.element):t.unbindFromComment(this.element)}setSizeX(e){return(e=Math.round(e))>=2*ne.gridSize&&(this.element.setNodeWidth(e),!0)}setSizeY(e){return(e=Math.round(e))>=2*ne.gridSize&&(this.element.setNodeHeight(e),!0)}endResize(){this.manageNodesBind()}topBoundary(e=!1){return this.element.locationY}rightBoundary(e=!1){return this.element.locationX+this.element.sizeX}bottomBoundary(e=!1){return e?this.element.locationY+this.#Oe:super.bottomBoundary()}leftBoundary(e=!1){return this.element.locationX}}class Yt extends Pt{#Re;#ze=null;#_e=e=>{if(!this.enteredPin){this.linkValid=!1,this.enteredPin=e.target;const t=this.link.source??this.target,i=this.enteredPin,s=t.isOutput()?t:i;t.nodeElement.getType()===ne.paths.knot||i.nodeElement.getType()===ne.paths.knot?(this.link.setMessageCorrect(),this.linkValid=!0):t.getNodeElement()===i.getNodeElement()?this.link.setMessageSameNode():t.isOutput()===i.isOutput()?this.link.setMessageDirectionsIncompatible():this.blueprint.getLinks(t,i).length?(this.link.setMessageReplaceLink(),this.linkValid=!0):"exec"===s.entity.getType()&&s.isLinked?(this.link.setMessageReplaceOutputLink(),this.linkValid=!0):"object"==t.entity.PinType.PinCategory&&"object"==i.entity.PinType.PinCategory||t.pinType==i.pinType?(this.link.setMessageCorrect(),this.linkValid=!0):(this.link.setMessageTypesIncompatible(t,i),this.linkValid=!1)}};#Fe=e=>{this.enteredPin==e.target&&(this.enteredPin=null,this.linkValid=!1,this.link?.setMessagePlaceNode())};link;enteredPin;linkValid=!1;constructor(e,t,i={}){i.scrollGraphEdge??=!0,super(e,t,i)}startDrag(e){this.target.nodeElement.getType()==ne.paths.knot&&(this.#ze=this.target),this.link=gt.getConstructor("ueb-link").newObject(this.target,null),this.blueprint.template.linksContainerElement.prepend(this.link),this.link.setMessagePlaceNode(),this.#Re=this.blueprint.querySelectorAll("ueb-pin"),this.#Re.forEach((e=>{e!=this.target&&(e.addEventListener("mouseenter",this.#_e),e.addEventListener("mouseleave",this.#Fe))})),this.link.startDragging(),this.link.setDestinationLocation(e)}dragTo(e,t){this.link.setDestinationLocation(e)}endDrag(){if(this.#Re.forEach((e=>{e.removeEventListener("mouseenter",this.#_e),e.removeEventListener("mouseleave",this.#Fe)})),this.#Re=null,this.enteredPin&&this.linkValid){if(this.#ze){const e=this.#ze!==this.link.source?this.link.source:this.enteredPin;if(this.#ze.isInput()&&e.isInput()||this.#ze.isOutput()&&e.isOutput()){const e=this.#ze.template.getOppositePin();this.#ze===this.link.source?this.link.source=e:this.enteredPin=e}}else this.enteredPin.nodeElement.getType()===ne.paths.knot&&(this.enteredPin=this.enteredPin.template.getOppositePin());this.link.source.getLinks().find((e=>e.equals(this.enteredPin.createPinReference())))?this.link.remove():(this.blueprint.addGraphElement(this.link),this.link.destination=this.enteredPin)}else this.link.remove();this.enteredPin=null,this.link.removeMessage(),this.link.finishDragging(),this.link=null}}class Xt extends Ut{#je=!1;#Ue=!1;#We="";static nodeStyleClasses=["ueb-node-style-glass"];initialize(e){super.initialize(e),this.#We=this.element.nodeDisplayName}render(){return G`
${this.#We?G`
${this.#We}
`:O} ${this.#je?G`
`:O} ${this.#Ue?G`
`:O} ${this.pinInserter?G`
Add pin ${Ae.plusCircle}
`:O}
`}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden())).map((e=>{this.#je||=e.isInput(),this.#Ue||=e.isOutput();return gt.getConstructor("ueb-pin").newObject(e,void 0,this.element)}))}}class Zt extends Xt{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-conversion"]}class qt extends Xt{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-operation"]}class Qt extends Lt{static canWrapInput=!0;#Ke;get iconElement(){return this.#Ke}#Ye;get wrapperElement(){return this.#Ye}isNameRendered=!0;initialize(e){if(super.initialize(e),this.element.nodeElement){const e=this.element.nodeElement.template;this.isNameRendered=!(e instanceof Zt||e instanceof qt)}}setup(){super.setup(),this.element.nodeElement=this.element.closest("ueb-node");const e=this.element.nodeElement.template;(e instanceof Zt||e instanceof qt)&&(this.isNameRendered=!1,this.element.requestUpdate())}createInputObjects(){return[new Yt(this.element,this.blueprint,{moveEverywhere:!0,draggableElement:this.getClickableElement()})]}render(){const e=G`
${this.renderIcon()}
`,t=G`
${this.isNameRendered?this.renderName():O} ${this.isInputRendered()?this.renderInput():G``}
`;return G`
${this.element.isInput()?G`${e}${t}`:G`${t}${e}`}
`}renderIcon(){if(this.element.nodeElement.entity.isPcg())switch(this.element.entity.getType()){case"Any":return Ae.pcgPin;case"Param":case"Param[]":return Ae.pcgParamPin;case"Spatial":case"Spatial[]":return Ae.pcgSpatialPin;case"Any[]":case"Point[]":case"Surface[]":case"Volume[]":if(this.element.isOutput())return Ae.pcgPin;case"Point":case"Surface":case"Volume":return Ae.pcgStackPin}switch(this.element.entity.PinType?.ContainerType?.toString()){case"Array":return Ae.arrayPin;case"Set":return Ae.setPin;case"Map":return Ae.mapPin}return"delegate"===this.element.entity.PinType?.PinCategory?.toLocaleLowerCase()?Ae.delegate:this.element.nodeElement?.template instanceof qt?Ae.operationPin:Ae.genericPin}renderName(){let e=this.element.getPinDisplayName();const t=this.element.nodeElement,i=this.element.getPinName();return t.getType()==ne.paths.makeStruct&&i==t.entity.StructType.getName()&&(e=i),G`${e}`}isInputRendered(){return this.element.isInput()&&!this.element.entity.bDefaultValueIsIgnored&&!this.element.entity.PinType.bIsReference}renderInput(){return G``}updated(e){if(super.updated(e),this.element.isInput()&&e.has("isLinked")){const e=this.element.nodeElement;this.element.requestUpdate(),this.element.updateComplete.then((()=>e.acknowledgeReflow()))}}firstUpdated(e){super.firstUpdated(e),this.element.style.setProperty("--ueb-pin-color-rgb",this.element.entity.pinColor().cssText),this.#Ke=this.element.querySelector(".ueb-pin-icon svg")??this.element,this.#Ye=this.element.querySelector(".ueb-pin-wrapper")}getLinkLocation(){const e=this.iconElement.getBoundingClientRect(),t=[this.element.isInput()?e.left:e.right+1,(e.top+e.bottom)/2],i=he.convertLocation(t,this.blueprint.template.gridElement);return this.blueprint.compensateTranslation(i[0],i[1])}getClickableElement(){return this.#Ye??this.element}}class Jt extends Qt{render(){return G`
${this.renderIcon()}
`}}class ei extends Ut{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-event"];firstUpdated(e){super.firstUpdated(e),this.element.querySelector(".ueb-node-top").appendChild(this.createDelegatePinElement())}renderTop(){const e=this.renderNodeIcon(),t=this.renderNodeName(),i=this.element.getType()===ne.paths.customEvent&&(this.element.entity.CustomFunctionName||this.element.entity.FunctionReference.MemberParent);return G`
${e?G`
${e}
`:O} ${t?G`
${t} ${i?G`
Custom Event
`:O}
`:O}
`}createDelegatePinElement(){const e=gt.getConstructor("ueb-pin").newObject(this.element.getPinEntities().find((e=>!e.isHidden()&&"delegate"===e.PinType.PinCategory)),new Jt,this.element);return e.template.isNameRendered=!1,e}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden()&&"delegate"!==e.PinType.PinCategory)).map((e=>gt.getConstructor("ueb-pin").newObject(e,void 0,this.element)))}}class ti extends Jt{render(){return this.element.isOutput()?super.render():G``}getOppositePin(){const e=this.element.nodeElement.template;return this.element.isOutput()?e.inputPin:e.outputPin}getLinkLocation(){const e=(this.element.isInput()?this.element.nodeElement.template.outputPin.template:this).iconElement.getBoundingClientRect(),t=[this.element.isInput()?e.left:e.right+1,(e.top+e.bottom)/2],i=he.convertLocation(t,this.blueprint.template.gridElement);return this.blueprint.compensateTranslation(i[0],i[1])}}class ii extends Ut{static#Xe=new Set;#Ze=null;#qe;get inputPin(){return this.#qe}#Qe;get outputPin(){return this.#Qe}initialize(e){super.initialize(e),this.element.classList.add("ueb-node-style-minimal")}findDirectionaPin(e){if(e.nodeElement.getType()!==ne.paths.knot||ii.#Xe.has(e))return ii.#Xe.clear(),!0;ii.#Xe.add(e);for(let t of e.getLinks().map((e=>this.blueprint.getPin(e))))if(this.findDirectionaPin(t))return!0;return!1}render(){return G`
`}setupPins(){this.element.getPinElements().forEach((e=>this.element.querySelector(".ueb-node-border").appendChild(e)))}getPinElements(e){return e.querySelectorAll("ueb-pin")}createPinElements(){const e=this.element.getPinEntities().filter((e=>!e.isHidden())),t=e[e[0].isInput()?0:1],i=e[e[0].isOutput()?0:1],s=gt.getConstructor("ueb-pin");return[this.#qe=s.newObject(t,new ti,this.element),this.#Qe=s.newObject(i,new ti,this.element)]}linksChanged(){}}class si extends Xt{initialize(e){super.initialize(e),e.getType()===ne.paths.variableGet?this.element.classList.add("ueb-node-style-getter"):e.getType()===ne.paths.variableSet&&this.element.classList.add("ueb-node-style-setter")}setupPins(){super.setupPins();let e=this.element.getPinElements().find((e=>!e.entity.isHidden()&&!e.entity.isExecution()));this.element.style.setProperty("--ueb-node-color",e.getColor().cssText)}}class ri extends xt{static properties={...super.properties,selected:{type:Boolean,attribute:"data-selected",reflect:!0,converter:he.booleanConverter}};dragHandler=e=>this.addLocation(...e.detail.value);constructor(){super(),this.selected=!1,this.listeningDrag=!1}setup(){super.setup(),this.setSelected(this.selected)}cleanup(){super.cleanup(),this.blueprint.removeEventListener(ne.nodeDragGeneralEventName,this.dragHandler)}setSelected(e=!0){this.selected=e,this.blueprint&&(this.selected?(this.listeningDrag=!0,this.blueprint.addEventListener(ne.nodeDragGeneralEventName,this.dragHandler)):(this.blueprint.removeEventListener(ne.nodeDragGeneralEventName,this.dragHandler),this.listeningDrag=!1))}}class ni extends ri{static properties={...ri.properties,typePath:{type:String,attribute:"data-type",reflect:!0},nodeTitle:{type:String,attribute:"data-title",reflect:!0},advancedPinDisplay:{type:String,attribute:"data-advanced-display",converter:De.attributeConverter,reflect:!0},enabledState:{type:String,attribute:"data-enabled-state",reflect:!0},nodeDisplayName:{type:String,attribute:!1},pureFunction:{type:Boolean,converter:he.booleanConverter,attribute:"data-pure-function",reflect:!0}};static dragEventName=ne.nodeDragEventName;static dragGeneralEventName=ne.nodeDragGeneralEventName;get blueprint(){return super.blueprint}set blueprint(e){super.blueprint=e,this.#Je.forEach((t=>t.blueprint=e))}#et;get nodeNameElement(){return this.#et}set nodeNameElement(e){this.#et=e}#Je=[];boundComments=[];#tt=!1;#it=e=>{this.selected||this.#tt||(this.#tt=!0,this.requestUpdate(),this.updateComplete.then((()=>this.#tt=!1)),this.addLocation(...e.detail.value))};static getTypeTemplate(e){if(e.getClass()===ne.paths.callFunction||e.getClass()===ne.paths.commutativeAssociativeBinaryOperator||e.getClass()===ne.paths.callArrayFunction){const t=e.FunctionReference?.MemberParent?.path??"",i=e.FunctionReference?.MemberName;if(i&&(t===ne.paths.kismetMathLibrary||t===ne.paths.kismetArrayLibrary)){if(i.startsWith("Conv_"))return Zt;if(i.startsWith("And_")||i.startsWith("Boolean")||i.startsWith("Cross_")||i.startsWith("Dot_")||i.startsWith("Not_")||i.startsWith("Or_")||i.startsWith("Percent_")||i.startsWith("Xor_"))return qt;switch(i){case"Abs":case"Array_Add":case"Array_AddUnique":case"Array_Identical":case"BMax":case"BMin":case"CrossProduct2D":case"DotProduct2D":case"Exp":case"FMax":case"FMin":case"GetPI":case"Max":case"MaxInt64":case"Min":case"MinInt64":case"Sqrt":case"Square":case"Vector4_CrossProduct3":case"Vector4_DotProduct":case"Vector4_DotProduct3":case"Acos":case"Asin":case"Cos":case"DegAcos":case"DegCos":case"DegSin":case"DegTan":case"Sin":case"Tan":return qt}}if(t===ne.paths.blueprintSetLibrary)return qt;if(t===ne.paths.blueprintMapLibrary)return qt}switch(e.getClass()){case ne.paths.comment:case ne.paths.materialGraphNodeComment:return Kt;case ne.paths.createDelegate:return Ut;case ne.paths.promotableOperator:return qt;case ne.paths.knot:return ii;case ne.paths.literal:case ne.paths.variableGet:case ne.paths.variableSet:return si}return e.isEvent()?ei:Ut}static fromSerializedObject(e){e=e.trim();let t=Me.getSerializer(ut).read(e);return ni.newObject(t)}static newObject(e=new ut,t=new(ni.getTypeTemplate(e))){const i=new ni;return i.initialize(e,t),i}initialize(e=new ut,t=new(ni.getTypeTemplate(e))){this.typePath=e.getType(),this.nodeTitle=e.getObjectName(),this.advancedPinDisplay=e.AdvancedPinDisplay?.toString(),this.enabledState=e.EnabledState,this.nodeDisplayName=e.nodeDisplayName(),this.pureFunction=e.bIsPureFunc,this.dragLinkObjects=[],super.initialize(e,t),this.#Je=this.template.createPinElements(),super.setLocation(this.entity.getNodePosX(),this.entity.getNodePosY()),this.entity.NodeWidth&&this.entity.NodeHeight?(this.sizeX=this.entity.NodeWidth.value,this.sizeY=this.entity.NodeHeight.value):this.updateComplete.then((()=>this.computeSizes()))}async getUpdateComplete(){let e=await super.getUpdateComplete();for(const t of this.getPinElements())e&&=await t.updateComplete;return e}bindToComment(e){e==this||this.boundComments.includes(e)||(e.addEventListener(ne.nodeDragEventName,this.#it),this.boundComments.push(e))}unbindFromComment(e){const t=this.boundComments.indexOf(e);t>=0&&(e.removeEventListener(ne.nodeDragEventName,this.#it),this.boundComments[t]=this.boundComments[this.boundComments.length-1],this.boundComments.pop())}isInsideComment(e){return this.topBoundary()>=e.topBoundary()&&this.rightBoundary()<=e.rightBoundary()&&this.bottomBoundary()<=e.bottomBoundary()&&this.leftBoundary()>=e.leftBoundary()}getType(){return this.entity.getType()}getNodeName(){return this.entity.getObjectName()}computeNodeDisplayName(){this.nodeDisplayName=this.entity.nodeDisplayName()}setNodeWidth(e){this.entity.setNodeWidth(e),this.sizeX=e,this.acknowledgeReflow()}setNodeHeight(e){this.entity.setNodeHeight(e),this.sizeY=e,this.acknowledgeReflow()}sanitizeLinks(e=[]){this.getPinElements().forEach((t=>t.sanitizeLinks(e)))}rename(e){if(this.entity.Name==e)return!1;for(let t of this.getPinElements())for(let i of t.getLinks())this.blueprint.getPin(i).redirectLink(t,new Qe({objectName:e,pinGuid:t.entity.PinId}));this.entity.Name=e,this.nodeTitle=this.entity.Name}getPinElements(){return this.#Je}getPinEntities(){return this.entity.getPinEntities()}setLocation(e=0,t=0,i=!0){this.entity.setNodePosX(e),this.entity.setNodePosY(t),super.setLocation(e,t,i)}acknowledgeReflow(){this.requestUpdate(),this.updateComplete.then((()=>this.computeSizes()));let e=new CustomEvent(ne.nodeReflowEventName);this.dispatchEvent(e)}setShowAdvancedPinDisplay(e){this.entity.AdvancedPinDisplay=new De(e?"Shown":"Hidden"),this.advancedPinDisplay=this.entity.AdvancedPinDisplay}toggleShowAdvancedPinDisplay(){this.setShowAdvancedPinDisplay("Shown"!=this.entity.AdvancedPinDisplay?.toString())}}class ai extends Ct{static properties={selecting:{type:Boolean,attribute:"data-selecting",reflect:!0,converter:he.booleanConverter},scrolling:{type:Boolean,attribute:"data-scrolling",reflect:!0,converter:he.booleanConverter},focused:{type:Boolean,attribute:"data-focused",reflect:!0,converter:he.booleanConverter},zoom:{type:Number,attribute:"data-zoom",reflect:!0},scrollX:{type:Number,attribute:!1},scrollY:{type:Number,attribute:!1},additionalX:{type:Number,attribute:!1},additionalY:{type:Number,attribute:!1},translateX:{type:Number,attribute:!1},translateY:{type:Number,attribute:!1}};static nodeBoundariesSupplier=e=>({primaryInf:e.leftBoundary(!0),primarySup:e.rightBoundary(!0),secondaryInf:e.topBoundary(!0),secondarySup:e.bottomBoundary(!0)});static nodeSelectToggleFunction=(e,t)=>{e.setSelected(t)};#st=new Map;#rt=0;#nt=0;nodes=[];links=[];nodesNames=new Map;mousePosition=[0,0];waitingExpandUpdate=!1;constructor(){super(),this.selecting=!1,this.scrolling=!1,this.focused=!1,this.zoom=0,this.scrollX=ne.expandGridSize,this.scrollY=ne.expandGridSize,this.translateX=ne.expandGridSize,this.translateY=ne.expandGridSize,super.initialize({},new Mt)}initialize(){}getGridDOMElement(){return this.template.gridElement}getScroll(){return[this.scrollX,this.scrollY]}setScroll(e,t){this.scrollX=e,this.scrollY=t}scrollDelta(e=0,t=0,i=!1,s=ne.smoothScrollTime){if(i){let i=[0,0];this.#rt&&cancelAnimationFrame(this.#rt),this.#nt&&cancelAnimationFrame(this.#nt),he.animate(0,e,s,(e=>{this.scrollDelta(e-i[0],0,!1),i[0]=e}),(e=>this.#rt=e)),he.animate(0,t,s,(e=>{this.scrollDelta(0,e-i[1],!1),i[1]=e}),(e=>this.#nt=e))}else{const i=[2*ne.expandGridSize,2*ne.expandGridSize];let s=this.getScroll(),r=[s[0]+e,s[1]+t],n=[0,0];for(let e=0;e<2;++e)r[e]i[e]-ne.gridExpandThreshold*ne.expandGridSize&&(n[e]=1);0==n[0]&&0==n[1]||this.seamlessExpand(n[0],n[1]),s=this.getScroll(),r=[s[0]+e,s[1]+t],this.setScroll(r[0],r[1])}}scrollCenter(e=!1){const t=this.getScroll(),i=[this.translateX-t[0],this.translateY-t[1]],s=this.getViewportSize().map((e=>e/2)),r=[i[0]-s[0],i[1]-s[1]];this.scrollDelta(r[0],r[1],e)}getViewportSize(){return[this.template.viewportElement.clientWidth,this.template.viewportElement.clientHeight]}getScrollMax(){return[this.template.viewportElement.scrollWidth-this.template.viewportElement.clientWidth,this.template.viewportElement.scrollHeight-this.template.viewportElement.clientHeight]}snapToGrid(e,t){return he.snapToGrid(e,t,ne.gridSize)}seamlessExpand(e,t){e=Math.round(e),t=Math.round(t);let i=this.getScale();[e,t]=[-e*ne.expandGridSize,-t*ne.expandGridSize],0!=e&&(this.scrollX+=e,e/=i),0!=t&&(this.scrollY+=t,t/=i),this.translateX+=e,this.translateY+=t}progressiveSnapToGrid(e){return ne.expandGridSize*Math.round(e/ne.expandGridSize+.5*Math.sign(e))}getZoom(){return this.zoom}setZoom(e,t){if((e=he.clamp(e,ne.minZoom,ne.maxZoom))==this.zoom)return;let i=this.getScale();if(this.zoom=e,t){t[0]+=this.translateX,t[1]+=this.translateY;let e=this.getScale()/i,s=[e*t[0],e*t[1]];this.scrollDelta((s[0]-t[0])*i,(s[1]-t[1])*i)}}getScale(){return ne.scale[this.getZoom()]}scaleCorrect(e){return e/this.getScale()}scaleCorrectReverse(e){return e*this.getScale()}compensateTranslation(e,t){return[e-=this.translateX,t-=this.translateY]}getNodes(e=!1,[t,i,s,r]=[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER]){let n=this.nodes;return e&&(n=n.filter((e=>e.selected))),(t>Number.MIN_SAFE_INTEGER||iNumber.MIN_SAFE_INTEGER)&&(n=n.filter((e=>e.topBoundary()>=t&&e.rightBoundary()<=i&&e.bottomBoundary()<=s&&e.leftBoundary()>=r))),n}getCommentNodes(e=!1){let t=[...this.template.getCommentNodes(e)];return 0===t.length&&(t=this.nodes.filter((t=>t.getType()===ne.paths.comment&&(!e||t.selected)))),t}getPin(e){let t=this.template.getPin(e);return t&&t.nodeElement.getNodeName()==e.objectName.toString()||(t=[...this.nodes.find((t=>e.objectName.toString()==t.getNodeName()))?.getPinElements()??[]].find((t=>e.pinGuid.toString()==t.getPinId().toString()))),t}getLinks(e=null,t=null){if(null==e!=(null==t)){const i=e??t;return this.links.filter((e=>e.source==i||e.destination==i))}return null!=e&&null!=t?this.links.filter((i=>i.source==e&&i.destination==t||i.source==t&&i.destination==e)):this.links}getLink(e,t,i=!1){return this.links.find((s=>s.source==e&&s.destination==t||!i&&s.source==t&&s.destination==e))}selectAll(){this.getNodes().forEach((e=>ai.nodeSelectToggleFunction(e,!0)))}unselectAll(){this.getNodes().forEach((e=>ai.nodeSelectToggleFunction(e,!1)))}addGraphElement(...e){const t=e=>{const i=e.currentTarget;i.removeEventListener(ne.removeEventName,t);const s=i instanceof ni?this.nodes:i instanceof Dt?this.links:null,r=s?.indexOf(i);if(r>=0){const e=s.pop();rt.entity.getObjectName()==e));if(s){let e=s.entity.getObjectName(!0);this.#st[e]=this.#st[e]??-1;do{++this.#st[e]}while(this.nodes.find((t=>t.entity.getObjectName()==ne.nodeTitle(e,this.#st[e]))));s.rename(ne.nodeTitle(e,this.#st[e]))}this.nodes.push(i),i.addEventListener(ne.removeEventName,t),this.template.nodesContainerElement?.appendChild(i)}else i instanceof Dt&&!this.links.includes(i)&&(this.links.push(i),i.addEventListener(ne.removeEventName,t),this.template.linksContainerElement&&!this.template.linksContainerElement.contains(i)&&this.template.linksContainerElement.appendChild(i));e.filter((e=>e instanceof ni)).forEach((t=>t.sanitizeLinks(e))),e.filter((e=>e instanceof ni&&e.getType()==ne.paths.comment)).forEach((e=>e.updateComplete.then((()=>e.template.manageNodesBind()))))}removeGraphElement(...e){for(let t of e){if(t.closest("ueb-blueprint")!==this)return;t.remove()}}setFocused(e=!0){if(this.focused==e)return;let t=new CustomEvent(e?ne.focusEventName.begin:ne.focusEventName.end);this.focused=e,this.focused||this.unselectAll(),this.dispatchEvent(t)}acknowledgeEditText(e){const t=new CustomEvent(e?ne.editTextEventName.begin:ne.editTextEventName.end);this.dispatchEvent(t)}}customElements.define("ueb-blueprint",ai);class oi extends _t{#at;get locationChangeCallback(){return this.#at}set locationChangeCallback(e){this.#at=e}movementSpace;movementSpaceSize=[0,0];firstUpdated(e){super.firstUpdated(e),this.movementSpace=this.element.parentElement}setup(){super.setup();const e=this.movementSpace.getBoundingClientRect();this.movementSpaceSize=[e.width,e.height]}createDraggableObject(){return new Ot(this.element,this.blueprint,{draggableElement:this.movementSpace,ignoreTranslateCompensate:!0,moveEverywhere:!0,movementSpace:this.movementSpace,repositionOnClick:!0,stepSize:1})}adjustLocation(e,t){return this.locationChangeCallback?.(e,t),[e,t]}}class li extends oi{adjustLocation(e,t){const i=Math.round(this.movementSpaceSize[0]/2);e-=i,t=-(t-i);let[s,r]=he.getPolarCoordinates(e,t);return s=Math.min(s,i),[e,t]=he.getCartesianCoordinates(s,r),this.locationChangeCallback?.(e/i,t/i),[e=Math.round(e+i),t=Math.round(-t+i)]}}class ci extends xt{windowElement;setup(){super.setup(),this.windowElement=this.closest("ueb-window")}setLocation(e,t){super.setLocation(...this.template.adjustLocation(e,t))}}class ui extends ci{constructor(){super(),super.initialize({},new li)}static newObject(){return new ui}initialize(){}}class hi extends oi{adjustLocation(e,t){return e=he.clamp(e,0,this.movementSpaceSize[0]),t=he.clamp(t,0,this.movementSpaceSize[1]),this.locationChangeCallback?.(e/this.movementSpaceSize[0],1-t/this.movementSpaceSize[1]),[e,t]}}class pi extends ci{constructor(){super(),super.initialize({},new hi)}static newObject(){return new pi}initialize(){}}class di extends Pt{constructor(e,t,i={}){i.consumeEvent=!0,super(e,t,i)}}class mi extends Lt{#ot;#lt;#ct=e=>this.element.selectedOption=e.target.selectedOptions[0].value;render(){return G` `}firstUpdated(e){super.firstUpdated(e),this.#ot=this.element.querySelector("select:first-child"),this.#lt=this.element.querySelector("select:last-child");const t=new Event("input",{bubbles:!0});this.#ot.dispatchEvent(t)}updated(e){super.updated(e);const t=this.#lt.getBoundingClientRect();this.element.style.setProperty("--ueb-dropdown-width",t.width+"px")}createInputObjects(){return[...super.createInputObjects(),new di(this.element,this.blueprint)]}setSelectedValue(e){this.element.querySelector(`option[value="${e}"]`).defaultSelected=!0}getSelectedValue(){return this.#ot.value}}class gi extends Ct{static properties={...super.properties,options:{type:Object},selectedOption:{type:String}};constructor(){super(),super.initialize({},new mi),this.options=[],this.selectedOption=""}static newObject(e){return new gi}initialize(){}getValue(){return this.template.getSelectedValue()}}class bi extends Lt{#ut=()=>{this.blueprint.acknowledgeEditText(!0),this.element.selectOnFocus&&getSelection().selectAllChildren(this.element)};#ht=()=>{this.blueprint.acknowledgeEditText(!1),getSelection().removeAllRanges()};#pt=e=>e.target.querySelectorAll("br").forEach((e=>e.remove()));#dt=e=>{"Enter"!=e.code||e.shiftKey||e.target.blur()};initialize(e){super.initialize(e),this.element.classList.add("ueb-pin-input-content"),this.element.setAttribute("role","textbox"),this.element.contentEditable="true"}firstUpdated(e){super.firstUpdated(e);const t=new Event("input",{bubbles:!0});this.element.dispatchEvent(t)}createInputObjects(){return[...super.createInputObjects(),new di(this.element,this.blueprint)]}setup(){super.setup(),this.element.addEventListener("focus",this.#ut),this.element.addEventListener("focusout",this.#ht),this.element.singleLine&&this.element.addEventListener("input",this.#pt),this.element.blurOnEnter&&this.element.addEventListener("keydown",this.#dt)}cleanup(){super.cleanup(),this.element.removeEventListener("focus",this.#ut),this.element.removeEventListener("focusout",this.#ht),this.element.removeEventListener("input",this.#pt),this.element.removeEventListener("keydown",this.#dt)}}class vi extends Ct{static properties={...super.properties,singleLine:{type:Boolean,attribute:"data-single-line",converter:he.booleanConverter,reflect:!0},selectOnFocus:{type:Boolean,attribute:"data-select-focus",converter:he.booleanConverter,reflect:!0},blurOnEnter:{type:Boolean,attribute:"data-blur-enter",converter:he.booleanConverter,reflect:!0}};constructor(){super(),this.singleLine=!1,this.selectOnFocus=!0,this.blurOnEnter=!0,super.initialize({},new bi)}static newObject(){return new vi}initialize(){}}class yi extends Qt{#mt;#gt=()=>this.element.setDefaultValue(this.#mt.checked);firstUpdated(e){super.firstUpdated(e),this.#mt=this.element.querySelector(".ueb-pin-input")}setup(){super.setup(),this.#mt?.addEventListener("change",this.#gt)}cleanup(){super.cleanup(),this.#mt?.removeEventListener("change",this.#gt)}createInputObjects(){return[...super.createInputObjects(),new di(this.#mt,this.blueprint)]}renderInput(){return G``}}class fi extends Qt{static singleLineInput=!1;static selectOnFocus=!0;static saveEachInputChange=!1;#bt;get inputWrapper(){return this.#bt}#vt;static stringFromInputToUE(e){return e.replace(/(?=\n\s*)\n$/,"")}static stringFromUEToInput(e){return e.replaceAll(/(?:\r|(?<=(?:^|[^\\])(?:\\\\)*)\\r)(?=\n)/g,"").replace(/(?<=\n\s*)$/,"\n")}#yt=()=>this.setInputs(this.getInputs(),!0);#ft=e=>this.#wt(e.target);#wt(e){const t=this.blueprint.scaleCorrect(this.#bt.getBoundingClientRect().width)+this.nameWidth,i=this.element.classList.contains("ueb-pin-input-wrap");!i&&t>ne.pinInputWrapWidth?this.element.classList.add("ueb-pin-input-wrap"):i&&t<=ne.pinInputWrapWidth&&this.element.classList.remove("ueb-pin-input-wrap")}firstUpdated(e){super.firstUpdated(e);this.constructor.canWrapInput&&this.isInputRendered()&&(this.element.addEventListener("input",this.#ft),this.nameWidth=this.blueprint.scaleCorrect(this.element.querySelector(".ueb-pin-name")?.getBoundingClientRect().width??0)),this.#bt=this.element.querySelector(".ueb-pin-input-wrapper"),this.#vt=[...this.element.querySelectorAll("ueb-input")]}setup(){super.setup();const e=this.constructor;e.saveEachInputChange?this.element.addEventListener("input",this.#yt):this.element.addEventListener("focusout",this.#yt),e.canWrapInput&&this.isInputRendered()&&(this.element.addEventListener("input",this.#ft),this.element.nodeElement.addEventListener(ne.nodeReflowEventName,this.#ft))}cleanup(){super.cleanup(),this.element.nodeElement.removeEventListener(ne.nodeReflowEventName,this.#ft),this.element.removeEventListener("input",this.#ft),this.element.removeEventListener("input",this.#yt),this.element.removeEventListener("focusout",this.#yt)}getInput(){return this.getInputs().reduce(((e,t)=>e+t),"")}getInputs(){return this.#vt.map((e=>he.clearHTMLWhitespace(e.innerHTML)))}setInputs(e=[],t=!0){this.#vt.forEach(this.constructor.singleLineInput?(t,i)=>t.innerText=e[i]:(t,i)=>t.innerText=e[i].replaceAll("\n","")),t&&this.setDefaultValue(e.map((e=>fi.stringFromInputToUE(e))),e),this.element.requestUpdate(),this.element.nodeElement.acknowledgeReflow()}setDefaultValue(e=[],t=e){this.element.setDefaultValue(e.join(""))}renderInput(){const e=this.constructor,t=e.singleLineInput,i=e.selectOnFocus;return G`
`}}class wi extends fi{static saveEachInputChange=!0;#St;#Et=[];setup(){super.setup();const e=this.element.nodeElement.entity.EnumEntries;this.#Et=e?.map((e=>(""===e&&(e="None"),[e,this.element.nodeElement.getPinEntities().find((t=>e===t.PinName))?.PinFriendlyName.toString()??e])))??ne.CommonEnums[this.element.entity.getSubCategory()]?.map((e=>e instanceof Array?e:[e,he.formatStringName(e)]))??[];const t=this.element.getDefaultValue().toString();this.#Et.find((([e,i])=>e===t))||this.#Et.push([t,he.formatStringName(t)]),this.element.requestUpdate()}renderInput(){return this.element.nodeElement.entity,G``}firstUpdated(e){super.firstUpdated(e),this.#St=this.element.querySelector("ueb-dropdown")}getInputs(){return[this.#St.getValue()]}}class Si extends Qt{renderIcon(){return Ae.execPin}renderName(){let e=this.element.entity.PinName;if(this.element.entity.PinFriendlyName)e=this.element.entity.PinFriendlyName.toString();else if("execute"===e||"then"===e)return G``;return G`${this.element.getPinDisplayName()}`}}class Ei extends fi{static singleLineInput=!0;setInputs(e=[],t=!1){if(e&&0!=e.length||(e=[this.getInput()]),super.setInputs(e,!1),t){let i=[];for(const s of e){let e=parseFloat(s);isNaN(e)&&(e=0,t=!1),i.push(e)}this.setDefaultValue(i,e)}}setDefaultValue(e=[],t){this.element.setDefaultValue(e[0]),this.element.requestUpdate()}}class Ci extends Ei{setDefaultValue(e=[],t){this.element.setDefaultValue(new Ze(e[0])),this.element.requestUpdate()}renderInput(){return G`
`}}class xi extends Ei{setDefaultValue(e=[],t){this.element.setDefaultValue(new Oe(e[0])),this.element.requestUpdate()}renderInput(){return G`
`}} +var te,ie;class re extends b{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var e,t;const i=super.createRenderRoot();return null!==(e=(t=this.renderOptions).renderBefore)&&void 0!==e||(t.renderBefore=i.firstChild),i}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=((e,t,i)=>{var r,s;const n=null!==(r=null==i?void 0:i.renderBefore)&&void 0!==r?r:t;let a=n._$litPart$;if(void 0===a){const e=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:null;n._$litPart$=a=new K(t.insertBefore(P(),e),e,void 0,null!=i?i:{})}return a._$AI(e),a})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!1)}render(){return D}}re.finalized=!0,re._$litElement$=!0,null===(te=globalThis.litElementHydrateSupport)||void 0===te||te.call(globalThis,{LitElement:re});const se=globalThis.litElementPolyfillSupport;null==se||se({LitElement:re}),(null!==(ie=globalThis.litElementVersions)&&void 0!==ie?ie:globalThis.litElementVersions=[]).push("3.3.3");class ne{static nodeColors={black:n``,blue:n``,darkBlue:n``,darkerBlue:n``,darkTurquoise:n``,gray:n``,green:n``,intenseGreen:n``,lime:n``,red:n``,turquoise:n``,violet:n``,yellow:n``};static alphaPattern="repeating-conic-gradient(#7c8184 0% 25%, #c2c3c4 0% 50%) 50% / 10px 10px";static colorDragEventName="ueb-color-drag";static colorPickEventName="ueb-color-pick";static colorWindowEventName="ueb-color-window";static colorWindowName="Color Picker";static defaultCommentHeight=96;static defaultCommentWidth=400;static distanceThreshold=5;static dragEventName="ueb-drag";static dragGeneralEventName="ueb-drag-general";static edgeScrollThreshold=50;static editTextEventName={begin:"ueb-edit-text-begin",end:"ueb-edit-text-end"};static expandGridSize=400;static focusEventName={begin:"blueprint-focus",end:"blueprint-unfocus"};static fontSize=n``;static gridAxisLineColor=n``;static gridExpandThreshold=.25;static gridLineColor=n``;static gridLineWidth=1;static gridSet=8;static gridSetLineColor=n``;static gridShrinkThreshold=4;static gridSize=16;static hexColorRegex=/^\s*#(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})([0-9a-fA-F]{2})?|#(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])\s*$/;static indentation=" ";static keysSeparator=/[\.\(\)]/;static knotOffset=[-ne.gridSize,-.5*ne.gridSize];static lineTracePattern=/LineTrace(Single|Multi)(\w*)/;static linkCurveHeight=15;static linkCurveWidth=80;static linkMinWidth=100;static nameRegexSpaceReplacement=new RegExp("^K2(?:[Nn]ode)?_|(?<=[a-z])(?=[A-Z0-9])|(?<=[A-Z])(?{let r=100-e;return`M ${e} 0 C ${t.toFixed(3)} 0, ${i.toFixed(3)} 0, 50 50 S ${(r-t+e).toFixed(3)} 100, ${r.toFixed(3)} 100`};static maxZoom=7;static minZoom=-12;static mouseClickButton=0;static mouseRightClickButton=2;static mouseWheelZoomThreshold=80;static nodeDragEventName="ueb-node-drag";static nodeDragGeneralEventName="ueb-node-drag-general";static nodeTitle=(e,t)=>`${e}_${t}`;static nodeRadius=8;static nodeReflowEventName="ueb-node-reflow";static paths={actorBoundEvent:"/Script/BlueprintGraph.K2Node_ActorBoundEvent",addDelegate:"/Script/BlueprintGraph.K2Node_AddDelegate",ambientSound:"/Script/Engine.AmbientSound",asyncAction:"/Script/BlueprintGraph.K2Node_AsyncAction",blueprint:"/Script/Engine.Blueprint",blueprintGameplayTagLibrary:"/Script/GameplayTags.BlueprintGameplayTagLibrary",blueprintMapLibrary:"/Script/Engine.BlueprintMapLibrary",blueprintSetLibrary:"/Script/Engine.BlueprintSetLibrary",callArrayFunction:"/Script/BlueprintGraph.K2Node_CallArrayFunction",callDelegate:"/Script/BlueprintGraph.K2Node_CallDelegate",callFunction:"/Script/BlueprintGraph.K2Node_CallFunction",comment:"/Script/UnrealEd.EdGraphNode_Comment",commutativeAssociativeBinaryOperator:"/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator",componentBoundEvent:"/Script/BlueprintGraph.K2Node_ComponentBoundEvent",createDelegate:"/Script/BlueprintGraph.K2Node_CreateDelegate",customEvent:"/Script/BlueprintGraph.K2Node_CustomEvent",doN:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:Do N",doOnce:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:DoOnce",dynamicCast:"/Script/BlueprintGraph.K2Node_DynamicCast",eAttachmentRule:"/Script/Engine.EAttachmentRule",edGraph:"/Script/Engine.EdGraph",eDrawDebugTrace:"/Script/Engine.EDrawDebugTrace",eMaterialSamplerType:"/Script/Engine.EMaterialSamplerType",eNiagara_Float4Channel:"/Niagara/Enums/ENiagara_Float4Channel.ENiagara_Float4Channel",enum:"/Script/CoreUObject.Enum",enumLiteral:"/Script/BlueprintGraph.K2Node_EnumLiteral",eSamplerSourceMode:"/Script/Engine.ESamplerSourceMode",eSearchCase:"/Script/CoreUObject.ESearchCase",eSearchDir:"/Script/CoreUObject.ESearchDir",eSpawnActorCollisionHandlingMethod:"/Script/Engine.ESpawnActorCollisionHandlingMethod",eTextureMipValueMode:"/Script/Engine.ETextureMipValueMode",eTraceTypeQuery:"/Script/Engine.ETraceTypeQuery",event:"/Script/BlueprintGraph.K2Node_Event",executionSequence:"/Script/BlueprintGraph.K2Node_ExecutionSequence",flipflop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:FlipFlop",forEachElementInEnum:"/Script/BlueprintGraph.K2Node_ForEachElementInEnum",forEachLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForEachLoop",forEachLoopWithBreak:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForEachLoopWithBreak",forLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForLoop",forLoopWithBreak:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ForLoopWithBreak",functionEntry:"/Script/BlueprintGraph.K2Node_FunctionEntry",functionResult:"/Script/BlueprintGraph.K2Node_FunctionResult",gameplayTag:"/Script/GameplayTags.GameplayTag",getInputAxisKeyValue:"/Script/BlueprintGraph.K2Node_GetInputAxisKeyValue",ifThenElse:"/Script/BlueprintGraph.K2Node_IfThenElse",inputAxisKeyEvent:"/Script/BlueprintGraph.K2Node_InputAxisKeyEvent",inputDebugKey:"/Script/InputBlueprintNodes.K2Node_InputDebugKey",inputKey:"/Script/BlueprintGraph.K2Node_InputKey",inputVectorAxisEvent:"/Script/BlueprintGraph.K2Node_InputVectorAxisEvent",isValid:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:IsValid",kismetArrayLibrary:"/Script/Engine.KismetArrayLibrary",kismetMathLibrary:"/Script/Engine.KismetMathLibrary",knot:"/Script/BlueprintGraph.K2Node_Knot",linearColor:"/Script/CoreUObject.LinearColor",literal:"/Script/BlueprintGraph.K2Node_Literal",macro:"/Script/BlueprintGraph.K2Node_MacroInstance",makeArray:"/Script/BlueprintGraph.K2Node_MakeArray",makeMap:"/Script/BlueprintGraph.K2Node_MakeMap",makeSet:"/Script/BlueprintGraph.K2Node_MakeSet",makeStruct:"/Script/BlueprintGraph.K2Node_MakeStruct",materialExpressionComponentMask:"/Script/Engine.MaterialExpressionComponentMask",materialExpressionConstant:"/Script/Engine.MaterialExpressionConstant",materialExpressionConstant2Vector:"/Script/Engine.MaterialExpressionConstant2Vector",materialExpressionConstant3Vector:"/Script/Engine.MaterialExpressionConstant3Vector",materialExpressionConstant4Vector:"/Script/Engine.MaterialExpressionConstant4Vector",materialExpressionFunctionInput:"/Script/Engine.MaterialExpressionFunctionInput",materialExpressionLogarithm:"/Script/InterchangeImport.MaterialExpressionLogarithm",materialExpressionLogarithm10:"/Script/Engine.MaterialExpressionLogarithm10",materialExpressionLogarithm2:"/Script/Engine.MaterialExpressionLogarithm2",materialExpressionMaterialFunctionCall:"/Script/Engine.MaterialExpressionMaterialFunctionCall",materialExpressionSquareRoot:"/Script/Engine.MaterialExpressionSquareRoot",materialExpressionTextureCoordinate:"/Script/Engine.MaterialExpressionTextureCoordinate",materialExpressionTextureSample:"/Script/Engine.MaterialExpressionTextureSample",materialGraphNode:"/Script/UnrealEd.MaterialGraphNode",materialGraphNodeComment:"/Script/UnrealEd.MaterialGraphNode_Comment",metasoundEditorGraphExternalNode:"/Script/MetasoundEditor.MetasoundEditorGraphExternalNode",multiGate:"/Script/BlueprintGraph.K2Node_MultiGate",niagaraBool:"/Script/Niagara.NiagaraBool",niagaraClipboardContent:"/Script/NiagaraEditor.NiagaraClipboardContent",niagaraDataInterfaceVolumeTexture:"/Script/Niagara.NiagaraDataInterfaceVolumeTexture",niagaraFloat:"/Script/Niagara.NiagaraFloat",niagaraMatrix:"/Script/Niagara.NiagaraMatrix",niagaraNodeFunctionCall:"/Script/NiagaraEditor.NiagaraNodeFunctionCall",niagaraNodeOp:"/Script/NiagaraEditor.NiagaraNodeOp",niagaraNumeric:"/Script/Niagara.NiagaraNumeric",niagaraPosition:"/Script/Niagara.NiagaraPosition",pawn:"/Script/Engine.Pawn",pcgEditorGraphNode:"/Script/PCGEditor.PCGEditorGraphNode",pcgEditorGraphNodeInput:"/Script/PCGEditor.PCGEditorGraphNodeInput",pcgEditorGraphNodeOutput:"/Script/PCGEditor.PCGEditorGraphNodeOutput",pcgHiGenGridSizeSettings:"/Script/PCG.PCGHiGenGridSizeSettings",pcgSubgraphSettings:"/Script/PCG.PCGSubgraphSettings",promotableOperator:"/Script/BlueprintGraph.K2Node_PromotableOperator",quat4f:"/Script/CoreUObject.Quat4f",reverseForEachLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop",rotator:"/Script/CoreUObject.Rotator",select:"/Script/BlueprintGraph.K2Node_Select",self:"/Script/BlueprintGraph.K2Node_Self",slateBlueprintLibrary:"/Script/UMG.SlateBlueprintLibrary",spawnActorFromClass:"/Script/BlueprintGraph.K2Node_SpawnActorFromClass",switchEnum:"/Script/BlueprintGraph.K2Node_SwitchEnum",switchGameplayTag:"/Script/GameplayTagsEditor.GameplayTagsK2Node_SwitchGameplayTag",switchInteger:"/Script/BlueprintGraph.K2Node_SwitchInteger",switchName:"/Script/BlueprintGraph.K2Node_SwitchName",switchString:"/Script/BlueprintGraph.K2Node_SwitchString",timeline:"/Script/BlueprintGraph.K2Node_Timeline",timeManagementBlueprintLibrary:"/Script/TimeManagement.TimeManagementBlueprintLibrary",transform:"/Script/CoreUObject.Transform",userDefinedEnum:"/Script/Engine.UserDefinedEnum",variableGet:"/Script/BlueprintGraph.K2Node_VariableGet",variableSet:"/Script/BlueprintGraph.K2Node_VariableSet",vector:"/Script/CoreUObject.Vector",vector2D:"/Script/CoreUObject.Vector2D",vector3f:"/Script/CoreUObject.Vector3f",vector4f:"/Script/CoreUObject.Vector4f",whileLoop:"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop"};static pinInputWrapWidth=143;static removeEventName="ueb-element-delete";static scale={[-12]:.133333,[-11]:.166666,[-10]:.2,[-9]:.233333,[-8]:.266666,[-7]:.3,[-6]:.333333,[-5]:.375,[-4]:.5,[-3]:.675,[-2]:.75,[-1]:.875,0:1,1:1.25,2:1.375,3:1.5,4:1.675,5:1.75,6:1.875,7:2};static smoothScrollTime=1e3;static stringEscapedCharacters=/["\\]/g;static subObjectAttributeNamePrefix="#SubObject";static subObjectAttributeNameFromEntity=(e,t=!1)=>this.subObjectAttributeNamePrefix+(!t&&e.Class?`_${e.Class.type}`:"")+"_"+e.Name;static subObjectAttributeNameFromReference=(e,t=!1)=>this.subObjectAttributeNamePrefix+(t?"":"_"+e.type)+"_"+e.path;static subObjectAttributeNameFromName=e=>this.subObjectAttributeNamePrefix+"_"+e;static switchTargetPattern=/\/Script\/[\w\.\/\:]+K2Node_Switch([A-Z]\w+)+/;static trackingMouseEventName={begin:"ueb-tracking-mouse-begin",end:"ueb-tracking-mouse-end"};static unescapedBackslash=/(?<=(?:[^\\]|^)(?:\\\\)*)\\(?!\\)/;static windowApplyEventName="ueb-window-apply";static windowApplyButtonText="OK";static windowCancelEventName="ueb-window-cancel";static windowCancelButtonText="Cancel";static windowCloseEventName="ueb-window-close";static CommonEnums={[this.paths.eAttachmentRule]:["KeepRelative","KeepWorld","SnapToTarget"],[this.paths.eMaterialSamplerType]:["Color","Grayscale","Alpha","Normal","Masks","Distance Field Font","Linear Color","Linear Grayscale","Data","External","Virtual Color","Virtual Grayscale","Virtual Alpha","Virtual Normal","Virtual Mask","Virtual Linear Color","Virtual Linear Grayscal"],[this.paths.eNiagara_Float4Channel]:[["NewEnumerator0","R"],["NewEnumerator1","G"],["NewEnumerator2","B"],["NewEnumerator3","A"]],[this.paths.eSamplerSourceMode]:["From texture asset","Shared: Wrap","Shared: Clamp","Hidden"],[this.paths.eSpawnActorCollisionHandlingMethod]:[["Undefined","Default"],["AlwaysSpawn","Always Spawn, Ignore Collisions"],["AdjustIfPossibleButAlwaysSpawn","Try To Adjust Location, But Always Spawn"],["AdjustIfPossibleButDontSpawnIfColliding","Try To Adjust Location, Don't Spawn If Still Colliding"],["DontSpawnIfColliding","Do Not Spawn"]],[this.paths.eSearchCase]:["CaseSensitive","IgnoreCase"],[this.paths.eSearchDir]:["FromStart","FromEnd"],[this.paths.eDrawDebugTrace]:["None","ForOneFrame","ForDuration","Persistent"],[this.paths.eTextureMipValueMode]:["None (use computed mip level)","MipLevel (absolute, 0 is full resolution)","MipBias (relative to the computed mip level)","Derivative (explicit derivative to compute mip level)"],[this.paths.eTraceTypeQuery]:[["TraceTypeQuery1","Visibility"],["TraceTypeQuery2","Camera"]]};static ModifierKeys=["Ctrl","Shift","Alt","Meta"];static rgba=["R","G","B","A"];static Keys={Backspace:"Backspace",Tab:"Tab",LeftControl:"ControlLeft",RightControl:"ControlRight",LeftShift:"ShiftLeft",RightShift:"ShiftRight",LeftAlt:"AltLeft",RightAlt:"AltRight",Enter:"Enter",Pause:"Pause",CapsLock:"CapsLock",Escape:"Escape",Space:"Space",PageUp:"PageUp",PageDown:"PageDown",End:"End",Home:"Home",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",PrintScreen:"PrintScreen",Insert:"Insert",Delete:"Delete",Zero:"Digit0",One:"Digit1",Two:"Digit2",Three:"Digit3",Four:"Digit4",Five:"Digit5",Six:"Digit6",Seven:"Digit7",Eight:"Digit8",Nine:"Digit9",A:"KeyA",B:"KeyB",C:"KeyC",D:"KeyD",E:"KeyE",F:"KeyF",G:"KeyG",H:"KeyH",I:"KeyI",K:"KeyK",L:"KeyL",M:"KeyM",N:"KeyN",O:"KeyO",P:"KeyP",Q:"KeyQ",R:"KeyR",S:"KeyS",T:"KeyT",U:"KeyU",V:"KeyV",W:"KeyW",X:"KeyX",Y:"KeyY",Z:"KeyZ",NumPadZero:"Numpad0",NumPadOne:"Numpad1",NumPadTwo:"Numpad2",NumPadThree:"Numpad3",NumPadFour:"Numpad4",NumPadFive:"Numpad5",NumPadSix:"Numpad6",NumPadSeven:"Numpad7",NumPadEight:"Numpad8",NumPadNine:"Numpad9",Multiply:"NumpadMultiply",Add:"NumpadAdd",Subtract:"NumpadSubtract",Decimal:"NumpadDecimal",Divide:"NumpadDivide",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12",NumLock:"NumLock",ScrollLock:"ScrollLock"}}class ae{#e;constructor(e){this.#e=e}compute(e){return this.#e(e)}}class oe{static#t={nullable:!1,ignored:!1,serialized:!1,expected:!1,inlined:!1,quoted:!1,silent:!1,uninitialized:!1};constructor(e){this.type=e.type??e.default?.constructor,this.default=e.default,this.nullable=e.nullable??null===e.default,this.ignored=e.ignored,this.serialized=e.serialized,this.expected=e.expected,this.inlined=e.inlined,this.quoted=e.quoted,this.silent=e.silent,this.uninitialized=e.uninitialized,this.predicate=e.predicate,this.type===Array&&this.default instanceof Array&&this.default.length>0&&(this.type=this.default.map((e=>e.constructor)).reduce(((e,t)=>(e.includes(t)||e.push(t),e)),[]))}static createType(e){return new oe({type:e})}static createValue(e){return new oe({default:e})}static hasAttribute(e,t,i,r=e.constructor){const s=e,n=s.attributes[t]?.[i];return n??r?.attributes?.[t]?.[i]??oe.#t[i]}static getAttribute(e,t,i,r=e.constructor){let s=e.attributes?.[t]?.[i];return void 0!==s?s:(s=(r?.attributes)?.[t]?.[i],void 0!==s?s:(s=oe.#t[i],void 0!==s?s:void 0))}get(e){return this[e]??oe.#t[e]}}class le{static attributes={type:new oe({ignored:!0}),getter:new oe({ignored:!0})};constructor(e,t=null){this.type=e,this.getter=t}get(){return this.getter()}getTargetType(){const e=this.type;return e instanceof le?e.getTargetType():e}}class ce{#i;get values(){return this.#i}constructor(...e){this.#i=e}}class ue{static booleanConverter={fromAttribute:(e,t)=>{},toAttribute:(e,t)=>!0===e?"true":!1===e?"false":""};static sigmoid(e,t=1.7){return 1/(1+e/(1-e)**-t)}static sigmoidPositive(e,t=3.7,i=1.1){return 1-Math.exp(-((e/i)**t))}static clamp(e,t=-1/0,i=1/0){return Math.min(Math.max(e,t),i)}static getScale(e){const t=e.blueprint?.getScale()??getComputedStyle(e).getPropertyValue("--ueb-scale");return""!=t?parseFloat(t):1}static minDecimals(e,t=1,i=1e-8){const r=e*10**t;return Math.abs(r%1)>i?e.toString():e.toFixed(t)}static numberFromText(e=""){switch(e=e.toLowerCase()){case"zero":return 0;case"one":return 1;case"two":return 2;case"three":return 3;case"four":return 4;case"five":return 5;case"six":return 6;case"seven":return 7;case"eight":return 8;case"nine":return 9}}static roundDecimals(e,t=1){const i=10**t;return Math.round(e*i)/i}static printNumber(e){return e==Number.POSITIVE_INFINITY?"inf":e==Number.NEGATIVE_INFINITY?"-inf":ue.minDecimals(e)}static printExponential(e){if(e==Number.POSITIVE_INFINITY)return"inf";if(e==Number.NEGATIVE_INFINITY)return"-inf";const t=Math.round(e);if(t>=1e3){const i=Math.floor(Math.log10(t));return`${Math.round(e/10**(i-2))/100}e+${i<10?"0":""}${i}`}return 0==Math.floor(e)?e.toString():this.roundDecimals(e,Math.max(0,3-Math.floor(e).toString().length)).toString()}static approximatelyEqual(e,t,i=1e-8){return!(Math.abs(e-t)>i)}static convertLocation(e,t,i=!1){const r=i?1:1/ue.getScale(t),s=t.getBoundingClientRect();return[Math.round((e[0]-s.x)*r),Math.round((e[1]-s.y)*r)]}static isSerialized(e,t){return e.attributes?.[t]?.serialized??e.constructor.attributes?.[t]?.serialized??!1}static objectGet(e,t,i=void 0){if(void 0!==e){if(!(t instanceof Array))throw new TypeError("UEBlueprint: Expected keys to be an array");return 0!=t.length&&t[0]in e&&void 0!==e[t[0]]?1==t.length?e[t[0]]:ue.objectGet(e[t[0]],t.slice(1),i):i}}static objectSet(e,t,i,r=Object){if(!(t instanceof Array))throw new TypeError("Expected keys to be an array.");if(1==t.length){if(t[0]in e||void 0===e[t[0]])return e[t[0]]=i,!0}else if(t.length>0)return e[t[0]]instanceof Object||(e[t[0]]=new r),ue.objectSet(e[t[0]],t.slice(1),i,r);return!1}static equals(e,t){for(;e instanceof le;)e=e.get();for(;t instanceof le;)t=t.get();return e?.equals&&t?.equals?e.equals(t):(e=ue.sanitize(e),t=ue.sanitize(t),e?.constructor===BigInt&&t?.constructor===Number?t=BigInt(t):e?.constructor===Number&&t?.constructor===BigInt&&(e=BigInt(e)),e===t||e instanceof Array&&t instanceof Array&&(e.length===t.length&&e.every(((e,i)=>ue.equals(e,t[i])))))}static getType(e){return null===e?null:e?.constructor===Object&&e?.type instanceof Function?e.type:e?.constructor}static isValueOfType(e,t,i=!1){return t instanceof le&&(t=t.getTargetType()),i&&null===e||e instanceof t||e?.constructor===t}static sanitize(e,t=e?.constructor){if(t instanceof Array&&(t=t[0]),t instanceof ae)return e;if(t instanceof ce){let i=t.values.find((t=>ue.isValueOfType(e,t,!1)));i||(i=t.values[0]),t=i}return t instanceof le?e instanceof le?e:ue.sanitize(e,t.getTargetType()):(t&&!ue.isValueOfType(e,t,!0)&&(e=t===BigInt?BigInt(e):new t(e)),(e instanceof Boolean||e instanceof Number||e instanceof String)&&(e=e.valueOf()),e)}static snapToGrid(e,t,i){return 1===i?[e,t]:[i*Math.floor(e/i),i*Math.floor(t/i)]}static mergeArrays(e=[],t=[],i=((e,t)=>e==t)){let r=[];e=[...e],t=[...t];e:for(;;){for(let s=0;s|
/g,"\n").replaceAll(/(\)/g,"")}static encodeHTMLWhitespace(e){return e.replaceAll(" "," ")}static capitalFirstLetter(e){return 0===e.length?e:e.charAt(0).toUpperCase()+e.slice(1)}static formatStringName(e=""){return e.replace(/^\s*b(?=[A-Z])/,"").replaceAll(ne.nameRegexSpaceReplacement," ").trim().split(" ").map((e=>ue.capitalFirstLetter(e))).join(" ")}static getIdFromReference(e){return e.replace(/(?:.+\.)?([^\.]+)$/,"$1").replaceAll(/(?<=[a-z\d])(?=[A-Z])|(?<=[a-zA-Z])(?=\d)|(?<=[A-Z]{2})(?=[A-Z][a-z])/g,"-").toLowerCase()}static getNameFromPath(e,t=!1){const i=t?/([^\.\/]+?)(?:_\d+)$/:/([^\.\/]+)$/;return e.match(i)?.[1]??""}static printLinearColor(e){return`${Math.round(255*e.R.valueOf())}, ${Math.round(255*e.G.valueOf())}, ${Math.round(255*e.B.valueOf())}`}static getPolarCoordinates(e,t,i=!1){let r=Math.atan2(t,e);return i&&r<0&&(r=2*Math.PI+r),[Math.sqrt(e*e+t*t),r]}static getCartesianCoordinates(e,t){return[e*Math.cos(t),e*Math.sin(t)]}static range(e=0,t=0,i=(t>=e?1:-1)){return Array.from({length:Math.ceil((t-e)/i)},((t,r)=>e+r*i))}static getFirstWordOrder(e){return new RegExp(/\s*/.source+e.join(/[^\n]+\n\s*/.source)+/\s*/.source)}static paste(e,t){const i=new ClipboardEvent("paste",{bubbles:!0,cancelable:!0,clipboardData:new DataTransfer});i.clipboardData.setData("text",t),e.dispatchEvent(i)}static async copy(e){const t=new ClipboardEvent("copy",{bubbles:!0,cancelable:!0,clipboardData:new DataTransfer});e.dispatchEvent(t)}static animate(e,t,i,r,s=(e=>{}),n=(e=>{const t=e**3.5;return t/(t+(1-e)**3.5)})){let a;const o=l=>{void 0===a&&(a=l);let c=(l-a)/i;ue.approximatelyEqual(c,1)||c>1?c=1:s(requestAnimationFrame(o));const u=e+(t-e)*n(c);r(u)};s(requestAnimationFrame(o))}}class he extends re{#r;get blueprint(){return this.#r}set blueprint(e){this.#r=e}#s;get entity(){return this.#s}set entity(e){this.#s=e}#n;get template(){return this.#n}isInitialized=!1;isSetup=!1;inputObjects=[];initialize(e,t){this.requestUpdate(),this.#s=e,this.#n=t,this.#n.initialize(this),this.isConnected&&this.updateComplete.then((()=>this.setup())),this.isInitialized=!0}connectedCallback(){super.connectedCallback(),this.blueprint=this.closest("ueb-blueprint"),this.isInitialized&&(this.requestUpdate(),this.updateComplete.then((()=>this.setup())))}disconnectedCallback(){super.disconnectedCallback(),this.isSetup&&this.updateComplete.then((()=>this.cleanup())),this.acknowledgeDelete()}createRenderRoot(){return this}setup(){this.template.setup(),this.isSetup=!0}cleanup(){this.template.cleanup(),this.isSetup=!1}willUpdate(e){super.willUpdate(e),this.template.willUpdate(e)}update(e){super.update(e),this.template.update(e)}render(){return this.template.render()}firstUpdated(e){super.firstUpdated(e),this.template.firstUpdated(e),this.template.inputSetup()}updated(e){super.updated(e),this.template.updated(e)}acknowledgeDelete(){let e=new CustomEvent(ne.removeEventName);this.dispatchEvent(e)}isSameGraph(e){return this.blueprint&&this.blueprint==e?.blueprint}}class pe extends he{static properties={...super.properties,fromX:{type:Number,attribute:!1},fromY:{type:Number,attribute:!1},toX:{type:Number,attribute:!1},toY:{type:Number,attribute:!1}};constructor(){super(),this.fromX=0,this.fromY=0,this.toX=0,this.toY=0}setBothLocations([e,t]){this.fromX=e,this.fromY=t,this.toX=e,this.toY=t}addSourceLocation(e,t){this.fromX+=e,this.fromY+=t}addDestinationLocation(e,t){this.toX+=e,this.toY+=t}}class de{static deleteNodes="Delete";static duplicateNodes="(bCtrl=True,Key=D)";static enableLinkDelete="LeftAlt";static enableZoomIn=["LeftControl","RightControl"];static selectAllNodes="(bCtrl=True,Key=A)"}class me{static#a=new Map;static registerElement(e,t){me.#a.set(e,t)}static getConstructor(e){return me.#a.get(e)}}class ge{static makeSuccess(e,t,i=null,r=0){return{status:!0,value:t,position:e,bestParser:i,bestPosition:r}}static makeFailure(e=0,t=null,i=0){return{status:!1,value:null,position:e,bestParser:t,bestPosition:i}}static makeContext(e=null,t=""){return{parsernostrum:e,input:t,highlighted:null}}static makePathNode(e,t=0,i=null){return{parent:i,current:e,index:t}}}class be{static indentation=" ";static highlight="Last valid parser";Self;static frame(e,t="",i=""){t=e?"[ "+t+" ]":"";let r=e.split("\n");const s=Math.max(...r.map((e=>e.length))),n=s";const s=this.isVisited(i),n=this.isHighlighted(e,i);let a=s?"<...>":this.doToString(e,n?"":t,i,r);return n&&(a=be.frame(a,be.highlight,t)),a}doToString(e,t,i,r){return`${this.constructor.name} does not implement toString()`}}class ve extends be{#o;get value(){return this.#o}constructor(e){super(),this.#o=e}parse(e,t,i,r){i=this.makePath(i,r);const s=t+this.#o.length,n=e.input.substring(t,s);return this.#o===n?ge.makeSuccess(s,this.#o,i,s):ge.makeFailure()}doToString(e,t,i,r){return`"${this.value.replaceAll("\n","\\n").replaceAll('"','\\"')}"`}}class ye extends be{static instance=new ye;parse(e,t,i,r){return i=this.makePath(i,r),ge.makeSuccess(t,"",i,0)}doToString(e,t,i,r){return""}}class fe extends be{#l;get parsers(){return this.#l}constructor(...e){super(),this.#l=e}parse(e,t,i,r){i=this.makePath(i,r);const s=ge.makeSuccess(0,"");for(let r=0;rs.bestPosition&&(s.bestParser=n.bestParser,s.bestPosition=n.bestPosition),n.status)return s.value=n.value,s.position=n.position,s}return s.status=!1,s.value=null,s}doToString(e,t,i,r){if(2===this.#l.length&&this.#l[1]instanceof ye){let r=this.#l[0].toString(e,t,i,0);return this.#l[0]instanceof ve||(r="<"+r+">"),r+="?",r}const s=t+be.indentation;let n="ALT<\n"+s+this.#l.map(((t,r)=>t.toString(e,s+" ".repeat(0===r?0:be.indentation.length-2),i,r))).join("\n"+s+"| ")+"\n"+t+">";return n}}class we extends be{#c;get parser(){return this.#c}#u;constructor(e,t){super(),this.#c=e,this.#u=t}parse(e,t,i,r){i=this.makePath(i,r);const s=this.#c.parse(e,t,i,0);if(!s.status)return s;const n=this.#u(s.value,e.input,s.position).getParser().parse(e,s.position);return s.bestPosition>n.bestPosition&&(n.bestParser=s.bestParser,n.bestPosition=s.bestPosition),n}doToString(e,t,i,r){return this.#c.toString(e,t,i,0)+" => chained"}}class Se extends be{static instance=new Se;parse(e,t,i,r){return ge.makeFailure()}doToString(e,t,i,r){return""}}class Ee extends be{#c;get parser(){return this.#c}#h="";constructor(e,t){super(),this.#c=e,this.#h=t}makePath(e,t){return e}parse(e,t,i,r){return this.parse=this.#c.parse.bind(this.#c),this.parse(e,t,i,r)}doToString(e,t,i,r){let s=this.#c.toString(e,"",i,r);return s=be.frame(s,this.#h,t),s}}class Ce extends be{#c;#p;constructor(e){super(),this.#c=e}makePath(e,t){return e}isHighlighted(e,t){if(super.isHighlighted(e,t)){const i={parent:t,parser:this.#p,index:0};e.highlighted=e.highlighted instanceof be?this.#p:i}return!1}resolve(){return this.#p||(this.#p=this.#c().getParser()),this.#p}parse(e,t,i,r){return this.resolve(),this.parse=this.#p.parse.bind(this.#p),this.parse(e,t,i,r)}doToString(e,t,i,r){return this.resolve(),this.doToString=this.#p.toString.bind(this.#p),this.doToString(e,t,i,r)}}class Ne extends be{#c;get parser(){return this.#c}#d;get type(){return this.#d}static Type={NEGATIVE_AHEAD:"?!",NEGATIVE_BEHIND:"?String.raw`[^${e}\\]*(?:\\.[^${e}\\]*)*`;static#y=/[-\+]?(?:\d*\.)?\d+/;static common={number:new RegExp(this.#y.source+String.raw`(?!\.)`),numberInteger:/[\-\+]?\d+(?!\.\d)/,numberNatural:/\d+/,numberExponential:new RegExp(this.#y.source+String.raw`(?:[eE][\+\-]?\d+)?(?!\.)`),numberUnit:/\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/,numberByte:/0*(?:25[0-5]|2[0-4]\d|1?\d?\d)(?!\d|\.)/,whitespace:/\s+/,whitespaceOpt:/\s*/,whitespaceInline:/[^\S\n]+/,whitespaceInlineOpt:/[^\S\n]*/,whitespaceMultiline:/\s*?\n\s*/,doubleQuotedString:new RegExp(`"(${this.#v('"')})"`),singleQuotedString:new RegExp(`'(${this.#v("'")})'`),backtickQuotedString:new RegExp("`("+this.#v("`")+")`")};constructor(e,t){super(),this.#m=e,this.#g=new RegExp(`^(?:${e.source})`,e.flags),this.#b=t}parse(e,t,i,r){i=this.makePath(i,r);const s=this.#g.exec(e.input.substring(t));s&&(t+=s[0].length);return s?ge.makeSuccess(t,this.#b(s),i,t):ge.makeFailure()}doToString(e,t,i,r){let s="/"+this.#m.source+"/";const n=Object.entries(xe.common).find((([e,t])=>t.source===this.#m.source))?.[0];return n&&(s="P."+n),s}}class Pe extends be{#c;get parser(){return this.#c}#f;get mapper(){return this.#f}constructor(e,t){super(),this.#c=e,this.#f=t}isHighlighted(e,t){if(super.isHighlighted(e,t)){const i={parent:t,parser:this.#c,index:0};e.highlighted=e.highlighted instanceof be?this.#c:i}return!1}parse(e,t,i,r){i=this.makePath(i,r);const s=this.#c.parse(e,t,i,0);return s.status&&(s.value=this.#f(s.value)),s}doToString(e,t,i,r){let s=this.#c.toString(e,t,i,0);if(this.#c instanceof xe&&Object.values(xe.common).includes(this.#c.regexp))return this.#c.regexp===xe.common.numberInteger&&this.#f===BigInt?"P.numberBigInteger":s;let n=this.#f.toString();return(n.length>60||n.includes("\n"))&&(n="(...) => { ... }"),s+=` -> map<${n}>`,s}}class ke extends xe{static#f=e=>e;constructor(e){super(e,ke.#f)}}class Te extends xe{constructor(e,t=0){super(e,(e=>e[t]))}}class Ae extends be{#l;get parsers(){return this.#l}constructor(...e){super(),this.#l=e}parse(e,t,i,r){i=this.makePath(i,r);const s=new Array(this.#l.length),n=ge.makeSuccess(t,s);for(let t=0;tn.bestPosition&&(n.bestParser=r.bestParser,n.bestPosition=r.bestPosition),!r.status){n.status=!1,n.value=null;break}n.value[t]=r.value,n.position=r.position}return n}doToString(e,t,i,r){const s=t+be.indentation,n="SEQ<\n"+s+this.#l.map(((t,r)=>t.toString(e,s,i,r))).join("\n"+s)+"\n"+t+">";return n}}class Le extends be{#c;get parser(){return this.#c}#w;get min(){return this.#w}#S;get max(){return this.#S}constructor(e,t=0,i=Number.POSITIVE_INFINITY){if(super(),t>i)throw new Error("Min is greater than max");this.#c=e,this.#w=t,this.#S=i}parse(e,t,i,r){i=this.makePath(i,r);const s=ge.makeSuccess(t,[],i);for(let t=0;ts.bestPosition&&(s.bestParser=r.bestParser,s.bestPosition=r.bestPosition),!r.status){te;static#C=([e,t])=>t;static#N=([e,t])=>[e,...t];static#x=e=>e instanceof Array?e.join(""):e;static#v=e=>String.raw`[^${e}\\]*(?:\\.[^${e}\\]*)*`;static number=this.reg(xe.common.number).map(Number);static numberInteger=this.reg(xe.common.numberInteger).map(Number);static numberBigInteger=this.reg(this.numberInteger.getParser().parser.regexp).map(BigInt);static numberNatural=this.reg(xe.common.numberNatural).map(Number);static numberExponential=this.reg(xe.common.numberExponential).map(Number);static numberUnit=this.reg(xe.common.numberUnit).map(Number);static numberByte=this.reg(xe.common.numberByte).map(Number);static whitespace=this.reg(xe.common.whitespace);static whitespaceOpt=this.reg(xe.common.whitespaceOpt);static whitespaceInline=this.reg(xe.common.whitespaceInline);static whitespaceInlineOpt=this.reg(xe.common.whitespaceInlineOpt);static whitespaceMultiline=this.reg(xe.common.whitespaceMultiline);static doubleQuotedString=this.reg(xe.common.doubleQuotedString,1);static singleQuotedString=this.reg(xe.common.singleQuotedString,1);static backtickQuotedString=this.reg(xe.common.backtickQuotedString,1);constructor(e,t=!1){this.#c=e}static#P(e){const t=[];for(;e;)t.push(e),e=e.parent;t.reverse();let i=new Map;for(let e=1;ei<=r||i>e))),i.set(t[e].current,r),t.splice(r+1,e-r),e=r):i.set(t[e].current,e)}return t[t.length-1]}getParser(){return this.#c}run(e){const t=this.#c.parse(ge.makeContext(this,e),0,ge.makePathNode(),0);return t.position!==e.length&&(t.status=!1),t}parse(e,t=!0){const i=this.run(e);if(i.status)return i.value;const r=[Math.ceil(30),Math.floor(30)],s=Me.lineColumnFromOffset(e,i.bestPosition);let n=i.bestPosition;const a=e.replaceAll(/^(\s)+|\s{6,}|\s*?\n\s*/g,((e,t,r)=>{let s=t?"...":" ... ";return r<=i.bestPosition&&(i.bestPosition0&&(u="..."+u,c+=3),r[1]e.getParser()))));return t}static alt(...e){return new this(new fe(...e.map((e=>e.getParser()))))}static lookahead(e){return new this(new Ne(e.getParser(),Ne.Type.POSITIVE_AHEAD))}static lazy(e){return new this(new Ce(e))}times(e,t=e){return new Me(new Le(this.#c,e,t))}many(){return this.times(0,Number.POSITIVE_INFINITY)}atLeast(e){return this.times(e,Number.POSITIVE_INFINITY)}atMost(e){return this.times(0,e)}opt(){return Me.alt(this,Me.success())}sepBy(e,t=!1){return Me.seq(this,Me.seq(e,this).map(Me.#C).many()).map(Me.#N)}skipSpace(){return Me.seq(this,Me.whitespaceOpt).map(Me.#E)}map(e){return new Me(new Pe(this.#c,e))}chain(e){return new Me(new we(this.#c,e))}assert(e){return this.chain(((t,i,r)=>e(t,i,r)?Me.success().map((()=>t)):Me.failure()))}join(e=""){return this.map(Me.#x)}label(e=""){return new Me(new Ee(this.#c,e))}toString(e="",t=!1,i=null){i instanceof Me&&(i=i.getParser());const r=ge.makeContext(this,"");r.highlighted=i;const s=ge.makePathNode();return(t?"\n"+e:"")+this.#c.toString(r,e,s)}}class Ie{static grammar=this.createGrammar();static createGrammar(){return Me.failure()}}class Be{static#k=new Map;static registerSerializer(e,t){Be.#k.set(e,t)}static getSerializer(e){return Be.#k.get(e)}}class Ve extends Ie{static attributes={attributes:new oe({ignored:!0}),lookbehind:new oe({default:"",ignored:!0,uninitialized:!0})};#T;get _keys(){return this.#T}set _keys(e){this.#T=e}constructor(e={},t=!1){super();const i=this.constructor;this.attributes,this.lookbehind;const r=Object.keys(e),s=e.attributes?ue.mergeArrays(Object.keys(e.attributes),Object.keys(i.attributes)):Object.keys(i.attributes),n=ue.mergeArrays(r,s);for(const r of n){let s=e[r];if(!(t||r in e||r in i.attributes||r.startsWith(ne.subObjectAttributeNamePrefix))){const e=s instanceof Array?`[${s[0]?.constructor.name}]`:s.constructor.name;console.warn(`UEBlueprint: Attribute ${r} (of type ${e}) in the serialized data is not defined in ${i.name}.attributes`)}if(!(r in i.attributes)){this[r]=s;continue}i.attributes.lookbehind;const n=oe.getAttribute(e,r,"predicate",i),a=n?e=>{Object.defineProperties(this,{["#"+r]:{writable:!0,enumerable:!1},[r]:{enumerable:!0,get(){return this["#"+r]},set(e){n(e)?this["#"+r]=e:console.warn(`UEBlueprint: Tried to assign attribute ${r} to ${i.name} not satisfying the predicate`)}}}),this[r]=e}:e=>this[r]=e;let o=oe.getAttribute(e,r,"default",i);o instanceof Function&&(o=o(this));let l=oe.getAttribute(e,r,"type",i);if(l instanceof ae&&(l=l.compute(this)),l instanceof Array&&(l=Array),void 0===l&&(l=ue.getType(o)),void 0===s)void 0===o||oe.getAttribute(e,r,"uninitialized",i)||a(o);else{if(s?.constructor===String&&oe.getAttribute(e,r,"serialized",i)&&l!==String)try{s=Be.getSerializer(l).read(s)}catch(e){a(s);continue}a(ue.sanitize(s,l))}}}static defaultValueProviderFromType(e){return e!==Boolean&&(e===Number?0:e===BigInt?0n:e===String?"":e===Array||e instanceof Array?()=>[]:e instanceof ce?this.defaultValueProviderFromType(e.values[0]):e instanceof le?()=>new le(e.type,e.getter):e instanceof ae?void 0:()=>new e)}static isValueOfType(e,t){return null!=e&&(e instanceof t||e.constructor===t)}static defineAttributes(e,t){Object.defineProperty(e,"attributes",{writable:!0,configurable:!1}),e.attributes=t}listenAttribute(e,t){const i=Object.getOwnPropertyDescriptor(this,e),r=i.set;r?(i.set=e=>{r(e),t(e)},Object.defineProperties(this,{[e]:i})):i.value&&Object.defineProperties(this,{["#"+e]:{value:i.value,writable:!0,enumerable:!1},[e]:{enumerable:!0,get(){return this["#"+e]},set(i){i!=this["#"+e]&&(t(i),this["#"+e]=i)}}})}getLookbehind(){let e=this.lookbehind??oe.getAttribute(this,"lookbehind","default");return e=e instanceof ce?e.values[0]:e,e}unexpectedKeys(){return Object.keys(this).length-Object.keys(this.constructor.attributes).length}equals(e){const t=Object.keys(this),i=Object.keys(e);if(t.length!=i.length)return!1;for(const i of t){if(this[i]instanceof Ve&&!this[i].equals(e[i]))return!1;if(!ue.equals(this[i],e[i]))return!1}return!0}}class $e{static separatedBy=(e,t,i=1)=>new RegExp(e+"(?:"+t+e+")"+(1===i?"*":2===i?"+":`{${i},}`));static Regex=class{static HexDigit=/[0-9a-fA-F]/;static InsideString=/(?:[^"\\]|\\.)*/;static InsideSingleQuotedString=/(?:[^'\\]|\\.)*/;static Integer=/[\-\+]?\d+(?!\d|\.)/;static Number=/[-\+]?(?:\d*\.)?\d+(?!\d|\.)/;static RealUnit=/\+?(?:0(?:\.\d+)?|1(?:\.0+)?)(?![\.\d])/;static Word=$e.separatedBy("[a-zA-Z]","_");static Symbol=/[a-zA-Z_]\w*/;static DotSeparatedSymbols=$e.separatedBy(this.Symbol.source,"\\.");static MultipleWordsSymbols=$e.separatedBy(this.Symbol.source,"(?:\\.|\\ +)");static PathFragment=$e.separatedBy(this.Symbol.source,"[\\.:]");static PathSpaceFragment=$e.separatedBy(this.Symbol.source,"[\\.:\\ ]");static Path=new RegExp(`(?:\\/${this.PathFragment.source}){2,}`)};static null=Me.reg(/\(\s*\)/).map((()=>null));static true=Me.reg(/true/i).map((()=>!0));static false=Me.reg(/false/i).map((()=>!1));static boolean=Me.regArray(/(true)|false/i).map((e=>!!e[1]));static number=Me.regArray(new RegExp(`(${Me.number.getParser().parser.regexp.source})|(\\+?inf)|(-inf)`)).map((([e,t,i,r])=>t?Number(t):i?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY));static bigInt=Me.reg(new RegExp(Me.number.getParser().parser.regexp.source)).map(BigInt).map((e=>void 0!==e[2]?Number.POSITIVE_INFINITY:void 0!==e[3]?Number.NEGATIVE_INFINITY:Number(e[1])));static naturalNumber=Me.lazy((()=>Me.reg(/\d+/).map(Number)));static string=Me.doubleQuotedString.map((e=>ue.unescapeString(e)));static colorValue=Me.numberByte;static word=Me.reg($e.Regex.Word);static symbol=Me.reg($e.Regex.Symbol);static symbolQuoted=Me.reg(new RegExp('"('+$e.Regex.Symbol.source+')"'),1);static attributeName=Me.reg($e.Regex.DotSeparatedSymbols);static attributeNameQuoted=Me.reg(new RegExp('"('+$e.Regex.InsideString.source+')"'),1);static guid=Me.reg(new RegExp(`${$e.Regex.HexDigit.source}{32}`));static commaSeparation=Me.reg(/\s*,\s*(?!\))/);static commaOrSpaceSeparation=Me.reg(/\s*,\s*(?!\))|\s+/);static equalSeparation=Me.reg(/\s*=\s*/);static hexColorChannel=Me.reg(new RegExp($e.Regex.HexDigit.source+"{2}"));static grammarFor(e,t=e?.type,i=this.unknownValue){let r=i;if(t===Array||t instanceof Array){if(e?.inlined)return this.grammarFor(void 0,t[0]);r=Me.seq(Me.reg(/\(\s*/),this.grammarFor(void 0,t[0]).sepBy(this.commaSeparation).opt(),Me.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>t instanceof Array?t:[]))}else if(t instanceof ce)r=t.values.map((e=>this.grammarFor(void 0,e))).reduce(((e,t)=>t&&t!==this.unknownValue&&e!==this.unknownValue?Me.alt(e,t):this.unknownValue));else{if(t instanceof le)return this.grammarFor(void 0,t.getTargetType()).map((e=>new le(t.type,(()=>e))));if(e?.constructor===Object)r=this.grammarFor(void 0,t);else switch(t){case Boolean:r=this.boolean;break;case null:r=this.null;break;case Number:r=this.number;break;case BigInt:r=this.bigInt;break;case String:r=this.string;break;default:t?.prototype instanceof Ie&&(r=t.grammar)}}return e&&(e.serialized&&t.constructor!==String&&(r=r==this.unknownValue?this.string:Me.seq(Me.str('"'),r,Me.str('"')).map((([e,t,i])=>t))),e.nullable&&(r=Me.alt(r,this.null))),r}static getAttribute(e,t){let i,r;if(e instanceof ce)for(let r of e.values)if(i=this.getAttribute(r,t))return i;return e instanceof Ve.constructor?(i=e.attributes[t[0]],r=i?.type):e instanceof Array&&(i=e[t[0]],r=i),t.length>1?this.getAttribute(r,t.slice(1)):i}static createAttributeGrammar(e,t=this.attributeName,i=this.equalSeparation,r=((e,t,i)=>{})){return Me.seq(t,i).chain((([t,i])=>{const s=t.split(ne.keysSeparator),n=this.getAttribute(e,s);return this.grammarFor(n).map((e=>t=>{r(t,s,e),ue.objectSet(t,s,e)}))}))}static createEntityGrammar(e,t=!0,i=this.commaSeparation){const r=e.attributes.lookbehind.default;return Me.seq(Me.reg(r instanceof ce?new RegExp(`(${r.values.reduce(((e,t)=>e+"|"+t))})\\s*\\(\\s*`):r.constructor==String&&r.length>0?new RegExp(`(${r})\\s*\\(\\s*`):/()\(\s*/,1),this.createAttributeGrammar(e).sepBy(i),Me.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{let r={};return t.forEach((e=>e(r))),e.length&&(r.lookbehind=e),r})).chain((i=>{let r=Object.keys(i);if(Object.keys(e.attributes).filter((t=>e.attributes[t].expected)).find((e=>!r.includes(e)&&e)))return Me.failure();const s=Object.keys(i).filter((t=>!(t in e.attributes))).length;return!t&&s>0?Me.failure():Me.success().map((()=>new e(i)))}))}static unknownValue}class Oe extends Ve{static attributes={...super.attributes,value:oe.createValue(0)};static grammar=this.createGrammar();static createGrammar(){return Me.number.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toFixed(6)}}class He extends Ve{static attributes={...super.attributes,R:new oe({type:Oe,default:()=>new Oe,expected:!0}),G:new oe({type:Oe,default:()=>new Oe,expected:!0}),B:new oe({type:Oe,default:()=>new Oe,expected:!0}),A:new oe({type:Oe,default:()=>new Oe(1)}),H:new oe({type:Oe,default:()=>new Oe,ignored:!0}),S:new oe({type:Oe,default:()=>new Oe,ignored:!0}),V:new oe({type:Oe,default:()=>new Oe,ignored:!0})};static grammar=this.createGrammar();static linearToSRGB(e){return e<=0?0:e>=1?1:e<.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055}static sRGBtoLinear(e){return e<=0?0:e>=1?1:e<.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}static getWhite(){return new He({R:1,G:1,B:1})}static createGrammar(){return $e.createEntityGrammar(this,!1)}static getLinearColorFromHexGrammar(){return Me.regArray(new RegExp("#("+$e.Regex.HexDigit.source+"{2})("+$e.Regex.HexDigit.source+"{2})("+$e.Regex.HexDigit.source+"{2})("+$e.Regex.HexDigit.source+"{2})?")).map((([e,t,i,r,s])=>new this({R:parseInt(t,16)/255,G:parseInt(i,16)/255,B:parseInt(r,16)/255,A:parseInt(s??"FF",16)/255})))}static getLinearColorRGBListGrammar(){return Me.seq(Me.numberByte,$e.commaSeparation,Me.numberByte,$e.commaSeparation,Me.numberByte).map((([e,t,i,r,s])=>new this({R:e/255,G:i/255,B:s/255,A:1})))}static getLinearColorRGBGrammar(){return Me.seq(Me.reg(/rgb\s*\(\s*/),this.getLinearColorRGBListGrammar(),Me.reg(/\s*\)/)).map((([e,t,i])=>t))}static getLinearColorRGBAGrammar(){return Me.seq(Me.reg(/rgba\s*\(\s*/),this.getLinearColorRGBListGrammar(),Me.reg(/\s*\)/)).map((([e,t,i])=>t))}static getLinearColorFromAnyFormat(){return Me.alt(this.getLinearColorFromHexGrammar(),this.getLinearColorRGBAGrammar(),this.getLinearColorRGBGrammar(),this.getLinearColorRGBListGrammar())}constructor(e){e instanceof Array&&(e={R:e[0]??0,G:e[1]??0,B:e[2]??0,A:e[3]??1}),super(e),this.R,this.G,this.B,this.A,this.H,this.S,this.V,this.#A()}#A(){const e=this.R.value,t=this.G.value,i=this.B.value;if(ue.approximatelyEqual(e,t)&&ue.approximatelyEqual(e,i)&&ue.approximatelyEqual(t,i))return this.S.value=0,void(this.V.value=e);const r=Math.max(e,t,i),s=Math.min(e,t,i),n=r-s;let a;switch(r){case s:a=0;break;case e:a=(t-i)/n+(te.toString(16).toUpperCase().padStart(2,"0"))).join("")}toSRGBAString(){return this.toSRGBA().map((e=>e.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(255*this.R.value)<<24)+(Math.round(255*this.G.value)<<16)+(Math.round(255*this.B.value)<<8)+Math.round(255*this.A.value)}setFromRGBANumber(e){this.A.value=(255&e)/255,this.B.value=(e>>8&255)/255,this.G.value=(e>>16&255)/255,this.R.value=(e>>24&255)/255,this.#A()}setFromSRGBANumber(e){this.A.value=(255&e)/255,this.B.value=He.sRGBtoLinear((e>>8&255)/255),this.G.value=He.sRGBtoLinear((e>>16&255)/255),this.R.value=He.sRGBtoLinear((e>>24&255)/255),this.#A()}toArray(){return[this.R.value,this.G.value,this.B.value,this.A.value]}toString(){return ue.printLinearColor(this)}}class De{static arrayPin=H``;static branchNode=H``;static breakStruct=H``;static cast=H``;static close=H``;static convert=H``;static correct=H``;static delegate=H``;static doN=H``;static doOnce=H``;static enum=H``;static event=H``;static execPin=H``;static expandIcon=H``;static flipflop=H``;static forEachLoop=H``;static functionSymbol=H``;static gamepad=H``;static genericPin=H``;static keyboard=H``;static loop=H``;static macro=H``;static mapPin=H``;static makeArray=H``;static makeMap=H``;static makeSet=H``;static makeStruct=H``;static metasoundFunction=H``;static mouse=H``;static node=H``;static operationPin=H``;static pcgStackPin=H``;static pcgPin=H``;static pcgParamPin=H``;static pcgSpatialPin=H``;static plusCircle=H``;static questionMark=H``;static referencePin=H``;static reject=H``;static setPin=H``;static select=H``;static sequence=H``;static sound=H``;static spawnActor=H``;static switch=H``;static timer=H``;static touchpad=H``}const Ge=/\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/,Re={A_AccentGrave:"à",Add:"Num +",C_Cedille:"ç",Decimal:"Num .",Divide:"Num /",E_AccentAigu:"é",E_AccentGrave:"è",F1:"F1",F10:"F10",F11:"F11",F12:"F12",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",Gamepad_Special_Left_X:"Touchpad Button X Axis",Gamepad_Special_Left_Y:"Touchpad Button Y Axis",Mouse2D:"Mouse XY 2D-Axis",Multiply:"Num *",Section:"§",Subtract:"Num -",Tilde:"`"};function _e(e){let t;switch(e.getType()){case ne.paths.asyncAction:if(e.ProxyFactoryFunctionName)return ue.formatStringName(e.ProxyFactoryFunctionName);case ne.paths.actorBoundEvent:case ne.paths.componentBoundEvent:return`${ue.formatStringName(e.DelegatePropertyName)} (${e.ComponentPropertyName??"Unknown"})`;case ne.paths.callDelegate:return`Call ${e.DelegateReference?.MemberName??"None"}`;case ne.paths.createDelegate:return"Create Event";case ne.paths.customEvent:if(e.CustomFunctionName)return e.CustomFunctionName;case ne.paths.dynamicCast:return e.TargetType?`Cast To ${e.TargetType?.getName()}`:"Bad cast node";case ne.paths.enumLiteral:return`Literal enum ${e.Enum?.getName()}`;case ne.paths.event:return`Event ${(e.EventReference?.MemberName??"").replace(/^Receive/,"")}`;case ne.paths.executionSequence:return"Sequence";case ne.paths.forEachElementInEnum:return`For Each ${e.Enum?.getName()}`;case ne.paths.forEachLoopWithBreak:return"For Each Loop with Break";case ne.paths.functionEntry:return"UserConstructionScript"===e.FunctionReference?.MemberName?"Construction Script":e.FunctionReference?.MemberName;case ne.paths.functionResult:return"Return Node";case ne.paths.ifThenElse:return"Branch";case ne.paths.makeStruct:if(e.StructType)return`Make ${e.StructType.getName()}`;case ne.paths.materialExpressionComponentMask:{const t=e.getMaterialSubobject();return`Mask ( ${ne.rgba.filter((e=>!0===t[e].get())).map((e=>e+" ")).join("")})`}case ne.paths.materialExpressionConstant:t??=[e.getCustomproperties().find((e=>"Value"==e.PinName))?.DefaultValue];case ne.paths.materialExpressionConstant2Vector:t??=[e.getCustomproperties().find((e=>"X"==e.PinName))?.DefaultValue,e.getCustomproperties().find((e=>"Y"==e.PinName))?.DefaultValue];case ne.paths.materialExpressionConstant3Vector:if(!t){const i=e.getCustomproperties().find((e=>"Constant"==e.PinName))?.DefaultValue;t=[i.X,i.Y,i.Z]}case ne.paths.materialExpressionConstant4Vector:if(!t){const i=e.getCustomproperties().find((e=>"Constant"==e.PinName))?.DefaultValue;t=[i.R,i.G,i.B,i.A].map((e=>e.valueOf()))}if(t.length>0)return t.map((e=>ue.printExponential(e))).reduce(((e,t)=>e+","+t));break;case ne.paths.materialExpressionFunctionInput:{const t=e.getMaterialSubobject();return`Input ${t?.InputName??"In"} (${t?.InputType?.value.match(/^.+?_(\w+)$/)?.[1]??"Vector3"})`}case ne.paths.materialExpressionLogarithm:return"Ln";case ne.paths.materialExpressionLogarithm10:return"Log10";case ne.paths.materialExpressionLogarithm2:return"Log2";case ne.paths.materialExpressionMaterialFunctionCall:const i=e.getMaterialSubobject()?.MaterialFunction;if(i)return i.getName();break;case ne.paths.materialExpressionSquareRoot:return"Sqrt";case ne.paths.metasoundEditorGraphExternalNode:{const t=e.ClassName?.Name;if(t)return"Add"===t?"+":t}case ne.paths.pcgEditorGraphNodeInput:return"Input";case ne.paths.pcgEditorGraphNodeOutput:return"Output";case ne.paths.spawnActorFromClass:let r=e.getCustomproperties().find((e=>"ReturnValue"==e.PinName))?.PinType?.PinSubCategoryObject?.getName();return"Actor"===r&&(r=null),`SpawnActor ${ue.formatStringName(r??"NONE")}`;case ne.paths.switchEnum:return`Switch on ${e.Enum?.getName()??"Enum"}`;case ne.paths.switchInteger:return"Switch on Int";case ne.paths.variableGet:return"";case ne.paths.variableSet:return"SET"}let i=e.switchTarget();if(i)return"E"!==i[0]&&(i=ue.formatStringName(i)),`Switch on ${i}`;if(e.isComment())return e.NodeComment;const r=e.getHIDAttribute();if(r){const t=r.toString();let i=function(e){let t=Re[e];if(t)return t;if(t=ue.numberFromText(e)?.toString(),t)return t;const i=e.match(/NumPad([a-zA-Z]+)/);return i&&(t=ue.numberFromText(i[1]).toString(),t)?"Num "+t:void 0}(t)??ue.formatStringName(t);return e.getClass()===ne.paths.inputDebugKey?i="Debug Key "+i:e.getClass()===ne.paths.getInputAxisKeyValue&&(i="Get "+i),i}if(e.getClass()===ne.paths.macro)return ue.formatStringName(e.MacroGraphReference?.getMacroName());if(e.isMaterial()&&e.getMaterialSubobject()){let t=_e(e.getMaterialSubobject());return t=t.match(/Material Expression (.+)/)?.[1]??t,t}if(e.isPcg()&&e.getPcgSubobject()){let t=e.getPcgSubobject();return t.NodeTitle?t.NodeTitle:_e(t)}const s=e.getSubgraphObject();if(s)return s.Graph.getName();const n=e.getSettingsObject();if(n){if(n.ExportPath.type===ne.paths.pcgHiGenGridSizeSettings)return`Grid Size: ${n.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")??n.HiGenGridSize?.toString().match(/^\w+$/)?.[0]??"256"}`;if(n.BlueprintElementInstance)return ue.formatStringName(n.BlueprintElementType.getName());if(n.Operation){const e=n.Name.match(/PCGMetadata(\w+)Settings_\d+/);if(e)return ue.formatStringName(e[1]+": "+n.Operation)}const e=n.getSubgraphObject();if(e&&e.Graph)return e.Graph.getName()}let a=e.FunctionReference?.MemberName;if(a){const t=e.FunctionReference.MemberParent?.path??"";switch(a){case"AddKey":let e=t.match(Ge);if(e)return`Add Key (${ue.formatStringName(e[1])})`;case"Concat_StrStr":return"Append"}const i=a.match(ne.lineTracePattern);if(i)return"Line Trace"+("Multi"===i[1]?" Multi ":" ")+(""===i[2]?"By Channel":ue.formatStringName(i[2]));switch(t){case ne.paths.blueprintGameplayTagLibrary:case ne.paths.kismetMathLibrary:case ne.paths.slateBlueprintLibrary:case ne.paths.timeManagementBlueprintLibrary:const e=a.match(/[BF]([A-Z]\w+)/);switch(e&&(a=e[1]),a){case"Abs":return"ABS";case"BooleanAND":return"AND";case"BooleanNAND":return"NAND";case"BooleanOR":return"OR";case"Exp":return"e";case"LineTraceSingle":return"Line Trace By Channel";case"Max":case"MaxInt64":return"MAX";case"Min":case"MinInt64":return"MIN";case"Not_PreBool":return"NOT";case"Sin":return"SIN";case"Sqrt":return"SQRT";case"Square":return"^2";case"CrossProduct2D":return"cross";case"Vector4_CrossProduct3":return"cross3";case"DotProduct2D":case"Vector4_DotProduct":return"dot";case"Vector4_DotProduct3":return"dot3"}if(a.startsWith("Add_"))return"+";if(a.startsWith("And_"))return"&";if(a.startsWith("Conv_"))return"";if(a.startsWith("Cross_"))return"cross";if(a.startsWith("Divide_"))return String.fromCharCode(247);if(a.startsWith("Dot_"))return"dot";if(a.startsWith("EqualEqual_"))return"==";if(a.startsWith("Greater_"))return">";if(a.startsWith("GreaterEqual_"))return">=";if(a.startsWith("Less_"))return"<";if(a.startsWith("LessEqual_"))return"<=";if(a.startsWith("Multiply_"))return String.fromCharCode(10799);if(a.startsWith("Not_"))return"~";if(a.startsWith("NotEqual_"))return"!=";if(a.startsWith("Or_"))return"|";if(a.startsWith("Percent_"))return"%";if(a.startsWith("Subtract_"))return"-";if(a.startsWith("Xor_"))return"^";break;case ne.paths.blueprintSetLibrary:{const e=a.match(/Set_(\w+)/);if(e)return ue.formatStringName(e[1]).toUpperCase()}break;case ne.paths.blueprintMapLibrary:{const e=a.match(/Map_(\w+)/);if(e)return ue.formatStringName(e[1]).toUpperCase()}break;case ne.paths.kismetArrayLibrary:{const e=a.match(/Array_(\w+)/);if(e)return e[1].toUpperCase()}}return ue.formatStringName(a)}if(e.OpName){switch(e.OpName){case"Boolean::LogicAnd":return"Logic AND";case"Boolean::LogicEq":return"==";case"Boolean::LogicNEq":return"!=";case"Boolean::LogicNot":return"Logic NOT";case"Boolean::LogicOr":return"Logic OR";case"Matrix::MatrixMultiply":return"Multiply (Matrix * Matrix)";case"Matrix::MatrixVectorMultiply":return"Multiply (Matrix * Vector4)";case"Numeric::Abs":return"Abs";case"Numeric::Add":return"+";case"Numeric::DistancePos":return"Distance";case"Numeric::Mul":return String.fromCharCode(10799)}return ue.formatStringName(e.OpName).replaceAll("::"," ")}return e.FunctionDisplayName?ue.formatStringName(e.FunctionDisplayName):e.ObjectRef?e.ObjectRef.getName():ue.formatStringName(e.getNameAndCounter()[0])}var ze;"undefined"==typeof window?import("crypto").then((e=>ze=e.default)).catch():ze=window.crypto;class Fe extends Ve{static attributes={...super.attributes,value:oe.createValue("")};static grammar=this.createGrammar();static createGrammar(){return $e.guid.map((e=>new this(e)))}static generateGuid(e=!0){let t=new Uint32Array(4);!0===e&&ze.getRandomValues(t);let i="";return t.forEach((e=>{i+=("0".repeat(8)+e.toString(16).toUpperCase()).slice(-8)})),new Fe({value:i})}constructor(e){e||(e=Fe.generateGuid().value),e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class je extends Ve{static attributes={...super.attributes,type:new oe({default:"",serialized:!0}),path:new oe({default:"",serialized:!0}),_full:new oe({ignored:!0})};static quoted=Me.regArray(new RegExp(`'"(${$e.Regex.InsideString.source})"'|'(${$e.Regex.InsideSingleQuotedString.source})'`)).map((([e,t,i])=>t??i));static path=this.quoted.getParser().parser.regexp.source+"|"+$e.Regex.Path.source;static typeReference=Me.reg(new RegExp($e.Regex.Path.source+"|"+$e.symbol.getParser().regexp.source));static fullReferenceGrammar=Me.regArray(new RegExp("("+this.typeReference.getParser().regexp.source+")(?:"+this.quoted.getParser().parser.regexp.source+")")).map((([e,t,...i])=>new this({type:t,path:i.find((e=>e)),_full:e})));static fullReferenceSerializedGrammar=Me.regArray(new RegExp("("+this.typeReference.getParser().regexp.source+")'("+$e.Regex.InsideSingleQuotedString.source+")'")).map((([e,t,...i])=>new this({type:t,path:i.find((e=>e)),_full:e})));static typeReferenceGrammar=this.typeReference.map((e=>new this({type:e,path:"",_full:e})));static grammar=this.createGrammar();constructor(e={}){e.constructor===String&&(e={path:e}),super(e),e._full&&0!==e._full.length||(this._full=`"${this.type+(this.path?`'${this.path}'`:"")}"`),this.type,this.path}static createGrammar(){return Me.alt(Me.seq(Me.str('"'),Me.alt(this.fullReferenceSerializedGrammar,this.typeReferenceGrammar),Me.str('"')).map((([e,t,i])=>(t._full=`"${t._full}"`,t))),this.fullReferenceGrammar,this.typeReferenceGrammar)}static createNoneInstance(){return new je({type:"None",path:""})}getName(e=!1){return ue.getNameFromPath(this.path.replace(/_C$/,""),e)}toString(){return this._full}}class We extends Ve{static attributes={...super.attributes,MemberParent:oe.createType(je),MemberName:oe.createType(String),MemberGuid:oe.createType(Fe)};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this)}constructor(e){super(e),this.MemberParent,this.MemberName,this.MemberGuid}}class Ue extends Ve{static attributes={...super.attributes,value:oe.createValue("")};static attributeConverter={fromAttribute:(e,t)=>new Ue(e),toAttribute:(e,t)=>e.toString()};static grammar=this.createGrammar();static createGrammar(){return $e.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Ke extends Ve{static attributes={...super.attributes,value:new oe({default:0,predicate:e=>e%1==0&&e>1<<31&&e<-(1<<31)})};static grammar=this.createGrammar();static createGrammar(){return Me.numberInteger.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),e.value=Math.floor(e.value),-0===e.value&&(e.value=0),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toString()}}class Ye extends Ve{static attributes={...super.attributes,MacroGraph:new oe({type:je,default:()=>new je}),GraphBlueprint:new oe({type:je,default:()=>new je}),GraphGuid:new oe({type:Fe,default:()=>new Fe})};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this)}constructor(e){super(e),this.MacroGraph,this.GraphBlueprint,this.GuidEntity}getMacroName(){const e=this.MacroGraph.path.search(":");return this.MacroGraph.path.substring(e+1)}}const Xe={[ne.paths.niagaraBool]:n``,[ne.paths.niagaraDataInterfaceVolumeTexture]:n``,[ne.paths.niagaraFloat]:n``,[ne.paths.niagaraMatrix]:n``,[ne.paths.niagaraNumeric]:n``,[ne.paths.niagaraPosition]:n``,[ne.paths.quat4f]:n``,[ne.paths.rotator]:n``,[ne.paths.transform]:n``,[ne.paths.vector]:n``,[ne.paths.vector3f]:n``,[ne.paths.vector4f]:n``,Any:n``,"Any[]":n``,audio:n``,blue:n``,bool:n``,byte:n``,class:n``,default:n``,delegate:n``,enum:n``,exec:n``,float:n``,green:n``,int:n``,int32:n``,int64:n``,interface:n``,name:n``,object:n``,Param:n``,"Param[]":n``,Point:n``,"Point[]":n``,real:n``,red:n``,string:n``,struct:n``,Surface:n``,"Surface[]":n``,text:n``,time:n``,Volume:n``,"Volume[]":n``,wildcard:n``},Ze=n``;class qe extends Ke{static attributes={...super.attributes,value:new oe({...super.attributes.value,predicate:e=>e%1==0&&e>=0&&e<256})};static grammar=this.createGrammar();static createGrammar(){return Me.numberByte.map((e=>new this(e)))}constructor(e=0){super(e)}}class Qe extends Ve{static attributes={...super.attributes,value:oe.createValue("")};static grammar=this.createGrammar();static createGrammar(){return $e.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class Je extends Qe{static grammar=this.createGrammar();static createGrammar(){return $e.symbol.map((e=>new this(e)))}}class et extends Je{static grammar=this.createGrammar();static createGrammar(){return Me.reg($e.Regex.InsideString).map((e=>new this(e)))}}class tt extends Ve{static attributes={...super.attributes,value:oe.createValue(""),lookbehind:new oe({...super.attributes.lookbehind,default:"INVTEXT"})};static grammar=this.createGrammar();static createGrammar(){return Me.alt(Me.seq(Me.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)),$e.grammarFor(this.attributes.value),Me.reg(/\s*\)/)).map((([e,t,i])=>t)),Me.reg(new RegExp(this.attributes.lookbehind.default)).map((()=>""))).map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}toString(){return this.value}}class it extends Ve{static attributes={...super.attributes,namespace:oe.createValue(""),key:oe.createValue(""),value:oe.createValue(""),lookbehind:new oe({...super.attributes.lookbehind,default:"NSLOCTEXT"})};static grammar=this.createGrammar();static createGrammar(){return Me.regArray(new RegExp(String.raw`${this.attributes.lookbehind.default}\s*\(`+String.raw`\s*"(${$e.Regex.InsideString.source})"\s*,`+String.raw`\s*"(${$e.Regex.InsideString.source})"\s*,`+String.raw`\s*"(${$e.Regex.InsideString.source})"\s*`+String.raw`(?:,\s+)?`+String.raw`\)`,"m")).map((e=>new this({namespace:ue.unescapeString(e[1]),key:ue.unescapeString(e[2]),value:ue.unescapeString(e[3])})))}constructor(e){super(e),this.namespace,this.key,this.value}toString(){return ue.capitalFirstLetter(this.value)}}class rt extends Ve{static attributes={...super.attributes,value:new oe({type:[new ce(String,it,tt,rt)],default:[]}),lookbehind:new oe({...super.attributes.lookbehind,default:new ce("LOCGEN_FORMAT_NAMED","LOCGEN_FORMAT_ORDERED")})};static grammar=this.createGrammar();static createGrammar(){return Me.seq(Me.reg(new RegExp(`(${this.attributes.lookbehind.default.values.reduce(((e,t)=>e+"|"+t))})\\s*`),1),$e.grammarFor(this.attributes.value)).map((([e,t])=>new this({value:t,lookbehind:e})))}constructor(e){super(e),this.value}toString(){const e=this.value?.[0]?.toString();if(!e)return"";const t=this.value.slice(1).map((e=>e.toString()));return"LOCGEN_FORMAT_NAMED"==this.lookbehind?e.replaceAll(/\{([a-zA-Z]\w*)\}/g,((e,i)=>{const r=t.indexOf(i)+1;return r>0&&r{const r=Number(i);return re>=-(1n<<63n)&&e<1n<<63n})};static grammar=this.createGrammar();static createGrammar(){return Me.numberBigInteger.map((e=>new this(e)))}constructor(e=0){e.constructor!==Object&&(e={value:e}),-0===e.value&&(e.value=0n),super(e),this.value}valueOf(){return this.value}toString(){return this.value.toString()}}class nt extends Ve{static attributes={...super.attributes,value:new oe({default:""})};static grammar=this.createGrammar();static createGrammar(){return $e.symbol.map((e=>new this(e)))}constructor(e){e.constructor!==Object&&(e={value:e}),super(e),this.value}valueOf(){return this.value}toString(){return this.value}}class at extends Ve{static attributes={...super.attributes,objectName:oe.createType(nt),pinGuid:oe.createType(Fe)};static grammar=this.createGrammar();static createGrammar(){return Me.seq(nt.grammar,Me.whitespace,Fe.grammar).map((([e,t,i])=>new this({objectName:e,pinGuid:i})))}constructor(e){super(e),this.objectName,this.pinGuid}}class ot extends Ve{static attributes={...super.attributes,PinCategory:oe.createValue(""),PinSubCategory:oe.createValue(""),PinSubCategoryObject:new oe({type:je,default:()=>je.createNoneInstance()}),PinSubCategoryMemberReference:new oe({type:We,default:null}),PinValueType:new oe({type:ot,default:null}),ContainerType:oe.createType(nt),bIsReference:oe.createValue(!1),bIsConst:oe.createValue(!1),bIsWeakPointer:oe.createValue(!1),bIsUObjectWrapper:oe.createValue(!1),bSerializeAsSinglePrecisionFloat:oe.createValue(!1)};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this)}constructor(e={},t=!1){super(e,t),this.PinCategory,this.PinSubCategory,this.PinSubCategoryObject,this.PinSubCategoryMemberReference,this.PinValueType,this.ContainerType,this.bIsReference,this.bIsConst,this.bIsWeakPointer,this.bIsUObjectWrapper,this.bIsUObjectWrapper,this.bSerializeAsSinglePrecisionFloat}copyTypeFrom(e){this.PinCategory=e.PinCategory,this.PinSubCategory=e.PinSubCategory,this.PinSubCategoryObject=e.PinSubCategoryObject,this.PinSubCategoryMemberReference=e.PinSubCategoryMemberReference,this.PinValueType=e.PinValueType,this.ContainerType=e.ContainerType,this.bIsReference=e.bIsReference,this.bIsConst=e.bIsConst,this.bIsWeakPointer=e.bIsWeakPointer,this.bIsUObjectWrapper=e.bIsUObjectWrapper,this.bSerializeAsSinglePrecisionFloat=e.bSerializeAsSinglePrecisionFloat}}class lt extends Ve{static attributes={...super.attributes,X:new oe({default:0,expected:!0}),Y:new oe({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this,!1)}constructor(e){super(e),this.X,this.Y}toArray(){return[this.X,this.Y]}}class ct extends lt{static grammar=this.createGrammar();static createGrammar(){return Me.alt(Me.regArray(new RegExp(/X\s*=\s*/.source+"(?"+Me.number.getParser().parser.regexp.source+")\\s+"+/Y\s*=\s*/.source+"(?"+Me.number.getParser().parser.regexp.source+")")).map((({groups:{x:e,y:t}})=>new this({X:Number(e),Y:Number(t)}))),lt.grammar)}}class ut extends Ve{static attributes={...super.attributes,R:new oe({default:0,expected:!0}),P:new oe({default:0,expected:!0}),Y:new oe({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this,!1)}constructor(e){super(e),this.R,this.P,this.Y}getRoll(){return this.R}getPitch(){return this.P}getYaw(){return this.Y}}class ht extends ut{static grammar=this.createGrammar();static createGrammar(){const e=Me.number.getParser().parser.regexp.source;return Me.alt(Me.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")")).map((([e,t,i,r])=>new this({R:Number(r),P:Number(t),Y:Number(i)}))),ut.grammar)}}class pt extends lt{static grammar=this.createGrammar();static createGrammar(){const e=Me.number.getParser().parser.regexp.source;return Me.alt(Me.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")")).map((([e,t,i])=>new this({X:Number(t),Y:Number(i)}))),lt.grammar)}}class dt extends Ve{static attributes={...super.attributes,X:new oe({default:0,expected:!0}),Y:new oe({default:0,expected:!0}),Z:new oe({default:0,expected:!0}),W:new oe({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(dt,!1)}constructor(e){super(e),this.X,this.Y,this.Z,this.W}toArray(){return[this.X,this.Y,this.Z,this.W]}}class mt extends dt{static grammar=this.createGrammar();static createGrammar(){const e=Me.number.getParser().parser.regexp.source;return Me.alt(Me.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")")).map((([e,t,i,r,s])=>new this({X:Number(t),Y:Number(i),Z:Number(r),W:Number(s)}))),dt.grammar)}}class gt extends Ve{static attributes={...super.attributes,X:new oe({default:0,expected:!0}),Y:new oe({default:0,expected:!0}),Z:new oe({default:0,expected:!0})};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(gt,!1)}constructor(e){super(e),this.X,this.Y,this.Z}toArray(){return[this.X,this.Y,this.Z]}}class bt extends gt{static grammar=this.createGrammar();static createGrammar(){const e=Me.number.getParser().parser.regexp.source;return Me.alt(Me.regArray(new RegExp("("+e+")\\s*,\\s*("+e+")\\s*,\\s*("+e+")")).map((([e,t,i,r])=>new this({X:Number(t),Y:Number(i),Z:Number(r)}))),gt.grammar)}}class vt extends Ve{static#L={[ne.paths.linearColor]:He,[ne.paths.rotator]:ut,[ne.paths.vector]:gt,[ne.paths.vector2D]:lt,[ne.paths.vector4f]:dt,bool:Boolean,byte:qe,enum:Je,exec:String,int:Ke,int64:st,name:String,real:Number,string:String};static#M={enum:et,rg:ct,[ne.paths.rotator]:ht,[ne.paths.vector]:bt,[ne.paths.vector2D]:pt,[ne.paths.vector3f]:bt,[ne.paths.vector4f]:mt};static attributes={...super.attributes,lookbehind:new oe({default:"Pin",ignored:!0}),objectEntity:new oe({ignored:!0}),pinIndex:new oe({type:Number,ignored:!0}),PinId:new oe({type:Fe,default:()=>new Fe}),PinName:oe.createValue(""),PinFriendlyName:oe.createType(new ce(it,rt,tt,String)),PinToolTip:oe.createType(String),Direction:oe.createType(String),PinType:new oe({type:ot,default:()=>new ot,inlined:!0}),LinkedTo:oe.createType([at]),SubPins:oe.createType([at]),ParentPin:oe.createType(at),DefaultValue:new oe({type:new ae((e=>e.getEntityType(!0)??String)),serialized:!0}),AutogeneratedDefaultValue:oe.createType(String),DefaultObject:oe.createType(je),PersistentGuid:oe.createType(Fe),bHidden:oe.createValue(!1),bNotConnectable:oe.createValue(!1),bDefaultValueIsReadOnly:oe.createValue(!1),bDefaultValueIsIgnored:oe.createValue(!1),bAdvancedView:oe.createValue(!1),bOrphanedPin:oe.createValue(!1)};static grammar=this.createGrammar();#I=!1;set recomputesNodeTitleOnChange(e){this.#I=e}get recomputesNodeTitleOnChange(){return this.#I}static createGrammar(){return $e.createEntityGrammar(this)}constructor(e={},t=!1){super(e,t),this.objectEntity,this.pinIndex,this.PinId,this.PinName,this.PinFriendlyName,this.PinToolTip,this.Direction,this.PinType,this.LinkedTo,this.DefaultValue,this.AutogeneratedDefaultValue,this.DefaultObject,this.PersistentGuid,this.bHidden,this.bNotConnectable,this.bDefaultValueIsReadOnly,this.bDefaultValueIsIgnored,this.bAdvancedView,this.bOrphanedPin}static fromLegacyObject(e){return new vt(e,!0)}getType(){const e=this.PinType.PinCategory.toLocaleLowerCase();if("struct"===e||"class"===e||"object"===e||"type"===e)return this.PinType.PinSubCategoryObject.path;if(this.isEnum())return"enum";if(this.objectEntity?.isPcg()){const e=this.objectEntity.getPcgSubobject(),t=this.isInput()?e.InputPins?.[this.pinIndex]:e.OutputPins?.[this.pinIndex];if(t){const i=e[ne.subObjectAttributeNameFromReference(t,!0)];let r=i.Properties?.AllowedTypes?.toString()??"";if(""==r&&(r=this.PinType.PinCategory??"",""==r&&(r="Any")),r)return!1!==i.Properties.bAllowMultipleData&&!1!==i.Properties.bAllowMultipleConnections&&(r+="[]"),r}}if("optional"===e)switch(this.PinType.PinSubCategory){case"red":return"real";case"rg":return"rg";case"rgb":return ne.paths.vector;case"rgba":return ne.paths.linearColor;default:return this.PinType.PinSubCategory}return e}getEntityType(e=!1){const t=this.getType(),i=vt.#L[t],r=vt.#M[t];return e&&void 0!==r?r:i}pinTitle(){return function(e){let t,i=e.PinFriendlyName?e.PinFriendlyName.toString():ue.formatStringName(e.PinName??"");return e.PinToolTip&&(t=e.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))&&t[1].toLowerCase()===i.toLowerCase()?t[1]:i}(this)}copyTypeFrom(e){this.PinType.PinCategory=e.PinType.PinCategory,this.PinType.PinSubCategory=e.PinType.PinSubCategory,this.PinType.PinSubCategoryObject=e.PinType.PinSubCategoryObject,this.PinType.PinSubCategoryMemberReference=e.PinType.PinSubCategoryMemberReference,this.PinType.PinValueType=e.PinType.PinValueType,this.PinType.ContainerType=e.PinType.ContainerType,this.PinType.bIsReference=e.PinType.bIsReference,this.PinType.bIsConst=e.PinType.bIsConst,this.PinType.bIsWeakPointer=e.PinType.bIsWeakPointer,this.PinType.bIsUObjectWrapper=e.PinType.bIsUObjectWrapper,this.PinType.bSerializeAsSinglePrecisionFloat=e.PinType.bSerializeAsSinglePrecisionFloat}getDefaultValue(e=!1){return void 0===this.DefaultValue&&e&&(this.DefaultValue=new(this.getEntityType(!0))),this.DefaultValue}isEnum(){const e=this.PinType.PinSubCategoryObject.type;return e===ne.paths.enum||e===ne.paths.userDefinedEnum||"enum"===e.toLowerCase()}isExecution(){return"exec"===this.PinType.PinCategory}isHidden(){return this.bHidden}isInput(){return!this.bHidden&&"EGPD_Output"!=this.Direction}isOutput(){return!this.bHidden&&"EGPD_Output"==this.Direction}isLinked(){return this.LinkedTo?.length>0??!1}linkTo(e,t){const i=this.LinkedTo?.some((i=>i.objectName.toString()==e&&i.pinGuid.valueOf()==t.PinId.valueOf()));return!i&&((this.LinkedTo??=[]).push(new at({objectName:e,pinGuid:t.PinId})),!0)}unlinkFrom(e,t){const i=this.LinkedTo?.findIndex((i=>i.objectName.toString()==e&&i.pinGuid.valueOf()==t.PinId.valueOf()));return i>=0&&(this.LinkedTo.splice(i,1),0===this.LinkedTo.length&&void 0===vt.attributes.LinkedTo.default&&(this.LinkedTo=void 0),!0)}getSubCategory(){return this.PinType.PinSubCategoryObject.path}pinColor(){return function(e){if("mask"==e.PinType.PinCategory){const t=Xe[e.PinType.PinSubCategory];if(t)return t}else if("optional"==e.PinType.PinCategory)return Ze;return Xe[e.getType()]??Xe[e.PinType.PinCategory.toLowerCase()]??Xe.default}(this)}}class yt extends Ve{static attributes={...super.attributes,ScriptVariable:oe.createType(je),OriginalChangeId:oe.createType(Fe)};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this)}constructor(e={},t=!1){super(e,t),this.ScriptVariable,this.OriginalChangeId}}class ft extends vt{static grammar=this.createGrammar();static createGrammar(){return Me.seq(Me.reg(new RegExp(`(${$e.Regex.Symbol.source})\\s*\\(\\s*`),1),$e.createAttributeGrammar(this).sepBy($e.commaSeparation),Me.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{e??="";let r={};return e.length&&(r.lookbehind=e),t.forEach((e=>e(r))),new this(r)}))}constructor(e={}){super(e,!0)}}class wt extends Ve{static attributes={...super.attributes,MemberScope:oe.createType(String),MemberName:oe.createValue(""),MemberGuid:oe.createType(Fe),bSelfContext:oe.createType(Boolean)};static grammar=this.createGrammar();static createGrammar(){return $e.createEntityGrammar(this)}constructor(e){super(e),this.MemberName,this.GuidEntity,this.bSelfContext}}const St=e=>e.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0)-"A".charCodeAt(0);class Et extends Ve{static attributes={...super.attributes,isExported:new oe({type:Boolean,ignored:!0}),Class:oe.createType(je),Name:oe.createType(String),Archetype:oe.createType(je),ExportPath:oe.createType(je),ObjectRef:oe.createType(je),BlueprintElementType:oe.createType(je),BlueprintElementInstance:oe.createType(je),PinTags:new oe({type:[null],inlined:!0}),PinNames:new oe({type:[String],inlined:!0}),AxisKey:oe.createType(Qe),InputAxisKey:oe.createType(Qe),InputName:oe.createType(String),InputType:oe.createType(Qe),NumAdditionalInputs:oe.createType(Number),bIsPureFunc:oe.createType(Boolean),bIsConstFunc:oe.createType(Boolean),bIsCaseSensitive:oe.createType(Boolean),VariableReference:oe.createType(wt),SelfContextInfo:oe.createType(Qe),DelegatePropertyName:oe.createType(String),DelegateOwnerClass:oe.createType(je),ComponentPropertyName:oe.createType(String),EventReference:oe.createType(We),FunctionReference:oe.createType(We),FunctionScript:oe.createType(je),CustomFunctionName:oe.createType(String),TargetType:oe.createType(je),MacroGraphReference:oe.createType(Ye),Enum:oe.createType(je),EnumEntries:new oe({type:[String],inlined:!0}),InputKey:oe.createType(Qe),OpName:oe.createType(String),CachedChangeId:oe.createType(Fe),FunctionDisplayName:oe.createType(String),AddedPins:new oe({type:[ft],default:()=>[],inlined:!0,silent:!0}),ChangeId:oe.createType(Fe),MaterialFunction:oe.createType(je),bOverrideFunction:oe.createType(Boolean),bInternalEvent:oe.createType(Boolean),bConsumeInput:oe.createType(Boolean),bExecuteWhenPaused:oe.createType(Boolean),bOverrideParentBinding:oe.createType(Boolean),bControl:oe.createType(Boolean),bAlt:oe.createType(Boolean),bShift:oe.createType(Boolean),bCommand:oe.createType(Boolean),CommentColor:oe.createType(He),bCommentBubbleVisible_InDetailsPanel:oe.createType(Boolean),bColorCommentBubble:oe.createType(Boolean),ProxyFactoryFunctionName:oe.createType(String),ProxyFactoryClass:oe.createType(je),ProxyClass:oe.createType(je),StructType:oe.createType(je),MaterialExpression:oe.createType(je),MaterialExpressionComment:oe.createType(je),MoveMode:oe.createType(Qe),TimelineName:oe.createType(String),TimelineGuid:oe.createType(Fe),SizeX:oe.createType(new le(Ke)),SizeY:oe.createType(new le(Ke)),Text:oe.createType(new le(String)),MaterialExpressionEditorX:oe.createType(new le(Ke)),MaterialExpressionEditorY:oe.createType(new le(Ke)),NodeTitle:oe.createType(String),NodeTitleColor:oe.createType(He),PositionX:oe.createType(new le(Ke)),PositionY:oe.createType(new le(Ke)),SettingsInterface:oe.createType(je),PCGNode:oe.createType(je),HiGenGridSize:oe.createType(Qe),Operation:oe.createType(Qe),NodePosX:oe.createType(Ke),NodePosY:oe.createType(Ke),NodeHeight:oe.createType(Ke),NodeWidth:oe.createType(Ke),Graph:oe.createType(je),SubgraphInstance:oe.createType(String),InputPins:new oe({type:[je],inlined:!0}),OutputPins:new oe({type:[je],inlined:!0}),bExposeToLibrary:oe.createType(Boolean),bCanRenameNode:oe.createType(Boolean),bCommentBubblePinned:oe.createType(Boolean),bCommentBubbleVisible:oe.createType(Boolean),NodeComment:oe.createType(String),AdvancedPinDisplay:oe.createType(Ue),DelegateReference:oe.createType(wt),EnabledState:oe.createType(Ue),NodeGuid:oe.createType(Fe),ErrorType:oe.createType(Ke),ErrorMsg:oe.createType(String),ScriptVariables:new oe({type:[yt],inlined:!0}),Node:oe.createType(new le(je)),ExportedNodes:oe.createType(String),CustomProperties:oe.createType([new ce(vt,ft)])};static nameRegex=/^(\w+?)(?:_(\d+))?$/;static customPropertyGrammar=Me.seq(Me.reg(/CustomProperties\s+/),$e.grammarFor(void 0,this.attributes.CustomProperties.type[0])).map((([e,t])=>e=>{e.CustomProperties||(e.CustomProperties=[]),e.CustomProperties.push(t)}));static inlinedArrayEntryGrammar=Me.seq(Me.alt($e.symbolQuoted.map((e=>[e,!0])),$e.symbol.map((e=>[e,!1]))),Me.reg(new RegExp("\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*"),1).map(Number)).chain((([[e,t],i])=>$e.grammarFor(this.attributes[e]).map((r=>s=>{(s[e]??=[])[i]=r,ue.objectSet(s,["attributes",e,"quoted"],t),this.attributes[e]?.inlined||(s.attributes||Ve.defineAttributes(s,{}),ue.objectSet(s,["attributes",e,"type"],[r.constructor]),ue.objectSet(s,["attributes",e,"inlined"],!0))}))));static grammar=this.createGrammar();static createSubObjectGrammar(){return Me.lazy((()=>this.grammar)).map((e=>t=>t[ne.subObjectAttributeNameFromEntity(e)]=e))}static createGrammar(){return Me.seq(Me.reg(/Begin +Object/),Me.seq(Me.whitespace,Me.alt(this.createSubObjectGrammar(),this.customPropertyGrammar,$e.createAttributeGrammar(this,Me.reg($e.Regex.MultipleWordsSymbols)),$e.createAttributeGrammar(this,$e.attributeNameQuoted,void 0,((e,t,i)=>ue.objectSet(e,["attributes",...t,"quoted"],!0))),this.inlinedArrayEntryGrammar)).map((([e,t])=>t)).many(),Me.reg(/\s+End +Object/)).map((([e,t,i])=>{const r={};return t.forEach((e=>e(r))),new this(r)}))}static getMultipleObjectsGrammar(){return Me.seq(Me.whitespaceOpt,this.grammar,Me.seq(Me.whitespace,this.grammar).map((([e,t])=>t)).many(),Me.whitespaceOpt).map((([e,t,i,r])=>[t,...i]))}#B;constructor(e={},t=!1){if("NodePosX"in e!="NodePosY"in e){const t=Object.entries(e),[i,r]="NodePosX"in e?["NodePosY",Object.keys(e).indexOf("NodePosX")+1]:["NodePosX",Object.keys(e).indexOf("NodePosY")],s=[i,new(oe.getAttribute(e,i,"type",Et))];t.splice(r,0,s),e=Object.fromEntries(t)}super(e,t),this.R,this.G,this.B,this.A,this.CustomProperties,this.bIsPureFunc,this.isExported,this.ComponentPropertyName,this.EventReference,this.FunctionReference,this.AdvancedPinDisplay,this.EnabledState,this.NodeHeight,this.NodePosX,this.NodePosY,this.NodeWidth,this.CommentColor,this.NodeTitleColor,this.MacroGraphReference,this.MaterialExpressionEditorX,this.MaterialExpressionEditorY,this.SizeX,this.SizeY,this.Text,this.PositionX,this.PositionY,this.Node,this.PinTags,this.NumAdditionalInputs,this.InputPins,this.OutputPins,this.Archetype,this.BlueprintElementInstance,this.BlueprintElementType,this.Class,this.Enum,this.ExportPath,this.FunctionScript,this.Graph,this.MaterialExpression,this.MaterialExpressionComment,this.MaterialFunction,this.ObjectRef,this.PCGNode,this.SettingsInterface,this.StructType,this.TargetType,this.ScriptVariables,this.EnumEntries,this.PinNames,this.CustomFunctionName,this.DelegatePropertyName,this.ExportedNodes,this.FunctionDisplayName,this.InputName,this.Name,this.NodeComment,this.NodeTitle,this.Operation,this.OpName,this.ProxyFactoryFunctionName,this.SubgraphInstance,this.Text,this.AxisKey,this.HiGenGridSize,this.InputAxisKey,this.InputKey,this.InputType,this.AddedPins,this.DelegateReference,this.VariableReference,this.Pins instanceof Array&&this.Pins.forEach((e=>{const t=this[ne.subObjectAttributeNameFromReference(e,!0)];if(t){const e=vt.fromLegacyObject(t);e.LinkedTo=[],this.getCustomproperties(!0).push(e),ue.objectSet(this,["attributes","CustomProperties","ignored"],!0)}}));const i=this.getMaterialSubobject();if(i){const e=i;if(void 0!==e.SizeX&&(e.SizeX.getter=()=>this.NodeWidth),e.SizeY&&(e.SizeY.getter=()=>this.NodeHeight),e.Text&&(e.Text.getter=()=>this.NodeComment),e.MaterialExpressionEditorX&&(e.MaterialExpressionEditorX.getter=()=>this.NodePosX),e.MaterialExpressionEditorY&&(e.MaterialExpressionEditorY.getter=()=>this.NodePosY),this.getType()===ne.paths.materialExpressionComponentMask){const t=ne.rgba.map((e=>this.getPinEntities().find((t=>t.PinName===e&&(t.recomputesNodeTitleOnChange=!0)))));e.R=new le(Boolean,(()=>t[0].DefaultValue)),e.G=new le(Boolean,(()=>t[1].DefaultValue)),e.B=new le(Boolean,(()=>t[2].DefaultValue)),e.A=new le(Boolean,(()=>t[3].DefaultValue)),ue.objectSet(e,["attributes","R","default"],!1),ue.objectSet(e,["attributes","R","silent"],!0),ue.objectSet(e,["attributes","G","default"],!1),ue.objectSet(e,["attributes","G","silent"],!0),ue.objectSet(e,["attributes","B","default"],!1),ue.objectSet(e,["attributes","B","silent"],!0),ue.objectSet(e,["attributes","A","default"],!1),ue.objectSet(e,["attributes","A","silent"],!0),e._keys=[...ne.rgba,...Object.keys(e).filter((e=>!ne.rgba.includes(e)))]}}const r=this.getPcgSubobject();r&&(r.PositionX&&(r.PositionX.getter=()=>this.NodePosX),r.PositionY&&(r.PositionY.getter=()=>this.NodePosY),r.getSubobjects().forEach((e=>{if(void 0!==e.Node){const t=e.Node.get();t.type===this.PCGNode.type&&t.path===`${this.Name}.${this.PCGNode.path}`&&(e.Node.getter=()=>new je({type:this.PCGNode.type,path:`${this.Name}.${this.PCGNode.path}`}))}})));let s=0,n=0;this.CustomProperties?.forEach(((e,t)=>{e.objectEntity=this,e.pinIndex=e.isInput()?s++:e.isOutput()?n++:t}))}getClass(){if(!this.#B&&(this.#B=(this.Class?.path?this.Class.path:this.Class?.type)??(this.ExportPath?.path?this.ExportPath.path:this.ExportPath?.type)??"",this.#B&&!this.#B.startsWith("/"))){let e=Object.values(ne.paths).find((e=>e.endsWith("."+this.#B)));e&&(this.#B=e)}return this.#B}getType(){let e=this.getClass();return this.MacroGraphReference?.MacroGraph?.path?this.MacroGraphReference.MacroGraph.path:this.MaterialExpression?this.MaterialExpression.type:e}getObjectName(e=!1){return e?this.getNameAndCounter()[0]:this.Name}getNameAndCounter(){const e=this.getObjectName().match(Et.nameRegex);return e?[e[1]??"",parseInt(e[2]??"0")]:["",0]}getCounter(){return this.getNameAndCounter()[1]}getNodeWidth(){return this.NodeWidth??this.isComment()?ne.defaultCommentWidth:void 0}setNodeWidth(e){this.NodeWidth||(this.NodeWidth=new Ke),this.NodeWidth.value=e}getNodeHeight(){return this.NodeHeight??this.isComment()?ne.defaultCommentHeight:void 0}setNodeHeight(e){this.NodeHeight||(this.NodeHeight=new Ke),this.NodeHeight.value=e}getNodePosX(){return this.NodePosX?.value??0}setNodePosX(e){this.NodePosX||(this.NodePosX=new Ke),this.NodePosX.value=Math.round(e)}getNodePosY(){return this.NodePosY?.value??0}setNodePosY(e){this.NodePosY||(this.NodePosY=new Ke),this.NodePosY.value=Math.round(e)}getCustomproperties(e=!1){return e&&!this.CustomProperties&&(this.CustomProperties=[]),this.CustomProperties??[]}getPinEntities(){return this.getCustomproperties().filter((e=>e.constructor===vt))}getSubobjects(){return Object.keys(this).filter((e=>e.startsWith(ne.subObjectAttributeNamePrefix))).flatMap((e=>[this[e],...this[e].getSubobjects()]))}switchTarget(){const e=this.getClass().match(ne.switchTargetPattern);if(e)return e[1]}isEvent(){switch(this.getClass()){case ne.paths.actorBoundEvent:case ne.paths.componentBoundEvent:case ne.paths.customEvent:case ne.paths.event:case ne.paths.inputAxisKeyEvent:case ne.paths.inputVectorAxisEvent:return!0}return!1}isComment(){switch(this.getClass()){case ne.paths.comment:case ne.paths.materialGraphNodeComment:return!0}return!1}isMaterial(){return this.getClass()===ne.paths.materialGraphNode}getMaterialSubobject(){const e=this.MaterialExpression??this.MaterialExpressionComment;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}isPcg(){return this.getClass()===ne.paths.pcgEditorGraphNode||this.getPcgSubobject()}isNiagara(){return this.Class&&(this.Class.type?this.Class.type:this.Class.path)?.startsWith("/Script/NiagaraEditor.")}getPcgSubobject(){const e=this.PCGNode;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}getSettingsObject(){const e=this.SettingsInterface;return e?this[ne.subObjectAttributeNameFromReference(e,!0)]:null}getSubgraphObject(){const e=this.SubgraphInstance;return e?this[ne.subObjectAttributeNameFromName(e)]:null}isDevelopmentOnly(){const e=this.getClass();return"DevelopmentOnly"===this.EnabledState?.toString()||e.includes("Debug",Math.max(0,e.lastIndexOf(".")))}getHIDAttribute(){return this.InputKey??this.AxisKey??this.InputAxisKey}getDelegatePin(){return this.getCustomproperties().find((e=>"delegate"===e.PinType.PinCategory))}nodeColor(){return function(e){switch(e.getType()){case ne.paths.materialExpressionConstant2Vector:case ne.paths.materialExpressionConstant3Vector:case ne.paths.materialExpressionConstant4Vector:return ne.nodeColors.yellow;case ne.paths.makeStruct:return ne.nodeColors.darkBlue;case ne.paths.materialExpressionMaterialFunctionCall:return ne.nodeColors.blue;case ne.paths.materialExpressionFunctionInput:return ne.nodeColors.red;case ne.paths.materialExpressionTextureSample:return ne.nodeColors.darkTurquoise;case ne.paths.materialExpressionTextureCoordinate:case ne.paths.pcgEditorGraphNodeInput:case ne.paths.pcgEditorGraphNodeOutput:return ne.nodeColors.red}switch(e.getClass()){case ne.paths.callFunction:return e.bIsPureFunc?ne.nodeColors.green:ne.nodeColors.blue;case ne.paths.niagaraNodeFunctionCall:return ne.nodeColors.darkerBlue;case ne.paths.dynamicCast:return ne.nodeColors.turquoise;case ne.paths.inputDebugKey:case ne.paths.inputKey:return ne.nodeColors.red;case ne.paths.createDelegate:case ne.paths.enumLiteral:case ne.paths.makeArray:case ne.paths.makeMap:case ne.paths.materialGraphNode:case ne.paths.select:return ne.nodeColors.green;case ne.paths.executionSequence:case ne.paths.ifThenElse:case ne.paths.macro:case ne.paths.multiGate:return ne.nodeColors.gray;case ne.paths.functionEntry:case ne.paths.functionResult:return ne.nodeColors.violet;case ne.paths.timeline:return ne.nodeColors.yellow}if(e.switchTarget())return ne.nodeColors.lime;if(e.isEvent())return ne.nodeColors.red;if(e.isComment())return(e.CommentColor?e.CommentColor:He.getWhite()).toDimmedColor().toCSSRGBValues();const t=e.getPcgSubobject();if(t){if(t.NodeTitleColor)return t.NodeTitleColor.toDimmedColor(.1).toCSSRGBValues();switch(e.PCGNode?.getName(!0)){case"Branch":case"Select":return ne.nodeColors.intenseGreen}}return e.bIsPureFunc?ne.nodeColors.green:ne.nodeColors.blue}(this)}nodeIcon(){return function(e){if(e.isMaterial()||e.isPcg()||e.isNiagara())return null;switch(e.getType()){case ne.paths.addDelegate:case ne.paths.asyncAction:case ne.paths.callDelegate:case ne.paths.createDelegate:case ne.paths.functionEntry:case ne.paths.functionResult:return De.node;case ne.paths.customEvent:return De.event;case ne.paths.doN:return De.doN;case ne.paths.doOnce:return De.doOnce;case ne.paths.dynamicCast:return De.cast;case ne.paths.enumLiteral:return De.enum;case ne.paths.event:return De.event;case ne.paths.executionSequence:case ne.paths.multiGate:return De.sequence;case ne.paths.flipflop:return De.flipflop;case ne.paths.forEachElementInEnum:case ne.paths.forLoop:case ne.paths.forLoopWithBreak:case ne.paths.whileLoop:return De.loop;case ne.paths.forEachLoop:case ne.paths.forEachLoopWithBreak:return De.forEachLoop;case ne.paths.ifThenElse:return De.branchNode;case ne.paths.isValid:return De.questionMark;case ne.paths.makeArray:return De.makeArray;case ne.paths.makeMap:return De.makeMap;case ne.paths.makeSet:return De.makeSet;case ne.paths.makeStruct:return De.makeStruct;case ne.paths.metasoundEditorGraphExternalNode:return De.metasoundFunction;case ne.paths.select:return De.select;case ne.paths.spawnActorFromClass:return De.spawnActor;case ne.paths.timeline:return De.timer}if(e.switchTarget())return De.switch;if(_e(e).startsWith("Break"))return De.breakStruct;if(e.getClass()===ne.paths.macro)return De.macro;const t=e.getHIDAttribute()?.toString();return t?t.includes("Mouse")?De.mouse:t.includes("Gamepad_Special")?De.keyboard:t.includes("Gamepad")||t.includes("Steam")?De.gamepad:t.includes("Touch")?De.touchpad:De.keyboard:e.getDelegatePin()?De.event:e.ObjectRef?.type===ne.paths.ambientSound?De.sound:De.functionSymbol}(this)}additionalPinInserter(){return function(e){let t,i,r,s;switch(e.getType()){case ne.paths.commutativeAssociativeBinaryOperator:case ne.paths.promotableOperator:switch(s=e.FunctionReference?.MemberName,s){default:if(!(s?.startsWith("Add_")||s?.startsWith("Subtract_")||s?.startsWith("Multiply_")||s?.startsWith("Divide_")))break;case"And_Int64Int64":case"And_IntInt":case"BMax":case"BMin":case"BooleanAND":case"BooleanNAND":case"BooleanOR":case"Concat_StrStr":case"FMax":case"FMin":case"Max":case"MaxInt64":case"Min":case"MinInt64":case"Or_Int64Int64":case"Or_IntInt":t??=()=>e.getPinEntities().filter((e=>e.isInput())),i??=St,r??=(i,r=-1,s=-1)=>{const n=String.fromCharCode(i>=0?i:s+"A".charCodeAt(0)+1);return e.NumAdditionalInputs=t().length-1,n}}break;case ne.paths.multiGate:t??=()=>e.getPinEntities().filter((e=>e.isOutput())),i??=e=>Number(e.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]),r??=(e,t=-1,i=-1,r)=>`Out ${e>=0?e:t>0?"Out 0":i+1}`;break;case ne.paths.switchInteger:t??=()=>e.getPinEntities().filter((e=>e.isOutput())),i??=e=>Number(e.PinName.match(/^\s*(\d+)\s*$/)?.[1]),r??=(e,t=-1,i=-1,r)=>(e<0?i+1:e).toString();break;case ne.paths.switchGameplayTag:r??=(t,i=-1,r=-1,s)=>{const n=`Case_${t>=0?t:i>0?"0":r+1}`;return e.PinNames??=[],e.PinNames.push(n),delete e.PinTags[e.PinTags.length-1],e.PinTags[e.PinTags.length]=null,n};case ne.paths.switchName:case ne.paths.switchString:t??=()=>e.getPinEntities().filter((e=>e.isOutput())),i??=e=>Number(e.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]),r??=(t,i=-1,r=-1,s)=>{const n=`Case_${t>=0?t:i>0?"0":r+1}`;return e.PinNames??=[],e.PinNames.push(n),n}}if(t)return()=>{let s=Number.MAX_SAFE_INTEGER,n=Number.MIN_SAFE_INTEGER,a=[];const o=t().reduce(((e,t)=>{const r=i(t);if(isNaN(r)){if(void 0===e)return t}else if(a.push(r),s=Math.min(r,s),r>n)return n=r,t;return e}),void 0);if(s!==Number.MAX_SAFE_INTEGER&&n!==Number.MIN_SAFE_INTEGER||(s=void 0,n=void 0),!o)return null;a.sort(((e,t)=>e{const t=e-l>1;return l=e,t}));const u=new vt(o);return u.PinId=Fe.generateGuid(),u.PinName=r(c,s,n,u),u.PinToolTip=void 0,e.getCustomproperties(!0).push(u),u}}(this)}}class Ct extends Et{constructor(e={},t=void 0){e.Class=new je(ne.paths.knot),e.Name="K2Node_Knot";const i=new vt({PinName:"InputPin"},!0),r=new vt({PinName:"OutputPin",Direction:"EGPD_Output"},!0);t&&(i.copyTypeFrom(t),r.copyTypeFrom(t)),e.CustomProperties=[i,r],super(e,!0)}}class Nt{#V;get target(){return this.#V}#r;get blueprint(){return this.#r}consumeEvent;options;listenHandler=()=>this.listenEvents();unlistenHandler=()=>this.unlistenEvents();constructor(e,t,i={}){i.consumeEvent??=!1,i.listenOnFocus??=!1,i.unlistenOnTextEdit??=!1,this.#V=e,this.#r=t,this.consumeEvent=i.consumeEvent,this.options=i}setup(){this.options.listenOnFocus&&(this.blueprint.addEventListener(ne.focusEventName.begin,this.listenHandler),this.blueprint.addEventListener(ne.focusEventName.end,this.unlistenHandler)),this.options.unlistenOnTextEdit&&(this.blueprint.addEventListener(ne.editTextEventName.begin,this.unlistenHandler),this.blueprint.addEventListener(ne.editTextEventName.end,this.listenHandler)),this.blueprint.focused&&this.listenEvents()}cleanup(){this.unlistenEvents(),this.blueprint.removeEventListener(ne.focusEventName.begin,this.listenHandler),this.blueprint.removeEventListener(ne.focusEventName.end,this.unlistenHandler),this.blueprint.removeEventListener(ne.editTextEventName.begin,this.unlistenHandler),this.blueprint.removeEventListener(ne.editTextEventName.end,this.listenHandler)}listenEvents(){}unlistenEvents(){}}class xt extends Ve{static attributes={...super.attributes,ActionName:oe.createValue(""),bShift:oe.createValue(!1),bCtrl:oe.createValue(!1),bAlt:oe.createValue(!1),bCmd:oe.createValue(!1),Key:oe.createType(Ue)};static grammar=this.createGrammar();static createGrammar(){return Me.alt(Ue.grammar.map((e=>new this({Key:e}))),$e.createEntityGrammar(this))}constructor(e={}){super(e,!0),this.ActionName,this.bShift,this.bCtrl,this.bAlt,this.bCmd,this.Key}}class Pt extends Nt{static#$=e=>{};#O;pressedKey="";constructor(e,t,i={},r=Pt.#$,s=Pt.#$){i.activationKeys??=[],i.consumeEvent??=!0,i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,i.activationKeys instanceof Array||(i.activationKeys=[i.activationKeys]),i.activationKeys=i.activationKeys.map((e=>{if(e instanceof xt)return e;if(e.constructor===String){const t=xt.grammar.run(e);if(t.status)return t.value}throw new Error("Unexpected key value")})),super(e,t,i),this.onKeyDown=r,this.onKeyUp=s,this.#O=this.options.activationKeys??[];let n=this;this.keyDownHandler=e=>{n.#O.some((t=>(e=>e.bShift||"LeftShift"==e.Key||"RightShift"==e.Key)(t)==e.shiftKey&&(e=>e.bCtrl||"LeftControl"==e.Key||"RightControl"==e.Key)(t)==e.ctrlKey&&(e=>e.bAlt||"LeftAlt"==e.Key||"RightAlt"==e.Key)(t)==e.altKey&&ne.Keys[t.Key.value]==e.code))&&(this.consumeEvent&&(e.preventDefault(),e.stopImmediatePropagation()),this.pressedKey=e.code,n.fire(),document.removeEventListener("keydown",n.keyDownHandler),document.addEventListener("keyup",n.keyUpHandler))},this.keyUpHandler=e=>{n.#O.some((t=>t.bShift&&"Shift"==e.key||t.bCtrl&&"Control"==e.key||t.bAlt&&"Alt"==e.key||t.bCmd&&"Meta"==e.key||ne.Keys[t.Key.value]==e.code))&&(this.consumeEvent&&e.stopImmediatePropagation(),n.unfire(),this.pressedKey="",document.removeEventListener("keyup",this.keyUpHandler),document.addEventListener("keydown",this.keyDownHandler))}}listenEvents(){document.addEventListener("keydown",this.keyDownHandler)}unlistenEvents(){document.removeEventListener("keydown",this.keyDownHandler)}fire(){this.onKeyDown(this)}unfire(){this.onKeyUp(this)}}class kt extends Nt{#H=[0,0];get location(){return this.#H}#D;get enablerKey(){return this.#D}#G=!0;get enablerActivated(){return this.#G}constructor(e,t,i={}){i.ignoreTranslateCompensate??=!1,i.ignoreScale??=!1,i.movementSpace??=t.getGridDOMElement()??document.documentElement,super(e,t,i),this.movementSpace=i.movementSpace,i.enablerKey&&(this.#D=i.enablerKey,this.#D.onKeyDown=()=>this.#G=!0,this.#D.onKeyUp=()=>this.#G=!1,this.#D.consumeEvent=!1,this.#D.listenEvents(),this.#G=!1)}setLocationFromEvent(e){let t=ue.convertLocation([e.clientX,e.clientY],this.movementSpace,this.options.ignoreScale);return t=this.options.ignoreTranslateCompensate?t:this.blueprint.compensateTranslation(t[0],t[1]),this.#H=[...t],this.#H}}class Tt extends kt{static#$=e=>{};#R=e=>{if(this.blueprint.setFocused(!0),!this.enablerKey||this.enablerActivated)if(e.button===this.options.clickButton)this.options.strictTarget&&e.target!==e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),document.addEventListener("mouseup",this.#_),this.setLocationFromEvent(e),this.clickedPosition[0]=this.location[0],this.clickedPosition[1]=this.location[1],this.blueprint.mousePosition[0]=this.location[0],this.blueprint.mousePosition[1]=this.location[1],this.clicked(this.clickedPosition));else this.options.exitAnyButton||this.#_(e)};#_=e=>{this.options.exitAnyButton&&e.button!=this.options.clickButton||(this.consumeEvent&&e.stopImmediatePropagation(),document.removeEventListener("mouseup",this.#_),this.unclicked())};clickedPosition=[0,0];constructor(e,t,i={},r=Tt.#$,s=Tt.#$){i.clickButton??=ne.mouseClickButton,i.consumeEvent??=!0,i.exitAnyButton??=!0,i.strictTarget??=!1,super(e,t,i),this.onClick=r,this.onUnclick=s,this.listenEvents()}listenEvents(){this.target.addEventListener("mousedown",this.#R),this.options.clickButton===ne.mouseRightClickButton&&this.target.addEventListener("contextmenu",(e=>e.preventDefault()))}unlistenEvents(){this.target.removeEventListener("mousedown",this.#R)}clicked(e){this.onClick(this)}unclicked(e){this.onUnclick(this)}}class At extends kt{static ignoreDbClick=e=>{};#z=e=>{this.options.strictTarget&&e.target!==e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),this.clickedPosition=this.setLocationFromEvent(e),this.blueprint.mousePosition=[...this.clickedPosition],this.dbclicked(this.clickedPosition))};#F;get onDbClick(){return this.#F}set onDbClick(e){this.#F=e}clickedPosition=[0,0];constructor(e,t,i={},r=At.ignoreDbClick){i.consumeEvent??=!0,i.strictTarget??=!1,super(e,t,i),this.#F=r,this.listenEvents()}listenEvents(){this.target.addEventListener("dblclick",this.#z)}unlistenEvents(){this.target.removeEventListener("dblclick",this.#z)}dbclicked(e){this.onDbClick(e)}}class Lt{element;get blueprint(){return this.element.blueprint}#j=[];get inputObjects(){return this.#j}initialize(e){this.element=e}createInputObjects(){return[]}setup(){this.#j.forEach((e=>e.setup()))}cleanup(){this.#j.forEach((e=>e.cleanup()))}willUpdate(e){}update(e){}render(){return H``}firstUpdated(e){}updated(e){}inputSetup(){this.#j=this.createInputObjects()}}class Mt extends Lt{update(e){super.update(e);const[t,i,r,s]=[Math.round(this.element.fromX),Math.round(this.element.fromY),Math.round(this.element.toX),Math.round(this.element.toY)],[n,a,o,l]=[Math.min(t,r),Math.min(i,s),Math.abs(t-r),Math.abs(i-s)];(e.has("fromX")||e.has("toX"))&&(this.element.style.left=`${n}px`,this.element.style.width=`${o}px`),(e.has("fromY")||e.has("toY"))&&(this.element.style.top=`${a}px`,this.element.style.height=`${l}px`)}}class It extends Mt{static decreasingValue(e,t){const i=-e*t[0]**2,r=t[1]-i/t[0];return e=>i/e+r}static clampedLine(e,t){if(e[0]>t[0]){const i=e;e=t,t=i}const i=(t[1]-e[1])/(t[0]-e[0]),r=e[1]-i*e[0];return s=>st[0]?t[1]:i*s+r}static c1DecreasingValue=It.decreasingValue(-.15,[100,15]);static c2DecreasingValue=It.decreasingValue(-.05,[500,130]);static c2Clamped=It.clampedLine([0,80],[200,40]);#W=`ueb-id-${Math.floor(1e12*Math.random())}`;#U=e=>{const t=new Ct({},this.element.source.entity),i=me.getConstructor("ueb-node").newObject(t);i.setLocation(...this.blueprint.snapToGrid(...e));const r=i.template;this.blueprint.addGraphElement(i);const s=this.element.getInputPin(),n=this.element.getOutputPin();this.element.source=null,this.element.destination=null;const a=me.getConstructor("ueb-link").newObject(n,r.inputPin);this.blueprint.addGraphElement(a),this.element.source=r.outputPin,this.element.destination=s};createInputObjects(){const e=this.element.querySelector(".ueb-link-area");return[...super.createInputObjects(),new At(e,this.blueprint,void 0,(e=>{e[0]+=ne.knotOffset[0],e[1]+=ne.knotOffset[1],e=ue.snapToGrid(e[0],e[1],ne.gridSize),this.#U(e)})),new Tt(e,this.blueprint,{enablerKey:new Pt(this.blueprint,this.blueprint,{activationKeys:de.enableLinkDelete})},(()=>this.blueprint.removeGraphElement(this.element)))]}willUpdate(e){super.willUpdate(e);const t=this.element.source,i=this.element.destination;if(e.has("fromX")||e.has("toX")){const e=this.element.fromX,r=this.element.toX,s=t?.nodeElement.getType()==ne.paths.knot,n=i?.nodeElement.getType()==ne.paths.knot;!s||i&&!n||(t?.isInput()&&r>e+ne.distanceThreshold?this.element.source=t.nodeElement.template.outputPin:t?.isOutput()&&re+ne.distanceThreshold&&(this.element.destination=i.nodeElement.template.inputPin))}const r=Math.max(Math.abs(this.element.fromX-this.element.toX),1),s=Math.max(Math.abs(this.element.fromY-this.element.toY),1),n=Math.max(r,ne.linkMinWidth),a=r/n,o=this.element.originatesFromInput?this.element.fromXthis.element.toY?1:0)),this.element.style.setProperty("--ueb-start-percentage",`${Math.round(this.element.startPercentage)}%`),this.element.style.setProperty("--ueb-link-start",`${Math.round(this.element.startPixels)}`)}render(){return H` ${this.element.linkMessageIcon||this.element.linkMessageText?H``:G}`}}class Bt extends pe{static properties={...super.properties,dragging:{type:Boolean,attribute:"data-dragging",converter:ue.booleanConverter,reflect:!0},originatesFromInput:{type:Boolean,attribute:!1},svgPathD:{type:String,attribute:!1},linkMessageIcon:{type:String,attribute:!1},linkMessageText:{type:String,attribute:!1}};#K;get source(){return this.#K}set source(e){this.#Y(e,!1)}#X;get destination(){return this.#X}set destination(e){this.#Y(e,!0)}#Z=()=>this.remove();#q=e=>this.addSourceLocation(...e.detail.value);#Q=e=>this.addDestinationLocation(...e.detail.value);#J=e=>this.setSourceLocation();#ee=e=>this.setDestinationLocation();linkMessageIcon=G;linkMessageText=G;pathElement;constructor(){super(),this.dragging=!1,this.originatesFromInput=!1,this.startPercentage=0,this.svgPathD="",this.startPixels=0}static newObject(e,t){const i=new Bt;return i.initialize(e,t),i}initialize(e,t){super.initialize({},new It),e&&(this.source=e,t||(this.toX=this.fromX,this.toY=this.fromY)),t&&(this.destination=t,e||(this.fromX=this.toX,this.fromY=this.toY))}#Y(e,t){const i=()=>t?this.destination:this.source;if(i()!=e){if(i()){const e=i().getNodeElement();e.removeEventListener(ne.removeEventName,this.#Z),e.removeEventListener(ne.nodeDragEventName,t?this.#Q:this.#q),e.removeEventListener(ne.nodeReflowEventName,t?this.#ee:this.#J),this.#te()}if(t?this.#X=e:this.#K=e,i()){const e=i().getNodeElement();e.addEventListener(ne.removeEventName,this.#Z),e.addEventListener(ne.nodeDragEventName,t?this.#Q:this.#q),e.addEventListener(ne.nodeReflowEventName,t?this.#ee:this.#J),t?this.setDestinationLocation():(this.setSourceLocation(),this.originatesFromInput=this.source.isInput()),this.#ie()}}}#ie(){this.source&&this.destination&&(this.source.linkTo(this.destination),this.destination.linkTo(this.source))}#te(){this.source&&this.destination&&(this.source.unlinkFrom(this.destination,!1),this.destination.unlinkFrom(this.source,!1))}cleanup(){super.cleanup(),this.#te(),this.source=null,this.destination=null}setSourceLocation(e=null,t=!0){if(null==e){const i=this;if(t&&(!this.hasUpdated||!this.source.hasUpdated))return void Promise.all([this.updateComplete,this.source.updateComplete]).then((()=>i.setSourceLocation(null,!1)));e=this.source.template.getLinkLocation()}const[i,r]=e;this.fromX=i,this.fromY=r}setDestinationLocation(e=null,t=!0){if(null==e){const i=this;if(t&&(!this.hasUpdated||!this.destination.hasUpdated))return void Promise.all([this.updateComplete,this.destination.updateComplete]).then((()=>i.setDestinationLocation(null,!1)));e=this.destination.template.getLinkLocation()}this.toX=e[0],this.toY=e[1]}getInputPin(){return this.source?.isInput()?this.source:this.destination}setInputPin(e){this.source?.isInput()&&(this.source=e),this.destination=e}getOutputPin(){return this.destination?.isOutput()?this.destination:this.source}setOutputPin(e){this.destination?.isOutput()&&(this.destination=e),this.source=e}startDragging(){this.dragging=!0}finishDragging(){this.dragging=!1}removeMessage(){this.linkMessageIcon=G,this.linkMessageText=G}setMessageConvertType(){this.linkMessageIcon=De.convert,this.linkMessageText=H`Convert ${this.source.pinType} to ${this.destination.pinType}.`}setMessageCorrect(){this.linkMessageIcon=De.correct,this.linkMessageText=G}setMessageReplace(){this.linkMessageIcon=De.correct,this.linkMessageText=G}setMessageDirectionsIncompatible(){this.linkMessageIcon=De.reject,this.linkMessageText=H`Directions are not compatbile.`}setMessagePlaceNode(){this.linkMessageIcon=G,this.linkMessageText=H`Place a new node.`}setMessageReplaceLink(){this.linkMessageIcon=De.correct,this.linkMessageText=H`Replace existing input connections.`}setMessageReplaceOutputLink(){this.linkMessageIcon=De.correct,this.linkMessageText=H`Replace existing output connections.`}setMessageSameNode(){this.linkMessageIcon=De.reject,this.linkMessageText=H`Both are on the same node.`}setMessageTypesIncompatible(e,t){this.linkMessageIcon=De.reject,this.linkMessageText=H`${ue.capitalFirstLetter(e.pinType)} is not compatible with ${ue.capitalFirstLetter(t.pinType)}.`}}class Vt extends he{static properties={...super.properties,locationX:{type:Number,attribute:!1},locationY:{type:Number,attribute:!1},sizeX:{type:Number,attribute:!1},sizeY:{type:Number,attribute:!1}};static dragEventName=ne.dragEventName;static dragGeneralEventName=ne.dragGeneralEventName;constructor(){super(),this.locationX=0,this.locationY=0,this.sizeX=0,this.sizeY=0}computeSizes(){const e=this.getBoundingClientRect();this.sizeX=this.blueprint.scaleCorrect(e.width),this.sizeY=this.blueprint.scaleCorrect(e.height)}firstUpdated(e){super.firstUpdated(e),this.computeSizes()}setLocation(e,t,i=!0){const r=e-this.locationX,s=t-this.locationY;if(this.locationX=e,this.locationY=t,this.blueprint&&i){const e=new CustomEvent(this.constructor.dragEventName,{detail:{value:[r,s]},bubbles:!1,cancelable:!0});this.dispatchEvent(e)}}addLocation(e,t,i=!0){this.setLocation(this.locationX+e,this.locationY+t,i)}acknowledgeDrag(e){const t=new CustomEvent(this.constructor.dragGeneralEventName,{detail:{value:e},bubbles:!0,cancelable:!0});this.dispatchEvent(t)}snapToGrid(){const e=ue.snapToGrid(this.locationX,this.locationY,ne.gridSize);this.locationX==e[0]&&this.locationY==e[1]||this.setLocation(e[0],e[1])}topBoundary(e=!1){return this.template.topBoundary(e)}rightBoundary(e=!1){return this.template.rightBoundary(e)}bottomBoundary(e=!1){return this.template.bottomBoundary(e)}leftBoundary(e=!1){return this.template.leftBoundary(e)}}class $t extends kt{#R=e=>{if(this.blueprint.setFocused(!0),e.button===this.options.clickButton)this.options.strictTarget&&e.target!=e.currentTarget||(this.consumeEvent&&e.stopImmediatePropagation(),this.#re.addEventListener("mousemove",this.#se),document.addEventListener("mouseup",this.#_),this.setLocationFromEvent(e),this.clickedPosition[0]=this.location[0],this.clickedPosition[1]=this.location[1],this.blueprint.mousePosition[0]=this.location[0],this.blueprint.mousePosition[1]=this.location[1],this.target instanceof Vt&&(this.clickedOffset=[this.clickedPosition[0]-this.target.locationX,this.clickedPosition[1]-this.target.locationY]),this.clicked(this.clickedPosition));else this.options.exitAnyButton||this.#_(e)};#se=e=>{this.consumeEvent&&e.stopImmediatePropagation(),this.#re.removeEventListener("mousemove",this.#se),this.#re.addEventListener("mousemove",this.#ne);const t=this.getEvent(ne.trackingMouseEventName.begin);this.#ae=0==this.target.dispatchEvent(t),this.setLocationFromEvent(e),this.lastLocation=ue.snapToGrid(this.clickedPosition[0],this.clickedPosition[1],this.stepSize),this.startDrag(this.location),this.started=!0,this.#ne(e)};#ne=e=>{this.consumeEvent&&e.stopImmediatePropagation();const t=this.setLocationFromEvent(e),i=[e.movementX,e.movementY];if(this.dragTo(t,i),this.#ae&&(this.blueprint.mousePosition=t),this.options.scrollGraphEdge){const e=Math.sqrt(i[0]*i[0]+i[1]*i[1]),r=this.blueprint.scaleCorrect(ne.edgeScrollThreshold),s=this.blueprint.template.gridLeftVisibilityBoundary()+r,n=this.blueprint.template.gridRightVisibilityBoundary()-r;let a=0;t[0]n&&(a=t[0]-n);const o=this.blueprint.template.gridTopVisibilityBoundary()+r,l=this.blueprint.template.gridBottomVisibilityBoundary()-r;let c=0;t[1]l&&(c=t[1]-l),a=ue.clamp(this.blueprint.scaleCorrectReverse(a)**3*e*.6,-20,20),c=ue.clamp(this.blueprint.scaleCorrectReverse(c)**3*e*.6,-20,20),this.blueprint.scrollDelta(a,c)}};#_=e=>{if(!this.options.exitAnyButton||e.button==this.options.clickButton){if(this.consumeEvent&&e.stopImmediatePropagation(),this.#re.removeEventListener("mousemove",this.#se),this.#re.removeEventListener("mousemove",this.#ne),document.removeEventListener("mouseup",this.#_),this.started&&this.endDrag(),this.unclicked(),this.#ae){const e=this.getEvent(ne.trackingMouseEventName.end);this.target.dispatchEvent(e),this.#ae=!1}this.started=!1}};#ae=!1;#re;#oe;get draggableElement(){return this.#oe}clickedOffset=[0,0];clickedPosition=[0,0];lastLocation=[0,0];started=!1;stepSize=1;constructor(e,t,i={}){i.clickButton??=ne.mouseClickButton,i.consumeEvent??=!0,i.draggableElement??=e,i.exitAnyButton??=!0,i.moveEverywhere??=!1,i.movementSpace??=t?.getGridDOMElement(),i.repositionOnClick??=!1,i.scrollGraphEdge??=!1,i.strictTarget??=!1,super(e,t,i),this.stepSize=Number(i.stepSize??ne.gridSize),this.#re=this.options.moveEverywhere?document.documentElement:this.movementSpace,this.#oe=this.options.draggableElement,this.listenEvents()}listenEvents(){super.listenEvents(),this.#oe.addEventListener("mousedown",this.#R),this.options.clickButton===ne.mouseRightClickButton&&this.#oe.addEventListener("contextmenu",(e=>e.preventDefault()))}unlistenEvents(){super.unlistenEvents(),this.#oe.removeEventListener("mousedown",this.#R)}getEvent(e){return new CustomEvent(e,{detail:{tracker:this},bubbles:!0,cancelable:!0})}clicked(e){}startDrag(e){}dragTo(e,t){}endDrag(){}unclicked(e){}}class Ot extends $t{clicked(e){this.options.repositionOnClick&&(this.target.setLocation(...this.stepSize>1?ue.snapToGrid(e[0],e[1],this.stepSize):e),this.clickedOffset=[0,0])}dragTo(e,t){const i=[this.target.locationX??this.lastLocation[0],this.target.locationY??this.lastLocation[1]],[r,s]=this.stepSize>1?[ue.snapToGrid(e[0],e[1],this.stepSize),ue.snapToGrid(i[0],i[1],this.stepSize)]:[e,i];0==(t=[r[0]-this.lastLocation[0],r[1]-this.lastLocation[1]])[0]&&0==t[1]||(t[0]+=s[0]-i[0],t[1]+=s[1]-i[1],this.dragAction(r,t),this.lastLocation=r)}dragAction(e,t){this.target.setLocation(e[0]-this.clickedOffset[0],e[1]-this.clickedOffset[1])}}class Ht extends Ot{#le;#ce;#ue;#he;constructor(e,t,i={}){super(e,t,i),i.onClicked&&(this.#le=i.onClicked),i.onStartDrag&&(this.#ce=i.onStartDrag),i.onDrag&&(this.#ue=i.onDrag),i.onEndDrag&&(this.#he=i.onEndDrag)}clicked(e){super.clicked(e),this.#le?.()}startDrag(){super.startDrag(),this.#ce?.()}dragAction(e,t){this.#ue?.(e,t)}endDrag(){super.endDrag(),this.#he?.()}}class Dt extends Ot{constructor(e,t,i={}){super(e,t,i),this.draggableElement.classList.add("ueb-draggable")}startDrag(){this.target.selected||(this.blueprint.unselectAll(),this.target.setSelected(!0))}dragAction(e,t){this.target.acknowledgeDrag(t)}unclicked(){this.started?(this.blueprint.getNodes(!0).forEach((e=>e.boundComments.filter((t=>!e.isInsideComment(t))).forEach((t=>e.unbindFromComment(t))))),this.blueprint.getCommentNodes().forEach((e=>e.template.manageNodesBind()))):(this.blueprint.unselectAll(),this.target.setSelected(!0))}}class Gt extends Lt{getDraggableElement(){return this.element}createDraggableObject(){const e=this.getDraggableElement();return new Ot(this.element,this.blueprint,{draggableElement:e})}createInputObjects(){return[...super.createInputObjects(),this.createDraggableObject(),new Pt(this.element,this.blueprint,{activationKeys:[ne.Keys.ArrowUp,ne.Keys.ArrowRight,ne.Keys.ArrowDown,ne.Keys.ArrowLeft]},(e=>e.target.acknowledgeDrag([e.pressedKey===ne.Keys.ArrowLeft?-ne.gridSize:e.pressedKey===ne.Keys.ArrowRight?ne.gridSize:0,e.pressedKey===ne.Keys.ArrowUp?-ne.gridSize:e.pressedKey===ne.Keys.ArrowDown?ne.gridSize:0])))]}topBoundary(e=!1){return this.element.locationY}rightBoundary(e=!1){return this.element.locationX+this.element.sizeX}bottomBoundary(e=!1){return this.element.locationY+this.element.sizeY}leftBoundary(e=!1){return this.element.locationX}centerInViewport(){const e=Math.min(this.blueprint.template.viewportSize[0]/10,this.blueprint.template.viewportSize[1]/10),t=this.leftBoundary()-this.blueprint.template.gridLeftVisibilityBoundary(),i=this.blueprint.template.gridRightVisibilityBoundary()-this.rightBoundary();let r=Math.max((t+i)/2,e);const s=this.topBoundary()-this.blueprint.template.gridTopVisibilityBoundary(),n=this.blueprint.template.gridBottomVisibilityBoundary()-this.bottomBoundary();let a=Math.max((s+n)/2,e);this.blueprint.scrollDelta(t-r,s-a,!0)}}class Rt extends Gt{update(e){super.update(e),e.has("locationX")&&(this.element.style.left=`${this.element.locationX}px`),e.has("locationY")&&(this.element.style.top=`${this.element.locationY}px`)}}class _t extends Rt{getDraggableElement(){return this.element}createDraggableObject(){return new Dt(this.element,this.blueprint,{draggableElement:this.getDraggableElement(),scrollGraphEdge:!0})}firstUpdated(e){super.firstUpdated(e),this.element.selected&&!this.element.listeningDrag&&this.element.setSelected(!0)}}class zt extends _t{static nodeStyleClasses=["ueb-node-style-default"];#pe=!1;pinInserter;inputContainer;outputContainer;pinElement;addPinHandler=()=>{const e=this.pinInserter?.();e&&(this.defaultPin&&this.defaultPin.isInput()===e.isInput()?this.defaultPin.before(this.createPinElement(e)):(e.isInput()?this.inputContainer:this.outputContainer).appendChild(this.createPinElement(e)),this.element.acknowledgeReflow())};toggleAdvancedDisplayHandler=()=>{this.element.toggleShowAdvancedPinDisplay(),this.element.requestUpdate(),this.element.updateComplete.then((()=>this.element.acknowledgeReflow()))};createPinElement(e){const t=me.getConstructor("ueb-pin").newObject(e,void 0,this.element);return this.pinInserter&&!this.defaultPin&&"Default"===t.getPinName()&&(this.defaultPin=t,this.defaultPin.classList.add("ueb-node-variadic-default")),t}initialize(e){super.initialize(e),this.element.classList.add(...this.constructor.nodeStyleClasses),this.element.style.setProperty("--ueb-node-color",this.getColor().cssText),this.pinInserter=this.element.entity.additionalPinInserter(),this.pinInserter&&this.element.classList.add("ueb-node-is-variadic")}getColor(){return this.element.entity.nodeColor()}render(){return H`
${this.renderTop()}
${this.pinInserter?H`
Add pin ${De.plusCircle}
`:G} ${this.element.entity.isDevelopmentOnly()?H`
Development Only
`:G} ${this.element.advancedPinDisplay?H`
${De.expandIcon}
`:G}
`}renderNodeIcon(){return this.element.entity.nodeIcon()}renderNodeName(){return this.element.nodeDisplayName}renderTop(){const e=this.renderNodeIcon(),t=this.renderNodeName();return H`
${e?H`
${e}
`:G} ${t?H`
${t} ${this.#pe&&this.getTargetType().length>0?H`
Target is ${ue.formatStringName(this.getTargetType())}
`:G}
`:G}
`}firstUpdated(e){super.firstUpdated(e),this.inputContainer=this.element.querySelector(".ueb-node-inputs"),this.outputContainer=this.element.querySelector(".ueb-node-outputs"),this.setupPins(),this.element.updateComplete.then((()=>this.element.acknowledgeReflow()))}setupPins(){this.element.nodeNameElement=this.element.querySelector(".ueb-node-name-text");let e=!1,t=!1;for(const i of this.element.getPinElements())i!==this.defaultPin&&(i.isInput()?(this.inputContainer.appendChild(i),e=!0):i.isOutput()&&(this.outputContainer.appendChild(i),t=!0));this.defaultPin&&(this.defaultPin.isInput()?this.inputContainer:this.outputContainer).appendChild(this.defaultPin),e&&this.element.classList.add("ueb-node-has-inputs"),t&&this.element.classList.add("ueb-node-has-outputs")}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden())).map((e=>(this.#pe=this.#pe||"self"===e.PinName&&"Target"===e.pinTitle(),this.createPinElement(e))))}getTargetType(){return this.element.entity.FunctionReference?.MemberParent?.getName()??"Untitled"}getPinElements(e){return e.querySelectorAll("ueb-pin")}linksChanged(){}}class Ft extends zt{#de=document.createElement("div");#me=document.createElement("div");#ge=document.createElement("div");#be=document.createElement("div");#ve=document.createElement("div");#ye=document.createElement("div");#fe=document.createElement("div");#we=document.createElement("div");initialize(e){super.initialize(e),this.element.classList.add("ueb-resizeable"),this.#de.classList.add("ueb-resizeable-top"),this.#me.classList.add("ueb-resizeable-right"),this.#ge.classList.add("ueb-resizeable-bottom"),this.#be.classList.add("ueb-resizeable-left"),this.#ve.classList.add("ueb-resizeable-top-right"),this.#ye.classList.add("ueb-resizeable-bottom-right"),this.#fe.classList.add("ueb-resizeable-bottom-left"),this.#we.classList.add("ueb-resizeable-top-left")}update(e){super.update(e),this.element.sizeX>=0&&e.has("sizeX")&&(this.element.style.width=`${this.element.sizeX}px`),this.element.sizeY>=0&&e.has("sizeY")&&(this.element.style.height=`${this.element.sizeY}px`)}firstUpdated(e){super.firstUpdated(e),this.element.append(this.#de,this.#me,this.#ge,this.#be,this.#ve,this.#ye,this.#fe,this.#we)}createInputObjects(){return[...super.createInputObjects(),new Ht(this.#de,this.blueprint,{onDrag:(e,t)=>{t[1]=e[1]-this.element.topBoundary(),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()}),new Ht(this.#me,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),this.setSizeX(this.element.sizeX+t[0])},onEndDrag:()=>this.endResize()}),new Ht(this.#ge,this.blueprint,{onDrag:(e,t)=>{t[1]=e[1]-this.element.bottomBoundary(),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Ht(this.#be,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1)},onEndDrag:()=>this.endResize()}),new Ht(this.#ve,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),t[1]=e[1]-this.element.topBoundary(),this.setSizeX(this.element.sizeX+t[0]),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()}),new Ht(this.#ye,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.rightBoundary(),t[1]=e[1]-this.element.bottomBoundary(),this.setSizeX(this.element.sizeX+t[0]),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Ht(this.#fe,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),t[1]=e[1]-this.element.bottomBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1),this.setSizeY(this.element.sizeY+t[1])},onEndDrag:()=>this.endResize()}),new Ht(this.#we,this.blueprint,{onDrag:(e,t)=>{t[0]=e[0]-this.element.leftBoundary(),t[1]=e[1]-this.element.topBoundary(),this.setSizeX(this.element.sizeX-t[0])&&this.element.addLocation(t[0],0,!1),this.setSizeY(this.element.sizeY-t[1])&&this.element.addLocation(0,t[1],!1)},onEndDrag:()=>this.endResize()})]}setSizeX(e){return this.element.setNodeWidth(e),!0}setSizeY(e){return this.element.setNodeHeight(e),!0}endResize(){}}class jt extends Ft{#Se=0;initialize(e){super.initialize(e),e.classList.add("ueb-node-style-comment","ueb-node-resizeable"),e.sizeX=25*ne.gridSize,e.sizeY=6*ne.gridSize,super.initialize(e)}getDraggableElement(){return this.element.querySelector(".ueb-node-top")}render(){return H`
`}firstUpdated(e){super.firstUpdated(e);const t=this.getDraggableElement().getBoundingClientRect();this.#Se=t.height}manageNodesBind(){let e=this.blueprint.getNodes();for(let t of e)t.topBoundary()>=this.element.topBoundary()&&t.rightBoundary()<=this.element.rightBoundary()&&t.bottomBoundary()<=this.element.bottomBoundary()&&t.leftBoundary()>=this.element.leftBoundary()?t.bindToComment(this.element):t.unbindFromComment(this.element)}setSizeX(e){return(e=Math.round(e))>=2*ne.gridSize&&(this.element.setNodeWidth(e),!0)}setSizeY(e){return(e=Math.round(e))>=2*ne.gridSize&&(this.element.setNodeHeight(e),!0)}endResize(){this.manageNodesBind()}topBoundary(e=!1){return this.element.locationY}rightBoundary(e=!1){return this.element.locationX+this.element.sizeX}bottomBoundary(e=!1){return e?this.element.locationY+this.#Se:super.bottomBoundary()}leftBoundary(e=!1){return this.element.locationX}}class Wt extends $t{#Ee;#Ce=null;#Ne=e=>{if(!this.enteredPin){this.linkValid=!1,this.enteredPin=e.target;const t=this.link.source??this.target,i=this.enteredPin,r=t.isOutput()?t:i;t.nodeElement.getType()===ne.paths.knot||i.nodeElement.getType()===ne.paths.knot?(this.link.setMessageCorrect(),this.linkValid=!0):t.getNodeElement()===i.getNodeElement()?this.link.setMessageSameNode():t.isOutput()===i.isOutput()?this.link.setMessageDirectionsIncompatible():this.blueprint.getLinks(t,i).length?(this.link.setMessageReplaceLink(),this.linkValid=!0):"exec"===r.entity.getType()&&r.isLinked?(this.link.setMessageReplaceOutputLink(),this.linkValid=!0):"object"==t.entity.PinType.PinCategory&&"object"==i.entity.PinType.PinCategory||t.pinType==i.pinType?(this.link.setMessageCorrect(),this.linkValid=!0):(this.link.setMessageTypesIncompatible(t,i),this.linkValid=!1)}};#xe=e=>{this.enteredPin==e.target&&(this.enteredPin=null,this.linkValid=!1,this.link?.setMessagePlaceNode())};link;enteredPin;linkValid=!1;constructor(e,t,i={}){i.scrollGraphEdge??=!0,super(e,t,i)}startDrag(e){this.target.nodeElement.getType()==ne.paths.knot&&(this.#Ce=this.target),this.link=me.getConstructor("ueb-link").newObject(this.target,null),this.blueprint.template.linksContainerElement.prepend(this.link),this.link.setMessagePlaceNode(),this.#Ee=this.blueprint.querySelectorAll("ueb-pin"),this.#Ee.forEach((e=>{e!=this.target&&(e.addEventListener("mouseenter",this.#Ne),e.addEventListener("mouseleave",this.#xe))})),this.link.startDragging(),this.link.setDestinationLocation(e)}dragTo(e,t){this.link.setDestinationLocation(e)}endDrag(){if(this.#Ee.forEach((e=>{e.removeEventListener("mouseenter",this.#Ne),e.removeEventListener("mouseleave",this.#xe)})),this.#Ee=null,this.enteredPin&&this.linkValid){if(this.#Ce){const e=this.#Ce!==this.link.source?this.link.source:this.enteredPin;if(this.#Ce.isInput()&&e.isInput()||this.#Ce.isOutput()&&e.isOutput()){const e=this.#Ce.template.getOppositePin();this.#Ce===this.link.source?this.link.source=e:this.enteredPin=e}}else this.enteredPin.nodeElement.getType()===ne.paths.knot&&(this.enteredPin=this.enteredPin.template.getOppositePin());this.link.source.getLinks().find((e=>e.equals(this.enteredPin.createPinReference())))?this.link.remove():(this.blueprint.addGraphElement(this.link),this.link.destination=this.enteredPin)}else this.link.remove();this.enteredPin=null,this.link.removeMessage(),this.link.finishDragging(),this.link=null}}class Ut extends zt{#Pe=!1;#ke=!1;displayName="";static nodeStyleClasses=["ueb-node-style-glass"];initialize(e){super.initialize(e),this.displayName=this.element.nodeDisplayName}render(){return H`
${this.displayName?H`
${this.displayName}
`:G} ${this.#Pe?H`
`:G} ${this.#ke?H`
`:G} ${this.pinInserter?H`
Add pin ${De.plusCircle}
`:G}
`}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden())).map((e=>{this.#Pe||=e.isInput(),this.#ke||=e.isOutput();return me.getConstructor("ueb-pin").newObject(e,void 0,this.element)}))}}class Kt extends Ut{static nodeStyleClasses=["ueb-node-style-metasound","ueb-node-style-operation"]}class Yt extends Ut{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-conversion"]}class Xt extends Ut{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-operation"]}class Zt extends Lt{static canWrapInput=!0;#Te;get iconElement(){return this.#Te}#Ae;get wrapperElement(){return this.#Ae}isNameRendered=!0;initialize(e){if(super.initialize(e),this.element.nodeElement){const e=this.element.nodeElement.template;this.isNameRendered=!(e instanceof Yt||e instanceof Xt||e instanceof Kt)}}setup(){super.setup(),this.element.nodeElement=this.element.closest("ueb-node");const e=this.element.nodeElement.template;(e instanceof Yt||e instanceof Xt)&&(this.isNameRendered=!1,this.element.requestUpdate())}createInputObjects(){return[new Wt(this.element,this.blueprint,{moveEverywhere:!0,draggableElement:this.getClickableElement()})]}render(){const e=H`
${this.renderIcon()}
`,t=H`
${this.isNameRendered?this.renderName():G} ${this.isInputRendered()?this.renderInput():H``}
`;return H`
${this.element.isInput()?H`${e}${t}`:H`${t}${e}`}
`}renderIcon(){if(this.element.nodeElement.entity.isPcg())switch(this.element.entity.getType()){case"Any":return De.pcgPin;case"Param":case"Param[]":return De.pcgParamPin;case"Spatial":case"Spatial[]":return De.pcgSpatialPin;case"Any[]":case"Point[]":case"Surface[]":case"Volume[]":if(this.element.isOutput())return De.pcgPin;case"Point":case"Surface":case"Volume":return De.pcgStackPin}switch(this.element.entity.PinType?.ContainerType?.toString()){case"Array":return De.arrayPin;case"Set":return De.setPin;case"Map":return De.mapPin}return"delegate"===this.element.entity.PinType?.PinCategory?.toLocaleLowerCase()?De.delegate:this.element.nodeElement?.template instanceof Xt?De.operationPin:De.genericPin}renderName(){let e=this.element.getPinDisplayName();const t=this.element.nodeElement,i=this.element.getPinName();return t.getType()==ne.paths.makeStruct&&i==t.entity.StructType.getName()&&(e=i),H`${e}`}isInputRendered(){return this.element.isInput()&&!this.element.entity.bDefaultValueIsIgnored&&!this.element.entity.PinType.bIsReference}renderInput(){return H``}updated(e){if(super.updated(e),this.element.isInput()&&e.has("isLinked")){const e=this.element.nodeElement;this.element.requestUpdate(),this.element.updateComplete.then((()=>e.acknowledgeReflow()))}}firstUpdated(e){super.firstUpdated(e),this.element.style.setProperty("--ueb-pin-color-rgb",this.element.entity.pinColor().cssText),this.#Te=this.element.querySelector(".ueb-pin-icon svg")??this.element,this.#Ae=this.element.querySelector(".ueb-pin-wrapper")}getLinkLocation(){const e=this.iconElement.getBoundingClientRect(),t=[this.element.isInput()?e.left:e.right+1,(e.top+e.bottom)/2],i=ue.convertLocation(t,this.blueprint.template.gridElement);return this.blueprint.compensateTranslation(i[0],i[1])}getClickableElement(){return this.#Ae??this.element}}class qt extends Zt{render(){return H`
${this.renderIcon()}
`}}class Qt extends zt{static nodeStyleClasses=[...super.nodeStyleClasses,"ueb-node-style-event"];firstUpdated(e){super.firstUpdated(e),this.element.querySelector(".ueb-node-top").appendChild(this.createDelegatePinElement())}renderTop(){const e=this.renderNodeIcon(),t=this.renderNodeName(),i=this.element.getType()===ne.paths.customEvent&&(this.element.entity.CustomFunctionName||this.element.entity.FunctionReference.MemberParent);return H`
${e?H`
${e}
`:G} ${t?H`
${t} ${i?H`
Custom Event
`:G}
`:G}
`}createDelegatePinElement(){const e=me.getConstructor("ueb-pin").newObject(this.element.getPinEntities().find((e=>!e.isHidden()&&"delegate"===e.PinType.PinCategory)),new qt,this.element);return e.template.isNameRendered=!1,e}createPinElements(){return this.element.getPinEntities().filter((e=>!e.isHidden()&&"delegate"!==e.PinType.PinCategory)).map((e=>me.getConstructor("ueb-pin").newObject(e,void 0,this.element)))}}class Jt extends qt{render(){return this.element.isOutput()?super.render():H``}getOppositePin(){const e=this.element.nodeElement.template;return this.element.isOutput()?e.inputPin:e.outputPin}getLinkLocation(){const e=(this.element.isInput()?this.element.nodeElement.template.outputPin.template:this).iconElement.getBoundingClientRect(),t=[this.element.isInput()?e.left:e.right+1,(e.top+e.bottom)/2],i=ue.convertLocation(t,this.blueprint.template.gridElement);return this.blueprint.compensateTranslation(i[0],i[1])}}class ei extends zt{static#Le=new Set;#Me=null;#Ie;get inputPin(){return this.#Ie}#Be;get outputPin(){return this.#Be}initialize(e){super.initialize(e),this.element.classList.add("ueb-node-style-minimal")}findDirectionaPin(e){if(e.nodeElement.getType()!==ne.paths.knot||ei.#Le.has(e))return ei.#Le.clear(),!0;ei.#Le.add(e);for(let t of e.getLinks().map((e=>this.blueprint.getPin(e))))if(this.findDirectionaPin(t))return!0;return!1}render(){return H`
`}setupPins(){this.element.getPinElements().forEach((e=>this.element.querySelector(".ueb-node-border").appendChild(e)))}getPinElements(e){return e.querySelectorAll("ueb-pin")}createPinElements(){const e=this.element.getPinEntities().filter((e=>!e.isHidden())),t=e[e[0].isInput()?0:1],i=e[e[0].isOutput()?0:1],r=me.getConstructor("ueb-pin");return[this.#Ie=r.newObject(t,new Jt,this.element),this.#Be=r.newObject(i,new Jt,this.element)]}linksChanged(){}}class ti extends zt{static nodeStyleClasses=["ueb-node-style-metasound"]}class ii extends Ut{initialize(e){super.initialize(e);const t=e.getType();t===ne.paths.variableGet||t===ne.paths.self?(this.element.classList.add("ueb-node-style-getter"),this.displayName=""):t===ne.paths.variableSet&&this.element.classList.add("ueb-node-style-setter")}setupPins(){super.setupPins();let e=this.element.getPinElements().find((e=>!e.entity.isHidden()&&!e.entity.isExecution()));this.element.style.setProperty("--ueb-node-color",e.getColor().cssText)}}function ri(e){if(e.getClass()===ne.paths.callFunction||e.getClass()===ne.paths.commutativeAssociativeBinaryOperator||e.getClass()===ne.paths.callArrayFunction){const t=e.FunctionReference?.MemberParent?.path??"",i=e.FunctionReference?.MemberName;if(i&&(t===ne.paths.kismetMathLibrary||t===ne.paths.kismetArrayLibrary)){if(i.startsWith("Conv_"))return Yt;if(i.startsWith("Add_")||i.startsWith("And_")||i.startsWith("Boolean")||i.startsWith("Cross_")||i.startsWith("Dot_")||i.startsWith("Not_")||i.startsWith("Or_")||i.startsWith("Percent_")||i.startsWith("Xor_"))return Xt;switch(i){case"Abs":case"Array_Add":case"Array_AddUnique":case"Array_Identical":case"BMax":case"BMin":case"CrossProduct2D":case"DotProduct2D":case"Exp":case"FMax":case"FMin":case"GetPI":case"Max":case"MaxInt64":case"Min":case"MinInt64":case"Sqrt":case"Square":case"Vector4_CrossProduct3":case"Vector4_DotProduct":case"Vector4_DotProduct3":case"Acos":case"Asin":case"Cos":case"DegAcos":case"DegCos":case"DegSin":case"DegTan":case"Sin":case"Tan":return Xt}}if(t===ne.paths.blueprintSetLibrary)return Xt;if(t===ne.paths.blueprintMapLibrary)return Xt}switch(e.getClass()){case ne.paths.comment:case ne.paths.materialGraphNodeComment:return jt;case ne.paths.createDelegate:return zt;case ne.paths.metasoundEditorGraphExternalNode:return"Add"==e.ClassName?.Name?Kt:ti;case ne.paths.niagaraNodeOp:if(["Boolean::LogicEq","Boolean::LogicNEq","Numeric::Abs","Numeric::Add","Numeric::Mul"].includes(e.OpName))return Xt;break;case ne.paths.promotableOperator:return Xt;case ne.paths.knot:return ei;case ne.paths.literal:case ne.paths.self:case ne.paths.variableGet:case ne.paths.variableSet:return ii}return e.isEvent()?Qt:zt}class si extends Vt{static properties={...super.properties,selected:{type:Boolean,attribute:"data-selected",reflect:!0,converter:ue.booleanConverter}};dragHandler=e=>this.addLocation(...e.detail.value);constructor(){super(),this.selected=!1,this.listeningDrag=!1}setup(){super.setup(),this.setSelected(this.selected)}cleanup(){super.cleanup(),this.blueprint.removeEventListener(ne.nodeDragGeneralEventName,this.dragHandler)}setSelected(e=!0){this.selected=e,this.blueprint&&(this.selected?(this.listeningDrag=!0,this.blueprint.addEventListener(ne.nodeDragGeneralEventName,this.dragHandler)):(this.blueprint.removeEventListener(ne.nodeDragGeneralEventName,this.dragHandler),this.listeningDrag=!1))}}class ni extends si{static properties={...si.properties,typePath:{type:String,attribute:"data-type",reflect:!0},nodeTitle:{type:String,attribute:"data-title",reflect:!0},advancedPinDisplay:{type:String,attribute:"data-advanced-display",converter:Ue.attributeConverter,reflect:!0},enabledState:{type:String,attribute:"data-enabled-state",reflect:!0},nodeDisplayName:{type:String,attribute:!1},pureFunction:{type:Boolean,converter:ue.booleanConverter,attribute:"data-pure-function",reflect:!0}};static dragEventName=ne.nodeDragEventName;static dragGeneralEventName=ne.nodeDragGeneralEventName;get blueprint(){return super.blueprint}set blueprint(e){super.blueprint=e,this.#Ve.forEach((t=>t.blueprint=e))}#$e;get nodeNameElement(){return this.#$e}set nodeNameElement(e){this.#$e=e}#Ve=[];boundComments=[];#Oe=!1;#He=e=>{this.selected||this.#Oe||(this.#Oe=!0,this.requestUpdate(),this.updateComplete.then((()=>this.#Oe=!1)),this.addLocation(...e.detail.value))};static fromSerializedObject(e){e=e.trim();let t=Be.getSerializer(Et).read(e);return ni.newObject(t)}static newObject(e=new Et,t=new(ri(e))){const i=new ni;return i.initialize(e,t),i}#De(e){for(let t of this.getPinElements())for(let i of t.getLinks())this.blueprint.getPin(i).redirectLink(t,new at({objectName:e,pinGuid:t.entity.PinId}))}initialize(e=new Et,t=new(ri(e))){this.typePath=e.getType(),this.nodeTitle=e.getObjectName(),this.advancedPinDisplay=e.AdvancedPinDisplay?.toString(),this.enabledState=e.EnabledState,this.nodeDisplayName=_e(e),this.pureFunction=e.bIsPureFunc,this.dragLinkObjects=[],super.initialize(e,t),this.#Ve=this.template.createPinElements(),super.setLocation(this.entity.getNodePosX(),this.entity.getNodePosY()),this.entity.NodeWidth&&this.entity.NodeHeight?(this.sizeX=this.entity.NodeWidth.value,this.sizeY=this.entity.NodeHeight.value):this.updateComplete.then((()=>this.computeSizes())),e.listenAttribute("Name",(t=>{this.nodeTitle=e.Name,this.nodeDisplayName=_e(e),this.#De(t)}))}async getUpdateComplete(){let e=await super.getUpdateComplete();for(const t of this.getPinElements())e&&=await t.updateComplete;return e}bindToComment(e){e==this||this.boundComments.includes(e)||(e.addEventListener(ne.nodeDragEventName,this.#He),this.boundComments.push(e))}unbindFromComment(e){const t=this.boundComments.indexOf(e);t>=0&&(e.removeEventListener(ne.nodeDragEventName,this.#He),this.boundComments[t]=this.boundComments[this.boundComments.length-1],this.boundComments.pop())}isInsideComment(e){return this.topBoundary()>=e.topBoundary()&&this.rightBoundary()<=e.rightBoundary()&&this.bottomBoundary()<=e.bottomBoundary()&&this.leftBoundary()>=e.leftBoundary()}getType(){return this.entity.getType()}getNodeName(){return this.entity.getObjectName()}computeNodeDisplayName(){this.nodeDisplayName=_e(this.entity)}setNodeWidth(e){this.entity.setNodeWidth(e),this.sizeX=e,this.acknowledgeReflow()}setNodeHeight(e){this.entity.setNodeHeight(e),this.sizeY=e,this.acknowledgeReflow()}sanitizeLinks(e=[]){this.getPinElements().forEach((t=>t.sanitizeLinks(e)))}getPinElements(){return this.#Ve}getPinEntities(){return this.entity.getPinEntities()}setLocation(e=0,t=0,i=!0){this.entity.setNodePosX(e),this.entity.setNodePosY(t),super.setLocation(e,t,i)}acknowledgeReflow(){this.requestUpdate(),this.updateComplete.then((()=>this.computeSizes()));let e=new CustomEvent(ne.nodeReflowEventName);this.dispatchEvent(e)}setShowAdvancedPinDisplay(e){this.entity.AdvancedPinDisplay=new Ue(e?"Shown":"Hidden"),this.advancedPinDisplay=this.entity.AdvancedPinDisplay}toggleShowAdvancedPinDisplay(){this.setShowAdvancedPinDisplay("Shown"!=this.entity.AdvancedPinDisplay?.toString())}}class ai extends Et{#Ge=new Map;#Re=[];get objectEntities(){return this.#Re}getHomonymObjectEntity(e){const t=e.getObjectName(!1);return this.#Re.find((e=>e.getObjectName()==t))}takeFreeName(e){e=e.replace(/_\d+$/,"");const t=(this.#Ge.get(e)??-1)+1;return this.#Ge.set(e,t),ne.nodeTitle(e,t)}addObjectEntity(e){if(!this.#Re.includes(e)){this.#Re.push(e);const[t,i]=e.getNameAndCounter();return this.#Ge.set(t,Math.max(this.#Ge.get(t)??0,i)),!0}return!1}removeObjectEntity(e){const t=this.#Re.indexOf(e);if(t>=0){const e=this.#Re.pop();return te.OriginalChangeId.value==t.OriginalChangeId.value));if(t.length===this.ScriptVariables.length)return this;const i=t.concat(t).map(((i,r)=>{const s=ne.subObjectAttributeNameFromReference(i.ScriptVariable,r>=t.length);return[s,this[s]??e[s]]}));return i.push(...Object.entries(this).filter((([e,t])=>!e.startsWith(ne.subObjectAttributeNamePrefix)&&"ExportedNodes"!==e))),new ai(Object.fromEntries(i))}}class oi{static same=e=>e;static notWrapped=(e,t)=>t;static bracketsWrapped=(e,t)=>`(${t})`;constructor(e,t=((e,t)=>t),i=",",r=!1,s="=",n=oi.same){this.entityType=e,this.wrap=t,this.attributeSeparator=i,this.trailingSeparator=r,this.attributeValueConjunctionSign=s,this.attributeKeyPrinter=n}read(e){return this.doRead(e.trim())}write(e,t=!1){return this.doWrite(e,t)}doRead(e){const t=$e.grammarFor(void 0,this.entityType).run(e);if(!t.status)throw new Error(this.entityType?`Error when trying to parse the entity ${this.entityType.prototype.constructor.name}`:"Error when trying to parse null");return t.value}doWrite(e,t=!1,i="",r=this.wrap,s=this.attributeSeparator,n=this.trailingSeparator,a=this.attributeValueConjunctionSign,o=this.attributeKeyPrinter){let l="";const c=e._keys??Object.keys(e);let u=!0;for(const r of c){const n=e[r];if(void 0!==n&&this.showProperty(e,r)){let c=e instanceof Array?`(${r})`:r;oe.getAttribute(e,r,"quoted")&&(c=`"${c}"`);const h=oe.getAttribute(e,r,"serialized");if(u?u=!1:l+=s,oe.getAttribute(e,r,"inlined")){l+=this.doWrite(n,t,i,oi.notWrapped,s,!1,a,oe.getAttribute(e,r,"type")instanceof Array?e=>o(`${c}${e}`):e=>o(`${c}.${e}`));continue}const p=o(c),d=s.includes("\n")?i:"";l+=(p.length?d+p+this.attributeValueConjunctionSign:"")+(h?`"${this.doWriteValue(n,!0,i)}"`:this.doWriteValue(n,t,i))}}return n&&l.length&&(l+=s),r(e,l)}doWriteValue(e,t,i=""){const r=ue.getType(e),s=Be.getSerializer(r);if(!s)throw new Error(`Unknown value type "${r.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`);return s.doWrite(e,t,i)}showProperty(e,t){if(e instanceof Ve){if(oe.getAttribute(e,t,"ignored"))return!1;if(oe.getAttribute(e,t,"silent")){let i=oe.getAttribute(e,t,"default");if(i instanceof Function&&(i=i(e)),ue.equals(e[t],i))return!1}}return!0}}class li extends oi{constructor(e=Et){super(e,void 0,"\n",!0,void 0,oi.same)}showProperty(e,t){switch(t){case"Class":case"Name":case"Archetype":case"ExportPath":case"CustomProperties":return!1}return super.showProperty(e,t)}write(e,t=!1){return this.doWrite(e,t)+"\n"}doRead(e){return $e.grammarFor(void 0,this.entityType).parse(e)}readMultiple(e){return Et.getMultipleObjectsGrammar().parse(e)}doWrite(e,t,i="",r=this.wrap,s=this.attributeSeparator,n=this.trailingSeparator,a=this.attributeValueConjunctionSign,o=this.attributeKeyPrinter){const l=i+ne.indentation;if(!(e instanceof Et))return super.doWrite(e,t,i,r,s,n,a,(t=>e[t]instanceof Et?"":o(t)));return i+"Begin Object"+(e.Class?.type||e.Class?.path?` Class=${this.doWriteValue(e.Class,t)}`:"")+(e.Name?` Name=${this.doWriteValue(e.Name,t)}`:"")+(e.Archetype?` Archetype=${this.doWriteValue(e.Archetype,t)}`:"")+(e.ExportPath?.type||e.ExportPath?.path?` ExportPath=${this.doWriteValue(e.ExportPath,t)}`:"")+"\n"+super.doWrite(e,t,l,r,s,!0,a,(t=>e[t]instanceof Et?"":o(t)))+(oe.getAttribute(e,"CustomProperties","ignored")?"":e.getCustomproperties().map((e=>l+o("CustomProperties ")+Be.getSerializer(vt).doWrite(e,t)+this.attributeSeparator)).join(""))+i+"End Object"}}class ci extends Nt{static#_e=new li;#ze;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let r=this;this.#ze=()=>r.copied()}listenEvents(){window.addEventListener("copy",this.#ze)}unlistenEvents(){window.removeEventListener("copy",this.#ze)}getSerializedText(){const e=this.blueprint.getNodes(!0).map((e=>e.entity)),t=e.filter((e=>e.isExported)).map((e=>ci.#_e.write(e,!1))),i=e.filter((e=>!e.isExported)).map((e=>ci.#_e.write(e,!1)));return t.length&&(this.blueprint.entity.ExportedNodes=btoa(t.join("")),i.splice(0,0,ci.#_e.write(this.blueprint.entity,!1)),delete this.blueprint.entity.ExportedNodes),i.join("")}copied(){const e=this.getSerializedText();return navigator.clipboard.writeText(e),e}}class ui extends Nt{static#_e=new li;#Fe;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let r=this;this.#Fe=()=>r.cut()}listenEvents(){window.addEventListener("cut",this.#Fe)}unlistenEvents(){window.removeEventListener("cut",this.#Fe)}getSerializedText(){return this.blueprint.getNodes(!0).map((e=>ui.#_e.write(e.entity,!1))).join("")}cut(){this.blueprint.template.getCopyInputObject().copied(),this.blueprint.removeGraphElement(...this.blueprint.getNodes(!0))}}class hi extends Nt{static#_e=new li;#je;constructor(e,t,i={}){i.listenOnFocus??=!0,i.unlistenOnTextEdit??=!0,super(e,t,i);let r=this;this.#je=e=>r.pasted(e.clipboardData.getData("Text"))}listenEvents(){window.addEventListener("paste",this.#je)}unlistenEvents(){window.removeEventListener("paste",this.#je)}pasted(e){let t=0,i=0,r=0,s=hi.#_e.readMultiple(e).map((e=>{let s=me.getConstructor("ueb-node").newObject(e);return t+=s.locationY,i+=s.locationX,++r,s}));t/=r,i/=r,s.length>0&&this.blueprint.unselectAll();let n=this.blueprint.mousePosition;return s.forEach((e=>{e.addLocation(n[0]-i,n[1]-t),e.snapToGrid(),e.setSelected(!0)})),this.blueprint.addGraphElement(...s),s}}class pi extends kt{static#$=e=>{};#We=0;get variation(){return this.#We}#Ue=e=>{this.enablerKey&&!this.enablerActivated||(e.preventDefault(),this.#We=e.deltaY,this.setLocationFromEvent(e),this.wheel())};#Ke=e=>e.preventDefault();constructor(e,t,i={},r=pi.#$){i.listenOnFocus=!0,i.strictTarget??=!1,super(e,t,i),this.strictTarget=i.strictTarget,this.onWheel=r}listenEvents(){this.movementSpace.addEventListener("wheel",this.#Ue,!1),this.movementSpace.parentElement?.addEventListener("wheel",this.#Ke)}unlistenEvents(){this.movementSpace.removeEventListener("wheel",this.#Ue,!1),this.movementSpace.parentElement?.removeEventListener("wheel",this.#Ke)}wheel(){this.onWheel(this)}}class di extends pi{#Ye=0;#Xe=!1;get enableZoonIn(){return this.#Xe}set enableZoonIn(e){e!=this.#Xe&&(this.#Xe=e)}wheel(){if(this.#Ye+=-this.variation,Math.abs(this.#Ye)0||(e+=Math.sign(this.#Ye),this.blueprint.setZoom(e,this.location),this.#Ye=0)}}class mi extends Pt{#Ze;constructor(e,t,i={}){i.activationKeys=de.enableZoomIn,super(e,t,i)}fire(){this.#Ze=this.blueprint.template.getZoomInputObject(),this.#Ze.enableZoonIn=!0}unfire(){this.#Ze.enableZoonIn=!1}}class gi extends $t{startDrag(){this.blueprint.scrolling=!0}dragTo(e,t){this.blueprint.scrollDelta(-t[0],-t[1])}endDrag(){this.blueprint.scrolling=!1}}class bi extends kt{#qe=null;#Qe=e=>{e.preventDefault(),this.setLocationFromEvent(e),this.blueprint.mousePosition=[...this.location]};#Je=e=>{this.#qe||(e.preventDefault(),this.#qe=e.detail.tracker,this.unlistenMouseMove())};#et=e=>{this.#qe==e.detail.tracker&&(e.preventDefault(),this.#qe=null,this.listenMouseMove())};constructor(e,t,i={}){i.listenOnFocus=!0,super(e,t,i)}listenMouseMove(){this.target.addEventListener("mousemove",this.#Qe)}unlistenMouseMove(){this.target.removeEventListener("mousemove",this.#Qe)}listenEvents(){this.listenMouseMove(),this.blueprint.addEventListener(ne.trackingMouseEventName.begin,this.#Je),this.blueprint.addEventListener(ne.trackingMouseEventName.end,this.#et)}unlistenEvents(){this.unlistenMouseMove(),this.blueprint.removeEventListener(ne.trackingMouseEventName.begin,this.#Je),this.blueprint.removeEventListener(ne.trackingMouseEventName.end,this.#et)}}class vi extends $t{constructor(e,t,i={}){i.scrollGraphEdge??=!0,super(e,t,i),this.selectorElement=this.blueprint.template.selectorElement}startDrag(){this.selectorElement.beginSelect(this.clickedPosition)}dragTo(e,t){this.selectorElement.selectTo(e)}endDrag(){this.started&&this.selectorElement.endSelect()}unclicked(){this.started||this.blueprint.unselectAll()}}class yi extends Nt{#tt=e=>this.clickedSomewhere(e.target);constructor(e,t,i={}){i.listenOnFocus=!0,super(e,t,i),this.blueprint.focus&&document.addEventListener("click",this.#tt)}clickedSomewhere(e){e.closest("ueb-blueprint")||this.blueprint.setFocused(!1)}listenEvents(){document.addEventListener("click",this.#tt)}unlistenEvents(){document.removeEventListener("click",this.#tt)}}class fi extends Lt{static styleVariables={"--ueb-font-size":`${ne.fontSize}`,"--ueb-grid-axis-line-color":`${ne.gridAxisLineColor}`,"--ueb-grid-expand":`${ne.expandGridSize}px`,"--ueb-grid-line-color":`${ne.gridLineColor}`,"--ueb-grid-line-width":`${ne.gridLineWidth}px`,"--ueb-grid-set-line-color":`${ne.gridSetLineColor}`,"--ueb-grid-set":`${ne.gridSet}`,"--ueb-grid-size":`${ne.gridSize}px`,"--ueb-link-min-width":`${ne.linkMinWidth}`,"--ueb-node-radius":`${ne.nodeRadius}px`};#it=new ResizeObserver((e=>{const t=e.find((e=>e.target===this.viewportElement))?.devicePixelContentBoxSize?.[0];t&&(this.viewportSize[0]=t.inlineSize,this.viewportSize[1]=t.blockSize)}));#rt;#st;#Ze;headerElement;overlayElement;viewportElement;selectorElement;gridElement;linksContainerElement;nodesContainerElement;viewportSize=[0,0];initialize(e){super.initialize(e),this.element.style.cssText=Object.entries(fi.styleVariables).map((([e,t])=>`${e}:${t};`)).join("");const t=this.element.querySelector(":scope > template")?.content.textContent;t&&(this.element.requestUpdate(),this.element.updateComplete.then((()=>{this.blueprint.mousePosition=[Math.round(this.viewportSize[0]/2),Math.round(this.viewportSize[1]/2)],this.getPasteInputObject().pasted(t),this.blueprint.unselectAll()})))}setup(){super.setup(),this.#it.observe(this.viewportElement,{box:"device-pixel-content-box"});const e=this.viewportElement.getBoundingClientRect();this.viewportSize[0]=e.width,this.viewportSize[1]=e.height,this.blueprint.nodes.length>0&&(this.blueprint.requestUpdate(),this.blueprint.updateComplete.then((()=>this.centerContentInViewport())))}cleanup(){super.cleanup(),this.#it.unobserve(this.viewportElement)}createInputObjects(){const e=this.element.getGridDOMElement();return this.#rt=new ci(e,this.blueprint),this.#st=new hi(e,this.blueprint),this.#Ze=new di(e,this.blueprint),[...super.createInputObjects(),this.#rt,this.#st,this.#Ze,new ui(e,this.blueprint),new Pt(e,this.blueprint,{activationKeys:de.duplicateNodes},(()=>this.blueprint.template.getPasteInputObject().pasted(this.blueprint.template.getCopyInputObject().copied()))),new Pt(e,this.blueprint,{activationKeys:de.deleteNodes},(()=>this.blueprint.removeGraphElement(...this.blueprint.getNodes(!0)))),new Pt(e,this.blueprint,{activationKeys:de.selectAllNodes},(()=>this.blueprint.selectAll())),new vi(e,this.blueprint,{clickButton:ne.mouseClickButton,exitAnyButton:!0,moveEverywhere:!0}),new gi(e,this.blueprint,{clickButton:ne.mouseRightClickButton,exitAnyButton:!1,moveEverywhere:!0}),new yi(e,this.blueprint),new bi(e,this.blueprint),new mi(e,this.blueprint)]}render(){return H`
Zoom ${0==this.blueprint.zoom?"1:1":(this.blueprint.zoom>0?"+":"")+this.blueprint.zoom}
`}firstUpdated(e){super.firstUpdated(e),this.headerElement=this.blueprint.querySelector(".ueb-viewport-header"),this.overlayElement=this.blueprint.querySelector(".ueb-viewport-overlay"),this.viewportElement=this.blueprint.querySelector(".ueb-viewport-body"),this.selectorElement=this.blueprint.querySelector("ueb-selector"),this.gridElement=this.viewportElement.querySelector(".ueb-grid"),this.linksContainerElement=this.blueprint.querySelector("[data-links]"),this.linksContainerElement.append(...this.blueprint.getLinks()),this.nodesContainerElement=this.blueprint.querySelector("[data-nodes]"),this.nodesContainerElement.append(...this.blueprint.getNodes()),this.viewportElement.scroll(ne.expandGridSize,ne.expandGridSize)}willUpdate(e){super.willUpdate(e),this.headerElement&&e.has("zoom")&&(this.headerElement.classList.add("ueb-zoom-changed"),this.headerElement.addEventListener("animationend",(()=>this.headerElement.classList.remove("ueb-zoom-changed"))))}updated(e){if(super.updated(e),(e.has("scrollX")||e.has("scrollY"))&&this.viewportElement.scroll(this.blueprint.scrollX,this.blueprint.scrollY),e.has("zoom")){this.blueprint.style.setProperty("--ueb-scale",this.blueprint.getScale());const t=e.get("zoom"),i=Math.min(t,this.blueprint.zoom),r=Math.max(t,this.blueprint.zoom),s=ue.range(i,r),n=e=>`ueb-zoom-${e}`;te<0)).map(n)),this.blueprint.classList.add(...s.filter((e=>e>0)).map(n))):(this.blueprint.classList.remove(...s.filter((e=>e>0)).map(n)),this.blueprint.classList.add(...s.filter((e=>e<0)).map(n)))}}getCommentNodes(e=!1){return this.blueprint.querySelectorAll(`ueb-node[data-type="${ne.paths.comment}"]${e?'[data-selected="true"]':""}, ueb-node[data-type="${ne.paths.materialGraphNodeComment}"]${e?'[data-selected="true"]':""}`)}getPin(e){return this.blueprint.querySelector(`ueb-node[data-title="${e.objectName}"] ueb-pin[data-id="${e.pinGuid}"]`)}getCopyInputObject(){return this.#rt}getPasteInputObject(){return this.#st}getZoomInputObject(){return this.#Ze}isPointVisible(e,t){return!1}gridTopVisibilityBoundary(){return this.blueprint.scaleCorrect(this.blueprint.scrollY)-this.blueprint.translateY}gridRightVisibilityBoundary(){return this.gridLeftVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[0])}gridBottomVisibilityBoundary(){return this.gridTopVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[1])}gridLeftVisibilityBoundary(){return this.blueprint.scaleCorrect(this.blueprint.scrollX)-this.blueprint.translateX}centerViewport(e=0,t=0,i=!0){const r=this.gridLeftVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[0]/2),s=this.gridTopVisibilityBoundary()+this.blueprint.scaleCorrect(this.viewportSize[1]/2);this.blueprint.scrollDelta(this.blueprint.scaleCorrectReverse(e-r),this.blueprint.scaleCorrectReverse(t-s),i)}centerContentInViewport(e=!0){let t=0,i=0,r=Number.MAX_SAFE_INTEGER,s=Number.MIN_SAFE_INTEGER,n=Number.MAX_SAFE_INTEGER,a=Number.MIN_SAFE_INTEGER;const o=this.blueprint.getNodes();for(const e of o)t+=e.leftBoundary()+e.rightBoundary(),i+=e.topBoundary()+e.bottomBoundary(),r=Math.min(r,e.leftBoundary()),s=Math.max(s,e.rightBoundary()),n=Math.min(n,e.topBoundary()),a=Math.max(a,e.bottomBoundary());t=Math.round(s-r<=this.viewportSize[0]?(s+r)/2:t/(2*o.length)),i=Math.round(a-n<=this.viewportSize[1]?(a+n)/2:i/(2*o.length)),this.centerViewport(t,i,e)}}class wi extends he{static properties={selecting:{type:Boolean,attribute:"data-selecting",reflect:!0,converter:ue.booleanConverter},scrolling:{type:Boolean,attribute:"data-scrolling",reflect:!0,converter:ue.booleanConverter},focused:{type:Boolean,attribute:"data-focused",reflect:!0,converter:ue.booleanConverter},zoom:{type:Number,attribute:"data-zoom",reflect:!0},scrollX:{type:Number,attribute:!1},scrollY:{type:Number,attribute:!1},additionalX:{type:Number,attribute:!1},additionalY:{type:Number,attribute:!1},translateX:{type:Number,attribute:!1},translateY:{type:Number,attribute:!1}};static nodeBoundariesSupplier=e=>({primaryInf:e.leftBoundary(!0),primarySup:e.rightBoundary(!0),secondaryInf:e.topBoundary(!0),secondarySup:e.bottomBoundary(!0)});static nodeSelectToggleFunction=(e,t)=>{e.setSelected(t)};#nt=0;#at=0;nodes=[];links=[];nodesNames=new Map;mousePosition=[0,0];waitingExpandUpdate=!1;constructor(){super(),this.selecting=!1,this.scrolling=!1,this.focused=!1,this.zoom=0,this.scrollX=ne.expandGridSize,this.scrollY=ne.expandGridSize,this.translateX=ne.expandGridSize,this.translateY=ne.expandGridSize,super.initialize(new ai,new fi)}initialize(){}getGridDOMElement(){return this.template.gridElement}getScroll(){return[this.scrollX,this.scrollY]}setScroll(e,t){this.scrollX=e,this.scrollY=t}scrollDelta(e=0,t=0,i=!1,r=ne.smoothScrollTime){if(i){let i=[0,0];this.#nt&&cancelAnimationFrame(this.#nt),this.#at&&cancelAnimationFrame(this.#at),ue.animate(0,e,r,(e=>{this.scrollDelta(e-i[0],0,!1),i[0]=e}),(e=>this.#nt=e)),ue.animate(0,t,r,(e=>{this.scrollDelta(0,e-i[1],!1),i[1]=e}),(e=>this.#at=e))}else{const i=[2*ne.expandGridSize,2*ne.expandGridSize];let r=this.getScroll(),s=[r[0]+e,r[1]+t],n=[0,0];for(let e=0;e<2;++e)s[e]i[e]-ne.gridExpandThreshold*ne.expandGridSize&&(n[e]=1);0==n[0]&&0==n[1]||this.seamlessExpand(n[0],n[1]),r=this.getScroll(),s=[r[0]+e,r[1]+t],this.setScroll(s[0],s[1])}}scrollCenter(e=!1){const t=this.getScroll(),i=[this.translateX-t[0],this.translateY-t[1]],r=this.getViewportSize().map((e=>e/2)),s=[i[0]-r[0],i[1]-r[1]];this.scrollDelta(s[0],s[1],e)}getViewportSize(){return[this.template.viewportElement.clientWidth,this.template.viewportElement.clientHeight]}getScrollMax(){return[this.template.viewportElement.scrollWidth-this.template.viewportElement.clientWidth,this.template.viewportElement.scrollHeight-this.template.viewportElement.clientHeight]}snapToGrid(e,t){return ue.snapToGrid(e,t,ne.gridSize)}seamlessExpand(e,t){e=Math.round(e),t=Math.round(t);let i=this.getScale();[e,t]=[-e*ne.expandGridSize,-t*ne.expandGridSize],0!=e&&(this.scrollX+=e,e/=i),0!=t&&(this.scrollY+=t,t/=i),this.translateX+=e,this.translateY+=t}progressiveSnapToGrid(e){return ne.expandGridSize*Math.round(e/ne.expandGridSize+.5*Math.sign(e))}getZoom(){return this.zoom}setZoom(e,t){if((e=ue.clamp(e,ne.minZoom,ne.maxZoom))==this.zoom)return;let i=this.getScale();if(this.zoom=e,t){t[0]+=this.translateX,t[1]+=this.translateY;let e=this.getScale()/i,r=[e*t[0],e*t[1]];this.scrollDelta((r[0]-t[0])*i,(r[1]-t[1])*i)}}getScale(){return ne.scale[this.getZoom()]}scaleCorrect(e){return e/this.getScale()}scaleCorrectReverse(e){return e*this.getScale()}compensateTranslation(e,t){return[e-=this.translateX,t-=this.translateY]}getNodes(e=!1,[t,i,r,s]=[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER]){let n=this.nodes;return e&&(n=n.filter((e=>e.selected))),(t>Number.MIN_SAFE_INTEGER||iNumber.MIN_SAFE_INTEGER)&&(n=n.filter((e=>e.topBoundary()>=t&&e.rightBoundary()<=i&&e.bottomBoundary()<=r&&e.leftBoundary()>=s))),n}getCommentNodes(e=!1){let t=[...this.template.getCommentNodes(e)];return 0===t.length&&(t=this.nodes.filter((t=>t.getType()===ne.paths.comment&&(!e||t.selected)))),t}getPin(e){let t=this.template.getPin(e);return t&&t.nodeElement.getNodeName()==e.objectName.toString()||(t=[...this.nodes.find((t=>e.objectName.toString()==t.getNodeName()))?.getPinElements()??[]].find((t=>e.pinGuid.toString()==t.getPinId().toString()))),t}getLinks(e=null,t=null){if(null==e!=(null==t)){const i=e??t;return this.links.filter((e=>e.source==i||e.destination==i))}return null!=e&&null!=t?this.links.filter((i=>i.source==e&&i.destination==t||i.source==t&&i.destination==e)):this.links}getLink(e,t,i=!1){return this.links.find((r=>r.source==e&&r.destination==t||!i&&r.source==t&&r.destination==e))}selectAll(){this.getNodes().forEach((e=>wi.nodeSelectToggleFunction(e,!0)))}unselectAll(){this.getNodes().forEach((e=>wi.nodeSelectToggleFunction(e,!1)))}addGraphElement(...e){const t=e=>{const i=e.currentTarget;i.removeEventListener(ne.removeEventName,t);const[r,s]=i instanceof ni?[this.nodes,i.entity]:i instanceof Bt?[this.links]:null,n=r?.indexOf(i);if(n>=0){const e=r.pop();ne.entity.isExported=!0));continue}const e=i.entity.getObjectName(),r=this.entity.getHomonymObjectEntity(i.entity);r&&(r.Name=this.entity.takeFreeName(e)),this.nodes.push(i),this.entity.addObjectEntity(i.entity),i.addEventListener(ne.removeEventName,t),this.template.nodesContainerElement?.appendChild(i)}else i instanceof Bt&&!this.links.includes(i)&&(this.links.push(i),i.addEventListener(ne.removeEventName,t),this.template.linksContainerElement&&!this.template.linksContainerElement.contains(i)&&this.template.linksContainerElement.appendChild(i));e.filter((e=>e instanceof ni)).forEach((t=>t.sanitizeLinks(e))),e.filter((e=>e instanceof ni&&e.getType()==ne.paths.comment)).forEach((e=>e.updateComplete.then((()=>e.template.manageNodesBind()))))}removeGraphElement(...e){for(let t of e){if(t.closest("ueb-blueprint")!==this)return;t.remove()}}setFocused(e=!0){if(this.focused==e)return;let t=new CustomEvent(e?ne.focusEventName.begin:ne.focusEventName.end);this.focused=e,this.focused||this.unselectAll(),this.dispatchEvent(t)}acknowledgeEditText(e){const t=new CustomEvent(e?ne.editTextEventName.begin:ne.editTextEventName.end);this.dispatchEvent(t)}}customElements.define("ueb-blueprint",wi);class Si extends Gt{#ot;get locationChangeCallback(){return this.#ot}set locationChangeCallback(e){this.#ot=e}movementSpace;movementSpaceSize=[0,0];firstUpdated(e){super.firstUpdated(e),this.movementSpace=this.element.parentElement}setup(){super.setup();const e=this.movementSpace.getBoundingClientRect();this.movementSpaceSize=[e.width,e.height]}createDraggableObject(){return new Ot(this.element,this.blueprint,{draggableElement:this.movementSpace,ignoreTranslateCompensate:!0,moveEverywhere:!0,movementSpace:this.movementSpace,repositionOnClick:!0,stepSize:1})}adjustLocation(e,t){return this.locationChangeCallback?.(e,t),[e,t]}}class Ei extends Si{adjustLocation(e,t){const i=Math.round(this.movementSpaceSize[0]/2);e-=i,t=-(t-i);let[r,s]=ue.getPolarCoordinates(e,t);return r=Math.min(r,i),[e,t]=ue.getCartesianCoordinates(r,s),this.locationChangeCallback?.(e/i,t/i),[e=Math.round(e+i),t=Math.round(-t+i)]}}class Ci extends Vt{windowElement;setup(){super.setup(),this.windowElement=this.closest("ueb-window")}setLocation(e,t){super.setLocation(...this.template.adjustLocation(e,t))}}class Ni extends Ci{constructor(){super(),super.initialize({},new Ei)}static newObject(){return new Ni}initialize(){}}class xi extends Si{adjustLocation(e,t){return e=ue.clamp(e,0,this.movementSpaceSize[0]),t=ue.clamp(t,0,this.movementSpaceSize[1]),this.locationChangeCallback?.(e/this.movementSpaceSize[0],1-t/this.movementSpaceSize[1]),[e,t]}}class Pi extends Ci{constructor(){super(),super.initialize({},new xi)}static newObject(){return new Pi}initialize(){}}class ki extends $t{constructor(e,t,i={}){i.consumeEvent=!0,super(e,t,i)}}class Ti extends Lt{#lt;#ct;#ut=e=>this.element.selectedOption=e.target.selectedOptions[0].value;render(){return H` `}firstUpdated(e){super.firstUpdated(e),this.#lt=this.element.querySelector("select:first-child"),this.#ct=this.element.querySelector("select:last-child");const t=new Event("input",{bubbles:!0});this.#lt.dispatchEvent(t)}updated(e){super.updated(e);const t=this.#ct.getBoundingClientRect();this.element.style.setProperty("--ueb-dropdown-width",t.width+"px")}createInputObjects(){return[...super.createInputObjects(),new ki(this.element,this.blueprint)]}setSelectedValue(e){this.element.querySelector(`option[value="${e}"]`).defaultSelected=!0}getSelectedValue(){return this.#lt.value}}class Ai extends he{static properties={...super.properties,options:{type:Object},selectedOption:{type:String}};constructor(){super(),super.initialize({},new Ti),this.options=[],this.selectedOption=""}static newObject(e){return new Ai}initialize(){}getValue(){return this.template.getSelectedValue()}}class Li extends Lt{#ht=()=>{this.blueprint.acknowledgeEditText(!0),this.element.selectOnFocus&&getSelection().selectAllChildren(this.element)};#pt=()=>{this.blueprint.acknowledgeEditText(!1),getSelection().removeAllRanges()};#dt=e=>e.target.querySelectorAll("br").forEach((e=>e.remove()));#mt=e=>{"Enter"!=e.code||e.shiftKey||e.target.blur()};initialize(e){super.initialize(e),this.element.classList.add("ueb-pin-input-content"),this.element.setAttribute("role","textbox"),this.element.contentEditable="true"}firstUpdated(e){super.firstUpdated(e);const t=new Event("input",{bubbles:!0});this.element.dispatchEvent(t)}createInputObjects(){return[...super.createInputObjects(),new ki(this.element,this.blueprint)]}setup(){super.setup(),this.element.addEventListener("focus",this.#ht),this.element.addEventListener("focusout",this.#pt),this.element.singleLine&&this.element.addEventListener("input",this.#dt),this.element.blurOnEnter&&this.element.addEventListener("keydown",this.#mt)}cleanup(){super.cleanup(),this.element.removeEventListener("focus",this.#ht),this.element.removeEventListener("focusout",this.#pt),this.element.removeEventListener("input",this.#dt),this.element.removeEventListener("keydown",this.#mt)}}class Mi extends he{static properties={...super.properties,singleLine:{type:Boolean,attribute:"data-single-line",converter:ue.booleanConverter,reflect:!0},selectOnFocus:{type:Boolean,attribute:"data-select-focus",converter:ue.booleanConverter,reflect:!0},blurOnEnter:{type:Boolean,attribute:"data-blur-enter",converter:ue.booleanConverter,reflect:!0}};constructor(){super(),this.singleLine=!1,this.selectOnFocus=!0,this.blurOnEnter=!0,super.initialize({},new Li)}static newObject(){return new Mi}initialize(){}}class Ii extends Zt{#gt;#bt=()=>this.element.setDefaultValue(this.#gt.checked);firstUpdated(e){super.firstUpdated(e),this.#gt=this.element.querySelector(".ueb-pin-input")}setup(){super.setup(),this.#gt?.addEventListener("change",this.#bt)}cleanup(){super.cleanup(),this.#gt?.removeEventListener("change",this.#bt)}createInputObjects(){return[...super.createInputObjects(),new ki(this.#gt,this.blueprint)]}renderInput(){return H``}}class Bi extends Zt{static singleLineInput=!1;static selectOnFocus=!0;static saveEachInputChange=!1;#vt;get inputWrapper(){return this.#vt}#yt;static stringFromInputToUE(e){return e.replace(/(?=\n\s*)\n$/,"")}static stringFromUEToInput(e){return e.replaceAll(/(?:\r|(?<=(?:^|[^\\])(?:\\\\)*)\\r)(?=\n)/g,"").replace(/(?<=\n\s*)$/,"\n")}#ft=()=>this.setInputs(this.getInputs(),!0);#wt=e=>this.#St(e.target);#St(e){const t=this.blueprint.scaleCorrect(this.#vt.getBoundingClientRect().width)+this.nameWidth,i=this.element.classList.contains("ueb-pin-input-wrap");!i&&t>ne.pinInputWrapWidth?this.element.classList.add("ueb-pin-input-wrap"):i&&t<=ne.pinInputWrapWidth&&this.element.classList.remove("ueb-pin-input-wrap")}firstUpdated(e){super.firstUpdated(e);this.constructor.canWrapInput&&this.isInputRendered()&&(this.element.addEventListener("input",this.#wt),this.nameWidth=this.blueprint.scaleCorrect(this.element.querySelector(".ueb-pin-name")?.getBoundingClientRect().width??0)),this.#vt=this.element.querySelector(".ueb-pin-input-wrapper"),this.#yt=[...this.element.querySelectorAll("ueb-input")]}setup(){super.setup();const e=this.constructor;e.saveEachInputChange?this.element.addEventListener("input",this.#ft):this.element.addEventListener("focusout",this.#ft),e.canWrapInput&&this.isInputRendered()&&(this.element.addEventListener("input",this.#wt),this.element.nodeElement.addEventListener(ne.nodeReflowEventName,this.#wt))}cleanup(){super.cleanup(),this.element.nodeElement.removeEventListener(ne.nodeReflowEventName,this.#wt),this.element.removeEventListener("input",this.#wt),this.element.removeEventListener("input",this.#ft),this.element.removeEventListener("focusout",this.#ft)}getInput(){return this.getInputs().reduce(((e,t)=>e+t),"")}getInputs(){return this.#yt.map((e=>ue.clearHTMLWhitespace(e.innerHTML)))}setInputs(e=[],t=!0){this.#yt.forEach(this.constructor.singleLineInput?(t,i)=>t.innerText=e[i]:(t,i)=>t.innerText=e[i].replaceAll("\n","")),t&&this.setDefaultValue(e.map((e=>Bi.stringFromInputToUE(e))),e),this.element.requestUpdate(),this.element.nodeElement.acknowledgeReflow()}setDefaultValue(e=[],t=e){this.element.setDefaultValue(e.join(""))}renderInput(){const e=this.constructor,t=e.singleLineInput,i=e.selectOnFocus;return H`
`}}class Vi extends Zt{renderIcon(){return De.execPin}renderName(){let e=this.element.entity.PinName;if(this.element.entity.PinFriendlyName)e=this.element.entity.PinFriendlyName.toString();else if("execute"===e||"then"===e)return H``;return H`${this.element.getPinDisplayName()}`}}class $i extends Bi{static singleLineInput=!0;setInputs(e=[],t=!1){if(e&&0!=e.length||(e=[this.getInput()]),super.setInputs(e,!1),t){let i=[];for(const r of e){let e=parseFloat(r);isNaN(e)&&(e=0,t=!1),i.push(e)}this.setDefaultValue(i,e)}}setDefaultValue(e=[],t){this.element.setDefaultValue(e[0]),this.element.requestUpdate()}}class Oi extends $i{setDefaultValue(e=[],t){this.element.setDefaultValue(new Ke(e[0])),this.element.requestUpdate()}renderInput(){return H`
`}} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */const Pi=1;class Ni{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}} + */const Hi=1;class Di{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}} /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */const ki="important",Ti=" !"+ki,Ai=(e=>(...t)=>({_$litDirective$:e,values:t}))(class extends Ni{constructor(e){var t;if(super(e),e.type!==Pi||"style"!==e.name||(null===(t=e.strings)||void 0===t?void 0:t.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(e){return Object.keys(e).reduce(((t,i)=>{const s=e[i];return null==s?t:t+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`}),"")}update(e,[t]){const{style:i}=e.element;if(void 0===this.ht){this.ht=new Set;for(const e in t)this.ht.add(e);return this.render(t)}this.ht.forEach((e=>{null==t[e]&&(this.ht.delete(e),e.includes("-")?i.removeProperty(e):i[e]="")}));for(const e in t){const s=t[e];if(null!=s){this.ht.add(e);const t="string"==typeof s&&s.endsWith(Ti);e.includes("-")||t?i.setProperty(e,t?s.slice(0,-11):s,t?ki:""):i[e]=s}}return D}});class Li extends Ft{toggleAdvancedDisplayHandler;getDraggableElement(){return this.element.querySelector(".ueb-window-top")}createDraggableObject(){return new Ot(this.element,this.blueprint,{draggableElement:this.getDraggableElement(),ignoreScale:!0,ignoreTranslateCompensate:!1,movementSpace:this.blueprint,stepSize:1})}setup(){const e=this.blueprint.template.gridLeftVisibilityBoundary(),t=this.blueprint.template.gridTopVisibilityBoundary();this.element.locationX=this.blueprint.scaleCorrectReverse(this.blueprint.mousePosition[0]-e),this.element.locationY=this.blueprint.scaleCorrectReverse(this.blueprint.mousePosition[1]-t),this.element.updateComplete.then((()=>{const e=this.blueprint.getBoundingClientRect();this.element.locationX+this.element.sizeX>e.width&&(this.element.locationX=e.width-this.element.sizeX),this.element.locationX=Math.max(0,this.element.locationX),this.element.locationY+this.element.sizeY>e.height&&(this.element.locationY=e.height-this.element.sizeY),this.element.locationY=Math.max(0,this.element.locationY)}))}render(){return G`
${this.renderWindowName()}
${Ae.close}
${this.renderContent()}
`}renderWindowName(){return G`Window`}renderContent(){return G``}apply(){this.element.dispatchEvent(new CustomEvent(ne.windowApplyEventName)),this.element.remove()}cancel(){this.element.dispatchEvent(new CustomEvent(ne.windowCancelEventName)),this.element.remove()}}class Mi extends Li{#Ct;#xt;#Pt;#Nt;#kt;#Tt;#At;#Lt;#Mt;#It;#Bt=e=>{const t=he.clearHTMLWhitespace(e.target.innerHTML),i=parseInt(t,16);isNaN(i)||(this.color.setFromRGBANumber(i),this.element.requestUpdate())};#$t=e=>{const t=he.clearHTMLWhitespace(e.target.innerHTML),i=parseInt(t,16);isNaN(i)||(this.color.setFromSRGBANumber(i),this.element.requestUpdate())};#Ht=e=>t=>{"Enter"==t.code&&(t.preventDefault(),e(t))};#Vt=new ze;get color(){return this.#Vt}set color(e){e.toNumber()!=this.color?.toNumber()&&(this.element.requestUpdate("color",this.#Vt),this.#Vt=e)}#Gt=new ze;get fullColor(){return this.#Gt}#Dt;get initialColor(){return this.#Dt}#Ot=new ze;#Rt(e,t,i=!1){const s=this.color.toRGBAString(),r=`${s.substring(0,2*e)}${t}${s.substring(2+2*e)}`;return i?`${r.substring(0,6)}FF`:r}initialize(e){super.initialize(e),this.#Dt=this.element.windowOptions.getPinColor(),this.color.setFromHSVA(this.initialColor.H.value,this.initialColor.S.value,this.initialColor.V.value,this.initialColor.A.value),this.fullColor.setFromHSVA(this.color.H.value,1,1,1)}firstUpdated(e){this.#Ct=this.element.querySelector(".ueb-color-picker-wheel ueb-color-handler"),this.#xt=this.element.querySelector(".ueb-color-picker-saturation ueb-ui-slider"),this.#Pt=this.element.querySelector(".ueb-color-picker-value ueb-ui-slider"),this.#Nt=this.element.querySelector(".ueb-color-picker-r ueb-ui-slider"),this.#kt=this.element.querySelector(".ueb-color-picker-g ueb-ui-slider"),this.#Tt=this.element.querySelector(".ueb-color-picker-b ueb-ui-slider"),this.#At=this.element.querySelector(".ueb-color-picker-a ueb-ui-slider"),this.#Lt=this.element.querySelector(".ueb-color-picker-h ueb-ui-slider"),this.#Mt=this.element.querySelector(".ueb-color-picker-s ueb-ui-slider"),this.#It=this.element.querySelector(".ueb-color-picker-v ueb-ui-slider"),this.#Ct.template.locationChangeCallback=(e,t)=>{this.color.setFromWheelLocation(e,t,this.color.V.value,this.color.A.value),this.fullColor.setFromHSVA(this.color.H.value,1,1,1),this.element.requestUpdate()},this.#xt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,t,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#Pt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,this.color.S.value,t,this.color.A.value),this.element.requestUpdate()},this.#Nt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(e,this.color.G.value,this.color.B.value,this.color.A.value),this.element.requestUpdate()},this.#kt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,e,this.color.B.value,this.color.A.value),this.element.requestUpdate()},this.#Tt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,this.color.G.value,e,this.color.A.value),this.element.requestUpdate()},this.#At.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,this.color.G.value,this.color.B.value,e),this.element.requestUpdate()},this.#Lt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(e,this.color.S.value,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#Mt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,e,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#It.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,this.color.S.value,e,this.color.A.value),this.element.requestUpdate()}}renderSlider(e){let t="",i=0,s="";const r=e=>`linear-gradient(to right, #${this.#Rt(e,"00",!0)}, #${this.#Rt(e,"ff",!0)})`;switch(e){case 0:t="r",i=this.color.R.value,s=r(e);break;case 1:t="g",i=this.color.G.value,s=r(e);break;case 2:t="b",i=this.color.B.value,s=r(e);break;case 3:t="a",i=this.color.A.value,s=`${ne.alphaPattern}, ${r(e)}`;break;case 4:t="h",i=360*this.color.H.value,s="linear-gradient(to right, #f00 0%, #ff0 16.666%, #0f0 33.333%, #0ff 50%, #00f 66.666%, #f0f 83.333%, #f00 100%)";break;case 5:t="s",i=this.color.S.value,s=`linear-gradient(to right,#${this.#Ot.setFromHSVA(this.color.H.value,0,this.color.V.value,1),this.#Ot.toRGBAString()},#${this.#Ot.setFromHSVA(this.color.H.value,1,this.color.V.value,1),this.#Ot.toRGBAString()})`;break;case 6:t="v",i=this.color.V.value,s=`linear-gradient(to right, #000, #${this.fullColor.toRGBAString()})`}return s=`background: ${s};`,G`
${t.toUpperCase()}
`}renderContent(){const e=2*this.color.H.value*Math.PI,t={"--ueb-color-r":this.color.R.toString(),"--ueb-color-g":this.color.G.toString(),"--ueb-color-b":this.color.B.toString(),"--ueb-color-a":this.color.A.toString(),"--ueb-color-h":this.color.H.toString(),"--ueb-color-s":this.color.S.toString(),"--ueb-color-v":this.color.V.toString(),"--ueb-color-wheel-x":100*(this.color.S.value*Math.cos(e)*.5+.5)+"%","--ueb-color-wheel-y":100*(this.color.S.value*Math.sin(e)*.5+.5)+"%"},i=this.color.toRGBAString(),s=this.color.toSRGBAString(),r=this.fullColor.toRGBAString();return G`
Old
New
Advanced
${this.renderSlider(0)} ${this.renderSlider(1)} ${this.renderSlider(2)} ${this.renderSlider(3)}
${this.renderSlider(4)} ${this.renderSlider(5)} ${this.renderSlider(6)}
Hex Linear
Hex sRGB
${ne.windowApplyButtonText}
${ne.windowCancelButtonText}
`}renderWindowName(){return G`${ne.colorWindowName}`}}class Ii extends Qt{#zt;#_t=e=>{e.preventDefault(),this.blueprint.setFocused(!0),this.#zt=gt.getConstructor("ueb-window").newObject({type:new Mi,windowOptions:{getPinColor:()=>this.element.defaultValue,setPinColor:e=>this.element.setDefaultValue(e)}}),this.blueprint.append(this.#zt);const t=()=>{this.element.setDefaultValue(this.#zt.template.color)},i=()=>{this.#zt.removeEventListener(ne.windowApplyEventName,t),this.#zt.removeEventListener(ne.windowCloseEventName,i),this.#zt=null};this.#zt.addEventListener(ne.windowApplyEventName,t),this.#zt.addEventListener(ne.windowCloseEventName,i)};renderInput(){return G``}}class Bi extends fi{static singleLineInput=!0}class $i extends Ei{setDefaultValue(e=[],t=e){this.element.setDefaultValue(e[0])}renderInput(){return G`
`}}class Hi extends Qt{renderIcon(){return Ae.referencePin}}class Vi extends Ei{#Ft(){return he.printNumber(this.element.getDefaultValue()?.R??0)}#jt(){return he.printNumber(this.element.getDefaultValue()?.P??0)}#Ut(){return he.printNumber(this.element.getDefaultValue()?.Y??0)}setDefaultValue(e=[],t=e){const i=this.element.getDefaultValue(!0);if(!(i instanceof it))throw new TypeError("Expected DefaultValue to be a RotatorEntity");i.R=e[0],i.P=e[1],i.Y=e[2],this.element.requestUpdate("DefaultValue",i)}renderInput(){return G`
X
Y
Z
`}}class Gi extends fi{}class Di extends Ei{#Wt(){return he.printNumber(this.element.getDefaultValue()?.X??0)}#Ut(){return he.printNumber(this.element.getDefaultValue()?.Y??0)}setDefaultValue(e,t){const i=this.element.getDefaultValue(!0);if(!(i instanceof et))throw new TypeError("Expected DefaultValue to be a Vector2DEntity");i.X=e[0],i.Y=e[1],this.element.requestUpdate("DefaultValue",i)}renderInput(){return G`
X
Y
`}}class Oi extends Ei{#Wt(){return he.printNumber(this.element.getDefaultValue()?.X??0)}#Ut(){return he.printNumber(this.element.getDefaultValue()?.Y??0)}#Kt(){return he.printNumber(this.element.getDefaultValue()?.Z??0)}setDefaultValue(e,t){const i=this.element.getDefaultValue(!0);if(!(i instanceof nt))throw new TypeError("Expected DefaultValue to be a VectorEntity");i.X=e[0],i.Y=e[1],i.Z=e[2],this.element.requestUpdate("DefaultValue",i)}renderInput(){return G`
X
Y
Z
`}}class Ri extends Ct{static#Yt={bool:yi,byte:xi,enum:wi,int:xi,int64:Ci,MUTABLE_REFERENCE:Hi,name:Bi,rg:Di,real:$i,string:Gi,[ne.paths.linearColor]:Ii,[ne.paths.rotator]:Vi,[ne.paths.vector]:Oi,[ne.paths.vector2D]:Di};static properties={pinId:{type:He,converter:{fromAttribute:(e,t)=>e?He.grammar.parse(e):null,toAttribute:(e,t)=>e?.toString()},attribute:"data-id",reflect:!0},pinType:{type:String,attribute:"data-type",reflect:!0},advancedView:{type:String,attribute:"data-advanced-view",reflect:!0},color:{type:ze,converter:{fromAttribute:(e,t)=>e?ze.getLinearColorFromAnyFormat().parse(e):null,toAttribute:(e,t)=>e?he.printLinearColor(e):null},attribute:"data-color",reflect:!0},defaultValue:{type:String,attribute:!1},isLinked:{type:Boolean,converter:he.booleanConverter,attribute:"data-linked",reflect:!0},pinDirection:{type:String,attribute:"data-direction",reflect:!0},connectable:{type:Boolean,converter:he.booleanConverter,attribute:"data-connectable",reflect:!0}};nodeElement;static getTypeTemplate(e){return"Array"===e.PinType.ContainerType?.toString()?Qt:e.PinType.bIsReference&&!e.PinType.bIsConst?Ri.#Yt.MUTABLE_REFERENCE:"exec"===e.getType()?Si:(e.isInput()?Ri.#Yt[e.getType()]:Qt)??Qt}static newObject(e=new ot,t=new(Ri.getTypeTemplate(e)),i=void 0){const s=new Ri;return s.initialize(e,t,i),s}initialize(e=new ot,t=new(Ri.getTypeTemplate(e)),i=void 0){this.nodeElement=i,this.advancedView=e.bAdvancedView,this.isLinked=!1,this.connectable=!e.bNotConnectable,super.initialize(e,t),this.pinType=this.entity.getType(),this.defaultValue=this.entity.getDefaultValue(),this.color=Ri.properties.color.converter.fromAttribute(this.getColor().toString()),this.pinDirection=e.isInput()?"input":e.isOutput()?"output":"hidden"}setup(){super.setup(),this.nodeElement=this.closest("ueb-node")}createPinReference(){return new Qe({objectName:this.nodeElement.getNodeName(),pinGuid:this.getPinId()})}getPinId(){return this.entity.PinId}getPinName(){return this.entity.PinName}getPinDisplayName(){return this.entity.pinDisplayName()}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(e=!1){return this.defaultValue=this.entity.getDefaultValue(e)}setDefaultValue(e){this.entity.DefaultValue=e,this.defaultValue=e,this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName()}sanitizeLinks(e=[]){this.entity.LinkedTo=this.entity.LinkedTo?.filter((t=>{let i=this.blueprint.getPin(t);if(i){if(e.length&&!e.includes(i.nodeElement))return!1;let t=this.blueprint.getLink(this,i);t||(t=gt.getConstructor("ueb-link").newObject(this,i),this.blueprint.addGraphElement(t))}return i})),this.isLinked=this.entity.isLinked()}linkTo(e){const t=this.createPinReference();!this.isLinked||!this.isOutput()||"exec"!==this.pinType&&"exec"!==e.pinType||this.getLinks().some((e=>t.equals(e)))||this.unlinkFromAll(),this.entity.linkTo(e.getNodeElement().getNodeName(),e.entity)&&(this.isLinked=this.entity.isLinked(),this.nodeElement?.template.linksChanged(),this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName())}unlinkFrom(e,t=!0){this.entity.unlinkFrom(e.getNodeElement().getNodeName(),e.entity)&&(this.isLinked=this.entity.isLinked(),this.nodeElement?.template.linksChanged(),t&&this.blueprint.getLink(this,e)?.remove(),this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName())}unlinkFromAll(){const e=this.getLinks().length;this.getLinks().map((e=>this.blueprint.getPin(e))).forEach((e=>this.unlinkFrom(e))),e&&this.nodeElement?.template.linksChanged()}redirectLink(e,t){const i=this.getLinks().findIndex((t=>t.objectName.toString()==e.getNodeElement().getNodeName()&&t.pinGuid.valueOf()==e.entity.PinId.valueOf()));return i>=0&&(this.entity.LinkedTo[i]=t,!0)}}class zi{constructor(e=(e=>e),t=null){this.array=new Uint32Array(t),this.comparisonValueSupplier=e,this.length=0,this.currentPosition=0}get(e){return e>=0&&e=0&&this.currentPosition=0&&this.currentPosition0?this.get(this.currentPosition-1):null}getPrevValue(){return this.currentPosition>0?this.comparisonValueSupplier(this.get(this.currentPosition-1)):Number.MIN_SAFE_INTEGER}shiftLeft(e,t=1){this.array.set(this.array.subarray(e+t),e)}shiftRight(e,t=1){this.array.set(this.array.subarray(e,-t),e+t)}}class _i{constructor(e,t,i,s){this.initialPosition=e,this.finalPosition=e,this.metadata=new Array(t.length),this.primaryOrder=new zi((e=>this.metadata[e].primaryBoundary)),this.secondaryOrder=new zi((e=>this.metadata[e].secondaryBoundary)),this.selectFunc=s,this.rectangles=t,this.primaryOrder.reserve(this.rectangles.length),this.secondaryOrder.reserve(this.rectangles.length),t.forEach(((e,t)=>{let r={primaryBoundary:this.initialPosition[0],secondaryBoundary:this.initialPosition[1],rectangle:t,onSecondaryAxis:!1};this.metadata[t]=r,s(e,!1);const n=i(e);this.initialPosition[1]{if(this.metadata[i].onSecondaryAxis)this.selectFunc(this.rectangles[i],s);else if(s){this.secondaryOrder.insert(i,e[1]);const s=this.metadata[i].secondaryBoundary;Math.sign(e[1]-s)==t[1]&&Math.sign(s-this.initialPosition[1])==t[1]&&this.selectFunc(this.rectangles[i],!0)}else this.selectFunc(this.rectangles[i],!1),this.secondaryOrder.remove(i);this.computeBoundaries(),this.selectTo(e)};e[0]this.boundaries.primaryN.v&&e[0]this.boundaries.primaryP.v&&(++this.primaryOrder.currentPosition,i(this.boundaries.primaryP.i,this.initialPosition[0]{this.selectFunc(this.rectangles[t],i),this.computeBoundaries(),this.selectTo(e)};e[1]this.boundaries.secondaryN.v&&e[1]this.boundaries.secondaryP.v&&(++this.secondaryOrder.currentPosition,s(this.boundaries.secondaryP.i,this.initialPosition[1]Ui.#Xt[e],toAttribute:(e,t)=>Object.entries(Ui.#Xt).find((([t,i])=>e.constructor===i))?.[0]}}};static newObject(e={},t=e.type??new Li){const i=new Ui;return i.initialize(e,t),i}initialize(e={},t=e.type??new Li){e.windowOptions??={},this.type=e.type,this.windowOptions=e.windowOptions,super.initialize(e,t)}computeSizes(){const e=this.getBoundingClientRect();this.sizeX=e.width,this.sizeY=e.height}cleanup(){super.cleanup(),this.acknowledgeClose()}acknowledgeClose(){let e=new CustomEvent(ne.windowCloseEventName);this.dispatchEvent(e)}}class Wi extends Ie{static attributes={...super.attributes,TerminalCategory:le.createType(String),TerminalSubCategory:le.createType(String),bTerminalIsConst:le.createType(Boolean),bTerminalIsWeakPointer:le.createType(Boolean),bTerminalIsUObjectWrapper:le.createType(Boolean)};constructor(e){super(e),this.TerminalCategory,this.TerminalSubCategory,this.bTerminalIsConst,this.bTerminalIsWeakPointer,this.bTerminalIsUObjectWrapper}}class Ki extends Ie{static grammar=this.createGrammar();static createGrammar(){return Te.seq(Te.reg(new RegExp(`(${Be.Regex.Path.source}|${Be.Regex.Symbol.source}\\s*)?\\(\\s*`),1),Te.seq(Be.attributeName,Be.equalSeparation).map((([e,t])=>e)).chain((e=>Be.unknownValue.map((t=>i=>i[e]=t)))).sepBy(Be.commaSeparation),Te.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{e??="";let s={};return e.length&&(s.lookbehind=e),t.forEach((e=>e(s))),new this(s)}))}constructor(e){super(e,!0)}}class Yi extends ht{#Zt;constructor(e,t){super(t),this.#Zt=e}doWrite(e,t,i=""){return this.#Zt(e,t)}}class Xi extends ht{constructor(e){super(e)}doWrite(e,t,i=""){return t||e.constructor!==String?he.escapeString(e.toString()):`"${he.escapeString(e.toString())}"`}}Be.unknownValue=Te.alt(Be.boolean,He.createGrammar(),Te.str("None").map((()=>new Ve({type:"None"}))),Be.null,Be.number,Ve.fullReferenceGrammar,Be.string,Ye.createGrammar(),Ke.createGrammar(),Xe.createGrammar(),Qe.createGrammar(),nt.createGrammar(),it.createGrammar(),ze.createGrammar(),et.createGrammar(),Ki.createGrammar(),je.createGrammar(),Be.grammarFor(void 0,[Qe]),Be.grammarFor(void 0,[new ue(Number,String,je)])),Me.registerSerializer(null,new Yi(((e,t)=>"()"),null)),Me.registerSerializer(Array,new Yi(((e,t)=>`(${e.map((e=>Me.getSerializer(he.getType(e)).write(e,t)+",")).join("")})`),Array)),Me.registerSerializer(BigInt,new Xi(BigInt)),Me.registerSerializer(Boolean,new Yi(((e,t)=>e?t?"true":"True":t?"false":"False"),Boolean)),Me.registerSerializer(Fe,new Xi(Fe)),Me.registerSerializer(Re,new Xi(Re)),Me.registerSerializer(We,new Xi(We)),Me.registerSerializer(Ue,new Xi(Ue)),Me.registerSerializer(Xe,new Yi(((e,t)=>e.getLookbehind()+"("+e.value.map((e=>Me.getSerializer(he.getType(e)).write(e,t))).join(", ")+")"),Xe)),Me.registerSerializer(Ge,new ht(Ge,ht.bracketsWrapped)),Me.registerSerializer(He,new Xi(He)),Me.registerSerializer(De,new Xi(De)),Me.registerSerializer(Ze,new Xi(Ze)),Me.registerSerializer(Oe,new Xi(Oe)),Me.registerSerializer(Ke,new ht(Ke,((e,t)=>`${e.getLookbehind()}(${t})`),", ",!1,"",(()=>""))),Me.registerSerializer(vt,new ht(vt,ht.bracketsWrapped)),Me.registerSerializer(ze,new ht(ze,ht.bracketsWrapped)),Me.registerSerializer(Ye,new ht(Ye,((e,t)=>`${e.getLookbehind()}(${t})`),", ",!1,"",(()=>""))),Me.registerSerializer(_e,new ht(_e,ht.bracketsWrapped)),Me.registerSerializer(ce,new Yi(((e,t)=>Me.getSerializer(e.getTargetType()).write(e.get(),t)),ce)),Me.registerSerializer(Number,new Xi(Number)),Me.registerSerializer(ut,new pt),Me.registerSerializer(Ve,new Yi((e=>{let t=e.type??"",i=e.path??"",s=e.delim??"";t&&i&&he.isSerialized(e,"path")&&(i=s+i+s.split("").reverse().join(""));let r=t+i;return he.isSerialized(e,"type")&&(r=`"${r}"`),r}),Ve)),Me.registerSerializer(qe,new Xi(qe)),Me.registerSerializer(ot,new ht(ot,((e,t)=>`${e.getLookbehind()} (${t})`),",",!1)),Me.registerSerializer(Qe,new ht(Qe,void 0," ",!1,"",(()=>""))),Me.registerSerializer(Je,new ht(Je)),Me.registerSerializer(Wi,new ht(Wi,ht.bracketsWrapped)),Me.registerSerializer(tt,new Yi(((e,t)=>`X=${e.X} Y=${e.Y}`),tt)),Me.registerSerializer(it,new ht(it,ht.bracketsWrapped)),Me.registerSerializer(String,new Yi(((e,t)=>t?he.escapeString(e):`"${he.escapeString(e)}"`),String)),Me.registerSerializer(st,new Yi(((e,t)=>`${e.P}, ${e.Y}, ${e.R}`),st)),Me.registerSerializer(rt,new Yi(((e,t)=>`${e.X}, ${e.Y}`),rt)),Me.registerSerializer(at,new Yi(((e,t)=>`${e.X}, ${e.Y}, ${e.Z}`),at)),Me.registerSerializer(je,new Xi(je)),Me.registerSerializer(Ki,new ht(Ki,((e,t)=>`${e.getLookbehind()??""}(${t})`))),Me.registerSerializer(ct,new ht(ct,ht.bracketsWrapped)),Me.registerSerializer(et,new ht(et,ht.bracketsWrapped)),Me.registerSerializer(nt,new ht(nt,ht.bracketsWrapped)),function(){const e=(e,t)=>{customElements.define(e,t),gt.registerElement(e,t)};e("ueb-color-handler",ui),e("ueb-dropdown",gi),e("ueb-input",vi),e("ueb-link",Dt),e("ueb-node",ni),e("ueb-pin",Ri),e("ueb-selector",ji),e("ueb-ui-slider",pi),e("ueb-window",Ui)}();export{ai as Blueprint,ne as Configuration,Dt as LinkElement,ni as NodeElement,he as Utility}; + */const Gi="important",Ri=" !"+Gi,_i=(e=>(...t)=>({_$litDirective$:e,values:t}))(class extends Di{constructor(e){var t;if(super(e),e.type!==Hi||"style"!==e.name||(null===(t=e.strings)||void 0===t?void 0:t.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(e){return Object.keys(e).reduce(((t,i)=>{const r=e[i];return null==r?t:t+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${r};`}),"")}update(e,[t]){const{style:i}=e.element;if(void 0===this.ht){this.ht=new Set;for(const e in t)this.ht.add(e);return this.render(t)}this.ht.forEach((e=>{null==t[e]&&(this.ht.delete(e),e.includes("-")?i.removeProperty(e):i[e]="")}));for(const e in t){const r=t[e];if(null!=r){this.ht.add(e);const t="string"==typeof r&&r.endsWith(Ri);e.includes("-")||t?i.setProperty(e,t?r.slice(0,-11):r,t?Gi:""):i[e]=r}}return D}});class zi extends Rt{toggleAdvancedDisplayHandler;getDraggableElement(){return this.element.querySelector(".ueb-window-top")}createDraggableObject(){return new Ot(this.element,this.blueprint,{draggableElement:this.getDraggableElement(),ignoreScale:!0,ignoreTranslateCompensate:!1,movementSpace:this.blueprint,stepSize:1})}setup(){const e=this.blueprint.template.gridLeftVisibilityBoundary(),t=this.blueprint.template.gridTopVisibilityBoundary();this.element.locationX=this.blueprint.scaleCorrectReverse(this.blueprint.mousePosition[0]-e),this.element.locationY=this.blueprint.scaleCorrectReverse(this.blueprint.mousePosition[1]-t),this.element.updateComplete.then((()=>{const e=this.blueprint.getBoundingClientRect();this.element.locationX+this.element.sizeX>e.width&&(this.element.locationX=e.width-this.element.sizeX),this.element.locationX=Math.max(0,this.element.locationX),this.element.locationY+this.element.sizeY>e.height&&(this.element.locationY=e.height-this.element.sizeY),this.element.locationY=Math.max(0,this.element.locationY)}))}render(){return H`
${this.renderWindowName()}
${De.close}
${this.renderContent()}
`}renderWindowName(){return H`Window`}renderContent(){return H``}apply(){this.element.dispatchEvent(new CustomEvent(ne.windowApplyEventName)),this.element.remove()}cancel(){this.element.dispatchEvent(new CustomEvent(ne.windowCancelEventName)),this.element.remove()}}class Fi extends zi{#Et;#Ct;#Nt;#xt;#Pt;#kt;#Tt;#At;#Lt;#Mt;#It=e=>{const t=ue.clearHTMLWhitespace(e.target.innerHTML),i=parseInt(t,16);isNaN(i)||(this.color.setFromRGBANumber(i),this.element.requestUpdate())};#Bt=e=>{const t=ue.clearHTMLWhitespace(e.target.innerHTML),i=parseInt(t,16);isNaN(i)||(this.color.setFromSRGBANumber(i),this.element.requestUpdate())};#Vt=e=>t=>{"Enter"==t.code&&(t.preventDefault(),e(t))};#$t=new He;get color(){return this.#$t}set color(e){e.toNumber()!=this.color?.toNumber()&&(this.element.requestUpdate("color",this.#$t),this.#$t=e)}#Ot=new He;get fullColor(){return this.#Ot}#Ht;get initialColor(){return this.#Ht}#Dt=new He;#Gt(e,t,i=!1){const r=this.color.toRGBAString(),s=`${r.substring(0,2*e)}${t}${r.substring(2+2*e)}`;return i?`${s.substring(0,6)}FF`:s}initialize(e){super.initialize(e),this.#Ht=this.element.windowOptions.getPinColor(),this.color.setFromHSVA(this.initialColor.H.value,this.initialColor.S.value,this.initialColor.V.value,this.initialColor.A.value),this.fullColor.setFromHSVA(this.color.H.value,1,1,1)}firstUpdated(e){this.#Et=this.element.querySelector(".ueb-color-picker-wheel ueb-color-handler"),this.#Ct=this.element.querySelector(".ueb-color-picker-saturation ueb-ui-slider"),this.#Nt=this.element.querySelector(".ueb-color-picker-value ueb-ui-slider"),this.#xt=this.element.querySelector(".ueb-color-picker-r ueb-ui-slider"),this.#Pt=this.element.querySelector(".ueb-color-picker-g ueb-ui-slider"),this.#kt=this.element.querySelector(".ueb-color-picker-b ueb-ui-slider"),this.#Tt=this.element.querySelector(".ueb-color-picker-a ueb-ui-slider"),this.#At=this.element.querySelector(".ueb-color-picker-h ueb-ui-slider"),this.#Lt=this.element.querySelector(".ueb-color-picker-s ueb-ui-slider"),this.#Mt=this.element.querySelector(".ueb-color-picker-v ueb-ui-slider"),this.#Et.template.locationChangeCallback=(e,t)=>{this.color.setFromWheelLocation(e,t,this.color.V.value,this.color.A.value),this.fullColor.setFromHSVA(this.color.H.value,1,1,1),this.element.requestUpdate()},this.#Ct.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,t,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#Nt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,this.color.S.value,t,this.color.A.value),this.element.requestUpdate()},this.#xt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(e,this.color.G.value,this.color.B.value,this.color.A.value),this.element.requestUpdate()},this.#Pt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,e,this.color.B.value,this.color.A.value),this.element.requestUpdate()},this.#kt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,this.color.G.value,e,this.color.A.value),this.element.requestUpdate()},this.#Tt.template.locationChangeCallback=(e,t)=>{this.color.setFromRGBA(this.color.R.value,this.color.G.value,this.color.B.value,e),this.element.requestUpdate()},this.#At.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(e,this.color.S.value,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#Lt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,e,this.color.V.value,this.color.A.value),this.element.requestUpdate()},this.#Mt.template.locationChangeCallback=(e,t)=>{this.color.setFromHSVA(this.color.H.value,this.color.S.value,e,this.color.A.value),this.element.requestUpdate()}}renderSlider(e){let t="",i=0,r="";const s=e=>`linear-gradient(to right, #${this.#Gt(e,"00",!0)}, #${this.#Gt(e,"ff",!0)})`;switch(e){case 0:t="r",i=this.color.R.value,r=s(e);break;case 1:t="g",i=this.color.G.value,r=s(e);break;case 2:t="b",i=this.color.B.value,r=s(e);break;case 3:t="a",i=this.color.A.value,r=`${ne.alphaPattern}, ${s(e)}`;break;case 4:t="h",i=360*this.color.H.value,r="linear-gradient(to right, #f00 0%, #ff0 16.666%, #0f0 33.333%, #0ff 50%, #00f 66.666%, #f0f 83.333%, #f00 100%)";break;case 5:t="s",i=this.color.S.value,r=`linear-gradient(to right,#${this.#Dt.setFromHSVA(this.color.H.value,0,this.color.V.value,1),this.#Dt.toRGBAString()},#${this.#Dt.setFromHSVA(this.color.H.value,1,this.color.V.value,1),this.#Dt.toRGBAString()})`;break;case 6:t="v",i=this.color.V.value,r=`linear-gradient(to right, #000, #${this.fullColor.toRGBAString()})`}return r=`background: ${r};`,H`
${t.toUpperCase()}
`}renderContent(){const e=2*this.color.H.value*Math.PI,t={"--ueb-color-r":this.color.R.toString(),"--ueb-color-g":this.color.G.toString(),"--ueb-color-b":this.color.B.toString(),"--ueb-color-a":this.color.A.toString(),"--ueb-color-h":this.color.H.toString(),"--ueb-color-s":this.color.S.toString(),"--ueb-color-v":this.color.V.toString(),"--ueb-color-wheel-x":100*(this.color.S.value*Math.cos(e)*.5+.5)+"%","--ueb-color-wheel-y":100*(this.color.S.value*Math.sin(e)*.5+.5)+"%"},i=this.color.toRGBAString(),r=this.color.toSRGBAString(),s=this.fullColor.toRGBAString();return H`
Old
New
Advanced
${this.renderSlider(0)} ${this.renderSlider(1)} ${this.renderSlider(2)} ${this.renderSlider(3)}
${this.renderSlider(4)} ${this.renderSlider(5)} ${this.renderSlider(6)}
Hex Linear
Hex sRGB
${ne.windowApplyButtonText}
${ne.windowCancelButtonText}
`}renderWindowName(){return H`${ne.colorWindowName}`}}class ji extends $i{#Rt(){return ue.printNumber(this.element.getDefaultValue()?.X??0)}#_t(){return ue.printNumber(this.element.getDefaultValue()?.Y??0)}setDefaultValue(e,t){const i=this.element.getDefaultValue(!0);if(!(i instanceof lt))throw new TypeError("Expected DefaultValue to be a Vector2DEntity");i.X=e[0],i.Y=e[1],this.element.requestUpdate("DefaultValue",i)}renderInput(){return H`
X
Y
`}}class Wi extends $i{#Rt(){return ue.printNumber(this.element.getDefaultValue()?.X??0)}#_t(){return ue.printNumber(this.element.getDefaultValue()?.Y??0)}#zt(){return ue.printNumber(this.element.getDefaultValue()?.Z??0)}setDefaultValue(e,t){const i=this.element.getDefaultValue(!0);if(!(i instanceof gt))throw new TypeError("Expected DefaultValue to be a VectorEntity");i.X=e[0],i.Y=e[1],i.Z=e[2],this.element.requestUpdate("DefaultValue",i)}renderInput(){return H`
X
Y
Z
`}}const Ui={[ne.paths.linearColor]:class extends Zt{#Ft;#jt=e=>{e.preventDefault(),this.blueprint.setFocused(!0),this.#Ft=me.getConstructor("ueb-window").newObject({type:new Fi,windowOptions:{getPinColor:()=>this.element.defaultValue,setPinColor:e=>this.element.setDefaultValue(e)}}),this.blueprint.append(this.#Ft);const t=()=>{this.element.setDefaultValue(this.#Ft.template.color)},i=()=>{this.#Ft.removeEventListener(ne.windowApplyEventName,t),this.#Ft.removeEventListener(ne.windowCloseEventName,i),this.#Ft=null};this.#Ft.addEventListener(ne.windowApplyEventName,t),this.#Ft.addEventListener(ne.windowCloseEventName,i)};renderInput(){return H``}},[ne.paths.niagaraBool]:Ii,[ne.paths.niagaraPosition]:Wi,[ne.paths.rotator]:class extends $i{#Wt(){return ue.printNumber(this.element.getDefaultValue()?.R??0)}#Ut(){return ue.printNumber(this.element.getDefaultValue()?.P??0)}#_t(){return ue.printNumber(this.element.getDefaultValue()?.Y??0)}setDefaultValue(e=[],t=e){const i=this.element.getDefaultValue(!0);if(!(i instanceof ut))throw new TypeError("Expected DefaultValue to be a RotatorEntity");i.R=e[0],i.P=e[1],i.Y=e[2],this.element.requestUpdate("DefaultValue",i)}renderInput(){return H`
X
Y
Z
`}},[ne.paths.vector]:Wi,[ne.paths.vector2D]:ji,[ne.paths.vector3f]:Wi,[ne.paths.vector4f]:class extends $i{#Rt(){return ue.printNumber(this.element.getDefaultValue()?.X??0)}#_t(){return ue.printNumber(this.element.getDefaultValue()?.Y??0)}#zt(){return ue.printNumber(this.element.getDefaultValue()?.Z??0)}#Kt(){return ue.printNumber(this.element.getDefaultValue()?.W??0)}setDefaultValue(e,t){const i=this.element.getDefaultValue(!0);if(!(i instanceof dt))throw new TypeError("Expected DefaultValue to be a Vector4DEntity");i.X=e[0],i.Y=e[1],i.Z=e[2],i.W=e[3],this.element.requestUpdate("DefaultValue",i)}renderInput(){return H`
X
Y
Z
W
`}},bool:Ii,byte:Oi,enum:class extends Bi{static saveEachInputChange=!0;#Yt;#Xt=[];setup(){super.setup();const e=this.element.nodeElement.entity.EnumEntries;this.#Xt=e?.map((e=>(""===e&&(e="None"),[e,this.element.nodeElement.getPinEntities().find((t=>e===t.PinName))?.PinFriendlyName.toString()??e])))??ne.CommonEnums[this.element.entity.getSubCategory()]?.map((e=>e instanceof Array?e:[e,ue.formatStringName(e)]))??[];const t=this.element.getDefaultValue().toString();this.#Xt.find((([e,i])=>e===t))||this.#Xt.push([t,ue.formatStringName(t)]),this.element.requestUpdate()}renderInput(){return this.element.nodeElement.entity,H``}firstUpdated(e){super.firstUpdated(e),this.#Yt=this.element.querySelector("ueb-dropdown")}getInputs(){return[this.#Yt.getValue()]}},int:Oi,int64:class extends $i{setDefaultValue(e=[],t){this.element.setDefaultValue(new st(e[0])),this.element.requestUpdate()}renderInput(){return H`
`}},MUTABLE_REFERENCE:class extends Zt{renderIcon(){return De.referencePin}},name:class extends Bi{static singleLineInput=!0},rg:ji,real:class extends $i{setDefaultValue(e=[],t=e){this.element.setDefaultValue(e[0])}renderInput(){return H`
`}},string:class extends Bi{}};function Ki(e){return"Array"===e.PinType.ContainerType?.toString()?Zt:e.PinType.bIsReference&&!e.PinType.bIsConst?Ui.MUTABLE_REFERENCE:"exec"===e.getType()?Vi:(e.isInput()?Ui[e.getType()]:Zt)??Zt}class Yi extends he{static properties={pinId:{type:Fe,converter:{fromAttribute:(e,t)=>e?Fe.grammar.parse(e):null,toAttribute:(e,t)=>e?.toString()},attribute:"data-id",reflect:!0},pinType:{type:String,attribute:"data-type",reflect:!0},advancedView:{type:String,attribute:"data-advanced-view",reflect:!0},color:{type:He,converter:{fromAttribute:(e,t)=>e?He.getLinearColorFromAnyFormat().parse(e):null,toAttribute:(e,t)=>e?ue.printLinearColor(e):null},attribute:"data-color",reflect:!0},defaultValue:{type:String,attribute:!1},isLinked:{type:Boolean,converter:ue.booleanConverter,attribute:"data-linked",reflect:!0},pinDirection:{type:String,attribute:"data-direction",reflect:!0},connectable:{type:Boolean,converter:ue.booleanConverter,attribute:"data-connectable",reflect:!0}};nodeElement;static newObject(e=new vt,t=new(Ki(e)),i=void 0){const r=new Yi;return r.initialize(e,t,i),r}initialize(e=new vt,t=new(Ki(e)),i=void 0){this.nodeElement=i,this.advancedView=e.bAdvancedView,this.isLinked=!1,this.connectable=!e.bNotConnectable,super.initialize(e,t),this.pinType=this.entity.getType(),this.defaultValue=this.entity.getDefaultValue(),this.color=Yi.properties.color.converter.fromAttribute(this.getColor().toString()),this.pinDirection=e.isInput()?"input":e.isOutput()?"output":"hidden"}setup(){super.setup(),this.nodeElement=this.closest("ueb-node")}createPinReference(){return new at({objectName:this.nodeElement.getNodeName(),pinGuid:this.getPinId()})}getPinId(){return this.entity.PinId}getPinName(){return this.entity.PinName}getPinDisplayName(){return this.entity.pinTitle()}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(e=!1){return this.defaultValue=this.entity.getDefaultValue(e)}setDefaultValue(e){this.entity.DefaultValue=e,this.defaultValue=e,this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName()}sanitizeLinks(e=[]){this.entity.LinkedTo=this.entity.LinkedTo?.filter((t=>{let i=this.blueprint.getPin(t);if(i){if(e.length&&!e.includes(i.nodeElement))return!1;let t=this.blueprint.getLink(this,i);t||(t=me.getConstructor("ueb-link").newObject(this,i),this.blueprint.addGraphElement(t))}return i})),this.isLinked=this.entity.isLinked()}linkTo(e){const t=this.createPinReference();!this.isLinked||!this.isOutput()||"exec"!==this.pinType&&"exec"!==e.pinType||this.getLinks().some((e=>t.equals(e)))||this.unlinkFromAll(),this.entity.linkTo(e.getNodeElement().getNodeName(),e.entity)&&(this.isLinked=this.entity.isLinked(),this.nodeElement?.template.linksChanged(),this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName())}unlinkFrom(e,t=!0){this.entity.unlinkFrom(e.getNodeElement().getNodeName(),e.entity)&&(this.isLinked=this.entity.isLinked(),this.nodeElement?.template.linksChanged(),t&&this.blueprint.getLink(this,e)?.remove(),this.entity.recomputesNodeTitleOnChange&&this.nodeElement?.computeNodeDisplayName())}unlinkFromAll(){const e=this.getLinks().length;this.getLinks().map((e=>this.blueprint.getPin(e))).forEach((e=>this.unlinkFrom(e))),e&&this.nodeElement?.template.linksChanged()}redirectLink(e,t){const i=this.getLinks().findIndex((t=>t.objectName.toString()==e.getNodeElement().getNodeName()&&t.pinGuid.valueOf()==e.entity.PinId.valueOf()));return i>=0&&(this.entity.LinkedTo[i]=t,!0)}}class Xi{constructor(e=(e=>e),t=null){this.array=new Uint32Array(t),this.comparisonValueSupplier=e,this.length=0,this.currentPosition=0}get(e){return e>=0&&e=0&&this.currentPosition=0&&this.currentPosition0?this.get(this.currentPosition-1):null}getPrevValue(){return this.currentPosition>0?this.comparisonValueSupplier(this.get(this.currentPosition-1)):Number.MIN_SAFE_INTEGER}shiftLeft(e,t=1){this.array.set(this.array.subarray(e+t),e)}shiftRight(e,t=1){this.array.set(this.array.subarray(e,-t),e+t)}}class Zi{constructor(e,t,i,r){this.initialPosition=e,this.finalPosition=e,this.metadata=new Array(t.length),this.primaryOrder=new Xi((e=>this.metadata[e].primaryBoundary)),this.secondaryOrder=new Xi((e=>this.metadata[e].secondaryBoundary)),this.selectFunc=r,this.rectangles=t,this.primaryOrder.reserve(this.rectangles.length),this.secondaryOrder.reserve(this.rectangles.length),t.forEach(((e,t)=>{let s={primaryBoundary:this.initialPosition[0],secondaryBoundary:this.initialPosition[1],rectangle:t,onSecondaryAxis:!1};this.metadata[t]=s,r(e,!1);const n=i(e);this.initialPosition[1]{if(this.metadata[i].onSecondaryAxis)this.selectFunc(this.rectangles[i],r);else if(r){this.secondaryOrder.insert(i,e[1]);const r=this.metadata[i].secondaryBoundary;Math.sign(e[1]-r)==t[1]&&Math.sign(r-this.initialPosition[1])==t[1]&&this.selectFunc(this.rectangles[i],!0)}else this.selectFunc(this.rectangles[i],!1),this.secondaryOrder.remove(i);this.computeBoundaries(),this.selectTo(e)};e[0]this.boundaries.primaryN.v&&e[0]this.boundaries.primaryP.v&&(++this.primaryOrder.currentPosition,i(this.boundaries.primaryP.i,this.initialPosition[0]{this.selectFunc(this.rectangles[t],i),this.computeBoundaries(),this.selectTo(e)};e[1]this.boundaries.secondaryN.v&&e[1]this.boundaries.secondaryP.v&&(++this.secondaryOrder.currentPosition,r(this.boundaries.secondaryP.i,this.initialPosition[1]Ji.#Zt[e],toAttribute:(e,t)=>Object.entries(Ji.#Zt).find((([t,i])=>e.constructor===i))?.[0]}}};static newObject(e={},t=e.type??new zi){const i=new Ji;return i.initialize(e,t),i}initialize(e={},t=e.type??new zi){e.windowOptions??={},this.type=e.type,this.windowOptions=e.windowOptions,super.initialize(e,t)}computeSizes(){const e=this.getBoundingClientRect();this.sizeX=e.width,this.sizeY=e.height}cleanup(){super.cleanup(),this.acknowledgeClose()}acknowledgeClose(){let e=new CustomEvent(ne.windowCloseEventName);this.dispatchEvent(e)}}class er extends Ve{static attributes={...super.attributes,TerminalCategory:oe.createType(String),TerminalSubCategory:oe.createType(String),bTerminalIsConst:oe.createType(Boolean),bTerminalIsWeakPointer:oe.createType(Boolean),bTerminalIsUObjectWrapper:oe.createType(Boolean)};constructor(e){super(e),this.TerminalCategory,this.TerminalSubCategory,this.bTerminalIsConst,this.bTerminalIsWeakPointer,this.bTerminalIsUObjectWrapper}}class tr extends Ve{static grammar=this.createGrammar();static createGrammar(){return Me.seq(Me.reg(new RegExp(`(${$e.Regex.Path.source}|${$e.Regex.Symbol.source}\\s*)?\\(\\s*`),1),Me.seq($e.attributeName,$e.equalSeparation).map((([e,t])=>e)).chain((e=>$e.unknownValue.map((t=>i=>i[e]=t)))).sepBy($e.commaSeparation),Me.reg(/\s*(?:,\s*)?\)/)).map((([e,t,i])=>{e??="";let r={};return e.length&&(r.lookbehind=e),t.forEach((e=>e(r))),new this(r)}))}constructor(e){super(e,!0)}}class ir extends oi{#qt;constructor(e,t){super(t),this.#qt=e}doWrite(e,t,i=""){return this.#qt(e,t)}}class rr extends oi{constructor(e,t=!0){super(e),t&&(this.wrap=(e,t)=>ue.escapeString(t))}doWrite(e,t,i=""){return t||e.constructor!==String?this.wrap(e,e.toString()):`"${this.wrap(e,e.toString())}"`}}$e.unknownValue=Me.alt($e.boolean,Fe.grammar,Me.str("None").map((()=>new je({type:"None"}))),$e.null,$e.number,je.fullReferenceGrammar,$e.string,it.grammar,tt.grammar,rt.grammar,at.grammar,dt.grammar,gt.grammar,ut.grammar,He.grammar,lt.grammar,tr.grammar,Qe.grammar,$e.grammarFor(void 0,[at]),$e.grammarFor(void 0,[new ce(Number,String,Qe)]),Me.lazy((()=>$e.grammarFor(void 0,[void 0])))),Be.registerSerializer(null,new ir(((e,t)=>"()"),null)),Be.registerSerializer(Array,new ir(((e,t)=>`(${e.map((e=>Be.getSerializer(ue.getType(e)).write(e,t))).join(",")})`),Array)),Be.registerSerializer(BigInt,new rr(BigInt)),Be.registerSerializer(ai,new li(ai)),Be.registerSerializer(Boolean,new ir(((e,t)=>e?t?"true":"True":t?"false":"False"),Boolean)),Be.registerSerializer(qe,new rr(qe)),Be.registerSerializer(Oe,new rr(Oe)),Be.registerSerializer(et,new rr(et)),Be.registerSerializer(Je,new rr(Je)),Be.registerSerializer(rt,new ir(((e,t)=>e.getLookbehind()+"("+e.value.map((e=>Be.getSerializer(ue.getType(e)).write(e,t))).join(", ")+")"),rt)),Be.registerSerializer(We,new oi(We,oi.bracketsWrapped)),Be.registerSerializer(Fe,new rr(Fe)),Be.registerSerializer(Ue,new rr(Ue)),Be.registerSerializer(st,new rr(st)),Be.registerSerializer(Ke,new rr(Ke)),Be.registerSerializer(tt,new oi(tt,((e,t)=>`${e.getLookbehind()}(${t})`),", ",!1,"",(()=>""))),Be.registerSerializer(xt,new oi(xt,oi.bracketsWrapped)),Be.registerSerializer(He,new oi(He,oi.bracketsWrapped)),Be.registerSerializer(it,new oi(it,((e,t)=>`${e.getLookbehind()}(${t})`),", ",!1,"",(()=>""))),Be.registerSerializer(Ye,new oi(Ye,oi.bracketsWrapped)),Be.registerSerializer(le,new ir(((e,t)=>Be.getSerializer(e.getTargetType()).write(e.get(),t)),le)),Be.registerSerializer(Number,new rr(Number)),Be.registerSerializer(Et,new li),Be.registerSerializer(je,new rr(je,!1)),Be.registerSerializer(nt,new rr(nt)),Be.registerSerializer(vt,new oi(vt,((e,t)=>`${e.getLookbehind()} (${t})`),",",!0)),Be.registerSerializer(at,new oi(at,void 0," ",!1,"",(()=>""))),Be.registerSerializer(ot,new oi(ot)),Be.registerSerializer(er,new oi(er,oi.bracketsWrapped)),Be.registerSerializer(ct,new ir(((e,t)=>`X=${e.X} Y=${e.Y}`),ct)),Be.registerSerializer(ut,new oi(ut,oi.bracketsWrapped)),Be.registerSerializer(yt,new oi(yt,oi.bracketsWrapped)),Be.registerSerializer(String,new ir(((e,t)=>t?ue.escapeString(e):`"${ue.escapeString(e)}"`),String)),Be.registerSerializer(ht,new ir(((e,t)=>`${e.P}, ${e.Y}, ${e.R}`),ht)),Be.registerSerializer(pt,new ir(((e,t)=>`${e.X}, ${e.Y}`),pt)),Be.registerSerializer(bt,new ir(((e,t)=>`${e.X}, ${e.Y}, ${e.Z}`),bt)),Be.registerSerializer(mt,new ir(((e,t)=>`${e.X}, ${e.Y}, ${e.Z}, ${e.W}`),mt)),Be.registerSerializer(Qe,new rr(Qe)),Be.registerSerializer(tr,new oi(tr,((e,t)=>`${e.getLookbehind()??""}(${t})`))),Be.registerSerializer(wt,new oi(wt,oi.bracketsWrapped)),Be.registerSerializer(lt,new oi(lt,oi.bracketsWrapped)),Be.registerSerializer(gt,new oi(gt,oi.bracketsWrapped)),Be.registerSerializer(dt,new oi(dt,oi.bracketsWrapped)),function(){const e=(e,t)=>{customElements.define(e,t),me.registerElement(e,t)};e("ueb-color-handler",Ni),e("ueb-dropdown",Ai),e("ueb-input",Mi),e("ueb-link",Bt),e("ueb-node",ni),e("ueb-pin",Yi),e("ueb-selector",Qi),e("ueb-ui-slider",Pi),e("ueb-window",Ji)}();export{wi as Blueprint,ne as Configuration,Bt as LinkElement,ni as NodeElement,ue as Utility}; diff --git a/js/Blueprint.js b/js/Blueprint.js index 492b3d71..5f386ce0 100755 --- a/js/Blueprint.js +++ b/js/Blueprint.js @@ -1,11 +1,12 @@ -import BlueprintTemplate from "./template/BlueprintTemplate.js" import Configuration from "./Configuration.js" +import Utility from "./Utility.js" import IElement from "./element/IElement.js" import LinkElement from "./element/LinkElement.js" import NodeElement from "./element/NodeElement.js" -import Utility from "./Utility.js" +import BlueprintEntity from "./entity/BlueprintEntity.js" +import BlueprintTemplate from "./template/BlueprintTemplate.js" -/** @extends {IElement} */ +/** @extends {IElement} */ export default class Blueprint extends IElement { static properties = { @@ -72,8 +73,6 @@ export default class Blueprint extends IElement { node.setSelected(selected) } - /** @type {Map} */ - #nodeNameCounter = new Map() #xScrollingAnimationId = 0 #yScrollingAnimationId = 0 /** @type {NodeElement[]}" */ @@ -96,7 +95,7 @@ export default class Blueprint extends IElement { this.scrollY = Configuration.expandGridSize this.translateX = Configuration.expandGridSize this.translateY = Configuration.expandGridSize - super.initialize({}, new BlueprintTemplate()) + super.initialize(new BlueprintEntity(), new BlueprintTemplate()) } initialize() { @@ -390,10 +389,10 @@ export default class Blueprint extends IElement { const removeEventHandler = event => { const target = event.currentTarget target.removeEventListener(Configuration.removeEventName, removeEventHandler) - const graphElementsArray = target instanceof NodeElement - ? this.nodes + const [graphElementsArray, entity] = target instanceof NodeElement + ? [this.nodes, target.entity] : target instanceof LinkElement - ? this.links + ? [this.links] : null // @ts-expect-error const index = graphElementsArray?.indexOf(target) @@ -403,24 +402,27 @@ export default class Blueprint extends IElement { graphElementsArray[index] = last } } + if (entity) { + this.entity.removeObjectEntity(entity) + } } for (const element of graphElements) { element.blueprint = this if (element instanceof NodeElement && !this.nodes.includes(element)) { + if (element.getType() == Configuration.paths.niagaraClipboardContent) { + this.entity = this.entity.mergeWith(element.entity) + const additionalSerialization = atob(element.entity.ExportedNodes) + this.template.getPasteInputObject().pasted(additionalSerialization) + .forEach(node => node.entity.isExported = true) + continue + } const name = element.entity.getObjectName() - const homonymNode = this.nodes.find(node => node.entity.getObjectName() == name) - if (homonymNode) { - // Inserted node keeps tha name and the homonym nodes is renamed - let name = homonymNode.entity.getObjectName(true) - this.#nodeNameCounter[name] = this.#nodeNameCounter[name] ?? -1 - do { - ++this.#nodeNameCounter[name] - } while (this.nodes.find(node => - node.entity.getObjectName() == Configuration.nodeTitle(name, this.#nodeNameCounter[name]) - )) - homonymNode.rename(Configuration.nodeTitle(name, this.#nodeNameCounter[name])) + const homonym = this.entity.getHomonymObjectEntity(element.entity) + if (homonym) { + homonym.Name = this.entity.takeFreeName(name) } this.nodes.push(element) + this.entity.addObjectEntity(element.entity) element.addEventListener(Configuration.removeEventName, removeEventHandler) this.template.nodesContainerElement?.appendChild(element) } else if (element instanceof LinkElement && !this.links.includes(element)) { diff --git a/js/Configuration.js b/js/Configuration.js index 62c5a42c..75813ead 100755 --- a/js/Configuration.js +++ b/js/Configuration.js @@ -5,9 +5,11 @@ export default class Configuration { black: css`20, 20, 20`, blue: css`84, 122, 156`, darkBlue: css`32, 80, 128`, + darkerBlue: css`18, 18, 130`, darkTurquoise: css`19, 100, 137`, gray: css`150,150,150`, green: css`95, 129, 90`, + intenseGreen: css`42, 140, 42`, lime: css`150, 160, 30`, red: css`151, 33, 32`, turquoise: css`46, 104, 106`, @@ -116,6 +118,7 @@ export default class Configuration { edGraph: "/Script/Engine.EdGraph", eDrawDebugTrace: "/Script/Engine.EDrawDebugTrace", eMaterialSamplerType: "/Script/Engine.EMaterialSamplerType", + eNiagara_Float4Channel: "/Niagara/Enums/ENiagara_Float4Channel.ENiagara_Float4Channel", enum: "/Script/CoreUObject.Enum", enumLiteral: "/Script/BlueprintGraph.K2Node_EnumLiteral", eSamplerSourceMode: "/Script/Engine.ESamplerSourceMode", @@ -167,7 +170,17 @@ export default class Configuration { materialExpressionTextureSample: "/Script/Engine.MaterialExpressionTextureSample", materialGraphNode: "/Script/UnrealEd.MaterialGraphNode", materialGraphNodeComment: "/Script/UnrealEd.MaterialGraphNode_Comment", + metasoundEditorGraphExternalNode: "/Script/MetasoundEditor.MetasoundEditorGraphExternalNode", multiGate: "/Script/BlueprintGraph.K2Node_MultiGate", + niagaraBool: "/Script/Niagara.NiagaraBool", + niagaraClipboardContent: "/Script/NiagaraEditor.NiagaraClipboardContent", + niagaraDataInterfaceVolumeTexture: "/Script/Niagara.NiagaraDataInterfaceVolumeTexture", + niagaraFloat: "/Script/Niagara.NiagaraFloat", + niagaraMatrix: "/Script/Niagara.NiagaraMatrix", + niagaraNodeFunctionCall: "/Script/NiagaraEditor.NiagaraNodeFunctionCall", + niagaraNodeOp: "/Script/NiagaraEditor.NiagaraNodeOp", + niagaraNumeric: "/Script/Niagara.NiagaraNumeric", + niagaraPosition: "/Script/Niagara.NiagaraPosition", pawn: "/Script/Engine.Pawn", pcgEditorGraphNode: "/Script/PCGEditor.PCGEditorGraphNode", pcgEditorGraphNodeInput: "/Script/PCGEditor.PCGEditorGraphNodeInput", @@ -175,9 +188,11 @@ export default class Configuration { pcgHiGenGridSizeSettings: "/Script/PCG.PCGHiGenGridSizeSettings", pcgSubgraphSettings: "/Script/PCG.PCGSubgraphSettings", promotableOperator: "/Script/BlueprintGraph.K2Node_PromotableOperator", + quat4f: "/Script/CoreUObject.Quat4f", reverseForEachLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop", rotator: "/Script/CoreUObject.Rotator", select: "/Script/BlueprintGraph.K2Node_Select", + self: "/Script/BlueprintGraph.K2Node_Self", slateBlueprintLibrary: "/Script/UMG.SlateBlueprintLibrary", spawnActorFromClass: "/Script/BlueprintGraph.K2Node_SpawnActorFromClass", switchEnum: "/Script/BlueprintGraph.K2Node_SwitchEnum", @@ -193,44 +208,10 @@ export default class Configuration { variableSet: "/Script/BlueprintGraph.K2Node_VariableSet", vector: "/Script/CoreUObject.Vector", vector2D: "/Script/CoreUObject.Vector2D", + vector3f: "/Script/CoreUObject.Vector3f", + vector4f: "/Script/CoreUObject.Vector4f", whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop", } - static pinColor = { - [this.paths.rotator]: css`157, 177, 251`, - [this.paths.transform]: css`227, 103, 0`, - [this.paths.vector]: css`251, 198, 34`, - "Any": css`132, 132, 132`, - "Any[]": css`132, 132, 132`, - "blue": css`0, 0, 255`, - "bool": css`147, 0, 0`, - "byte": css`0, 109, 99`, - "class": css`88, 0, 186`, - "default": css`255, 255, 255`, - "delegate": css`255, 56, 56`, - "enum": css`0, 109, 99`, - "exec": css`240, 240, 240`, - "green": css`0, 255, 0`, - "int": css`31, 224, 172`, - "int64": css`169, 223, 172`, - "interface": css`238, 252, 168`, - "name": css`201, 128, 251`, - "object": css`0, 167, 240`, - "Param": css`255, 166, 39`, - "Param[]": css`255, 166, 39`, - "Point": css`63, 137, 255`, - "Point[]": css`63, 137, 255`, - "real": css`54, 208, 0`, - "red": css`255, 0, 0`, - "string": css`251, 0, 209`, - "struct": css`0, 88, 201`, - "Surface": css`69, 196, 126`, - "Surface[]": css`69, 196, 126`, - "text": css`226, 121, 167`, - "Volume": css`230, 69, 188`, - "Volume[]": css`230, 69, 188`, - "wildcard": css`128, 120, 120`, - } - static pinColorMaterial = css`120, 120, 120` static pinInputWrapWidth = 143 // px static removeEventName = "ueb-element-delete" static scale = { @@ -303,6 +284,12 @@ export default class Configuration { "Virtual Linear Color", "Virtual Linear Grayscal", ], + [this.paths.eNiagara_Float4Channel]: [ + ["NewEnumerator0", "R"], + ["NewEnumerator1", "G"], + ["NewEnumerator2", "B"], + ["NewEnumerator3", "A"], + ], [this.paths.eSamplerSourceMode]: ["From texture asset", "Shared: Wrap", "Shared: Clamp", "Hidden"], [this.paths.eSpawnActorCollisionHandlingMethod]: [ ["Undefined", "Default"], @@ -328,7 +315,6 @@ export default class Configuration { "Alt", "Meta", ] - /** @type {["R", "G", "B", "A"]} */ static rgba = ["R", "G", "B", "A"] static Keys = { /* UE name: JS name */ diff --git a/js/SVGIcon.js b/js/SVGIcon.js index f3e1f9ac..aa2c9f6f 100644 --- a/js/SVGIcon.js +++ b/js/SVGIcon.js @@ -250,6 +250,12 @@ export default class SVGIcon { ` + static metasoundFunction = html` + + + + ` + static mouse = html` diff --git a/js/Utility.js b/js/Utility.js index 5d52f08f..44519d8a 100755 --- a/js/Utility.js +++ b/js/Utility.js @@ -295,8 +295,9 @@ export default class Utility { * @template T * @param {Array} a * @param {Array} b + * @param {(l: T, r: T) => Boolean} predicate */ - static mergeArrays(a = [], b = []) { + static mergeArrays(a = [], b = [], predicate = (l, r) => l == r) { let result = [] a = [...a] b = [...b] @@ -304,7 +305,7 @@ export default class Utility { while (true) { for (let j = 0; j < b.length; ++j) { for (let i = 0; i < a.length; ++i) { - if (a[i] == b[j]) { + if (predicate(a[i], b[j])) { // Found an element in common in the two arrays result.push( // Take and append all the elements skipped from a @@ -325,6 +326,13 @@ export default class Utility { return [...(new Set(result.concat(...a, ...b)))] } + /** @param {String} value */ + static escapeNewlines(value) { + return value + .replaceAll("\n", "\\n") // Replace newline with \n + .replaceAll("\t", "\\t") // Replace tab with \t + } + /** @param {String} value */ static escapeString(value) { return value @@ -384,8 +392,10 @@ export default class Utility { } /** @param {String} pathValue */ - static getNameFromPath(pathValue) { - return pathValue.match(/[^\.\/]+$/)?.[0] ?? "" + static getNameFromPath(pathValue, dropCounter = false) { + // From end to the first "/" or "." + const regex = dropCounter ? /([^\.\/]+?)(?:_\d+)$/ : /([^\.\/]+)$/ + return pathValue.match(regex)?.[1] ?? "" } /** @param {LinearColorEntity} value */ diff --git a/js/decoding/nodeColor.js b/js/decoding/nodeColor.js new file mode 100644 index 00000000..42164da2 --- /dev/null +++ b/js/decoding/nodeColor.js @@ -0,0 +1,81 @@ +import Configuration from "../Configuration.js" +import LinearColorEntity from "../entity/LinearColorEntity.js" + +/** @param {ObjectEntity} entity */ +export default function nodeColor(entity) { + switch (entity.getType()) { + case Configuration.paths.materialExpressionConstant2Vector: + case Configuration.paths.materialExpressionConstant3Vector: + case Configuration.paths.materialExpressionConstant4Vector: + return Configuration.nodeColors.yellow + case Configuration.paths.makeStruct: + return Configuration.nodeColors.darkBlue + case Configuration.paths.materialExpressionMaterialFunctionCall: + return Configuration.nodeColors.blue + case Configuration.paths.materialExpressionFunctionInput: + return Configuration.nodeColors.red + case Configuration.paths.materialExpressionTextureSample: + return Configuration.nodeColors.darkTurquoise + case Configuration.paths.materialExpressionTextureCoordinate: + return Configuration.nodeColors.red + case Configuration.paths.pcgEditorGraphNodeInput: + case Configuration.paths.pcgEditorGraphNodeOutput: + return Configuration.nodeColors.red + } + switch (entity.getClass()) { + case Configuration.paths.callFunction: + return entity.bIsPureFunc + ? Configuration.nodeColors.green + : Configuration.nodeColors.blue + case Configuration.paths.niagaraNodeFunctionCall: + return Configuration.nodeColors.darkerBlue + case Configuration.paths.dynamicCast: + return Configuration.nodeColors.turquoise + case Configuration.paths.inputDebugKey: + case Configuration.paths.inputKey: + return Configuration.nodeColors.red + case Configuration.paths.createDelegate: + case Configuration.paths.enumLiteral: + case Configuration.paths.makeArray: + case Configuration.paths.makeMap: + case Configuration.paths.materialGraphNode: + case Configuration.paths.select: + return Configuration.nodeColors.green + case Configuration.paths.executionSequence: + case Configuration.paths.ifThenElse: + case Configuration.paths.macro: + case Configuration.paths.multiGate: + return Configuration.nodeColors.gray + case Configuration.paths.functionEntry: + case Configuration.paths.functionResult: + return Configuration.nodeColors.violet + case Configuration.paths.timeline: + return Configuration.nodeColors.yellow + } + if (entity.switchTarget()) { + return Configuration.nodeColors.lime + } + if (entity.isEvent()) { + return Configuration.nodeColors.red + } + if (entity.isComment()) { + return (entity.CommentColor ? entity.CommentColor : LinearColorEntity.getWhite()) + .toDimmedColor() + .toCSSRGBValues() + } + const pcgSubobject = entity.getPcgSubobject() + if (pcgSubobject) { + if (pcgSubobject.NodeTitleColor) { + return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues() + } + switch (entity.PCGNode?.getName(true)) { + case "Branch": + case "Select": + return Configuration.nodeColors.intenseGreen + } + } + if (entity.bIsPureFunc) { + return Configuration.nodeColors.green + } + return Configuration.nodeColors.blue +} diff --git a/js/decoding/nodeIcon.js b/js/decoding/nodeIcon.js new file mode 100644 index 00000000..ab327a6a --- /dev/null +++ b/js/decoding/nodeIcon.js @@ -0,0 +1,78 @@ +import Configuration from "../Configuration.js" +import SVGIcon from "../SVGIcon.js" +import nodeTitle from "./nodeTitle.js" + +/** @param {ObjectEntity} entity */ +export default function nodeIcon(entity) { + if (entity.isMaterial() || entity.isPcg() || entity.isNiagara()) { + return null + } + switch (entity.getType()) { + case Configuration.paths.addDelegate: + case Configuration.paths.asyncAction: + case Configuration.paths.callDelegate: + case Configuration.paths.createDelegate: + case Configuration.paths.functionEntry: + case Configuration.paths.functionResult: + return SVGIcon.node + case Configuration.paths.customEvent: return SVGIcon.event + case Configuration.paths.doN: return SVGIcon.doN + case Configuration.paths.doOnce: return SVGIcon.doOnce + case Configuration.paths.dynamicCast: return SVGIcon.cast + case Configuration.paths.enumLiteral: return SVGIcon.enum + case Configuration.paths.event: return SVGIcon.event + case Configuration.paths.executionSequence: + case Configuration.paths.multiGate: + return SVGIcon.sequence + case Configuration.paths.flipflop: + return SVGIcon.flipflop + case Configuration.paths.forEachElementInEnum: + case Configuration.paths.forLoop: + case Configuration.paths.forLoopWithBreak: + case Configuration.paths.whileLoop: + return SVGIcon.loop + case Configuration.paths.forEachLoop: + case Configuration.paths.forEachLoopWithBreak: + return SVGIcon.forEachLoop + case Configuration.paths.ifThenElse: return SVGIcon.branchNode + case Configuration.paths.isValid: return SVGIcon.questionMark + case Configuration.paths.makeArray: return SVGIcon.makeArray + case Configuration.paths.makeMap: return SVGIcon.makeMap + case Configuration.paths.makeSet: return SVGIcon.makeSet + case Configuration.paths.makeStruct: return SVGIcon.makeStruct + case Configuration.paths.metasoundEditorGraphExternalNode: return SVGIcon.metasoundFunction + case Configuration.paths.select: return SVGIcon.select + case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor + case Configuration.paths.timeline: return SVGIcon.timer + } + if (entity.switchTarget()) { + return SVGIcon.switch + } + if (nodeTitle(entity).startsWith("Break")) { + return SVGIcon.breakStruct + } + if (entity.getClass() === Configuration.paths.macro) { + return SVGIcon.macro + } + const hidValue = entity.getHIDAttribute()?.toString() + if (hidValue) { + if (hidValue.includes("Mouse")) { + return SVGIcon.mouse + } else if (hidValue.includes("Gamepad_Special")) { + return SVGIcon.keyboard // It is called Touchpad in UE + } else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) { + return SVGIcon.gamepad + } else if (hidValue.includes("Touch")) { + return SVGIcon.touchpad + } else { + return SVGIcon.keyboard + } + } + if (entity.getDelegatePin()) { + return SVGIcon.event + } + if (entity.ObjectRef?.type === Configuration.paths.ambientSound) { + return SVGIcon.sound + } + return SVGIcon.functionSymbol +} diff --git a/js/decoding/nodeTemplate.js b/js/decoding/nodeTemplate.js new file mode 100644 index 00000000..7d230b65 --- /dev/null +++ b/js/decoding/nodeTemplate.js @@ -0,0 +1,123 @@ +import Configuration from "../Configuration.js" +import CommentNodeTemplate from "../template/node/CommentNodeTemplate.js" +import EventNodeTemplate from "../template/node/EventNodeTemplate.js" +import KnotNodeTemplate from "../template/node/KnotNodeTemplate.js" +import MetasoundNodeTemplate from "../template/node/MetasoundNodeTemplate.js" +import MetasoundOperationTemplate from "../template/node/MetasoundOperationTemplate.js" +import NodeTemplate from "../template/node/NodeTemplate.js" +import VariableAccessNodeTemplate from "../template/node/VariableAccessNodeTemplate.js" +import VariableConversionNodeTemplate from "../template/node/VariableConversionNodeTemplate.js" +import VariableOperationNodeTemplate from "../template/node/VariableOperationNodeTemplate.js" + +/** + * @param {ObjectEntity} nodeEntity + * @return {new () => NodeTemplate} + */ +export default function nodeTemplateClass(nodeEntity) { + if ( + nodeEntity.getClass() === Configuration.paths.callFunction + || nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator + || nodeEntity.getClass() === Configuration.paths.callArrayFunction + ) { + const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? "" + const memberName = nodeEntity.FunctionReference?.MemberName + if ( + memberName && ( + memberParent === Configuration.paths.kismetMathLibrary + || memberParent === Configuration.paths.kismetArrayLibrary + )) { + if (memberName.startsWith("Conv_")) { + return VariableConversionNodeTemplate + } + if ( + memberName.startsWith("Add_") + || memberName.startsWith("And_") + || memberName.startsWith("Boolean") // Boolean logic operations + || memberName.startsWith("Cross_") + || memberName.startsWith("Dot_") + || memberName.startsWith("Not_") + || memberName.startsWith("Or_") + || memberName.startsWith("Percent_") + || memberName.startsWith("Xor_") + ) { + return VariableOperationNodeTemplate + } + switch (memberName) { + case "Abs": + case "Array_Add": + case "Array_AddUnique": + case "Array_Identical": + case "BMax": + case "BMin": + case "CrossProduct2D": + case "DotProduct2D": + case "Exp": + case "FMax": + case "FMin": + case "GetPI": + case "Max": + case "MaxInt64": + case "Min": + case "MinInt64": + case "Sqrt": + case "Square": + case "Vector4_CrossProduct3": + case "Vector4_DotProduct": + case "Vector4_DotProduct3": + // Trigonometry + case "Acos": + case "Asin": + case "Cos": + case "DegAcos": + case "DegCos": + case "DegSin": + case "DegTan": + case "Sin": + case "Tan": + return VariableOperationNodeTemplate + } + } + if (memberParent === Configuration.paths.blueprintSetLibrary) { + return VariableOperationNodeTemplate + } + if (memberParent === Configuration.paths.blueprintMapLibrary) { + return VariableOperationNodeTemplate + } + } + switch (nodeEntity.getClass()) { + case Configuration.paths.comment: + case Configuration.paths.materialGraphNodeComment: + return CommentNodeTemplate + case Configuration.paths.createDelegate: + return NodeTemplate + case Configuration.paths.metasoundEditorGraphExternalNode: + if (nodeEntity["ClassName"]?.["Name"] == "Add") { + return MetasoundOperationTemplate + } + return MetasoundNodeTemplate + case Configuration.paths.niagaraNodeOp: + if ([ + "Boolean::LogicEq", + "Boolean::LogicNEq", + "Numeric::Abs", + "Numeric::Add", + "Numeric::Mul", + ].includes(nodeEntity.OpName)) { + return VariableOperationNodeTemplate + } + break + case Configuration.paths.promotableOperator: + return VariableOperationNodeTemplate + case Configuration.paths.knot: + return KnotNodeTemplate + case Configuration.paths.literal: + case Configuration.paths.self: + case Configuration.paths.variableGet: + case Configuration.paths.variableSet: + return VariableAccessNodeTemplate + } + if (nodeEntity.isEvent()) { + return EventNodeTemplate + } + return NodeTemplate +} diff --git a/js/decoding/nodeTitle.js b/js/decoding/nodeTitle.js new file mode 100644 index 00000000..50c03587 --- /dev/null +++ b/js/decoding/nodeTitle.js @@ -0,0 +1,404 @@ +import Configuration from "../Configuration.js" +import Utility from "../Utility.js" + +const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/ +const keyNameValue = { + "A_AccentGrave": "à", + "Add": "Num +", + "C_Cedille": "ç", + "Decimal": "Num .", + "Divide": "Num /", + "E_AccentAigu": "é", + "E_AccentGrave": "è", + "F1": "F1", // Otherwise F and number will be separated + "F10": "F10", + "F11": "F11", + "F12": "F12", + "F2": "F2", + "F3": "F3", + "F4": "F4", + "F5": "F5", + "F6": "F6", + "F7": "F7", + "F8": "F8", + "F9": "F9", + "Gamepad_Special_Left_X": "Touchpad Button X Axis", + "Gamepad_Special_Left_Y": "Touchpad Button Y Axis", + "Mouse2D": "Mouse XY 2D-Axis", + "Multiply": "Num *", + "Section": "§", + "Subtract": "Num -", + "Tilde": "`", +} + +function keyName(value) { + /** @type {String} */ + let result = keyNameValue[value] + if (result) { + return result + } + result = Utility.numberFromText(value)?.toString() + if (result) { + return result + } + const match = value.match(/NumPad([a-zA-Z]+)/) + if (match) { + result = Utility.numberFromText(match[1]).toString() + if (result) { + return "Num " + result + } + } +} + +/** @param {ObjectEntity} entity */ +export default function nodeTitle(entity) { + let input + switch (entity.getType()) { + case Configuration.paths.asyncAction: + if (entity.ProxyFactoryFunctionName) { + return Utility.formatStringName(entity.ProxyFactoryFunctionName) + } + case Configuration.paths.actorBoundEvent: + case Configuration.paths.componentBoundEvent: + return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})` + case Configuration.paths.callDelegate: + return `Call ${entity.DelegateReference?.MemberName ?? "None"}` + case Configuration.paths.createDelegate: + return "Create Event" + case Configuration.paths.customEvent: + if (entity.CustomFunctionName) { + return entity.CustomFunctionName + } + case Configuration.paths.dynamicCast: + if (!entity.TargetType) { + return "Bad cast node" // Target type not found + } + return `Cast To ${entity.TargetType?.getName()}` + case Configuration.paths.enumLiteral: + return `Literal enum ${entity.Enum?.getName()}` + case Configuration.paths.event: + return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}` + case Configuration.paths.executionSequence: + return "Sequence" + case Configuration.paths.forEachElementInEnum: + return `For Each ${entity.Enum?.getName()}` + case Configuration.paths.forEachLoopWithBreak: + return "For Each Loop with Break" + case Configuration.paths.functionEntry: + return entity.FunctionReference?.MemberName === "UserConstructionScript" + ? "Construction Script" + : entity.FunctionReference?.MemberName + case Configuration.paths.functionResult: + return "Return Node" + case Configuration.paths.ifThenElse: + return "Branch" + case Configuration.paths.makeStruct: + if (entity.StructType) { + return `Make ${entity.StructType.getName()}` + } + case Configuration.paths.materialExpressionComponentMask: { + const materialObject = entity.getMaterialSubobject() + return `Mask ( ${Configuration.rgba + .filter(k => /** @type {MirroredEntity} */(materialObject[k]).get() === true) + .map(v => v + " ") + .join("")})` + } + case Configuration.paths.materialExpressionConstant: + input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue] + case Configuration.paths.materialExpressionConstant2Vector: + input ??= [ + entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue, + entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue, + ] + case Configuration.paths.materialExpressionConstant3Vector: + if (!input) { + /** @type {VectorEntity} */ + const vector = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "Constant") + ?.DefaultValue + input = [vector.X, vector.Y, vector.Z] + } + case Configuration.paths.materialExpressionConstant4Vector: + if (!input) { + /** @type {LinearColorEntity} */ + const vector = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "Constant") + ?.DefaultValue + input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf()) + } + if (input.length > 0) { + return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur) + } + break + case Configuration.paths.materialExpressionFunctionInput: { + const materialObject = entity.getMaterialSubobject() + const inputName = materialObject?.InputName ?? "In" + const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3" + return `Input ${inputName} (${inputType})` + } + case Configuration.paths.materialExpressionLogarithm: + return "Ln" + case Configuration.paths.materialExpressionLogarithm10: + return "Log10" + case Configuration.paths.materialExpressionLogarithm2: + return "Log2" + case Configuration.paths.materialExpressionMaterialFunctionCall: + const materialFunction = entity.getMaterialSubobject()?.MaterialFunction + if (materialFunction) { + return materialFunction.getName() + } + break + case Configuration.paths.materialExpressionSquareRoot: + return "Sqrt" + case Configuration.paths.metasoundEditorGraphExternalNode: { + const name = entity["ClassName"]?.["Name"] + if (name) { + switch (name) { + case "Add": return "+" + default: return name + } + } + } + case Configuration.paths.pcgEditorGraphNodeInput: + return "Input" + case Configuration.paths.pcgEditorGraphNodeOutput: + return "Output" + case Configuration.paths.spawnActorFromClass: + let className = entity.getCustomproperties() + .find(pinEntity => pinEntity.PinName == "ReturnValue") + ?.PinType + ?.PinSubCategoryObject + ?.getName() + if (className === "Actor") { + className = null + } + return `SpawnActor ${Utility.formatStringName(className ?? "NONE")}` + case Configuration.paths.switchEnum: + return `Switch on ${entity.Enum?.getName() ?? "Enum"}` + case Configuration.paths.switchInteger: + return `Switch on Int` + case Configuration.paths.variableGet: + return "" + case Configuration.paths.variableSet: + return "SET" + } + let switchTarget = entity.switchTarget() + if (switchTarget) { + if (switchTarget[0] !== "E") { + switchTarget = Utility.formatStringName(switchTarget) + } + return `Switch on ${switchTarget}` + } + if (entity.isComment()) { + return entity.NodeComment + } + const keyNameSymbol = entity.getHIDAttribute() + if (keyNameSymbol) { + const name = keyNameSymbol.toString() + let title = keyName(name) ?? Utility.formatStringName(name) + if (entity.getClass() === Configuration.paths.inputDebugKey) { + title = "Debug Key " + title + } else if (entity.getClass() === Configuration.paths.getInputAxisKeyValue) { + title = "Get " + title + } + return title + } + if (entity.getClass() === Configuration.paths.macro) { + return Utility.formatStringName(entity.MacroGraphReference?.getMacroName()) + } + if (entity.isMaterial() && entity.getMaterialSubobject()) { + let result = nodeTitle(entity.getMaterialSubobject()) + result = result.match(/Material Expression (.+)/)?.[1] ?? result + return result + } + if (entity.isPcg() && entity.getPcgSubobject()) { + let pcgSubobject = entity.getPcgSubobject() + let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : nodeTitle(pcgSubobject) + return result + } + const subgraphObject = entity.getSubgraphObject() + if (subgraphObject) { + return subgraphObject.Graph.getName() + } + const settingsObject = entity.getSettingsObject() + if (settingsObject) { + if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) { + return `Grid Size: ${( + settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00") + ?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0] + ) ?? "256"}` + } + if (settingsObject.BlueprintElementInstance) { + return Utility.formatStringName(settingsObject.BlueprintElementType.getName()) + } + if (settingsObject.Operation) { + const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/) + if (match) { + return Utility.formatStringName(match[1] + ": " + settingsObject.Operation) + } + } + const settingsSubgraphObject = settingsObject.getSubgraphObject() + if (settingsSubgraphObject && settingsSubgraphObject.Graph) { + return settingsSubgraphObject.Graph.getName() + } + } + let memberName = entity.FunctionReference?.MemberName + if (memberName) { + const memberParent = entity.FunctionReference.MemberParent?.path ?? "" + switch (memberName) { + case "AddKey": + let result = memberParent.match(sequencerScriptingNameRegex) + if (result) { + return `Add Key (${Utility.formatStringName(result[1])})` + } + case "Concat_StrStr": + return "Append" + } + const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern) + if (memberNameTraceLineMatch) { + return "Line Trace" + + (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ") + + (memberNameTraceLineMatch[2] === "" + ? "By Channel" + : Utility.formatStringName(memberNameTraceLineMatch[2]) + ) + } + switch (memberParent) { + case Configuration.paths.blueprintGameplayTagLibrary: + case Configuration.paths.kismetMathLibrary: + case Configuration.paths.slateBlueprintLibrary: + case Configuration.paths.timeManagementBlueprintLibrary: + const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/) + if (leadingLetter) { + // Some functions start with B or F (Like FCeil, FMax, BMin) + memberName = leadingLetter[1] + } + switch (memberName) { + case "Abs": return "ABS" + case "BooleanAND": return "AND" + case "BooleanNAND": return "NAND" + case "BooleanOR": return "OR" + case "Exp": return "e" + case "LineTraceSingle": return "Line Trace By Channel" + case "Max": return "MAX" + case "MaxInt64": return "MAX" + case "Min": return "MIN" + case "MinInt64": return "MIN" + case "Not_PreBool": return "NOT" + case "Sin": return "SIN" + case "Sqrt": return "SQRT" + case "Square": return "^2" + // Dot products not respecting MemberName pattern + case "CrossProduct2D": return "cross" + case "Vector4_CrossProduct3": return "cross3" + case "DotProduct2D": + case "Vector4_DotProduct": + return "dot" + case "Vector4_DotProduct3": return "dot3" + } + if (memberName.startsWith("Add_")) { + return "+" + } + if (memberName.startsWith("And_")) { + return "&" + } + if (memberName.startsWith("Conv_")) { + return "" // Conversion nodes do not have visible names + } + if (memberName.startsWith("Cross_")) { + return "cross" + } + if (memberName.startsWith("Divide_")) { + return String.fromCharCode(0x00f7) + } + if (memberName.startsWith("Dot_")) { + return "dot" + } + if (memberName.startsWith("EqualEqual_")) { + return "==" + } + if (memberName.startsWith("Greater_")) { + return ">" + } + if (memberName.startsWith("GreaterEqual_")) { + return ">=" + } + if (memberName.startsWith("Less_")) { + return "<" + } + if (memberName.startsWith("LessEqual_")) { + return "<=" + } + if (memberName.startsWith("Multiply_")) { + return String.fromCharCode(0x2a2f) + } + if (memberName.startsWith("Not_")) { + return "~" + } + if (memberName.startsWith("NotEqual_")) { + return "!=" + } + if (memberName.startsWith("Or_")) { + return "|" + } + if (memberName.startsWith("Percent_")) { + return "%" + } + if (memberName.startsWith("Subtract_")) { + return "-" + } + if (memberName.startsWith("Xor_")) { + return "^" + } + break + case Configuration.paths.blueprintSetLibrary: + { + const setOperationMatch = memberName.match(/Set_(\w+)/) + if (setOperationMatch) { + return Utility.formatStringName(setOperationMatch[1]).toUpperCase() + } + } + break + case Configuration.paths.blueprintMapLibrary: + { + const setOperationMatch = memberName.match(/Map_(\w+)/) + if (setOperationMatch) { + return Utility.formatStringName(setOperationMatch[1]).toUpperCase() + } + } + break + case Configuration.paths.kismetArrayLibrary: + { + const arrayOperationMath = memberName.match(/Array_(\w+)/) + if (arrayOperationMath) { + return arrayOperationMath[1].toUpperCase() + } + } + break + } + return Utility.formatStringName(memberName) + } + if (entity.OpName) { + switch (entity.OpName) { + case "Boolean::LogicAnd": return "Logic AND" + case "Boolean::LogicEq": return "==" + case "Boolean::LogicNEq": return "!=" + case "Boolean::LogicNot": return "Logic NOT" + case "Boolean::LogicOr": return "Logic OR" + case "Matrix::MatrixMultiply": return "Multiply (Matrix * Matrix)" + case "Matrix::MatrixVectorMultiply": return "Multiply (Matrix * Vector4)" + case "Numeric::Abs": return "Abs" + case "Numeric::Add": return "+" + case "Numeric::DistancePos": return "Distance" + case "Numeric::Mul": return String.fromCharCode(0x2a2f) + } + return Utility.formatStringName(entity.OpName).replaceAll("::", " ") + } + if (entity.FunctionDisplayName) { + return Utility.formatStringName(entity.FunctionDisplayName) + } + if (entity.ObjectRef) { + return entity.ObjectRef.getName() + } + return Utility.formatStringName(entity.getNameAndCounter()[0]) +} diff --git a/js/decoding/nodeVariadic.js b/js/decoding/nodeVariadic.js new file mode 100644 index 00000000..b63d8166 --- /dev/null +++ b/js/decoding/nodeVariadic.js @@ -0,0 +1,148 @@ +import Configuration from "../Configuration.js" +import GuidEntity from "../entity/GuidEntity.js" +import PinEntity from "../entity/PinEntity.js" + +/** @param {PinEntity} pinEntity */ +const indexFromUpperCaseLetterName = pinEntity => + pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0) + +/** @param {ObjectEntity} entity */ +export default function nodeVariadic(entity) { + /** @type {() => PinEntity[]} */ + let pinEntities + /** @type {(pinEntity: PinEntity) => Number} */ + let pinIndexFromEntity + /** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number, newPin: PinEntity) => String} */ + let pinNameFromIndex + const type = entity.getType() + let name + switch (type) { + case Configuration.paths.commutativeAssociativeBinaryOperator: + case Configuration.paths.promotableOperator: + name = entity.FunctionReference?.MemberName + switch (name) { + default: + if ( + !name?.startsWith("Add_") + && !name?.startsWith("Subtract_") + && !name?.startsWith("Multiply_") + && !name?.startsWith("Divide_") + ) { + break + } + case "And_Int64Int64": + case "And_IntInt": + case "BMax": + case "BMin": + case "BooleanAND": + case "BooleanNAND": + case "BooleanOR": + case "Concat_StrStr": + case "FMax": + case "FMin": + case "Max": + case "MaxInt64": + case "Min": + case "MinInt64": + case "Or_Int64Int64": + case "Or_IntInt": + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput()) + pinIndexFromEntity ??= indexFromUpperCaseLetterName + pinNameFromIndex ??= (index, min = -1, max = -1) => { + const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1) + entity.NumAdditionalInputs = pinEntities().length - 1 + return result + } + break + } + break + case Configuration.paths.multiGate: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()) + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]) + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => + `Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}` + break + // case Configuration.paths.niagaraNodeOp: + // pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput()) + // pinIndexFromEntity ??= indexFromUpperCaseLetterName + // pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + // const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1) + // entity.AddedPins ??= [] + // entity.AddedPins.push(newPin) + // return result + // } + // break + case Configuration.paths.switchInteger: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()) + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1]) + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString() + break + case Configuration.paths.switchGameplayTag: + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}` + entity.PinNames ??= [] + entity.PinNames.push(result) + delete entity.PinTags[entity.PinTags.length - 1] + entity.PinTags[entity.PinTags.length] = null + return result + } + case Configuration.paths.switchName: + case Configuration.paths.switchString: + pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput()) + pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]) + pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => { + const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}` + entity.PinNames ??= [] + entity.PinNames.push(result) + return result + } + break + } + if (pinEntities) { + return () => { + let min = Number.MAX_SAFE_INTEGER + let max = Number.MIN_SAFE_INTEGER + let values = [] + const modelPin = pinEntities().reduce( + (acc, cur) => { + const value = pinIndexFromEntity(cur) + if (!isNaN(value)) { + values.push(value) + min = Math.min(value, min) + if (value > max) { + max = value + return cur + } + } else if (acc === undefined) { + return cur + } + return acc + }, + undefined + ) + if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) { + min = undefined + max = undefined + } + if (!modelPin) { + return null + } + values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1) + let prev = values[0] + let index = values.findIndex( + // Search for a gap + value => { + const result = value - prev > 1 + prev = value + return result + } + ) + const newPin = new PinEntity(modelPin) + newPin.PinId = GuidEntity.generateGuid() + newPin.PinName = pinNameFromIndex(index, min, max, newPin) + newPin.PinToolTip = undefined + entity.getCustomproperties(true).push(newPin) + return newPin + } + } +} diff --git a/js/decoding/pinColor.js b/js/decoding/pinColor.js new file mode 100644 index 00000000..c216f314 --- /dev/null +++ b/js/decoding/pinColor.js @@ -0,0 +1,68 @@ +import { css } from "lit" +import Configuration from "../Configuration.js" + +const colors = { + [Configuration.paths.niagaraBool]: css`146, 0, 0`, + [Configuration.paths.niagaraDataInterfaceVolumeTexture]: css`0, 168, 242`, + [Configuration.paths.niagaraFloat]: css`160, 250, 68`, + [Configuration.paths.niagaraMatrix]: css`0, 88, 200`, + [Configuration.paths.niagaraNumeric]: css`0, 88, 200`, + [Configuration.paths.niagaraPosition]: css`251, 146, 251`, + [Configuration.paths.quat4f]: css`0, 88, 200`, + [Configuration.paths.rotator]: css`157, 177, 251`, + [Configuration.paths.transform]: css`227, 103, 0`, + [Configuration.paths.vector]: css`251, 198, 34`, + [Configuration.paths.vector3f]: css`250, 200, 36`, + [Configuration.paths.vector4f]: css`0, 88, 200`, + "Any": css`132, 132, 132`, + "Any[]": css`132, 132, 132`, + "audio": css`252, 148, 252`, + "blue": css`0, 0, 255`, + "bool": css`146, 0, 0`, + "byte": css`0, 109, 99`, + "class": css`88, 0, 186`, + "default": css`255, 255, 255`, + "delegate": css`255, 56, 56`, + "enum": css`0, 109, 99`, + "exec": css`240, 240, 240`, + "float": css`160, 252, 70`, + "green": css`0, 255, 0`, + "int": css`31, 224, 172`, + "int32": css`30, 224, 172`, + "int64": css`169, 223, 172`, + "interface": css`238, 252, 168`, + "name": css`201, 128, 251`, + "object": css`0, 168, 242`, + "Param": css`255, 166, 39`, + "Param[]": css`255, 166, 39`, + "Point": css`63, 137, 255`, + "Point[]": css`63, 137, 255`, + "real": css`54, 208, 0`, + "red": css`255, 0, 0`, + "string": css`251, 0, 208`, + "struct": css`0, 88, 200`, + "Surface": css`69, 196, 126`, + "Surface[]": css`69, 196, 126`, + "text": css`226, 121, 167`, + "time": css`148, 252, 252`, + "Volume": css`230, 69, 188`, + "Volume[]": css`230, 69, 188`, + "wildcard": css`128, 120, 120`, +} + +const pinColorMaterial = css`120, 120, 120` + +/** @param {PinEntity} entity */ +export default function pinColor(entity) { + if (entity.PinType.PinCategory == "mask") { + const result = colors[entity.PinType.PinSubCategory] + if (result) { + return result + } + } else if (entity.PinType.PinCategory == "optional") { + return pinColorMaterial + } + return colors[entity.getType()] + ?? colors[entity.PinType.PinCategory.toLowerCase()] + ?? colors["default"] +} diff --git a/js/decoding/pinTemplate.js b/js/decoding/pinTemplate.js new file mode 100644 index 00000000..51b4c762 --- /dev/null +++ b/js/decoding/pinTemplate.js @@ -0,0 +1,51 @@ +import Configuration from "../Configuration.js" +import BoolPinTemplate from "../template/pin/BoolPinTemplate.js" +import EnumPinTemplate from "../template/pin/EnumPinTemplate.js" +import ExecPinTemplate from "../template/pin/ExecPinTemplate.js" +import Int64PinTemplate from "../template/pin/Int64PinTemplate.js" +import IntPinTemplate from "../template/pin/IntPinTemplate.js" +import LinearColorPinTemplate from "../template/pin/LinearColorPinTemplate.js" +import NamePinTemplate from "../template/pin/NamePinTemplate.js" +import PinTemplate from "../template/pin/PinTemplate.js" +import RealPinTemplate from "../template/pin/RealPinTemplate.js" +import ReferencePinTemplate from "../template/pin/ReferencePinTemplate.js" +import RotatorPinTemplate from "../template/pin/RotatorPinTemplate.js" +import StringPinTemplate from "../template/pin/StringPinTemplate.js" +import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate.js" +import Vector4DPinTemplate from "../template/pin/Vector4DPinTemplate.js" +import VectorPinTemplate from "../template/pin/VectorPinTemplate.js" + +const inputPinTemplates = { + [Configuration.paths.linearColor]: LinearColorPinTemplate, + [Configuration.paths.niagaraBool]: BoolPinTemplate, + [Configuration.paths.niagaraPosition]: VectorPinTemplate, + [Configuration.paths.rotator]: RotatorPinTemplate, + [Configuration.paths.vector]: VectorPinTemplate, + [Configuration.paths.vector2D]: Vector2DPinTemplate, + [Configuration.paths.vector3f]: VectorPinTemplate, + [Configuration.paths.vector4f]: Vector4DPinTemplate, + "bool": BoolPinTemplate, + "byte": IntPinTemplate, + "enum": EnumPinTemplate, + "int": IntPinTemplate, + "int64": Int64PinTemplate, + "MUTABLE_REFERENCE": ReferencePinTemplate, + "name": NamePinTemplate, + "rg": Vector2DPinTemplate, + "real": RealPinTemplate, + "string": StringPinTemplate, +} + +/** @param {PinEntity} entity */ +export default function pinTemplate(entity) { + if (entity.PinType.ContainerType?.toString() === "Array") { + return PinTemplate + } + if (entity.PinType.bIsReference && !entity.PinType.bIsConst) { + return inputPinTemplates["MUTABLE_REFERENCE"] + } + if (entity.getType() === "exec") { + return ExecPinTemplate + } + return (entity.isInput() ? inputPinTemplates[entity.getType()] : PinTemplate) ?? PinTemplate +} diff --git a/js/decoding/pinTitle.js b/js/decoding/pinTitle.js new file mode 100644 index 00000000..09efce80 --- /dev/null +++ b/js/decoding/pinTitle.js @@ -0,0 +1,19 @@ +import Utility from "../Utility.js" + +/** @param {PinEntity} entity */ +export default function pinTitle(entity) { + let result = entity.PinFriendlyName + ? entity.PinFriendlyName.toString() + : Utility.formatStringName(entity.PinName ?? "") + let match + if ( + entity.PinToolTip + // Match up until the first \n excluded or last character + && (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/)) + ) { + if (match[1].toLowerCase() === result.toLowerCase()) { + return match[1] // In case they match, then keep the case of the PinToolTip + } + } + return result +} diff --git a/js/element/ISelectableDraggableElement.js b/js/element/ISelectableDraggableElement.js index 4b31652d..987ad9b9 100644 --- a/js/element/ISelectableDraggableElement.js +++ b/js/element/ISelectableDraggableElement.js @@ -1,6 +1,6 @@ import Configuration from "../Configuration.js" -import IDraggableElement from "./IDraggableElement.js" import Utility from "../Utility.js" +import IDraggableElement from "./IDraggableElement.js" /** * @template {IEntity} EntityT diff --git a/js/element/NodeElement.js b/js/element/NodeElement.js index a1a2b980..81690cae 100644 --- a/js/element/NodeElement.js +++ b/js/element/NodeElement.js @@ -1,17 +1,13 @@ import Configuration from "../Configuration.js" import Utility from "../Utility.js" +import nodeTemplateClass from "../decoding/nodeTemplate.js" +import nodeTitle from "../decoding/nodeTitle.js" import IdentifierEntity from "../entity/IdentifierEntity.js" import ObjectEntity from "../entity/ObjectEntity.js" import PinEntity from "../entity/PinEntity.js" import PinReferenceEntity from "../entity/PinReferenceEntity.js" import SerializerFactory from "../serialization/SerializerFactory.js" -import CommentNodeTemplate from "../template/node/CommentNodeTemplate.js" -import EventNodeTemplate from "../template/node/EventNodeTemplate.js" -import KnotNodeTemplate from "../template/node/KnotNodeTemplate.js" import NodeTemplate from "../template/node/NodeTemplate.js" -import VariableAccessNodeTemplate from "../template/node/VariableAccessNodeTemplate.js" -import VariableConversionNodeTemplate from "../template/node/VariableConversionNodeTemplate.js" -import VariableOperationNodeTemplate from "../template/node/VariableOperationNodeTemplate.js" import ISelectableDraggableElement from "./ISelectableDraggableElement.js" /** @extends {ISelectableDraggableElement} */ @@ -87,101 +83,6 @@ export default class NodeElement extends ISelectableDraggableElement { } } - /** - * @param {ObjectEntity} nodeEntity - * @return {new () => NodeTemplate} - */ - static getTypeTemplate(nodeEntity) { - if ( - nodeEntity.getClass() === Configuration.paths.callFunction - || nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator - || nodeEntity.getClass() === Configuration.paths.callArrayFunction - ) { - const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? "" - const memberName = nodeEntity.FunctionReference?.MemberName - if ( - memberName && ( - memberParent === Configuration.paths.kismetMathLibrary - || memberParent === Configuration.paths.kismetArrayLibrary - )) { - if (memberName.startsWith("Conv_")) { - return VariableConversionNodeTemplate - } - if ( - memberName.startsWith("And_") - || memberName.startsWith("Boolean") // Boolean logic operations - || memberName.startsWith("Cross_") - || memberName.startsWith("Dot_") - || memberName.startsWith("Not_") - || memberName.startsWith("Or_") - || memberName.startsWith("Percent_") - || memberName.startsWith("Xor_") - ) { - return VariableOperationNodeTemplate - } - switch (memberName) { - case "Abs": - case "Array_Add": - case "Array_AddUnique": - case "Array_Identical": - case "BMax": - case "BMin": - case "CrossProduct2D": - case "DotProduct2D": - case "Exp": - case "FMax": - case "FMin": - case "GetPI": - case "Max": - case "MaxInt64": - case "Min": - case "MinInt64": - case "Sqrt": - case "Square": - case "Vector4_CrossProduct3": - case "Vector4_DotProduct": - case "Vector4_DotProduct3": - // Trigonometry - case "Acos": - case "Asin": - case "Cos": - case "DegAcos": - case "DegCos": - case "DegSin": - case "DegTan": - case "Sin": - case "Tan": - return VariableOperationNodeTemplate - } - } - if (memberParent === Configuration.paths.blueprintSetLibrary) { - return VariableOperationNodeTemplate - } - if (memberParent === Configuration.paths.blueprintMapLibrary) { - return VariableOperationNodeTemplate - } - } - switch (nodeEntity.getClass()) { - case Configuration.paths.comment: - case Configuration.paths.materialGraphNodeComment: - return CommentNodeTemplate - case Configuration.paths.createDelegate: - return NodeTemplate - case Configuration.paths.promotableOperator: - return VariableOperationNodeTemplate - case Configuration.paths.knot: - return KnotNodeTemplate - case Configuration.paths.literal: - case Configuration.paths.variableGet: - case Configuration.paths.variableSet: - return VariableAccessNodeTemplate - } - if (nodeEntity.isEvent()) { - return EventNodeTemplate - } - return NodeTemplate - } - /** @param {String} str */ static fromSerializedObject(str) { str = str.trim() @@ -193,18 +94,29 @@ export default class NodeElement extends ISelectableDraggableElement { * @param {ObjectEntity} entity * @param {NodeTemplate} template */ - static newObject(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { + static newObject(entity = new ObjectEntity(), template = new (nodeTemplateClass(entity))()) { const result = new NodeElement() result.initialize(entity, template) return result } - initialize(entity = new ObjectEntity(), template = new (NodeElement.getTypeTemplate(entity))()) { + #redirectLinksAfterRename(name) { + 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, + })) + } + } + } + + initialize(entity = new ObjectEntity(), template = new (nodeTemplateClass(entity))()) { this.typePath = entity.getType() this.nodeTitle = entity.getObjectName() this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString() this.enabledState = entity.EnabledState - this.nodeDisplayName = entity.nodeDisplayName() + this.nodeDisplayName = nodeTitle(entity) this.pureFunction = entity.bIsPureFunc this.dragLinkObjects = [] super.initialize(entity, template) @@ -216,6 +128,11 @@ export default class NodeElement extends ISelectableDraggableElement { } else { this.updateComplete.then(() => this.computeSizes()) } + entity.listenAttribute("Name", name => { + this.nodeTitle = entity.Name + this.nodeDisplayName = nodeTitle(entity) + this.#redirectLinksAfterRename(name) + }) } async getUpdateComplete() { @@ -261,7 +178,7 @@ export default class NodeElement extends ISelectableDraggableElement { } computeNodeDisplayName() { - this.nodeDisplayName = this.entity.nodeDisplayName() + this.nodeDisplayName = nodeTitle(this.entity) } /** @param {Number} value */ @@ -283,23 +200,6 @@ export default class NodeElement extends ISelectableDraggableElement { 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.nodeTitle = this.entity.Name - } - getPinElements() { return this.#pins } diff --git a/js/element/PinElement.js b/js/element/PinElement.js index c9952ad9..99ee549c 100644 --- a/js/element/PinElement.js +++ b/js/element/PinElement.js @@ -1,23 +1,10 @@ -import Configuration from "../Configuration.js" import Utility from "../Utility.js" +import pinTemplate from "../decoding/pinTemplate.js" import GuidEntity from "../entity/GuidEntity.js" import LinearColorEntity from "../entity/LinearColorEntity.js" import PinEntity from "../entity/PinEntity.js" import PinReferenceEntity from "../entity/PinReferenceEntity.js" -import BoolPinTemplate from "../template/pin/BoolPinTemplate.js" -import EnumPinTemplate from "../template/pin/EnumPinTemplate.js" -import ExecPinTemplate from "../template/pin/ExecPinTemplate.js" -import Int64PinTemplate from "../template/pin/Int64PinTemplate.js" -import IntPinTemplate from "../template/pin/IntPinTemplate.js" -import LinearColorPinTemplate from "../template/pin/LinearColorPinTemplate.js" -import NamePinTemplate from "../template/pin/NamePinTemplate.js" import PinTemplate from "../template/pin/PinTemplate.js" -import RealPinTemplate from "../template/pin/RealPinTemplate.js" -import ReferencePinTemplate from "../template/pin/ReferencePinTemplate.js" -import RotatorPinTemplate from "../template/pin/RotatorPinTemplate.js" -import StringPinTemplate from "../template/pin/StringPinTemplate.js" -import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate.js" -import VectorPinTemplate from "../template/pin/VectorPinTemplate.js" import ElementFactory from "./ElementFactory.js" import IElement from "./IElement.js" @@ -27,23 +14,6 @@ import IElement from "./IElement.js" */ export default class PinElement extends IElement { - static #inputPinTemplates = { - "bool": BoolPinTemplate, - "byte": IntPinTemplate, - "enum": EnumPinTemplate, - "int": IntPinTemplate, - "int64": Int64PinTemplate, - "MUTABLE_REFERENCE": ReferencePinTemplate, - "name": NamePinTemplate, - "rg": Vector2DPinTemplate, - "real": RealPinTemplate, - "string": StringPinTemplate, - [Configuration.paths.linearColor]: LinearColorPinTemplate, - [Configuration.paths.rotator]: RotatorPinTemplate, - [Configuration.paths.vector]: VectorPinTemplate, - [Configuration.paths.vector2D]: Vector2DPinTemplate, - } - static properties = { pinId: { type: GuidEntity, @@ -103,23 +73,9 @@ export default class PinElement extends IElement { /** @type {NodeElement} */ nodeElement - /** @param {PinEntity} pinEntity */ - static getTypeTemplate(pinEntity) { - if (pinEntity.PinType.ContainerType?.toString() === "Array") { - return PinTemplate - } - if (pinEntity.PinType.bIsReference && !pinEntity.PinType.bIsConst) { - return PinElement.#inputPinTemplates["MUTABLE_REFERENCE"] - } - if (pinEntity.getType() === "exec") { - return ExecPinTemplate - } - return (pinEntity.isInput() ? PinElement.#inputPinTemplates[pinEntity.getType()] : PinTemplate) ?? PinTemplate - } - static newObject( entity = new PinEntity(), - template = /** @type {PinTemplate} */(new (PinElement.getTypeTemplate(entity))()), + template = /** @type {PinTemplate} */(new (pinTemplate(entity))()), nodeElement = undefined ) { const result = new PinElement() @@ -129,7 +85,7 @@ export default class PinElement extends IElement { initialize( entity = /** @type {PinEntity} */(new PinEntity()), - template = /** @type {PinTemplate} */(new (PinElement.getTypeTemplate(entity))()), + template = /** @type {PinTemplate} */(new (pinTemplate(entity))()), nodeElement = undefined ) { this.nodeElement = nodeElement @@ -166,7 +122,7 @@ export default class PinElement extends IElement { } getPinDisplayName() { - return this.entity.pinDisplayName() + return this.entity.pinTitle() } /** @return {CSSResult} */ diff --git a/js/entity/AttributeInfo.js b/js/entity/AttributeInfo.js index 79f1f7c6..48562434 100644 --- a/js/entity/AttributeInfo.js +++ b/js/entity/AttributeInfo.js @@ -10,6 +10,7 @@ * inlined?: Boolean, * quoted?: Boolean, * silent?: Boolean, + * uninitialized?: Boolean, * predicate?: (value: T) => Boolean, * }} AttributeInfoSource */ @@ -27,6 +28,7 @@ export default class AttributeInfo { inlined: false, // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123) quoted: false, // Key is serialized with quotes silent: false, // Do not serialize if default + uninitialized: false, // Do not initialize with default } /** @param {AttributeInfoSource} source */ @@ -40,6 +42,7 @@ export default class AttributeInfo { this.inlined = source.inlined this.quoted = source.quoted this.silent = source.silent + this.uninitialized = source.uninitialized this.predicate = source.predicate if (this.type === Array && this.default instanceof Array && this.default.length > 0) { this.type = this.default diff --git a/js/entity/Base64ObjectsEncoded.js b/js/entity/Base64ObjectsEncoded.js deleted file mode 100644 index b6b338c3..00000000 --- a/js/entity/Base64ObjectsEncoded.js +++ /dev/null @@ -1,23 +0,0 @@ -import AttributeInfo from "./AttributeInfo.js" -import IEntity from "./IEntity.js" - -export default class Base64ObjectsEncoded extends IEntity { - - static attributes = { - ...super.attributes, - value: AttributeInfo.createType(String), - objects: new AttributeInfo({ - ignored: true, - }), - } - - constructor(values) { - super(values) - /** @type {String} */this.value - /** @type {ObjectEntity[]} */this.objects - } - - decode() { - - } -} diff --git a/js/entity/BlueprintEntity.js b/js/entity/BlueprintEntity.js new file mode 100644 index 00000000..85663fdf --- /dev/null +++ b/js/entity/BlueprintEntity.js @@ -0,0 +1,88 @@ +import Configuration from "../Configuration.js" +import Utility from "../Utility.js" +import ObjectEntity from "./ObjectEntity.js" + +export default class BlueprintEntity extends ObjectEntity { + + /** @type {Map} */ + #objectEntitiesNameCounter = new Map() + + /** @type {ObjectEntity[]}" */ + #objectEntities = [] + get objectEntities() { + return this.#objectEntities + } + + /** @param {ObjectEntity} entity */ + getHomonymObjectEntity(entity) { + const name = entity.getObjectName(false) + return this.#objectEntities.find(entity => entity.getObjectName() == name) + } + + /** @param {String} name */ + takeFreeName(name) { + name = name.replace(/_\d+$/, "") + const counter = (this.#objectEntitiesNameCounter.get(name) ?? -1) + 1 + this.#objectEntitiesNameCounter.set(name, counter) + return Configuration.nodeTitle(name, counter) + } + + /** @param {ObjectEntity} entity */ + addObjectEntity(entity) { + if (!this.#objectEntities.includes(entity)) { + this.#objectEntities.push(entity) + const [name, counter] = entity.getNameAndCounter() + this.#objectEntitiesNameCounter.set( + name, + Math.max((this.#objectEntitiesNameCounter.get(name) ?? 0), counter) + ) + return true + } + return false + } + + /** @param {ObjectEntity} entity */ + removeObjectEntity(entity) { + const index = this.#objectEntities.indexOf(entity) + if (index >= 0) { + const last = this.#objectEntities.pop() + if (index < this.#objectEntities.length) { + this.#objectEntities[index] = last + } + return true + } + return false + } + + /** @param {ObjectEntity} entity */ + mergeWith(entity) { + if (!entity.ScriptVariables || entity.ScriptVariables.length === 0) { + return this + } + if (!this.ScriptVariables || this.ScriptVariables.length === 0) { + this.ScriptVariables = entity.ScriptVariables + } + let scriptVariables = Utility.mergeArrays( + this.ScriptVariables, + entity.ScriptVariables, + (l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value + ) + if (scriptVariables.length === this.ScriptVariables.length) { + return this + } + const entries = scriptVariables.concat(scriptVariables).map((v, i) => { + const name = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, i >= scriptVariables.length) + return [ + name, + this[name] ?? entity[name] + ] + }) + entries.push( + ...Object.entries(this).filter(([k, v]) => + !k.startsWith(Configuration.subObjectAttributeNamePrefix) + && k !== "ExportedNodes" + ) + ) + return new BlueprintEntity(Object.fromEntries(entries)) + } +} diff --git a/js/entity/IEntity.js b/js/entity/IEntity.js index 5fbeef5b..e1e7d0d4 100644 --- a/js/entity/IEntity.js +++ b/js/entity/IEntity.js @@ -18,9 +18,19 @@ export default class IEntity extends Serializable { lookbehind: new AttributeInfo({ default: /** @type {String | Union} */(""), ignored: true, + uninitialized: true, }), } + /** @type {String[]} */ + #_keys + get _keys() { + return this.#_keys + } + set _keys(keys) { + this.#_keys = keys + } + constructor(values = {}, suppressWarns = false) { super() const Self = /** @type {typeof IEntity} */(this.constructor) @@ -110,7 +120,7 @@ export default class IEntity extends Serializable { assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor} */(defaultType))) continue // We have a value, need nothing more } - if (defaultValue !== undefined) { + if (defaultValue !== undefined && !AttributeInfo.getAttribute(values, key, "uninitialized", Self)) { assignAttribute(defaultValue) } } @@ -156,6 +166,44 @@ export default class IEntity extends Serializable { object.attributes = attributes } + /** + * + * @param {String} attribute + * @param {(v: any) => void} callback + */ + listenAttribute(attribute, callback) { + const descriptor = Object.getOwnPropertyDescriptor(this, attribute) + const setter = descriptor.set + if (setter) { + descriptor.set = v => { + setter(v) + callback(v) + } + Object.defineProperties(this, { [attribute]: descriptor }) + } else if (descriptor.value) { + Object.defineProperties(this, { + ["#" + attribute]: { + value: descriptor.value, + writable: true, + enumerable: false, + }, + [attribute]: { + enumerable: true, + get() { + return this["#" + attribute] + }, + set(v) { + if (v == this["#" + attribute]) { + return + } + callback(v) + this["#" + attribute] = v + } + }, + }) + } + } + getLookbehind() { let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default") lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind diff --git a/js/entity/InvariantTextEntity.js b/js/entity/InvariantTextEntity.js index 7c5ca74d..809eee49 100644 --- a/js/entity/InvariantTextEntity.js +++ b/js/entity/InvariantTextEntity.js @@ -37,4 +37,8 @@ export default class InvariantTextEntity extends IEntity { super(values) /** @type {String} */ this.value } + + toString() { + return this.value + } } diff --git a/js/entity/ObjectEntity.js b/js/entity/ObjectEntity.js index a46a213e..c7aa8d73 100755 --- a/js/entity/ObjectEntity.js +++ b/js/entity/ObjectEntity.js @@ -1,7 +1,8 @@ import Parsernostrum from "parsernostrum" import Configuration from "../Configuration.js" -import SVGIcon from "../SVGIcon.js" import Utility from "../Utility.js" +import nodeColor from "../decoding/nodeColor.js" +import nodeIcon from "../decoding/nodeIcon.js" import Grammar from "../serialization/Grammar.js" import AttributeInfo from "./AttributeInfo.js" import FunctionReferenceEntity from "./FunctionReferenceEntity.js" @@ -14,67 +15,25 @@ import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity.js" import MirroredEntity from "./MirroredEntity.js" import ObjectReferenceEntity from "./ObjectReferenceEntity.js" import PinEntity from "./PinEntity.js" +import ScriptVariableEntity from "./ScriptVariableEntity.js" import SymbolEntity from "./SymbolEntity.js" import Union from "./Union.js" import UnknownPinEntity from "./UnknownPinEntity.js" import VariableReferenceEntity from "./VariableReferenceEntity.js" +import nodeVariadic from "../decoding/nodeVariadic.js" export default class ObjectEntity extends IEntity { - static #keyName = { - "A_AccentGrave": "à", - "Add": "Num +", - "C_Cedille": "ç", - "Decimal": "Num .", - "Divide": "Num /", - "E_AccentAigu": "é", - "E_AccentGrave": "è", - "F1": "F1", // Otherwise F and number will be separated - "F10": "F10", - "F11": "F11", - "F12": "F12", - "F2": "F2", - "F3": "F3", - "F4": "F4", - "F5": "F5", - "F6": "F6", - "F7": "F7", - "F8": "F8", - "F9": "F9", - "Gamepad_Special_Left_X": "Touchpad Button X Axis", - "Gamepad_Special_Left_Y": "Touchpad Button Y Axis", - "Mouse2D": "Mouse XY 2D-Axis", - "Multiply": "Num *", - "Section": "§", - "Subtract": "Num -", - "Tilde": "`", - } static attributes = { ...super.attributes, + isExported: new AttributeInfo({ + type: Boolean, + ignored: true, + }), Class: AttributeInfo.createType(ObjectReferenceEntity), Name: AttributeInfo.createType(String), Archetype: AttributeInfo.createType(ObjectReferenceEntity), ExportPath: AttributeInfo.createType(ObjectReferenceEntity), - R: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - G: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - B: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), - A: new AttributeInfo({ - type: new Union(Boolean, Number), - default: false, - silent: true, - }), ObjectRef: AttributeInfo.createType(ObjectReferenceEntity), BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity), BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity), @@ -101,6 +60,7 @@ export default class ObjectEntity extends IEntity { ComponentPropertyName: AttributeInfo.createType(String), EventReference: AttributeInfo.createType(FunctionReferenceEntity), FunctionReference: AttributeInfo.createType(FunctionReferenceEntity), + FunctionScript: AttributeInfo.createType(ObjectReferenceEntity), CustomFunctionName: AttributeInfo.createType(String), TargetType: AttributeInfo.createType(ObjectReferenceEntity), MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity), @@ -110,6 +70,16 @@ export default class ObjectEntity extends IEntity { inlined: true, }), InputKey: AttributeInfo.createType(SymbolEntity), + OpName: AttributeInfo.createType(String), + CachedChangeId: AttributeInfo.createType(GuidEntity), + FunctionDisplayName: AttributeInfo.createType(String), + AddedPins: new AttributeInfo({ + type: [UnknownPinEntity], + default: () => [], + inlined: true, + silent: true, + }), + ChangeId: AttributeInfo.createType(GuidEntity), MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity), bOverrideFunction: AttributeInfo.createType(Boolean), bInternalEvent: AttributeInfo.createType(Boolean), @@ -170,11 +140,15 @@ export default class ObjectEntity extends IEntity { NodeGuid: AttributeInfo.createType(GuidEntity), ErrorType: AttributeInfo.createType(IntegerEntity), ErrorMsg: AttributeInfo.createType(String), + ScriptVariables: new AttributeInfo({ + type: [ScriptVariableEntity], + inlined: true, + }), Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)), + ExportedNodes: AttributeInfo.createType(String), CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]), } static nameRegex = /^(\w+?)(?:_(\d+))?$/ - static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/ static customPropertyGrammar = Parsernostrum.seq( Parsernostrum.reg(/CustomProperties\s+/), Grammar.grammarFor( @@ -226,22 +200,22 @@ export default class ObjectEntity extends IEntity { static createGrammar() { return Parsernostrum.seq( - Parsernostrum.reg(/Begin\s+Object/), + Parsernostrum.reg(/Begin +Object/), Parsernostrum.seq( Parsernostrum.whitespace, Parsernostrum.alt( + this.createSubObjectGrammar(), this.customPropertyGrammar, - Grammar.createAttributeGrammar(this), + Grammar.createAttributeGrammar(this, Parsernostrum.reg(Grammar.Regex.MultipleWordsSymbols)), Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) => Utility.objectSet(obj, ["attributes", ...k, "quoted"], true) ), this.inlinedArrayEntryGrammar, - this.createSubObjectGrammar() ) ) .map(([_0, entry]) => entry) .many(), - Parsernostrum.reg(/\s+End\s+Object/), + Parsernostrum.reg(/\s+End +Object/), ) .map(([_0, attributes, _2]) => { const values = {} @@ -250,33 +224,13 @@ export default class ObjectEntity extends IEntity { }) } - /** @param {String} value */ - static keyName(value) { - /** @type {String} */ - let result = ObjectEntity.#keyName[value] - if (result) { - return result - } - result = Utility.numberFromText(value)?.toString() - if (result) { - return result - } - const match = value.match(/NumPad([a-zA-Z]+)/) - if (match) { - result = Utility.numberFromText(match[1]).toString() - if (result) { - return "Num " + result - } - } - } - static getMultipleObjectsGrammar() { return Parsernostrum.seq( Parsernostrum.whitespaceOpt, - this.createGrammar(), + this.grammar, Parsernostrum.seq( Parsernostrum.whitespace, - this.createGrammar(), + this.grammar, ) .map(([_0, object]) => object) .many(), @@ -309,6 +263,7 @@ export default class ObjectEntity extends IEntity { // Attributes /** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties /** @type {Boolean} */ this.bIsPureFunc + /** @type {Boolean} */ this.isExported /** @type {FunctionReferenceEntity} */ this.ComponentPropertyName /** @type {FunctionReferenceEntity} */ this.EventReference /** @type {FunctionReferenceEntity} */ this.FunctionReference @@ -339,6 +294,7 @@ export default class ObjectEntity extends IEntity { /** @type {ObjectReferenceEntity} */ this.Class /** @type {ObjectReferenceEntity} */ this.Enum /** @type {ObjectReferenceEntity} */ this.ExportPath + /** @type {ObjectReferenceEntity} */ this.FunctionScript /** @type {ObjectReferenceEntity} */ this.Graph /** @type {ObjectReferenceEntity} */ this.MaterialExpression /** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment @@ -348,15 +304,19 @@ export default class ObjectEntity extends IEntity { /** @type {ObjectReferenceEntity} */ this.SettingsInterface /** @type {ObjectReferenceEntity} */ this.StructType /** @type {ObjectReferenceEntity} */ this.TargetType + /** @type {ScriptVariableEntity[]} */ this.ScriptVariables /** @type {String[]} */ this.EnumEntries /** @type {String[]} */ this.PinNames /** @type {String} */ this.CustomFunctionName /** @type {String} */ this.DelegatePropertyName + /** @type {String} */ this.ExportedNodes + /** @type {String} */ this.FunctionDisplayName /** @type {String} */ this.InputName /** @type {String} */ this.Name /** @type {String} */ this.NodeComment /** @type {String} */ this.NodeTitle /** @type {String} */ this.Operation + /** @type {String} */ this.OpName /** @type {String} */ this.ProxyFactoryFunctionName /** @type {String} */ this.SubgraphInstance /** @type {String} */ this.Text @@ -365,6 +325,7 @@ export default class ObjectEntity extends IEntity { /** @type {SymbolEntity} */ this.InputAxisKey /** @type {SymbolEntity} */ this.InputKey /** @type {SymbolEntity} */ this.InputType + /** @type {UnknownPinEntity[]} */ this.AddedPins /** @type {VariableReferenceEntity} */ this.DelegateReference /** @type {VariableReferenceEntity} */ this.VariableReference @@ -402,6 +363,15 @@ export default class ObjectEntity extends IEntity { obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue) obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue) obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue) + Utility.objectSet(obj, ["attributes", "R", "default"], false) + Utility.objectSet(obj, ["attributes", "R", "silent"], true) + Utility.objectSet(obj, ["attributes", "G", "default"], false) + Utility.objectSet(obj, ["attributes", "G", "silent"], true) + Utility.objectSet(obj, ["attributes", "B", "default"], false) + Utility.objectSet(obj, ["attributes", "B", "silent"], true) + Utility.objectSet(obj, ["attributes", "A", "default"], false) + Utility.objectSet(obj, ["attributes", "A", "silent"], true) + obj._keys = [...Configuration.rgba, ...Object.keys(obj).filter(k => !Configuration.rgba.includes(k))] } } /** @type {ObjectEntity} */ @@ -409,24 +379,23 @@ export default class ObjectEntity extends IEntity { if (pcgObject) { pcgObject.PositionX && (pcgObject.PositionX.getter = () => this.NodePosX) pcgObject.PositionY && (pcgObject.PositionY.getter = () => this.NodePosY) - pcgObject.getSubobjects() - .forEach( - /** @param {ObjectEntity} obj */ - obj => { - if (obj.Node !== undefined) { - const nodeRef = obj.Node.get() - if ( - nodeRef.type === this.PCGNode.type - && nodeRef.path === `${this.Name}.${this.PCGNode.path}` - ) { - obj.Node.getter = () => new ObjectReferenceEntity({ - type: this.PCGNode.type, - path: `${this.Name}.${this.PCGNode.path}`, - }) - } + pcgObject.getSubobjects().forEach( + /** @param {ObjectEntity} obj */ + obj => { + if (obj.Node !== undefined) { + const nodeRef = obj.Node.get() + if ( + nodeRef.type === this.PCGNode.type + && nodeRef.path === `${this.Name}.${this.PCGNode.path}` + ) { + obj.Node.getter = () => new ObjectReferenceEntity({ + type: this.PCGNode.type, + path: `${this.Name}.${this.PCGNode.path}`, + }) } } - ) + } + ) } let inputIndex = 0 @@ -477,19 +446,12 @@ export default class ObjectEntity extends IEntity { /** @returns {[String, Number]} */ getNameAndCounter() { - const result = this.getObjectName(false).match(ObjectEntity.nameRegex) + const result = this.getObjectName().match(ObjectEntity.nameRegex) let name = "" let counter = null - if (result) { - if (result.length > 1) { - name = result[1] - } - if (result.length > 2) { - counter = parseInt(result[2]) - } - return [name, counter] - } - return ["", 0] + return result + ? [result[1] ?? "", parseInt(result[2] ?? "0")] + : ["", 0] } getCounter() { @@ -628,6 +590,10 @@ export default class ObjectEntity extends IEntity { || this.getPcgSubobject() } + isNiagara() { + return this.Class && (this.Class.type ? this.Class.type : this.Class.path)?.startsWith("/Script/NiagaraEditor.") + } + /** @return {ObjectEntity} */ getPcgSubobject() { const node = this.PCGNode @@ -646,9 +612,9 @@ export default class ObjectEntity extends IEntity { /** @return {ObjectEntity} */ getSubgraphObject() { - const node = this.SubgraphInstance - return node - ? this[Configuration.subObjectAttributeNameFromName(node)] + const name = this.SubgraphInstance + return name + ? this[Configuration.subObjectAttributeNameFromName(name)] : null } @@ -666,594 +632,15 @@ export default class ObjectEntity extends IEntity { return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate") } - /** @returns {String} */ - nodeDisplayName() { - let input - switch (this.getType()) { - case Configuration.paths.asyncAction: - if (this.ProxyFactoryFunctionName) { - return Utility.formatStringName(this.ProxyFactoryFunctionName) - } - case Configuration.paths.actorBoundEvent: - case Configuration.paths.componentBoundEvent: - return `${Utility.formatStringName(this.DelegatePropertyName)} (${this.ComponentPropertyName ?? "Unknown"})` - case Configuration.paths.callDelegate: - return `Call ${this.DelegateReference?.MemberName ?? "None"}` - case Configuration.paths.createDelegate: - return "Create Event" - case Configuration.paths.customEvent: - if (this.CustomFunctionName) { - return this.CustomFunctionName - } - case Configuration.paths.dynamicCast: - if (!this.TargetType) { - return "Bad cast node" // Target type not found - } - return `Cast To ${this.TargetType?.getName()}` - case Configuration.paths.enumLiteral: - return `Literal enum ${this.Enum?.getName()}` - case Configuration.paths.event: - return `Event ${(this.EventReference?.MemberName ?? "").replace(/^Receive/, "")}` - case Configuration.paths.executionSequence: - return "Sequence" - case Configuration.paths.forEachElementInEnum: - return `For Each ${this.Enum?.getName()}` - case Configuration.paths.forEachLoopWithBreak: - return "For Each Loop with Break" - case Configuration.paths.functionEntry: - return this.FunctionReference?.MemberName === "UserConstructionScript" - ? "Construction Script" - : this.FunctionReference?.MemberName - case Configuration.paths.functionResult: - return "Return Node" - case Configuration.paths.ifThenElse: - return "Branch" - case Configuration.paths.makeStruct: - if (this.StructType) { - return `Make ${this.StructType.getName()}` - } - case Configuration.paths.materialExpressionComponentMask: { - const materialObject = this.getMaterialSubobject() - return `Mask ( ${Configuration.rgba - .filter(k => /** @type {MirroredEntity} */(materialObject[k]).get() === true) - .map(v => v + " ") - .join("")})` - } - case Configuration.paths.materialExpressionConstant: - input ??= [this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue] - case Configuration.paths.materialExpressionConstant2Vector: - input ??= [ - this.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue, - this.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue, - ] - case Configuration.paths.materialExpressionConstant3Vector: - if (!input) { - /** @type {VectorEntity} */ - const vector = this.getCustomproperties() - .find(pinEntity => pinEntity.PinName == "Constant") - ?.DefaultValue - input = [vector.X, vector.Y, vector.Z] - } - case Configuration.paths.materialExpressionConstant4Vector: - if (!input) { - /** @type {LinearColorEntity} */ - const vector = this.getCustomproperties() - .find(pinEntity => pinEntity.PinName == "Constant") - ?.DefaultValue - input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf()) - } - if (input.length > 0) { - return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur) - } - break - case Configuration.paths.materialExpressionFunctionInput: { - const materialObject = this.getMaterialSubobject() - const inputName = materialObject?.InputName ?? "In" - const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3" - return `Input ${inputName} (${inputType})` - } - case Configuration.paths.materialExpressionLogarithm: - return "Ln" - case Configuration.paths.materialExpressionLogarithm10: - return "Log10" - case Configuration.paths.materialExpressionLogarithm2: - return "Log2" - case Configuration.paths.materialExpressionMaterialFunctionCall: - const materialFunction = this.getMaterialSubobject()?.MaterialFunction - if (materialFunction) { - return materialFunction.getName() - } - break - case Configuration.paths.materialExpressionSquareRoot: - return "Sqrt" - case Configuration.paths.pcgEditorGraphNodeInput: - return "Input" - case Configuration.paths.pcgEditorGraphNodeOutput: - return "Output" - case Configuration.paths.spawnActorFromClass: - return `SpawnActor ${Utility.formatStringName( - this.getCustomproperties().find(pinEntity => pinEntity.getType() == "class")?.DefaultObject?.getName() - ?? "NONE" - )}` - case Configuration.paths.switchEnum: - return `Switch on ${this.Enum?.getName() ?? "Enum"}` - case Configuration.paths.switchInteger: - return `Switch on Int` - case Configuration.paths.variableGet: - return "" - case Configuration.paths.variableSet: - return "SET" - } - let switchTarget = this.switchTarget() - if (switchTarget) { - if (switchTarget[0] !== "E") { - switchTarget = Utility.formatStringName(switchTarget) - } - return `Switch on ${switchTarget}` - } - if (this.isComment()) { - return this.NodeComment - } - const keyNameSymbol = this.getHIDAttribute() - if (keyNameSymbol) { - const keyName = keyNameSymbol.toString() - let title = ObjectEntity.keyName(keyName) ?? Utility.formatStringName(keyName) - if (this.getClass() === Configuration.paths.inputDebugKey) { - title = "Debug Key " + title - } else if (this.getClass() === Configuration.paths.getInputAxisKeyValue) { - title = "Get " + title - } - return title - } - if (this.getClass() === Configuration.paths.macro) { - return Utility.formatStringName(this.MacroGraphReference?.getMacroName()) - } - if (this.isMaterial() && this.getMaterialSubobject()) { - let result = this.getMaterialSubobject().nodeDisplayName() - result = result.match(/Material Expression (.+)/)?.[1] ?? result - return result - } - if (this.isPcg() && this.getPcgSubobject()) { - let pcgSubobject = this.getPcgSubobject() - let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : pcgSubobject.nodeDisplayName() - return result - } - const subgraphObject = this.getSubgraphObject() - if (subgraphObject) { - return subgraphObject.Graph.getName() - } - const settingsObject = this.getSettingsObject() - if (settingsObject) { - if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) { - return `Grid Size: ${( - settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00") - ?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0] - ) ?? "256"}` - } - if (settingsObject.BlueprintElementInstance) { - return Utility.formatStringName(settingsObject.BlueprintElementType.getName()) - } - if (settingsObject.Operation) { - const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/) - if (match) { - return Utility.formatStringName(match[1] + ": " + settingsObject.Operation) - } - } - const settingsSubgraphObject = settingsObject.getSubgraphObject() - if (settingsSubgraphObject && settingsSubgraphObject.Graph) { - return settingsSubgraphObject.Graph.getName() - } - } - let memberName = this.FunctionReference?.MemberName - if (memberName) { - const memberParent = this.FunctionReference.MemberParent?.path ?? "" - switch (memberName) { - case "AddKey": - let result = memberParent.match(ObjectEntity.sequencerScriptingNameRegex) - if (result) { - return `Add Key (${Utility.formatStringName(result[1])})` - } - case "Concat_StrStr": - return "Append" - } - const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern) - if (memberNameTraceLineMatch) { - return "Line Trace" - + (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ") - + (memberNameTraceLineMatch[2] === "" - ? "By Channel" - : Utility.formatStringName(memberNameTraceLineMatch[2]) - ) - } - switch (memberParent) { - case Configuration.paths.blueprintGameplayTagLibrary: - case Configuration.paths.kismetMathLibrary: - case Configuration.paths.slateBlueprintLibrary: - case Configuration.paths.timeManagementBlueprintLibrary: - const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/) - if (leadingLetter) { - // Some functions start with B or F (Like FCeil, FMax, BMin) - memberName = leadingLetter[1] - } - switch (memberName) { - case "Abs": return "ABS" - case "BooleanAND": return "AND" - case "BooleanNAND": return "NAND" - case "BooleanOR": return "OR" - case "Exp": return "e" - case "LineTraceSingle": return "Line Trace By Channel" - case "Max": return "MAX" - case "MaxInt64": return "MAX" - case "Min": return "MIN" - case "MinInt64": return "MIN" - case "Not_PreBool": return "NOT" - case "Sin": return "SIN" - case "Sqrt": return "SQRT" - case "Square": return "^2" - // Dot products not respecting MemberName pattern - case "CrossProduct2D": return "cross" - case "Vector4_CrossProduct3": return "cross3" - case "DotProduct2D": - case "Vector4_DotProduct": - return "dot" - case "Vector4_DotProduct3": return "dot3" - } - if (memberName.startsWith("Add_")) { - return "+" - } - if (memberName.startsWith("And_")) { - return "&" - } - if (memberName.startsWith("Conv_")) { - return "" // Conversion nodes do not have visible names - } - if (memberName.startsWith("Cross_")) { - return "cross" - } - if (memberName.startsWith("Divide_")) { - return String.fromCharCode(0x00f7) - } - if (memberName.startsWith("Dot_")) { - return "dot" - } - if (memberName.startsWith("EqualEqual_")) { - return "==" - } - if (memberName.startsWith("Greater_")) { - return ">" - } - if (memberName.startsWith("GreaterEqual_")) { - return ">=" - } - if (memberName.startsWith("Less_")) { - return "<" - } - if (memberName.startsWith("LessEqual_")) { - return "<=" - } - if (memberName.startsWith("Multiply_")) { - return String.fromCharCode(0x2a2f) - } - if (memberName.startsWith("Not_")) { - return "~" - } - if (memberName.startsWith("NotEqual_")) { - return "!=" - } - if (memberName.startsWith("Or_")) { - return "|" - } - if (memberName.startsWith("Percent_")) { - return "%" - } - if (memberName.startsWith("Subtract_")) { - return "-" - } - if (memberName.startsWith("Xor_")) { - return "^" - } - break - case Configuration.paths.blueprintSetLibrary: - { - const setOperationMatch = memberName.match(/Set_(\w+)/) - if (setOperationMatch) { - return Utility.formatStringName(setOperationMatch[1]).toUpperCase() - } - } - break - case Configuration.paths.blueprintMapLibrary: - { - const setOperationMatch = memberName.match(/Map_(\w+)/) - if (setOperationMatch) { - return Utility.formatStringName(setOperationMatch[1]).toUpperCase() - } - } - break - case Configuration.paths.kismetArrayLibrary: - { - const arrayOperationMath = memberName.match(/Array_(\w+)/) - if (arrayOperationMath) { - return arrayOperationMath[1].toUpperCase() - } - } - break - } - return Utility.formatStringName(memberName) - } - if (this.ObjectRef) { - return this.ObjectRef.getName() - } - return Utility.formatStringName(this.getNameAndCounter()[0]) - } - nodeColor() { - switch (this.getType()) { - case Configuration.paths.materialExpressionConstant2Vector: - case Configuration.paths.materialExpressionConstant3Vector: - case Configuration.paths.materialExpressionConstant4Vector: - return Configuration.nodeColors.yellow - case Configuration.paths.makeStruct: - return Configuration.nodeColors.darkBlue - case Configuration.paths.materialExpressionMaterialFunctionCall: - return Configuration.nodeColors.blue - case Configuration.paths.materialExpressionFunctionInput: - return Configuration.nodeColors.red - case Configuration.paths.materialExpressionTextureSample: - return Configuration.nodeColors.darkTurquoise - case Configuration.paths.materialExpressionTextureCoordinate: - return Configuration.nodeColors.red - case Configuration.paths.pcgEditorGraphNodeInput: - case Configuration.paths.pcgEditorGraphNodeOutput: - return Configuration.nodeColors.red - } - switch (this.getClass()) { - case Configuration.paths.callFunction: - return this.bIsPureFunc - ? Configuration.nodeColors.green - : Configuration.nodeColors.blue - case Configuration.paths.dynamicCast: - return Configuration.nodeColors.turquoise - case Configuration.paths.inputDebugKey: - case Configuration.paths.inputKey: - return Configuration.nodeColors.red - case Configuration.paths.createDelegate: - case Configuration.paths.enumLiteral: - case Configuration.paths.makeArray: - case Configuration.paths.makeMap: - case Configuration.paths.materialGraphNode: - case Configuration.paths.select: - return Configuration.nodeColors.green - case Configuration.paths.executionSequence: - case Configuration.paths.ifThenElse: - case Configuration.paths.macro: - case Configuration.paths.multiGate: - return Configuration.nodeColors.gray - case Configuration.paths.functionEntry: - case Configuration.paths.functionResult: - return Configuration.nodeColors.violet - case Configuration.paths.timeline: - return Configuration.nodeColors.yellow - } - if (this.switchTarget()) { - return Configuration.nodeColors.lime - } - if (this.isEvent()) { - return Configuration.nodeColors.red - } - if (this.isComment()) { - return (this.CommentColor ? this.CommentColor : LinearColorEntity.getWhite()) - .toDimmedColor() - .toCSSRGBValues() - } - const pcgSubobject = this.getPcgSubobject() - if (pcgSubobject && pcgSubobject.NodeTitleColor) { - return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues() - } - if (this.bIsPureFunc) { - return Configuration.nodeColors.green - } - return Configuration.nodeColors.blue + return nodeColor(this) } nodeIcon() { - if (this.isMaterial() || this.isPcg()) { - return null - } - switch (this.getType()) { - case Configuration.paths.addDelegate: - case Configuration.paths.asyncAction: - case Configuration.paths.callDelegate: - case Configuration.paths.createDelegate: - case Configuration.paths.functionEntry: - case Configuration.paths.functionResult: - return SVGIcon.node - case Configuration.paths.customEvent: return SVGIcon.event - case Configuration.paths.doN: return SVGIcon.doN - case Configuration.paths.doOnce: return SVGIcon.doOnce - case Configuration.paths.dynamicCast: return SVGIcon.cast - case Configuration.paths.enumLiteral: return SVGIcon.enum - case Configuration.paths.event: return SVGIcon.event - case Configuration.paths.executionSequence: - case Configuration.paths.multiGate: - return SVGIcon.sequence - case Configuration.paths.flipflop: - return SVGIcon.flipflop - case Configuration.paths.forEachElementInEnum: - case Configuration.paths.forLoop: - case Configuration.paths.forLoopWithBreak: - case Configuration.paths.whileLoop: - return SVGIcon.loop - case Configuration.paths.forEachLoop: - case Configuration.paths.forEachLoopWithBreak: - return SVGIcon.forEachLoop - case Configuration.paths.ifThenElse: return SVGIcon.branchNode - case Configuration.paths.isValid: return SVGIcon.questionMark - case Configuration.paths.makeArray: return SVGIcon.makeArray - case Configuration.paths.makeMap: return SVGIcon.makeMap - case Configuration.paths.makeSet: return SVGIcon.makeSet - case Configuration.paths.makeStruct: return SVGIcon.makeStruct - case Configuration.paths.select: return SVGIcon.select - case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor - case Configuration.paths.timeline: return SVGIcon.timer - } - if (this.switchTarget()) { - return SVGIcon.switch - } - if (this.nodeDisplayName().startsWith("Break")) { - return SVGIcon.breakStruct - } - if (this.getClass() === Configuration.paths.macro) { - return SVGIcon.macro - } - const hidValue = this.getHIDAttribute()?.toString() - if (hidValue) { - if (hidValue.includes("Mouse")) { - return SVGIcon.mouse - } else if (hidValue.includes("Gamepad_Special")) { - return SVGIcon.keyboard // This is called Touchpad in UE - } else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) { - return SVGIcon.gamepad - } else if (hidValue.includes("Touch")) { - return SVGIcon.touchpad - } else { - return SVGIcon.keyboard - } - } - if (this.getDelegatePin()) { - return SVGIcon.event - } - if (this.ObjectRef?.type === Configuration.paths.ambientSound) { - return SVGIcon.sound - } - return SVGIcon.functionSymbol + return nodeIcon(this) } additionalPinInserter() { - /** @type {() => PinEntity[]} */ - let pinEntities - /** @type {(pinEntity: PinEntity) => Number} */ - let pinIndexFromEntity - /** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number) => String} */ - let pinNameFromIndex - switch (this.getType()) { - case Configuration.paths.commutativeAssociativeBinaryOperator: - case Configuration.paths.promotableOperator: - switch (this.FunctionReference?.MemberName) { - default: - if ( - !this.FunctionReference?.MemberName?.startsWith("Add_") - && !this.FunctionReference?.MemberName?.startsWith("Subtract_") - && !this.FunctionReference?.MemberName?.startsWith("Multiply_") - && !this.FunctionReference?.MemberName?.startsWith("Divide_") - ) { - break - } - case "And_Int64Int64": - case "And_IntInt": - case "BMax": - case "BMin": - case "BooleanAND": - case "BooleanNAND": - case "BooleanOR": - case "Concat_StrStr": - case "FMax": - case "FMin": - case "Max": - case "MaxInt64": - case "Min": - case "MinInt64": - case "Or_Int64Int64": - case "Or_IntInt": - - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isInput()) - pinIndexFromEntity ??= pinEntity => - pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0) - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1) - this.NumAdditionalInputs = pinEntities().length - 1 - return result - } - break - } - break - case Configuration.paths.multiGate: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()) - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1]) - pinNameFromIndex ??= (index, min = -1, max = -1) => - `Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}` - break - case Configuration.paths.switchInteger: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()) - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1]) - pinNameFromIndex ??= (index, min = -1, max = -1) => (index < 0 ? max + 1 : index).toString() - break - case Configuration.paths.switchGameplayTag: - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}` - this.PinNames ??= [] - this.PinNames.push(result) - delete this.PinTags[this.PinTags.length - 1] - this.PinTags[this.PinTags.length] = null - return result - } - case Configuration.paths.switchName: - case Configuration.paths.switchString: - pinEntities ??= () => this.getPinEntities().filter(pinEntity => pinEntity.isOutput()) - pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1]) - pinNameFromIndex ??= (index, min = -1, max = -1) => { - const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}` - this.PinNames ??= [] - this.PinNames.push(result) - return result - } - break - } - if (pinEntities) { - return () => { - let min = Number.MAX_SAFE_INTEGER - let max = Number.MIN_SAFE_INTEGER - let values = [] - const modelPin = pinEntities().reduce( - (acc, cur) => { - const value = pinIndexFromEntity(cur) - if (!isNaN(value)) { - values.push(value) - min = Math.min(value, min) - if (value > max) { - max = value - return cur - } - } else if (acc === undefined) { - return cur - } - return acc - }, - undefined - ) - if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) { - min = undefined - max = undefined - } - if (!modelPin) { - return null - } - values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1) - let prev = values[0] - let index = values.findIndex( - // Search for a gap - value => { - const result = value - prev > 1 - prev = value - return result - } - ) - const newPin = new PinEntity(modelPin) - newPin.PinId = GuidEntity.generateGuid() - newPin.PinName = pinNameFromIndex(index, min, max) - newPin.PinToolTip = undefined - this.getCustomproperties(true).push(newPin) - return newPin - } - } + return nodeVariadic(this) } } diff --git a/js/entity/ObjectReferenceEntity.js b/js/entity/ObjectReferenceEntity.js index 032afe90..b1a5c343 100755 --- a/js/entity/ObjectReferenceEntity.js +++ b/js/entity/ObjectReferenceEntity.js @@ -1,5 +1,4 @@ import Parsernostrum from "parsernostrum" -import Configuration from "../Configuration.js" import Utility from "../Utility.js" import Grammar from "../serialization/Grammar.js" import AttributeInfo from "./AttributeInfo.js" @@ -7,11 +6,6 @@ import IEntity from "./IEntity.js" export default class ObjectReferenceEntity extends IEntity { - static #quoteSymbols = [ - [`'"`, Grammar.Regex.InsideString.source], - [`'`, Grammar.Regex.InsideSingleQuotedString.source], - [`"`, Grammar.Regex.InsideString.source] - ] static attributes = { ...super.attributes, type: new AttributeInfo({ @@ -22,14 +16,15 @@ export default class ObjectReferenceEntity extends IEntity { default: "", serialized: true, }), - delim: new AttributeInfo({ + _full: new AttributeInfo({ ignored: true, }), } static quoted = Parsernostrum.regArray(new RegExp( - this.#quoteSymbols.map(([delim, parser]) => - delim + "(" + parser + ")" + delim.split("").reverse().join("")).join("|") - )).map(([_0, a, b, c]) => a ?? b ?? c) + `'"(${Grammar.Regex.InsideString.source})"'` + + "|" + + `'(${Grammar.Regex.InsideSingleQuotedString.source})'` + )).map(([_0, a, b]) => a ?? b) static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source static typeReference = Parsernostrum.reg( new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source) @@ -37,28 +32,32 @@ export default class ObjectReferenceEntity extends IEntity { static fullReferenceGrammar = Parsernostrum.regArray( new RegExp( "(" + this.typeReference.getParser().regexp.source + ")" - + /\s*/.source + "(?:" + this.quoted.getParser().parser.regexp.source + ")" ) - ).map(([_0, type, ...path]) => new this({ - type, - path: path.find(v => v), - delim: this.#quoteSymbols[path.findIndex(v => v)]?.[0] ?? "", - })) + ).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full })) static fullReferenceSerializedGrammar = Parsernostrum.regArray( new RegExp( "(" + this.typeReference.getParser().regexp.source + ")" - + /\s*/.source + `'(` + Grammar.Regex.InsideSingleQuotedString.source + `)'` ) - ).map(([_0, type, ...path]) => new this({ - type, - path: path.find(v => v), - delim: "'", - })) - static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "" })) + ).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full })) + static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "", _full: v })) static grammar = this.createGrammar() + constructor(values = {}) { + if (values.constructor === String) { + values = { + path: values + } + } + super(values) + if (!values._full || values._full.length === 0) { + this._full = `"${this.type + (this.path ? (`'${this.path}'`) : "")}"` + } + /** @type {String} */ this.type + /** @type {String} */ this.path + } + static createGrammar() { return Parsernostrum.alt( Parsernostrum.seq( @@ -68,33 +67,21 @@ export default class ObjectReferenceEntity extends IEntity { this.typeReferenceGrammar, ), Parsernostrum.str('"'), - ).map(([_0, objectReference, _1]) => objectReference), - this.fullReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)), - this.typeReferenceGrammar.map(v => (Utility.objectSet(v, ["attributes", "type", "serialized"], false), v)), + ).map(([_0, objectReference, _1]) => (objectReference._full = `"${objectReference._full}"`, objectReference)), + this.fullReferenceGrammar, + this.typeReferenceGrammar, ) } - constructor(values = {}) { - if (values.constructor === String) { - values = { - path: values - } - } - super(values) - /** @type {String} */ this.type - /** @type {String} */ this.path - /** @type {String} */ this.delim - } - static createNoneInstance() { return new ObjectReferenceEntity({ type: "None", path: "" }) } - getName() { - return Utility.getNameFromPath(this.path.replace(/_C$/, "")) + getName(dropCounter = false) { + return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter) } toString() { - return this.type + (this.path ? (this.delim + this.path + this.delim.split("").reverse().join("")) : "") + return this._full } } diff --git a/js/entity/PinEntity.js b/js/entity/PinEntity.js index 733625f0..54c3b88b 100755 --- a/js/entity/PinEntity.js +++ b/js/entity/PinEntity.js @@ -1,5 +1,6 @@ import Configuration from "../Configuration.js" -import Utility from "../Utility.js" +import pinColor from "../decoding/pinColor.js" +import pinTitle from "../decoding/pinTitle.js" import Grammar from "../serialization/Grammar.js" import AttributeInfo from "./AttributeInfo.js" import ByteEntity from "./ByteEntity.js" @@ -11,6 +12,7 @@ import GuidEntity from "./GuidEntity.js" import IEntity from "./IEntity.js" import Integer64Entity from "./Integer64Entity.js" import IntegerEntity from "./IntegerEntity.js" +import InvariantTextEntity from "./InvariantTextEntity.js" import LinearColorEntity from "./LinearColorEntity.js" import LocalizedTextEntity from "./LocalizedTextEntity.js" import ObjectReferenceEntity from "./ObjectReferenceEntity.js" @@ -20,9 +22,11 @@ import RBSerializationVector2DEntity from "./RBSerializationVector2DEntity.js" import RotatorEntity from "./RotatorEntity.js" import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity.js" import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity.js" +import SimpleSerializationVector4DEntity from "./SimpleSerializationVector4DEntity.js" import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity.js" import Union from "./Union.js" import Vector2DEntity from "./Vector2DEntity.js" +import Vector4DEntity from "./Vector4DEntity.js" import VectorEntity from "./VectorEntity.js" /** @template {TerminalAttribute} T */ @@ -33,6 +37,7 @@ export default class PinEntity extends IEntity { [Configuration.paths.rotator]: RotatorEntity, [Configuration.paths.vector]: VectorEntity, [Configuration.paths.vector2D]: Vector2DEntity, + [Configuration.paths.vector4f]: Vector4DEntity, "bool": Boolean, "byte": ByteEntity, "enum": EnumEntity, @@ -49,6 +54,8 @@ export default class PinEntity extends IEntity { [Configuration.paths.rotator]: SimpleSerializationRotatorEntity, [Configuration.paths.vector]: SimpleSerializationVectorEntity, [Configuration.paths.vector2D]: SimpleSerializationVector2DEntity, + [Configuration.paths.vector3f]: SimpleSerializationVectorEntity, + [Configuration.paths.vector4f]: SimpleSerializationVector4DEntity, } static attributes = { ...super.attributes, @@ -68,7 +75,7 @@ export default class PinEntity extends IEntity { default: () => new GuidEntity() }), PinName: AttributeInfo.createValue(""), - PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, String)), + PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, InvariantTextEntity, String)), PinToolTip: AttributeInfo.createType(String), Direction: AttributeInfo.createType(String), PinType: new AttributeInfo({ @@ -139,8 +146,8 @@ export default class PinEntity extends IEntity { } getType() { - const category = this.PinType.PinCategory - if (category === "struct" || category === "object") { + const category = this.PinType.PinCategory.toLocaleLowerCase() + if (category === "struct" || category === "class" || category === "object" || category === "type") { return this.PinType.PinSubCategoryObject.path } if (this.isEnum()) { @@ -198,21 +205,8 @@ export default class PinEntity extends IEntity { : entity } - pinDisplayName() { - let result = this.PinFriendlyName - ? this.PinFriendlyName.toString() - : Utility.formatStringName(this.PinName ?? "") - let match - if ( - this.PinToolTip - // Match up until the first \n excluded or last character - && (match = this.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/)) - ) { - if (match[1].toLowerCase() === result.toLowerCase()) { - return match[1] // In case they match, then keep the case of the PinToolTip - } - } - return result + pinTitle() { + return pinTitle(this) } /** @param {PinEntity} other */ @@ -309,18 +303,7 @@ export default class PinEntity extends IEntity { return this.PinType.PinSubCategoryObject.path } - /** @return {CSSResult} */ pinColor() { - if (this.PinType.PinCategory == "mask") { - const result = Configuration.pinColor[this.PinType.PinSubCategory] - if (result) { - return result - } - } else if (this.PinType.PinCategory == "optional") { - return Configuration.pinColorMaterial - } - return Configuration.pinColor[this.getType()] - ?? Configuration.pinColor[this.PinType.PinCategory.toLowerCase()] - ?? Configuration.pinColor["default"] + return pinColor(this) } } diff --git a/js/entity/PinReferenceEntity.js b/js/entity/PinReferenceEntity.js index e0a09f2b..b6dc6ca3 100755 --- a/js/entity/PinReferenceEntity.js +++ b/js/entity/PinReferenceEntity.js @@ -15,9 +15,9 @@ export default class PinReferenceEntity extends IEntity { static createGrammar() { return Parsernostrum.seq( - PathSymbolEntity.createGrammar(), + PathSymbolEntity.grammar, Parsernostrum.whitespace, - GuidEntity.createGrammar() + GuidEntity.grammar ).map( ([objectName, _1, pinGuid]) => new this({ objectName: objectName, diff --git a/js/entity/RBSerializationVector2DEntity.js b/js/entity/RBSerializationVector2DEntity.js index 45192a71..7c9176dd 100644 --- a/js/entity/RBSerializationVector2DEntity.js +++ b/js/entity/RBSerializationVector2DEntity.js @@ -15,7 +15,7 @@ export default class RBSerializationVector2DEntity extends Vector2DEntity { X: Number(x), Y: Number(y), })), - Vector2DEntity.createGrammar() + Vector2DEntity.grammar ) } } diff --git a/js/entity/ScriptVariableEntity.js b/js/entity/ScriptVariableEntity.js new file mode 100644 index 00000000..a94c152e --- /dev/null +++ b/js/entity/ScriptVariableEntity.js @@ -0,0 +1,25 @@ +import Grammar from "../serialization/Grammar.js" +import AttributeInfo from "./AttributeInfo.js" +import GuidEntity from "./GuidEntity.js" +import IEntity from "./IEntity.js" +import ObjectReferenceEntity from "./ObjectReferenceEntity.js" + +export default class ScriptVariableEntity extends IEntity { + + static attributes = { + ...super.attributes, + ScriptVariable: AttributeInfo.createType(ObjectReferenceEntity), + OriginalChangeId: AttributeInfo.createType(GuidEntity), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Grammar.createEntityGrammar(this) + } + + constructor(values = {}, suppressWarns = false) { + super(values, suppressWarns) + /** @type {ObjectReferenceEntity} */ this.ScriptVariable + /** @type {GuidEntity} */ this.OriginalChangeId + } +} diff --git a/js/entity/SimpleSerializationRotatorEntity.js b/js/entity/SimpleSerializationRotatorEntity.js index 9622e200..34ae923a 100644 --- a/js/entity/SimpleSerializationRotatorEntity.js +++ b/js/entity/SimpleSerializationRotatorEntity.js @@ -19,7 +19,7 @@ export default class SimpleSerializationRotatorEntity extends RotatorEntity { P: Number(p), Y: Number(y), })), - RotatorEntity.createGrammar() + RotatorEntity.grammar ) } } diff --git a/js/entity/SimpleSerializationVector2DEntity.js b/js/entity/SimpleSerializationVector2DEntity.js index 458bf951..6b9cdd09 100644 --- a/js/entity/SimpleSerializationVector2DEntity.js +++ b/js/entity/SimpleSerializationVector2DEntity.js @@ -16,7 +16,7 @@ export default class SimpleSerializationVector2DEntity extends Vector2DEntity { X: Number(x), Y: Number(y), })), - Vector2DEntity.createGrammar() + Vector2DEntity.grammar ) } } diff --git a/js/entity/SimpleSerializationVector4DEntity.js b/js/entity/SimpleSerializationVector4DEntity.js new file mode 100644 index 00000000..9261426d --- /dev/null +++ b/js/entity/SimpleSerializationVector4DEntity.js @@ -0,0 +1,29 @@ +import Parsernostrum from "parsernostrum" +import Vector4DEntity from "./Vector4DEntity.js" + +export default class SimpleSerializationVector4DEntity extends Vector4DEntity { + + static grammar = this.createGrammar() + + static createGrammar() { + const number = Parsernostrum.number.getParser().parser.regexp.source + return Parsernostrum.alt( + Parsernostrum.regArray(new RegExp( + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + + "\\s*,\\s*" + + "(" + number + ")" + )) + .map(([_0, x, y, z, w]) => new this({ + X: Number(x), + Y: Number(y), + Z: Number(z), + W: Number(w), + })), + Vector4DEntity.grammar + ) + } +} diff --git a/js/entity/SimpleSerializationVectorEntity.js b/js/entity/SimpleSerializationVectorEntity.js index 9c0c3873..0642df38 100644 --- a/js/entity/SimpleSerializationVectorEntity.js +++ b/js/entity/SimpleSerializationVectorEntity.js @@ -20,7 +20,7 @@ export default class SimpleSerializationVectorEntity extends VectorEntity { Y: Number(y), Z: Number(z), })), - VectorEntity.createGrammar() + VectorEntity.grammar ) } } diff --git a/js/entity/Vector4DEntity.js b/js/entity/Vector4DEntity.js new file mode 100644 index 00000000..6fa899fb --- /dev/null +++ b/js/entity/Vector4DEntity.js @@ -0,0 +1,44 @@ +import Grammar from "../serialization/Grammar.js" +import AttributeInfo from "./AttributeInfo.js" +import IEntity from "./IEntity.js" + +export default class Vector4DEntity extends IEntity { + + static attributes = { + ...super.attributes, + X: new AttributeInfo({ + default: 0, + expected: true, + }), + Y: new AttributeInfo({ + default: 0, + expected: true, + }), + Z: new AttributeInfo({ + default: 0, + expected: true, + }), + W: new AttributeInfo({ + default: 0, + expected: true, + }), + } + static grammar = this.createGrammar() + + static createGrammar() { + return Grammar.createEntityGrammar(Vector4DEntity, false) + } + + constructor(values) { + super(values) + /** @type {Number} */ this.X + /** @type {Number} */ this.Y + /** @type {Number} */ this.Z + /** @type {Number} */ this.W + } + + /** @returns {[Number, Number, Number, Number]} */ + toArray() { + return [this.X, this.Y, this.Z, this.W] + } +} diff --git a/js/input/common/Copy.js b/js/input/common/Copy.js index 66284318..18809807 100755 --- a/js/input/common/Copy.js +++ b/js/input/common/Copy.js @@ -1,5 +1,5 @@ -import IInput from "../IInput.js" import ObjectSerializer from "../../serialization/ObjectSerializer.js" +import IInput from "../IInput.js" /** * @typedef {import("../IInput.js").Options & { @@ -32,10 +32,15 @@ export default class Copy extends IInput { } getSerializedText() { - return this.blueprint - .getNodes(true) - .map(node => Copy.#serializer.write(node.entity, false)) - .join("") + const allNodes = this.blueprint.getNodes(true).map(n => n.entity) + const exported = allNodes.filter(n => n.isExported).map(n => Copy.#serializer.write(n, false)) + const result = allNodes.filter(n => !n.isExported).map(n => Copy.#serializer.write(n, false)) + if (exported.length) { + this.blueprint.entity.ExportedNodes = btoa(exported.join("")) + result.splice(0, 0, Copy.#serializer.write(this.blueprint.entity, false)) + delete this.blueprint.entity.ExportedNodes + } + return result.join("") } copied() { diff --git a/js/input/common/Cut.js b/js/input/common/Cut.js index ce47347f..04048a06 100755 --- a/js/input/common/Cut.js +++ b/js/input/common/Cut.js @@ -1,5 +1,5 @@ -import IInput from "../IInput.js" import ObjectSerializer from "../../serialization/ObjectSerializer.js" +import IInput from "../IInput.js" /** * @typedef {import("../IInput.js").Options & { diff --git a/js/input/common/Paste.js b/js/input/common/Paste.js index 9188e5a7..d66935c0 100755 --- a/js/input/common/Paste.js +++ b/js/input/common/Paste.js @@ -62,6 +62,6 @@ export default class Paste extends IInput { node.setSelected(true) }) this.blueprint.addGraphElement(...nodes) - return true + return nodes } } diff --git a/js/serialization/Grammar.js b/js/serialization/Grammar.js index ef793e14..abc5263b 100755 --- a/js/serialization/Grammar.js +++ b/js/serialization/Grammar.js @@ -25,6 +25,7 @@ export default class Grammar { static Word = Grammar.separatedBy("[a-zA-Z]", "_") static Symbol = /[a-zA-Z_]\w*/ static DotSeparatedSymbols = Grammar.separatedBy(this.Symbol.source, "\\.") + static MultipleWordsSymbols = Grammar.separatedBy(this.Symbol.source, "(?:\\.|\\ +)") static PathFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:]") static PathSpaceFragment = Grammar.separatedBy(this.Symbol.source, "[\\.:\\ ]") static Path = new RegExp(`(?:\\/${this.PathFragment.source}){2,}`) // Multiple (2+) /PathFragment @@ -57,7 +58,7 @@ export default class Grammar { static symbol = Parsernostrum.reg(Grammar.Regex.Symbol) static symbolQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.Symbol.source + ')"'), 1) static attributeName = Parsernostrum.reg(Grammar.Regex.DotSeparatedSymbols) - static attributeNameQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.DotSeparatedSymbols.source + ')"'), 1) + static attributeNameQuoted = Parsernostrum.reg(new RegExp('"(' + Grammar.Regex.InsideString.source + ')"'), 1) static guid = Parsernostrum.reg(new RegExp(`${Grammar.Regex.HexDigit.source}{32}`)) static commaSeparation = Parsernostrum.reg(/\s*,\s*(?!\))/) static commaOrSpaceSeparation = Parsernostrum.reg(/\s*,\s*(?!\))|\s+/) @@ -69,11 +70,12 @@ export default class Grammar { /** * @template T * @param {AttributeInfo} attribute + * @param {Parsernostrum} defaultGrammar * @returns {Parsernostrum} */ static grammarFor(attribute, type = attribute?.type, defaultGrammar = this.unknownValue) { let result = defaultGrammar - if (type instanceof Array) { + if (type === Array || type instanceof Array) { if (attribute?.inlined) { return this.grammarFor(undefined, type[0]) } @@ -123,7 +125,7 @@ export default class Grammar { if (result == this.unknownValue) { result = this.string } else { - result = Parsernostrum.seq(Parsernostrum.str('"'), result, Parsernostrum.str('"')) + result = Parsernostrum.seq(Parsernostrum.str('"'), result, Parsernostrum.str('"')).map(([_0, value, _2]) => value) } } if (attribute.nullable) { @@ -233,7 +235,6 @@ export default class Grammar { }) } - /* --- Entity --- */ - + /** @type {Parsernostrum} */ static unknownValue // Defined in initializeSerializerFactor to avoid circular include } diff --git a/js/serialization/Serializer.js b/js/serialization/Serializer.js index 2477414d..7d752ddc 100644 --- a/js/serialization/Serializer.js +++ b/js/serialization/Serializer.js @@ -80,7 +80,7 @@ export default class Serializer { attributeKeyPrinter = this.attributeKeyPrinter ) { let result = "" - const keys = Object.keys(entity) + const keys = entity._keys ?? Object.keys(entity) let first = true for (const key of keys) { const value = entity[key] @@ -150,15 +150,18 @@ export default class Serializer { */ showProperty(entity, key) { if (entity instanceof IEntity) { - if ( - AttributeInfo.getAttribute(entity, key, "ignored") - || AttributeInfo.getAttribute(entity, key, "silent") && Utility.equals( - AttributeInfo.getAttribute(entity, key, "default"), - entity[key] - ) - ) { + if (AttributeInfo.getAttribute(entity, key, "ignored")) { return false } + if (AttributeInfo.getAttribute(entity, key, "silent")) { + let defaultValue = AttributeInfo.getAttribute(entity, key, "default") + if (defaultValue instanceof Function) { + defaultValue = defaultValue(entity) + } + if (Utility.equals(entity[key], defaultValue)) { + return false + } + } } return true } diff --git a/js/serialization/ToStringSerializer.js b/js/serialization/ToStringSerializer.js index 9ed9e36e..f16852c6 100755 --- a/js/serialization/ToStringSerializer.js +++ b/js/serialization/ToStringSerializer.js @@ -8,8 +8,11 @@ import Serializer from "./Serializer.js" export default class ToStringSerializer extends Serializer { /** @param {T} entityType */ - constructor(entityType) { + constructor(entityType, escape = true) { super(entityType) + if (escape) { + this.wrap = (entity, serialized) => Utility.escapeString(serialized) + } } /** @@ -17,8 +20,9 @@ export default class ToStringSerializer extends Serializer { * @param {Boolean} insideString */ doWrite(entity, insideString, indentation = "") { + return !insideString && entity.constructor === String - ? `"${Utility.escapeString(entity.toString())}"` // String will have quotes if not inside a string already - : Utility.escapeString(entity.toString()) + ? `"${this.wrap(entity, entity.toString())}"` // String will have quotes if not inside a string already + : this.wrap(entity, entity.toString()) } } diff --git a/js/serialization/initializeSerializerFactory.js b/js/serialization/initializeSerializerFactory.js index 7ee3efe1..1f2770ed 100755 --- a/js/serialization/initializeSerializerFactory.js +++ b/js/serialization/initializeSerializerFactory.js @@ -1,5 +1,6 @@ import Parsernostrum from "parsernostrum" import Utility from "../Utility.js" +import BlueprintEntity from "../entity/BlueprintEntity.js" import ByteEntity from "../entity/ByteEntity.js" import ColorChannelEntity from "../entity/ColorChannelEntity.js" import EnumDisplayValueEntity from "../entity/EnumDisplayValueEntity.js" @@ -24,8 +25,10 @@ import PinReferenceEntity from "../entity/PinReferenceEntity.js" import PinTypeEntity from "../entity/PinTypeEntity.js" import RBSerializationVector2DEntity from "../entity/RBSerializationVector2DEntity.js" import RotatorEntity from "../entity/RotatorEntity.js" +import ScriptVariableEntity from "../entity/ScriptVariableEntity.js" import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity.js" import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity.js" +import SimpleSerializationVector4DEntity from "../entity/SimpleSerializationVector4DEntity.js" import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity.js" import SymbolEntity from "../entity/SymbolEntity.js" import TerminalTypeEntity from "../entity/TerminalTypeEntity.js" @@ -33,6 +36,7 @@ import Union from "../entity/Union.js" import UnknownKeysEntity from "../entity/UnknownKeysEntity.js" import VariableReferenceEntity from "../entity/VariableReferenceEntity.js" import Vector2DEntity from "../entity/Vector2DEntity.js" +import Vector4DEntity from "../entity/Vector4DEntity.js" import VectorEntity from "../entity/VectorEntity.js" import CustomSerializer from "./CustomSerializer.js" import Grammar from "./Grammar.js" @@ -45,24 +49,26 @@ Grammar.unknownValue = Parsernostrum.alt( // Remember to keep the order, otherwise parsing might fail Grammar.boolean, - GuidEntity.createGrammar(), + GuidEntity.grammar, Parsernostrum.str("None").map(() => new ObjectReferenceEntity({ type: "None" })), Grammar.null, Grammar.number, ObjectReferenceEntity.fullReferenceGrammar, Grammar.string, - LocalizedTextEntity.createGrammar(), - InvariantTextEntity.createGrammar(), - FormatTextEntity.createGrammar(), - PinReferenceEntity.createGrammar(), - VectorEntity.createGrammar(), - RotatorEntity.createGrammar(), - LinearColorEntity.createGrammar(), - Vector2DEntity.createGrammar(), - UnknownKeysEntity.createGrammar(), - SymbolEntity.createGrammar(), + LocalizedTextEntity.grammar, + InvariantTextEntity.grammar, + FormatTextEntity.grammar, + PinReferenceEntity.grammar, + Vector4DEntity.grammar, + VectorEntity.grammar, + RotatorEntity.grammar, + LinearColorEntity.grammar, + Vector2DEntity.grammar, + UnknownKeysEntity.grammar, + SymbolEntity.grammar, Grammar.grammarFor(undefined, [PinReferenceEntity]), Grammar.grammarFor(undefined, [new Union(Number, String, SymbolEntity)]), + Parsernostrum.lazy(() => Grammar.grammarFor(undefined, [undefined])), ) export default function initializeSerializerFactory() { @@ -80,10 +86,8 @@ export default function initializeSerializerFactory() { new CustomSerializer( (array, insideString) => `(${array - .map(v => - SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString) + "," - ) - .join("") + .map(v => SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString)) + .join(",") })`, Array ) @@ -94,6 +98,11 @@ export default function initializeSerializerFactory() { new ToStringSerializer(BigInt) ) + SerializerFactory.registerSerializer( + BlueprintEntity, + new ObjectSerializer(BlueprintEntity), + ) + SerializerFactory.registerSerializer( Boolean, new CustomSerializer( @@ -213,22 +222,7 @@ export default function initializeSerializerFactory() { SerializerFactory.registerSerializer( ObjectReferenceEntity, - new CustomSerializer( - objectReference => { - let type = objectReference.type ?? "" - let path = objectReference.path ?? "" - let delim = objectReference.delim ?? "" - if (type && path && Utility.isSerialized(objectReference, "path")) { - path = delim + path + delim.split("").reverse().join("") - } - let result = type + path - if (Utility.isSerialized(objectReference, "type")) { - result = `"${result}"` - } - return result - }, - ObjectReferenceEntity - ) + new ToStringSerializer(ObjectReferenceEntity, false) ) SerializerFactory.registerSerializer( @@ -238,7 +232,7 @@ export default function initializeSerializerFactory() { SerializerFactory.registerSerializer( PinEntity, - new Serializer(PinEntity, (entity, v) => `${entity.getLookbehind()} (${v})`, ",", false) + new Serializer(PinEntity, (entity, v) => `${entity.getLookbehind()} (${v})`, ",", true) ) SerializerFactory.registerSerializer( @@ -269,6 +263,11 @@ export default function initializeSerializerFactory() { new Serializer(RotatorEntity, Serializer.bracketsWrapped) ) + SerializerFactory.registerSerializer( + ScriptVariableEntity, + new Serializer(ScriptVariableEntity, Serializer.bracketsWrapped) + ) + SerializerFactory.registerSerializer( String, new CustomSerializer( @@ -303,6 +302,14 @@ export default function initializeSerializerFactory() { ) ) + SerializerFactory.registerSerializer( + SimpleSerializationVector4DEntity, + new CustomSerializer( + (value, insideString) => `${value.X}, ${value.Y}, ${value.Z}, ${value.W}`, + SimpleSerializationVector4DEntity + ) + ) + SerializerFactory.registerSerializer( SymbolEntity, new ToStringSerializer(SymbolEntity) @@ -327,4 +334,9 @@ export default function initializeSerializerFactory() { VectorEntity, new Serializer(VectorEntity, Serializer.bracketsWrapped) ) + + SerializerFactory.registerSerializer( + Vector4DEntity, + new Serializer(Vector4DEntity, Serializer.bracketsWrapped) + ) } diff --git a/js/template/BlueprintTemplate.js b/js/template/BlueprintTemplate.js index b4a6c649..1b8310c4 100755 --- a/js/template/BlueprintTemplate.js +++ b/js/template/BlueprintTemplate.js @@ -56,10 +56,6 @@ export default class BlueprintTemplate extends ITemplate { /** @type {HTMLElement} */ nodesContainerElement viewportSize = [0, 0] - #setViewportSize() { - - } - /** @param {Blueprint} element */ initialize(element) { super.initialize(element) diff --git a/js/template/node/MetasoundNodeTemplate.js b/js/template/node/MetasoundNodeTemplate.js new file mode 100644 index 00000000..1e4ecf8d --- /dev/null +++ b/js/template/node/MetasoundNodeTemplate.js @@ -0,0 +1,6 @@ +import NodeTemplate from "./NodeTemplate.js" + +export default class MetasoundNodeTemplate extends NodeTemplate { + + static nodeStyleClasses = ["ueb-node-style-metasound"] +} diff --git a/js/template/node/MetasoundOperationTemplate.js b/js/template/node/MetasoundOperationTemplate.js new file mode 100644 index 00000000..5e6f99e0 --- /dev/null +++ b/js/template/node/MetasoundOperationTemplate.js @@ -0,0 +1,5 @@ +import VariableManagementNodeTemplate from "./VariableMangementNodeTemplate.js" + +export default class MetasoundOperationTemplate extends VariableManagementNodeTemplate { + static nodeStyleClasses = ["ueb-node-style-metasound", "ueb-node-style-operation"] +} diff --git a/js/template/node/NodeTemplate.js b/js/template/node/NodeTemplate.js index 060e5620..1926315f 100755 --- a/js/template/node/NodeTemplate.js +++ b/js/template/node/NodeTemplate.js @@ -165,7 +165,7 @@ export default class NodeTemplate extends ISelectableDraggableTemplate { .filter(v => !v.isHidden()) .map(pinEntity => { this.#hasSubtitle = this.#hasSubtitle - || pinEntity.PinName === "self" && pinEntity.pinDisplayName() === "Target" + || pinEntity.PinName === "self" && pinEntity.pinTitle() === "Target" return this.createPinElement(pinEntity) }) } diff --git a/js/template/node/VariableAccessNodeTemplate.js b/js/template/node/VariableAccessNodeTemplate.js index e5c3e908..a99c5e90 100644 --- a/js/template/node/VariableAccessNodeTemplate.js +++ b/js/template/node/VariableAccessNodeTemplate.js @@ -6,9 +6,14 @@ export default class VariableAccessNodeTemplate extends VariableManagementNodeTe /** @param {NodeElement} element */ initialize(element) { super.initialize(element) - if (element.getType() === Configuration.paths.variableGet) { + const type = element.getType() + if ( + type === Configuration.paths.variableGet + || type === Configuration.paths.self + ) { this.element.classList.add("ueb-node-style-getter") - } else if (element.getType() === Configuration.paths.variableSet) { + this.displayName = "" + } else if (type === Configuration.paths.variableSet) { this.element.classList.add("ueb-node-style-setter") } } diff --git a/js/template/node/VariableMangementNodeTemplate.js b/js/template/node/VariableMangementNodeTemplate.js index 0d436cca..6e1f2e58 100644 --- a/js/template/node/VariableMangementNodeTemplate.js +++ b/js/template/node/VariableMangementNodeTemplate.js @@ -7,25 +7,25 @@ export default class VariableManagementNodeTemplate extends NodeTemplate { #hasInput = false #hasOutput = false - #displayName = "" + displayName = "" static nodeStyleClasses = ["ueb-node-style-glass"] /** @param {NodeElement} element */ initialize(element) { super.initialize(element) - this.#displayName = this.element.nodeDisplayName + this.displayName = this.element.nodeDisplayName } render() { return html`
- ${this.#displayName ? html` + ${this.displayName ? html`
- ${this.#displayName} + ${this.displayName}
diff --git a/js/template/pin/InternalPinTemplate.js b/js/template/pin/InternalPinTemplate.js new file mode 100644 index 00000000..29374aaf --- /dev/null +++ b/js/template/pin/InternalPinTemplate.js @@ -0,0 +1,5 @@ +import MinimalPinTemplate from "./MinimalPinTemplate.js" + +export default class InternalPinTemplate extends MinimalPinTemplate { + +} diff --git a/js/template/pin/PinTemplate.js b/js/template/pin/PinTemplate.js index 1733d301..7c736a3c 100755 --- a/js/template/pin/PinTemplate.js +++ b/js/template/pin/PinTemplate.js @@ -4,6 +4,7 @@ import SVGIcon from "../../SVGIcon.js" import Utility from "../../Utility.js" import MouseCreateLink from "../../input/mouse/MouseCreateLink.js" import ITemplate from "../ITemplate.js" +import MetasoundOperationTemplate from "../node/MetasoundOperationTemplate.js" import VariableConversionNodeTemplate from "../node/VariableConversionNodeTemplate.js" import VariableOperationNodeTemplate from "../node/VariableOperationNodeTemplate.js" @@ -42,6 +43,7 @@ export default class PinTemplate extends ITemplate { this.isNameRendered = !( nodeTemplate instanceof VariableConversionNodeTemplate || nodeTemplate instanceof VariableOperationNodeTemplate + || nodeTemplate instanceof MetasoundOperationTemplate ) } } diff --git a/js/template/pin/Vector2DPinTemplate.js b/js/template/pin/Vector2DPinTemplate.js index 558ea287..2738a402 100644 --- a/js/template/pin/Vector2DPinTemplate.js +++ b/js/template/pin/Vector2DPinTemplate.js @@ -6,7 +6,7 @@ import INumericPinTemplate from "./INumericPinTemplate.js" /** * @extends INumericPinTemplate */ -export default class VectorInputPinTemplate extends INumericPinTemplate { +export default class Vector2DPinTemplate extends INumericPinTemplate { #getX() { return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0) diff --git a/js/template/pin/Vector4DPinTemplate.js b/js/template/pin/Vector4DPinTemplate.js new file mode 100644 index 00000000..2c34f877 --- /dev/null +++ b/js/template/pin/Vector4DPinTemplate.js @@ -0,0 +1,63 @@ +import { html } from "lit" +import Utility from "../../Utility.js" +import INumericPinTemplate from "./INumericPinTemplate.js" +import Vector4DEntity from "../../entity/Vector4DEntity.js" + +/** @extends INumericPinTemplate */ +export default class Vector4DPinTemplate extends INumericPinTemplate { + + #getX() { + return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0) + } + + #getY() { + return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0) + } + + #getZ() { + return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0) + } + + #getW() { + return Utility.printNumber(this.element.getDefaultValue()?.W ?? 0) + } + + /** + * @param {Number[]} values + * @param {String[]} rawValues + */ + setDefaultValue(values, rawValues) { + const vector = this.element.getDefaultValue(true) + if (!(vector instanceof Vector4DEntity)) { + throw new TypeError("Expected DefaultValue to be a Vector4DEntity") + } + vector.X = values[0] + vector.Y = values[1] + vector.Z = values[2] + vector.W = values[3] + this.element.requestUpdate("DefaultValue", vector) + } + + renderInput() { + return html` +
+ X +
+ +
+ Y +
+ +
+ Z +
+ +
+ W +
+ +
+
+ ` + } +} diff --git a/package.json b/package.json index 3d950987..f215d7fa 100755 --- a/package.json +++ b/package.json @@ -1,14 +1,13 @@ { "name": "ueblueprint", + "description": "UE's Blueprint visualisation library", "version": "1.0.0", "type": "module", - "description": "UE's Blueprint visualisation library", "main": "dist/ueblueprint.js", "types": "types.js", "scripts": { "build": "rollup --config && sass scss/export.scss:dist/css/ueb-style.css && sass scss/export.scss:dist/css/ueb-style.min.css --style=compressed", "test": "npm run build && export UEBLUEPRINT_TEST_SERVER_PORT=8181 && npx concurrently -k \"http-server -s -p $UEBLUEPRINT_TEST_SERVER_PORT\" \"npx cypress run --env UEBLUEPRINT_TEST_SERVER_PORT=8181\"", - "cypress": "npm run build && export UEBLUEPRINT_TEST_SERVER_PORT=8181 && npx playwright test --ui", "start": "npx http-server" }, "repository": { @@ -27,15 +26,13 @@ }, "homepage": "https://github.com/barsdeveloper/ueblueprint#readme", "devDependencies": { - "@playwright/test": "^1.40.1", - "@rollup/plugin-commonjs": "^24", + "@playwright/test": "^1", + "@rollup/plugin-commonjs": "^25", "@rollup/plugin-node-resolve": "^15", "@rollup/plugin-terser": "^0", "concurrently": "^8", - "cypress": "^12", "http-server": "^14", - "minify-html-literals": "^1", - "rollup": "^3||^2", + "rollup": "^4 || ^3 || ^2", "rollup-plugin-copy": "^3", "rollup-plugin-minify-html-literals": "^1", "sass": "^1", diff --git a/scss/ueb-node.scss b/scss/ueb-node.scss index 390d9011..4d54c751 100644 --- a/scss/ueb-node.scss +++ b/scss/ueb-node.scss @@ -66,9 +66,10 @@ ueb-node[data-selected="true"]>.ueb-node-border { overflow: hidden; } -.ueb-node-style-operation .ueb-node-wrapper { +ueb-node.ueb-node-style-operation .ueb-node-wrapper { grid-template-rows: min-content auto auto min-content min-content; grid-template-columns: 50% 0% 1fr; + box-shadow: none; } .ueb-node-outputs ueb-pin[data-type="exec"] .ueb-pin-wrapper { @@ -142,7 +143,7 @@ ueb-node.ueb-node-style-event .ueb-node-top { .ueb-node-name { display: flex; align-items: center; - background: radial-gradient(ellipse 100% 100% at 40% 50%, rgba(0, 0, 0, 0.5) 20%, transparent 50%); + background: radial-gradient(ellipse 100% 100% at 35% 55%, rgba(0, 0, 0, 0.5) 15%, transparent 50%); margin: -1px -15px; padding: 2px 15px; } @@ -327,6 +328,14 @@ ueb-node.ueb-node-style-operation .ueb-node-top { font-stretch: condensed; font-weight: bold; line-height: 100%; +} + +ueb-node.ueb-node-style-operation.ueb-node-style-metasound .ueb-node-top { + font-size: 333%; + font-stretch: expanded; +} + +ueb-node.ueb-node-style-operation.ueb-node-style-glass .ueb-node-top { z-index: -1; } @@ -340,6 +349,32 @@ ueb-node.ueb-node-style-operation .ueb-node-variadic { margin-left: -100px; } +ueb-node.ueb-node-style-metasound .ueb-node-wrapper { + border: 1px solid black; + box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.4) inset; + background: linear-gradient(to bottom, + rgba(52, 55, 52, 0.9) 0, + rgba(42, 44, 42, 0.9) 8px, + rgba(31, 32, 31, 0.8) 16px, + rgba(13, 14, 13, 0.9)); +} + +ueb-node.ueb-node-style-metasound .ueb-node-name-text, +ueb-node.ueb-node-style-metasound .ueb-node-name-symbol { + color: #8cc483; +} + +ueb-node.ueb-node-style-metasound.ueb-node-style-operation .ueb-node-name-text { + color: #7c7c7c; +} + +ueb-node.ueb-node-style-metasound .ueb-node-name { + margin: 0px; + padding: 1px 15px; + justify-content: center; + background: none; +} + ueb-node:not(.ueb-node-style-comment) { z-index: 10; } diff --git a/scss/ueb-pin.scss b/scss/ueb-pin.scss index 787e60c4..7200b5d6 100644 --- a/scss/ueb-pin.scss +++ b/scss/ueb-pin.scss @@ -44,6 +44,10 @@ ueb-node[data-advanced-display="Hidden"] ueb-pin[data-advanced-view="true"][data align-items: center; } +ueb-node.ueb-node-style-operation .ueb-pin-wrapper { + padding: 0; +} + ueb-node.ueb-node-style-minimal .ueb-pin-wrapper { min-height: 0; } diff --git a/tests/nodes.spec.js b/tests/nodes.spec.js index 4cb80460..e4efd886 100644 --- a/tests/nodes.spec.js +++ b/tests/nodes.spec.js @@ -7,6 +7,7 @@ import InputNodes from "./resources/InputNodes.js" import IssuesNodes1 from "./resources/IssuesNodes1.js" import LegacyNodes from "./resources/LegacyNodes.js" import MaterialNodes from "./resources/MaterialNodes.js" +import NiagaraNodes from "./resources/NiagaraNodes.js" import OperationsNodes from "./resources/OperationsNodes.js" import OtherNodes from "./resources/OtherNodes.js" import PCGNodes from "./resources/PCGNodes.js" @@ -15,165 +16,175 @@ const nodeTests = [ ...EventNodes.get(), ...FlowControlNodes.get(), ...InputNodes.get(), + ...IssuesNodes1.get(), ...LegacyNodes.get(), ...MaterialNodes.get(), + ...NiagaraNodes.get(), ...OperationsNodes.get(), ...OtherNodes.get(), ...PCGNodes.get(), - ...IssuesNodes1.get(), ] -test.describe.configure({ mode: "parallel" }) +const batchSize = 16 + +let i = 0 +while (i < nodeTests.length) { + + test.describe("Batch " + Math.floor(i / batchSize), () => { + test.describe.configure({ mode: "parallel" }) + do { + const nodeTest = nodeTests[i] + test.describe(nodeTest.name, () => { + + test.describe.configure({ mode: "serial" }) + test.beforeAll(async ({ blueprintPage }) => { + await blueprintPage.removeNodes() + await blueprintPage.paste(nodeTest.value) + }) + + nodeTest.title ??= nodeTest.name + + if (nodeTest.color) { + test( + `${nodeTest.name}: Has correct color`, + async ({ blueprintPage }) => { + expect( + await blueprintPage.node.evaluate(node => node.entity.nodeColor().toString()) + ).toBe(nodeTest.color.toString()) + } + ) + } -for (const nodeTest of nodeTests) { - test.describe(nodeTest.name, () => { + test( + `${nodeTest.name}: Has correct delegate`, + async ({ blueprintPage }) => { + const delegate = blueprintPage.blueprintLocator.locator( + 'ueb-node .ueb-node-top ueb-pin[data-type="delegate"]' + ) + if (nodeTest.delegate) { + await expect(delegate).toBeVisible() + } else { + await expect(delegate).toBeHidden() + } + } + ) - test.describe.configure({ mode: "serial" }) - test.beforeAll(async ({ blueprintPage }) => { - await blueprintPage.removeNodes() - await blueprintPage.paste(nodeTest.value) - }) + test( + `${nodeTest.name}: Has title ${nodeTest.title}`, + async ({ blueprintPage }) => expect( + await blueprintPage.node.evaluate(node => node.nodeDisplayName) + ).toBe(nodeTest.title) + ) - if (nodeTest.color) { - test( - `${nodeTest.name}: Has correct color`, - async ({ blueprintPage }) => { - expect( - await blueprintPage.node.evaluate(node => node.entity.nodeColor().toString()) - ).toBe(nodeTest.color.toString()) + if (nodeTest.subtitle) { + test( + `${nodeTest.name}: Has expected subtitle ${nodeTest.subtitle}`, + async ({ blueprintPage }) => await expect(blueprintPage.node.locator(".ueb-node-subtitle-text")) + .toHaveText(nodeTest.subtitle, { useInnerText: true }) + ) } - ) - } - - test( - `${nodeTest.name}: Has correct delegate`, - async ({ blueprintPage }) => { - const delegate = blueprintPage.blueprintLocator.locator( - 'ueb-node .ueb-node-top ueb-pin[data-type="delegate"]' - ) - if (nodeTest.delegate) { - await expect(delegate).toBeVisible() - } else { - await expect(delegate).toBeHidden() + + if (nodeTest.size) { + test( + `${nodeTest.name}: Has approximately the expected size`, + async ({ blueprintPage }) => { + const expectedSize = await blueprintPage.node.evaluate( + (node, gridSize) => { + const bounding = node.getBoundingClientRect() + const expectedSize = [bounding.width / gridSize, bounding.height / gridSize] + return expectedSize + }, + Configuration.gridSize + ) + expect(Math.abs(nodeTest.size[0] - expectedSize[0])).toBeLessThan(1.5) + expect(Math.abs(nodeTest.size[1] - expectedSize[1])).toBeLessThan(1.5) + if ( + Math.abs(nodeTest.size[0] - expectedSize[0]) > 0.6 + || Math.abs(nodeTest.size[1] - expectedSize[1]) > 0.6 + ) { + console.error(`Node "${nodeTest.name}" size does not match`) + } + } + ) } - } - ) - - if (nodeTest.title) { - test( - `${nodeTest.name}: Has title ${nodeTest.title}`, - async ({ blueprintPage }) => expect( - await blueprintPage.node.evaluate(node => node.nodeDisplayName) - ).toBe(nodeTest.title) - ) - } - - if (nodeTest.subtitle) { - test( - `${nodeTest.name}: Has expected subtitle ${nodeTest.subtitle}`, - async ({ blueprintPage }) => await expect(blueprintPage.node.locator(".ueb-node-subtitle-text")) - .toHaveText(nodeTest.subtitle, { useInnerText: true }) - ) - } - - if (nodeTest.size) { - test( - `${nodeTest.name}: Has approximately the expected size`, - async ({ blueprintPage }) => { - const expectedSize = await blueprintPage.node.evaluate( - (node, gridSize) => { - const bounding = node.getBoundingClientRect() - const expectedSize = [bounding.width / gridSize, bounding.height / gridSize] - return expectedSize - }, - Configuration.gridSize + + if (nodeTest.icon !== undefined) { + test( + `${nodeTest.name}: Has the correct icon`, + async ({ blueprintPage }) => expect( + await blueprintPage.node.evaluate( + node => node.entity.nodeIcon()?.strings.join("") + ) + ).toBe(nodeTest.icon?.strings.join("")) ) - expect(Math.abs(nodeTest.size[0] - expectedSize[0])).toBeLessThan(1.5) - expect(Math.abs(nodeTest.size[1] - expectedSize[1])).toBeLessThan(1.5) - if ( - Math.abs(nodeTest.size[0] - expectedSize[0]) > 0.6 - || Math.abs(nodeTest.size[1] - expectedSize[1]) > 0.6 - ) { - console.error(`Node "${nodeTest.name}" size does not match`) - } } - ) - } - - if (nodeTest.icon !== undefined) { - test( - `${nodeTest.name}: Has the correct icon`, - async ({ blueprintPage }) => expect( - await blueprintPage.node.evaluate( - node => node.entity.nodeIcon()?.strings.join("") + + if (nodeTest.pins !== undefined) { + test( + `${nodeTest.name}: Has ${nodeTest.pins} pins`, + async ({ blueprintPage }) => expect( + await blueprintPage.node.evaluate( + node => node.querySelectorAll("ueb-pin").length + ) + ).toBe(nodeTest.pins) ) - ).toBe(nodeTest.icon?.strings.join("")) - ) - } - - if (nodeTest.pins !== undefined) { - test( - `${nodeTest.name}: Has ${nodeTest.pins} pins`, - async ({ blueprintPage }) => expect( - await blueprintPage.node.evaluate( - node => node.querySelectorAll("ueb-pin").length + } + + if (nodeTest.pinNames) { + test( + `${nodeTest.name}: Has correct pin names`, + async ({ blueprintPage }) => { + const innerTexts = await blueprintPage.node.locator(".ueb-pin-content .ueb-pin-name").allInnerTexts() + const pinNames = innerTexts.map(v => v.trim()).filter(v => v.length > 0) + expect(pinNames).toStrictEqual(nodeTest.pinNames) + } ) - ).toBe(nodeTest.pins) - ) - } - - if (nodeTest.pinNames) { - test( - `${nodeTest.name}: Has correct pin names`, - async ({ blueprintPage }) => { - const innerTexts = await blueprintPage.node.locator(".ueb-pin-content .ueb-pin-name").allInnerTexts() - const pinNames = innerTexts.map(v => v.trim()).filter(v => v.length > 0) - expect(pinNames).toStrictEqual(nodeTest.pinNames) } - ) - } - - test( - `${nodeTest.name}: Expected development`, - async ({ blueprintPage }) => expect( - await blueprintPage.node.evaluate(node => node.entity.isDevelopmentOnly()) - ).toBe(nodeTest.development) - ) - - test( - `${nodeTest.name}: Maintains the order of attributes`, - async ({ blueprintPage }) => { - const actualSerialization = await blueprintPage.getSerializedNodes() - const expectedWords = nodeTest.value - .split("\n") - .map(row => row.match(/\s*("?\w+(\s+\w+)*).+/)?.[1]) - .filter(v => v?.length > 0) - expect(actualSerialization).toMatch(Utility.getFirstWordOrder(expectedWords)) - } - ) - - if (nodeTest.variadic) { - test( - `${nodeTest.name}: Can add new pins`, - async ({ blueprintPage }) => { - const variadic = blueprintPage.node.getByText("Add pin") - await expect(variadic).toBeVisible() - await variadic.click() - expect(await blueprintPage.node.locator("ueb-pin").all()).toHaveLength(nodeTest.pins + 1) + + test( + `${nodeTest.name}: Expected development`, + async ({ blueprintPage }) => expect( + await blueprintPage.node.evaluate(node => node.entity.isDevelopmentOnly()) + ).toBe(nodeTest.development) + ) + + test( + `${nodeTest.name}: Maintains the order of attributes`, + async ({ blueprintPage }) => { + const actualSerialization = await blueprintPage.getSerializedNodes() + const expectedWords = nodeTest.value + .split("\n") + .map(row => row.match(/\s*("?\w+(\s+\w+)*).+/)?.[1]) + .filter(v => v?.length > 0) + expect(actualSerialization).toMatch(Utility.getFirstWordOrder(expectedWords)) + } + ) + + if (nodeTest.variadic) { + test( + `${nodeTest.name}: Can add new pins`, + async ({ blueprintPage }) => { + const variadic = blueprintPage.node.getByText("Add pin") + await expect(variadic).toBeVisible() + await variadic.click() + expect(await blueprintPage.node.locator("ueb-pin").all()).toHaveLength(nodeTest.pins + 1) + } + ) } - ) - } - - if (nodeTest.additionalTest) { - test( - `${nodeTest.name}: Additional tests`, - async ({ blueprintPage }) => - nodeTest.additionalTest( - blueprintPage.node, - await blueprintPage.node.locator("ueb-pin").all(), - blueprintPage, + + if (nodeTest.additionalTest) { + test( + `${nodeTest.name}: Additional tests`, + async ({ blueprintPage }) => + nodeTest.additionalTest( + blueprintPage.node, + await blueprintPage.node.locator("ueb-pin").all(), + blueprintPage, + ) ) - ) - } + } + }) + ++i + } while (i < nodeTests.length && i % batchSize !== 0) }) } diff --git a/tests/parsing.spec.js b/tests/parsing.spec.js index 47367ede..7ee7fa96 100644 --- a/tests/parsing.spec.js +++ b/tests/parsing.spec.js @@ -7,7 +7,6 @@ import KeyBindingEntity from "../js/entity/KeyBindingEntity.js" import LinearColorEntity from "../js/entity/LinearColorEntity.js" import ObjectReferenceEntity from "../js/entity/ObjectReferenceEntity.js" import PinEntity from "../js/entity/PinEntity.js" -import PinTypeEntity from "../js/entity/PinTypeEntity.js" import RotatorEntity from "../js/entity/RotatorEntity.js" import SimpleSerializationRotatorEntity from "../js/entity/SimpleSerializationRotatorEntity.js" import SimpleSerializationVector2DEntity from "../js/entity/SimpleSerializationVector2DEntity.js" @@ -24,6 +23,58 @@ test.beforeAll(() => initializeSerializerFactory()) test.describe.configure({ mode: "parallel" }) +test("Array", () => { + const serializer = SerializerFactory.getSerializer(Array) + + expect(serializer.read("()")).toStrictEqual([]) + expect(serializer.read("( )")).toStrictEqual([]) + expect(serializer.read("(1, 2, 3, 4, 5, 6)")).toStrictEqual([1, 2, 3, 4, 5, 6]) + expect(serializer.read(`( + "alpha", + "beta", + 123, + 3BEF2168446CAA32D5B54289FAB2F0BA, + Some(a=1, b="2") + )`)).toStrictEqual([ + "alpha", + "beta", + 123, + new GuidEntity("3BEF2168446CAA32D5B54289FAB2F0BA"), + new UnknownKeysEntity({ + lookbehind: "Some", + a: 1, + b: "2", + }) + ]) + expect(serializer.read(`( + A(first = (9,8,7,6,5), second = 00000000000000000000000000000000), + B(key="hello"), + )`)).toStrictEqual([ + new UnknownKeysEntity({ + lookbehind: "A", + first: [9, 8, 7, 6, 5], + second: new GuidEntity("00000000000000000000000000000000"), + }), + new UnknownKeysEntity({ + lookbehind: "B", + key: "hello", + }) + ]) + + // Nested + expect(serializer.read("((1, 2), (3, 4))")).toStrictEqual([[1, 2], [3, 4]]) + expect(serializer.read('(((1, 2), (3, 4)), 5)')).toStrictEqual([[[1, 2], [3, 4]], 5]) + expect(serializer.read(`( + One(a = (1,(2,(3,(4)))), b = ()), + )`)).toStrictEqual([ + new UnknownKeysEntity({ + lookbehind: "One", + a: [1, [2, [3, [4]]]], + b: null, + }), + ]) +}) + test("Boolean", () => { let serializer = SerializerFactory.getSerializer(Boolean) expect(serializer.read("true")).toStrictEqual(true) @@ -396,7 +447,7 @@ test("String", () => { expect(() => serializer.read("Hello")).toThrow() expect(serializer.write(`"/Script/CoreUObject.Class'/Script/Interhaptics.HapticSource'"`)) - .toBe(`"/Script/CoreUObject.Class'/Script/Interhaptics.HapticSource'"`) + .toBe(String.raw`"\"/Script/CoreUObject.Class'/Script/Interhaptics.HapticSource'\""`) }) test("UnknownKeysValue", () => { @@ -438,7 +489,6 @@ test("UnknownKeysEntity", () => { unknown = serializer.read('(A = (-1,-2,-3), B = SomeFunction(B1 = "b1", B2 = (X=101,Y=102,Z=103)))') expect(unknown).toBeInstanceOf(UnknownKeysEntity) expect(unknown).toMatchObject({ - lookbehind: "", A: [-1, -2, -3], B: new UnknownKeysEntity({ lookbehind: "SomeFunction", diff --git a/tests/resources/EventNodes.js b/tests/resources/EventNodes.js index 2392c0b1..b3de0c3a 100644 --- a/tests/resources/EventNodes.js +++ b/tests/resources/EventNodes.js @@ -66,7 +66,7 @@ export default class EventNodes extends NodeTests { development: false, }, { - name: "Call AS!%sasd Adsad DD", + name: "Call AS!%sasdAdsadDD", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CallDelegate Name="K2Node_CallDelegate_0" ExportPath=/Script/BlueprintGraph.K2Node_CallDelegate'"/PCG/BP_Elements/PCGAsset.PCGAsset:EventGraph.K2Node_CallDelegate_0"' "DelegateReference"=(MemberName="AS!%sasdAdsadDD",MemberGuid=FB6F7CD342716A4FA22AA6AD6E6B7ED9,bSelfContext=True) diff --git a/tests/resources/NiagaraNodes.js b/tests/resources/NiagaraNodes.js new file mode 100644 index 00000000..6bbf7223 --- /dev/null +++ b/tests/resources/NiagaraNodes.js @@ -0,0 +1,482 @@ +import { css } from "lit" +import Configuration from "../../js/Configuration.js" +import Utility from "../../js/Utility.js" +import PinElement from "../../js/element/PinElement.js" +import Vector4DEntity from "../../js/entity/Vector4DEntity.js" +import { expect } from "../fixtures/test.js" +import NodeTests from "./NodeTests.js" + +export default class NiagaraNodes extends NodeTests { + static { + this.set([ + { + name: "LogicEq", + title: "==", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_84" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_84'" + OpName="Boolean::LogicEq" + ChangeId=E8F9B2BA0D564EB9B931C93DD152FB69 + NodePosX=208 + NodePosY=144 + NodeGuid=E810B307E310413697CB9914DBC2A4B4 + CustomProperties Pin (PinId=BFC2067F95B7478B938D12EC5808D554,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",AutogeneratedDefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=653D4C4C85D349DEA2B79066FB615AD6,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",AutogeneratedDefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=4E45D3F396CE40C69D3F3949D18F1832,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10.5, 5], + pins: 3, + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeTruthy() + for (const pin of pins) { + expect(await pin.evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + } + }, + }, + { + name: "LogicNEq", + title: "!=", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_85" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_85'" + OpName="Boolean::LogicNEq" + ChangeId=3526C49A19484FCAA258F5F1399E5199 + NodePosX=-80 + NodePosY=128 + NodeGuid=591862B832B847AE8F2194837B5C9A68 + CustomProperties Pin (PinId=0E889E7597AD478C9D4E65D8593F9192,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",AutogeneratedDefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A6BF9D71327E4000BD8D29B52CE0C168,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",AutogeneratedDefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=36E21F9BAA3D45BEB74DD7D06C763524,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10, 5], + pins: 3, + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeTruthy() + for (const pin of pins) { + expect(await pin.evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + } + }, + }, + { + name: "Niagara Logic AND", + title: "Logic AND", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_81" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_81'" + OpName="Boolean::LogicAnd" + ChangeId=43E42097BB3043EA8402318509E52B6C + NodePosX=-256 + NodePosY=128 + NodeGuid=A78CF37F79A2480DB705D21E5EF84B07 + CustomProperties Pin (PinId=62F6392991E14BB1A7F0E26AB98DB6DA,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",AutogeneratedDefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=F4F10FA3AC084A1AB2A6ABC3323B83EA,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",AutogeneratedDefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=022AFAE2280C4C8AB3D57EB3F1FECCE9,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A2A828255F8D460C8044F32D6A5E9FCA,PinName="Add",PinType.PinCategory="Misc",PinType.PinSubCategory="DynamicAddPin",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10, 8], + pins: 4, + pinNames: ["A", "B", "Add", "Result"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[0].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + expect(await pins[1].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + expect(await pins[3].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + }, + }, + { + name: "Niagara Logic OR", + title: "Logic OR", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_82" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_82'" + OpName="Boolean::LogicOr" + ChangeId=EB6169ADF12F4DB4AFB4F6E735519E61 + NodePosX=384 + NodePosY=128 + NodeGuid=5E46FFF127D24923A96DB2280941D891 + CustomProperties Pin (PinId=1A8E536F5DB04FCEB0DD5C28120B55F4,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",AutogeneratedDefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=1ABD86FA4F894D4C850E10C0BDFFE9BB,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",AutogeneratedDefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A46C02BB383F42D796497153C25F81CE,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=22A699B9331C4E21911CF5D79481C74E,PinName="Add",PinType.PinCategory="Misc",PinType.PinSubCategory="DynamicAddPin",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10.5, 8], + pins: 4, + pinNames: ["A", "B", "Add", "Result"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[0].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + expect(await pins[1].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + expect(await pins[3].evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + }, + }, + { + name: "Niagara Logic NOT", + title: "Logic NOT", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_83" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_83'" + OpName="Boolean::LogicNot" + ChangeId=850211C079724647B479BFA82D533C76 + NodePosX=128 + NodePosY=128 + NodeGuid=1F3DA06DD27E488A9B6766E8CD203F81 + CustomProperties Pin (PinId=1396C29ABE9F4330B91B88E4788956D0,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",AutogeneratedDefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=F4FABE23D34A4B0C915D6B7117C11B57,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10, 4], + pins: 2, + pinNames: ["A", "Result"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + for (const pin of pins) { + expect(await pin.evaluate(element => element.entity.pinColor().cssText)) + .toBe(css`146, 0, 0`.cssText) + } + }, + }, + { + name: "Niagara Mul", + title: String.fromCharCode(0x2a2f), + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_2" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_2'" + OpName="Numeric::Mul" + ChangeId=D255C58006D942A3B9EA40A7D0A9A26C + NodePosX=-432 + NodePosY=432 + NodeGuid=6C57BD28235446B286204A350C20824E + CustomProperties Pin (PinId=2CA19EFC7B8943C59D997AE9E20EDD9D,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0",AutogeneratedDefaultValue="1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A6992BD09EBC4303B4B30A86BCF295A9,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0",AutogeneratedDefaultValue="1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=8539D0F6BC1F4A85A708B488C37C6FD9,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=C484E4BEEE6E41779EFFB696880FCAF0,PinName="Add",PinType.PinCategory="Misc",PinType.PinSubCategory="DynamicAddPin",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [9.5, 6], + pins: 4, + delegate: false, + development: false, + additionalTest: async node => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeTruthy() + }, + }, + { + name: "Matrix Transform Position", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_93" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_93'" + OpName="Matrix::TransformPosition" + ChangeId=B50CCE545D064D12991CD1407DF14437 + NodePosX=800 + NodePosY=352 + NodeGuid=C8731F92EF32437D949081E06D044FC5 + CustomProperties Pin (PinId=B92730CFC09540B7A8B7C89EBC2A9526,PinName="M",PinToolTip="M",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraMatrix'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0,0.0,0.0,0.0,\\t\\t0.0,1.0,0.0,0.0,\\t\\t0.0,0.0,1.0,0.0,\\t\\t0.0,0.0,0.0,1.0",AutogeneratedDefaultValue="1.0,0.0,0.0,0.0,\\t\\t0.0,1.0,0.0,0.0,\\t\\t0.0,0.0,1.0,0.0,\\t\\t0.0,0.0,0.0,1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=D970C7D4FA4949918AD657EF0D55462B,PinName="V",PinToolTip="V",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0,1.0,1.0",AutogeneratedDefaultValue="1.0,1.0,1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=356EC3AE7343453EB6554019FC162416,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [17.5, 6], + pins: 3, + pinNames: ["M", "V", "Result"], + delegate: false, + development: false, + additionalTest: async node => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + }, + }, + { + name: "Distance", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_59" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_59'" + OpName="Numeric::DistancePos" + ChangeId=4EE3F52A93A84F89AA06ED1D00DBB194 + NodePosX=-128 + NodePosY=384 + NodeGuid=C1DF3F04B05344D4A8BA11F5D9B7022C + CustomProperties Pin (PinId=EBEA639B227A45AEAF9D988854C0B00F,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0",AutogeneratedDefaultValue="1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=D33EAF7D8B0042438A9985EF503D663D,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0",AutogeneratedDefaultValue="1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=4BAEA4BD06C445908F7F5750358768D5,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraFloat'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [17.5, 7], + pins: 3, + pinNames: ["A", "B", "Result"], + delegate: false, + development: false, + additionalTest: async node => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + }, + }, + { + name: "Multiply (Matrix * Vector4)", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_92" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_92'" + OpName="Matrix::MatrixVectorMultiply" + ChangeId=27BF0290BEB64ADA8B924165BC1B4638 + NodePosX=512 + NodePosY=480 + NodeGuid=597CA99BCD6540339C6EDD3F779DD5E2 + CustomProperties Pin (PinId=B18A8E214AA24427AD350D7548E67682,PinName="M",PinToolTip="M",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraMatrix'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0,0.0,0.0,0.0,\t\t0.0,1.0,0.0,0.0,\t\t0.0,0.0,1.0,0.0,\t\t0.0,0.0,0.0,1.0",AutogeneratedDefaultValue="1.0,0.0,0.0,0.0,\t\t0.0,1.0,0.0,0.0,\t\t0.0,0.0,1.0,0.0,\t\t0.0,0.0,0.0,1.0",LinkedTo=(NiagaraNodeParameterMapGet_2 77AD8CE312C44F3996A0AF04607A4DD6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=28E62E1F14204AD1857565EF6C7A63F3,PinName="V",PinToolTip="V",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="22.000000,33.000000,44.000000,55.000000",AutogeneratedDefaultValue="1.0,1.0,1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=9A2423D0FFC945DA85DA823BD71D6BA0,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [22.5, 7], + pins: 3, + pinNames: ["M", "V", "Result"], + delegate: false, + development: false, + additionalTest: async (node, pins, blueprintPage) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[1].evaluate(/** @param {PinElement} pin */pin => pin.entity.DefaultValue.toArray())) + .toStrictEqual([22, 33, 44, 55]) + let inputs = await pins[1].locator("ueb-input").all() + await inputs[0].fill("-1") + await inputs[1].fill("-2") + await inputs[2].fill("-3") + await inputs[3].fill("-4") + await blueprintPage.blur() + await expect(inputs[0]).toContainText("-1.0") + await expect(inputs[1]).toContainText("-2.0") + await expect(inputs[2]).toContainText("-3.0") + await expect(inputs[3]).toContainText("-4.0") + const resultSerialization = await blueprintPage.blueprintLocator.evaluate(blueprint => { + blueprint.selectAll() + return blueprint.template.getCopyInputObject().getSerializedText() + }) + const expectedSerialization = String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_92" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_92'" + OpName="Matrix::MatrixVectorMultiply" + ChangeId=27BF0290BEB64ADA8B924165BC1B4638 + NodePosX=512 + NodePosY=480 + NodeGuid=597CA99BCD6540339C6EDD3F779DD5E2 + CustomProperties Pin (PinId=B18A8E214AA24427AD350D7548E67682,PinName="M",PinToolTip="M",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraMatrix'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0,0.0,0.0,0.0,\t\t0.0,1.0,0.0,0.0,\t\t0.0,0.0,1.0,0.0,\t\t0.0,0.0,0.0,1.0",AutogeneratedDefaultValue="1.0,0.0,0.0,0.0,\t\t0.0,1.0,0.0,0.0,\t\t0.0,0.0,1.0,0.0,\t\t0.0,0.0,0.0,1.0",LinkedTo=(NiagaraNodeParameterMapGet_2 77AD8CE312C44F3996A0AF04607A4DD6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=28E62E1F14204AD1857565EF6C7A63F3,PinName="V",PinToolTip="V",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="-1.000000,-2.000000,-3.000000,-4.000000",AutogeneratedDefaultValue="1.0,1.0,1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=9A2423D0FFC945DA85DA823BD71D6BA0,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + ` + const words = expectedSerialization + .split("\n") + .map(row => row.match(/\s*("?\w+(\s+\w+)*).+/)?.[1]) + .filter(v => v?.length > 0) + expect(resultSerialization).toMatch(Utility.getFirstWordOrder(words)) + }, + }, + { + name: "Align Quaternions", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeFunctionCall Name="NiagaraNodeFunctionCall_125" ExportPath="/Script/NiagaraEditor.NiagaraNodeFunctionCall'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeFunctionCall_125'" + FunctionScript="/Script/Niagara.NiagaraScript'/Niagara/Functions/Rotation/AlignQuaternions.AlignQuaternions'" + CachedChangeId=46822C6B48B121BA33580E809890283B + FunctionDisplayName="AlignQuaternions" + ChangeId=1773D73F6A644287931126B8B2F67371 + NodePosX=-512 + NodePosY=128 + NodeGuid=DF5F941A4DE7430989CE74C42E9F0E6F + CustomProperties Pin (PinId=AE6D0569136F4208B97DD62D9FB2EFF6,PinName="BaselineRotation",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000,1.000",AutogeneratedDefaultValue="0.000,0.000,0.000,1.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=32A04106CE014CF3B7A74E4CE3B7064B,PinName="NewRotation",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000,1.000",AutogeneratedDefaultValue="0.000,0.000,0.000,1.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=5F121C4330074978A1F9792824AE3B5A,PinName="NewOutput",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [16, 5.5], + icon: null, + color: Configuration.nodeColors.darkerBlue, + pins: 3, + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[0].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[2].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + }, + }, + { + name: "Calculate A Volume Textures Distance Field Gradient", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeFunctionCall Name="NiagaraNodeFunctionCall_118" ExportPath="/Script/NiagaraEditor.NiagaraNodeFunctionCall'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeFunctionCall_118'" + FunctionScript="/Script/Niagara.NiagaraScript'/Niagara/Functions/Textures/CalculateA_VolumeTexturesDistanceFieldGradient.CalculateA_VolumeTexturesDistanceFieldGradient'" + SelectedScriptVersion=FE54E5B243F724F59CBE18B14F541840 + CachedChangeId=DC7328824039207713ADFF82DEAF3A32 + FunctionDisplayName="CalculateA_VolumeTexturesDistanceFieldGradient" + ChangeId=5A8AF63A07AB47CCBF806D83FFB9020F + NodePosX=-512 + NodePosY=-512 + NodeGuid=3803C1B7F13C4D818D7AF71764B9957B + CustomProperties Pin (PinId=1C49656E46D540D0AC5961597B947D85,PinName="Sample Position",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000",AutogeneratedDefaultValue="0.000,0.000,0.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=1FF0DD84BE324BEE94AC35A5D83896E1,PinName="Scale Factor",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraFloat'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.000000",AutogeneratedDefaultValue="1.000000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A2030D2728C34982A599AC7EAE2FDDE0,PinName="Volume Texture",PinType.PinCategory="Class",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/Niagara.NiagaraDataInterfaceVolumeTexture'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=FCCC0AC1CCD54A099E450D4A4FA6A23C,PinName="Volume Center Location",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000",AutogeneratedDefaultValue="0.000,0.000,0.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=F1BA4EFE91554666A80389736EB5005B,PinName="Volume Extents",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector3f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000",AutogeneratedDefaultValue="0.000,0.000,0.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=40512C3E4F5C4B85BAC287179458EDEF,PinName="Distance Field Texture Channel",PinType.PinCategory="Enum",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/Engine.UserDefinedEnum'/Niagara/Enums/ENiagara_Float4Channel.ENiagara_Float4Channel'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="NewEnumerator0",AutogeneratedDefaultValue="NewEnumerator0",PersistentGuid=CC547F87ED03FB8EEA661F18ABDF8C16,bHidden=False,bNotConnectable=True,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A5FEC96B447C42EB9CA7BE93AB04F93E,PinName="Vector To Surface",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector3f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=6D55F00292734872B750828E15779CD5,PinName="Distance To Surface",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraFloat'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=CC5826BFCC784BECA71B5B09C145DCF7,PinName="Sampled Color",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A22662833F8446309AF4E72E09B2A6C5,PinName="Field Gradient",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Vector3f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=59A2DB801C7D4E9180793F5F1F597B4F,PinName="IsoSurface Position",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraPosition'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [25, 18], + icon: null, + color: Configuration.nodeColors.darkerBlue, + pins: 11, + pinNames: [ + "Sample Position", + "Scale Factor", + "Volume Texture", + "Volume Center Location", + "Volume Extents", + "Distance Field Texture Channel", + "Vector To Surface", + "Distance To Surface", + "Sampled Color", + "Field Gradient", + "Iso Surface Position", + ], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[0].evaluate(p => p.entity.pinColor().cssText)).toBe(css`251, 146, 251`.cssText) + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`160, 250, 68`.cssText) + expect(await pins[2].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 168, 242`.cssText) + expect(await pins[3].evaluate(p => p.entity.pinColor().cssText)).toBe(css`251, 146, 251`.cssText) + expect(await pins[4].evaluate(p => p.entity.pinColor().cssText)).toBe(css`250, 200, 36`.cssText) + expect(await pins[6].evaluate(p => p.entity.pinColor().cssText)).toBe(css`250, 200, 36`.cssText) + expect(await pins[7].evaluate(p => p.entity.pinColor().cssText)).toBe(css`160, 250, 68`.cssText) + expect(await pins[8].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[9].evaluate(p => p.entity.pinColor().cssText)).toBe(css`250, 200, 36`.cssText) + expect(await pins[10].evaluate(p => p.entity.pinColor().cssText)).toBe(css`251, 146, 251`.cssText) + }, + }, + { + name: "Interpolate Over Time Quaternion", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeFunctionCall Name="NiagaraNodeFunctionCall_160" ExportPath="/Script/NiagaraEditor.NiagaraNodeFunctionCall'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeFunctionCall_160'" + FunctionScript="/Script/Niagara.NiagaraScript'/Niagara/Functions/Math/InterpolateOverTime_Quaternion.InterpolateOverTime_Quaternion'" + SelectedScriptVersion=5A6359E649629A32B6B4CA9D6A4CF08B + CachedChangeId=BD6C65FF48EE55BD0354208C6E7CFC5E + FunctionDisplayName="InterpolateOverTime_Quaternion" + ChangeId=AFBFC05C8AFD4E3D9256AB1181E12D82 + NodePosX=544 + NodePosY=-384 + NodeGuid=12612978C2CC4F0BB84C22D4B4EEC3E1 + CustomProperties Pin (PinId=A123640517EC4387958B42AE6AAAF9FA,PinName="Map",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraParameterMap'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=167B2B0C6E354603A988999007C19BA1,PinName="Rate Of Change",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraFloat'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=E6184BA334B24985A45FC5F9E73F81D2,PinName="FirstFrame",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraBool'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=1BDC577CF32A4990A7EEEB9AC3BBDAA2,PinName="Delta Time",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraFloat'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=7C1356D8BDD74EBBA569EB9E1905EA0D,PinName="Target Value",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000,1.000",AutogeneratedDefaultValue="0.000,0.000,0.000,1.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A57085A8B43144C1BCD7D67A81E09054,PinName="Initial Value",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.000,0.000,0.000,1.000",AutogeneratedDefaultValue="0.000,0.000,0.000,1.000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=0A10BAB1865F442EA0BFE5F320469DE3,PinName="Map",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraParameterMap'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=3CF7483A34D64238BE6A3E9C20CE5111,PinName="Moving Average",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/CoreUObject.Quat4f'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=True,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [17, 13], + icon: null, + color: Configuration.nodeColors.darkerBlue, + pins: 8, + pinNames: [ + "Map", + "Rate Of Change", + "First Frame", + "Delta Time", + "Target Value", + "Initial Value", + "Map", + "Moving Average", + ], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`160, 250, 68`.cssText) + expect(await pins[2].evaluate(p => p.entity.pinColor().cssText)).toBe(css`146, 0, 0`.cssText) + expect(await pins[3].evaluate(p => p.entity.pinColor().cssText)).toBe(css`160, 250, 68`.cssText) + expect(await pins[4].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[5].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[7].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + }, + }, + { + name: "Niagara Abs", + title: "Abs", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_12" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_12'" + OpName="Numeric::Abs" + ChangeId=29B8C1201A5A453DAF589029C8FF3C71 + NodePosX=224 + NodePosY=336 + NodeGuid=F18211DAA458447E912DC2409D2257FA + CustomProperties Pin (PinId=946C3D0E755A4B75A23E5B462C1A7B82,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1.0",AutogeneratedDefaultValue="1.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=9CD6DB4E94234A179733ED462921D416,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10, 2.5], + icon: null, + pins: 2, + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeTruthy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeFalsy() + expect(await pins[0].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + }, + }, + { + name: "Niagara Add", + title: "+", + value: String.raw` + Begin Object Class=/Script/NiagaraEditor.NiagaraNodeOp Name="NiagaraNodeOp_0" ExportPath="/Script/NiagaraEditor.NiagaraNodeOp'/Engine/Transient.NewNiagaraScript:NiagaraScriptSource_0.NiagaraGraph_0.NiagaraNodeOp_0'" + OpName="Numeric::Add" + ChangeId=264B3D2C683F4201966C715DBA00A590 + NodePosX=800 + NodePosY=272 + NodeGuid=E62FD339348E42159E41F661B803F129 + CustomProperties Pin (PinId=553D489D7489472EA81C9C009029186B,PinName="A",PinToolTip="A",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.0",AutogeneratedDefaultValue="0.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=9905D9F666734D179C8DA03CAE65FCD2,PinName="B",PinToolTip="B",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.0",AutogeneratedDefaultValue="0.0",LinkedTo=(NiagaraNodeFunctionCall_179 73E46ACCCD224AE6958C0DB9D300767E,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=B1DDBB31FCF24C5FBD99B7205274CF48,PinName="Result",PinToolTip="Result",Direction="EGPD_Output",PinType.PinCategory="Type",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.ScriptStruct'/Script/Niagara.NiagaraNumeric'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=481ADC022BBF4D37BD5D5CE91D03C9C3,PinName="Add",PinType.PinCategory="Misc",PinType.PinSubCategory="DynamicAddPin",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [9.5, 6], + icon: null, + pins: 4, + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeTruthy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeFalsy() + expect(await pins[0].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + expect(await pins[3].evaluate(p => p.entity.pinColor().cssText)).toBe(css`0, 88, 200`.cssText) + }, + }, + ]) + } +} diff --git a/tests/resources/OperationsNodes.js b/tests/resources/OperationsNodes.js index f0cb9864..add00e2a 100644 --- a/tests/resources/OperationsNodes.js +++ b/tests/resources/OperationsNodes.js @@ -6,7 +6,43 @@ export default class OperationsNodes extends NodeTests { static { this.set([ { - name: "Less", + name: "Add double double", + title: "+", + value: String.raw` + Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_1" ExportPath="/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator'/Game/Map.Map:PersistentLevel.Map.EventGraph.K2Node_CommutativeAssociativeBinaryOperator_1'" + NumAdditionalInputs=3 + bIsPureFunc=True + FunctionReference=(MemberParent="/Script/CoreUObject.Class'/Script/Engine.KismetMathLibrary'",MemberName="Add_DoubleDouble") + NodePosX=1920 + NodePosY=-1424 + NodeGuid=898B12095E2149EB85F3D66ABF812C8D + CustomProperties Pin (PinId=7E49A86445F1A83779096DA2653ED2F9,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet Math Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject="/Script/CoreUObject.Class'/Script/Engine.KismetMathLibrary'",PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__KismetMathLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=42E8757A4F34479027E6F3AC60B85161,PinName="A",PinToolTip="A\nFloat (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="123.000000",AutogeneratedDefaultValue="0.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=4631AA9648980812910BB6A44F8DB926,PinName="B",PinToolTip="B\nFloat (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="234.000000",AutogeneratedDefaultValue="1.000000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=46E01E834DC9C4ADC2A371BA3E6750FD,PinName="ReturnValue",PinToolTip="Return Value\nFloat (double-precision)\n\nAddition (A + B)",Direction="EGPD_Output",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="0.0",AutogeneratedDefaultValue="0.0",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=EA79B248A6876A3D535FD66AF32AD617,PinName="C",PinToolTip="C\nFloat (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="345.000000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=A1A5D31E765DD0B28389412697654E59,PinName="D",PinToolTip="D\nFloat (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="456.000000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=F622CBB2B1AD29C2A0F225F8D5275852,PinName="E",PinToolTip="E\nFloat (double-precision)",PinType.PinCategory="real",PinType.PinSubCategory="double",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="567.000000",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10.5, 11], + pins: 6, + delegate: false, + development: false, + variadic: true, + additionalTest: async (node, pins) => { + const values = ["123.0", "234.0", "345.0", "456.0", "567.0"] + for (let i = 0; i < pins.length; ++i) { + expect(await pins[i].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toStrictEqual(SVGIcon.operationPin.strings.join("")) + if (i < values.length) { + await expect(pins[i].locator("ueb-input")).toContainText(values[i]) + } + } + } + }, + { + name: "Less timespan timespan", title: "<", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_0" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_0"' @@ -32,7 +68,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Less Equal", + name: "Less equal double double", title: "<=", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_6" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_6"' @@ -59,7 +95,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Equal", + name: "Equal slate brush", title: "==", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_0" @@ -85,7 +121,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Greater", + name: "Greater timespan timespan", title: ">", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_3" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_3"' @@ -111,7 +147,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Greater Equal", + name: "Greater equal byte byte", title: ">=", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_8" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_8"' @@ -192,7 +228,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise AND 1", + name: "Bitwise AND int", title: "&", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_7" ExportPath=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CommutativeAssociativeBinaryOperator_7"' @@ -220,7 +256,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise AND 2", + name: "Bitwise AND int64", title: "&", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_8" ExportPath=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CommutativeAssociativeBinaryOperator_8"' @@ -248,7 +284,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise OR 1", + name: "Bitwise OR int", title: "|", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_9" ExportPath=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CommutativeAssociativeBinaryOperator_9"' @@ -280,7 +316,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise OR 2", + name: "Bitwise OR int64", title: "|", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_10" ExportPath=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CommutativeAssociativeBinaryOperator_10"' @@ -310,7 +346,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise NOT 1", + name: "Bitwise NOT int", title: "~", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_10" ExportPath=/Script/BlueprintGraph.K2Node_CallFunction'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CallFunction_10"' @@ -337,7 +373,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise NOT 2", + name: "Bitwise NOT int64", title: "~", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_11" ExportPath=/Script/BlueprintGraph.K2Node_CallFunction'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CallFunction_11"' @@ -364,7 +400,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise XOR 1", + name: "Bitwise XOR int", title: "^", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_12" ExportPath=/Script/BlueprintGraph.K2Node_CallFunction'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CallFunction_12"' @@ -393,7 +429,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Bitwise XOR 2", + name: "Bitwise XOR int64", title: "^", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_13" ExportPath=/Script/BlueprintGraph.K2Node_CallFunction'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_CallFunction_13"' @@ -447,7 +483,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Not Equal", + name: "Not equal gameplay tag", title: "!=", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_0" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_0"' @@ -474,7 +510,7 @@ export default class OperationsNodes extends NodeTests { } }, { - name: "Equal 2", + name: "Equal byte", title: "==", value: String.raw` Begin Object Class=/Script/BlueprintGraph.K2Node_PromotableOperator Name="K2Node_PromotableOperator_1" ExportPath=/Script/BlueprintGraph.K2Node_PromotableOperator'"/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_PromotableOperator_1"' diff --git a/tests/resources/OtherNodes.js b/tests/resources/OtherNodes.js index 5b7e948b..ebd695b6 100644 --- a/tests/resources/OtherNodes.js +++ b/tests/resources/OtherNodes.js @@ -601,6 +601,24 @@ export default class OtherNodes extends NodeTests { await expect(pins[6].locator("input")).toBeChecked() } }, + { + name: "Self", + value: String.raw` + Begin Object Class=/Script/BlueprintGraph.K2Node_Self Name="K2Node_Self_0" ExportPath="/Script/BlueprintGraph.K2Node_Self'/Temp/Untitled_1.Untitled_1:PersistentLevel.Untitled.EventGraph.K2Node_Self_0'" + NodePosX=224 + NodePosY=848 + NodeGuid=70A71735B5F247699242DBC67A166772 + CustomProperties Pin (PinId=3B41006840BA3918576575AC4419E030,PinName="self",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="self",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_1 C3A04D2142AC39C30B0A62876AF7ECC8,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [8, 2.5], + pins: 1, + delegate: false, + development: false, + additionalTest: async node => { + expect(await (node.evaluate(n => n.classList.contains("ueb-node-style-getter")))).toBeTruthy() + } + }, ]) } } diff --git a/tests/resources/PCGNodes.js b/tests/resources/PCGNodes.js index 55e7abbb..a89d05ac 100644 --- a/tests/resources/PCGNodes.js +++ b/tests/resources/PCGNodes.js @@ -1,3 +1,5 @@ +import { css } from "lit" +import Configuration from "../../js/Configuration.js" import SVGIcon from "../../js/SVGIcon.js" import { expect } from "../fixtures/test.js" import NodeTests from "./NodeTests.js" @@ -91,6 +93,303 @@ export default class PCGNodes extends NodeTests { .toEqual(SVGIcon.pcgSpatialPin.strings.join("")) } }, + { + name: "Set Point Color", + value: String.raw` + Begin Object Class=/Script/PCGEditor.PCGEditorGraphNode Name="PCGEditorGraphNode_1" ExportPath="/Script/PCGEditor.PCGEditorGraphNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1'" + Begin Object Class=/Script/PCG.PCGNode Name="ExecuteBlueprint_1" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Begin Object Class=/Script/PCG.PCGBlueprintSettings Name="PCGBlueprintSettings_1" ExportPath="/Script/PCG.PCGBlueprintSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGBlueprintSettings_1'" + Begin Object Class=/PCG/BP_Elements/SetPointColor.SetPointColor_C Name="SetPointColor_C_0" ExportPath="/PCG/BP_Elements/SetPointColor.SetPointColor_C'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGBlueprintSettings_1.SetPointColor_C_0'" + End Object + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_0'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_1'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_2'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_3'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_4'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_5" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_5'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_6" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_6'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_7" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_7'" + End Object + End Object + Begin Object Name="ExecuteBlueprint_1" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Begin Object Name="PCGBlueprintSettings_1" ExportPath="/Script/PCG.PCGBlueprintSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGBlueprintSettings_1'" + Begin Object Name="SetPointColor_C_0" ExportPath="/PCG/BP_Elements/SetPointColor.SetPointColor_C'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGBlueprintSettings_1.SetPointColor_C_0'" + End Object + BlueprintElementType="/Script/Engine.BlueprintGeneratedClass'/PCG/BP_Elements/SetPointColor.SetPointColor_C'" + BlueprintElementInstance="/PCG/BP_Elements/SetPointColor.SetPointColor_C'SetPointColor_C_0'" + Seed=-1868785340 + bExposeToLibrary=False + CachedOverridableParams(0)=(Label="Seed",PropertiesNames=("Seed"),PropertyClass="/Script/CoreUObject.Class'/Script/PCG.PCGBlueprintSettings'") + CachedOverridableParams(1)=(Label="R",PropertiesNames=("Linear Color","R"),PropertyClass="/Script/Engine.BlueprintGeneratedClass'/PCG/BP_Elements/SetPointColor.SetPointColor_C'") + CachedOverridableParams(2)=(Label="G",PropertiesNames=("Linear Color","G"),PropertyClass="/Script/Engine.BlueprintGeneratedClass'/PCG/BP_Elements/SetPointColor.SetPointColor_C'") + CachedOverridableParams(3)=(Label="B",PropertiesNames=("Linear Color","B"),PropertyClass="/Script/Engine.BlueprintGeneratedClass'/PCG/BP_Elements/SetPointColor.SetPointColor_C'") + CachedOverridableParams(4)=(Label="A",PropertiesNames=("Linear Color","A"),PropertyClass="/Script/Engine.BlueprintGeneratedClass'/PCG/BP_Elements/SetPointColor.SetPointColor_C'") + End Object + Begin Object Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_0'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="In",AllowedTypes=Point) + End Object + Begin Object Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_1'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="Overrides",AllowedTypes=Param,bAdvancedPin=True,Tooltip=NSLOCTEXT("PCGSettings", "GlobalParamPinTooltip", "Atribute Set containing multiple parameters to override. Names must match perfectly.")) + End Object + Begin Object Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_2'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="Seed",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "int32", "Seed")) + End Object + Begin Object Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_3'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="Out",AllowedTypes=Point) + End Object + Begin Object Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_4'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="R",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "float", "Linear Color/R")) + End Object + Begin Object Name="PCGPin_5" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_5'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="G",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "float", "Linear Color/G")) + End Object + Begin Object Name="PCGPin_6" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_6'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="B",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "float", "Linear Color/B")) + End Object + Begin Object Name="PCGPin_7" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_1.ExecuteBlueprint_1.PCGPin_7'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_1.ExecuteBlueprint_1'" + Properties=(Label="A",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "float", "Linear Color/A")) + End Object + PositionX=512 + PositionY=256 + SettingsInterface="/Script/PCG.PCGBlueprintSettings'PCGBlueprintSettings_1'" + InputPins(0)="/Script/PCG.PCGPin'PCGPin_0'" + InputPins(1)="/Script/PCG.PCGPin'PCGPin_1'" + InputPins(2)="/Script/PCG.PCGPin'PCGPin_2'" + InputPins(3)="/Script/PCG.PCGPin'PCGPin_4'" + InputPins(4)="/Script/PCG.PCGPin'PCGPin_5'" + InputPins(5)="/Script/PCG.PCGPin'PCGPin_6'" + InputPins(6)="/Script/PCG.PCGPin'PCGPin_7'" + OutputPins(0)="/Script/PCG.PCGPin'PCGPin_3'" + End Object + PCGNode="/Script/PCG.PCGNode'ExecuteBlueprint_1'" + NodePosX=512 + NodePosY=256 + AdvancedPinDisplay=Shown + bUserSetEnabledState=True + NodeGuid=74E6A6E9EF084BF4A24B56E43712EC85 + CustomProperties Pin (PinId=53B558601FD94E0A81D59879F7C84EE4,PinName="In",PinFriendlyName="In",PinType.PinCategory="Concrete Data",PinType.PinSubCategory="Point Data",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=D2D1A26AF9524E569FCA2DD6B15C7667,PinName="Overrides",PinFriendlyName="Overrides",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=3506C39048EA435D8E16028C6E481FFE,PinName="Seed",PinFriendlyName="Seed",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=7FBFCE159BE5483F9C0AF8E8053BF3B9,PinName="R",PinFriendlyName="R",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=996EFC1D746C46D39BB50E0AF3A156D4,PinName="G",PinFriendlyName="G",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=4693B14DC236425097A5FA8B3DDD46A3,PinName="B",PinFriendlyName="B",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=831312342DB14283A98ECB2D331C1B3D,PinName="A",PinFriendlyName="A",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=6F13FFA544CF43BD94FA007B9D6FC25A,PinName="Out",PinFriendlyName="Out",Direction="EGPD_Output",PinType.PinCategory="Concrete Data",PinType.PinSubCategory="Point Data",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [10, 16], + pins: 8, + pinNames: ["In", "Overrides", "Seed", "R", "G", "B", "A", "Out"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await pins[0].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgStackPin.strings.join("")) + expect(await pins[0].evaluate(pin => pin.entity.pinColor().cssText)).toEqual("63, 137, 255") + expect(await pins[1].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[2].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[3].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[4].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[5].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[6].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[7].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgPin.strings.join("")) + expect(await pins[7].evaluate(pin => pin.entity.pinColor().cssText)).toEqual("63, 137, 255") + } + }, + { + name: "PCG Branch", + title: "Branch", + value: String.raw` + Begin Object Class=/Script/PCGEditor.PCGEditorGraphNode Name="PCGEditorGraphNode_2" ExportPath="/Script/PCGEditor.PCGEditorGraphNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2'" + Begin Object Class=/Script/PCG.PCGNode Name="Branch_2" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2'" + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_4'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_3'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_2'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_1'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_0'" + End Object + Begin Object Class=/Script/PCG.PCGBranchSettings Name="PCGBranchSettings_0" ExportPath="/Script/PCG.PCGBranchSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGBranchSettings_0'" + End Object + End Object + Begin Object Name="Branch_2" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2'" + Begin Object Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_4'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_2.Branch_2'" + Properties=(Label="Output B",Tooltip=NSLOCTEXT("FPCGBranchElement", "OutputPinTooltipB", "Will only route input if \'Output To B\' (overridable) is true")) + End Object + Begin Object Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_3'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_2.Branch_2'" + Properties=(Label="Output A",Tooltip=NSLOCTEXT("FPCGBranchElement", "OutputPinTooltipA", "Will only route input if \'Output To B\' (overridable) is false")) + End Object + Begin Object Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_2'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_2.Branch_2'" + Properties=(Label="Output to B",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", "bool", "bOutputToB")) + End Object + Begin Object Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_1'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_2.Branch_2'" + Properties=(Label="Overrides",AllowedTypes=Param,bAdvancedPin=True,Tooltip=NSLOCTEXT("PCGSettings", "GlobalParamPinTooltip", "Atribute Set containing multiple parameters to override. Names must match perfectly.")) + End Object + Begin Object Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGPin_0'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_2.Branch_2'" + Properties=(Label="In") + End Object + Begin Object Name="PCGBranchSettings_0" ExportPath="/Script/PCG.PCGBranchSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_2.Branch_2.PCGBranchSettings_0'" + Seed=-420875123 + CachedOverridableParams(0)=(Label="Output to B",PropertiesNames=("bOutputToB"),PropertyClass="/Script/CoreUObject.Class'/Script/PCG.PCGBranchSettings'") + End Object + PositionX=384 + PositionY=128 + SettingsInterface="/Script/PCG.PCGBranchSettings'PCGBranchSettings_0'" + InputPins(0)="/Script/PCG.PCGPin'PCGPin_0'" + InputPins(1)="/Script/PCG.PCGPin'PCGPin_1'" + InputPins(2)="/Script/PCG.PCGPin'PCGPin_2'" + OutputPins(0)="/Script/PCG.PCGPin'PCGPin_3'" + OutputPins(1)="/Script/PCG.PCGPin'PCGPin_4'" + End Object + PCGNode="/Script/PCG.PCGNode'Branch_2'" + NodePosX=384 + NodePosY=128 + AdvancedPinDisplay=Shown + bUserSetEnabledState=True + NodeGuid=BCEBB6C85A8844F2B06322DF95805CB5 + CustomProperties Pin (PinId=4CBC2755317F47F7BA23B7E928C7A436,PinName="In",PinFriendlyName="In",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=390B992FE20D4AAAB63EC14DE8732AD2,PinName="Overrides",PinFriendlyName="Overrides",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=15D942E8E7C640719B71544BBEA5365D,PinName="Output to B",PinFriendlyName="Output To B",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=9397BA7C5D694D9FB070B353BB26BA57,PinName="Output A",PinFriendlyName="Output A",Direction="EGPD_Output",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=32167D5DBA9A4498B162B90B0F16A191,PinName="Output B",PinFriendlyName="Output B",Direction="EGPD_Output",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [13, 9], + color: Configuration.nodeColors.intenseGreen, + pins: 5, + pinNames: ["In", "Overrides", "Output To B", "Output A", "Output B"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await pins[0].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgStackPin.strings.join("")) + expect(await pins[0].evaluate(pin => pin.entity.pinColor().cssText)).toEqual("132, 132, 132") + expect(await pins[1].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[2].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgParamPin.strings.join("")) + expect(await pins[3].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgPin.strings.join("")) + expect(await pins[3].evaluate(pin => pin.entity.pinColor().cssText)).toEqual("132, 132, 132") + expect(await pins[4].evaluate(pin => pin.template.renderIcon().strings.join(""))) + .toEqual(SVGIcon.pcgPin.strings.join("")) + expect(await pins[4].evaluate(pin => pin.entity.pinColor().cssText)).toEqual("132, 132, 132") + } + }, + { + name: "PCG Select", + title: "Select", + value: String.raw` + Begin Object Class=/Script/PCGEditor.PCGEditorGraphNode Name="PCGEditorGraphNode_0" ExportPath="/Script/PCGEditor.PCGEditorGraphNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0'" + Begin Object Class=/Script/PCG.PCGNode Name="Select_1" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1'" + Begin Object Class=/Script/PCG.PCGBooleanSelectSettings Name="PCGBooleanSelectSettings_0" ExportPath="/Script/PCG.PCGBooleanSelectSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGBooleanSelectSettings_0'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_0'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_1'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_2'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_3'" + End Object + Begin Object Class=/Script/PCG.PCGPin Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_4'" + End Object + End Object + Begin Object Name="Select_1" ExportPath="/Script/PCG.PCGNode'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1'" + Begin Object Name="PCGBooleanSelectSettings_0" ExportPath="/Script/PCG.PCGBooleanSelectSettings'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGBooleanSelectSettings_0'" + Seed=-764420405 + CachedOverridableParams(0)=(Label="Use Input B",PropertiesNames=("bUseInputB"),PropertyClass="/Script/CoreUObject.Class'/Script/PCG.PCGBooleanSelectSettings'") + End Object + Begin Object Name="PCGPin_0" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_0'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_0.Select_1'" + Properties=(Label="Input A",Tooltip=NSLOCTEXT("FPCGBooleanSelectElement", "FirstInputPinTooltip", "Will only be used if \'Use Input B\' (overridable) is false")) + End Object + Begin Object Name="PCGPin_1" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_1'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_0.Select_1'" + Properties=(Label="Input B",Tooltip=NSLOCTEXT("FPCGBooleanSelectElement", "SecondInputPinTooltip", "Will only be used if \'Use Input B\' (overridable) is true")) + End Object + Begin Object Name="PCGPin_2" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_2'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_0.Select_1'" + Properties=(Label="Overrides",AllowedTypes=Param,bAdvancedPin=True,Tooltip=NSLOCTEXT("PCGSettings", "GlobalParamPinTooltip", "Atribute Set containing multiple parameters to override. Names must match perfectly.")) + End Object + Begin Object Name="PCGPin_3" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_3'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_0.Select_1'" + Properties=(Label="Use Input B",AllowedTypes=Param,bAllowMultipleData=False,bAllowMultipleConnections=False,bAdvancedPin=True,Tooltip=LOCGEN_FORMAT_ORDERED(NSLOCTEXT("PCGSettings", "OverridableParamPinTooltip", "{0}Attribute type is \"{1}\" and its exact name is \"{2}\""), "", NSLOCTEXT("", "16C4E377C2E74CE5BA25165971FD45EF", "bool"), NSLOCTEXT("", "C2769CCD78C64949B3473D033EAF34BC", "bUseInputB"))) + End Object + Begin Object Name="PCGPin_4" ExportPath="/Script/PCG.PCGPin'/Game/NewPCGGraph.NewPCGGraph:PCGEditorGraph_0.PCGEditorGraphNode_0.Select_1.PCGPin_4'" + Node="/Script/PCG.PCGNode'PCGEditorGraphNode_0.Select_1'" + Properties=(Label="Out",Tooltip=NSLOCTEXT("FPCGBooleanSelectElement", "OutputPinTooltip", "All input will gathered into a single data collection")) + End Object + PositionX=896 + PositionY=256 + SettingsInterface="/Script/PCG.PCGBooleanSelectSettings'PCGBooleanSelectSettings_0'" + InputPins(0)="/Script/PCG.PCGPin'PCGPin_0'" + InputPins(1)="/Script/PCG.PCGPin'PCGPin_1'" + InputPins(2)="/Script/PCG.PCGPin'PCGPin_2'" + InputPins(3)="/Script/PCG.PCGPin'PCGPin_3'" + OutputPins(0)="/Script/PCG.PCGPin'PCGPin_4'" + End Object + PCGNode="/Script/PCG.PCGNode'Select_1'" + NodePosX=896 + NodePosY=256 + AdvancedPinDisplay=Shown + bUserSetEnabledState=True + NodeGuid=DC0AF7AFAE5643AEAA102848F6C250A1 + CustomProperties Pin (PinId=44103E0035144DDA9CBDF09DABD6FFBB,PinName="Input A",PinFriendlyName="Input A",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=DDAD3EAD7E13487AB8307520FEC50BFE,PinName="Input B",PinFriendlyName="Input B",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + CustomProperties Pin (PinId=3368F1F000994A78A1AE7445BE7A84F5,PinName="Overrides",PinFriendlyName="Overrides",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=0EFB81FA173B41D294EB0795B08D8881,PinName="Use Input B",PinFriendlyName="Use Input B",PinType.PinCategory="Attribute Set",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,) + CustomProperties Pin (PinId=49C207211518412D826C77C76B639411,PinName="Out",PinFriendlyName="Out",Direction="EGPD_Output",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) + End Object + `, + size: [11, 11], + color: Configuration.nodeColors.intenseGreen, + icon: null, + pins: 5, + pinNames: ["Input A", "Input B", "Overrides", "Use Input B", "Out"], + delegate: false, + development: false, + additionalTest: async (node, pins) => { + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-glass"))).toBeFalsy() + expect(await node.evaluate(node => node.classList.contains("ueb-node-style-default"))).toBeTruthy() + expect(await pins[0].evaluate(p => p.entity.pinColor().cssText)).toBe(css`132, 132, 132`.cssText) + expect(await pins[1].evaluate(p => p.entity.pinColor().cssText)).toBe(css`132, 132, 132`.cssText) + expect(await pins[2].evaluate(p => p.entity.pinColor().cssText)).toBe(css`255, 166, 39`.cssText) + expect(await pins[3].evaluate(p => p.entity.pinColor().cssText)).toBe(css`255, 166, 39`.cssText) + expect(await pins[4].evaluate(p => p.entity.pinColor().cssText)).toBe(css`132, 132, 132`.cssText) + }, + }, ]) } } diff --git a/tests/resources/serializedEntity2.js b/tests/resources/serializedEntity2.js index 37734777..d16ed85e 100644 --- a/tests/resources/serializedEntity2.js +++ b/tests/resources/serializedEntity2.js @@ -5,7 +5,7 @@ export default `{ someBoolean: True someBoolean2: False someObjectString: "gamma" - someArray: (400,500,600,700,800,) - someArray2: (400,500,600,700,800,) + someArray: (400,500,600,700,800) + someArray2: (400,500,600,700,800) someEntity: Entity1(a=8, b=9) }` diff --git a/tests/resources/serializedEntity3.js b/tests/resources/serializedEntity3.js index c5812dd2..472021e8 100644 --- a/tests/resources/serializedEntity3.js +++ b/tests/resources/serializedEntity3.js @@ -8,8 +8,8 @@ export default `[[ golf: () hotel: () india: () - juliett: ("a","b","c","d","e",) - kilo: (True,False,False,True,True,) + juliett: ("a","b","c","d","e") + kilo: (True,False,False,True,True) mike: "Bar" november: 0 oscar: Entity1(a=8, b=9) @@ -23,7 +23,7 @@ export default `[[ sierra.someBoolean: True sierra.someBoolean2: False sierra.someObjectString: "gamma" - sierra.someArray: (400,500,600,700,800,) - sierra.someArray2: (400,500,600,700,800,) + sierra.someArray: (400,500,600,700,800) + sierra.someArray2: (400,500,600,700,800) sierra.someEntity: Entity1(a=8, b=9) ]]` diff --git a/tests/resources/serializedEntity4.js b/tests/resources/serializedEntity4.js index 1b476c16..5cc4ffb1 100644 --- a/tests/resources/serializedEntity4.js +++ b/tests/resources/serializedEntity4.js @@ -8,8 +8,8 @@ export default `Begin \${first.golf} => () \${first.hotel} => () \${first.india} => () - \${first.juliett} => ("a","b","c","d","e",) - \${first.kilo} => (True,False,False,True,True,) + \${first.juliett} => ("a","b","c","d","e") + \${first.kilo} => (True,False,False,True,True) \${first.mike} => "Bar" \${first.november} => 0 \${first.oscar} => E1[A:8 - B:9] @@ -22,8 +22,8 @@ export default `Begin \${first.sierra.someBoolean} => True \${first.sierra.someBoolean2} => False \${first.sierra.someObjectString} => "gamma" - \${first.sierra.someArray} => (400,500,600,700,800,) - \${first.sierra.someArray2} => (400,500,600,700,800,) + \${first.sierra.someArray} => (400,500,600,700,800) + \${first.sierra.someArray2} => (400,500,600,700,800) \${first.sierra.someEntity} => E1[A:8 - B:9] \${second(0).a} => 1 \${second(0).b} => 2 diff --git a/tests/utility.spec.js b/tests/utility.spec.js index 6e389c3e..22fdc144 100644 --- a/tests/utility.spec.js +++ b/tests/utility.spec.js @@ -53,8 +53,11 @@ test("approximatelyEqual method test", () => { test("equals method test", () => { expect(Utility.equals(0.2, 0.2)).toBeTruthy() + // @ts-expect-error expect(Utility.equals(new Number(0.7), 0.7)).toBeTruthy() + // @ts-expect-error expect(Utility.equals(-40.3, new Number(-40.3))).toBeTruthy() + // @ts-expect-error expect(Utility.equals(new Number(-40.3), new Number(-40.3))).toBeTruthy() expect(Utility.equals(0.2 + 0.1, 0.3)).toBeFalsy() // Strict equality expect(Utility.equals(null, undefined)).toBeFalsy() @@ -65,8 +68,10 @@ test("equals method test", () => { expect(Utility.equals(-6845, -6845n)).toBeTruthy() expect(Utility.equals(7735n, 7736)).toBeFalsy() expect(Utility.equals("abc", "abc")).toBeTruthy() + // @ts-expect-error expect(Utility.equals(new String("abc"), new String("abc"))).toBeTruthy() expect(Utility.equals("abc", "aBc")).toBeFalsy() + expect(Utility.equals([], [])).toBeTruthy() expect(Utility.equals( [-2, "alpha", new String("beta"), new Number(40), [1, 2, 3]], [new Number(-2), new String("alpha"), new String("beta"), new Number(40), new Array(1, 2, 3)] @@ -172,15 +177,9 @@ test("String escaping methods test", () => { expect(Utility.escapeString('"')).toBe('\\"') expect(Utility.unescapeString('\\"')).toBe('"') - expect(Utility.escapeString("'")).toBe("\\'") - expect(Utility.unescapeString("\\'")).toBe("'") - expect(Utility.escapeString(String.raw`\"`)).toBe(String.raw`\\\"`) expect(Utility.unescapeString(String.raw`\"`)).toBe('"') - expect(Utility.escapeString(String.raw`\'`)).toBe(String.raw`\\\'`) - expect(Utility.unescapeString(String.raw`\'`)).toBe("'") - expect(Utility.escapeString(String.raw`Hello \"World\"`)).toBe(String.raw`Hello \\\"World\\\"`) expect(Utility.unescapeString(String.raw`Hello \"World\"`)).toBe('Hello "World"') diff --git a/types.js b/types.js index de48dc8f..37c39111 100644 --- a/types.js +++ b/types.js @@ -1,12 +1,12 @@ /** -* @template T -* @typedef {new (...args: any) => T} AnyConstructor -*/ + * @template T + * @typedef {new (...args: any) => T} AnyConstructor + */ /** * @template {Attribute} T * @typedef {AnyConstructor & EntityConstructor | StringConstructor | NumberConstructor | BigIntConstructor -* | BooleanConstructor | ArrayConstructor | MirroredEntityConstructor} AttributeConstructor -*/ + * | BooleanConstructor | ArrayConstructor | MirroredEntityConstructor} AttributeConstructor + */ /** * @typedef {[Number, Number]} Coordinates * @typedef {IEntity | String | Number | BigInt | Boolean | Array} TerminalAttribute @@ -56,10 +56,10 @@ /** * @template T * @typedef {T extends [infer A] ? DescribedType -* : T extends [infer A, ...infer B] ? (DescribedType | DescribedTypesFromArray) -* : any -* } DescribedTypesFromArray -**/ + * : T extends [infer A, ...infer B] ? (DescribedType | DescribedTypesFromArray) + * : any + * } DescribedTypesFromArray + **/ /** * @template T * @typedef {T extends AnyConstructor @@ -128,7 +128,6 @@ * @typedef {import("./js/element/PinElement.js").default} PinElement * @typedef {import("./js/element/SelectorElement.js").default} SelectorElement * @typedef {import("./js/element/WindowElement.js").default} WindowElement - * @typedef {import("./js/entity/Base64ObjectsEncoded.js").default} Base64ObjectsEncoded * @typedef {import("./js/entity/ByteEntity.js").default} ByteEntity * @typedef {import("./js/entity/ColorChannelEntity.js").default} ColorChannelEntity * @typedef {import("./js/entity/ComputedType.js").default} ComputedType diff --git a/vscode/package.json b/vscode/package.json new file mode 100644 index 00000000..320d57af --- /dev/null +++ b/vscode/package.json @@ -0,0 +1,32 @@ +{ + "name": "ueblueprint-vscode", + "description": "UE's Blueprint visualisation library - VS Code extension", + "version": "0.0.1", + "type": "module", + "publisher": "barsdeveloper", + "engines": { + "vscode": "^1.74.0" + }, + "activationEvents": [], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "catCoding.start", + "title": "Start new cat coding session", + "category": "Cat Coding" + } + ] + }, + "scripts": { + "vscode:prepublish": "tsc -p ./", + "compile": "tsc -watch -p ./", + "postinstall": "node ./node_modules/vscode/bin/install" + }, + "dependencies": { + "vscode": "*" + }, + "devDependencies": { + "@types/node": "^9.4.6" + } +}