decue 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/decue.min.js CHANGED
@@ -1 +1 @@
1
- var decue=function(){const d=document.currentScript.getAttribute("shadow")||"none";if(!["open","closed","none"].includes(d)){throw`Invalid default shadow DOM mode "${d}". Must be one of "open", "closed" or "none".`}const a=/{([a-zA-Z_0-9]+)((?:\|[.]?[a-zA-Z_0-9]+)*)}/g;const c=t=>!(t==="shadow"||t.startsWith("decue-"));const u=t=>{var e=t;while(e&&e.parentNode){if(e.parentNode===document){return true}else if(e.parentNode instanceof ShadowRoot){e=e.parentNode.host}else{e=e.parentNode}}return false};const n=o=>(t,e)=>{const s=e.startsWith(".")?e.substring(1):undefined;const i=window[e];if(!s&&!i){throw`Global function "${e}" not found. Make sure to include it (not deferred) before this element is created.`}return s?typeof t[s]==="function"?t[s]():t[s]:i.apply(o,[t])};const r=(i,o,t)=>t.replaceAll(a,(t,e,s)=>o.hasAttribute(e)?s.split("|").slice(1).reduce(n(i),o.getAttribute(e)):t);const h=(t,[e,s,i])=>{const o=r(e,t,s);if(e instanceof Text){if(o!==e.data){e.data=o}}else if(e instanceof HTMLElement){if(o!==e.getAttribute(i)){e.setAttribute(i,o)}}};const f=(t,s)=>{const e=document.createTreeWalker(t,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT);while(e.nextNode()){const i=e.currentNode;if(i instanceof Text){[...i.data.matchAll(a)].forEach(t=>s(i,undefined,t[1]))}else if(i instanceof HTMLElement){i.getAttributeNames().forEach(e=>[...i.getAttribute(e).matchAll(a)].forEach(t=>s(i,e,t[1])))}}};const b=(i,t)=>{t.querySelectorAll("slot[name]").forEach(t=>{const e=i.querySelectorAll(`:scope > [slot="${t.getAttribute("name")}"]`);const s=e.length>0?e:t.children;t.replaceWith.apply(t,s)});const e=t.querySelector("slot:not([name])");if(e){const s=[...i.childNodes].filter(t=>t.nodeType!==Node.ELEMENT_NODE||!t.hasAttribute("slot"));const o=s.length>0?s:e.children;e.replaceWith.apply(e,o)}};const i=(o,a,n,t,e)=>{const r=t=>o?console.log(a+": "+t):undefined;var l=e;if(n){f(n.content,(t,e,s)=>l.push(s))}l=[...new Set(l.filter(c))];window.customElements.define(a,class extends HTMLElement{static formAssociated=t;static observedAttributes=l;constructor(){super();this._template=n||[...document.getElementsByTagName("template")].find(t=>t.getAttribute("decue")===a);if(!this._template){throw`Template for "${a}" not found. Make sure it comes in the DOM before any corresponding custom element.`}if(!t&&this._template.hasAttribute("formAssociated")){throw`Cannot declare a predefined custom element "${a}" as formAssociated. Move it from elements="..." to formAssociated="...".`}const i=this;this.getAttributeNames().filter(t=>t.startsWith("decue-on:")).map(t=>t.substring("decue-on:".length)).forEach(t=>{const e=i.getAttribute("decue-on:"+t);const s=window[e];if(!s){throw`Global handler function "${e}" for event "${t}" not found.`}r("Registering event listener for event: "+t);i.addEventListener(t,s)});if(t){const o=this.attachInternals();var s=this.getAttribute("value");r("Making form-associated with value: "+s);Object.defineProperties(this,{internals:{value:o,writable:false},value:{get:()=>s,set:t=>{s=t;o.setFormValue(s);i.checkValidity()}},name:{get:()=>this.getAttribute("name")},form:{get:()=>o.form},labels:{get:()=>o.labels},validity:{get:()=>o.validity},validationMessage:{get:()=>o.validationMessage},willValidate:{get:()=>o.willValidate},setFormValue:{value:(t,e)=>o.setFormValue(s=t,e),writable:false},setValidity:{value:o.setValidity.bind(o),writable:false},checkValidity:{value:()=>{m(i,"checkvalidity");return o.checkValidity()},writable:false},reportValidity:{value:o.reportValidity.bind(o),writable:false}});this.value=s;if(!this.hasAttribute("tabindex")){this.tabIndex=0}}}connectedCallback(){this._shadow=this.getAttribute("shadow")||this._template.getAttribute("shadow")||d;if(!["open","closed","none"].includes(this._shadow)){throw`Invalid shadow DOM mode "${this._shadow}" for element "${a}". Must be one of "open", "closed" or "none".`}const t=this._shadow==="none"?this:this.attachShadow({mode:this._shadow,delegatesFocus:true});const i=this._template.content.cloneNode(true);const e=()=>{r("Finalizing...");if(this._shadow==="none"){b(t,i);r("Slots initialized")}this._boundNodes=[];[i,this].forEach(t=>f(t,(t,e,s)=>this._boundNodes.push(t instanceof HTMLElement?[t,t.getAttribute(e),e]:[t,t.data,undefined])));const e=this;const s=this.getAttributeNames().filter(c).filter(t=>!l.includes(t));if(s.length>0&&this._boundNodes.length>0){new MutationObserver(t=>t.forEach(t=>{if(s.includes(t.attributeName)){e.attributeChangedCallback(t.attributeName,t.oldValue,t.target.getAttribute(t.attributeName))}})).observe(this,{attributes:true});this._decueMutationObserved=true;if(o){r("Observing attributes with MutationObserver: "+s.join(" "));this.setAttribute("data-decue-mutation-observed-attributes",s.join(" "))}}if(o&&l.length>0){r("Observing attributes with observedAttributes: "+l.join(" "));this.setAttribute("data-decue-observed-attributes",l.join(" "))}t.append(i);this._boundNodes.forEach(t=>h(e,t));m(this,"connect")};if(n){e()}else{const s=new MutationObserver(()=>{s.disconnect();e()});s.observe(this.parentElement,{childList:true})}}attributeChangedCallback(t,e,s){if(e!==s){if(t==="value"&&this.value!==s){this.value=s}if(this._boundNodes){const i=this._boundNodes.filter(([t,e])=>u(t));if(i.length!==this._boundNodes.length){this._boundNodes=i}const o=this;this._boundNodes.forEach(t=>h(o,t))}m(this,"attributechange",{name:t,oldValue:e,newValue:s})}}disconnectedCallback(){m(this,"disconnect")}adoptedCallback(){m(this,"adopt")}formAssociatedCallback(t){m(this,"formassociate",{form:t})}formDisabledCallback(t){m(this,"formdisable",{disabled:t})}formResetCallback(){m(this,"formreset")}formStateRestoreCallback(t,e){m(this,"formstaterestore",{state:t,mode:e})}})};const m=(t,e,s)=>{t.dispatchEvent(new CustomEvent(e,{detail:s||{},bubbles:true}))};const o=document.currentScript.hasAttribute("debug");[{attr:"elements",formAssociated:false},{attr:"form-associated",formAssociated:true}].forEach(({attr:t,formAssociated:s})=>{if(document.currentScript.hasAttribute(t)){document.currentScript.getAttribute(t).split(/\s+/).map(t=>t.split(/\[|]/)).forEach(([t,e])=>i(o,t,undefined,s,e?e.split(","):[]))}});const e=t=>{const e=t.getAttribute("decue");if(e&&!window.customElements.get(e)){i(o,e,t,t.hasAttribute("form-associated"),[])}};window.addEventListener("DOMContentLoaded",()=>{[...document.getElementsByTagName("template")].forEach(e);[...document.getElementsByTagName("object")].filter(t=>t.getAttribute("type")==="text/html").forEach(t=>{t.addEventListener("load",()=>[...t.contentDocument.getElementsByTagName("template")].forEach(e));[...t.contentDocument.getElementsByTagName("template")].forEach(e)})});return{processTemplate:e,defineElement:i}}();
1
+ var decue=function(){var r=("currentScript"in document&&document["currentScript"]?document["currentScript"].hasAttribute("debug"):false)||false;var n=("currentScript"in document&&document["currentScript"]?document["currentScript"].getAttribute("shadow"):undefined)||"none";if(!["open","closed","none"].includes(n)){throw new Error('Invalid default shadow DOM mode "'+n+'". Must be one of "open", "closed" or "none".')}var s=/{(\.?[a-zA-Z_0-9]+)((?:[.|][a-zA-Z_0-9]+)*)}/g;var l=function(e){return!(e==="shadow"||e.startsWith("decue-"))};var a=function(o){return function(e,t){var n=t.startsWith(".")?t.substring(1):undefined;var r=n&&typeof e[n]==="function";var i=c[t];if(!n&&!i){throw new Error('Global function "'+t+'" not found. Make sure to include it (not deferred) before this element is created.')}return n?r?e[n]():e[n]:i.apply(o,[e])}};var u=function(i,o,e){return e.replaceAll(s,function(e,t,n){var r=(t+n).replaceAll(/([^|])\./g,"$1|.").split("|");return o.hasAttribute(t)?r.slice(1).reduce(a(i),o.getAttribute(t)):t.startsWith(".")?r.reduce(a(i),o):e})};var d=function(e,t){var n=t[0];var r=t[1];var i=t[2];var o=u(n,e,r);if(n.nodeType===Node.TEXT_NODE){var a=n;if(o!==a.data){a.data=o}}else if(n.nodeType===Node.ELEMENT_NODE){var s=n;if(o!==s.getAttribute(i)){s.setAttribute(i,o)}}};var f=function(e,r){var t=document.createTreeWalker(e,4|1);while(t.nextNode()){var n=t.currentNode;if(n.nodeType===Node.TEXT_NODE){var i=n;var o;while((o=s.exec(i.data))!==null){r(i,null,o[1])}}else if(n.nodeType===Node.ELEMENT_NODE){var a=n;a.getAttributeNames().forEach(function(e){var t=a.getAttribute(e);var n;while((n=s.exec(t))!==null){r(a,e,n[1])}})}}};var h=function(r,e){e.querySelectorAll("slot[name]").forEach(function(e){var t=r.querySelectorAll(':scope > [slot="'+e.getAttribute("name")+'"]');var n=t.length>0?t:e.children;e.replaceWith.apply(e,n)});var t=e.querySelector("slot:not([name])");if(t){var n=Array.prototype.slice.call(r.childNodes).filter(function(e){return e.nodeType!==Node.ELEMENT_NODE||!e.hasAttribute("slot")});var i=n.length>0?n:t.children;t.replaceWith.apply(t,i)}};var b=function(r){return r.getAttributeNames().filter(function(e){return e.startsWith("decue-on:")}).map(function(e){return e.substring("decue-on:".length)}).map(function(e){var t=r.getAttribute("decue-on:"+e);var n=globalThis[t];if(!n){throw'Global handler function "'+t+'" for event "'+e+'" not found.'}r.addEventListener(e,n);return[e,n]})};var v=function(t,e){if(e){var n=e.filter(function(e){return e[0].ownerDocument!==undefined});if(n.length!==e.length){e=n}e.forEach(function(e){d(t,e)})}return e};var i=function(i,o,a,s,e){var u=function(){if(i&&console){console.log.apply(null,[o].concat(Array.prototype.slice.call(arguments)))}};var c=e;if(a){f(a.content,function(e,t,n){c.push(n)})}c=c.filter(l).filter(function(e,t,n){return n.indexOf(e)===t});globalThis.customElements.define(o,class extends HTMLElement{static get formAssociated(){return s}static get observedAttributes(){return c}constructor(){super();this._template=a||Array.prototype.slice.call(document.getElementsByTagName("template")).filter(function(e){return e.getAttribute("decue")===o}).concat([undefined])[0];if(!this._template){throw`Template for "${o}" not found. Make sure it comes in the DOM before any corresponding custom element.`}if(!s&&this._template.hasAttribute("formAssociated")){throw`Cannot declare a predefined custom element "${o}" as formAssociated. Move it from elements="..." to formAssociated="...".`}if(s){var n=this.attachInternals();var r;var i=this;Object.defineProperties(this,{internals:{value:n,writable:false},value:{get:function(){return r},set:function(e){r=e;n.setFormValue(r);i.checkValidity();i._boundNodes=v(i,i._boundNodes)}},name:{get:function(){return this.getAttribute("name")}},form:{get:function(){return n.form}},labels:{get:function(){return n.labels}},validity:{get:function(){return n.validity}},validationMessage:{get:function(){return n.validationMessage}},willValidate:{get:function(){return n.willValidate}},setFormValue:{value:function(e,t){n.setFormValue(r=e,t);i._boundNodes=v(i,i._boundNodes)},writable:false},setValidity:{value:n.setValidity.bind(n),writable:false},checkValidity:{value:function(){m(i,"checkvalidity");return n.checkValidity()},writable:false},reportValidity:{value:n.reportValidity.bind(n),writable:false}})}}connectedCallback(){this._shadow=this.getAttribute("shadow")||this._template.getAttribute("shadow")||n;if(!["open","closed","none"].includes(this._shadow)){throw`Invalid shadow DOM mode "${this._shadow}" for element "${o}". Must be one of "open", "closed" or "none".`}if(!this._shadowRoot){this._shadowRoot=this._shadow==="none"?this:this.attachShadow({mode:this._shadow,delegatesFocus:true});if(s){this.value=this.getAttribute("value");u("Making form-associated with value",this.value);if(!this.hasAttribute("tabindex")){this.tabIndex=0}}this._eventListeners=b(this);var r=this;var e=function(){u("Finalizing...");var e=r._template.content.cloneNode(true);if(r._shadow==="none"){h(r._shadowRoot,e);u("Slots initialized")}r._boundNodes=[];[e,r].forEach(function(e){f(e,function(e,t){r._boundNodes.push(e.nodeType==Node.ELEMENT_NODE?[e,e.getAttribute(t),t]:[e,e.data,undefined])})});var n=r.getAttributeNames().filter(l).filter(function(e){return!c.includes(e)});if(n.length>0&&r._boundNodes.length>0){if(typeof MutationObserver==="undefined"){console.info(`Decue: Element "${o}" has attributes (${n.join(" ")}) which are not declared in observedAttributes, but no MutationObserver is available. These attributes won't be observed.`)}else{r._observer=new MutationObserver(function(e){e.forEach(function(e){if(n.includes(e.attributeName)){var t=e.target.getAttribute(e.attributeName);u("Attribute observer hit",e.attributeName,t,"(unobserved attributes: ",n,")");r.attributeChangedCallback(e.attributeName,e.oldValue,t)}})});r._observer.observe(r,{attributeOldValue:true});r._decueMutationObserved=true;if(i){u("Observing attributes with MutationObserver",n);r.setAttribute("data-decue-mutation-observed-attributes",n.join(" "))}}}if(i&&c.length>0){u("Observing attributes with observedAttributes",c);r.setAttribute("data-decue-observed-attributes",c.join(" "))}r._shadowRoot.append(e);r._boundNodes.forEach(function(e){d(r,e)});m(r,"connect")};if(a||r.ownerDocument.readyState!="loading"){e()}else if(typeof MutationObserver==="undefined"){throw new Error(`No MutationObserver available, cannot use predefined elements`)}else{var t=new MutationObserver(function(){t.disconnect();e()});t.observe(r.parentElement,{childList:true})}}}attributeChangedCallback(e,t,n){if(t!==n){if(s&&e==="value"&&this.value!==n){this.value=n}this._boundNodes=v(this,this._boundNodes);m(this,"attributechange",{name:e,oldValue:t,newValue:n})}}disconnectedCallback(){m(this,"disconnect");if(this._observer){u("Disconnecting MutationObserver");this._observer.disconnect();delete this._observer}if(this._eventListeners){u("Removing event listeners");this._eventListeners.forEach(function(e){this.removeEventListener(e[0],e[1])},this);delete this._eventListeners}}adoptedCallback(){m(this,"adopt")}formAssociatedCallback(e){m(this,"formassociate",{form:e})}formDisabledCallback(e){m(this,"formdisable",{disabled:e})}formResetCallback(){m(this,"formreset")}formStateRestoreCallback(e,t){m(this,"formstaterestore",{state:e,mode:t})}})};var m=function(e,t,n){if(typeof CustomEvent!=="undefined"){e.dispatchEvent(new CustomEvent(t,{detail:n||{},bubbles:true}))}else{e.dispatchEvent(new Event(t,{detail:n||{},bubbles:true}))}};[{attr:"elements",formAssociated:false},{attr:"form-associated",formAssociated:true}].forEach(function(n){if(document.currentScript.hasAttribute(n.attr)){document.currentScript.getAttribute(n.attr).split(/\s+/).map(function(e){return e.split(/\[|]/)}).forEach(function([e,t]){i(r,e,null,n.formAssociated,t?t.split(","):[])})}});var c={};if(document.currentScript&&document.currentScript.hasAttribute("functions")){document.currentScript.getAttribute("functions").split(/\s+/).forEach(function(n){var e=n;var t=n;var r=n.indexOf(":");if(r>=0){e=n.substring(0,r);t=n.substring(r+1)}var i=t.split(".").reduce(function(e,t){if(e&&t in e){return e[t]}else{throw`Cannot find namespace "${t}" while resolving function "${n}".`}},globalThis);if(typeof i!=="function"){throw`Function "${n}" not found. Make sure to include it (not deferred) before Decue script.`}c[e]=i})}var t=function(e){var t=e.getAttribute("decue");if(t&&!globalThis.customElements.get(t)){i(r,t,e,e.hasAttribute("form-associated"),[])}};var e=function(){[...document.getElementsByTagName("template")].forEach(t);[...document.getElementsByTagName("object")].filter(function(e){return e.getAttribute("type")==="text/html"}).forEach(function(e){e.addEventListener("load",function(){if(e.contentDocument){[...e.contentDocument.getElementsByTagName("template")].forEach(t)}});if(e.contentDocument){[...e.contentDocument.getElementsByTagName("template")].forEach(t)}})};if(typeof globalThis!=="undefined"){globalThis.addEventListener("DOMContentLoaded",e)}else if(typeof window!=="undefined"){window.addEventListener("DOMContentLoaded",e)}return{processTemplate:t,defineElement:i,registeredFunctions:c}}();if(typeof globalThis!=="undefined"){globalThis.decue=decue}else if(typeof window!=="undefined"){window.decue=decue}
@@ -0,0 +1,53 @@
1
+ import js from '@eslint/js';
2
+ import compat from 'eslint-plugin-compat';
3
+
4
+ export default [
5
+ js.configs.recommended,
6
+ {
7
+ ignores: ['coverage/', 'dist/', 'node_modules/', '**/*.min.js']
8
+ },
9
+ {
10
+ files: ['src/**/*.js'],
11
+ languageOptions: {
12
+ ecmaVersion: 2015,
13
+ sourceType: 'script',
14
+ globals: {
15
+ // Browser globals
16
+ globalThis: 'readonly',
17
+ window: 'readonly',
18
+ document: 'readonly',
19
+ console: 'readonly',
20
+ setTimeout: 'readonly',
21
+ clearTimeout: 'readonly',
22
+ performance: 'readonly',
23
+ customElements: 'readonly',
24
+ HTMLElement: 'readonly',
25
+ HTMLTableElement: 'readonly',
26
+ HTMLTableRowElement: 'readonly',
27
+ HTMLTableCellElement: 'readonly',
28
+ Element: 'readonly',
29
+ Node: 'readonly',
30
+ NodeList: 'readonly',
31
+ Event: 'readonly',
32
+ CustomEvent: 'readonly',
33
+ // Test globals
34
+ describe: 'readonly',
35
+ it: 'readonly',
36
+ beforeEach: 'readonly',
37
+ afterEach: 'readonly',
38
+ expect: 'readonly',
39
+ sinon: 'readonly',
40
+ assert: 'readonly'
41
+ }
42
+ },
43
+ plugins: {
44
+ compat
45
+ },
46
+ rules: {
47
+ 'compat/compat': 'error'
48
+ },
49
+ settings: {
50
+ polyfills: []
51
+ }
52
+ }
53
+ ];
package/examples.html CHANGED
@@ -2,8 +2,13 @@
2
2
  <html>
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'nonce-a' 'nonce-b' 'nonce-c' 'nonce-d'" />
5
6
  <title>DeCuE examples</title>
