p-elements-core 1.2.0-rc7 → 1.2.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.
@@ -0,0 +1,730 @@
1
+ const NAMESPACE_W3 = "http://www.w3.org/";
2
+ const NAMESPACE_SVG = NAMESPACE_W3 + "2000/svg";
3
+ const NAMESPACE_XLINK = NAMESPACE_W3 + "1999/xlink";
4
+
5
+ const emptyArray = <VNode[]>[];
6
+
7
+ const removedNodes: VNode[] = [];
8
+
9
+ let requestedIdleCallback = false;
10
+
11
+ const visitRemovedNode = (node: VNode) => {
12
+ (node.children || []).forEach(visitRemovedNode);
13
+
14
+ if (node?.properties?.afterRemoved) {
15
+ node.properties.afterRemoved.apply(
16
+ node.properties.bind || node.properties,
17
+ [<Element>node.domNode]
18
+ );
19
+ }
20
+ };
21
+
22
+ const same = (vnode1: VNode, vnode2: VNode) => {
23
+ if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
24
+ return false;
25
+ }
26
+ if (vnode1.properties && vnode2.properties) {
27
+ if (vnode1.properties.key !== vnode2.properties.key) {
28
+ return false;
29
+ }
30
+ return vnode1.properties.bind === vnode2.properties.bind;
31
+ }
32
+ return !vnode1.properties && !vnode2.properties;
33
+ };
34
+
35
+ const checkStyleValue = (styleValue: Object) => {
36
+ if (typeof styleValue !== "string") {
37
+ throw new Error("Style values must be strings");
38
+ }
39
+ };
40
+
41
+ const findIndexOfChild = (children: VNode[], sameAs: VNode, start: number) => {
42
+ if (sameAs.vnodeSelector !== "") {
43
+ // Never scan for text-nodes
44
+ for (let i = start; i < children.length; i++) {
45
+ if (same(children[i], sameAs)) {
46
+ return i;
47
+ }
48
+ }
49
+ }
50
+ return -1;
51
+ };
52
+
53
+ const checkDistinguishable = (
54
+ childNodes: VNode[],
55
+ indexToCheck: number,
56
+ parentVNode: VNode,
57
+ operation: string
58
+ ) => {
59
+ let childNode = childNodes[indexToCheck];
60
+ if (childNode.vnodeSelector === "") {
61
+ return; // Text nodes need not be distinguishable
62
+ }
63
+ let properties = childNode.properties;
64
+ let key = properties
65
+ ? properties.key === undefined
66
+ ? properties.bind
67
+ : properties.key
68
+ : undefined;
69
+ if (!key) {
70
+ // A key is just assumed to be unique
71
+ for (let i = 0; i < childNodes.length; i++) {
72
+ if (i !== indexToCheck) {
73
+ let node = childNodes[i];
74
+ if (same(node, childNode)) {
75
+ if (operation === "added") {
76
+ throw new Error(
77
+ parentVNode.vnodeSelector +
78
+ " had a " +
79
+ childNode.vnodeSelector +
80
+ " child " +
81
+ "added, but there is now more than one. You must add unique key properties to make them distinguishable."
82
+ );
83
+ } else {
84
+ throw new Error(
85
+ parentVNode.vnodeSelector +
86
+ " had a " +
87
+ childNode.vnodeSelector +
88
+ " child " +
89
+ "removed, but there were more than one. You must add unique key properties to make them distinguishable."
90
+ );
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ };
97
+
98
+ const nodeAdded = (vNode: VNode, transitions: TransitionStrategy) => {
99
+ if (vNode.properties) {
100
+ let enterAnimation = vNode.properties.enterAnimation;
101
+ if (enterAnimation) {
102
+ if (typeof enterAnimation === "function") {
103
+ enterAnimation(vNode.domNode as Element, vNode.properties);
104
+ } else {
105
+ transitions.enter(
106
+ vNode.domNode as Element,
107
+ vNode.properties,
108
+ enterAnimation as string
109
+ );
110
+ }
111
+ }
112
+ }
113
+ };
114
+
115
+ export const extend = <T>(base: T, overrides: any): T => {
116
+ let result = {} as any;
117
+ Object.keys(base).forEach(function (key) {
118
+ result[key] = (base as any)[key];
119
+ });
120
+ if (overrides) {
121
+ Object.keys(overrides).forEach((key) => {
122
+ result[key] = overrides[key];
123
+ });
124
+ }
125
+ return result;
126
+ };
127
+
128
+ const addChildren = (
129
+ domNode: Node,
130
+ children: VNode[] | undefined,
131
+ projectionOptions: ProjectionOptions
132
+ ) => {
133
+ if (!children) {
134
+ return;
135
+ }
136
+ for (let i = 0; i < children.length; i++) {
137
+ createDom(children[i], domNode, undefined, projectionOptions);
138
+ }
139
+ };
140
+
141
+ /**
142
+ * Adds or removes classes from an Element
143
+ * @param domNode the element
144
+ * @param classes a string separated list of classes
145
+ * @param on true means add classes, false means remove
146
+ */
147
+ const toggleClasses = (
148
+ domNode: HTMLElement,
149
+ classes: string | null | undefined,
150
+ on: boolean
151
+ ) => {
152
+ if (!classes) {
153
+ return;
154
+ }
155
+ classes.split(" ").forEach((classToToggle) => {
156
+ if (classToToggle) {
157
+ domNode.classList.toggle(classToToggle, on);
158
+ }
159
+ });
160
+ };
161
+
162
+ const setProperties = (
163
+ domNode: Node,
164
+ properties: VNodeProperties | undefined,
165
+ projectionOptions: ProjectionOptions
166
+ ) => {
167
+ if (!properties) {
168
+ return;
169
+ }
170
+
171
+ let eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
172
+ let propNames = Object.keys(properties);
173
+ let propCount = propNames.length;
174
+ for (let i = 0; i < propCount; i++) {
175
+ let propName = propNames[i];
176
+ let propValue = properties[propName];
177
+ if (propName === "className") {
178
+ throw new Error('Property "className" is not supported, use "class".');
179
+ } else if (propName === "class") {
180
+ toggleClasses(domNode as HTMLElement, propValue as string, true);
181
+ } else if (propName === "classes") {
182
+ // object with string keys and boolean values
183
+ let classNames = Object.keys(propValue);
184
+ let classNameCount = classNames.length;
185
+ for (let j = 0; j < classNameCount; j++) {
186
+ let className = classNames[j];
187
+ if (propValue[className]) {
188
+ (domNode as Element).classList.add(className);
189
+ }
190
+ }
191
+ } else if (propName === "styles") {
192
+ // object with string keys and string (!) values
193
+ let styleNames = Object.keys(propValue);
194
+ let styleCount = styleNames.length;
195
+ for (let j = 0; j < styleCount; j++) {
196
+ let styleName = styleNames[j];
197
+ let styleValue = propValue[styleName];
198
+ if (styleValue) {
199
+ checkStyleValue(styleValue);
200
+ projectionOptions.styleApplyer!(
201
+ <HTMLElement>domNode,
202
+ styleName,
203
+ styleValue
204
+ );
205
+ }
206
+ }
207
+ } else if (
208
+ propName !== "key" &&
209
+ propValue !== null &&
210
+ propValue !== undefined
211
+ ) {
212
+ let type = typeof propValue;
213
+ if (type === "function") {
214
+ if (propName.lastIndexOf("on", 0) === 0) {
215
+ // lastIndexOf(,0)===0 -> startsWith
216
+ if (eventHandlerInterceptor) {
217
+ propValue = eventHandlerInterceptor(
218
+ propName,
219
+ propValue,
220
+ domNode,
221
+ properties
222
+ ); // intercept eventhandlers
223
+ }
224
+ if (propName === "oninput") {
225
+ (function () {
226
+ // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
227
+ let oldPropValue = propValue;
228
+ propValue = function (this: HTMLElement, evt: Event) {
229
+ oldPropValue.apply(this, [evt]);
230
+ (evt.target as any)["oninput-value"] = (
231
+ evt.target as HTMLInputElement
232
+ ).value; // may be HTMLTextAreaElement as well
233
+ };
234
+ })();
235
+ }
236
+ }
237
+ (domNode as any)[propName] = propValue;
238
+ } else if (projectionOptions.namespace === NAMESPACE_SVG) {
239
+ if (propName === "href") {
240
+ (domNode as Element).setAttributeNS(
241
+ NAMESPACE_XLINK,
242
+ propName,
243
+ propValue
244
+ );
245
+ } else {
246
+ // all SVG attributes are read-only in DOM, so...
247
+ (domNode as Element).setAttribute(propName, propValue);
248
+ }
249
+ } else if (
250
+ type === "string" &&
251
+ propName !== "value" &&
252
+ propName !== "innerHTML"
253
+ ) {
254
+ (domNode as Element).setAttribute(propName, propValue);
255
+ } else {
256
+ (domNode as any)[propName] = propValue;
257
+ }
258
+ }
259
+ }
260
+ };
261
+
262
+ export const initPropertiesAndChildren = (
263
+ domNode: Node,
264
+ vnode: VNode,
265
+ projectionOptions: ProjectionOptions
266
+ ) => {
267
+ addChildren(domNode, vnode.children, projectionOptions); // children before properties, needed for value property of <select>.
268
+ if (vnode.text) {
269
+ domNode.textContent = vnode.text;
270
+ }
271
+
272
+ if (vnode.properties && vnode.properties.eventListeners) {
273
+ if (Array.isArray(vnode.properties.eventListeners)) {
274
+ vnode.properties.eventListeners.forEach((evListener: any) => {
275
+ domNode.addEventListener(evListener[0], evListener[1], evListener[2]);
276
+ });
277
+ } else {
278
+ throw new Error("eventListeners value must be an array");
279
+ }
280
+ }
281
+
282
+ setProperties(domNode, vnode.properties, projectionOptions);
283
+ if (vnode.properties && vnode.properties.afterCreate) {
284
+ vnode.properties.afterCreate.apply(
285
+ vnode.properties.bind || vnode.properties,
286
+ [
287
+ domNode as Element,
288
+ projectionOptions,
289
+ vnode.vnodeSelector,
290
+ vnode.properties,
291
+ vnode.children,
292
+ ]
293
+ );
294
+ }
295
+ };
296
+
297
+ export const createProjection = (
298
+ vnode: VNode,
299
+ projectionOptions: ProjectionOptions
300
+ ): Projection => {
301
+ return {
302
+ update: function (updatedVnode: VNode) {
303
+ if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
304
+ throw new Error(
305
+ "The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)"
306
+ );
307
+ }
308
+ updateDom(vnode, updatedVnode, projectionOptions);
309
+ vnode = updatedVnode;
310
+ },
311
+ domNode: <Element>vnode.domNode,
312
+ };
313
+ };
314
+
315
+ // export let createDom: () => void;
316
+
317
+ // let updateDom: (previous: VNode, vnode: VNode, projectionOptions: ProjectionOptions) => boolean;
318
+
319
+ export const createDom = (
320
+ vnode: VNode,
321
+ parentNode: Node,
322
+ insertBefore: Node | null | undefined,
323
+ projectionOptions: ProjectionOptions
324
+ ): void => {
325
+ let domNode: Node | undefined,
326
+ i: number,
327
+ c: string,
328
+ start = 0,
329
+ type: string,
330
+ found: string;
331
+ let vnodeSelector = vnode.vnodeSelector;
332
+ let doc = parentNode.ownerDocument;
333
+ if (vnodeSelector === "") {
334
+ domNode = vnode.domNode = doc.createTextNode(vnode.text!);
335
+ if (insertBefore !== undefined) {
336
+ parentNode.insertBefore(domNode, insertBefore);
337
+ } else {
338
+ parentNode.appendChild(domNode);
339
+ }
340
+ } else {
341
+ for (i = 0; i <= vnodeSelector.length; ++i) {
342
+ c = vnodeSelector.charAt(i);
343
+ if (i === vnodeSelector.length || c === "." || c === "#") {
344
+ type = vnodeSelector.charAt(start - 1);
345
+ found = vnodeSelector.slice(start, i);
346
+ if (type === ".") {
347
+ (domNode as HTMLElement).classList.add(found);
348
+ } else if (type === "#") {
349
+ (domNode as Element).id = found;
350
+ } else {
351
+ if (found === "svg") {
352
+ projectionOptions = extend(projectionOptions, {
353
+ namespace: NAMESPACE_SVG,
354
+ });
355
+ }
356
+ if (projectionOptions.namespace !== undefined) {
357
+ domNode = vnode.domNode = doc.createElementNS(
358
+ projectionOptions.namespace,
359
+ found
360
+ );
361
+ } else {
362
+ let n;
363
+
364
+ if (vnode.properties && (vnode as any).properties.is) {
365
+ // for ios 9
366
+ let tempElement = document.createElement("div");
367
+ tempElement.style.display = "none";
368
+ document.body.appendChild(tempElement);
369
+ tempElement.innerHTML = `<${found} is="${
370
+ (vnode as any).properties.is
371
+ }"></${found}>`;
372
+ setTimeout(() => {
373
+ document.body.removeChild(tempElement);
374
+ }, 10);
375
+
376
+ n = (doc as any).createElement(found, {
377
+ is: (vnode as any).properties.is,
378
+ });
379
+ } else {
380
+ n = doc.createElement(found);
381
+ }
382
+ domNode = vnode.domNode = vnode.domNode || n;
383
+ if (
384
+ found === "input" &&
385
+ vnode.properties &&
386
+ vnode.properties.type !== undefined
387
+ ) {
388
+ // IE8 and older don't support setting input type after the DOM Node has been added to the document
389
+ (domNode as Element).setAttribute("type", vnode.properties.type);
390
+ }
391
+ }
392
+ if (domNode) {
393
+ if (insertBefore !== undefined) {
394
+ parentNode.insertBefore(domNode, insertBefore);
395
+ } else if (domNode.parentNode !== parentNode) {
396
+ parentNode.appendChild(domNode);
397
+ }
398
+ }
399
+ }
400
+ start = i + 1;
401
+ }
402
+ }
403
+ initPropertiesAndChildren(domNode!, vnode, projectionOptions);
404
+ }
405
+ };
406
+
407
+ let nodeToRemove = (vNode: VNode, transitions: TransitionStrategy) => {
408
+ let domNode: Node = vNode.domNode!;
409
+ if (vNode.properties) {
410
+ if (vNode.properties.eventListeners) {
411
+ if (Array.isArray(vNode.properties.eventListeners)) {
412
+ vNode.properties.eventListeners.forEach((evListener) => {
413
+ domNode.removeEventListener(evListener[0], evListener[1]);
414
+ });
415
+ }
416
+ }
417
+
418
+ let exitAnimation = vNode.properties.exitAnimation;
419
+ if (exitAnimation) {
420
+ (domNode as HTMLElement).style.pointerEvents = "none";
421
+ let removeDomNode = function () {
422
+ if (domNode.parentNode) {
423
+ domNode.parentNode.removeChild(domNode);
424
+ scheduleNodeRemoval(vNode);
425
+ }
426
+ };
427
+ if (typeof exitAnimation === "function") {
428
+ exitAnimation(domNode as Element, removeDomNode, vNode.properties);
429
+ return;
430
+ } else {
431
+ transitions.exit(
432
+ vNode.domNode as Element,
433
+ vNode.properties,
434
+ exitAnimation as string,
435
+ removeDomNode
436
+ );
437
+ return;
438
+ }
439
+ }
440
+ }
441
+ if (domNode.parentNode) {
442
+ domNode.parentNode.removeChild(domNode);
443
+ scheduleNodeRemoval(vNode);
444
+ }
445
+ };
446
+
447
+ const scheduleNodeRemoval = (vNode: VNode): void => {
448
+ removedNodes.push(vNode);
449
+
450
+ if (!requestedIdleCallback) {
451
+ requestedIdleCallback = true;
452
+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
453
+ (window as any).requestIdleCallback(processPendingNodeRemovals, {
454
+ timeout: 16,
455
+ });
456
+ } else {
457
+ setTimeout(processPendingNodeRemovals, 16);
458
+ }
459
+ }
460
+ };
461
+
462
+ const processPendingNodeRemovals = (): void => {
463
+ requestedIdleCallback = false;
464
+
465
+ removedNodes.forEach(visitRemovedNode);
466
+ removedNodes.length = 0;
467
+ };
468
+
469
+ const updateChildren = (
470
+ vnode: VNode,
471
+ domNode: Node,
472
+ oldChildren: VNode[] | undefined,
473
+ newChildren: VNode[] | undefined,
474
+ projectionOptions: ProjectionOptions
475
+ ) => {
476
+ if (oldChildren === newChildren) {
477
+ return false;
478
+ }
479
+ oldChildren = oldChildren || emptyArray;
480
+ newChildren = newChildren || emptyArray;
481
+ let oldChildrenLength = oldChildren.length;
482
+ let newChildrenLength = newChildren.length;
483
+ let transitions = projectionOptions.transitions!;
484
+
485
+ let oldIndex = 0;
486
+ let newIndex = 0;
487
+ let i: number;
488
+ let textUpdated = false;
489
+ while (newIndex < newChildrenLength) {
490
+ let oldChild =
491
+ oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
492
+ let newChild = newChildren[newIndex];
493
+ if (oldChild !== undefined && same(oldChild, newChild)) {
494
+ textUpdated =
495
+ updateDom(oldChild, newChild, projectionOptions) || textUpdated;
496
+ oldIndex++;
497
+ } else {
498
+ let findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
499
+ if (findOldIndex >= 0) {
500
+ // Remove preceding missing children
501
+ for (i = oldIndex; i < findOldIndex; i++) {
502
+ nodeToRemove(oldChildren[i], transitions);
503
+ checkDistinguishable(oldChildren, i, vnode, "removed");
504
+ }
505
+ textUpdated =
506
+ updateDom(oldChildren[findOldIndex], newChild, projectionOptions) ||
507
+ textUpdated;
508
+ oldIndex = findOldIndex + 1;
509
+ } else {
510
+ // New child
511
+ createDom(
512
+ newChild,
513
+ domNode,
514
+ oldIndex < oldChildrenLength
515
+ ? oldChildren[oldIndex].domNode
516
+ : undefined,
517
+ projectionOptions
518
+ );
519
+ nodeAdded(newChild, transitions);
520
+ checkDistinguishable(newChildren, newIndex, vnode, "added");
521
+ }
522
+ }
523
+ newIndex++;
524
+ }
525
+ if (oldChildrenLength > oldIndex) {
526
+ // Remove child fragments
527
+ for (i = oldIndex; i < oldChildrenLength; i++) {
528
+ nodeToRemove(oldChildren[i], transitions);
529
+ checkDistinguishable(oldChildren, i, vnode, "removed");
530
+ }
531
+ }
532
+ return textUpdated;
533
+ };
534
+
535
+ export const updateDom = (
536
+ previous: VNode,
537
+ vnode: VNode,
538
+ projectionOptions: ProjectionOptions
539
+ ): boolean => {
540
+ let domNode = previous.domNode!;
541
+ let textUpdated = false;
542
+ if (previous === vnode) {
543
+ return false; // By contract, VNode objects may not be modified anymore after passing them to maquette
544
+ }
545
+ let updated = false;
546
+ if (vnode.vnodeSelector === "") {
547
+ if (vnode.text !== previous.text) {
548
+ let newVNode = domNode.ownerDocument.createTextNode(vnode.text!);
549
+ domNode.parentNode!.replaceChild(newVNode, domNode);
550
+ vnode.domNode = newVNode;
551
+ textUpdated = true;
552
+ return textUpdated;
553
+ }
554
+ } else {
555
+ if (vnode.vnodeSelector.lastIndexOf("svg", 0) === 0) {
556
+ // lastIndexOf(needle,0)===0 means StartsWith
557
+ projectionOptions = extend(projectionOptions, {
558
+ namespace: NAMESPACE_SVG,
559
+ });
560
+ }
561
+ if (previous.text !== vnode.text) {
562
+ updated = true;
563
+ if (vnode.text === undefined) {
564
+ domNode.removeChild(domNode.firstChild!); // the only textnode presumably
565
+ } else {
566
+ domNode.textContent = vnode.text;
567
+ }
568
+ }
569
+ updated =
570
+ updateChildren(
571
+ vnode,
572
+ domNode,
573
+ previous.children,
574
+ vnode.children,
575
+ projectionOptions
576
+ ) || updated;
577
+ updated =
578
+ updateProperties(
579
+ domNode,
580
+ previous.properties,
581
+ vnode.properties,
582
+ projectionOptions
583
+ ) || updated;
584
+ if (vnode.properties && vnode.properties.afterUpdate) {
585
+ vnode.properties.afterUpdate.apply(
586
+ vnode.properties.bind || vnode.properties,
587
+ [
588
+ <Element>domNode,
589
+ projectionOptions,
590
+ vnode.vnodeSelector,
591
+ vnode.properties,
592
+ vnode.children,
593
+ ]
594
+ );
595
+ }
596
+ }
597
+ if (updated && vnode.properties && vnode.properties.updateAnimation) {
598
+ vnode.properties.updateAnimation(
599
+ <Element>domNode,
600
+ vnode.properties,
601
+ previous.properties
602
+ );
603
+ }
604
+ vnode.domNode = previous.domNode;
605
+ return textUpdated;
606
+ };
607
+
608
+ const updateProperties = (
609
+ domNode: Node,
610
+ previousProperties: VNodeProperties | undefined,
611
+ properties: VNodeProperties | undefined,
612
+ projectionOptions: ProjectionOptions
613
+ ) => {
614
+ if (!properties) {
615
+ return;
616
+ }
617
+ let propertiesUpdated = false;
618
+ let propNames = Object.keys(properties);
619
+ let propCount = propNames.length;
620
+ for (let i = 0; i < propCount; i++) {
621
+ let propName = propNames[i];
622
+ // assuming that properties will be nullified instead of missing is by design
623
+ let propValue = properties[propName];
624
+ let previousValue = previousProperties![propName];
625
+ if (propName === "class") {
626
+ if (previousValue !== propValue) {
627
+ throw new Error(
628
+ '"class" property may not be updated. Use the "classes" property for conditional css classes.'
629
+ );
630
+ }
631
+ } else if (propName === "classes") {
632
+ let classList = (domNode as Element).classList;
633
+ let classNames = Object.keys(propValue);
634
+ let classNameCount = classNames.length;
635
+ for (let j = 0; j < classNameCount; j++) {
636
+ let className = classNames[j];
637
+ let on = !!propValue[className];
638
+ let previousOn = !!previousValue[className];
639
+ if (on === previousOn) {
640
+ continue;
641
+ }
642
+ propertiesUpdated = true;
643
+ if (on) {
644
+ classList.add(className);
645
+ } else {
646
+ classList.remove(className);
647
+ }
648
+ }
649
+ } else if (propName === "styles") {
650
+ let styleNames = Object.keys(propValue);
651
+ let styleCount = styleNames.length;
652
+ for (let j = 0; j < styleCount; j++) {
653
+ let styleName = styleNames[j];
654
+ let newStyleValue = propValue[styleName];
655
+ let oldStyleValue = previousValue[styleName];
656
+ if (newStyleValue === oldStyleValue) {
657
+ continue;
658
+ }
659
+ propertiesUpdated = true;
660
+ if (newStyleValue) {
661
+ checkStyleValue(newStyleValue);
662
+ projectionOptions.styleApplyer!(
663
+ domNode as HTMLElement,
664
+ styleName,
665
+ newStyleValue
666
+ );
667
+ } else {
668
+ projectionOptions.styleApplyer!(
669
+ domNode as HTMLElement,
670
+ styleName,
671
+ ""
672
+ );
673
+ }
674
+ }
675
+ } else {
676
+ if (!propValue && typeof previousValue === "string") {
677
+ propValue = "";
678
+ }
679
+ if (propName === "value") {
680
+ // value can be manipulated by the user directly and using event.preventDefault() is not an option
681
+ let domValue = (domNode as any)[propName];
682
+ if (
683
+ // The edge cases are described in the tests
684
+ domValue !== propValue && // The 'value' in the DOM tree !== newValue
685
+ ((domNode as any)["oninput-value"]
686
+ ? domValue === (domNode as any)["oninput-value"] // If the last reported value to 'oninput' does not match domValue, do nothing and wait for oninput
687
+ : propValue !== previousValue) // Only update the value if the vdom changed
688
+ ) {
689
+ (domNode as any)[propName] = propValue; // Reset the value, even if the virtual DOM did not change
690
+ (domNode as any)["oninput-value"] = undefined;
691
+ } // else do not update the domNode, otherwise the cursor position would be changed
692
+ if (propValue !== previousValue) {
693
+ propertiesUpdated = true;
694
+ }
695
+ } else if (propValue !== previousValue) {
696
+ let type = typeof propValue;
697
+ if (type === "function") {
698
+ throw new Error(
699
+ "Functions may not be updated on subsequent renders (property: " +
700
+ propName +
701
+ "). Hint: declare event handler functions outside the render() function."
702
+ );
703
+ }
704
+ if (type === "string" && propName !== "innerHTML") {
705
+ if (
706
+ projectionOptions.namespace === NAMESPACE_SVG &&
707
+ propName === "href"
708
+ ) {
709
+ (domNode as Element).setAttributeNS(
710
+ NAMESPACE_XLINK,
711
+ propName,
712
+ propValue
713
+ );
714
+ } else if (propName === "role" && propValue === "") {
715
+ (domNode as any).removeAttribute(propName);
716
+ } else {
717
+ (domNode as Element).setAttribute(propName, propValue);
718
+ }
719
+ } else {
720
+ if ((domNode as any)[propName] !== propValue) {
721
+ // Comparison is here for side-effects in Edge with scrollLeft and scrollTop
722
+ (domNode as any)[propName] = propValue;
723
+ }
724
+ }
725
+ propertiesUpdated = true;
726
+ }
727
+ }
728
+ }
729
+ return propertiesUpdated;
730
+ };