tangled
alpha
login
or
join now
yippee.fun
/
morphlex
0
fork
atom
Precise DOM morphing
morphing
typescript
dom
0
fork
atom
overview
issues
pulls
pipelines
Use an array for matches
joel.drapper.me
4 months ago
44905d1c
48092d3b
+41
-31
1 changed file
expand all
collapse all
unified
split
src
morphlex.ts
+41
-31
src/morphlex.ts
···
286
const candidateNodes: Set<ChildNode> = new Set()
287
const candidateElements: Set<Element> = new Set()
288
289
-
const unmatchedNodes: Set<ChildNode> = new Set()
290
-
const unmatchedElements: Set<Element> = new Set()
291
-
292
-
const matches: Map<ChildNode, ChildNode> = new Map()
293
294
for (const candidate of fromChildNodes) {
295
if (isElement(candidate)) candidateElements.add(candidate)
296
else candidateNodes.add(candidate)
297
}
298
299
-
for (const node of toChildNodes) {
300
-
if (isElement(node)) unmatchedElements.add(node)
301
-
else unmatchedNodes.add(node)
302
-
}
303
304
-
// Match elements by isEqualNode
305
-
for (const element of unmatchedElements) {
306
for (const candidate of candidateElements) {
307
if (candidate.isEqualNode(element)) {
308
-
matches.set(element, candidate)
309
-
unmatchedElements.delete(element)
310
candidateElements.delete(candidate)
311
break
312
}
···
314
}
315
316
// Match by exact id
317
-
for (const element of unmatchedElements) {
0
0
0
0
318
const id = element.id
319
if (id === "") continue
320
321
for (const candidate of candidateElements) {
322
if (element.localName === candidate.localName && id === candidate.id) {
323
-
matches.set(element, candidate)
324
-
unmatchedElements.delete(element)
325
candidateElements.delete(candidate)
326
break
327
}
···
329
}
330
331
// Match by idSet
332
-
for (const element of unmatchedElements) {
0
0
333
if (!isElement(element)) continue
0
334
const idSet = this.idMap.get(element)
335
if (!idSet) continue
336
···
340
if (candidateIdSet) {
341
for (const id of idSet) {
342
if (candidateIdSet.has(id)) {
343
-
matches.set(element, candidate)
344
-
unmatchedElements.delete(element)
345
candidateElements.delete(candidate)
346
break candidateLoop
347
}
···
352
}
353
354
// Match by huristics
355
-
for (const element of unmatchedElements) {
0
0
356
if (!isElement(element)) continue
0
357
const name = element.getAttribute("name")
358
const href = element.getAttribute("href")
359
const src = element.getAttribute("src")
···
366
(href !== "" && href === candidate.getAttribute("href")) ||
367
(src !== "" && src === candidate.getAttribute("src")))
368
) {
369
-
matches.set(element, candidate)
370
-
unmatchedElements.delete(element)
371
candidateElements.delete(candidate)
372
break
373
}
···
375
}
376
377
// Match by tagName
378
-
for (const element of unmatchedElements) {
0
0
0
0
379
const localName = element.localName
380
381
for (const candidate of candidateElements) {
···
384
// Treat inputs with different type as though they are different tags.
385
continue
386
}
387
-
matches.set(element, candidate)
388
-
unmatchedElements.delete(element)
389
candidateElements.delete(candidate)
390
break
391
}
···
393
}
394
395
// Match nodes by isEqualNode
396
-
for (const node of unmatchedNodes) {
0
0
0
0
397
for (const candidate of candidateNodes) {
398
if (candidate.isEqualNode(node)) {
399
-
matches.set(node, candidate)
400
-
unmatchedNodes.delete(node)
401
candidateNodes.delete(candidate)
402
break
403
}
···
405
}
406
407
// Match by nodeType
408
-
for (const node of unmatchedNodes) {
0
0
0
0
409
const nodeType = node.nodeType
410
411
for (const candidate of candidateNodes) {
412
if (nodeType === candidate.nodeType) {
413
-
matches.set(node, candidate)
414
-
unmatchedNodes.delete(node)
415
candidateNodes.delete(candidate)
416
break
417
}
···
422
let insertionPoint: ChildNode | null = parent.firstChild
423
for (let i = 0; i < toChildNodes.length; i++) {
424
const node = toChildNodes[i]!
425
-
const match = matches.get(node)
426
if (match) {
427
moveBefore(parent, match, insertionPoint)
428
this.morphOneToOne(match, node)
···
286
const candidateNodes: Set<ChildNode> = new Set()
287
const candidateElements: Set<Element> = new Set()
288
289
+
const matches: Array<ChildNode | null> = new Array(toChildNodes.length).fill(null)
0
0
0
290
291
for (const candidate of fromChildNodes) {
292
if (isElement(candidate)) candidateElements.add(candidate)
293
else candidateNodes.add(candidate)
294
}
295
296
+
// Match elements by isEqualNode
297
+
for (let i = 0; i < toChildNodes.length; i++) {
298
+
const element = toChildNodes[i]!
299
+
if (!isElement(element)) continue
300
0
0
301
for (const candidate of candidateElements) {
302
if (candidate.isEqualNode(element)) {
303
+
matches[i] = candidate
0
304
candidateElements.delete(candidate)
305
break
306
}
···
308
}
309
310
// Match by exact id
311
+
for (let i = 0; i < toChildNodes.length; i++) {
312
+
if (matches[i]) continue
313
+
const element = toChildNodes[i]!
314
+
if (!isElement(element)) continue
315
+
316
const id = element.id
317
if (id === "") continue
318
319
for (const candidate of candidateElements) {
320
if (element.localName === candidate.localName && id === candidate.id) {
321
+
matches[i] = candidate
0
322
candidateElements.delete(candidate)
323
break
324
}
···
326
}
327
328
// Match by idSet
329
+
for (let i = 0; i < toChildNodes.length; i++) {
330
+
if (matches[i]) continue
331
+
const element = toChildNodes[i]!
332
if (!isElement(element)) continue
333
+
334
const idSet = this.idMap.get(element)
335
if (!idSet) continue
336
···
340
if (candidateIdSet) {
341
for (const id of idSet) {
342
if (candidateIdSet.has(id)) {
343
+
matches[i] = candidate
0
344
candidateElements.delete(candidate)
345
break candidateLoop
346
}
···
351
}
352
353
// Match by huristics
354
+
for (let i = 0; i < toChildNodes.length; i++) {
355
+
if (matches[i]) continue
356
+
const element = toChildNodes[i]!
357
if (!isElement(element)) continue
358
+
359
const name = element.getAttribute("name")
360
const href = element.getAttribute("href")
361
const src = element.getAttribute("src")
···
368
(href !== "" && href === candidate.getAttribute("href")) ||
369
(src !== "" && src === candidate.getAttribute("src")))
370
) {
371
+
matches[i] = candidate
0
372
candidateElements.delete(candidate)
373
break
374
}
···
376
}
377
378
// Match by tagName
379
+
for (let i = 0; i < toChildNodes.length; i++) {
380
+
if (matches[i]) continue
381
+
const element = toChildNodes[i]!
382
+
if (!isElement(element)) continue
383
+
384
const localName = element.localName
385
386
for (const candidate of candidateElements) {
···
389
// Treat inputs with different type as though they are different tags.
390
continue
391
}
392
+
matches[i] = candidate
0
393
candidateElements.delete(candidate)
394
break
395
}
···
397
}
398
399
// Match nodes by isEqualNode
400
+
for (let i = 0; i < toChildNodes.length; i++) {
401
+
if (matches[i]) continue
402
+
const node = toChildNodes[i]!
403
+
if (isElement(node)) continue
404
+
405
for (const candidate of candidateNodes) {
406
if (candidate.isEqualNode(node)) {
407
+
matches[i] = candidate
0
408
candidateNodes.delete(candidate)
409
break
410
}
···
412
}
413
414
// Match by nodeType
415
+
for (let i = 0; i < toChildNodes.length; i++) {
416
+
if (matches[i]) continue
417
+
const node = toChildNodes[i]!
418
+
if (isElement(node)) continue
419
+
420
const nodeType = node.nodeType
421
422
for (const candidate of candidateNodes) {
423
if (nodeType === candidate.nodeType) {
424
+
matches[i] = candidate
0
425
candidateNodes.delete(candidate)
426
break
427
}
···
432
let insertionPoint: ChildNode | null = parent.firstChild
433
for (let i = 0; i < toChildNodes.length; i++) {
434
const node = toChildNodes[i]!
435
+
const match = matches[i]
436
if (match) {
437
moveBefore(parent, match, insertionPoint)
438
this.morphOneToOne(match, node)