@epa-wg/custom-element 0.0.11 → 0.0.12
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/.idea/inspectionProfiles/Project_Default.xml +22 -7
- package/.idea/misc.xml +4 -5
- package/.vs/VSWorkspaceState.json +8 -0
- package/.vs/custom-element/FileContentIndex/1487e471-3751-47bc-a499-d78eda924eda.vsidx +0 -0
- package/.vs/custom-element/v17/.wsuo +0 -0
- package/.vs/slnx.sqlite +0 -0
- package/README.md +26 -19
- package/custom-element.js +206 -60
- package/demo/a.html +25 -11
- package/demo/confused.svg +36 -36
- package/demo/dom-merge.html +121 -0
- package/demo/embed-1.html +2 -2
- package/demo/external-template.html +9 -7
- package/demo/html-template.html +125 -12
- package/demo/html-template.xhtml +44 -44
- package/demo/html-template.xml +44 -44
- package/demo/http-request.html +44 -57
- package/demo/local-storage.html +23 -18
- package/demo/location-element.html +59 -58
- package/demo/s.xml +1 -0
- package/demo/s.xslt +159 -0
- package/demo/table.xml +24 -24
- package/demo/table.xsl +292 -292
- package/demo/template.xsl +45 -45
- package/demo/tree.xml +24 -24
- package/demo/tree.xsl +32 -32
- package/demo/xhtml-template.xhtml +44 -44
- package/demo/z.html +42 -0
- package/demo/z.xml +60 -0
- package/http-request.js +28 -37
- package/index.html +21 -19
- package/input-text.js +17 -0
- package/local-storage.js +28 -35
- package/location-element.js +15 -16
- package/package.json +1 -1
- package/0/a.html +0 -19
- package/0/a.xml +0 -10
- package/0/a.xsl +0 -66
- package/0/a1.xsl +0 -38
- package/0/ab.xsl +0 -23
- package/0/az.xml +0 -30
- package/0/b.html +0 -90
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
<component name="InspectionProjectProfileManager">
|
|
2
2
|
<profile version="1.0">
|
|
3
3
|
<option name="myName" value="Project Default" />
|
|
4
|
+
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
|
5
|
+
<option name="myValues">
|
|
6
|
+
<value>
|
|
7
|
+
<list size="1">
|
|
8
|
+
<item index="0" class="java.lang.String" itemvalue="slice" />
|
|
9
|
+
</list>
|
|
10
|
+
</value>
|
|
11
|
+
</option>
|
|
12
|
+
<option name="myCustomValuesEnabled" value="true" />
|
|
13
|
+
</inspection_tool>
|
|
4
14
|
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
|
5
15
|
<option name="myValues">
|
|
6
16
|
<value>
|
|
7
|
-
<list size="
|
|
17
|
+
<list size="17">
|
|
8
18
|
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
|
9
19
|
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
|
10
20
|
<item index="2" class="java.lang.String" itemvalue="comment" />
|
|
11
21
|
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
|
12
22
|
<item index="4" class="java.lang.String" itemvalue="embed" />
|
|
13
23
|
<item index="5" class="java.lang.String" itemvalue="script" />
|
|
14
|
-
<item index="6" class="java.lang.String" itemvalue="
|
|
15
|
-
<item index="7" class="java.lang.String" itemvalue="
|
|
16
|
-
<item index="8" class="java.lang.String" itemvalue="
|
|
17
|
-
<item index="9" class="java.lang.String" itemvalue="dce-
|
|
18
|
-
<item index="10" class="java.lang.String" itemvalue="
|
|
19
|
-
<item index="11" class="java.lang.String" itemvalue="
|
|
24
|
+
<item index="6" class="java.lang.String" itemvalue="html-demo-element" />
|
|
25
|
+
<item index="7" class="java.lang.String" itemvalue="custom-element" />
|
|
26
|
+
<item index="8" class="java.lang.String" itemvalue="local-storage" />
|
|
27
|
+
<item index="9" class="java.lang.String" itemvalue="dce-1" />
|
|
28
|
+
<item index="10" class="java.lang.String" itemvalue="xhtml:table" />
|
|
29
|
+
<item index="11" class="java.lang.String" itemvalue="xhtml:tbody" />
|
|
30
|
+
<item index="12" class="java.lang.String" itemvalue="xhtml:tr" />
|
|
31
|
+
<item index="13" class="java.lang.String" itemvalue="xhtml:th" />
|
|
32
|
+
<item index="14" class="java.lang.String" itemvalue="xhtml:td" />
|
|
33
|
+
<item index="15" class="java.lang.String" itemvalue="xhtml:tfoot" />
|
|
34
|
+
<item index="16" class="java.lang.String" itemvalue="dce-2" />
|
|
20
35
|
</list>
|
|
21
36
|
</value>
|
|
22
37
|
</option>
|
package/.idea/misc.xml
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
</component>
|
|
1
|
+
<project version="4">
|
|
2
|
+
<component name="ProjectRootManager">
|
|
3
|
+
<output url="file://$PROJECT_DIR$/out" />
|
|
4
|
+
</component>
|
|
6
5
|
</project>
|
|
Binary file
|
|
Binary file
|
package/.vs/slnx.sqlite
ADDED
|
Binary file
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# custom-element
|
|
2
2
|
`Declarative Custom Element` is a part of pure `Declarative Web Application` stack. A proof of concept as a part of
|
|
3
|
-
[WCCG in Declarative custom elements](https://github.com/w3c/webcomponents-cg/issues/32#issuecomment-1321037301)
|
|
3
|
+
[WCCG in Declarative custom elements](https://github.com/w3c/webcomponents-cg/issues/32#issuecomment-1321037301) and [Declarative Web Application](https://github.com/EPA-WG/dwa#readme)
|
|
4
4
|
discussion. The functionality of DCE and its data access does not require programming using JavaScript.
|
|
5
5
|
|
|
6
6
|
It allows to define custom HTML tag with template filled from slots, attributes and data `slice` as of now from
|
|
@@ -32,13 +32,13 @@ yarn add @epa-wg/custom-element
|
|
|
32
32
|
## [Live demo 🔗][demo-url]
|
|
33
33
|
```html
|
|
34
34
|
<custom-element tag="pokemon-tile" hidden>
|
|
35
|
-
<h3
|
|
35
|
+
<h3>{title}</h3> <!-- title is an attribute in instance
|
|
36
36
|
mapped into /*/attributes/title -->
|
|
37
37
|
<xsl:if test="//smile"> <!-- data-smile DCE instance attribute,
|
|
38
38
|
mapped into /*/dataset/smile
|
|
39
39
|
used in condition -->
|
|
40
40
|
<!-- data-smile DCE instance attribute, used as HTML -->
|
|
41
|
-
<div>Smile as:
|
|
41
|
+
<div>Smile as: {//smile} </div>
|
|
42
42
|
</xsl:if>
|
|
43
43
|
<!-- image would not be visible in sandbox, see live demo -->
|
|
44
44
|
<img src="https://unpkg.com/pokeapi-sprites@2.0.2/sprites/pokemon/other/dream-world/{pokemon-id}.svg"
|
|
@@ -165,21 +165,28 @@ template engine.
|
|
|
165
165
|
|
|
166
166
|
# troubleshooting
|
|
167
167
|
## HTML parser is not compatible with templates
|
|
168
|
-
On many tags like `table`, or link `a` the attempt to use XSLT operations could lead to DOM order
|
|
169
|
-
in template. In such cases the `
|
|
168
|
+
On many tags like `table`, or link `a` the attempt to use XSLT operations could lead to DOM order mismatch to given
|
|
169
|
+
in template. In such cases the `xhtml:` prefix in front of troubled tag would solve the parsing.
|
|
170
170
|
|
|
171
171
|
```html
|
|
172
172
|
<custom-element tag="dce-2" hidden>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
</
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
<local-storage key="basket" slice="basket" live type="json"></local-storage>
|
|
174
|
+
<xhtml:table xmlns:xhtml="http://www.w3.org/1999/xhtml" >
|
|
175
|
+
<xhtml:tbody>
|
|
176
|
+
<xsl:for-each select="//basket/@*">
|
|
177
|
+
<xhtml:tr>
|
|
178
|
+
<xhtml:th> {name()} </xhtml:th>
|
|
179
|
+
<xhtml:td> {.} </xhtml:td>
|
|
180
|
+
</xhtml:tr>
|
|
181
|
+
</xsl:for-each>
|
|
182
|
+
</xhtml:tbody>
|
|
183
|
+
<xhtml:tfoot>
|
|
184
|
+
<xhtml:tr>
|
|
185
|
+
<xhtml:td><slot>🤔</slot></xhtml:td>
|
|
186
|
+
<xhtml:th> {sum(//slice/basket/@*)} </xhtml:th>
|
|
187
|
+
</xhtml:tr>
|
|
188
|
+
</xhtml:tfoot>
|
|
189
|
+
</xhtml:table>
|
|
183
190
|
</custom-element>
|
|
184
191
|
```
|
|
185
192
|
See [demo source](demo/local-storage.html) for detailed sample.
|
|
@@ -206,7 +213,7 @@ run transformation under debugger.
|
|
|
206
213
|
* try to add as attribute you could observe and put the value of node name or text to identify the current location in data
|
|
207
214
|
within template
|
|
208
215
|
```xml
|
|
209
|
-
<b title="{name(*)} : {text()}">xml tag name
|
|
216
|
+
<b title="{name(*)} : {text()}">xml tag name: <xsl:value-of select='name()'/></b>
|
|
210
217
|
```
|
|
211
218
|
|
|
212
219
|
[git-url]: https://github.com/EPA-WG/custom-element
|
|
@@ -218,9 +225,9 @@ within template
|
|
|
218
225
|
[github-image]: https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg
|
|
219
226
|
[npm-image]: https://img.shields.io/npm/v/@epa-wg/custom-element.svg
|
|
220
227
|
[npm-url]: https://npmjs.org/package/@epa-wg/custom-element
|
|
221
|
-
[coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.
|
|
222
|
-
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.
|
|
223
|
-
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.
|
|
228
|
+
[coverage-image]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/coverage/coverage.svg
|
|
229
|
+
[coverage-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/coverage/lcov-report/index.html
|
|
230
|
+
[storybook-url]: https://unpkg.com/@epa-wg/custom-element-test@0.0.12/storybook-static/index.html?path=/story/welcome--introduction
|
|
224
231
|
[sandbox-url]: https://stackblitz.com/github/EPA-WG/custom-element?file=index.html
|
|
225
232
|
[webcomponents-url]: https://www.webcomponents.org/element/@epa-wg/custom-element
|
|
226
233
|
[webcomponents-img]: https://img.shields.io/badge/webcomponents.org-published-blue.svg
|
package/custom-element.js
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
const
|
|
2
|
-
,
|
|
3
|
-
,
|
|
1
|
+
const XSL_NS_URL = 'http://www.w3.org/1999/XSL/Transform'
|
|
2
|
+
, HTML_NS_URL = 'http://www.w3.org/1999/xhtml'
|
|
3
|
+
, EXSL_NS_URL = 'http://exslt.org/common'
|
|
4
|
+
, DCE_NS_URL ="urn:schemas-epa-wg:dce";
|
|
4
5
|
|
|
5
6
|
// const log = x => console.debug( new XMLSerializer().serializeToString( x ) );
|
|
6
7
|
|
|
7
|
-
const attr = (el, attr)=> el.getAttribute(attr)
|
|
8
|
+
const attr = (el, attr)=> el.getAttribute?.(attr)
|
|
9
|
+
, isText = e => e.nodeType === 3
|
|
8
10
|
, create = ( tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElement( tag ))
|
|
9
|
-
,
|
|
11
|
+
, createText = ( d, t) => (d.ownerDocument || d ).createTextNode( t )
|
|
12
|
+
, createNS = ( ns, tag, t = '' ) => ( e => ((e.innerText = t||''),e) )(document.createElementNS( ns, tag ))
|
|
13
|
+
, xslNs = x => ( x?.setAttribute('xmlns:xsl', XSL_NS_URL ), x )
|
|
14
|
+
, xslHtmlNs = x => ( x?.setAttribute('xmlns:xhtml', HTML_NS_URL ), xslNs(x) );
|
|
10
15
|
|
|
16
|
+
function
|
|
17
|
+
ASSERT(x)
|
|
18
|
+
{
|
|
19
|
+
// if(!x)
|
|
20
|
+
// debugger
|
|
21
|
+
}
|
|
11
22
|
function
|
|
12
23
|
xml2dom( xmlString )
|
|
13
24
|
{
|
|
14
|
-
return new DOMParser().parseFromString(
|
|
25
|
+
return new DOMParser().parseFromString( xmlString, "application/xml" )
|
|
15
26
|
}
|
|
16
27
|
function
|
|
17
28
|
xmlString(doc){ return new XMLSerializer().serializeToString( doc ) }
|
|
18
29
|
|
|
19
30
|
function
|
|
20
31
|
injectData( root, sectionName, arr, cb )
|
|
21
|
-
{
|
|
32
|
+
{ const create = ( tag ) => root.ownerDocument.createElement( tag );
|
|
22
33
|
const inject = ( tag, parent, s ) =>
|
|
23
|
-
{
|
|
24
|
-
parent.append( s = createNS( DCE_NS_URL, tag ) );
|
|
34
|
+
{ parent.append( s = create( tag ) );
|
|
25
35
|
return s;
|
|
26
36
|
};
|
|
27
37
|
const l = inject( sectionName, root );
|
|
@@ -71,70 +81,133 @@ Json2Xml( o, tag )
|
|
|
71
81
|
ret.push("/>");
|
|
72
82
|
return ret.join('\n');
|
|
73
83
|
}
|
|
84
|
+
export function
|
|
85
|
+
tagUid( node )
|
|
86
|
+
{ // {} to xsl:value-of
|
|
87
|
+
forEach$(node,'*',d => [...d.childNodes].filter( e=>e.nodeType === 3 ).forEach( e=>
|
|
88
|
+
{ if( e.parentNode.localName === 'style' )
|
|
89
|
+
return;
|
|
90
|
+
const m = e.data.matchAll( /{([^}]*)}/g );
|
|
91
|
+
if(m)
|
|
92
|
+
{ let l = 0
|
|
93
|
+
, txt = t => createText(e,t||'')
|
|
94
|
+
, tt = [];
|
|
95
|
+
[...m].forEach(t=>
|
|
96
|
+
{ if( t.index > l )
|
|
97
|
+
tt.push( txt( t.input.substring( l, t.index ) ))
|
|
98
|
+
const v = e.ownerDocument.createElement('xsl:value-of');
|
|
99
|
+
v.setAttribute('select', t[1] );
|
|
100
|
+
tt.push(v);
|
|
101
|
+
l = t.index+t[0].length;
|
|
102
|
+
})
|
|
103
|
+
if( l < e.data.length)
|
|
104
|
+
tt.push( txt( e.data.substring(l,e.data.length) ));
|
|
105
|
+
if( tt.length )
|
|
106
|
+
{ for( let t of tt )
|
|
107
|
+
d.insertBefore(t,e);
|
|
108
|
+
d.removeChild(e);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
74
112
|
|
|
113
|
+
if( 'all' in node ) {
|
|
114
|
+
let i= 1;
|
|
115
|
+
for( let e of node.all )
|
|
116
|
+
e.setAttribute && !e.tagName.startsWith('xsl:') && e.setAttribute('data-dce-id', '' + i++)
|
|
117
|
+
}
|
|
118
|
+
return node
|
|
119
|
+
}
|
|
75
120
|
export function
|
|
76
121
|
createXsltFromDom( templateNode, S = 'xsl:stylesheet' )
|
|
77
122
|
{
|
|
78
123
|
if( templateNode.tagName === S || templateNode.documentElement?.tagName === S )
|
|
79
|
-
return templateNode
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
124
|
+
return tagUid(templateNode)
|
|
125
|
+
const sanitizeXsl = xml2dom(`<xsl:stylesheet version="1.0" xmlns:xsl="${ XSL_NS_URL }" xmlns:xhtml="${ HTML_NS_URL }" xmlns:exsl="${EXSL_NS_URL}" exclude-result-prefixes="exsl" >
|
|
126
|
+
<xsl:output method="xml" />
|
|
127
|
+
<xsl:template match="/"><dce-root><xsl:apply-templates select="*"/></dce-root></xsl:template>
|
|
128
|
+
<xsl:template match="*[name()='template']"><xsl:apply-templates mode="sanitize" select="*|text()"/></xsl:template>
|
|
129
|
+
<xsl:template match="*"><xsl:apply-templates mode="sanitize" select="*|text()"/></xsl:template>
|
|
130
|
+
<xsl:template match="*[name()='svg']|*[name()='math']"><xsl:apply-templates mode="sanitize" select="."/></xsl:template>
|
|
131
|
+
<xsl:template mode="sanitize" match="*[count(text())=1 and count(*)=0]"><xsl:copy><xsl:apply-templates mode="sanitize" select="@*"/><xsl:value-of select="text()"/></xsl:copy></xsl:template>
|
|
132
|
+
<xsl:template mode="sanitize" match="xhtml:*[count(text())=1 and count(*)=0]"><xsl:element name="{local-name()}"><xsl:apply-templates mode="sanitize" select="@*"/><xsl:value-of select="text()"/></xsl:element></xsl:template>
|
|
133
|
+
<xsl:template mode="sanitize" match="*|@*"><xsl:copy><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:copy></xsl:template>
|
|
134
|
+
<xsl:template mode="sanitize" match="text()[normalize-space(.) = '']"/>
|
|
135
|
+
<xsl:template mode="sanitize" match="text()"><dce-text><xsl:copy/></dce-text></xsl:template>
|
|
136
|
+
<xsl:template mode="sanitize" match="xsl:value-of|*[name()='slot']"><dce-text><xsl:copy><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:copy></dce-text></xsl:template>
|
|
137
|
+
<xsl:template mode="sanitize" match="xhtml:*"><xsl:element name="{local-name()}"><xsl:apply-templates mode="sanitize" select="*|@*|text()"/></xsl:element></xsl:template>
|
|
138
|
+
</xsl:stylesheet>`)
|
|
139
|
+
const sanitizeProcessor = new XSLTProcessor()
|
|
140
|
+
, tc = (n =>
|
|
141
|
+
{
|
|
142
|
+
forEach$(n,'script', s=> s.remove() );
|
|
143
|
+
const e = n.firstElementChild?.content || n.content
|
|
144
|
+
, asXmlNode = r => xslHtmlNs(xml2dom( '<xhtml/>' ).importNode(r, true));
|
|
145
|
+
if( e )
|
|
146
|
+
{ const t = create('div');
|
|
147
|
+
[ ...e.childNodes ].map( c => t.append(c.cloneNode(true)) )
|
|
148
|
+
return asXmlNode(t)
|
|
149
|
+
}
|
|
150
|
+
return asXmlNode(n.documentElement || n.body || n)
|
|
151
|
+
})(templateNode)
|
|
152
|
+
, xslDom = xml2dom(
|
|
153
|
+
`<xsl:stylesheet version="1.0"
|
|
154
|
+
xmlns:xsl="${ XSL_NS_URL }"
|
|
155
|
+
xmlns:dce="urn:schemas-epa-wg:dce"
|
|
156
|
+
xmlns:exsl="http://exslt.org/common"
|
|
157
|
+
exclude-result-prefixes="exsl"
|
|
83
158
|
>
|
|
84
|
-
<xsl:
|
|
85
|
-
|
|
159
|
+
<xsl:template mode="payload" match="attributes"></xsl:template>
|
|
86
160
|
<xsl:template match="/">
|
|
87
|
-
<xsl:
|
|
88
|
-
<xsl:call-template name="attributes"/>\t
|
|
89
|
-
</xsl:for-each>
|
|
161
|
+
<xsl:apply-templates mode="payload" select="/datadom/attributes"/>
|
|
90
162
|
</xsl:template>
|
|
91
163
|
<xsl:template name="slot" >
|
|
92
164
|
<xsl:param name="slotname" />
|
|
93
165
|
<xsl:param name="defaultvalue" />
|
|
94
166
|
<xsl:choose>
|
|
95
167
|
<xsl:when test="//payload/*[@slot=$slotname]">
|
|
96
|
-
|
|
168
|
+
<xsl:copy-of select="//payload/*[@slot=$slotname]"/>
|
|
97
169
|
</xsl:when>
|
|
98
170
|
<xsl:otherwise>
|
|
99
171
|
<xsl:copy-of select="$defaultvalue"/>
|
|
100
172
|
</xsl:otherwise>
|
|
101
173
|
</xsl:choose>
|
|
102
174
|
</xsl:template>
|
|
103
|
-
<xsl:
|
|
104
|
-
<xsl:variable name="slottemplate">
|
|
175
|
+
<xsl:variable name="js-injected-body">
|
|
105
176
|
<xsl:call-template name="slot" >
|
|
106
177
|
<xsl:with-param name="slotname" select="''"/>
|
|
107
178
|
<xsl:with-param name="defaultvalue"/>
|
|
108
179
|
</xsl:call-template>
|
|
109
180
|
</xsl:variable>
|
|
110
181
|
</xsl:stylesheet>`
|
|
111
|
-
|
|
182
|
+
);
|
|
112
183
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
sanitizeProcessor.importStylesheet( sanitizeXsl );
|
|
185
|
+
|
|
186
|
+
const fr = sanitizeProcessor.transformToFragment(tc, document)
|
|
187
|
+
, $ = (e,css) => e.querySelector(css)
|
|
188
|
+
, payload = $( xslDom, 'template[mode="payload"]');
|
|
189
|
+
if( !fr )
|
|
190
|
+
return console.error("transformation error",{ xml:tc.outerHTML, xsl: xmlString( sanitizeXsl ) });
|
|
191
|
+
|
|
192
|
+
for( const c of fr.childNodes )
|
|
193
|
+
payload.append(xslDom.importNode(c,true))
|
|
194
|
+
|
|
195
|
+
const embeddedTemplates = [...payload.querySelectorAll('template')];
|
|
196
|
+
embeddedTemplates.forEach(t=>payload.ownerDocument.documentElement.append(t));
|
|
124
197
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
198
|
+
const slotCall = $(xslDom,'call-template[name="slot"]')
|
|
199
|
+
, slot2xsl = s =>
|
|
200
|
+
{ const v = slotCall.cloneNode(true)
|
|
201
|
+
, name = attr(s,'name') || '';
|
|
202
|
+
name && v.firstElementChild.setAttribute('select',`'${ name }'`)
|
|
128
203
|
for( let c of s.childNodes)
|
|
129
204
|
v.lastElementChild.append(c)
|
|
130
205
|
return v
|
|
131
206
|
}
|
|
132
207
|
|
|
133
|
-
|
|
134
|
-
s.parentNode.replaceChild( slot2xsl(s), s )
|
|
208
|
+
forEach$( payload,'slot', s => s.parentNode.replaceChild( slot2xsl(s), s ) )
|
|
135
209
|
|
|
136
|
-
|
|
137
|
-
return dom
|
|
210
|
+
return tagUid(xslDom)
|
|
138
211
|
}
|
|
139
212
|
export async function
|
|
140
213
|
xhrTemplate(src)
|
|
@@ -175,9 +248,9 @@ deepEqual(a, b, O=false)
|
|
|
175
248
|
injectSlice( x, s, data )
|
|
176
249
|
{
|
|
177
250
|
const isString = typeof data === 'string' ;
|
|
178
|
-
|
|
251
|
+
const createXmlNode = ( tag, t = '' ) => ( e => ((e.append( createText(x, t||''))),e) )(x.ownerDocument.createElement( tag ))
|
|
179
252
|
const el = isString
|
|
180
|
-
?
|
|
253
|
+
? createXmlNode(s, data)
|
|
181
254
|
: document.adoptNode( xml2dom( Json2Xml( data, s ) ).documentElement);
|
|
182
255
|
[...x.children].filter( e=>e.localName === s ).map( el=>el.remove() );
|
|
183
256
|
el.data = data
|
|
@@ -186,8 +259,7 @@ injectSlice( x, s, data )
|
|
|
186
259
|
|
|
187
260
|
function forEach$( el, css, cb){
|
|
188
261
|
if( el.querySelectorAll )
|
|
189
|
-
|
|
190
|
-
cb(n)
|
|
262
|
+
[...el.querySelectorAll(css)].forEach(cb)
|
|
191
263
|
}
|
|
192
264
|
const getByHashId = ( n, id )=> ( p => n===p? null: (p && ( p.querySelector(id) || getByHashId(p,id) ) ))( n.getRootNode() )
|
|
193
265
|
const loadTemplateRoots = async ( src, dce )=>
|
|
@@ -216,6 +288,64 @@ const loadTemplateRoots = async ( src, dce )=>
|
|
|
216
288
|
return [dom]
|
|
217
289
|
}catch (error){ return [dce]}
|
|
218
290
|
}
|
|
291
|
+
export function mergeAttr( from, to )
|
|
292
|
+
{ if( isText(from) )
|
|
293
|
+
{
|
|
294
|
+
if( !isText(to) ){ debugger }
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
for( let a of from.attributes)
|
|
298
|
+
a.namespaceURI? to.setAttributeNS( a.namespaceURI, a.name, a.value ) : to.setAttribute( a.name, a.value )
|
|
299
|
+
}
|
|
300
|
+
export function assureUnique(n, id=0)
|
|
301
|
+
{
|
|
302
|
+
const m = {}
|
|
303
|
+
for( const e of n.childNodes )
|
|
304
|
+
{
|
|
305
|
+
const a = attr(e,'data-dce-id') || e.dceId || 0;
|
|
306
|
+
if( !m[a] )
|
|
307
|
+
{ if( !a )
|
|
308
|
+
{ m[a] = e.dceId = ++id;
|
|
309
|
+
if( e.setAttribute )
|
|
310
|
+
e.setAttribute('data-dce-id', e.dceId )
|
|
311
|
+
}else
|
|
312
|
+
m[a] = 1;
|
|
313
|
+
}else
|
|
314
|
+
{ const v = e.dceId = a + '-' + m[a]++;
|
|
315
|
+
if( e.setAttribute )
|
|
316
|
+
e.setAttribute('data-dce-id', v )
|
|
317
|
+
}
|
|
318
|
+
e.childNodes.length && assureUnique(e)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
export function merge( parent, fromArr )
|
|
322
|
+
{
|
|
323
|
+
const id2old = {};
|
|
324
|
+
for( let c of parent.childNodes)
|
|
325
|
+
{ ASSERT( !id2old[c.dceId] );
|
|
326
|
+
if( isText(c) )
|
|
327
|
+
{ ASSERT( c.data.trim() );
|
|
328
|
+
id2old[c.dceId || 0] = c;
|
|
329
|
+
} else
|
|
330
|
+
id2old[attr(c, 'data-dce-id') || 0] = c;
|
|
331
|
+
}
|
|
332
|
+
for( let e of [...fromArr] )
|
|
333
|
+
{
|
|
334
|
+
const o = id2old[ attr(e, 'data-dce-id') || e.dceId ];
|
|
335
|
+
if( o )
|
|
336
|
+
{ if( isText(e) )
|
|
337
|
+
{ if( o.nodeValue !== e.nodeValue )
|
|
338
|
+
o.nodeValue = e.nodeValue;
|
|
339
|
+
}else
|
|
340
|
+
{ mergeAttr(o,e)
|
|
341
|
+
if( o.childNodes.length || e.childNodes.length )
|
|
342
|
+
merge(o, e.childNodes)
|
|
343
|
+
}
|
|
344
|
+
}else
|
|
345
|
+
parent.append( e )
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
219
349
|
export class
|
|
220
350
|
CustomElement extends HTMLElement
|
|
221
351
|
{
|
|
@@ -225,7 +355,7 @@ CustomElement extends HTMLElement
|
|
|
225
355
|
, templateDocs = templateRoots.map( n => createXsltFromDom( n ) )
|
|
226
356
|
, xp = templateDocs.map( (td, p) =>{ p = new XSLTProcessor(); p.importStylesheet( td ); return p })
|
|
227
357
|
|
|
228
|
-
Object.defineProperty( this, "xsltString", { get: ()=>
|
|
358
|
+
Object.defineProperty( this, "xsltString", { get: ()=>templateDocs.map( td => xmlString(td) ).join('\n') });
|
|
229
359
|
|
|
230
360
|
const tag = attr( this, 'tag' );
|
|
231
361
|
const dce = this;
|
|
@@ -233,13 +363,18 @@ CustomElement extends HTMLElement
|
|
|
233
363
|
class DceElement extends HTMLElement
|
|
234
364
|
{
|
|
235
365
|
connectedCallback()
|
|
236
|
-
{ const x =
|
|
366
|
+
{ const x = xml2dom( '<datadom/>' ).documentElement;
|
|
367
|
+
const createXmlNode = ( tag, t = '' ) => ( e =>
|
|
368
|
+
{ if( t )
|
|
369
|
+
e.append( createText( x, t ))
|
|
370
|
+
return e;
|
|
371
|
+
})(x.ownerDocument.createElement( tag ))
|
|
237
372
|
injectData( x, 'payload' , this.childNodes, assureSlot );
|
|
238
|
-
|
|
239
|
-
injectData( x, '
|
|
240
|
-
|
|
373
|
+
this.innerHTML='';
|
|
374
|
+
injectData( x, 'attributes' , this.attributes, e => createXmlNode( e.nodeName, e.value ) );
|
|
375
|
+
injectData( x, 'dataset', Object.keys( this.dataset ), k => createXmlNode( k, this.dataset[ k ] ) );
|
|
376
|
+
const sliceRoot = injectData( x, 'slice', sliceNames, k => createXmlNode( k, '' ) );
|
|
241
377
|
this.xml = x;
|
|
242
|
-
const slices = {};
|
|
243
378
|
|
|
244
379
|
const sliceEvents=[];
|
|
245
380
|
const applySlices = ()=>
|
|
@@ -249,7 +384,7 @@ CustomElement extends HTMLElement
|
|
|
249
384
|
{ const s = attr( ev.target, 'slice');
|
|
250
385
|
if( processed[s] )
|
|
251
386
|
continue;
|
|
252
|
-
injectSlice( sliceRoot, s, ev.detail );
|
|
387
|
+
injectSlice( sliceRoot, s, 'object' === typeof ev.detail ? {...ev.detail}: ev.detail );
|
|
253
388
|
processed[s] = ev;
|
|
254
389
|
}
|
|
255
390
|
Object.keys(processed).length !== 0 && transform();
|
|
@@ -271,17 +406,28 @@ CustomElement extends HTMLElement
|
|
|
271
406
|
};
|
|
272
407
|
const transform = ()=>
|
|
273
408
|
{
|
|
274
|
-
const ff = xp.map( p
|
|
275
|
-
|
|
409
|
+
const ff = xp.map( (p,i) =>
|
|
410
|
+
{ const f = p.transformToFragment(x, document)
|
|
411
|
+
if( !f )
|
|
412
|
+
console.error( "XSLT transformation error. xsl:\n", xmlString(templateDocs[i]), '\nxml:\n', xmlString(x) );
|
|
413
|
+
return f
|
|
414
|
+
});
|
|
276
415
|
ff.map( f =>
|
|
277
|
-
{
|
|
416
|
+
{ if( !f )
|
|
417
|
+
return;
|
|
418
|
+
assureUnique(f)
|
|
419
|
+
merge( this, f.childNodes )
|
|
420
|
+
})
|
|
421
|
+
const changeCb = el=>this.onSlice({ detail: el[attr(el,'slice-prop') || 'value'], target: el })
|
|
422
|
+
, hasInitValue = el => el.hasAttribute('slice-prop') || el.hasAttribute('value') || el.value;
|
|
278
423
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
424
|
+
forEach$( this,'[slice]', el =>
|
|
425
|
+
{ if( !el.dceInitialized )
|
|
426
|
+
{ el.dceInitialized = 1;
|
|
427
|
+
el.addEventListener( attr(el,'slice-update')|| 'change', ()=>changeCb(el) )
|
|
428
|
+
if( hasInitValue(el) )
|
|
429
|
+
changeCb(el)
|
|
430
|
+
}
|
|
285
431
|
})
|
|
286
432
|
};
|
|
287
433
|
transform();
|
package/demo/a.html
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
2
|
+
<html lang="en" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
3
|
+
xmlns:html="http://www.w3.org/1999/xhtml">
|
|
3
4
|
<head>
|
|
4
5
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
5
6
|
<title>custom-element Declarative Custom Element implementation demo</title>
|
|
6
|
-
<link rel="icon" href="
|
|
7
|
+
<link rel="icon" href="./wc-square.svg" />
|
|
8
|
+
<script type="module" src="../location-element.js"></script>
|
|
7
9
|
<script type="module" src="../custom-element.js"></script>
|
|
10
|
+
|
|
8
11
|
<style>
|
|
9
|
-
@import "demo
|
|
10
|
-
|
|
12
|
+
@import "./demo.css";
|
|
13
|
+
|
|
14
|
+
button{ background: forestgreen; }
|
|
15
|
+
table{ min-width: 16rem; }
|
|
16
|
+
td{ border-bottom: 1px solid silver; }
|
|
17
|
+
tfoot td{ border-bottom: none; }
|
|
18
|
+
td,th{text-align: right; }
|
|
19
|
+
caption{ padding: 1rem; font-weight: bolder; font-family: sans-serif; }
|
|
11
20
|
</style>
|
|
12
21
|
</head>
|
|
13
22
|
<body>
|
|
14
23
|
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
<custom-element >
|
|
25
|
+
<local-storage key="basket" slice="basket"></local-storage>
|
|
26
|
+
<html:table>
|
|
27
|
+
<xsl:for-each select="//slice/basket/@*">
|
|
28
|
+
<html:tr>
|
|
29
|
+
<html:th> {name()} </html:th>
|
|
30
|
+
<html:td> {.} </html:td>
|
|
31
|
+
</html:tr>
|
|
32
|
+
</xsl:for-each>
|
|
33
|
+
</html:table>
|
|
34
|
+
count:<xsl:value-of select="count(//slice/basket/@*)"/>
|
|
35
|
+
</custom-element>
|
|
22
36
|
|
|
23
37
|
</body>
|
|
24
38
|
</html>
|