6
- <style>
7
+ <style nonce="a">
8
+ object {
9
+ position:absolute;
10
+ left: -99999px;
11
+ }
7
12
  main {
8
13
  display: flex;
9
14
  }
@@ -20,12 +25,12 @@
20
25
  outline: 2px solid blue;
21
26
  }
22
27
  </style>
23
- <script>
28
+ <script nonce="b">
24
29
  function toLower(str) {
25
30
  return str.toLowerCase();
26
31
  }
27
32
  </script>
28
- <script>
33
+ <script nonce="c">
29
34
  function validateme(ev) {
30
35
  const target = ev.target;
31
36
  if (target.value.indexOf('hello') < 0) {
@@ -45,9 +50,26 @@
45
50
  document.querySelector('[data-modifyvalues]').addEventListener('click', () => {
46
51
  document.querySelectorAll('[value]').forEach(x => {x.value = 'hello ' + x.value; x.dispatchEvent(new Event('change'));});
47
52
  });
53
+
54
+ setTimeout(() => {
55
+ document.querySelectorAll('.remove-then-append nested-element-with-attribute').forEach(el => {
56
+ const parent = el.parentNode;
57
+ parent.removeChild(el);
58
+ parent.appendChild(el);
59
+ });
60
+
61
+
62
+ document.querySelectorAll('.move nested-element-with-attribute').forEach(el => {
63
+ if (el.parentNode.moveBefore) {
64
+ el.parentNode.moveBefore(el, null);
65
+ } else {
66
+ el.parentNode.querySelector('.text').textContent = "(browser doesn't support moveBefore)";
67
+ }
68
+ });
69
+ }, 1000);
48
70
  });
