2 Commits
master ... vdom

Author SHA1 Message Date
d0442e785f VDom implementation 2021-04-05 17:21:45 +02:00
68fde339a8 Remove replace option 2021-03-31 16:17:01 +02:00
11 changed files with 457 additions and 506 deletions

View File

@@ -5,7 +5,7 @@ plugins {
} }
group = "nl.astraeus" group = "nl.astraeus"
version = "0.2.6-SNAPSHOT" version = "0.3.0-SNAPSHOT"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:commonMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.2.5-SNAPSHOT" type="JAVA_MODULE" version="4"> <module external.linked.project.id="komp:commonMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="kotlin-language" name="Kotlin"> <facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.6]/Native []/Native [general]" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin;/home/rnentjes/Development/komp/komp/src/commonMain/kotlin"> <configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.8]/Native []/Native [general]" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind> <newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings /> <compilerSettings />
<compilerArguments> <compilerArguments>
@@ -13,13 +13,13 @@
</option> </option>
<option name="pluginClasspaths"> <option name="pluginClasspaths">
<array> <array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.30/7d0085e5781847da84580d16239c1c4e0980bdb0/kotlin-scripting-jvm-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.32/49656d531bfab9d6e45d3f27bc4d03b542d6766/kotlin-scripting-jvm-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.30/c7896c5c6b504f44e7cf9f5719d4393e20680666/kotlin-scripting-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.32/6abda0fe69677f0e46e7539fd185e4bd093b7258/kotlin-scripting-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.30/d10d1e10f47006ee08162dde039e38ac487de4ac/kotlin-stdlib-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.32/461367948840adbb0839c51d91ed74ef4a9ccb52/kotlin-stdlib-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/bb9a3173350f55732416ee27956ea8f9b81f4dbb/kotlin-stdlib-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.30/b9c2a1fab9217623fc0fbadf6190e77eed6f054d/kotlin-script-runtime-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
</array> </array>
</option> </option>
<option name="multiPlatform" value="true" /> <option name="multiPlatform" value="true" />
@@ -32,7 +32,7 @@
<content url="file://$MODULE_DIR$/src/commonMain" /> <content url="file://$MODULE_DIR$/src/commonMain" />
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" /> <orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" />
</component> </component>
</module> </module>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:commonTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.2.5-SNAPSHOT" type="JAVA_MODULE" version="4"> <module external.linked.project.id="komp:commonTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="kotlin-language" name="Kotlin"> <facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.6]/Native []/Native [general]" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin;/home/rnentjes/Development/komp/komp/src/commonMain/kotlin"> <configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.8]/Native []/Native [general]" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind> <newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<externalSystemTestTasks> <externalSystemTestTasks>
<externalSystemTestTask>jsLegacyBrowserTest|komp:jsTest|jsLegacy</externalSystemTestTask> <externalSystemTestTask>jsLegacyBrowserTest|komp:jsTest|jsLegacy</externalSystemTestTask>
@@ -16,13 +16,13 @@
</option> </option>
<option name="pluginClasspaths"> <option name="pluginClasspaths">
<array> <array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.30/7d0085e5781847da84580d16239c1c4e0980bdb0/kotlin-scripting-jvm-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.32/49656d531bfab9d6e45d3f27bc4d03b542d6766/kotlin-scripting-jvm-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.30/c7896c5c6b504f44e7cf9f5719d4393e20680666/kotlin-scripting-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.32/6abda0fe69677f0e46e7539fd185e4bd093b7258/kotlin-scripting-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.30/d10d1e10f47006ee08162dde039e38ac487de4ac/kotlin-stdlib-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.32/461367948840adbb0839c51d91ed74ef4a9ccb52/kotlin-stdlib-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/bb9a3173350f55732416ee27956ea8f9b81f4dbb/kotlin-stdlib-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.30/b9c2a1fab9217623fc0fbadf6190e77eed6f054d/kotlin-script-runtime-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
</array> </array>
</option> </option>
<option name="multiPlatform" value="true" /> <option name="multiPlatform" value="true" />
@@ -36,9 +36,9 @@
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" /> <orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="komp.commonMain" scope="TEST" /> <orderEntry type="module" module-name="komp.commonMain" scope="TEST" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.32" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
</component> </component>
<component name="TestModuleProperties" production-module="komp.commonMain" /> <component name="TestModuleProperties" production-module="komp.commonMain" />
</module> </module>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.version="0.2.5-SNAPSHOT" type="JAVA_MODULE" version="4"> <module external.linked.project.id="komp" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">

View File

