@gluon/gluon 2.5.2 → 3.0.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/CHANGELOG.md +41 -0
- package/README.md +268 -122
- package/dist/gluon.d.ts +124 -0
- package/dist/gluon.min.js +4 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +254 -0
- package/dist/properties.d.ts +60 -0
- package/dist/styles.d.ts +26 -0
- package/package.json +44 -44
- package/gluon.es5.js +0 -2061
- package/gluon.es5.js.map +0 -1
- package/gluon.js +0 -126
- package/gluon.umd.js +0 -1591
- package/gluon.umd.js.map +0 -1
package/gluon.umd.js
DELETED
|
@@ -1,1591 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2018 Goffert van Gool
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
(function (global, factory) {
|
|
27
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
28
|
-
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
29
|
-
(factory((global.GluonJS = {})));
|
|
30
|
-
}(this, (function (exports) { 'use strict';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @license
|
|
34
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
35
|
-
* This code may only be used under the BSD style license found at
|
|
36
|
-
* http://polymer.github.io/LICENSE.txt
|
|
37
|
-
* The complete set of authors may be found at
|
|
38
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
39
|
-
* The complete set of contributors may be found at
|
|
40
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
41
|
-
* Code distributed by Google as part of the polymer project is also
|
|
42
|
-
* subject to an additional IP rights grant found at
|
|
43
|
-
* http://polymer.github.io/PATENTS.txt
|
|
44
|
-
*/
|
|
45
|
-
/**
|
|
46
|
-
* True if the custom elements polyfill is in use.
|
|
47
|
-
*/
|
|
48
|
-
const isCEPolyfill = window.customElements !== undefined &&
|
|
49
|
-
window.customElements.polyfillWrapFlushCallback !==
|
|
50
|
-
undefined;
|
|
51
|
-
/**
|
|
52
|
-
* Reparents nodes, starting from `startNode` (inclusive) to `endNode`
|
|
53
|
-
* (exclusive), into another container (could be the same container), before
|
|
54
|
-
* `beforeNode`. If `beforeNode` is null, it appends the nodes to the
|
|
55
|
-
* container.
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Removes nodes, starting from `startNode` (inclusive) to `endNode`
|
|
60
|
-
* (exclusive), from `container`.
|
|
61
|
-
*/
|
|
62
|
-
const removeNodes = (container, startNode, endNode = null) => {
|
|
63
|
-
let node = startNode;
|
|
64
|
-
while (node !== endNode) {
|
|
65
|
-
const n = node.nextSibling;
|
|
66
|
-
container.removeChild(node);
|
|
67
|
-
node = n;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @license
|
|
73
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
74
|
-
* This code may only be used under the BSD style license found at
|
|
75
|
-
* http://polymer.github.io/LICENSE.txt
|
|
76
|
-
* The complete set of authors may be found at
|
|
77
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
78
|
-
* The complete set of contributors may be found at
|
|
79
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
80
|
-
* Code distributed by Google as part of the polymer project is also
|
|
81
|
-
* subject to an additional IP rights grant found at
|
|
82
|
-
* http://polymer.github.io/PATENTS.txt
|
|
83
|
-
*/
|
|
84
|
-
/**
|
|
85
|
-
* An expression marker with embedded unique key to avoid collision with
|
|
86
|
-
* possible text in templates.
|
|
87
|
-
*/
|
|
88
|
-
const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
|
|
89
|
-
/**
|
|
90
|
-
* An expression marker used text-positions, multi-binding attributes, and
|
|
91
|
-
* attributes with markup-like text values.
|
|
92
|
-
*/
|
|
93
|
-
const nodeMarker = `<!--${marker}-->`;
|
|
94
|
-
const markerRegex = new RegExp(`${marker}|${nodeMarker}`);
|
|
95
|
-
/**
|
|
96
|
-
* Suffix appended to all bound attribute names.
|
|
97
|
-
*/
|
|
98
|
-
const boundAttributeSuffix = '$lit$';
|
|
99
|
-
/**
|
|
100
|
-
* An updateable Template that tracks the location of dynamic parts.
|
|
101
|
-
*/
|
|
102
|
-
class Template {
|
|
103
|
-
constructor(result, element) {
|
|
104
|
-
this.parts = [];
|
|
105
|
-
this.element = element;
|
|
106
|
-
let index = -1;
|
|
107
|
-
let partIndex = 0;
|
|
108
|
-
const nodesToRemove = [];
|
|
109
|
-
const _prepareTemplate = (template) => {
|
|
110
|
-
const content = template.content;
|
|
111
|
-
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be
|
|
112
|
-
// null
|
|
113
|
-
const walker = document.createTreeWalker(content, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false);
|
|
114
|
-
// Keeps track of the last index associated with a part. We try to delete
|
|
115
|
-
// unnecessary nodes, but we never want to associate two different parts
|
|
116
|
-
// to the same index. They must have a constant node between.
|
|
117
|
-
let lastPartIndex = 0;
|
|
118
|
-
while (walker.nextNode()) {
|
|
119
|
-
index++;
|
|
120
|
-
const node = walker.currentNode;
|
|
121
|
-
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
|
|
122
|
-
if (node.hasAttributes()) {
|
|
123
|
-
const attributes = node.attributes;
|
|
124
|
-
// Per
|
|
125
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap,
|
|
126
|
-
// attributes are not guaranteed to be returned in document order.
|
|
127
|
-
// In particular, Edge/IE can return them out of order, so we cannot
|
|
128
|
-
// assume a correspondance between part index and attribute index.
|
|
129
|
-
let count = 0;
|
|
130
|
-
for (let i = 0; i < attributes.length; i++) {
|
|
131
|
-
if (attributes[i].value.indexOf(marker) >= 0) {
|
|
132
|
-
count++;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
while (count-- > 0) {
|
|
136
|
-
// Get the template literal section leading up to the first
|
|
137
|
-
// expression in this attribute
|
|
138
|
-
const stringForPart = result.strings[partIndex];
|
|
139
|
-
// Find the attribute name
|
|
140
|
-
const name = lastAttributeNameRegex.exec(stringForPart)[2];
|
|
141
|
-
// Find the corresponding attribute
|
|
142
|
-
// All bound attributes have had a suffix added in
|
|
143
|
-
// TemplateResult#getHTML to opt out of special attribute
|
|
144
|
-
// handling. To look up the attribute value we also need to add
|
|
145
|
-
// the suffix.
|
|
146
|
-
const attributeLookupName = name.toLowerCase() + boundAttributeSuffix;
|
|
147
|
-
const attributeValue = node.getAttribute(attributeLookupName);
|
|
148
|
-
const strings = attributeValue.split(markerRegex);
|
|
149
|
-
this.parts.push({ type: 'attribute', index, name, strings });
|
|
150
|
-
node.removeAttribute(attributeLookupName);
|
|
151
|
-
partIndex += strings.length - 1;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (node.tagName === 'TEMPLATE') {
|
|
155
|
-
_prepareTemplate(node);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
else if (node.nodeType === 3 /* Node.TEXT_NODE */) {
|
|
159
|
-
const data = node.data;
|
|
160
|
-
if (data.indexOf(marker) >= 0) {
|
|
161
|
-
const parent = node.parentNode;
|
|
162
|
-
const strings = data.split(markerRegex);
|
|
163
|
-
const lastIndex = strings.length - 1;
|
|
164
|
-
// Generate a new text node for each literal section
|
|
165
|
-
// These nodes are also used as the markers for node parts
|
|
166
|
-
for (let i = 0; i < lastIndex; i++) {
|
|
167
|
-
parent.insertBefore((strings[i] === '') ? createMarker() :
|
|
168
|
-
document.createTextNode(strings[i]), node);
|
|
169
|
-
this.parts.push({ type: 'node', index: ++index });
|
|
170
|
-
}
|
|
171
|
-
// If there's no text, we must insert a comment to mark our place.
|
|
172
|
-
// Else, we can trust it will stick around after cloning.
|
|
173
|
-
if (strings[lastIndex] === '') {
|
|
174
|
-
parent.insertBefore(createMarker(), node);
|
|
175
|
-
nodesToRemove.push(node);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
node.data = strings[lastIndex];
|
|
179
|
-
}
|
|
180
|
-
// We have a part for each match found
|
|
181
|
-
partIndex += lastIndex;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
else if (node.nodeType === 8 /* Node.COMMENT_NODE */) {
|
|
185
|
-
if (node.data === marker) {
|
|
186
|
-
const parent = node.parentNode;
|
|
187
|
-
// Add a new marker node to be the startNode of the Part if any of
|
|
188
|
-
// the following are true:
|
|
189
|
-
// * We don't have a previousSibling
|
|
190
|
-
// * The previousSibling is already the start of a previous part
|
|
191
|
-
if (node.previousSibling === null || index === lastPartIndex) {
|
|
192
|
-
index++;
|
|
193
|
-
parent.insertBefore(createMarker(), node);
|
|
194
|
-
}
|
|
195
|
-
lastPartIndex = index;
|
|
196
|
-
this.parts.push({ type: 'node', index });
|
|
197
|
-
// If we don't have a nextSibling, keep this node so we have an end.
|
|
198
|
-
// Else, we can remove it to save future costs.
|
|
199
|
-
if (node.nextSibling === null) {
|
|
200
|
-
node.data = '';
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
nodesToRemove.push(node);
|
|
204
|
-
index--;
|
|
205
|
-
}
|
|
206
|
-
partIndex++;
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
let i = -1;
|
|
210
|
-
while ((i = node.data.indexOf(marker, i + 1)) !==
|
|
211
|
-
-1) {
|
|
212
|
-
// Comment node has a binding marker inside, make an inactive part
|
|
213
|
-
// The binding won't work, but subsequent bindings will
|
|
214
|
-
// TODO (justinfagnani): consider whether it's even worth it to
|
|
215
|
-
// make bindings in comments work
|
|
216
|
-
this.parts.push({ type: 'node', index: -1 });
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
_prepareTemplate(element);
|
|
223
|
-
// Remove text binding nodes after the walk to not disturb the TreeWalker
|
|
224
|
-
for (const n of nodesToRemove) {
|
|
225
|
-
n.parentNode.removeChild(n);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
const isTemplatePartActive = (part) => part.index !== -1;
|
|
230
|
-
// Allows `document.createComment('')` to be renamed for a
|
|
231
|
-
// small manual size-savings.
|
|
232
|
-
const createMarker = () => document.createComment('');
|
|
233
|
-
/**
|
|
234
|
-
* This regex extracts the attribute name preceding an attribute-position
|
|
235
|
-
* expression. It does this by matching the syntax allowed for attributes
|
|
236
|
-
* against the string literal directly preceding the expression, assuming that
|
|
237
|
-
* the expression is in an attribute-value position.
|
|
238
|
-
*
|
|
239
|
-
* See attributes in the HTML spec:
|
|
240
|
-
* https://www.w3.org/TR/html5/syntax.html#attributes-0
|
|
241
|
-
*
|
|
242
|
-
* "\0-\x1F\x7F-\x9F" are Unicode control characters
|
|
243
|
-
*
|
|
244
|
-
* " \x09\x0a\x0c\x0d" are HTML space characters:
|
|
245
|
-
* https://www.w3.org/TR/html5/infrastructure.html#space-character
|
|
246
|
-
*
|
|
247
|
-
* So an attribute is:
|
|
248
|
-
* * The name: any character except a control character, space character, ('),
|
|
249
|
-
* ("), ">", "=", or "/"
|
|
250
|
-
* * Followed by zero or more space characters
|
|
251
|
-
* * Followed by "="
|
|
252
|
-
* * Followed by zero or more space characters
|
|
253
|
-
* * Followed by:
|
|
254
|
-
* * Any character except space, ('), ("), "<", ">", "=", (`), or
|
|
255
|
-
* * (") then any non-("), or
|
|
256
|
-
* * (') then any non-(')
|
|
257
|
-
*/
|
|
258
|
-
const lastAttributeNameRegex = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* @license
|
|
262
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
263
|
-
* This code may only be used under the BSD style license found at
|
|
264
|
-
* http://polymer.github.io/LICENSE.txt
|
|
265
|
-
* The complete set of authors may be found at
|
|
266
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
267
|
-
* The complete set of contributors may be found at
|
|
268
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
269
|
-
* Code distributed by Google as part of the polymer project is also
|
|
270
|
-
* subject to an additional IP rights grant found at
|
|
271
|
-
* http://polymer.github.io/PATENTS.txt
|
|
272
|
-
*/
|
|
273
|
-
/**
|
|
274
|
-
* @module shady-render
|
|
275
|
-
*/
|
|
276
|
-
const walkerNodeFilter = 133;
|
|
277
|
-
/**
|
|
278
|
-
* Removes the list of nodes from a Template safely. In addition to removing
|
|
279
|
-
* nodes from the Template, the Template part indices are updated to match
|
|
280
|
-
* the mutated Template DOM.
|
|
281
|
-
*
|
|
282
|
-
* As the template is walked the removal state is tracked and
|
|
283
|
-
* part indices are adjusted as needed.
|
|
284
|
-
*
|
|
285
|
-
* div
|
|
286
|
-
* div#1 (remove) <-- start removing (removing node is div#1)
|
|
287
|
-
* div
|
|
288
|
-
* div#2 (remove) <-- continue removing (removing node is still div#1)
|
|
289
|
-
* div
|
|
290
|
-
* div <-- stop removing since previous sibling is the removing node (div#1,
|
|
291
|
-
* removed 4 nodes)
|
|
292
|
-
*/
|
|
293
|
-
function removeNodesFromTemplate(template, nodesToRemove) {
|
|
294
|
-
const { element: { content }, parts } = template;
|
|
295
|
-
const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
|
|
296
|
-
let partIndex = nextActiveIndexInTemplateParts(parts);
|
|
297
|
-
let part = parts[partIndex];
|
|
298
|
-
let nodeIndex = -1;
|
|
299
|
-
let removeCount = 0;
|
|
300
|
-
const nodesToRemoveInTemplate = [];
|
|
301
|
-
let currentRemovingNode = null;
|
|
302
|
-
while (walker.nextNode()) {
|
|
303
|
-
nodeIndex++;
|
|
304
|
-
const node = walker.currentNode;
|
|
305
|
-
// End removal if stepped past the removing node
|
|
306
|
-
if (node.previousSibling === currentRemovingNode) {
|
|
307
|
-
currentRemovingNode = null;
|
|
308
|
-
}
|
|
309
|
-
// A node to remove was found in the template
|
|
310
|
-
if (nodesToRemove.has(node)) {
|
|
311
|
-
nodesToRemoveInTemplate.push(node);
|
|
312
|
-
// Track node we're removing
|
|
313
|
-
if (currentRemovingNode === null) {
|
|
314
|
-
currentRemovingNode = node;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
// When removing, increment count by which to adjust subsequent part indices
|
|
318
|
-
if (currentRemovingNode !== null) {
|
|
319
|
-
removeCount++;
|
|
320
|
-
}
|
|
321
|
-
while (part !== undefined && part.index === nodeIndex) {
|
|
322
|
-
// If part is in a removed node deactivate it by setting index to -1 or
|
|
323
|
-
// adjust the index as needed.
|
|
324
|
-
part.index = currentRemovingNode !== null ? -1 : part.index - removeCount;
|
|
325
|
-
// go to the next active part.
|
|
326
|
-
partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
|
|
327
|
-
part = parts[partIndex];
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
nodesToRemoveInTemplate.forEach((n) => n.parentNode.removeChild(n));
|
|
331
|
-
}
|
|
332
|
-
const countNodes = (node) => {
|
|
333
|
-
let count = (node.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */) ? 0 : 1;
|
|
334
|
-
const walker = document.createTreeWalker(node, walkerNodeFilter, null, false);
|
|
335
|
-
while (walker.nextNode()) {
|
|
336
|
-
count++;
|
|
337
|
-
}
|
|
338
|
-
return count;
|
|
339
|
-
};
|
|
340
|
-
const nextActiveIndexInTemplateParts = (parts, startIndex = -1) => {
|
|
341
|
-
for (let i = startIndex + 1; i < parts.length; i++) {
|
|
342
|
-
const part = parts[i];
|
|
343
|
-
if (isTemplatePartActive(part)) {
|
|
344
|
-
return i;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return -1;
|
|
348
|
-
};
|
|
349
|
-
/**
|
|
350
|
-
* Inserts the given node into the Template, optionally before the given
|
|
351
|
-
* refNode. In addition to inserting the node into the Template, the Template
|
|
352
|
-
* part indices are updated to match the mutated Template DOM.
|
|
353
|
-
*/
|
|
354
|
-
function insertNodeIntoTemplate(template, node, refNode = null) {
|
|
355
|
-
const { element: { content }, parts } = template;
|
|
356
|
-
// If there's no refNode, then put node at end of template.
|
|
357
|
-
// No part indices need to be shifted in this case.
|
|
358
|
-
if (refNode === null || refNode === undefined) {
|
|
359
|
-
content.appendChild(node);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
|
|
363
|
-
let partIndex = nextActiveIndexInTemplateParts(parts);
|
|
364
|
-
let insertCount = 0;
|
|
365
|
-
let walkerIndex = -1;
|
|
366
|
-
while (walker.nextNode()) {
|
|
367
|
-
walkerIndex++;
|
|
368
|
-
const walkerNode = walker.currentNode;
|
|
369
|
-
if (walkerNode === refNode) {
|
|
370
|
-
insertCount = countNodes(node);
|
|
371
|
-
refNode.parentNode.insertBefore(node, refNode);
|
|
372
|
-
}
|
|
373
|
-
while (partIndex !== -1 && parts[partIndex].index === walkerIndex) {
|
|
374
|
-
// If we've inserted the node, simply adjust all subsequent parts
|
|
375
|
-
if (insertCount > 0) {
|
|
376
|
-
while (partIndex !== -1) {
|
|
377
|
-
parts[partIndex].index += insertCount;
|
|
378
|
-
partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
|
|
379
|
-
}
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* @license
|
|
389
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
390
|
-
* This code may only be used under the BSD style license found at
|
|
391
|
-
* http://polymer.github.io/LICENSE.txt
|
|
392
|
-
* The complete set of authors may be found at
|
|
393
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
394
|
-
* The complete set of contributors may be found at
|
|
395
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
396
|
-
* Code distributed by Google as part of the polymer project is also
|
|
397
|
-
* subject to an additional IP rights grant found at
|
|
398
|
-
* http://polymer.github.io/PATENTS.txt
|
|
399
|
-
*/
|
|
400
|
-
const directives = new WeakMap();
|
|
401
|
-
/**
|
|
402
|
-
* Brands a function as a directive so that lit-html will call the function
|
|
403
|
-
* during template rendering, rather than passing as a value.
|
|
404
|
-
*
|
|
405
|
-
* @param f The directive factory function. Must be a function that returns a
|
|
406
|
-
* function of the signature `(part: Part) => void`. The returned function will
|
|
407
|
-
* be called with the part object
|
|
408
|
-
*
|
|
409
|
-
* @example
|
|
410
|
-
*
|
|
411
|
-
* ```
|
|
412
|
-
* import {directive, html} from 'lit-html';
|
|
413
|
-
*
|
|
414
|
-
* const immutable = directive((v) => (part) => {
|
|
415
|
-
* if (part.value !== v) {
|
|
416
|
-
* part.setValue(v)
|
|
417
|
-
* }
|
|
418
|
-
* });
|
|
419
|
-
* ```
|
|
420
|
-
*/
|
|
421
|
-
// tslint:disable-next-line:no-any
|
|
422
|
-
|
|
423
|
-
const isDirective = (o) => {
|
|
424
|
-
return typeof o === 'function' && directives.has(o);
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* @license
|
|
429
|
-
* Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
|
430
|
-
* This code may only be used under the BSD style license found at
|
|
431
|
-
* http://polymer.github.io/LICENSE.txt
|
|
432
|
-
* The complete set of authors may be found at
|
|
433
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
434
|
-
* The complete set of contributors may be found at
|
|
435
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
436
|
-
* Code distributed by Google as part of the polymer project is also
|
|
437
|
-
* subject to an additional IP rights grant found at
|
|
438
|
-
* http://polymer.github.io/PATENTS.txt
|
|
439
|
-
*/
|
|
440
|
-
/**
|
|
441
|
-
* A sentinel value that signals that a value was handled by a directive and
|
|
442
|
-
* should not be written to the DOM.
|
|
443
|
-
*/
|
|
444
|
-
const noChange = {};
|
|
445
|
-
/**
|
|
446
|
-
* A sentinel value that signals a NodePart to fully clear its content.
|
|
447
|
-
*/
|
|
448
|
-
const nothing = {};
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* @license
|
|
452
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
453
|
-
* This code may only be used under the BSD style license found at
|
|
454
|
-
* http://polymer.github.io/LICENSE.txt
|
|
455
|
-
* The complete set of authors may be found at
|
|
456
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
457
|
-
* The complete set of contributors may be found at
|
|
458
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
459
|
-
* Code distributed by Google as part of the polymer project is also
|
|
460
|
-
* subject to an additional IP rights grant found at
|
|
461
|
-
* http://polymer.github.io/PATENTS.txt
|
|
462
|
-
*/
|
|
463
|
-
/**
|
|
464
|
-
* @module lit-html
|
|
465
|
-
*/
|
|
466
|
-
/**
|
|
467
|
-
* An instance of a `Template` that can be attached to the DOM and updated
|
|
468
|
-
* with new values.
|
|
469
|
-
*/
|
|
470
|
-
class TemplateInstance {
|
|
471
|
-
constructor(template, processor, options) {
|
|
472
|
-
this._parts = [];
|
|
473
|
-
this.template = template;
|
|
474
|
-
this.processor = processor;
|
|
475
|
-
this.options = options;
|
|
476
|
-
}
|
|
477
|
-
update(values) {
|
|
478
|
-
let i = 0;
|
|
479
|
-
for (const part of this._parts) {
|
|
480
|
-
if (part !== undefined) {
|
|
481
|
-
part.setValue(values[i]);
|
|
482
|
-
}
|
|
483
|
-
i++;
|
|
484
|
-
}
|
|
485
|
-
for (const part of this._parts) {
|
|
486
|
-
if (part !== undefined) {
|
|
487
|
-
part.commit();
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
_clone() {
|
|
492
|
-
// When using the Custom Elements polyfill, clone the node, rather than
|
|
493
|
-
// importing it, to keep the fragment in the template's document. This
|
|
494
|
-
// leaves the fragment inert so custom elements won't upgrade and
|
|
495
|
-
// potentially modify their contents by creating a polyfilled ShadowRoot
|
|
496
|
-
// while we traverse the tree.
|
|
497
|
-
const fragment = isCEPolyfill ?
|
|
498
|
-
this.template.element.content.cloneNode(true) :
|
|
499
|
-
document.importNode(this.template.element.content, true);
|
|
500
|
-
const parts = this.template.parts;
|
|
501
|
-
let partIndex = 0;
|
|
502
|
-
let nodeIndex = 0;
|
|
503
|
-
const _prepareInstance = (fragment) => {
|
|
504
|
-
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be
|
|
505
|
-
// null
|
|
506
|
-
const walker = document.createTreeWalker(fragment, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false);
|
|
507
|
-
let node = walker.nextNode();
|
|
508
|
-
// Loop through all the nodes and parts of a template
|
|
509
|
-
while (partIndex < parts.length && node !== null) {
|
|
510
|
-
const part = parts[partIndex];
|
|
511
|
-
// Consecutive Parts may have the same node index, in the case of
|
|
512
|
-
// multiple bound attributes on an element. So each iteration we either
|
|
513
|
-
// increment the nodeIndex, if we aren't on a node with a part, or the
|
|
514
|
-
// partIndex if we are. By not incrementing the nodeIndex when we find a
|
|
515
|
-
// part, we allow for the next part to be associated with the current
|
|
516
|
-
// node if neccessasry.
|
|
517
|
-
if (!isTemplatePartActive(part)) {
|
|
518
|
-
this._parts.push(undefined);
|
|
519
|
-
partIndex++;
|
|
520
|
-
}
|
|
521
|
-
else if (nodeIndex === part.index) {
|
|
522
|
-
if (part.type === 'node') {
|
|
523
|
-
const part = this.processor.handleTextExpression(this.options);
|
|
524
|
-
part.insertAfterNode(node.previousSibling);
|
|
525
|
-
this._parts.push(part);
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
this._parts.push(...this.processor.handleAttributeExpressions(node, part.name, part.strings, this.options));
|
|
529
|
-
}
|
|
530
|
-
partIndex++;
|
|
531
|
-
}
|
|
532
|
-
else {
|
|
533
|
-
nodeIndex++;
|
|
534
|
-
if (node.nodeName === 'TEMPLATE') {
|
|
535
|
-
_prepareInstance(node.content);
|
|
536
|
-
}
|
|
537
|
-
node = walker.nextNode();
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
_prepareInstance(fragment);
|
|
542
|
-
if (isCEPolyfill) {
|
|
543
|
-
document.adoptNode(fragment);
|
|
544
|
-
customElements.upgrade(fragment);
|
|
545
|
-
}
|
|
546
|
-
return fragment;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* @license
|
|
552
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
553
|
-
* This code may only be used under the BSD style license found at
|
|
554
|
-
* http://polymer.github.io/LICENSE.txt
|
|
555
|
-
* The complete set of authors may be found at
|
|
556
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
557
|
-
* The complete set of contributors may be found at
|
|
558
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
559
|
-
* Code distributed by Google as part of the polymer project is also
|
|
560
|
-
* subject to an additional IP rights grant found at
|
|
561
|
-
* http://polymer.github.io/PATENTS.txt
|
|
562
|
-
*/
|
|
563
|
-
/**
|
|
564
|
-
* @module lit-html
|
|
565
|
-
*/
|
|
566
|
-
/**
|
|
567
|
-
* The return type of `html`, which holds a Template and the values from
|
|
568
|
-
* interpolated expressions.
|
|
569
|
-
*/
|
|
570
|
-
class TemplateResult {
|
|
571
|
-
constructor(strings, values, type, processor) {
|
|
572
|
-
this.strings = strings;
|
|
573
|
-
this.values = values;
|
|
574
|
-
this.type = type;
|
|
575
|
-
this.processor = processor;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Returns a string of HTML used to create a `<template>` element.
|
|
579
|
-
*/
|
|
580
|
-
getHTML() {
|
|
581
|
-
const endIndex = this.strings.length - 1;
|
|
582
|
-
let html = '';
|
|
583
|
-
for (let i = 0; i < endIndex; i++) {
|
|
584
|
-
const s = this.strings[i];
|
|
585
|
-
// This exec() call does two things:
|
|
586
|
-
// 1) Appends a suffix to the bound attribute name to opt out of special
|
|
587
|
-
// attribute value parsing that IE11 and Edge do, like for style and
|
|
588
|
-
// many SVG attributes. The Template class also appends the same suffix
|
|
589
|
-
// when looking up attributes to create Parts.
|
|
590
|
-
// 2) Adds an unquoted-attribute-safe marker for the first expression in
|
|
591
|
-
// an attribute. Subsequent attribute expressions will use node markers,
|
|
592
|
-
// and this is safe since attributes with multiple expressions are
|
|
593
|
-
// guaranteed to be quoted.
|
|
594
|
-
const match = lastAttributeNameRegex.exec(s);
|
|
595
|
-
if (match) {
|
|
596
|
-
// We're starting a new bound attribute.
|
|
597
|
-
// Add the safe attribute suffix, and use unquoted-attribute-safe
|
|
598
|
-
// marker.
|
|
599
|
-
html += s.substr(0, match.index) + match[1] + match[2] +
|
|
600
|
-
boundAttributeSuffix + match[3] + marker;
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
// We're either in a bound node, or trailing bound attribute.
|
|
604
|
-
// Either way, nodeMarker is safe to use.
|
|
605
|
-
html += s + nodeMarker;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
return html + this.strings[endIndex];
|
|
609
|
-
}
|
|
610
|
-
getTemplateElement() {
|
|
611
|
-
const template = document.createElement('template');
|
|
612
|
-
template.innerHTML = this.getHTML();
|
|
613
|
-
return template;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* A TemplateResult for SVG fragments.
|
|
618
|
-
*
|
|
619
|
-
* This class wraps HTMl in an `<svg>` tag in order to parse its contents in the
|
|
620
|
-
* SVG namespace, then modifies the template to remove the `<svg>` tag so that
|
|
621
|
-
* clones only container the original fragment.
|
|
622
|
-
*/
|
|
623
|
-
|
|
624
|
-
/**
|
|
625
|
-
* @license
|
|
626
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
627
|
-
* This code may only be used under the BSD style license found at
|
|
628
|
-
* http://polymer.github.io/LICENSE.txt
|
|
629
|
-
* The complete set of authors may be found at
|
|
630
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
631
|
-
* The complete set of contributors may be found at
|
|
632
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
633
|
-
* Code distributed by Google as part of the polymer project is also
|
|
634
|
-
* subject to an additional IP rights grant found at
|
|
635
|
-
* http://polymer.github.io/PATENTS.txt
|
|
636
|
-
*/
|
|
637
|
-
/**
|
|
638
|
-
* @module lit-html
|
|
639
|
-
*/
|
|
640
|
-
const isPrimitive = (value) => {
|
|
641
|
-
return (value === null ||
|
|
642
|
-
!(typeof value === 'object' || typeof value === 'function'));
|
|
643
|
-
};
|
|
644
|
-
/**
|
|
645
|
-
* Sets attribute values for AttributeParts, so that the value is only set once
|
|
646
|
-
* even if there are multiple parts for an attribute.
|
|
647
|
-
*/
|
|
648
|
-
class AttributeCommitter {
|
|
649
|
-
constructor(element, name, strings) {
|
|
650
|
-
this.dirty = true;
|
|
651
|
-
this.element = element;
|
|
652
|
-
this.name = name;
|
|
653
|
-
this.strings = strings;
|
|
654
|
-
this.parts = [];
|
|
655
|
-
for (let i = 0; i < strings.length - 1; i++) {
|
|
656
|
-
this.parts[i] = this._createPart();
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Creates a single part. Override this to create a differnt type of part.
|
|
661
|
-
*/
|
|
662
|
-
_createPart() {
|
|
663
|
-
return new AttributePart(this);
|
|
664
|
-
}
|
|
665
|
-
_getValue() {
|
|
666
|
-
const strings = this.strings;
|
|
667
|
-
const l = strings.length - 1;
|
|
668
|
-
let text = '';
|
|
669
|
-
for (let i = 0; i < l; i++) {
|
|
670
|
-
text += strings[i];
|
|
671
|
-
const part = this.parts[i];
|
|
672
|
-
if (part !== undefined) {
|
|
673
|
-
const v = part.value;
|
|
674
|
-
if (v != null &&
|
|
675
|
-
(Array.isArray(v) ||
|
|
676
|
-
// tslint:disable-next-line:no-any
|
|
677
|
-
typeof v !== 'string' && v[Symbol.iterator])) {
|
|
678
|
-
for (const t of v) {
|
|
679
|
-
text += typeof t === 'string' ? t : String(t);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
else {
|
|
683
|
-
text += typeof v === 'string' ? v : String(v);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
text += strings[l];
|
|
688
|
-
return text;
|
|
689
|
-
}
|
|
690
|
-
commit() {
|
|
691
|
-
if (this.dirty) {
|
|
692
|
-
this.dirty = false;
|
|
693
|
-
this.element.setAttribute(this.name, this._getValue());
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
class AttributePart {
|
|
698
|
-
constructor(comitter) {
|
|
699
|
-
this.value = undefined;
|
|
700
|
-
this.committer = comitter;
|
|
701
|
-
}
|
|
702
|
-
setValue(value) {
|
|
703
|
-
if (value !== noChange && (!isPrimitive(value) || value !== this.value)) {
|
|
704
|
-
this.value = value;
|
|
705
|
-
// If the value is a not a directive, dirty the committer so that it'll
|
|
706
|
-
// call setAttribute. If the value is a directive, it'll dirty the
|
|
707
|
-
// committer if it calls setValue().
|
|
708
|
-
if (!isDirective(value)) {
|
|
709
|
-
this.committer.dirty = true;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
commit() {
|
|
714
|
-
while (isDirective(this.value)) {
|
|
715
|
-
const directive$$1 = this.value;
|
|
716
|
-
this.value = noChange;
|
|
717
|
-
directive$$1(this);
|
|
718
|
-
}
|
|
719
|
-
if (this.value === noChange) {
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
this.committer.commit();
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
class NodePart {
|
|
726
|
-
constructor(options) {
|
|
727
|
-
this.value = undefined;
|
|
728
|
-
this._pendingValue = undefined;
|
|
729
|
-
this.options = options;
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* Inserts this part into a container.
|
|
733
|
-
*
|
|
734
|
-
* This part must be empty, as its contents are not automatically moved.
|
|
735
|
-
*/
|
|
736
|
-
appendInto(container) {
|
|
737
|
-
this.startNode = container.appendChild(createMarker());
|
|
738
|
-
this.endNode = container.appendChild(createMarker());
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Inserts this part between `ref` and `ref`'s next sibling. Both `ref` and
|
|
742
|
-
* its next sibling must be static, unchanging nodes such as those that appear
|
|
743
|
-
* in a literal section of a template.
|
|
744
|
-
*
|
|
745
|
-
* This part must be empty, as its contents are not automatically moved.
|
|
746
|
-
*/
|
|
747
|
-
insertAfterNode(ref) {
|
|
748
|
-
this.startNode = ref;
|
|
749
|
-
this.endNode = ref.nextSibling;
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Appends this part into a parent part.
|
|
753
|
-
*
|
|
754
|
-
* This part must be empty, as its contents are not automatically moved.
|
|
755
|
-
*/
|
|
756
|
-
appendIntoPart(part) {
|
|
757
|
-
part._insert(this.startNode = createMarker());
|
|
758
|
-
part._insert(this.endNode = createMarker());
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Appends this part after `ref`
|
|
762
|
-
*
|
|
763
|
-
* This part must be empty, as its contents are not automatically moved.
|
|
764
|
-
*/
|
|
765
|
-
insertAfterPart(ref) {
|
|
766
|
-
ref._insert(this.startNode = createMarker());
|
|
767
|
-
this.endNode = ref.endNode;
|
|
768
|
-
ref.endNode = this.startNode;
|
|
769
|
-
}
|
|
770
|
-
setValue(value) {
|
|
771
|
-
this._pendingValue = value;
|
|
772
|
-
}
|
|
773
|
-
commit() {
|
|
774
|
-
while (isDirective(this._pendingValue)) {
|
|
775
|
-
const directive$$1 = this._pendingValue;
|
|
776
|
-
this._pendingValue = noChange;
|
|
777
|
-
directive$$1(this);
|
|
778
|
-
}
|
|
779
|
-
const value = this._pendingValue;
|
|
780
|
-
if (value === noChange) {
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
if (isPrimitive(value)) {
|
|
784
|
-
if (value !== this.value) {
|
|
785
|
-
this._commitText(value);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
else if (value instanceof TemplateResult) {
|
|
789
|
-
this._commitTemplateResult(value);
|
|
790
|
-
}
|
|
791
|
-
else if (value instanceof Node) {
|
|
792
|
-
this._commitNode(value);
|
|
793
|
-
}
|
|
794
|
-
else if (Array.isArray(value) ||
|
|
795
|
-
// tslint:disable-next-line:no-any
|
|
796
|
-
value[Symbol.iterator]) {
|
|
797
|
-
this._commitIterable(value);
|
|
798
|
-
}
|
|
799
|
-
else if (value === nothing) {
|
|
800
|
-
this.value = nothing;
|
|
801
|
-
this.clear();
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
// Fallback, will render the string representation
|
|
805
|
-
this._commitText(value);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
_insert(node) {
|
|
809
|
-
this.endNode.parentNode.insertBefore(node, this.endNode);
|
|
810
|
-
}
|
|
811
|
-
_commitNode(value) {
|
|
812
|
-
if (this.value === value) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
this.clear();
|
|
816
|
-
this._insert(value);
|
|
817
|
-
this.value = value;
|
|
818
|
-
}
|
|
819
|
-
_commitText(value) {
|
|
820
|
-
const node = this.startNode.nextSibling;
|
|
821
|
-
value = value == null ? '' : value;
|
|
822
|
-
if (node === this.endNode.previousSibling &&
|
|
823
|
-
node.nodeType === 3 /* Node.TEXT_NODE */) {
|
|
824
|
-
// If we only have a single text node between the markers, we can just
|
|
825
|
-
// set its value, rather than replacing it.
|
|
826
|
-
// TODO(justinfagnani): Can we just check if this.value is primitive?
|
|
827
|
-
node.data = value;
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
this._commitNode(document.createTextNode(typeof value === 'string' ? value : String(value)));
|
|
831
|
-
}
|
|
832
|
-
this.value = value;
|
|
833
|
-
}
|
|
834
|
-
_commitTemplateResult(value) {
|
|
835
|
-
const template = this.options.templateFactory(value);
|
|
836
|
-
if (this.value instanceof TemplateInstance &&
|
|
837
|
-
this.value.template === template) {
|
|
838
|
-
this.value.update(value.values);
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
// Make sure we propagate the template processor from the TemplateResult
|
|
842
|
-
// so that we use its syntax extension, etc. The template factory comes
|
|
843
|
-
// from the render function options so that it can control template
|
|
844
|
-
// caching and preprocessing.
|
|
845
|
-
const instance = new TemplateInstance(template, value.processor, this.options);
|
|
846
|
-
const fragment = instance._clone();
|
|
847
|
-
instance.update(value.values);
|
|
848
|
-
this._commitNode(fragment);
|
|
849
|
-
this.value = instance;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
_commitIterable(value) {
|
|
853
|
-
// For an Iterable, we create a new InstancePart per item, then set its
|
|
854
|
-
// value to the item. This is a little bit of overhead for every item in
|
|
855
|
-
// an Iterable, but it lets us recurse easily and efficiently update Arrays
|
|
856
|
-
// of TemplateResults that will be commonly returned from expressions like:
|
|
857
|
-
// array.map((i) => html`${i}`), by reusing existing TemplateInstances.
|
|
858
|
-
// If _value is an array, then the previous render was of an
|
|
859
|
-
// iterable and _value will contain the NodeParts from the previous
|
|
860
|
-
// render. If _value is not an array, clear this part and make a new
|
|
861
|
-
// array for NodeParts.
|
|
862
|
-
if (!Array.isArray(this.value)) {
|
|
863
|
-
this.value = [];
|
|
864
|
-
this.clear();
|
|
865
|
-
}
|
|
866
|
-
// Lets us keep track of how many items we stamped so we can clear leftover
|
|
867
|
-
// items from a previous render
|
|
868
|
-
const itemParts = this.value;
|
|
869
|
-
let partIndex = 0;
|
|
870
|
-
let itemPart;
|
|
871
|
-
for (const item of value) {
|
|
872
|
-
// Try to reuse an existing part
|
|
873
|
-
itemPart = itemParts[partIndex];
|
|
874
|
-
// If no existing part, create a new one
|
|
875
|
-
if (itemPart === undefined) {
|
|
876
|
-
itemPart = new NodePart(this.options);
|
|
877
|
-
itemParts.push(itemPart);
|
|
878
|
-
if (partIndex === 0) {
|
|
879
|
-
itemPart.appendIntoPart(this);
|
|
880
|
-
}
|
|
881
|
-
else {
|
|
882
|
-
itemPart.insertAfterPart(itemParts[partIndex - 1]);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
itemPart.setValue(item);
|
|
886
|
-
itemPart.commit();
|
|
887
|
-
partIndex++;
|
|
888
|
-
}
|
|
889
|
-
if (partIndex < itemParts.length) {
|
|
890
|
-
// Truncate the parts array so _value reflects the current state
|
|
891
|
-
itemParts.length = partIndex;
|
|
892
|
-
this.clear(itemPart && itemPart.endNode);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
clear(startNode = this.startNode) {
|
|
896
|
-
removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* Implements a boolean attribute, roughly as defined in the HTML
|
|
901
|
-
* specification.
|
|
902
|
-
*
|
|
903
|
-
* If the value is truthy, then the attribute is present with a value of
|
|
904
|
-
* ''. If the value is falsey, the attribute is removed.
|
|
905
|
-
*/
|
|
906
|
-
class BooleanAttributePart {
|
|
907
|
-
constructor(element, name, strings) {
|
|
908
|
-
this.value = undefined;
|
|
909
|
-
this._pendingValue = undefined;
|
|
910
|
-
if (strings.length !== 2 || strings[0] !== '' || strings[1] !== '') {
|
|
911
|
-
throw new Error('Boolean attributes can only contain a single expression');
|
|
912
|
-
}
|
|
913
|
-
this.element = element;
|
|
914
|
-
this.name = name;
|
|
915
|
-
this.strings = strings;
|
|
916
|
-
}
|
|
917
|
-
setValue(value) {
|
|
918
|
-
this._pendingValue = value;
|
|
919
|
-
}
|
|
920
|
-
commit() {
|
|
921
|
-
while (isDirective(this._pendingValue)) {
|
|
922
|
-
const directive$$1 = this._pendingValue;
|
|
923
|
-
this._pendingValue = noChange;
|
|
924
|
-
directive$$1(this);
|
|
925
|
-
}
|
|
926
|
-
if (this._pendingValue === noChange) {
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
const value = !!this._pendingValue;
|
|
930
|
-
if (this.value !== value) {
|
|
931
|
-
if (value) {
|
|
932
|
-
this.element.setAttribute(this.name, '');
|
|
933
|
-
}
|
|
934
|
-
else {
|
|
935
|
-
this.element.removeAttribute(this.name);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
this.value = value;
|
|
939
|
-
this._pendingValue = noChange;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Sets attribute values for PropertyParts, so that the value is only set once
|
|
944
|
-
* even if there are multiple parts for a property.
|
|
945
|
-
*
|
|
946
|
-
* If an expression controls the whole property value, then the value is simply
|
|
947
|
-
* assigned to the property under control. If there are string literals or
|
|
948
|
-
* multiple expressions, then the strings are expressions are interpolated into
|
|
949
|
-
* a string first.
|
|
950
|
-
*/
|
|
951
|
-
class PropertyCommitter extends AttributeCommitter {
|
|
952
|
-
constructor(element, name, strings) {
|
|
953
|
-
super(element, name, strings);
|
|
954
|
-
this.single =
|
|
955
|
-
(strings.length === 2 && strings[0] === '' && strings[1] === '');
|
|
956
|
-
}
|
|
957
|
-
_createPart() {
|
|
958
|
-
return new PropertyPart(this);
|
|
959
|
-
}
|
|
960
|
-
_getValue() {
|
|
961
|
-
if (this.single) {
|
|
962
|
-
return this.parts[0].value;
|
|
963
|
-
}
|
|
964
|
-
return super._getValue();
|
|
965
|
-
}
|
|
966
|
-
commit() {
|
|
967
|
-
if (this.dirty) {
|
|
968
|
-
this.dirty = false;
|
|
969
|
-
// tslint:disable-next-line:no-any
|
|
970
|
-
this.element[this.name] = this._getValue();
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
class PropertyPart extends AttributePart {
|
|
975
|
-
}
|
|
976
|
-
// Detect event listener options support. If the `capture` property is read
|
|
977
|
-
// from the options object, then options are supported. If not, then the thrid
|
|
978
|
-
// argument to add/removeEventListener is interpreted as the boolean capture
|
|
979
|
-
// value so we should only pass the `capture` property.
|
|
980
|
-
let eventOptionsSupported = false;
|
|
981
|
-
try {
|
|
982
|
-
const options = {
|
|
983
|
-
get capture() {
|
|
984
|
-
eventOptionsSupported = true;
|
|
985
|
-
return false;
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
// tslint:disable-next-line:no-any
|
|
989
|
-
window.addEventListener('test', options, options);
|
|
990
|
-
// tslint:disable-next-line:no-any
|
|
991
|
-
window.removeEventListener('test', options, options);
|
|
992
|
-
}
|
|
993
|
-
catch (_e) {
|
|
994
|
-
}
|
|
995
|
-
class EventPart {
|
|
996
|
-
constructor(element, eventName, eventContext) {
|
|
997
|
-
this.value = undefined;
|
|
998
|
-
this._pendingValue = undefined;
|
|
999
|
-
this.element = element;
|
|
1000
|
-
this.eventName = eventName;
|
|
1001
|
-
this.eventContext = eventContext;
|
|
1002
|
-
this._boundHandleEvent = (e) => this.handleEvent(e);
|
|
1003
|
-
}
|
|
1004
|
-
setValue(value) {
|
|
1005
|
-
this._pendingValue = value;
|
|
1006
|
-
}
|
|
1007
|
-
commit() {
|
|
1008
|
-
while (isDirective(this._pendingValue)) {
|
|
1009
|
-
const directive$$1 = this._pendingValue;
|
|
1010
|
-
this._pendingValue = noChange;
|
|
1011
|
-
directive$$1(this);
|
|
1012
|
-
}
|
|
1013
|
-
if (this._pendingValue === noChange) {
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
const newListener = this._pendingValue;
|
|
1017
|
-
const oldListener = this.value;
|
|
1018
|
-
const shouldRemoveListener = newListener == null ||
|
|
1019
|
-
oldListener != null &&
|
|
1020
|
-
(newListener.capture !== oldListener.capture ||
|
|
1021
|
-
newListener.once !== oldListener.once ||
|
|
1022
|
-
newListener.passive !== oldListener.passive);
|
|
1023
|
-
const shouldAddListener = newListener != null && (oldListener == null || shouldRemoveListener);
|
|
1024
|
-
if (shouldRemoveListener) {
|
|
1025
|
-
this.element.removeEventListener(this.eventName, this._boundHandleEvent, this._options);
|
|
1026
|
-
}
|
|
1027
|
-
if (shouldAddListener) {
|
|
1028
|
-
this._options = getOptions(newListener);
|
|
1029
|
-
this.element.addEventListener(this.eventName, this._boundHandleEvent, this._options);
|
|
1030
|
-
}
|
|
1031
|
-
this.value = newListener;
|
|
1032
|
-
this._pendingValue = noChange;
|
|
1033
|
-
}
|
|
1034
|
-
handleEvent(event) {
|
|
1035
|
-
if (typeof this.value === 'function') {
|
|
1036
|
-
this.value.call(this.eventContext || this.element, event);
|
|
1037
|
-
}
|
|
1038
|
-
else {
|
|
1039
|
-
this.value.handleEvent(event);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
// We copy options because of the inconsistent behavior of browsers when reading
|
|
1044
|
-
// the third argument of add/removeEventListener. IE11 doesn't support options
|
|
1045
|
-
// at all. Chrome 41 only reads `capture` if the argument is an object.
|
|
1046
|
-
const getOptions = (o) => o &&
|
|
1047
|
-
(eventOptionsSupported ?
|
|
1048
|
-
{ capture: o.capture, passive: o.passive, once: o.once } :
|
|
1049
|
-
o.capture);
|
|
1050
|
-
|
|
1051
|
-
/**
|
|
1052
|
-
* @license
|
|
1053
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1054
|
-
* This code may only be used under the BSD style license found at
|
|
1055
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1056
|
-
* The complete set of authors may be found at
|
|
1057
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1058
|
-
* The complete set of contributors may be found at
|
|
1059
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1060
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1061
|
-
* subject to an additional IP rights grant found at
|
|
1062
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1063
|
-
*/
|
|
1064
|
-
/**
|
|
1065
|
-
* The default TemplateFactory which caches Templates keyed on
|
|
1066
|
-
* result.type and result.strings.
|
|
1067
|
-
*/
|
|
1068
|
-
function templateFactory(result) {
|
|
1069
|
-
let templateCache = templateCaches.get(result.type);
|
|
1070
|
-
if (templateCache === undefined) {
|
|
1071
|
-
templateCache = {
|
|
1072
|
-
stringsArray: new WeakMap(),
|
|
1073
|
-
keyString: new Map()
|
|
1074
|
-
};
|
|
1075
|
-
templateCaches.set(result.type, templateCache);
|
|
1076
|
-
}
|
|
1077
|
-
let template = templateCache.stringsArray.get(result.strings);
|
|
1078
|
-
if (template !== undefined) {
|
|
1079
|
-
return template;
|
|
1080
|
-
}
|
|
1081
|
-
// If the TemplateStringsArray is new, generate a key from the strings
|
|
1082
|
-
// This key is shared between all templates with identical content
|
|
1083
|
-
const key = result.strings.join(marker);
|
|
1084
|
-
// Check if we already have a Template for this key
|
|
1085
|
-
template = templateCache.keyString.get(key);
|
|
1086
|
-
if (template === undefined) {
|
|
1087
|
-
// If we have not seen this key before, create a new Template
|
|
1088
|
-
template = new Template(result, result.getTemplateElement());
|
|
1089
|
-
// Cache the Template for this key
|
|
1090
|
-
templateCache.keyString.set(key, template);
|
|
1091
|
-
}
|
|
1092
|
-
// Cache all future queries for this TemplateStringsArray
|
|
1093
|
-
templateCache.stringsArray.set(result.strings, template);
|
|
1094
|
-
return template;
|
|
1095
|
-
}
|
|
1096
|
-
const templateCaches = new Map();
|
|
1097
|
-
|
|
1098
|
-
/**
|
|
1099
|
-
* @license
|
|
1100
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1101
|
-
* This code may only be used under the BSD style license found at
|
|
1102
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1103
|
-
* The complete set of authors may be found at
|
|
1104
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1105
|
-
* The complete set of contributors may be found at
|
|
1106
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1107
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1108
|
-
* subject to an additional IP rights grant found at
|
|
1109
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1110
|
-
*/
|
|
1111
|
-
/**
|
|
1112
|
-
* @module lit-html
|
|
1113
|
-
*/
|
|
1114
|
-
const parts = new WeakMap();
|
|
1115
|
-
/**
|
|
1116
|
-
* Renders a template to a container.
|
|
1117
|
-
*
|
|
1118
|
-
* To update a container with new values, reevaluate the template literal and
|
|
1119
|
-
* call `render` with the new result.
|
|
1120
|
-
*
|
|
1121
|
-
* @param result a TemplateResult created by evaluating a template tag like
|
|
1122
|
-
* `html` or `svg`.
|
|
1123
|
-
* @param container A DOM parent to render to. The entire contents are either
|
|
1124
|
-
* replaced, or efficiently updated if the same result type was previous
|
|
1125
|
-
* rendered there.
|
|
1126
|
-
* @param options RenderOptions for the entire render tree rendered to this
|
|
1127
|
-
* container. Render options must *not* change between renders to the same
|
|
1128
|
-
* container, as those changes will not effect previously rendered DOM.
|
|
1129
|
-
*/
|
|
1130
|
-
const render = (result, container, options) => {
|
|
1131
|
-
let part = parts.get(container);
|
|
1132
|
-
if (part === undefined) {
|
|
1133
|
-
removeNodes(container, container.firstChild);
|
|
1134
|
-
parts.set(container, part = new NodePart(Object.assign({ templateFactory }, options)));
|
|
1135
|
-
part.appendInto(container);
|
|
1136
|
-
}
|
|
1137
|
-
part.setValue(result);
|
|
1138
|
-
part.commit();
|
|
1139
|
-
};
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* @license
|
|
1143
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1144
|
-
* This code may only be used under the BSD style license found at
|
|
1145
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1146
|
-
* The complete set of authors may be found at
|
|
1147
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1148
|
-
* The complete set of contributors may be found at
|
|
1149
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1150
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1151
|
-
* subject to an additional IP rights grant found at
|
|
1152
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1153
|
-
*/
|
|
1154
|
-
/**
|
|
1155
|
-
* Creates Parts when a template is instantiated.
|
|
1156
|
-
*/
|
|
1157
|
-
class DefaultTemplateProcessor {
|
|
1158
|
-
/**
|
|
1159
|
-
* Create parts for an attribute-position binding, given the event, attribute
|
|
1160
|
-
* name, and string literals.
|
|
1161
|
-
*
|
|
1162
|
-
* @param element The element containing the binding
|
|
1163
|
-
* @param name The attribute name
|
|
1164
|
-
* @param strings The string literals. There are always at least two strings,
|
|
1165
|
-
* event for fully-controlled bindings with a single expression.
|
|
1166
|
-
*/
|
|
1167
|
-
handleAttributeExpressions(element, name, strings, options) {
|
|
1168
|
-
const prefix = name[0];
|
|
1169
|
-
if (prefix === '.') {
|
|
1170
|
-
const comitter = new PropertyCommitter(element, name.slice(1), strings);
|
|
1171
|
-
return comitter.parts;
|
|
1172
|
-
}
|
|
1173
|
-
if (prefix === '@') {
|
|
1174
|
-
return [new EventPart(element, name.slice(1), options.eventContext)];
|
|
1175
|
-
}
|
|
1176
|
-
if (prefix === '?') {
|
|
1177
|
-
return [new BooleanAttributePart(element, name.slice(1), strings)];
|
|
1178
|
-
}
|
|
1179
|
-
const comitter = new AttributeCommitter(element, name, strings);
|
|
1180
|
-
return comitter.parts;
|
|
1181
|
-
}
|
|
1182
|
-
/**
|
|
1183
|
-
* Create parts for a text-position binding.
|
|
1184
|
-
* @param templateFactory
|
|
1185
|
-
*/
|
|
1186
|
-
handleTextExpression(options) {
|
|
1187
|
-
return new NodePart(options);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
const defaultTemplateProcessor = new DefaultTemplateProcessor();
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* @license
|
|
1194
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1195
|
-
* This code may only be used under the BSD style license found at
|
|
1196
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1197
|
-
* The complete set of authors may be found at
|
|
1198
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1199
|
-
* The complete set of contributors may be found at
|
|
1200
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1201
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1202
|
-
* subject to an additional IP rights grant found at
|
|
1203
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1204
|
-
*/
|
|
1205
|
-
/**
|
|
1206
|
-
*
|
|
1207
|
-
* Main lit-html module.
|
|
1208
|
-
*
|
|
1209
|
-
* Main exports:
|
|
1210
|
-
*
|
|
1211
|
-
* - [[html]]
|
|
1212
|
-
* - [[svg]]
|
|
1213
|
-
* - [[render]]
|
|
1214
|
-
*
|
|
1215
|
-
* @module lit-html
|
|
1216
|
-
* @preferred
|
|
1217
|
-
*/
|
|
1218
|
-
/**
|
|
1219
|
-
* Do not remove this comment; it keeps typedoc from misplacing the module
|
|
1220
|
-
* docs.
|
|
1221
|
-
*/
|
|
1222
|
-
// IMPORTANT: do not change the property name or the assignment expression.
|
|
1223
|
-
// This line will be used in regexes to search for lit-html usage.
|
|
1224
|
-
// TODO(justinfagnani): inject version number at build time
|
|
1225
|
-
(window['litHtmlVersions'] || (window['litHtmlVersions'] = [])).push('1.0.0');
|
|
1226
|
-
/**
|
|
1227
|
-
* Interprets a template literal as an HTML template that can efficiently
|
|
1228
|
-
* render to and update a container.
|
|
1229
|
-
*/
|
|
1230
|
-
const html = (strings, ...values) => new TemplateResult(strings, values, 'html', defaultTemplateProcessor);
|
|
1231
|
-
/**
|
|
1232
|
-
* Interprets a template literal as an SVG template that can efficiently
|
|
1233
|
-
* render to and update a container.
|
|
1234
|
-
*/
|
|
1235
|
-
|
|
1236
|
-
/**
|
|
1237
|
-
* @license
|
|
1238
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1239
|
-
* This code may only be used under the BSD style license found at
|
|
1240
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1241
|
-
* The complete set of authors may be found at
|
|
1242
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1243
|
-
* The complete set of contributors may be found at
|
|
1244
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1245
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1246
|
-
* subject to an additional IP rights grant found at
|
|
1247
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1248
|
-
*/
|
|
1249
|
-
/**
|
|
1250
|
-
* Module to add shady DOM/shady CSS polyfill support to lit-html template
|
|
1251
|
-
* rendering. See the [[render]] method for details.
|
|
1252
|
-
*
|
|
1253
|
-
* @module shady-render
|
|
1254
|
-
* @preferred
|
|
1255
|
-
*/
|
|
1256
|
-
/**
|
|
1257
|
-
* Do not remove this comment; it keeps typedoc from misplacing the module
|
|
1258
|
-
* docs.
|
|
1259
|
-
*/
|
|
1260
|
-
// Get a key to lookup in `templateCaches`.
|
|
1261
|
-
const getTemplateCacheKey = (type, scopeName) => `${type}--${scopeName}`;
|
|
1262
|
-
let compatibleShadyCSSVersion = true;
|
|
1263
|
-
if (typeof window.ShadyCSS === 'undefined') {
|
|
1264
|
-
compatibleShadyCSSVersion = false;
|
|
1265
|
-
}
|
|
1266
|
-
else if (typeof window.ShadyCSS.prepareTemplateDom === 'undefined') {
|
|
1267
|
-
console.warn(`Incompatible ShadyCSS version detected.` +
|
|
1268
|
-
`Please update to at least @webcomponents/webcomponentsjs@2.0.2 and` +
|
|
1269
|
-
`@webcomponents/shadycss@1.3.1.`);
|
|
1270
|
-
compatibleShadyCSSVersion = false;
|
|
1271
|
-
}
|
|
1272
|
-
/**
|
|
1273
|
-
* Template factory which scopes template DOM using ShadyCSS.
|
|
1274
|
-
* @param scopeName {string}
|
|
1275
|
-
*/
|
|
1276
|
-
const shadyTemplateFactory = (scopeName) => (result) => {
|
|
1277
|
-
const cacheKey = getTemplateCacheKey(result.type, scopeName);
|
|
1278
|
-
let templateCache = templateCaches.get(cacheKey);
|
|
1279
|
-
if (templateCache === undefined) {
|
|
1280
|
-
templateCache = {
|
|
1281
|
-
stringsArray: new WeakMap(),
|
|
1282
|
-
keyString: new Map()
|
|
1283
|
-
};
|
|
1284
|
-
templateCaches.set(cacheKey, templateCache);
|
|
1285
|
-
}
|
|
1286
|
-
let template = templateCache.stringsArray.get(result.strings);
|
|
1287
|
-
if (template !== undefined) {
|
|
1288
|
-
return template;
|
|
1289
|
-
}
|
|
1290
|
-
const key = result.strings.join(marker);
|
|
1291
|
-
template = templateCache.keyString.get(key);
|
|
1292
|
-
if (template === undefined) {
|
|
1293
|
-
const element = result.getTemplateElement();
|
|
1294
|
-
if (compatibleShadyCSSVersion) {
|
|
1295
|
-
window.ShadyCSS.prepareTemplateDom(element, scopeName);
|
|
1296
|
-
}
|
|
1297
|
-
template = new Template(result, element);
|
|
1298
|
-
templateCache.keyString.set(key, template);
|
|
1299
|
-
}
|
|
1300
|
-
templateCache.stringsArray.set(result.strings, template);
|
|
1301
|
-
return template;
|
|
1302
|
-
};
|
|
1303
|
-
const TEMPLATE_TYPES = ['html', 'svg'];
|
|
1304
|
-
/**
|
|
1305
|
-
* Removes all style elements from Templates for the given scopeName.
|
|
1306
|
-
*/
|
|
1307
|
-
const removeStylesFromLitTemplates = (scopeName) => {
|
|
1308
|
-
TEMPLATE_TYPES.forEach((type) => {
|
|
1309
|
-
const templates = templateCaches.get(getTemplateCacheKey(type, scopeName));
|
|
1310
|
-
if (templates !== undefined) {
|
|
1311
|
-
templates.keyString.forEach((template) => {
|
|
1312
|
-
const { element: { content } } = template;
|
|
1313
|
-
// IE 11 doesn't support the iterable param Set constructor
|
|
1314
|
-
const styles = new Set();
|
|
1315
|
-
Array.from(content.querySelectorAll('style')).forEach((s) => {
|
|
1316
|
-
styles.add(s);
|
|
1317
|
-
});
|
|
1318
|
-
removeNodesFromTemplate(template, styles);
|
|
1319
|
-
});
|
|
1320
|
-
}
|
|
1321
|
-
});
|
|
1322
|
-
};
|
|
1323
|
-
const shadyRenderSet = new Set();
|
|
1324
|
-
/**
|
|
1325
|
-
* For the given scope name, ensures that ShadyCSS style scoping is performed.
|
|
1326
|
-
* This is done just once per scope name so the fragment and template cannot
|
|
1327
|
-
* be modified.
|
|
1328
|
-
* (1) extracts styles from the rendered fragment and hands them to ShadyCSS
|
|
1329
|
-
* to be scoped and appended to the document
|
|
1330
|
-
* (2) removes style elements from all lit-html Templates for this scope name.
|
|
1331
|
-
*
|
|
1332
|
-
* Note, <style> elements can only be placed into templates for the
|
|
1333
|
-
* initial rendering of the scope. If <style> elements are included in templates
|
|
1334
|
-
* dynamically rendered to the scope (after the first scope render), they will
|
|
1335
|
-
* not be scoped and the <style> will be left in the template and rendered
|
|
1336
|
-
* output.
|
|
1337
|
-
*/
|
|
1338
|
-
const prepareTemplateStyles = (renderedDOM, template, scopeName) => {
|
|
1339
|
-
shadyRenderSet.add(scopeName);
|
|
1340
|
-
// Move styles out of rendered DOM and store.
|
|
1341
|
-
const styles = renderedDOM.querySelectorAll('style');
|
|
1342
|
-
// If there are no styles, skip unnecessary work
|
|
1343
|
-
if (styles.length === 0) {
|
|
1344
|
-
// Ensure prepareTemplateStyles is called to support adding
|
|
1345
|
-
// styles via `prepareAdoptedCssText` since that requires that
|
|
1346
|
-
// `prepareTemplateStyles` is called.
|
|
1347
|
-
window.ShadyCSS.prepareTemplateStyles(template.element, scopeName);
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
const condensedStyle = document.createElement('style');
|
|
1351
|
-
// Collect styles into a single style. This helps us make sure ShadyCSS
|
|
1352
|
-
// manipulations will not prevent us from being able to fix up template
|
|
1353
|
-
// part indices.
|
|
1354
|
-
// NOTE: collecting styles is inefficient for browsers but ShadyCSS
|
|
1355
|
-
// currently does this anyway. When it does not, this should be changed.
|
|
1356
|
-
for (let i = 0; i < styles.length; i++) {
|
|
1357
|
-
const style = styles[i];
|
|
1358
|
-
style.parentNode.removeChild(style);
|
|
1359
|
-
condensedStyle.textContent += style.textContent;
|
|
1360
|
-
}
|
|
1361
|
-
// Remove styles from nested templates in this scope.
|
|
1362
|
-
removeStylesFromLitTemplates(scopeName);
|
|
1363
|
-
// And then put the condensed style into the "root" template passed in as
|
|
1364
|
-
// `template`.
|
|
1365
|
-
insertNodeIntoTemplate(template, condensedStyle, template.element.content.firstChild);
|
|
1366
|
-
// Note, it's important that ShadyCSS gets the template that `lit-html`
|
|
1367
|
-
// will actually render so that it can update the style inside when
|
|
1368
|
-
// needed (e.g. @apply native Shadow DOM case).
|
|
1369
|
-
window.ShadyCSS.prepareTemplateStyles(template.element, scopeName);
|
|
1370
|
-
if (window.ShadyCSS.nativeShadow) {
|
|
1371
|
-
// When in native Shadow DOM, re-add styling to rendered content using
|
|
1372
|
-
// the style ShadyCSS produced.
|
|
1373
|
-
const style = template.element.content.querySelector('style');
|
|
1374
|
-
renderedDOM.insertBefore(style.cloneNode(true), renderedDOM.firstChild);
|
|
1375
|
-
}
|
|
1376
|
-
else {
|
|
1377
|
-
// When not in native Shadow DOM, at this point ShadyCSS will have
|
|
1378
|
-
// removed the style from the lit template and parts will be broken as a
|
|
1379
|
-
// result. To fix this, we put back the style node ShadyCSS removed
|
|
1380
|
-
// and then tell lit to remove that node from the template.
|
|
1381
|
-
// NOTE, ShadyCSS creates its own style so we can safely add/remove
|
|
1382
|
-
// `condensedStyle` here.
|
|
1383
|
-
template.element.content.insertBefore(condensedStyle, template.element.content.firstChild);
|
|
1384
|
-
const removes = new Set();
|
|
1385
|
-
removes.add(condensedStyle);
|
|
1386
|
-
removeNodesFromTemplate(template, removes);
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
/**
|
|
1390
|
-
* Extension to the standard `render` method which supports rendering
|
|
1391
|
-
* to ShadowRoots when the ShadyDOM (https://github.com/webcomponents/shadydom)
|
|
1392
|
-
* and ShadyCSS (https://github.com/webcomponents/shadycss) polyfills are used
|
|
1393
|
-
* or when the webcomponentsjs
|
|
1394
|
-
* (https://github.com/webcomponents/webcomponentsjs) polyfill is used.
|
|
1395
|
-
*
|
|
1396
|
-
* Adds a `scopeName` option which is used to scope element DOM and stylesheets
|
|
1397
|
-
* when native ShadowDOM is unavailable. The `scopeName` will be added to
|
|
1398
|
-
* the class attribute of all rendered DOM. In addition, any style elements will
|
|
1399
|
-
* be automatically re-written with this `scopeName` selector and moved out
|
|
1400
|
-
* of the rendered DOM and into the document `<head>`.
|
|
1401
|
-
*
|
|
1402
|
-
* It is common to use this render method in conjunction with a custom element
|
|
1403
|
-
* which renders a shadowRoot. When this is done, typically the element's
|
|
1404
|
-
* `localName` should be used as the `scopeName`.
|
|
1405
|
-
*
|
|
1406
|
-
* In addition to DOM scoping, ShadyCSS also supports a basic shim for css
|
|
1407
|
-
* custom properties (needed only on older browsers like IE11) and a shim for
|
|
1408
|
-
* a deprecated feature called `@apply` that supports applying a set of css
|
|
1409
|
-
* custom properties to a given location.
|
|
1410
|
-
*
|
|
1411
|
-
* Usage considerations:
|
|
1412
|
-
*
|
|
1413
|
-
* * Part values in `<style>` elements are only applied the first time a given
|
|
1414
|
-
* `scopeName` renders. Subsequent changes to parts in style elements will have
|
|
1415
|
-
* no effect. Because of this, parts in style elements should only be used for
|
|
1416
|
-
* values that will never change, for example parts that set scope-wide theme
|
|
1417
|
-
* values or parts which render shared style elements.
|
|
1418
|
-
*
|
|
1419
|
-
* * Note, due to a limitation of the ShadyDOM polyfill, rendering in a
|
|
1420
|
-
* custom element's `constructor` is not supported. Instead rendering should
|
|
1421
|
-
* either done asynchronously, for example at microtask timing (for example
|
|
1422
|
-
* `Promise.resolve()`), or be deferred until the first time the element's
|
|
1423
|
-
* `connectedCallback` runs.
|
|
1424
|
-
*
|
|
1425
|
-
* Usage considerations when using shimmed custom properties or `@apply`:
|
|
1426
|
-
*
|
|
1427
|
-
* * Whenever any dynamic changes are made which affect
|
|
1428
|
-
* css custom properties, `ShadyCSS.styleElement(element)` must be called
|
|
1429
|
-
* to update the element. There are two cases when this is needed:
|
|
1430
|
-
* (1) the element is connected to a new parent, (2) a class is added to the
|
|
1431
|
-
* element that causes it to match different custom properties.
|
|
1432
|
-
* To address the first case when rendering a custom element, `styleElement`
|
|
1433
|
-
* should be called in the element's `connectedCallback`.
|
|
1434
|
-
*
|
|
1435
|
-
* * Shimmed custom properties may only be defined either for an entire
|
|
1436
|
-
* shadowRoot (for example, in a `:host` rule) or via a rule that directly
|
|
1437
|
-
* matches an element with a shadowRoot. In other words, instead of flowing from
|
|
1438
|
-
* parent to child as do native css custom properties, shimmed custom properties
|
|
1439
|
-
* flow only from shadowRoots to nested shadowRoots.
|
|
1440
|
-
*
|
|
1441
|
-
* * When using `@apply` mixing css shorthand property names with
|
|
1442
|
-
* non-shorthand names (for example `border` and `border-width`) is not
|
|
1443
|
-
* supported.
|
|
1444
|
-
*/
|
|
1445
|
-
const render$1 = (result, container, options) => {
|
|
1446
|
-
const scopeName = options.scopeName;
|
|
1447
|
-
const hasRendered = parts.has(container);
|
|
1448
|
-
const needsScoping = container instanceof ShadowRoot &&
|
|
1449
|
-
compatibleShadyCSSVersion && result instanceof TemplateResult;
|
|
1450
|
-
// Handle first render to a scope specially...
|
|
1451
|
-
const firstScopeRender = needsScoping && !shadyRenderSet.has(scopeName);
|
|
1452
|
-
// On first scope render, render into a fragment; this cannot be a single
|
|
1453
|
-
// fragment that is reused since nested renders can occur synchronously.
|
|
1454
|
-
const renderContainer = firstScopeRender ? document.createDocumentFragment() : container;
|
|
1455
|
-
render(result, renderContainer, Object.assign({ templateFactory: shadyTemplateFactory(scopeName) }, options));
|
|
1456
|
-
// When performing first scope render,
|
|
1457
|
-
// (1) We've rendered into a fragment so that there's a chance to
|
|
1458
|
-
// `prepareTemplateStyles` before sub-elements hit the DOM
|
|
1459
|
-
// (which might cause them to render based on a common pattern of
|
|
1460
|
-
// rendering in a custom element's `connectedCallback`);
|
|
1461
|
-
// (2) Scope the template with ShadyCSS one time only for this scope.
|
|
1462
|
-
// (3) Render the fragment into the container and make sure the
|
|
1463
|
-
// container knows its `part` is the one we just rendered. This ensures
|
|
1464
|
-
// DOM will be re-used on subsequent renders.
|
|
1465
|
-
if (firstScopeRender) {
|
|
1466
|
-
const part = parts.get(renderContainer);
|
|
1467
|
-
parts.delete(renderContainer);
|
|
1468
|
-
if (part.value instanceof TemplateInstance) {
|
|
1469
|
-
prepareTemplateStyles(renderContainer, part.value.template, scopeName);
|
|
1470
|
-
}
|
|
1471
|
-
removeNodes(container, container.firstChild);
|
|
1472
|
-
container.appendChild(renderContainer);
|
|
1473
|
-
parts.set(container, part);
|
|
1474
|
-
}
|
|
1475
|
-
// After elements have hit the DOM, update styling if this is the
|
|
1476
|
-
// initial render to this container.
|
|
1477
|
-
// This is needed whenever dynamic changes are made so it would be
|
|
1478
|
-
// safest to do every render; however, this would regress performance
|
|
1479
|
-
// so we leave it up to the user to call `ShadyCSSS.styleElement`
|
|
1480
|
-
// for dynamic changes.
|
|
1481
|
-
if (!hasRendered && needsScoping) {
|
|
1482
|
-
window.ShadyCSS.styleElement(container.host);
|
|
1483
|
-
}
|
|
1484
|
-
};
|
|
1485
|
-
|
|
1486
|
-
// Key to store the HTML tag in a custom element class
|
|
1487
|
-
const TAG = Symbol('tag');
|
|
1488
|
-
|
|
1489
|
-
// Key to store render status in a custom element instance
|
|
1490
|
-
const NEEDSRENDER = Symbol('needsRender');
|
|
1491
|
-
|
|
1492
|
-
// Transforms a camelCased string into a kebab-cased string
|
|
1493
|
-
const camelToKebab = camel => camel.replace(/([a-z](?=[A-Z]))|([A-Z](?=[A-Z][a-z]))/g, '$1$2-').toLowerCase();
|
|
1494
|
-
|
|
1495
|
-
// Creates an ID cache in the `$` property of a custom element instance
|
|
1496
|
-
const createIdCache = element => {
|
|
1497
|
-
element.$ = {};
|
|
1498
|
-
element.renderRoot.querySelectorAll('[id]').forEach(node => {
|
|
1499
|
-
element.$[node.id] = node;
|
|
1500
|
-
});
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
|
-
/**
|
|
1504
|
-
* A lightweight base class for custom elements
|
|
1505
|
-
*
|
|
1506
|
-
* Features:
|
|
1507
|
-
*
|
|
1508
|
-
* - Determines an appropriate HTML tagname based on an element's class name
|
|
1509
|
-
* - Efficient rendering engine using lit-html (https://github.com/Polymer/lit-html)
|
|
1510
|
-
* - Creates a cache for descendant nodes with an `id` in the `$` property
|
|
1511
|
-
*/
|
|
1512
|
-
class GluonElement extends HTMLElement {
|
|
1513
|
-
constructor() {
|
|
1514
|
-
super();
|
|
1515
|
-
this.renderRoot = this.createRenderRoot();
|
|
1516
|
-
|
|
1517
|
-
// This ensures that any properties that are set prior to upgrading this element
|
|
1518
|
-
// have their instance setters called
|
|
1519
|
-
Object.getOwnPropertyNames(this).forEach(property => {
|
|
1520
|
-
const propertyValue = this[property];
|
|
1521
|
-
delete this[property];
|
|
1522
|
-
this[property] = propertyValue;
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
/**
|
|
1527
|
-
* Returns an open shadowRoot as the default rendering root
|
|
1528
|
-
*
|
|
1529
|
-
* Override this method to provide an alternative rendering root
|
|
1530
|
-
* For example, return `this` to render the template as childNodes
|
|
1531
|
-
*/
|
|
1532
|
-
createRenderRoot() {
|
|
1533
|
-
return this.attachShadow({ mode: 'open' });
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
/**
|
|
1537
|
-
* Returns the HTML tagname for elements of this class
|
|
1538
|
-
*
|
|
1539
|
-
* It defaults to the kebab-cased version of the class name. To override,
|
|
1540
|
-
* defined a `static get is()` property on your custom element class, and return
|
|
1541
|
-
* whatever string you want to use for the HTML tagname
|
|
1542
|
-
*/
|
|
1543
|
-
static get is() {
|
|
1544
|
-
return (this.hasOwnProperty(TAG) && this[TAG]) || (this[TAG] = camelToKebab(this.name));
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
/**
|
|
1548
|
-
* Called when an element is connected to the DOM
|
|
1549
|
-
*
|
|
1550
|
-
* When an element has a `template`, attach a shadowRoot to the element,
|
|
1551
|
-
* and render the template. Once the template is rendered, creates an ID cache
|
|
1552
|
-
* in the `$` property
|
|
1553
|
-
*
|
|
1554
|
-
* When adding a `connectedCallback` to your custom element, you should call
|
|
1555
|
-
* `super.connectedCallback()` before doing anything other than actions
|
|
1556
|
-
* that alter the result of the template rendering.
|
|
1557
|
-
*/
|
|
1558
|
-
connectedCallback() {
|
|
1559
|
-
if ('template' in this) {
|
|
1560
|
-
this.render({ sync: true });
|
|
1561
|
-
createIdCache(this);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
/**
|
|
1566
|
-
* Renders the template for this element into the shadowRoot
|
|
1567
|
-
*
|
|
1568
|
-
* @param { sync }: perform a synchronous (blocking) render. The default render
|
|
1569
|
-
* is asynchronous, and multiple calls to `render()` are batched by default
|
|
1570
|
-
*
|
|
1571
|
-
* @returns a Promise that resolves once template has been rendered
|
|
1572
|
-
*/
|
|
1573
|
-
async render({ sync = false } = {}) {
|
|
1574
|
-
this[NEEDSRENDER] = true;
|
|
1575
|
-
if (!sync) {
|
|
1576
|
-
await 0;
|
|
1577
|
-
}
|
|
1578
|
-
if (this[NEEDSRENDER]) {
|
|
1579
|
-
this[NEEDSRENDER] = false;
|
|
1580
|
-
render$1(this.template, this.renderRoot, { scopeName: this.constructor.is, eventContext: this });
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
exports.GluonElement = GluonElement;
|
|
1586
|
-
exports.html = html;
|
|
1587
|
-
|
|
1588
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1589
|
-
|
|
1590
|
-
})));
|
|
1591
|
-
//# sourceMappingURL=gluon.umd.js.map
|