From fa6252832ba65989e826431d470885ff62a303e8 Mon Sep 17 00:00:00 2001 From: rnentjes Date: Tue, 29 Jun 2021 17:21:27 +0200 Subject: [PATCH] Cleanup --- komp.commonMain.iml | 2 +- komp.commonTest.iml | 4 +- .../kotlin/nl/astraeus/komp/DiffPatch.kt | 47 ++++ .../kotlin/nl/astraeus/komp/HtmlBuilder.kt | 207 +---------------- .../kotlin/nl/astraeus/komp/VDOMElement.kt | 214 ++++++++++++++++++ 5 files changed, 269 insertions(+), 205 deletions(-) create mode 100644 src/jsMain/kotlin/nl/astraeus/komp/VDOMElement.kt diff --git a/komp.commonMain.iml b/komp.commonMain.iml index c4f52a9..bafe558 100644 --- a/komp.commonMain.iml +++ b/komp.commonMain.iml @@ -2,7 +2,7 @@ - + SOURCE_SET_HOLDER diff --git a/komp.commonTest.iml b/komp.commonTest.iml index 4d4570e..4cab7e0 100644 --- a/komp.commonTest.iml +++ b/komp.commonTest.iml @@ -2,7 +2,7 @@ - + SOURCE_SET_HOLDER jsLegacyBrowserTest|komp:jsTest|jsLegacy @@ -36,9 +36,9 @@ + - \ No newline at end of file diff --git a/src/jsMain/kotlin/nl/astraeus/komp/DiffPatch.kt b/src/jsMain/kotlin/nl/astraeus/komp/DiffPatch.kt index 29892d0..21adc55 100644 --- a/src/jsMain/kotlin/nl/astraeus/komp/DiffPatch.kt +++ b/src/jsMain/kotlin/nl/astraeus/komp/DiffPatch.kt @@ -1,10 +1,57 @@ package nl.astraeus.komp +import org.w3c.dom.Element import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLInputElement import org.w3c.dom.Node +import org.w3c.dom.events.Event import org.w3c.dom.get +fun Element.removeKompEventListeners() { + val events = this.asDynamic().kompEvents as? MutableMap Unit> + + if (events != null) { + for ((name, event) in events) { + this.removeEventListener(name, event) + } + } +} + +fun Element.removeKompEventListener(name: String) { + val events = this.asDynamic().kompEvents as? MutableMap Unit> + + if (events != null) { + val event = events[name] + this.removeEventListener(name, event) + events.remove(name) + } +} + +fun Element.checkKompEventListenersEmpty() { + val events = this.asDynamic().kompEvents as? MutableMap Unit> + + if (events != null && !events.isEmpty()) { + console.log("Expected events to be empty, found:", events) + } +} + +fun Element.addKompEventListener(name: String, event: (Event) -> Unit) { + var events = this.asDynamic().kompEvents as? MutableMap Unit> + if (events == null) { + this.asDynamic().kompEvents = mutableMapOf Unit>() + events = this.asDynamic().kompEvents as? MutableMap Unit> + } + + events?.get(name)?.let { it -> + removeEventListener(name, it) + + console.log("Overwriting event $name on", this) + } + + events?.put(name, event) + addEventListener(name, event) +} + object DiffPatch { private fun updateKomponentOnNode(element: Node, newVdom: VDOMElement) { diff --git a/src/jsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt b/src/jsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt index 89932f6..4b779c1 100644 --- a/src/jsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt +++ b/src/jsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt @@ -1,208 +1,11 @@ package nl.astraeus.komp -import kotlinx.browser.document -import kotlinx.html.* -import org.w3c.dom.Element -import org.w3c.dom.Node +import kotlinx.html.DefaultUnsafe +import kotlinx.html.Entities +import kotlinx.html.Tag +import kotlinx.html.TagConsumer +import kotlinx.html.Unsafe import org.w3c.dom.events.Event -import kotlin.collections.MutableList -import kotlin.collections.MutableMap -import kotlin.collections.arrayListOf -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.collections.isNotEmpty -import kotlin.collections.iterator -import kotlin.collections.last -import kotlin.collections.lastIndex -import kotlin.collections.mutableListOf -import kotlin.collections.mutableMapOf -import kotlin.collections.set -import kotlin.collections.withIndex - -private fun attributeHash(key: String, value: String): Int = - 3 * key.hashCode() + - 5 * value.hashCode() - -private fun MutableMap.kompHash(): Int { - var result = 0 - - for ((name, value) in this) { - result += attributeHash(name, value) - } - - return result -} - -private fun MutableMap Unit>.kompHash(): Int { - var result = 0 - - for ((name, event) in this) { - result += attributeHash(name, event.toString()) - } - - return result -} - -private fun MutableList.kompHash(): Int { - var result = 0 - - for (vdom in this) { - result += 3 * vdom.hash.hashCode() - } - - return result -} - -enum class VDOMElementType { - TAG, - TEXT, - ENTITY, - UNSAFE, - COMMENT -} - -class VDOMElementHash( - var baseHash: Int, - var contentHash: Int, - var typeHash: Int, - var namespaceHash: Int = 0, - var attributesHash: Int = 0, - var eventsHash: Int = 0, - var childNodesHash: Int = 0 -) { - - override fun hashCode(): Int = baseHash + - 3 * contentHash + - 5 * typeHash + - 7 * namespaceHash + - 11 * attributesHash + - 13 * eventsHash + - 15 * childNodesHash - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is VDOMElementHash) return false - - return other.hashCode() == this.hashCode() - } - -} - -class VDOMElement( - val baseHash: Int, - var content: String, - var namespace: String? = null, - var type: VDOMElementType = VDOMElementType.TAG, -) { - val attributes: MutableMap = mutableMapOf() - val events: MutableMap Unit> = mutableMapOf() - val childNodes: MutableList = mutableListOf() - - val hash = VDOMElementHash( - baseHash, - content.hashCode(), - type.hashCode() - ) - - var id: String = "" - set(value) { - field = value - attributes["id"] = value - } - var komponent: Komponent? = null - - fun setKompEvent(event: String, value: (Event) -> Unit) { - val eventName = if (event.startsWith("on")) { - event.substring(2) - } else { - event - } - val recalculate = events.containsKey(eventName) - events[eventName] = value - if (recalculate) { - hash.eventsHash = events.kompHash() - } else { - hash.eventsHash += attributeHash(eventName, value.toString()) - } - } - - fun appendChild(element: VDOMElement) { - childNodes.add(element) - //hash.childNodesHash += element.hash.hashCode() - } - - fun updateChildHash() { - hash.childNodesHash = childNodes.kompHash() - } - - fun removeAttribute(attr: String) { - if (attributes.containsKey(attr)) { - hash.attributesHash -= attributeHash(attr, attributes[attr] ?: "") - } - attributes.remove(attr) - } - - fun setAttribute(attr: String, value: String) { - if (attributes.containsKey(attr)) { - hash.attributesHash -= attributeHash(attr, attributes[attr] ?: "") - } - if (attr.toLowerCase() == "id") { - id = value - } - attributes[attr] = value - hash.attributesHash += attributeHash(attr, value) - } - - fun findNodeHashIndex(hash: Int): Int { - for ((index, node) in this.childNodes.withIndex()) { - if (node.type == VDOMElementType.TAG && node.hash.hashCode() == hash) { - return index - } - } - - return -2 - } - - fun createElement(): Node { - val result = when (type) { - VDOMElementType.TAG -> { - val result: Element = if (namespace != null) { - document.createElementNS(namespace, content) - } else { - document.createElement(content) - } - - for ((name, value) in attributes) { - result.setAttribute(name, value) - } - - for ((name, value) in events) { - result.addEventListener(name, value) - } - - for (child in childNodes) { - result.appendChild(child.createElement()) - } - - result - } - VDOMElementType.ENTITY, - VDOMElementType.UNSAFE, - VDOMElementType.TEXT -> { - document.createTextNode(content) - } - VDOMElementType.COMMENT -> { - document.createComment(content) - } - } - - komponent?.also { - it.element = result - } - - return result - } -} interface HtmlConsumer : TagConsumer { fun append(node: VDOMElement) diff --git a/src/jsMain/kotlin/nl/astraeus/komp/VDOMElement.kt b/src/jsMain/kotlin/nl/astraeus/komp/VDOMElement.kt new file mode 100644 index 0000000..cde2864 --- /dev/null +++ b/src/jsMain/kotlin/nl/astraeus/komp/VDOMElement.kt @@ -0,0 +1,214 @@ +package nl.astraeus.komp + +import kotlinx.browser.document +import org.w3c.dom.Element +import org.w3c.dom.Node +import org.w3c.dom.asList +import org.w3c.dom.events.Event + +private fun attributeHash(key: String, value: String): Int = + 3 * key.hashCode() + + 5 * value.hashCode() + +private fun MutableMap.kompHash(): Int { + var result = 0 + + for ((name, value) in this) { + result += attributeHash(name, value) + } + + return result +} + +private fun MutableMap Unit>.kompHash(): Int { + var result = 0 + + for ((name, event) in this) { + result += attributeHash(name, event.toString()) + } + + return result +} + +private fun MutableList.kompHash(): Int { + var result = 0 + + for (vdom in this) { + result += 3 * vdom.hash.hashCode() + } + + return result +} + + +enum class VDOMElementType { + TAG, + TEXT, + ENTITY, + UNSAFE, + COMMENT +} + +class VDOMElementHash( + var baseHash: Int, + var contentHash: Int, + var typeHash: Int, + var namespaceHash: Int = 0, + var attributesHash: Int = 0, + var eventsHash: Int = 0, + var childIndexHash: Int = 0, + var childNodesHash: Int = 0 +) { + + override fun hashCode(): Int { + var result = baseHash + + result = result * 7 + contentHash + result = result * 7 + typeHash + result = result * 7 + namespaceHash + result = result * 7 + attributesHash + result = result * 7 + eventsHash + result = result * 7 + childIndexHash + result = result * 7 + childNodesHash + + return result + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is VDOMElementHash) return false + + return other.hashCode() == this.hashCode() + } + +} + +class VDOMElement( + val baseHash: Int, + var content: String, + var namespace: String? = null, + var type: VDOMElementType = VDOMElementType.TAG, +) { + val attributes: MutableMap = mutableMapOf() + val events: MutableMap Unit> = mutableMapOf() + val childNodes: MutableList = mutableListOf() + + val hash = VDOMElementHash( + baseHash, + content.hashCode(), + type.hashCode() + ) + + var id: String = "" + set(value) { + field = value + attributes["id"] = value + } + var komponent: Komponent? = null + + fun setKompEvent(event: String, value: (Event) -> Unit) { + val eventName = if (event.startsWith("on")) { + event.substring(2) + } else { + event + } + val recalculate = events.containsKey(eventName) + events[eventName] = value + if (recalculate) { + hash.eventsHash = events.kompHash() + } else { + hash.eventsHash += attributeHash(eventName, value.toString()) + } + } + + fun appendChild(element: VDOMElement) { + childNodes.add(element) + //hash.childNodesHash += element.hash.hashCode() + } + + fun updateChildHash() { + hash.childNodesHash = childNodes.kompHash() + } + + fun removeAttribute(attr: String) { + if (attributes.containsKey(attr)) { + hash.attributesHash -= attributeHash(attr, attributes[attr] ?: "") + } + attributes.remove(attr) + } + + fun setAttribute(attr: String, value: String) { + if (attributes.containsKey(attr)) { + hash.attributesHash -= attributeHash(attr, attributes[attr] ?: "") + } + if (attr.toLowerCase() == "id") { + id = value + } + attributes[attr] = value + hash.attributesHash += attributeHash(attr, value) + } + + fun findNodeHashIndex(hash: Int): Int { + for ((index, node) in this.childNodes.withIndex()) { + if (node.type == VDOMElementType.TAG && node.hash.hashCode() == hash) { + return index + } + } + + return -2 + } + + fun createElement(): Node = createActualElement() + + private fun createActualElement(currentNamespace: String? = null): Node { + val result = when (type) { + VDOMElementType.TAG -> { + var tagNamespace: String? = null + val result: Element = if (namespace != null) { + tagNamespace = namespace + document.createElementNS(namespace, content) + } else { + document.createElement(content) + } + + for ((name, value) in attributes) { + result.setAttribute(name, value) + } + + for ((name, value) in events) { + result.addEventListener(name, value) + } + + for (child in childNodes) { + result.appendChild(child.createActualElement(tagNamespace)) + } + + result + } + VDOMElementType.ENTITY -> { + println("Creating an entity is not supported!") + document.createTextNode(content) + } + VDOMElementType.UNSAFE, + VDOMElementType.TEXT -> { + val span = if (currentNamespace != null) { + document.createElementNS(currentNamespace, "span") + } else { + document.createElement("span") + } + + span.innerHTML = content + span.childNodes.asList().firstOrNull() ?: document.createTextNode(content) + } + VDOMElementType.COMMENT -> { + document.createComment(content) + } + } + + komponent?.also { + it.element = result + } + + return result + } +}