49
71
  </script>
50
- <script src="src/decue.js" debug elements="predefined-element predefined-element-with-attributes predefined-element-with-slot"></script>
72
+ <script src="src/decue.js" debug elements="predefined-element predefined-element-with-attributes predefined-element-with-slot" functions="toLower"></script>
51
73
  </head>
52
74
  <body>
53
75
 
@@ -67,7 +89,7 @@
67
89
  </template>
68
90
  <template decue="nesting-element">
69
91
  <nested-element title="a nesting element"></nested-element>
70
- <nested-element-with-attribute greetings="World!" title="a nesting element with an attribute"></nested-element-with-attribute>
92
+ <nested-element-with-attribute greetings="{greetings}" title="a nesting element with an attribute"></nested-element-with-attribute>
71
93
  </template>
72
94
 
73
95
  <template decue="default-slotted-element">
@@ -88,11 +110,11 @@
88
110
  <template decue="element-with-function">
89
111
  <div data-function="{greetings|toLower}" title="an element with a function {greetings|toLower}">Hello {greetings|toLower}</div>
90
112
  </template>
91
- <template decue="element-with-method" title="an element with a method {greetings|.toUpperCase}">
92
- <div data-method="{greetings|.toUpperCase}">Hello {greetings|.toUpperCase}</div>
113
+ <template decue="element-with-method" title="an element with a method {greetings.toUpperCase}">
114
+ <div data-method="{greetings.toUpperCase}">Hello {greetings.toUpperCase}</div>
93
115
  </template>
