Better diff option
This commit is contained in:
@@ -6,33 +6,43 @@ import org.w3c.dom.NodeList
|
|||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.get
|
import org.w3c.dom.get
|
||||||
|
|
||||||
|
const val HASH_VALUE = "komp-hash-value"
|
||||||
|
//const val HASH_ATTRIBUTE = "data-komp-hash"
|
||||||
|
const val EVENT_ATTRIBUTE = "data-komp-events"
|
||||||
|
|
||||||
private fun NodeList.findNodeWithHash(hash: String): HTMLElement? {
|
fun Node.getKompHash(): Int = this.asDynamic()[HASH_VALUE] as? Int? ?: -1
|
||||||
|
|
||||||
|
fun Node.setKompHash(hash: Int) {
|
||||||
|
this.asDynamic()[HASH_VALUE] = hash
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NodeList.findNodeHashIndex(hash: Int): Int {
|
||||||
for (index in 0..this.length) {
|
for (index in 0..this.length) {
|
||||||
val node = this[index]
|
val node = this[index]
|
||||||
if (node is HTMLElement && node.getAttribute(DiffPatch.HASH_ATTRIBUTE) == hash) {
|
if (node is HTMLElement && node.getKompHash() == hash) {
|
||||||
return node
|
return index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
object DiffPatch {
|
object DiffPatch {
|
||||||
const val HASH_ATTRIBUTE = "data-komp-hash"
|
|
||||||
|
|
||||||
fun hashesMatch(oldNode: Node, newNode: Node): Boolean {
|
fun hashesMatch(oldNode: Node, newNode: Node): Boolean {
|
||||||
return (
|
return (
|
||||||
oldNode is HTMLElement &&
|
oldNode is HTMLElement &&
|
||||||
newNode is HTMLElement &&
|
newNode is HTMLElement &&
|
||||||
oldNode.nodeName == newNode.nodeName &&
|
oldNode.nodeName == newNode.nodeName &&
|
||||||
oldNode.getAttribute(HASH_ATTRIBUTE) != null &&
|
oldNode.getKompHash() == newNode.getKompHash()
|
||||||
oldNode.getAttribute(HASH_ATTRIBUTE) == newNode.getAttribute(HASH_ATTRIBUTE)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateNode(oldNode: Node, newNode: Node): Node {
|
fun updateNode(oldNode: Node, newNode: Node): Node {
|
||||||
if (hashesMatch(oldNode, newNode)) {
|
if (hashesMatch(oldNode, newNode)) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Hashes match", oldNode, newNode, oldNode.getKompHash(), newNode.getKompHash())
|
||||||
|
}
|
||||||
return oldNode
|
return oldNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +52,8 @@ object DiffPatch {
|
|||||||
console.log("Updating text content", oldNode, newNode)
|
console.log("Updating text content", oldNode, newNode)
|
||||||
}
|
}
|
||||||
oldNode.textContent = newNode.textContent
|
oldNode.textContent = newNode.textContent
|
||||||
return oldNode
|
|
||||||
}
|
}
|
||||||
|
return oldNode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldNode is HTMLElement && newNode is HTMLElement) {
|
if (oldNode is HTMLElement && newNode is HTMLElement) {
|
||||||
@@ -57,12 +67,13 @@ object DiffPatch {
|
|||||||
}
|
}
|
||||||
updateChildren(oldNode, newNode)
|
updateChildren(oldNode, newNode)
|
||||||
updateEvents(oldNode, newNode)
|
updateEvents(oldNode, newNode)
|
||||||
|
oldNode.setKompHash(newNode.getKompHash())
|
||||||
return oldNode
|
return oldNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Replace node", oldNode, newNode)
|
console.log("Replace node (type)", oldNode.nodeType, oldNode, newNode)
|
||||||
}
|
}
|
||||||
replaceNode(oldNode, newNode)
|
replaceNode(oldNode, newNode)
|
||||||
return newNode
|
return newNode
|
||||||
@@ -96,12 +107,11 @@ object DiffPatch {
|
|||||||
var newIndex = 0
|
var newIndex = 0
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("updateChildren old/new count", oldNode.childNodes.length, newNode.childNodes.length)
|
console.log("updateChildren HTML old/new", oldNode.innerHTML, newNode.innerHTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (newIndex < newNode.childNodes.length) {
|
while (newIndex < newNode.childNodes.length) {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log(">>> updateChildren old/new count", oldNode.childNodes, newNode.childNodes)
|
|
||||||
console.log("Update Old/new", oldIndex, newIndex)
|
console.log("Update Old/new", oldIndex, newIndex)
|
||||||
}
|
}
|
||||||
val newChildNode = newNode.childNodes[newIndex]
|
val newChildNode = newNode.childNodes[newIndex]
|
||||||
@@ -109,41 +119,77 @@ object DiffPatch {
|
|||||||
if (oldIndex < oldNode.childNodes.length) {
|
if (oldIndex < oldNode.childNodes.length) {
|
||||||
val oldChildNode = oldNode.childNodes[oldIndex]
|
val oldChildNode = oldNode.childNodes[oldIndex]
|
||||||
|
|
||||||
|
|
||||||
if (oldChildNode != null && newChildNode != null) {
|
if (oldChildNode != null && newChildNode != null) {
|
||||||
if (!hashesMatch(oldChildNode, newChildNode) && newChildNode is HTMLElement && oldChildNode is HTMLElement) {
|
/*
|
||||||
val oldHash = oldChildNode.getAttribute("data-komp-hash")
|
if (Komponent.logReplaceEvent) {
|
||||||
val newHash = newChildNode.getAttribute("data-komp-hash")
|
console.log(">>> updateChildren old/new", oldChildNode, newChildNode)
|
||||||
|
|
||||||
if (oldHash != null) {
|
|
||||||
val nodeWithHash = oldNode.childNodes.findNodeWithHash(oldHash)
|
|
||||||
|
|
||||||
if (nodeWithHash != null) {
|
|
||||||
if (Komponent.logReplaceEvent) {
|
|
||||||
console.log(">-> swap nodes", oldNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldNode.replaceChild(oldChildNode, nodeWithHash)
|
|
||||||
oldNode.insertBefore(nodeWithHash, oldNode.childNodes[oldIndex])
|
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
|
||||||
console.log(">-> swapped nodes", oldNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (newHash != null) {
|
|
||||||
// if node found after current new index, insert new node
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Update node Old/new", oldChildNode, newChildNode)
|
console.log("Update node Old/new", oldChildNode, newChildNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNode(oldChildNode, newChildNode)
|
if (!hashesMatch(oldChildNode, newChildNode) && newChildNode is HTMLElement && oldChildNode is HTMLElement) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Hashes don't match")
|
||||||
|
}
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
val oldHash = oldChildNode.getKompHash()
|
||||||
console.log("--- Updated Old/new", oldNode.children, newNode.children)
|
val newHash = newChildNode.getKompHash()
|
||||||
|
|
||||||
|
if (newHash != null) {
|
||||||
|
val oldNodeWithNewHashIndex = oldNode.childNodes.findNodeHashIndex(newHash)
|
||||||
|
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("oldNodeWithNewHashIndex", newHash, oldNodeWithNewHashIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldNodeWithNewHashIndex > oldIndex) {
|
||||||
|
if (oldHash != null) {
|
||||||
|
val newNodeWithOldHashIndex = newNode.childNodes.findNodeHashIndex(oldHash)
|
||||||
|
|
||||||
|
// remove i.o. swap
|
||||||
|
if (newNodeWithOldHashIndex == -1) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Old node missing in new tree, remove node", oldChildNode)
|
||||||
|
}
|
||||||
|
oldNode.removeChild(oldChildNode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val nodeWithHash = oldNode.childNodes[oldNodeWithNewHashIndex]
|
||||||
|
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("nodeWithHash", nodeWithHash)
|
||||||
|
}
|
||||||
|
if (nodeWithHash != null) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log(">-> swap nodes", oldNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldNode.insertBefore(nodeWithHash, oldNode.childNodes[oldIndex])
|
||||||
|
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log(">-> swapped nodes", oldNode)
|
||||||
|
}
|
||||||
|
newIndex++
|
||||||
|
oldIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if (oldHash != null && newNode.childNodes.findNodeHashIndex(oldHash) > newIndex) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("newNodeWithOldHashIndex", oldHash, newNode.childNodes.findNodeHashIndex(oldHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
oldNode.insertBefore(newChildNode, oldChildNode)
|
||||||
|
oldIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateNode(oldChildNode, newChildNode)
|
||||||
} else {
|
} else {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Null node", oldChildNode, newChildNode)
|
console.log("Null node", oldChildNode, newChildNode)
|
||||||
@@ -156,9 +202,11 @@ object DiffPatch {
|
|||||||
oldNode.append(newChildNode)
|
oldNode.append(newChildNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("<<< Updated Old/new", oldNode.children, newNode.children)
|
console.log("<<< Updated Old/new", oldNode.innerHTML, newNode.innerHTML)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
oldIndex++
|
oldIndex++
|
||||||
newIndex++
|
newIndex++
|
||||||
@@ -172,15 +220,14 @@ object DiffPatch {
|
|||||||
|
|
||||||
oldNode.removeChild(it)
|
oldNode.removeChild(it)
|
||||||
}
|
}
|
||||||
oldIndex++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateEvents(oldNode: HTMLElement, newNode: HTMLElement) {
|
private fun updateEvents(oldNode: HTMLElement, newNode: HTMLElement) {
|
||||||
val oldEvents = mutableListOf<String>()
|
val oldEvents = mutableListOf<String>()
|
||||||
oldEvents.addAll((oldNode.getAttribute("data-komp-events") ?: "").split(","))
|
oldEvents.addAll((oldNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(","))
|
||||||
|
|
||||||
val newEvents = (newNode.getAttribute("data-komp-events") ?: "").split(",")
|
val newEvents = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||||
|
|
||||||
for (event in newEvents) {
|
for (event in newEvents) {
|
||||||
if (event.isNotBlank()) {
|
if (event.isNotBlank()) {
|
||||||
@@ -206,8 +253,8 @@ object DiffPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newNode.getAttribute("data-komp-events")?.also {
|
newNode.getAttribute(EVENT_ATTRIBUTE)?.also {
|
||||||
oldNode.setAttribute("data-komp-events", it)
|
oldNode.setAttribute(EVENT_ATTRIBUTE, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +262,7 @@ object DiffPatch {
|
|||||||
oldNode.parentNode?.also { parent ->
|
oldNode.parentNode?.also { parent ->
|
||||||
val clone = newNode.cloneNode(true)
|
val clone = newNode.cloneNode(true)
|
||||||
if (newNode is HTMLElement) {
|
if (newNode is HTMLElement) {
|
||||||
val events = (newNode.getAttribute("data-komp-events") ?: "").split(",")
|
val events = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||||
for (event in events) {
|
for (event in events) {
|
||||||
val foundEvent = newNode.asDynamic()["event-$event"]
|
val foundEvent = newNode.asDynamic()["event-$event"]
|
||||||
if (foundEvent != null) {
|
if (foundEvent != null) {
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ private inline fun HTMLElement.setEvent(name: String, noinline callback: (Event)
|
|||||||
addEventListener(eventName, callback, null)
|
addEventListener(eventName, callback, null)
|
||||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
//asDynamic()[name] = callback
|
//asDynamic()[name] = callback
|
||||||
val events = getAttribute("data-komp-events") ?: ""
|
val events = getAttribute(EVENT_ATTRIBUTE) ?: ""
|
||||||
|
|
||||||
setAttribute(
|
setAttribute(
|
||||||
"data-komp-events",
|
EVENT_ATTRIBUTE,
|
||||||
if (events.isBlank()) {
|
if (events.isBlank()) {
|
||||||
eventName
|
eventName
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +85,7 @@ class HtmlBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTagEnd(tag: Tag) {
|
override fun onTagEnd(tag: Tag) {
|
||||||
var hash: UInt = 0.toUInt()
|
var hash = 0
|
||||||
if (path.isEmpty() || path.last().tagName.toLowerCase() != tag.tagName.toLowerCase()) {
|
if (path.isEmpty() || path.last().tagName.toLowerCase() != tag.tagName.toLowerCase()) {
|
||||||
throw IllegalStateException("We haven't entered tag ${tag.tagName} but trying to leave")
|
throw IllegalStateException("We haven't entered tag ${tag.tagName} but trying to leave")
|
||||||
}
|
}
|
||||||
@@ -96,17 +96,17 @@ class HtmlBuilder(
|
|||||||
for (index in 0 until element.childNodes.length) {
|
for (index in 0 until element.childNodes.length) {
|
||||||
val child = element.childNodes[index]
|
val child = element.childNodes[index]
|
||||||
if (child is HTMLElement) {
|
if (child is HTMLElement) {
|
||||||
hash = hash * 37.toUInt() + (child.getAttribute(DiffPatch.HASH_ATTRIBUTE)?.toUInt(16) ?: 0.toUInt())
|
hash = hash * 37 + child.getKompHash()
|
||||||
} else {
|
} else {
|
||||||
hash = hash * 37.toUInt() + (child?.textContent?.hashCode()?.toUInt() ?: 0.toUInt())
|
hash = hash * 37 + (child?.textContent?.hashCode() ?: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tag.attributesEntries.forEach {
|
tag.attributesEntries.forEach {
|
||||||
val key_value = "${it.key}-${it.value}"
|
|
||||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
hash = hash * 37.toUInt() + key_value.hashCode().toUInt()
|
val key_value = "${it.key}-${it.value}"
|
||||||
|
hash = hash * 37 + key_value.hashCode()
|
||||||
}
|
}
|
||||||
if (it.key == "class") {
|
if (it.key == "class") {
|
||||||
val classes = it.value.split(Regex("\\s+"))
|
val classes = it.value.split(Regex("\\s+"))
|
||||||
@@ -150,7 +150,8 @@ class HtmlBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
element.setAttribute(DiffPatch.HASH_ATTRIBUTE, hash.toString(16))
|
element.setKompHash(hash)
|
||||||
|
//element.setAttribute(DiffPatch.HASH_ATTRIBUTE, hash.toString(16))
|
||||||
}
|
}
|
||||||
lastLeaved = path.removeAt(path.lastIndex)
|
lastLeaved = path.removeAt(path.lastIndex)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ abstract class Komponent {
|
|||||||
consumer.render()
|
consumer.render()
|
||||||
val result = consumer.finalize()
|
val result = consumer.finalize()
|
||||||
|
|
||||||
|
if (logReplaceEvent) {
|
||||||
|
console.log("Element hash", result, result.getKompHash())
|
||||||
|
}
|
||||||
|
|
||||||
element = result
|
element = result
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
Reference in New Issue
Block a user