@@ -55,6 +55,12 @@
<element id="module-output" name="komp.jsMain" /> <element id="module-output" name="komp.jsMain" />
</root> </root>
</artifact> </artifact>
<artifact type="jar" name="komp-jslegacy-0.3.0-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.3.0-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
</component> </component>
<component name="CheckStyle-IDEA"> <component name="CheckStyle-IDEA">
<option name="configuration"> <option name="configuration">
@@ -429,49 +435,49 @@
</option> </option>
</component> </component>
<component name="libraryTable"> <component name="libraryTable">
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30" type="kotlin.common"> <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" type="kotlin.common">
<CLASSES> <CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/bb9a3173350f55732416ee27956ea8f9b81f4dbb/kotlin-stdlib-common-1.4.30.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES> <SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/9427471650876e86fdb0dd878f147ff15c57eac0/kotlin-stdlib-common-1.4.30-sources.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/f3e47c26a177728549b3dd2a624fec333aa17b36/kotlin-stdlib-common-1.4.32-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.30" type="kotlin.js"> <library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.32" type="kotlin.js">
<CLASSES> <CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.4.30/6c66d1db9c724b087c3a6f37aecffd24d0b14f26/kotlin-stdlib-js-1.4.30.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.4.32/8dca0e318b64909eecfb2d00a9d045562f3e66f8/kotlin-stdlib-js-1.4.32.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES> <SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.4.30/5729944dc91bf28689461fe44b9e401fd41a84a7/kotlin-stdlib-js-1.4.30-sources.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.4.32/5729944dc91bf28689461fe44b9e401fd41a84a7/kotlin-stdlib-js-1.4.32-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.30" type="kotlin.common"> <library name="Gradle: org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.32" type="kotlin.common">
<CLASSES> <CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-annotations-common/1.4.30/e77a76b23fc3007baf57a9d3354fdd318fe1fa88/kotlin-test-annotations-common-1.4.30.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-annotations-common/1.4.32/f242cb223feb6d44e79588c26ca66bf57b0bc3a8/kotlin-test-annotations-common-1.4.32.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES> <SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-annotations-common/1.4.30/57512e29ee20defe37d54ce19507d599b0f2113/kotlin-test-annotations-common-1.4.30-sources.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-annotations-common/1.4.32/57512e29ee20defe37d54ce19507d599b0f2113/kotlin-test-annotations-common-1.4.32-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-common:1.4.30" type="kotlin.common"> <library name="Gradle: org.jetbrains.kotlin:kotlin-test-common:1.4.32" type="kotlin.common">
<CLASSES> <CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-common/1.4.30/823ed87441680d1334a1f0d23781c0089e16c0a6/kotlin-test-common-1.4.30.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-common/1.4.32/a10168dd408319a4af03763ad11f12e0fc1fc5ba/kotlin-test-common-1.4.32.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES> <SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-common/1.4.30/d1818d1bdc457b51289f006884a260b56a55cb77/kotlin-test-common-1.4.30-sources.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-common/1.4.32/d1818d1bdc457b51289f006884a260b56a55cb77/kotlin-test-common-1.4.32-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.4.30" type="kotlin.js"> <library name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.4.32" type="kotlin.js">
<CLASSES> <CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.4.30/5b1cb1ec31a344f6aa1047d6cae3a268825cb724/kotlin-test-js-1.4.30.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.4.32/f4b4bf663acffeff2a664677b9e780f206f6842f/kotlin-test-js-1.4.32.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES> <SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.4.30/8e70170db077d3f421c3fb6d24468b3938990f4a/kotlin-test-js-1.4.30-sources.jar!/" /> <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.4.32/8e70170db077d3f421c3fb6d24468b3938990f4a/kotlin-test-js-1.4.32-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" type="kotlin.common"> <library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" type="kotlin.common">

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:jsMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.2.5-SNAPSHOT" type="JAVA_MODULE" version="4"> <module external.linked.project.id="komp:jsMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="kotlin-language" name="Kotlin"> <facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin;/home/rnentjes/Development/komp/komp/src/commonMain/kotlin"> <configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<dependsOnModuleNames>komp:commonMain</dependsOnModuleNames> <dependsOnModuleNames>komp:commonMain</dependsOnModuleNames>
<sourceSets> <sourceSets>
<sourceSet>komp.commonMain</sourceSet> <sourceSet>komp.commonMain</sourceSet>
@@ -24,11 +24,11 @@
</option> </option>
<option name="pluginClasspaths"> <option name="pluginClasspaths">
<array> <array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.30/b9c2a1fab9217623fc0fbadf6190e77eed6f054d/kotlin-script-runtime-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.30/c7896c5c6b504f44e7cf9f5719d4393e20680666/kotlin-scripting-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.32/6abda0fe69677f0e46e7539fd185e4bd093b7258/kotlin-scripting-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.30/7d0085e5781847da84580d16239c1c4e0980bdb0/kotlin-scripting-jvm-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.32/49656d531bfab9d6e45d3f27bc4d03b542d6766/kotlin-scripting-jvm-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/bb9a3173350f55732416ee27956ea8f9b81f4dbb/kotlin-stdlib-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.30/d10d1e10f47006ee08162dde039e38ac487de4ac/kotlin-stdlib-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.32/461367948840adbb0839c51d91ed74ef4a9ccb52/kotlin-stdlib-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
</array> </array>
@@ -53,7 +53,7 @@
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="komp.commonMain" /> <orderEntry type="module" module-name="komp.commonMain" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.2" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.32" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
</component> </component>
</module> </module>

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:jsTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.2.5-SNAPSHOT" type="JAVA_MODULE" version="4"> <module external.linked.project.id="komp:jsTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="kotlin-language" name="Kotlin"> <facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin;/home/rnentjes/Development/komp/komp/src/commonMain/kotlin"> <configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-jsLegacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-jsIr/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<dependsOnModuleNames>komp:commonTest</dependsOnModuleNames> <dependsOnModuleNames>komp:commonTest</dependsOnModuleNames>
<sourceSets> <sourceSets>
<sourceSet>komp.jsMain</sourceSet>
<sourceSet>komp.commonTest</sourceSet> <sourceSet>komp.commonTest</sourceSet>
<sourceSet>komp.jsMain</sourceSet>
<sourceSet>komp.commonMain</sourceSet> <sourceSet>komp.commonMain</sourceSet>
</sourceSets> </sourceSets>
<newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind> <newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
@@ -29,11 +29,11 @@
</option> </option>
<option name="pluginClasspaths"> <option name="pluginClasspaths">
<array> <array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.30/b9c2a1fab9217623fc0fbadf6190e77eed6f054d/kotlin-script-runtime-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.30/c7896c5c6b504f44e7cf9f5719d4393e20680666/kotlin-scripting-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.32/6abda0fe69677f0e46e7539fd185e4bd093b7258/kotlin-scripting-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.30/7d0085e5781847da84580d16239c1c4e0980bdb0/kotlin-scripting-jvm-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.32/49656d531bfab9d6e45d3f27bc4d03b542d6766/kotlin-scripting-jvm-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.30/bb9a3173350f55732416ee27956ea8f9b81f4dbb/kotlin-stdlib-common-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.30/d10d1e10f47006ee08162dde039e38ac487de4ac/kotlin-stdlib-1.4.30.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.32/461367948840adbb0839c51d91ed74ef4a9ccb52/kotlin-stdlib-1.4.32.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.8/f62be6d4cbf27781c2969867b4ed952f38378492/kotlinx-coroutines-core-1.3.8.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
</array> </array>
@@ -59,12 +59,12 @@
<orderEntry type="module" module-name="komp.jsMain" scope="TEST" /> <orderEntry type="module" module-name="komp.jsMain" scope="TEST" />
<orderEntry type="module" module-name="komp.jsMain" scope="RUNTIME" /> <orderEntry type="module" module-name="komp.jsMain" scope="RUNTIME" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.4.32" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.32" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-common:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-common:1.4.32" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.30" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.32" level="project" />
</component> </component>
<component name="TestModuleProperties" production-module="komp.jsMain" /> <component name="TestModuleProperties" production-module="komp.jsMain" />
</module> </module>