94
116
  <template decue="element-with-pipe">
95
- <div data-piped="{greetings|.toUpperCase|toLower}" title="an element with a piping function {greetings|.toUpperCase|toLower}">Hello {greetings|.toUpperCase|toLower}</div>
117
+ <div data-piped="{greetings.toUpperCase|toLower}" title="an element with a piping function {greetings.toUpperCase|toLower}">Hello {greetings.toUpperCase|toLower}</div>
96
118
  </template>
97
119
 
98
120
  <template decue="predefined-element">
@@ -148,7 +170,7 @@
148
170
  <td>
149
171
  <fieldset>
150
172
  <legend>Nesting</legend>
151
- <nesting-element></nesting-element>
173
+ <nesting-element greetings="World!"></nesting-element>
152
174
  </fieldset>
153
175
 
154
176
  <fieldset>
@@ -190,7 +212,7 @@
190
212
  <td>
191
213
  <fieldset>
192
214
  <legend>Nesting</legend>
193
- <nesting-element shadow="open"></nesting-element>
215
+ <nesting-element greetings="World!" shadow="open"></nesting-element>
194
216
  </fieldset>
195
217
 
196
218
  <fieldset>
@@ -231,7 +253,7 @@
231
253
  <td>
232
254
  <fieldset>
233
255
  <legend>Nesting</legend>
