UNPKG

63.2 kBJavaScriptView Raw
1/**
2 * @preserve
3 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0.
5 */
6(function (global, factory) {
7 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8 typeof define === 'function' && define.amd ? define(['exports'], factory) :
9 (global = global || self, factory(global.IncrementalDOM = {}));
10}(this, function (exports) {
11 'use strict';
12 /**
13 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 * http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS-IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 */
27 /**
28 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
29 *
30 * Licensed under the Apache License, Version 2.0 (the "License");
31 * you may not use this file except in compliance with the License.
32 * You may obtain a copy of the License at
33 *
34 * http://www.apache.org/licenses/LICENSE-2.0
35 *
36 * Unless required by applicable law or agreed to in writing, software
37 * distributed under the License is distributed on an "AS-IS" BASIS,
38 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39 * See the License for the specific language governing permissions and
40 * limitations under the License.
41 */
42 /**
43 * The name of the HTML attribute that holds the element key
44 * (e.g. `<div key="foo">`). The attribute value, if it exists, is then used
45 * as the default key when importing an element.
46 * If null, no attribute value is used as the default key.
47 */
48 var keyAttributeName = "key";
49 function getKeyAttributeName() {
50 return keyAttributeName;
51 }
52 function setKeyAttributeName(name) {
53 keyAttributeName = name;
54 }
55 /**
56 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
57 *
58 * Licensed under the Apache License, Version 2.0 (the "License");
59 * you may not use this file except in compliance with the License.
60 * You may obtain a copy of the License at
61 *
62 * http://www.apache.org/licenses/LICENSE-2.0
63 *
64 * Unless required by applicable law or agreed to in writing, software
65 * distributed under the License is distributed on an "AS-IS" BASIS,
66 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
67 * See the License for the specific language governing permissions and
68 * limitations under the License.
69 */
70 /**
71 * Keeps track whether or not we are in an attributes declaration (after
72 * elementOpenStart, but before elementOpenEnd).
73 */
74 var inAttributes = false;
75 /**
76 * Keeps track whether or not we are in an element that should not have its
77 * children cleared.
78 */
79 var inSkip = false;
80 /**
81 * Keeps track of whether or not we are in a patch.
82 */
83 var inPatch = false;
84 /**
85 * Asserts that a value exists and is not null or undefined. goog.asserts
86 * is not used in order to avoid dependencies on external code.
87 * @param val The value to assert is truthy.
88 * @returns The value.
89 */
90 function assert(val) {
91 if (!val) {
92 throw new Error("Expected value to be defined");
93 }
94 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
95 return val;
96 }
97 /**
98 * Makes sure that there is a current patch context.
99 * @param functionName The name of the caller, for the error message.
100 */
101 function assertInPatch(functionName) {
102 if (!inPatch) {
103 throw new Error("Cannot call " + functionName + "() unless in patch.");
104 }
105 }
106 /**
107 * Makes sure that a patch closes every node that it opened.
108 * @param openElement
109 * @param root
110 */
111 function assertNoUnclosedTags(openElement, root) {
112 if (openElement === root) {
113 return;
114 }
115 var currentElement = openElement;
116 var openTags = [];
117 while (currentElement && currentElement !== root) {
118 openTags.push(currentElement.nodeName.toLowerCase());
119 currentElement = currentElement.parentNode;
120 }
121 throw new Error("One or more tags were not closed:\n" + openTags.join("\n"));
122 }
123 /**
124 * Makes sure that node being outer patched has a parent node.
125 * @param parent
126 */
127 function assertPatchOuterHasParentNode(parent) {
128 if (!parent) {
129 console.warn("patchOuter requires the node have a parent if there is a key.");
130 }
131 }
132 /**
133 * Makes sure that the caller is not where attributes are expected.
134 * @param functionName The name of the caller, for the error message.
135 */
136 function assertNotInAttributes(functionName) {
137 if (inAttributes) {
138 throw new Error(functionName +
139 "() can not be called between " +
140 "elementOpenStart() and elementOpenEnd().");
141 }
142 }
143 /**
144 * Makes sure that the caller is not inside an element that has declared skip.
145 * @param functionName The name of the caller, for the error message.
146 */
147 function assertNotInSkip(functionName) {
148 if (inSkip) {
149 throw new Error(functionName +
150 "() may not be called inside an element " +
151 "that has called skip().");
152 }
153 }
154 /**
155 * Makes sure that the caller is where attributes are expected.
156 * @param functionName The name of the caller, for the error message.
157 */
158 function assertInAttributes(functionName) {
159 if (!inAttributes) {
160 throw new Error(functionName +
161 "() can only be called after calling " +
162 "elementOpenStart().");
163 }
164 }
165 /**
166 * Makes sure the patch closes virtual attributes call
167 */
168 function assertVirtualAttributesClosed() {
169 if (inAttributes) {
170 throw new Error("elementOpenEnd() must be called after calling " + "elementOpenStart().");
171 }
172 }
173 /**
174 * Makes sure that tags are correctly nested.
175 * @param currentNameOrCtor
176 * @param nameOrCtor
177 */
178 function assertCloseMatchesOpenTag(currentNameOrCtor, nameOrCtor) {
179 if (currentNameOrCtor !== nameOrCtor) {
180 throw new Error('Received a call to close "' +
181 nameOrCtor +
182 '" but "' +
183 currentNameOrCtor +
184 '" was open.');
185 }
186 }
187 /**
188 * Makes sure that no children elements have been declared yet in the current
189 * element.
190 * @param functionName The name of the caller, for the error message.
191 * @param previousNode
192 */
193 function assertNoChildrenDeclaredYet(functionName, previousNode) {
194 if (previousNode !== null) {
195 throw new Error(functionName +
196 "() must come before any child " +
197 "declarations inside the current element.");
198 }
199 }
200 /**
201 * Checks that a call to patchOuter actually patched the element.
202 * @param maybeStartNode The value for the currentNode when the patch
203 * started.
204 * @param maybeCurrentNode The currentNode when the patch finished.
205 * @param expectedNextNode The Node that is expected to follow the
206 * currentNode after the patch;
207 * @param expectedPrevNode The Node that is expected to preceed the
208 * currentNode after the patch.
209 */
210 function assertPatchElementNoExtras(maybeStartNode, maybeCurrentNode, expectedNextNode, expectedPrevNode) {
211 var startNode = assert(maybeStartNode);
212 var currentNode = assert(maybeCurrentNode);
213 var wasUpdated = currentNode.nextSibling === expectedNextNode &&
214 currentNode.previousSibling === expectedPrevNode;
215 var wasChanged = currentNode.nextSibling === startNode.nextSibling &&
216 currentNode.previousSibling === expectedPrevNode;
217 var wasRemoved = currentNode === startNode;
218 if (!wasUpdated && !wasChanged && !wasRemoved) {
219 throw new Error("There must be exactly one top level call corresponding " +
220 "to the patched element.");
221 }
222 }
223 /**
224 * @param newContext The current patch context.
225 */
226 function updatePatchContext(newContext) {
227 inPatch = newContext != null;
228 }
229 /**
230 * Updates the state of being in an attribute declaration.
231 * @param value Whether or not the patch is in an attribute declaration.
232 * @return the previous value.
233 */
234 function setInAttributes(value) {
235 var previous = inAttributes;
236 inAttributes = value;
237 return previous;
238 }
239 /**
240 * Updates the state of being in a skip element.
241 * @param value Whether or not the patch is skipping the children of a
242 * parent node.
243 * @return the previous value.
244 */
245 function setInSkip(value) {
246 var previous = inSkip;
247 inSkip = value;
248 return previous;
249 }
250 /**
251 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
252 *
253 * Licensed under the Apache License, Version 2.0 (the "License");
254 * you may not use this file except in compliance with the License.
255 * You may obtain a copy of the License at
256 *
257 * http://www.apache.org/licenses/LICENSE-2.0
258 *
259 * Unless required by applicable law or agreed to in writing, software
260 * distributed under the License is distributed on an "AS-IS" BASIS,
261 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
262 * See the License for the specific language governing permissions and
263 * limitations under the License.
264 */
265 /**
266 * A cached reference to the hasOwnProperty function.
267 */
268 var hasOwnProperty = Object.prototype.hasOwnProperty;
269 /**
270 * A constructor function that will create blank objects.
271 */
272 function Blank() { }
273 Blank.prototype = Object.create(null);
274 /**
275 * Used to prevent property collisions between our "map" and its prototype.
276 * @param map The map to check.
277 * @param property The property to check.
278 * @return Whether map has property.
279 */
280 function has(map, property) {
281 return hasOwnProperty.call(map, property);
282 }
283 /**
284 * Creates an map object without a prototype.
285 * @returns An Object that can be used as a map.
286 */
287 function createMap() {
288 return new Blank();
289 }
290 /**
291 * Truncates an array, removing items up until length.
292 * @param arr The array to truncate.
293 * @param length The new length of the array.
294 */
295 function truncateArray(arr, length) {
296 while (arr.length > length) {
297 arr.pop();
298 }
299 }
300 /**
301 * Creates an array for a desired initial size. Note that the array will still
302 * be empty.
303 * @param initialAllocationSize The initial size to allocate.
304 * @returns An empty array, with an initial allocation for the desired size.
305 */
306 function createArray(initialAllocationSize) {
307 var arr = new Array(initialAllocationSize);
308 truncateArray(arr, 0);
309 return arr;
310 }
311 /**
312 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
313 *
314 * Licensed under the Apache License, Version 2.0 (the "License");
315 * you may not use this file except in compliance with the License.
316 * You may obtain a copy of the License at
317 *
318 * http://www.apache.org/licenses/LICENSE-2.0
319 *
320 * Unless required by applicable law or agreed to in writing, software
321 * distributed under the License is distributed on an "AS-IS" BASIS,
322 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
323 * See the License for the specific language governing permissions and
324 * limitations under the License.
325 */
326 var symbols = {
327 default: "__default"
328 };
329 /**
330 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
331 *
332 * Licensed under the Apache License, Version 2.0 (the "License");
333 * you may not use this file except in compliance with the License.
334 * You may obtain a copy of the License at
335 *
336 * http://www.apache.org/licenses/LICENSE-2.0
337 *
338 * Unless required by applicable law or agreed to in writing, software
339 * distributed under the License is distributed on an "AS-IS" BASIS,
340 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
341 * See the License for the specific language governing permissions and
342 * limitations under the License.
343 */
344 /**
345 * @param name The name of the attribute. For example "tabindex" or
346 * "xlink:href".
347 * @returns The namespace to use for the attribute, or null if there is
348 * no namespace.
349 */
350 function getNamespace(name) {
351 if (name.lastIndexOf("xml:", 0) === 0) {
352 return "http://www.w3.org/XML/1998/namespace";
353 }
354 if (name.lastIndexOf("xlink:", 0) === 0) {
355 return "http://www.w3.org/1999/xlink";
356 }
357 return null;
358 }
359 /**
360 * Applies an attribute or property to a given Element. If the value is null
361 * or undefined, it is removed from the Element. Otherwise, the value is set
362 * as an attribute.
363 * @param el The element to apply the attribute to.
364 * @param name The attribute's name.
365 * @param value The attribute's value.
366 */
367 function applyAttr(el, name, value) {
368 if (value == null) {
369 el.removeAttribute(name);
370 }
371 else {
372 var attrNS = getNamespace(name);
373 if (attrNS) {
374 el.setAttributeNS(attrNS, name, String(value));
375 }
376 else {
377 el.setAttribute(name, String(value));
378 }
379 }
380 }
381 /**
382 * Applies a property to a given Element.
383 * @param el The element to apply the property to.
384 * @param name The property's name.
385 * @param value The property's value.
386 */
387 function applyProp(el, name, value) {
388 el[name] = value;
389 }
390 /**
391 * Applies a value to a style declaration. Supports CSS custom properties by
392 * setting properties containing a dash using CSSStyleDeclaration.setProperty.
393 * @param style A style declaration.
394 * @param prop The property to apply. This can be either camelcase or dash
395 * separated. For example: "backgroundColor" and "background-color" are both
396 * supported.
397 * @param value The value of the property.
398 */
399 function setStyleValue(style, prop, value) {
400 if (prop.indexOf("-") >= 0) {
401 style.setProperty(prop, value);
402 }
403 else {
404 style[prop] = value;
405 }
406 }
407 /**
408 * Applies a style to an Element. No vendor prefix expansion is done for
409 * property names/values.
410 * @param el The Element to apply the style for.
411 * @param name The attribute's name.
412 * @param style The style to set. Either a string of css or an object
413 * containing property-value pairs.
414 */
415 function applyStyle(el, name, style) {
416 // MathML elements inherit from Element, which does not have style. We cannot
417 // do `instanceof HTMLElement` / `instanceof SVGElement`, since el can belong
418 // to a different document, so just check that it has a style.
419 assert("style" in el);
420 var elStyle = el.style;
421 if (typeof style === "string") {
422 elStyle.cssText = style;
423 }
424 else {
425 elStyle.cssText = "";
426 for (var prop in style) {
427 if (has(style, prop)) {
428 setStyleValue(elStyle, prop, style[prop]);
429 }
430 }
431 }
432 }
433 /**
434 * Updates a single attribute on an Element.
435 * @param el The Element to apply the attribute to.
436 * @param name The attribute's name.
437 * @param value The attribute's value. If the value is an object or
438 * function it is set on the Element, otherwise, it is set as an HTML
439 * attribute.
440 */
441 function applyAttributeTyped(el, name, value) {
442 var type = typeof value;
443 if (type === "object" || type === "function") {
444 applyProp(el, name, value);
445 }
446 else {
447 applyAttr(el, name, value);
448 }
449 }
450 /**
451 * A publicly mutable object to provide custom mutators for attributes.
452 * NB: The result of createMap() has to be recast since closure compiler
453 * will just assume attributes is "any" otherwise and throws away
454 * the type annotation set by tsickle.
455 */
456 var attributes = createMap();
457 // Special generic mutator that's called for any attribute that does not
458 // have a specific mutator.
459 attributes[symbols.default] = applyAttributeTyped;
460 attributes["style"] = applyStyle;
461 /**
462 * Calls the appropriate attribute mutator for this attribute.
463 * @param el The Element to apply the attribute to.
464 * @param name The attribute's name.
465 * @param value The attribute's value. If the value is an object or
466 * function it is set on the Element, otherwise, it is set as an HTML
467 * attribute.
468 */
469 function updateAttribute(el, name, value) {
470 var mutator = attributes[name] || attributes[symbols.default];
471 mutator(el, name, value);
472 }
473 /**
474 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
475 *
476 * Licensed under the Apache License, Version 2.0 (the "License");
477 * you may not use this file except in compliance with the License.
478 * You may obtain a copy of the License at
479 *
480 * http://www.apache.org/licenses/LICENSE-2.0
481 *
482 * Unless required by applicable law or agreed to in writing, software
483 * distributed under the License is distributed on an "AS-IS" BASIS,
484 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
485 * See the License for the specific language governing permissions and
486 * limitations under the License.
487 */
488 var notifications = {
489 nodesCreated: null,
490 nodesDeleted: null
491 };
492 /**
493 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
494 *
495 * Licensed under the Apache License, Version 2.0 (the "License");
496 * you may not use this file except in compliance with the License.
497 * You may obtain a copy of the License at
498 *
499 * http://www.apache.org/licenses/LICENSE-2.0
500 *
501 * Unless required by applicable law or agreed to in writing, software
502 * distributed under the License is distributed on an "AS-IS" BASIS,
503 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
504 * See the License for the specific language governing permissions and
505 * limitations under the License.
506 */
507 /**
508 * A context object keeps track of the state of a patch.
509 */
510 var Context = /** @class */ (function () {
511 function Context() {
512 this.created = [];
513 this.deleted = [];
514 }
515 Context.prototype.markCreated = function (node) {
516 this.created.push(node);
517 };
518 Context.prototype.markDeleted = function (node) {
519 this.deleted.push(node);
520 };
521 /**
522 * Notifies about nodes that were created during the patch operation.
523 */
524 Context.prototype.notifyChanges = function () {
525 if (notifications.nodesCreated && this.created.length > 0) {
526 notifications.nodesCreated(this.created);
527 }
528 if (notifications.nodesDeleted && this.deleted.length > 0) {
529 notifications.nodesDeleted(this.deleted);
530 }
531 };
532 return Context;
533 }());
534 /**
535 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
536 *
537 * Licensed under the Apache License, Version 2.0 (the "License");
538 * you may not use this file except in compliance with the License.
539 * You may obtain a copy of the License at
540 *
541 * http://www.apache.org/licenses/LICENSE-2.0
542 *
543 * Unless required by applicable law or agreed to in writing, software
544 * distributed under the License is distributed on an "AS-IS" BASIS,
545 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
546 * See the License for the specific language governing permissions and
547 * limitations under the License.
548 */
549 /**
550 * Checks if the node is the root of a document. This is either a Document
551 * or ShadowRoot. DocumentFragments are included for simplicity of the
552 * implementation, though we only want to consider Documents or ShadowRoots.
553 * @param node The node to check.
554 * @return True if the node the root of a document, false otherwise.
555 */
556 function isDocumentRoot(node) {
557 return node.nodeType === 11 || node.nodeType === 9;
558 }
559 /**
560 * Checks if the node is an Element. This is faster than an instanceof check.
561 * @param node The node to check.
562 * @return Whether or not the node is an Element.
563 */
564 function isElement(node) {
565 return node.nodeType === 1;
566 }
567 /**
568 * @param node The node to start at, inclusive.
569 * @param root The root ancestor to get until, exclusive.
570 * @return The ancestry of DOM nodes.
571 */
572 function getAncestry(node, root) {
573 var ancestry = [];
574 var cur = node;
575 while (cur !== root) {
576 var n = assert(cur);
577 ancestry.push(n);
578 cur = n.parentNode;
579 }
580 return ancestry;
581 }
582 /**
583 * @param this
584 * @returns The root node of the DOM tree that contains this node.
585 */
586 var getRootNode = (typeof Node !== "undefined" && Node.prototype.getRootNode) ||
587 function () {
588 var cur = this;
589 var prev = cur;
590 while (cur) {
591 prev = cur;
592 cur = cur.parentNode;
593 }
594 return prev;
595 };
596 /**
597 * @param node The node to get the activeElement for.
598 * @returns The activeElement in the Document or ShadowRoot
599 * corresponding to node, if present.
600 */
601 function getActiveElement(node) {
602 var root = getRootNode.call(node);
603 return isDocumentRoot(root) ? root.activeElement : null;
604 }
605 /**
606 * Gets the path of nodes that contain the focused node in the same document as
607 * a reference node, up until the root.
608 * @param node The reference node to get the activeElement for.
609 * @param root The root to get the focused path until.
610 * @returns The path of focused parents, if any exist.
611 */
612 function getFocusedPath(node, root) {
613 var activeElement = getActiveElement(node);
614 if (!activeElement || !node.contains(activeElement)) {
615 return [];
616 }
617 return getAncestry(activeElement, root);
618 }
619 /**
620 * Like insertBefore, but instead instead of moving the desired node, instead
621 * moves all the other nodes after.
622 * @param parentNode
623 * @param node
624 * @param referenceNode
625 */
626 function moveBefore(parentNode, node, referenceNode) {
627 var insertReferenceNode = node.nextSibling;
628 var cur = referenceNode;
629 while (cur !== null && cur !== node) {
630 var next = cur.nextSibling;
631 parentNode.insertBefore(cur, insertReferenceNode);
632 cur = next;
633 }
634 }
635 /**
636 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
637 *
638 * Licensed under the Apache License, Version 2.0 (the "License");
639 * you may not use this file except in compliance with the License.
640 * You may obtain a copy of the License at
641 *
642 * http://www.apache.org/licenses/LICENSE-2.0
643 *
644 * Unless required by applicable law or agreed to in writing, software
645 * distributed under the License is distributed on an "AS-IS" BASIS,
646 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
647 * See the License for the specific language governing permissions and
648 * limitations under the License.
649 */
650 /**
651 * Keeps track of information needed to perform diffs for a given DOM node.
652 */
653 var NodeData = /** @class */ (function () {
654 function NodeData(nameOrCtor, key, text) {
655 /**
656 * An array of attribute name/value pairs, used for quickly diffing the
657 * incomming attributes to see if the DOM node's attributes need to be
658 * updated.
659 */
660 this._attrsArr = null;
661 /**
662 * Whether or not the statics have been applied for the node yet.
663 */
664 this.staticsApplied = false;
665 this.nameOrCtor = nameOrCtor;
666 this.key = key;
667 this.text = text;
668 }
669 NodeData.prototype.hasEmptyAttrsArr = function () {
670 var attrs = this._attrsArr;
671 return !attrs || !attrs.length;
672 };
673 NodeData.prototype.getAttrsArr = function (length) {
674 return this._attrsArr || (this._attrsArr = createArray(length));
675 };
676 return NodeData;
677 }());
678 /**
679 * Initializes a NodeData object for a Node.
680 * @param node The Node to initialized data for.
681 * @param nameOrCtor The NameOrCtorDef to use when diffing.
682 * @param key The Key for the Node.
683 * @param text The data of a Text node, if importing a Text node.
684 * @returns A NodeData object with the existing attributes initialized.
685 */
686 function initData(node, nameOrCtor, key, text) {
687 var data = new NodeData(nameOrCtor, key, text);
688 node["__incrementalDOMData"] = data;
689 return data;
690 }
691 /**
692 * @param node The node to check.
693 * @returns True if the NodeData already exists, false otherwise.
694 */
695 function isDataInitialized(node) {
696 return Boolean(node["__incrementalDOMData"]);
697 }
698 /**
699 * Records the element's attributes.
700 * @param node The Element that may have attributes
701 * @param data The Element's data
702 */
703 function recordAttributes(node, data) {
704 var attributes = node.attributes;
705 var length = attributes.length;
706 if (!length) {
707 return;
708 }
709 var attrsArr = data.getAttrsArr(length);
710 // Use a cached length. The attributes array is really a live NamedNodeMap,
711 // which exists as a DOM "Host Object" (probably as C++ code). This makes the
712 // usual constant length iteration very difficult to optimize in JITs.
713 for (var i = 0, j = 0; i < length; i += 1, j += 2) {
714 var attr_1 = attributes[i];
715 var name_1 = attr_1.name;
716 var value = attr_1.value;
717 attrsArr[j] = name_1;
718 attrsArr[j + 1] = value;
719 }
720 }
721 /**
722 * Imports single node and its subtree, initializing caches, if it has not
723 * already been imported.
724 * @param node The node to import.
725 * @param fallbackKey A key to use if importing and no key was specified.
726 * Useful when not transmitting keys from serverside render and doing an
727 * immediate no-op diff.
728 * @returns The NodeData for the node.
729 */
730 function importSingleNode(node, fallbackKey) {
731 if (node["__incrementalDOMData"]) {
732 return node["__incrementalDOMData"];
733 }
734 var nodeName = isElement(node) ? node.localName : node.nodeName;
735 var keyAttrName = getKeyAttributeName();
736 var keyAttr = isElement(node) && keyAttrName != null
737 ? node.getAttribute(keyAttrName)
738 : null;
739 var key = isElement(node) ? keyAttr || fallbackKey : null;
740 var data = initData(node, nodeName, key);
741 if (isElement(node)) {
742 recordAttributes(node, data);
743 }
744 return data;
745 }
746 /**
747 * Imports node and its subtree, initializing caches.
748 * @param node The Node to import.
749 */
750 function importNode(node) {
751 importSingleNode(node);
752 for (var child = node.firstChild; child; child = child.nextSibling) {
753 importNode(child);
754 }
755 }
756 /**
757 * Retrieves the NodeData object for a Node, creating it if necessary.
758 * @param node The node to get data for.
759 * @param fallbackKey A key to use if importing and no key was specified.
760 * Useful when not transmitting keys from serverside render and doing an
761 * immediate no-op diff.
762 * @returns The NodeData for the node.
763 */
764 function getData(node, fallbackKey) {
765 return importSingleNode(node, fallbackKey);
766 }
767 /**
768 * Gets the key for a Node. note that the Node should have been imported
769 * by now.
770 * @param node The node to check.
771 * @returns The key used to create the node.
772 */
773 function getKey(node) {
774 assert(node["__incrementalDOMData"]);
775 return getData(node).key;
776 }
777 /**
778 * Clears all caches from a node and all of its children.
779 * @param node The Node to clear the cache for.
780 */
781 function clearCache(node) {
782 node["__incrementalDOMData"] = null;
783 for (var child = node.firstChild; child; child = child.nextSibling) {
784 clearCache(child);
785 }
786 }
787 /**
788 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
789 *
790 * Licensed under the Apache License, Version 2.0 (the "License");
791 * you may not use this file except in compliance with the License.
792 * You may obtain a copy of the License at
793 *
794 * http://www.apache.org/licenses/LICENSE-2.0
795 *
796 * Unless required by applicable law or agreed to in writing, software
797 * distributed under the License is distributed on an "AS-IS" BASIS,
798 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
799 * See the License for the specific language governing permissions and
800 * limitations under the License.
801 */
802 /**
803 * Gets the namespace to create an element (of a given tag) in.
804 * @param tag The tag to get the namespace for.
805 * @param parent The current parent Node, if any.
806 * @returns The namespace to use,
807 */
808 function getNamespaceForTag(tag, parent) {
809 if (tag === "svg") {
810 return "http://www.w3.org/2000/svg";
811 }
812 if (tag === "math") {
813 return "http://www.w3.org/1998/Math/MathML";
814 }
815 if (parent == null) {
816 return null;
817 }
818 if (getData(parent).nameOrCtor === "foreignObject") {
819 return null;
820 }
821 return parent.namespaceURI;
822 }
823 /**
824 * Creates an Element and initializes the NodeData.
825 * @param doc The document with which to create the Element.
826 * @param parent The parent of new Element.
827 * @param nameOrCtor The tag or constructor for the Element.
828 * @param key A key to identify the Element.
829 * @returns The newly created Element.
830 */
831 function createElement(doc, parent, nameOrCtor, key) {
832 var el;
833 if (typeof nameOrCtor === "function") {
834 el = new nameOrCtor();
835 }
836 else {
837 var namespace = getNamespaceForTag(nameOrCtor, parent);
838 if (namespace) {
839 el = doc.createElementNS(namespace, nameOrCtor);
840 }
841 else {
842 el = doc.createElement(nameOrCtor);
843 }
844 }
845 initData(el, nameOrCtor, key);
846 return el;
847 }
848 /**
849 * Creates a Text Node.
850 * @param doc The document with which to create the Element.
851 * @returns The newly created Text.
852 */
853 function createText(doc) {
854 var node = doc.createTextNode("");
855 initData(node, "#text", null);
856 return node;
857 }
858 /**
859 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
860 *
861 * Licensed under the Apache License, Version 2.0 (the "License");
862 * you may not use this file except in compliance with the License.
863 * You may obtain a copy of the License at
864 *
865 * http://www.apache.org/licenses/LICENSE-2.0
866 *
867 * Unless required by applicable law or agreed to in writing, software
868 * distributed under the License is distributed on an "AS-IS" BASIS,
869 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
870 * See the License for the specific language governing permissions and
871 * limitations under the License.
872 */
873 /**
874 * The default match function to use, if one was not specified when creating
875 * the patcher.
876 * @param matchNode The node to match against, unused.
877 * @param nameOrCtor The name or constructor as declared.
878 * @param expectedNameOrCtor The name or constructor of the existing node.
879 * @param key The key as declared.
880 * @param expectedKey The key of the existing node.
881 * @returns True if the node matches, false otherwise.
882 */
883 function defaultMatchFn(matchNode, nameOrCtor, expectedNameOrCtor, key, expectedKey) {
884 // Key check is done using double equals as we want to treat a null key the
885 // same as undefined. This should be okay as the only values allowed are
886 // strings, null and undefined so the == semantics are not too weird.
887 return nameOrCtor == expectedNameOrCtor && key == expectedKey;
888 }
889 var context = null;
890 var currentNode = null;
891 var currentParent = null;
892 var doc = null;
893 var focusPath = [];
894 var matchFn = defaultMatchFn;
895 /**
896 * Used to build up call arguments. Each patch call gets a separate copy, so
897 * this works with nested calls to patch.
898 */
899 var argsBuilder = [];
900 /**
901 * Used to build up attrs for the an element.
902 */
903 var attrsBuilder = [];
904 /**
905 * TODO(sparhami) We should just export argsBuilder directly when Closure
906 * Compiler supports ES6 directly.
907 * @returns The Array used for building arguments.
908 */
909 function getArgsBuilder() {
910 return argsBuilder;
911 }
912 /**
913 * TODO(sparhami) We should just export attrsBuilder directly when Closure
914 * Compiler supports ES6 directly.
915 * @returns The Array used for building arguments.
916 */
917 function getAttrsBuilder() {
918 return attrsBuilder;
919 }
920 /**
921 * Checks whether or not the current node matches the specified nameOrCtor and
922 * key. This uses the specified match function when creating the patcher.
923 * @param matchNode A node to match the data to.
924 * @param nameOrCtor The name or constructor to check for.
925 * @param key The key used to identify the Node.
926 * @return True if the node matches, false otherwise.
927 */
928 function matches(matchNode, nameOrCtor, key) {
929 var data = getData(matchNode, key);
930 return matchFn(matchNode, nameOrCtor, data.nameOrCtor, key, data.key);
931 }
932 /**
933 * Finds the matching node, starting at `node` and looking at the subsequent
934 * siblings if a key is used.
935 * @param matchNode The node to start looking at.
936 * @param nameOrCtor The name or constructor for the Node.
937 * @param key The key used to identify the Node.
938 * @returns The matching Node, if any exists.
939 */
940 function getMatchingNode(matchNode, nameOrCtor, key) {
941 if (!matchNode) {
942 return null;
943 }
944 var cur = matchNode;
945 do {
946 if (matches(cur, nameOrCtor, key)) {
947 return cur;
948 }
949 } while (key && (cur = cur.nextSibling));
950 return null;
951 }
952 /**
953 * Clears out any unvisited Nodes in a given range.
954 * @param maybeParentNode
955 * @param startNode The node to start clearing from, inclusive.
956 * @param endNode The node to clear until, exclusive.
957 */
958 function clearUnvisitedDOM(maybeParentNode, startNode, endNode) {
959 var parentNode = maybeParentNode;
960 var child = startNode;
961 while (child !== endNode) {
962 var next = child.nextSibling;
963 parentNode.removeChild(child);
964 context.markDeleted(child);
965 child = next;
966 }
967 }
968 /**
969 * @return The next Node to be patched.
970 */
971 function getNextNode() {
972 if (currentNode) {
973 return currentNode.nextSibling;
974 }
975 else {
976 return currentParent.firstChild;
977 }
978 }
979 /**
980 * Changes to the first child of the current node.
981 */
982 function enterNode() {
983 currentParent = currentNode;
984 currentNode = null;
985 }
986 /**
987 * Changes to the parent of the current node, removing any unvisited children.
988 */
989 function exitNode() {
990 clearUnvisitedDOM(currentParent, getNextNode(), null);
991 currentNode = currentParent;
992 currentParent = currentParent.parentNode;
993 }
994 /**
995 * Changes to the next sibling of the current node.
996 */
997 function nextNode() {
998 currentNode = getNextNode();
999 }
1000 /**
1001 * Creates a Node and marking it as created.
1002 * @param nameOrCtor The name or constructor for the Node.
1003 * @param key The key used to identify the Node.
1004 * @return The newly created node.
1005 */
1006 function createNode(nameOrCtor, key) {
1007 var node;
1008 if (nameOrCtor === "#text") {
1009 node = createText(doc);
1010 }
1011 else {
1012 node = createElement(doc, currentParent, nameOrCtor, key);
1013 }
1014 context.markCreated(node);
1015 return node;
1016 }
1017 /**
1018 * Aligns the virtual Node definition with the actual DOM, moving the
1019 * corresponding DOM node to the correct location or creating it if necessary.
1020 * @param nameOrCtor The name or constructor for the Node.
1021 * @param key The key used to identify the Node.
1022 */
1023 function alignWithDOM(nameOrCtor, key) {
1024 nextNode();
1025 var existingNode = getMatchingNode(currentNode, nameOrCtor, key);
1026 var node = existingNode || createNode(nameOrCtor, key);
1027 // If we are at the matching node, then we are done.
1028 if (node === currentNode) {
1029 return;
1030 }
1031 // Re-order the node into the right position, preserving focus if either
1032 // node or currentNode are focused by making sure that they are not detached
1033 // from the DOM.
1034 if (focusPath.indexOf(node) >= 0) {
1035 // Move everything else before the node.
1036 moveBefore(currentParent, node, currentNode);
1037 }
1038 else {
1039 currentParent.insertBefore(node, currentNode);
1040 }
1041 currentNode = node;
1042 }
1043 /**
1044 * Makes sure that the current node is an Element with a matching nameOrCtor and
1045 * key.
1046 *
1047 * @param nameOrCtor The tag or constructor for the Element.
1048 * @param key The key used to identify this element. This can be an
1049 * empty string, but performance may be better if a unique value is used
1050 * when iterating over an array of items.
1051 * @return The corresponding Element.
1052 */
1053 function open(nameOrCtor, key) {
1054 alignWithDOM(nameOrCtor, key);
1055 enterNode();
1056 return currentParent;
1057 }
1058 /**
1059 * Closes the currently open Element, removing any unvisited children if
1060 * necessary.
1061 * @returns The Element that was just closed.
1062 */
1063 function close() {
1064 {
1065 setInSkip(false);
1066 }
1067 exitNode();
1068 return currentNode;
1069 }
1070 /**
1071 * Makes sure the current node is a Text node and creates a Text node if it is
1072 * not.
1073 * @returns The Text node that was aligned or created.
1074 */
1075 function text() {
1076 alignWithDOM("#text", null);
1077 return currentNode;
1078 }
1079 /**
1080 * @returns The current Element being patched.
1081 */
1082 function currentElement() {
1083 {
1084 assertInPatch("currentElement");
1085 assertNotInAttributes("currentElement");
1086 }
1087 return currentParent;
1088 }
1089 /**
1090 * @return The Node that will be evaluated for the next instruction.
1091 */
1092 function currentPointer() {
1093 {
1094 assertInPatch("currentPointer");
1095 assertNotInAttributes("currentPointer");
1096 }
1097 // TODO(tomnguyen): assert that this is not null
1098 return getNextNode();
1099 }
1100 /**
1101 * Skips the children in a subtree, allowing an Element to be closed without
1102 * clearing out the children.
1103 */
1104 function skip() {
1105 {
1106 assertNoChildrenDeclaredYet("skip", currentNode);
1107 setInSkip(true);
1108 }
1109 currentNode = currentParent.lastChild;
1110 }
1111 /**
1112 * Returns a patcher function that sets up and restores a patch context,
1113 * running the run function with the provided data.
1114 * @param run The function that will run the patch.
1115 * @param patchConfig The configuration to use for the patch.
1116 * @returns The created patch function.
1117 */
1118 function createPatcher(run, patchConfig) {
1119 if (patchConfig === void 0) { patchConfig = {}; }
1120 var _a = patchConfig.matches, matches = _a === void 0 ? defaultMatchFn : _a;
1121 var f = function (node, fn, data) {
1122 var prevContext = context;
1123 var prevDoc = doc;
1124 var prevFocusPath = focusPath;
1125 var prevArgsBuilder = argsBuilder;
1126 var prevAttrsBuilder = attrsBuilder;
1127 var prevCurrentNode = currentNode;
1128 var prevCurrentParent = currentParent;
1129 var prevMatchFn = matchFn;
1130 var previousInAttributes = false;
1131 var previousInSkip = false;
1132 doc = node.ownerDocument;
1133 context = new Context();
1134 matchFn = matches;
1135 argsBuilder = [];
1136 attrsBuilder = [];
1137 currentNode = null;
1138 currentParent = node.parentNode;
1139 focusPath = getFocusedPath(node, currentParent);
1140 {
1141 previousInAttributes = setInAttributes(false);
1142 previousInSkip = setInSkip(false);
1143 updatePatchContext(context);
1144 }
1145 try {
1146 var retVal = run(node, fn, data);
1147 {
1148 assertVirtualAttributesClosed();
1149 }
1150 return retVal;
1151 }
1152 finally {
1153 context.notifyChanges();
1154 doc = prevDoc;
1155 context = prevContext;
1156 matchFn = prevMatchFn;
1157 argsBuilder = prevArgsBuilder;
1158 attrsBuilder = prevAttrsBuilder;
1159 currentNode = prevCurrentNode;
1160 currentParent = prevCurrentParent;
1161 focusPath = prevFocusPath;
1162 // Needs to be done after assertions because assertions rely on state
1163 // from these methods.
1164 {
1165 setInAttributes(previousInAttributes);
1166 setInSkip(previousInSkip);
1167 updatePatchContext(context);
1168 }
1169 }
1170 };
1171 return f;
1172 }
1173 /**
1174 * Creates a patcher that patches the document starting at node with a
1175 * provided function. This function may be called during an existing patch operation.
1176 * @param patchConfig The config to use for the patch.
1177 * @returns The created function for patching an Element's children.
1178 */
1179 function createPatchInner(patchConfig) {
1180 return createPatcher(function (node, fn, data) {
1181 currentNode = node;
1182 enterNode();
1183 fn(data);
1184 exitNode();
1185 {
1186 assertNoUnclosedTags(currentNode, node);
1187 }
1188 return node;
1189 }, patchConfig);
1190 }
1191 /**
1192 * Creates a patcher that patches an Element with the the provided function.
1193 * Exactly one top level element call should be made corresponding to `node`.
1194 * @param patchConfig The config to use for the patch.
1195 * @returns The created function for patching an Element.
1196 */
1197 function createPatchOuter(patchConfig) {
1198 return createPatcher(function (node, fn, data) {
1199 var startNode = { nextSibling: node };
1200 var expectedNextNode = null;
1201 var expectedPrevNode = null;
1202 {
1203 expectedNextNode = node.nextSibling;
1204 expectedPrevNode = node.previousSibling;
1205 }
1206 currentNode = startNode;
1207 fn(data);
1208 {
1209 assertPatchOuterHasParentNode(currentParent);
1210 assertPatchElementNoExtras(startNode, currentNode, expectedNextNode, expectedPrevNode);
1211 }
1212 if (currentParent) {
1213 clearUnvisitedDOM(currentParent, getNextNode(), node.nextSibling);
1214 }
1215 return startNode === currentNode ? null : currentNode;
1216 }, patchConfig);
1217 }
1218 var patchInner = createPatchInner();
1219 var patchOuter = createPatchOuter();
1220 /**
1221 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
1222 *
1223 * Licensed under the Apache License, Version 2.0 (the "License");
1224 * you may not use this file except in compliance with the License.
1225 * You may obtain a copy of the License at
1226 *
1227 * http://www.apache.org/licenses/LICENSE-2.0
1228 *
1229 * Unless required by applicable law or agreed to in writing, software
1230 * distributed under the License is distributed on an "AS-IS" BASIS,
1231 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1232 * See the License for the specific language governing permissions and
1233 * limitations under the License.
1234 */
1235 var buffer = [];
1236 var bufferStart = 0;
1237 /**
1238 * TODO(tomnguyen): This is a bit silly and really needs to be better typed.
1239 * @param fn A function to call.
1240 * @param a The first argument to the function.
1241 * @param b The second argument to the function.
1242 * @param c The third argument to the function.
1243 */
1244 function queueChange(fn, a, b, c) {
1245 buffer.push(fn);
1246 buffer.push(a);
1247 buffer.push(b);
1248 buffer.push(c);
1249 }
1250 /**
1251 * Flushes the changes buffer, calling the functions for each change.
1252 */
1253 function flush() {
1254 // A change may cause this function to be called re-entrantly. Keep track of
1255 // the portion of the buffer we are consuming. Updates the start pointer so
1256 // that the next call knows where to start from.
1257 var start = bufferStart;
1258 var end = buffer.length;
1259 bufferStart = end;
1260 for (var i = start; i < end; i += 4) {
1261 var fn = buffer[i];
1262 fn(buffer[i + 1], buffer[i + 2], buffer[i + 3]);
1263 }
1264 bufferStart = start;
1265 truncateArray(buffer, start);
1266 }
1267 /**
1268 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
1269 *
1270 * Licensed under the Apache License, Version 2.0 (the "License");
1271 * you may not use this file except in compliance with the License.
1272 * You may obtain a copy of the License at
1273 *
1274 * http://www.apache.org/licenses/LICENSE-2.0
1275 *
1276 * Unless required by applicable law or agreed to in writing, software
1277 * distributed under the License is distributed on an "AS-IS" BASIS,
1278 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1279 * See the License for the specific language governing permissions and
1280 * limitations under the License.
1281 */
1282 /**
1283 * Used to keep track of the previous values when a 2-way diff is necessary.
1284 * This object is cleared out and reused.
1285 */
1286 var prevValuesMap = createMap();
1287 /**
1288 * Calculates the diff between previous and next values, calling the update
1289 * function when an item has changed value. If an item from the previous values
1290 * is not present in the the next values, the update function is called with a
1291 * value of `undefined`.
1292 * @param prev The previous values, alternating name, value pairs.
1293 * @param next The next values, alternating name, value pairs.
1294 * @param updateCtx The context for the updateFn.
1295 * @param updateFn A function to call when a value has changed.
1296 */
1297 function calculateDiff(prev, next, updateCtx, updateFn) {
1298 var isNew = !prev.length;
1299 var i = 0;
1300 for (; i < next.length; i += 2) {
1301 var name_2 = next[i];
1302 if (isNew) {
1303 prev[i] = name_2;
1304 }
1305 else if (prev[i] !== name_2) {
1306 break;
1307 }
1308 var value = next[i + 1];
1309 if (isNew || prev[i + 1] !== value) {
1310 prev[i + 1] = value;
1311 queueChange(updateFn, updateCtx, name_2, value);
1312 }
1313 }
1314 // Items did not line up exactly as before, need to make sure old items are
1315 // removed. This should be a rare case.
1316 if (i < next.length || i < prev.length) {
1317 var startIndex = i;
1318 for (i = startIndex; i < prev.length; i += 2) {
1319 prevValuesMap[prev[i]] = prev[i + 1];
1320 }
1321 for (i = startIndex; i < next.length; i += 2) {
1322 var name_3 = next[i];
1323 var value = next[i + 1];
1324 if (prevValuesMap[name_3] !== value) {
1325 queueChange(updateFn, updateCtx, name_3, value);
1326 }
1327 prev[i] = name_3;
1328 prev[i + 1] = value;
1329 delete prevValuesMap[name_3];
1330 }
1331 truncateArray(prev, next.length);
1332 for (var name_4 in prevValuesMap) {
1333 queueChange(updateFn, updateCtx, name_4, undefined);
1334 delete prevValuesMap[name_4];
1335 }
1336 }
1337 flush();
1338 }
1339 /**
1340 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
1341 *
1342 * Licensed under the Apache License, Version 2.0 (the "License");
1343 * you may not use this file except in compliance with the License.
1344 * You may obtain a copy of the License at
1345 *
1346 * http://www.apache.org/licenses/LICENSE-2.0
1347 *
1348 * Unless required by applicable law or agreed to in writing, software
1349 * distributed under the License is distributed on an "AS-IS" BASIS,
1350 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351 * See the License for the specific language governing permissions and
1352 * limitations under the License.
1353 */
1354 /**
1355 * The offset in the virtual element declaration where the attributes are
1356 * specified.
1357 */
1358 var ATTRIBUTES_OFFSET = 3;
1359 /**
1360 * Used to keep track of the previous values when a 2-way diff is necessary.
1361 * This object is reused.
1362 * TODO(sparhamI) Scope this to a patch so you can call patch from an attribute
1363 * update.
1364 */
1365 var prevAttrsMap = createMap();
1366 /**
1367 * @param element The Element to diff the attrs for.
1368 * @param data The NodeData associated with the Element.
1369 */
1370 function diffAttrs(element, data) {
1371 var attrsBuilder = getAttrsBuilder();
1372 var prevAttrsArr = data.getAttrsArr(attrsBuilder.length);
1373 calculateDiff(prevAttrsArr, attrsBuilder, element, updateAttribute);
1374 truncateArray(attrsBuilder, 0);
1375 }
1376 /**
1377 * Applies the statics. When importing an Element, any existing attributes that
1378 * match a static are converted into a static attribute.
1379 * @param node The Element to apply statics for.
1380 * @param data The NodeData associated with the Element.
1381 * @param statics The statics array.
1382 */
1383 function diffStatics(node, data, statics) {
1384 if (data.staticsApplied) {
1385 return;
1386 }
1387 data.staticsApplied = true;
1388 if (!statics || !statics.length) {
1389 return;
1390 }
1391 if (data.hasEmptyAttrsArr()) {
1392 for (var i = 0; i < statics.length; i += 2) {
1393 updateAttribute(node, statics[i], statics[i + 1]);
1394 }
1395 return;
1396 }
1397 for (var i = 0; i < statics.length; i += 2) {
1398 prevAttrsMap[statics[i]] = i + 1;
1399 }
1400 var attrsArr = data.getAttrsArr(0);
1401 var j = 0;
1402 for (var i = 0; i < attrsArr.length; i += 2) {
1403 var name_5 = attrsArr[i];
1404 var value = attrsArr[i + 1];
1405 var staticsIndex = prevAttrsMap[name_5];
1406 if (staticsIndex) {
1407 // For any attrs that are static and have the same value, make sure we do
1408 // not set them again.
1409 if (statics[staticsIndex] === value) {
1410 delete prevAttrsMap[name_5];
1411 }
1412 continue;
1413 }
1414 // For any attrs that are dynamic, move them up to the right place.
1415 attrsArr[j] = name_5;
1416 attrsArr[j + 1] = value;
1417 j += 2;
1418 }
1419 // Anything after `j` was either moved up already or static.
1420 truncateArray(attrsArr, j);
1421 for (var name_6 in prevAttrsMap) {
1422 updateAttribute(node, name_6, statics[prevAttrsMap[name_6]]);
1423 delete prevAttrsMap[name_6];
1424 }
1425 }
1426 /**
1427 * Declares a virtual Element at the current location in the document. This
1428 * corresponds to an opening tag and a elementClose tag is required. This is
1429 * like elementOpen, but the attributes are defined using the attr function
1430 * rather than being passed as arguments. Must be folllowed by 0 or more calls
1431 * to attr, then a call to elementOpenEnd.
1432 * @param nameOrCtor The Element's tag or constructor.
1433 * @param key The key used to identify this element. This can be an
1434 * empty string, but performance may be better if a unique value is used
1435 * when iterating over an array of items.
1436 * @param statics An array of attribute name/value pairs of the static
1437 * attributes for the Element. Attributes will only be set once when the
1438 * Element is created.
1439 */
1440 function elementOpenStart(nameOrCtor, key, statics) {
1441 var argsBuilder = getArgsBuilder();
1442 {
1443 assertNotInAttributes("elementOpenStart");
1444 setInAttributes(true);
1445 }
1446 argsBuilder[0] = nameOrCtor;
1447 argsBuilder[1] = key;
1448 argsBuilder[2] = statics;
1449 }
1450 /**
1451 * Allows you to define a key after an elementOpenStart. This is useful in
1452 * templates that define key after an element has been opened ie
1453 * `<div key('foo')></div>`.
1454 * @param key The key to use for the next call.
1455 */
1456 function key(key) {
1457 var argsBuilder = getArgsBuilder();
1458 {
1459 assertInAttributes("key");
1460 assert(argsBuilder);
1461 }
1462 argsBuilder[1] = key;
1463 }
1464 /**
1465 * Buffers an attribute, which will get applied during the next call to
1466 * `elementOpen`, `elementOpenEnd` or `applyAttrs`.
1467 * @param name The of the attribute to buffer.
1468 * @param value The value of the attribute to buffer.
1469 */
1470 function attr(name, value) {
1471 var attrsBuilder = getAttrsBuilder();
1472 {
1473 assertInPatch("attr");
1474 }
1475 attrsBuilder.push(name);
1476 attrsBuilder.push(value);
1477 }
1478 /**
1479 * Closes an open tag started with elementOpenStart.
1480 * @return The corresponding Element.
1481 */
1482 function elementOpenEnd() {
1483 var argsBuilder = getArgsBuilder();
1484 {
1485 assertInAttributes("elementOpenEnd");
1486 setInAttributes(false);
1487 }
1488 var node = open(argsBuilder[0], argsBuilder[1]);
1489 var data = getData(node);
1490 diffStatics(node, data, argsBuilder[2]);
1491 diffAttrs(node, data);
1492 truncateArray(argsBuilder, 0);
1493 return node;
1494 }
1495 /**
1496 * @param nameOrCtor The Element's tag or constructor.
1497 * @param key The key used to identify this element. This can be an
1498 * empty string, but performance may be better if a unique value is used
1499 * when iterating over an array of items.
1500 * @param statics An array of attribute name/value pairs of the static
1501 * attributes for the Element. Attributes will only be set once when the
1502 * Element is created.
1503 * @param varArgs, Attribute name/value pairs of the dynamic attributes
1504 * for the Element.
1505 * @return The corresponding Element.
1506 */
1507 function elementOpen(nameOrCtor, key,
1508 // Ideally we could tag statics and varArgs as an array where every odd
1509 // element is a string and every even element is any, but this is hard.
1510 statics) {
1511 var varArgs = [];
1512 for (var _i = 3; _i < arguments.length; _i++) {
1513 varArgs[_i - 3] = arguments[_i];
1514 }
1515 {
1516 assertNotInAttributes("elementOpen");
1517 assertNotInSkip("elementOpen");
1518 }
1519 elementOpenStart(nameOrCtor, key, statics);
1520 for (var i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
1521 attr(arguments[i], arguments[i + 1]);
1522 }
1523 return elementOpenEnd();
1524 }
1525 /**
1526 * Applies the currently buffered attrs to the currently open element. This
1527 * clears the buffered attributes.
1528 */
1529 function applyAttrs() {
1530 var node = currentElement();
1531 var data = getData(node);
1532 diffAttrs(node, data);
1533 }
1534 /**
1535 * Applies the current static attributes to the currently open element. Note:
1536 * statics should be applied before calling `applyAtrs`.
1537 * @param statics The statics to apply to the current element.
1538 */
1539 function applyStatics(statics) {
1540 var node = currentElement();
1541 var data = getData(node);
1542 diffStatics(node, data, statics);
1543 }
1544 /**
1545 * Closes an open virtual Element.
1546 *
1547 * @param nameOrCtor The Element's tag or constructor.
1548 * @return The corresponding Element.
1549 */
1550 function elementClose(nameOrCtor) {
1551 {
1552 assertNotInAttributes("elementClose");
1553 }
1554 var node = close();
1555 {
1556 assertCloseMatchesOpenTag(getData(node).nameOrCtor, nameOrCtor);
1557 }
1558 return node;
1559 }
1560 /**
1561 * Declares a virtual Element at the current location in the document that has
1562 * no children.
1563 * @param nameOrCtor The Element's tag or constructor.
1564 * @param key The key used to identify this element. This can be an
1565 * empty string, but performance may be better if a unique value is used
1566 * when iterating over an array of items.
1567 * @param statics An array of attribute name/value pairs of the static
1568 * attributes for the Element. Attributes will only be set once when the
1569 * Element is created.
1570 * @param varArgs Attribute name/value pairs of the dynamic attributes
1571 * for the Element.
1572 * @return The corresponding Element.
1573 */
1574 function elementVoid(nameOrCtor, key,
1575 // Ideally we could tag statics and varArgs as an array where every odd
1576 // element is a string and every even element is any, but this is hard.
1577 statics) {
1578 var varArgs = [];
1579 for (var _i = 3; _i < arguments.length; _i++) {
1580 varArgs[_i - 3] = arguments[_i];
1581 }
1582 elementOpen.apply(null, arguments);
1583 return elementClose(nameOrCtor);
1584 }
1585 /**
1586 * Declares a virtual Text at this point in the document.
1587 *
1588 * @param value The value of the Text.
1589 * @param varArgs
1590 * Functions to format the value which are called only when the value has
1591 * changed.
1592 * @return The corresponding text node.
1593 */
1594 function text$1(value) {
1595 var varArgs = [];
1596 for (var _i = 1; _i < arguments.length; _i++) {
1597 varArgs[_i - 1] = arguments[_i];
1598 }
1599 {
1600 assertNotInAttributes("text");
1601 assertNotInSkip("text");
1602 }
1603 var node = text();
1604 var data = getData(node);
1605 if (data.text !== value) {
1606 data.text = value;
1607 var formatted = value;
1608 for (var i = 1; i < arguments.length; i += 1) {
1609 /*
1610 * Call the formatter function directly to prevent leaking arguments.
1611 * https://github.com/google/incremental-dom/pull/204#issuecomment-178223574
1612 */
1613 var fn = arguments[i];
1614 formatted = fn(formatted);
1615 }
1616 // Setting node.data resets the cursor in IE/Edge.
1617 if (node.data !== formatted) {
1618 node.data = formatted;
1619 }
1620 }
1621 return node;
1622 }
1623 /**
1624 * Copyright 2018 The Incremental DOM Authors. All Rights Reserved.
1625 *
1626 * Licensed under the Apache License, Version 2.0 (the "License");
1627 * you may not use this file except in compliance with the License.
1628 * You may obtain a copy of the License at
1629 *
1630 * http://www.apache.org/licenses/LICENSE-2.0
1631 *
1632 * Unless required by applicable law or agreed to in writing, software
1633 * distributed under the License is distributed on an "AS-IS" BASIS,
1634 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1635 * See the License for the specific language governing permissions and
1636 * limitations under the License.
1637 */
1638 exports.applyAttr = applyAttr;
1639 exports.applyProp = applyProp;
1640 exports.attributes = attributes;
1641 exports.alignWithDOM = alignWithDOM;
1642 exports.close = close;
1643 exports.createPatchInner = createPatchInner;
1644 exports.createPatchOuter = createPatchOuter;
1645 exports.currentElement = currentElement;
1646 exports.currentPointer = currentPointer;
1647 exports.open = open;
1648 exports.patch = patchInner;
1649 exports.patchInner = patchInner;
1650 exports.patchOuter = patchOuter;
1651 exports.skip = skip;
1652 exports.skipNode = nextNode;
1653 exports.setKeyAttributeName = setKeyAttributeName;
1654 exports.clearCache = clearCache;
1655 exports.getKey = getKey;
1656 exports.importNode = importNode;
1657 exports.isDataInitialized = isDataInitialized;
1658 exports.notifications = notifications;
1659 exports.symbols = symbols;
1660 exports.applyAttrs = applyAttrs;
1661 exports.applyStatics = applyStatics;
1662 exports.attr = attr;
1663 exports.elementClose = elementClose;
1664 exports.elementOpen = elementOpen;
1665 exports.elementOpenEnd = elementOpenEnd;
1666 exports.elementOpenStart = elementOpenStart;
1667 exports.elementVoid = elementVoid;
1668 exports.key = key;
1669 exports.text = text$1;
1670 Object.defineProperty(exports, '__esModule', { value: true });
1671}));
1672//# sourceMappingURL=bundle.umd.js.map