View File

@@ -1,297 +1,205 @@
package nl.astraeus.komp package nl.astraeus.komp
import org.w3c.dom.* import org.w3c.dom.HTMLElement
import org.w3c.dom.events.Event import org.w3c.dom.HTMLInputElement
import org.w3c.dom.Node
const val HASH_VALUE = "komp-hash-value" import org.w3c.dom.get
//const val HASH_ATTRIBUTE = "data-komp-hash"
const val EVENT_PROPERTY = "komp-events"
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) {
val node = this[index]
if (node is HTMLElement && node.getKompHash() == hash) {
return index
}
}
return -1
}
object DiffPatch { object DiffPatch {
fun hashesMatch(oldNode: Node, newNode: Node): Boolean { private fun updateKomponentOnNode(element: Node, newVdom: VDOMElement) {
return ( val komponent = newVdom.komponent
oldNode is HTMLElement &&
newNode is HTMLElement &&
oldNode.nodeName == newNode.nodeName &&
oldNode.getKompHash() != -1 &&
newNode.getKompHash() != -1 &&
oldNode.getKompHash() == newNode.getKompHash()
)
}
private fun updateKomponentOnNode(oldNode: Node, newNode: Node) {
val komponent = newNode.asDynamic()[KOMP_KOMPONENT] as? Komponent
if (komponent != null) { if (komponent != null) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Keeping oldNode, set oldNode element on Komponent", oldNode, komponent) console.log("Keeping oldNode, set oldNode element on Komponent", element, komponent)
} }
komponent.element = oldNode komponent.element = element
} }
} }
fun updateNode(oldNode: Node, newNode: Node): Node { fun updateNode(element: Node, oldVdom: VDOMElement, newVdom: VDOMElement): Node {
if (hashesMatch(oldNode, newNode)) { if (oldVdom.hash == newVdom.hash) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Hashes match", oldNode, newNode, oldNode.getKompHash(), newNode.getKompHash()) console.log("Hashes match", oldVdom, newVdom, oldVdom.hash, newVdom.hash)
} }
updateKomponentOnNode(oldNode, newNode) // no change
return oldNode return element
} }
if (oldNode.nodeType == newNode.nodeType && oldNode.nodeType == 3.toShort()) { if (oldVdom.type == newVdom.type && oldVdom.type == VDOMElementType.TEXT) {
if (oldNode.textContent != newNode.textContent) { if (oldVdom.content != newVdom.content) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Updating text content", oldNode, newNode) console.log("Updating text content", oldVdom, newVdom)
} }
oldNode.textContent = newNode.textContent element.textContent = newVdom.content
} }
updateKomponentOnNode(oldNode, newNode) return element
return oldNode
} }
if (oldNode is HTMLElement && newNode is HTMLElement) { if (oldVdom.type == newVdom.type && oldVdom.type == VDOMElementType.TAG) {
if (oldNode.nodeName == newNode.nodeName) { if (oldVdom.content == newVdom.content) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update attributes", oldNode.nodeName, newNode.nodeName) console.log("Update attributes", oldVdom.content, newVdom.content)
} }
updateAttributes(oldNode, newNode); updateAttributes(element as HTMLElement, oldVdom, newVdom)
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update events", oldNode.nodeName, newNode.nodeName) console.log("Update events", oldVdom.content, newVdom.content)
} }
updateEvents(oldNode, newNode) updateEvents(element as HTMLElement, oldVdom, newVdom)
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update children", oldNode.nodeName, newNode.nodeName) console.log("Update children", oldVdom.content, newVdom.content)
} }
updateKomponentOnNode(oldNode, newNode) updateKomponentOnNode(element, newVdom)
updateChildren(oldNode, newNode) updateChildren(element, oldVdom, newVdom)
oldNode.setKompHash(newNode.getKompHash())
return oldNode return element
} }
} }
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Replace node (type)", oldNode.nodeType, oldNode, newNode) console.log("Replace node (type)", newVdom.type, oldVdom, newVdom)
} }
oldNode.parentNode?.replaceChild(newNode, oldNode) val newNode = newVdom.createElement()
updateKomponentOnNode(newNode, newVdom)
element.parentNode?.replaceChild(newNode, element)
return newNode return newNode
} }
private fun updateAttributes(oldNode: HTMLElement, newNode: HTMLElement) { private fun updateAttributes(element: HTMLElement, oldVdom: VDOMElement, newVdom: VDOMElement) {
// removed attributes // removed attributes
for (name in oldNode.getAttributeNames()) { for ((name, attr) in oldVdom.attributes) {
val attr = oldNode.attributes[name] if (newVdom.attributes[name] == null) {
element.removeAttribute(name)
if (attr != null && newNode.getAttribute(name) == null) {
oldNode.removeAttribute(name)
} }
} }
for (name in newNode.getAttributeNames()) { for ((name, value) in newVdom.attributes) {
val value = newNode.getAttribute(name) val oldValue = oldVdom.attributes[name]
val oldValue = oldNode.getAttribute(name)
if (value != oldValue) { if (value != oldValue) {
if (value != null) { element.setAttribute(name, value)
oldNode.setAttribute(name, value) }
}else { }
oldNode.removeAttribute(name)
if (newVdom.content == "input" && oldVdom.content == "input") {
if (element is HTMLInputElement) {
element.value = newVdom.attributes["value"] ?: ""
element.checked = newVdom.attributes["checked"] == "true"
} }
} }
} }
if (newNode is HTMLInputElement && oldNode is HTMLInputElement) { private fun updateChildren(
oldNode.value = newNode.value element: HTMLElement,
oldNode.checked = newNode.checked oldVdom: VDOMElement,
} newVdom: VDOMElement
} ) {
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
var oldIndex = 0 var oldIndex = 0
var newIndex = 0 var newIndex = 0
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log( console.log(
"updateChildren HTML old(${oldNode.childNodes.length})", "updateChildren HTML old(${oldVdom.childNodes.size})",
oldNode.innerHTML oldVdom.toString()
) )
console.log( console.log(
"updateChildren HTML new(${newNode.childNodes.length})", "updateChildren HTML new(${newVdom.childNodes.size})",
newNode.innerHTML newVdom.toString()
) )
} }
while (newIndex < newNode.childNodes.length) { while (oldIndex < oldVdom.childNodes.size && newIndex < newVdom.childNodes.size) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update Old/new", oldIndex, newIndex) console.log("Update Old/new", oldIndex, newIndex)
} }
val newChildNode = newNode.childNodes[newIndex] val oldChildNode = oldVdom.childNodes[oldIndex]
val newChildNode = newVdom.childNodes[newIndex]
if (oldIndex < oldNode.childNodes.length) {
val oldChildNode = oldNode.childNodes[oldIndex]
if (oldChildNode != null && newChildNode != null) {
/*
if (Komponent.logReplaceEvent) {
console.log(">>> updateChildren old/new", oldChildNode, newChildNode)
}
*/
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update node Old/new", oldChildNode, newChildNode) console.log("Update node Old/new", oldChildNode, newChildNode)
} }
if (!hashesMatch(oldChildNode, newChildNode) && newChildNode is HTMLElement && oldChildNode is HTMLElement) { // scenarios:
// - hashes match, next
// - hashes don't match:
// -- old hash is down in new list
// --- delta == 1, insert new node
// -- new hash is down in old list
// --- delta == 1, remove current else swap?
// else: replace current node with new
if (oldVdom.hash != newVdom.hash &&
newChildNode.type == VDOMElementType.TAG &&
oldChildNode.type == VDOMElementType.TAG
) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Hashes don't match") console.log("Hashes don't match")
} }
val oldHash = oldChildNode.getKompHash() val oldHash = oldChildNode.hash.hashCode()
val newHash = newChildNode.getKompHash() val newHash = newChildNode.hash.hashCode()
if (newHash >= 0) { val newHashIndexInOld = oldVdom.findNodeHashIndex(newHash)
val oldNodeWithNewHashIndex = oldNode.childNodes.findNodeHashIndex(newHash) val oldHashIndexInNew = newVdom.findNodeHashIndex(oldHash)
if (Komponent.logReplaceEvent) { if (newHashIndexInOld == oldIndex + 1 && oldHashIndexInNew == newIndex) {
console.log("oldNodeWithNewHashIndex", newHash, oldNodeWithNewHashIndex) // remove
} element.removeChild(element.childNodes[oldIndex]!!)
oldIndex++
if (oldNodeWithNewHashIndex > oldIndex) {
if (oldHash >= 0) {
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 continue
} } else if (newHashIndexInOld == oldIndex && oldHashIndexInNew == newIndex + 1) {
} // insert
val nodeWithHash = oldNode.childNodes[oldNodeWithNewHashIndex] element.insertBefore(newChildNode.createElement(), element.childNodes[oldIndex]!!)
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++ newIndex++
oldIndex++ oldIndex++
continue continue
} }
} else if (oldHash >= 0 && newNode.childNodes.findNodeHashIndex(oldHash) > newIndex) {
if (Komponent.logReplaceEvent) {
console.log("newNodeWithOldHashIndex", oldHash, newNode.childNodes.findNodeHashIndex(oldHash))
}
oldNode.insertBefore(newChildNode, oldChildNode)
oldIndex++
continue
}
}
}
val updatedNode = updateNode(oldChildNode, newChildNode)
if (updatedNode == newChildNode) {
if (oldChildNode is HTMLElement && newChildNode is HTMLElement) {
updateEvents(oldChildNode, newChildNode)
}
oldIndex++
continue
}
} else {
if (Komponent.logReplaceEvent) {
console.log("Null node", oldChildNode, newChildNode)
}
} }
// update
updateNode(element.childNodes[oldIndex]!!, oldChildNode, newChildNode)
oldIndex++ oldIndex++
newIndex++ newIndex++
} else {
if (Komponent.logReplaceEvent) {
console.log("Append Old/new/node", oldIndex, newIndex, newChildNode)
}
oldNode.append(newChildNode)
oldIndex++
} }
/* while (element.childNodes.length > newVdom.childNodes.size) {
if (Komponent.logReplaceEvent) { element.childNodes[element.childNodes.length - 1]?.also {
console.log("<<< Updated Old/new", oldNode.innerHTML, newNode.innerHTML)
}
*/
}
while (oldIndex < oldNode.childNodes.length) {
oldNode.childNodes[oldIndex]?.also {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Remove old node", it) console.log("Remove old node", it)
} }
oldNode.removeChild(it) element.removeChild(it)
}
} }
} }
private fun updateEvents(oldNode: HTMLElement, newNode: HTMLElement) { while (newIndex < newVdom.childNodes.size) {
val oldEvents = (oldNode.asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf() newVdom.childNodes[newIndex].also {
val newEvents = (newNode.asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf() element.appendChild(it.createElement())
}
newIndex++
}
}
private fun updateEvents(element: HTMLElement, oldVdom: VDOMElement, newVdom: VDOMElement) {
val oldEvents = oldVdom.events
val newEvents = newVdom.events
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Update events", oldNode.getAttribute(EVENT_PROPERTY), newNode.getAttribute(EVENT_PROPERTY)) console.log("Update events", oldEvents, newEvents)
} }
for (event in oldEvents) { for ((name, event) in oldEvents) {
oldNode.removeKompEvent(event) element.removeEventListener(name, event)
} }
for (event in newEvents) { for ((name, event) in newEvents) {
val newNodeEvent = newNode.asDynamic()["event-$event"]
if (newNodeEvent != null) {
if (Komponent.logReplaceEvent) { if (Komponent.logReplaceEvent) {
console.log("Set event $event on", oldNode) console.log("Set event $event on", element)
} }
oldNode.setKompEvent(event, newNodeEvent as ((Event) -> Unit)) element.addEventListener(name, event)
}
}
if (newEvents.isEmpty()) {
oldNode.asDynamic()[EVENT_PROPERTY] = null
} else {
oldNode.asDynamic()[EVENT_PROPERTY] = newNode.asDynamic()[EVENT_PROPERTY]
} }
} }