234
- <nesting-element shadow="closed"></nesting-element>
256
+ <nesting-element greetings="World!" shadow="closed"></nesting-element>
235
257
  </fieldset>
236
258
 
237
259
  <fieldset>
@@ -270,9 +292,57 @@
270
292
  </fieldset>
271
293
  </td>
272
294
  </tr>
295
+
296
+ <tr>
297
+ <td>
298
+ <fieldset class="remove-then-append">
299
+ <legend>remove-then-append</legend>
300
+ <nested-element-with-attribute greetings="World!"></nested-element-with-attribute>
301
+ <div class="text">text originally in the end</div>
302
+ </fieldset>
303
+ </td>
304
+ <td>
305
+ <fieldset class="remove-then-append">
306
+ <legend>remove-then-append</legend>
307
+ <nested-element-with-attribute greetings="World!" shadow="open"></nested-element-with-attribute>
308
+ <div class="text">text originally in the end</div>
309
+ </fieldset>
310
+ </td>
311
+ <td>
312
+ <fieldset class="remove-then-append">
313
+ <legend>remove-then-append</legend>
314
+ <nested-element-with-attribute greetings="World!" shadow="closed"></nested-element-with-attribute>
315
+ <div class="text">text originally in the end</div>
316
+ </fieldset>
317
+ </td>
318
+ </tr>
319
+
320
+ <tr>
321
+ <td>
322
+ <fieldset class="move">
323
+ <legend>move</legend>
324
+ <nested-element-with-attribute greetings="World!"></nested-element-with-attribute>
325
+ <div class="text">text originally in the end</div>
326
+ </fieldset>
327
+ </td>
328
+ <td>
329
+ <fieldset class="move">
330
+ <legend>move</legend>
331
+ <nested-element-with-attribute greetings="World!" shadow="open"></nested-element-with-attribute>
332
+ <div class="text">text originally in the end</div>
333
+ </fieldset>
334
+ </td>
335
+ <td>
336
+ <fieldset class="move">
337
+ <legend>move</legend>
338
+ <nested-element-with-attribute greetings="World!" shadow="closed"></nested-element-with-attribute>
339
+ <div class="text">text originally in the end</div>
340
+ </fieldset>
341
+ </td>
342
+ </tr>
273
343
  </table>