View File

@@ -2,86 +2,223 @@ package nl.astraeus.komp
import kotlinx.browser.document import kotlinx.browser.document
import kotlinx.html.* import kotlinx.html.*
import org.w3c.dom.* import org.w3c.dom.Element
import org.w3c.dom.css.CSSStyleDeclaration import org.w3c.dom.Node
import org.w3c.dom.events.Event 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
@Suppress("NOTHING_TO_INLINE") private fun attributeHash(key: String, value: String): Int =
inline fun HTMLElement.setKompEvent(name: String, noinline callback: (Event) -> Unit) { 3 * key.hashCode() +
val eventName = if (name.startsWith("on")) { 5 * value.hashCode()
name.substring(2)
private fun MutableMap<String, String>.kompHash(): Int {
var result = 0
for ((name, value) in this) {
result += attributeHash(name, value)
}
return result
}
private fun MutableMap<String, (Event) -> Unit>.kompHash(): Int {
var result = 0
for ((name, event) in this) {
result += attributeHash(name, event.toString())
}
return result
}
private fun MutableList<VDOMElement>.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<String, String> = mutableMapOf()
val events: MutableMap<String, (Event) -> Unit> = mutableMapOf()
val childNodes: MutableList<VDOMElement> = 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 { } else {
name event
} }
addEventListener(eventName, callback, null) val recalculate = events.containsKey(eventName)
events[eventName] = value
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) { if (recalculate) {
//asDynamic()[name] = callback hash.eventsHash = events.kompHash()
val events: MutableList<String> = (asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
events.add(eventName)
asDynamic()[EVENT_PROPERTY] = events
asDynamic()["event-$eventName"] = callback
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun HTMLElement.removeKompEvent(name: String) {
val eventName = if (name.startsWith("on")) {
name.substring(2)
} else { } else {
name hash.eventsHash += attributeHash(eventName, value.toString())
}
removeEventListener(eventName, asDynamic()["event-$eventName"] as ((Event) -> Unit), null)
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
//asDynamic()[name] = callback
val events: MutableList<String> = (asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
events.remove(eventName)
asDynamic()["event-$eventName"] = null
} }
} }
fun HTMLElement.clearEvents() { fun appendChild(element: VDOMElement) {
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) { childNodes.add(element)
//asDynamic()[name] = callback //hash.childNodesHash += element.hash.hashCode()
val events = getAttribute(EVENT_PROPERTY) ?: "" }
for (eventName in events.split(",")) { fun updateChildHash() {
if (eventName.isNotBlank()) { hash.childNodesHash = childNodes.kompHash()
val event: (Event) -> Unit = asDynamic()["event-$eventName"]
removeEventListener(eventName, event)
} }
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
} }
} }
interface HtmlConsumer : TagConsumer<HTMLElement> { return -2
fun append(node: Node)
} }
fun HTMLElement.setStyles(cssStyle: CSSStyleDeclaration) { fun createElement(): Node {
for (index in 0 until cssStyle.length) { val result = when (type) {
val propertyName = cssStyle.item(index) VDOMElementType.TAG -> {
val result: Element = if (namespace != null) {
style.setProperty(propertyName, cssStyle.getPropertyValue(propertyName)) 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<VDOMElement> {
fun append(node: VDOMElement)
} }
class HtmlBuilder( class HtmlBuilder(
val komponent: Komponent,
val document: Document,
val baseHash: Int val baseHash: Int
) : HtmlConsumer { ) : HtmlConsumer {
private val path = arrayListOf<HTMLElement>() private val path = arrayListOf<VDOMElement>()
private var lastLeaved: HTMLElement? = null private var lastLeaved: VDOMElement? = null
override fun onTagStart(tag: Tag) { override fun onTagStart(tag: Tag) {
val element: HTMLElement = when { val element = VDOMElement(baseHash, tag.tagName, tag.namespace)
tag.namespace != null -> document.createElementNS(tag.namespace!!, tag.tagName).asDynamic()
else -> document.createElement(tag.tagName) as HTMLElement for (entry in tag.attributesEntries) {
element.setAttribute(entry.key, entry.value)
} }
if (path.isNotEmpty()) { if (path.isNotEmpty()) {
@@ -94,7 +231,7 @@ class HtmlBuilder(
override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) { override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) {
when { when {
path.isEmpty() -> throw IllegalStateException("No current tag") path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().tagName.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag") path.last().content.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().let { node -> else -> path.last().let { node ->
if (value == null) { if (value == null) {
node.removeAttribute(attribute) node.removeAttribute(attribute)
@@ -108,94 +245,18 @@ class HtmlBuilder(
override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) { override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) {
when { when {
path.isEmpty() -> throw IllegalStateException("No current tag") path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().tagName.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag") path.last().content.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().setKompEvent(event, value) else -> path.last().setKompEvent(event, value)
} }
} }
override fun onTagEnd(tag: Tag) { override fun onTagEnd(tag: Tag) {
var hash = 0 if (path.isEmpty() || path.last().content.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")
} }
val element = path.last()
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
for (index in 0 until element.childNodes.length) {
val child = element.childNodes[index]
if (child is HTMLElement) {
hash = hash * 37 + child.getKompHash()
} else {
hash = hash * 37 + (child?.textContent?.hashCode() ?: 0)
}
}
}
for ((key, value) in tag.attributesEntries) {
if (key == "class") {
val classes = value.split(Regex("\\s+"))
val classNames = StringBuilder()
for (cls in classes) {
val cssStyle = komponent.declaredStyles[cls]
if (cssStyle != null) {
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
hash = hash * 37 + cssStyle.hashCode()
}
if (cls.endsWith(":hover")) {
val oldOnMouseOver = element.onmouseover
val oldOnMouseOut = element.onmouseout
element.onmouseover = {
element.setStyles(cssStyle)
oldOnMouseOver?.invoke(it)
}
element.onmouseout = {
cls.split(':').firstOrNull()?.let {
komponent.declaredStyles[it]?.let { cssStyle ->
element.setStyles(cssStyle)
}
}
oldOnMouseOut?.invoke(it)
}
} else {
element.setStyles(cssStyle)
}
} else {
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
hash = hash * 37 + cls.hashCode()
}
classNames.append(cls)
classNames.append(" ")
}
}
element.className = classNames.toString()
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
val key_value = "${key}-${classNames}"
hash = hash * 37 + key_value.hashCode()
}
} else {
element.setAttribute(key, value)
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
val key_value = "${key}-${value}"
hash = hash * 37 + key_value.hashCode()
}
}
}
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
element.setKompHash(baseHash * 53 + hash)
}
lastLeaved = path.removeAt(path.lastIndex) lastLeaved = path.removeAt(path.lastIndex)
lastLeaved?.updateChildHash()
} }
override fun onTagContent(content: CharSequence) { override fun onTagContent(content: CharSequence) {
@@ -203,7 +264,7 @@ class HtmlBuilder(
throw IllegalStateException("No current DOM node") throw IllegalStateException("No current DOM node")
} }
path.last().appendChild(document.createTextNode(content.toString())) path.last().appendChild(VDOMElement(baseHash, content.toString(), type = VDOMElementType.TEXT))
} }
override fun onTagContentEntity(entity: Entities) { override fun onTagContentEntity(entity: Entities) {
@@ -212,15 +273,10 @@ class HtmlBuilder(
} }
// stupid hack as browsers don't support createEntityReference // stupid hack as browsers don't support createEntityReference
val s = document.createElement("span") as HTMLElement path.last().appendChild(VDOMElement(baseHash, entity.text, type = VDOMElementType.ENTITY))
s.innerHTML = entity.text
path.last().appendChild(s.childNodes.asList().first { it.nodeType == Node.TEXT_NODE })
// other solution would be
// pathLast().innerHTML += entity.text
} }
override fun append(node: Node) { override fun append(node: VDOMElement) {
path.last().appendChild(node) path.last().appendChild(node)
} }
@@ -228,7 +284,7 @@ class HtmlBuilder(
with(DefaultUnsafe()) { with(DefaultUnsafe()) {
block() block()
path.last().innerHTML += toString() path.last().appendChild(VDOMElement(baseHash, toString(), type = VDOMElementType.UNSAFE))
} }
} }
@@ -237,18 +293,17 @@ class HtmlBuilder(
throw IllegalStateException("No current DOM node") throw IllegalStateException("No current DOM node")
} }
path.last().appendChild(document.createComment(content.toString())) path.last().appendChild(VDOMElement(baseHash, content.toString(), type = VDOMElementType.COMMENT))
} }
override fun finalize(): HTMLElement = lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags") override fun finalize(): VDOMElement {
return lastLeaved ?: throw IllegalStateException("We can't finalize as there was no tags")
@Suppress("UNCHECKED_CAST") }
private fun HTMLElement.asR(): HTMLElement = this.asDynamic()
companion object { companion object {
fun create(content: HtmlBuilder.() -> Unit): HTMLElement { fun create(content: HtmlBuilder.() -> Unit): VDOMElement {
val komponent = DummyKomponent() val komponent = DummyKomponent()
val consumer = HtmlBuilder(komponent, document, komponent.hashCode()) val consumer = HtmlBuilder(komponent.hashCode())
content.invoke(consumer) content.invoke(consumer)
return consumer.finalize() return consumer.finalize()
} }

View File

@@ -9,8 +9,6 @@ import org.w3c.dom.Node
import org.w3c.dom.css.CSSStyleDeclaration import org.w3c.dom.css.CSSStyleDeclaration
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
const val KOMP_KOMPONENT = "komp-komponent"
typealias CssStyle = CSSStyleDeclaration.() -> Unit typealias CssStyle = CSSStyleDeclaration.() -> Unit
class StateDelegate<T>( class StateDelegate<T>(
@@ -34,20 +32,8 @@ class StateDelegate<T>(
inline fun <reified T> Komponent.state(initialValue: T): StateDelegate<T> = StateDelegate(this, initialValue) inline fun <reified T> Komponent.state(initialValue: T): StateDelegate<T> = StateDelegate(this, initialValue)
fun HtmlConsumer.include(component: Komponent) { fun HtmlConsumer.include(component: Komponent) {
if (Komponent.updateStrategy == UpdateStrategy.REPLACE) {
if (component.element != null) {
component.update()
} else {
component.refresh()
}
component.element?.also {
append(it)
}
} else {
append(component.create()) append(component.create())
} }
}
class DummyKomponent: Komponent() { class DummyKomponent: Komponent() {
override fun HtmlBuilder.render() { override fun HtmlBuilder.render() {
@@ -57,20 +43,16 @@ class DummyKomponent: Komponent() {
} }
} }
enum class UpdateStrategy {
REPLACE,
DOM_DIFF
}
abstract class Komponent { abstract class Komponent {
private var createIndex = getNextCreateIndex() private var createIndex = getNextCreateIndex()
private var dirty: Boolean = true private var dirty: Boolean = true
var vdom: VDOMElement? = null
var element: Node? = null var element: Node? = null
val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap() val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap()
open fun create(): HTMLElement { open fun create(): VDOMElement {
val consumer = HtmlBuilder(this, document, this.createIndex) val consumer = HtmlBuilder(this.createIndex)
try { try {
consumer.render() consumer.render()
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -84,8 +66,9 @@ abstract class Komponent {
result.id = "komp_${createIndex}" result.id = "komp_${createIndex}"
} }
element = result result.komponent = this
element.asDynamic()[KOMP_KOMPONENT] = this
vdom = result
dirty = false dirty = false
@@ -113,45 +96,27 @@ abstract class Komponent {
} }
internal fun refresh() { internal fun refresh() {
val oldElement = element val oldElement = vdom
if (logRenderEvent) { if (logRenderEvent) {
console.log("Rendering", this) console.log("Rendering", this)
} }
val newElement = create() val newElement = create()
if (oldElement != null) { element = if (oldElement != null && element != null) {
element = if (updateStrategy == UpdateStrategy.REPLACE) {
if (logReplaceEvent) {
console.log("Replacing", oldElement, newElement)
}
oldElement.parentNode?.replaceChild(newElement, oldElement)
newElement
} else {
if (logReplaceEvent) { if (logReplaceEvent) {
console.log("DomDiffing", oldElement, newElement) console.log("DomDiffing", oldElement, newElement)
} }
DiffPatch.updateNode(oldElement, newElement) DiffPatch.updateNode(element!!, oldElement, newElement)
} } else {
}
dirty = false
}
@JsName("remove")
fun remove() {
check(updateStrategy == UpdateStrategy.REPLACE) {
"remote only works with UpdateStrategy.REPLACE"
}
element?.let {
val parent = it.parentElement ?: throw IllegalArgumentException("Element has no parent!?")
if (logReplaceEvent) { if (logReplaceEvent) {
console.log("Remove", it) console.log("Create", newElement)
}
newElement.createElement()
} }
parent.removeChild(it) vdom = newElement
} dirty = false
} }
companion object { companion object {
@@ -161,10 +126,11 @@ abstract class Komponent {
var logRenderEvent = false var logRenderEvent = false
var logReplaceEvent = false var logReplaceEvent = false
var updateStrategy = UpdateStrategy.DOM_DIFF
fun create(parent: HTMLElement, component: Komponent, insertAsFirst: Boolean = false) { fun create(parent: HTMLElement, component: Komponent, insertAsFirst: Boolean = false) {
val element = component.create() val vdomElement = component.create()
val element = vdomElement.createElement()
component.element = element
if (insertAsFirst && parent.childElementCount > 0) { if (insertAsFirst && parent.childElementCount > 0) {
parent.insertBefore(element, parent.firstChild) parent.insertBefore(element, parent.firstChild)
@@ -207,7 +173,11 @@ abstract class Komponent {
console.log("Skip ${next.createIndex}") console.log("Skip ${next.createIndex}")
} }
} }
} else {
console.log("Komponent element has no id, ", next, element)
} }
} else {
console.log("Komponent element is null", next)
} }
} }

View File

@@ -2,46 +2,49 @@ package nl.astraeus.komp
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import org.w3c.dom.get import org.w3c.dom.get
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
fun nodesEqual(node1: Node, node2: Node): Boolean { fun nodesEqual(element: Element, vdom: VDOMElement): Boolean {
if (node1.childNodes.length != node1.childNodes.length) { if (element.childNodes.length != vdom.childNodes.size) {
return false return false
} }
if (node1 is HTMLElement && node2 is HTMLElement) { if (element.attributes.length != vdom.attributes.size) {
if (node1.attributes.length != node2.attributes.length) {
return false return false
} }
for (index in 0 until node1.attributes.length) { for ((name, value) in vdom.attributes) {
node1.attributes[index]?.also { attr1 -> if (value != element.getAttribute(name)) {
val attr2 = node2.getAttribute(attr1.name) return false
}
}
for ((index, child) in vdom.childNodes.withIndex()) {
if (index < element.childNodes.length) {
val elementChild = element.childNodes[index]
if (attr1.value != attr2) { if (child.type == VDOMElementType.TAG) {
if (!nodesEqual(elementChild as HTMLElement, child)) {
return false
}
} else if (child.type == VDOMElementType.TEXT) {
if (child.content != element.textContent) {
return false return false
} }
} }
} } else {
for (index in 0 until node1.childNodes.length) {
node1.childNodes[index]?.also { child1 ->
node2.childNodes[index]?.also { child2 ->
if (!nodesEqual(child1, child2)) {
return false return false
} }
} }
}
}
}
return true return true
} }
class TestUpdate { class TestUpdate {
@Test @Test
fun testCompare1() { fun testCompare() {
val dom1 = HtmlBuilder.create { val dom1 = HtmlBuilder.create {
div { div {
div(classes = "bla") { div(classes = "bla") {
@@ -72,9 +75,13 @@ class TestUpdate {
} }
} }
DiffPatch.updateNode(dom1, dom2) var element = dom1.createElement()
assertTrue(nodesEqual(dom1, dom2), "Updated dom not equal to original") assertTrue(nodesEqual(element as Element, dom1), "Created dom not equal to original")
element = DiffPatch.updateNode(element, dom1, dom2) as HTMLElement
assertTrue(nodesEqual(element, dom2), "Updated dom not equal to original")
} }
@Test @Test
@@ -123,10 +130,15 @@ class TestUpdate {
} }
} }
Komponent.logReplaceEvent = true Komponent.logRenderEvent = true
DiffPatch.updateNode(dom1, dom2)
assertTrue(nodesEqual(dom1, dom2), "Updated dom not equal to original") var element = dom1.createElement()
assertTrue(nodesEqual(element as Element, dom1), "Created dom not equal to original")
element = DiffPatch.updateNode(element, dom1, dom2) as HTMLElement
assertTrue(nodesEqual(element, dom2), "Updated dom not equal to original")
} }
} }