274
344
  </main>
275
345
 
276
- <object data="external.html" type="text/html" style="position:absolute; left: -99999px"></object>
346
+ <object nonce="d" data="external.html" type="text/html"></object>
277
347
  </body>
278
348
  </html>
package/external.html CHANGED
@@ -1,3 +1,4 @@
1
+ <!DOCTYPE html>
1
2
  <template decue="external-element">
2
3
  <slot><div title="an external element with default slot">default slot: Hello {greetings}</div></slot>
3
4
  </template>
package/npm.sh CHANGED
@@ -1,4 +1,4 @@
1
1
  #! /usr/bin/env nix-shell
2
- #! nix-shell -I channel:nixos-22.11-small -i bash -p nodejs-16_x
2
+ #! nix-shell -I channel:nixos-25.05-small -i bash -p nodejs
3
3
 
4
4
  npm $*
package/package.json CHANGED
@@ -1,25 +1,38 @@
1
1
  {
2
2
  "name": "decue",
3
- "version": "1.0.0",
4
- "description": "",
3
+ "version": "1.1.0",
4
+ "description": "Declarative Custom Elements. Create simple web components with just HTML, parameterized with slots and attributes. With or without shadow-dom.",
5
5
  "author": "Jyri-Matti Lähteenmäki <jyri-matti@lahteenmaki.net>",
6
- "keywords": [],
6
+ "keywords": [
7
+ "html",
8
+ "web-component",
9
+ "custom-element"
10
+ ],
7
11
  "license": "MIT",
12
+ "browserslist": [
13
+ "since 2010"
14
+ ],
8
15
  "scripts": {
9
- "test": "mocha-chrome test/index.html",
10
- "dist": "cp -r src/* dist/ && npm run-script uglify",
16
+ "test": "npx playwright install && web-test-runner",
17
+ "test:watch": "web-test-runner --watch",
18
+ "lint": "eslint src/",
19
+ "lint:fix": "eslint src/ --fix",
20
+ "compat": "eslint src/ --rule 'compat/compat: error'",
21
+ "dist": "npm test && npm run lint && npm run compat && cp -r src/* dist/ && npm run-script uglify",
11
22
  "uglify": "uglifyjs -m eval -o dist/decue.min.js dist/decue.js"
12
23
  },
13
24
  "repository": {
14
25
  "type": "git",
15
- "url": "git+https://codeberg.org/jyri-matti/decue.git"
26
+ "url": "https://codeberg.org/jyri-matti/decue.git"
16
27
  },
17
28
  "devDependencies": {
18
- "chai": "^4.3.6",
19
- "mocha": "^8.3.2",
20
- "sinon": "^9.2.4",
21
- "mocha-chrome": "^2.2.0",
22
- "mocha-webdriver-runner": "^0.6.4",
29
+ "@esm-bundle/chai": "^4.3.4",
30
+ "@web/test-runner": "^0.18.0",
31
+ "@web/test-runner-playwright": "^0.11.0",
32
+ "browserslist": "^4.28.1",
33
+ "eslint": "^9.39.2",
34
+ "eslint-plugin-compat": "^6.1.0",
35
+ "sinon": "^17.0.0",
23
36
  "uglify-js": "^3.15.0"
24
37
  }
25
38
  }