6 Commits

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
e87f7ba540 Fix events, fix per komponent hash 2021-03-31 16:01:42 +02:00
0a1ab1f326 Fix CI build 2021-02-10 14:18:44 +01:00
695a11efdb Fix build 2021-02-10 13:44:08 +01:00
66b3fb3c22 Merge remember branch, Update to 0.2.5-SNAPSHOT 2021-02-10 13:20:07 +01:00
13 changed files with 902 additions and 646 deletions

View File

@@ -1,10 +1,11 @@
plugins {
kotlin("multiplatform") version "1.3.71"
kotlin("multiplatform") version "1.4.32"
`maven-publish`
}
group = "nl.astraeus"
version = "0.1.21-SNAPSHOT"
version = "0.3.0-SNAPSHOT"
repositories {
mavenCentral()
@@ -15,7 +16,7 @@ kotlin {
/* Targets configuration omitted.
* To find out how to configure the targets, please follow the link:
* https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets */
js {
js(BOTH) {
browser {
//produceKotlinLibrary()
testTask {
@@ -31,14 +32,12 @@ kotlin {
dependencies {
implementation(kotlin("stdlib-common"))
//implementation("org.jetbrains.kotlinx:kotlinx-html:0.7.2-build-1711")
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.2")
}
}
val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.1")
}
}
val jsTest by getting {
@@ -51,34 +50,34 @@ kotlin {
publishing {
repositories {
maven {
name = "releases"
// change to point to your repo, e.g. http://my.org/repo
url = uri("http://nexus.astraeus.nl/nexus/content/repositories/releases")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
if (project.properties["nexusUsername"] != null) {
maven {
name = "releases"
url = uri("http://nexus.astraeus.nl/nexus/content/repositories/releases")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
username = nexusUsername
password = nexusPassword
username = nexusUsername
password = nexusPassword
}
}
}
maven {
name = "snapshots"
// change to point to your repo, e.g. http://my.org/repo
url = uri("http://nexus.astraeus.nl/nexus/content/repositories/snapshots")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
maven {
name = "snapshots"
url = uri("http://nexus.astraeus.nl/nexus/content/repositories/snapshots")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
username = nexusUsername
password = nexusPassword
username = nexusUsername
password = nexusPassword
}
}
} else {
println("Publishing disabled properties not found.")
}
}
publications {
val kotlinMultiplatform by getting {
//artifactId = "kotlin-css-generator"
}
val kotlinMultiplatform by getting {}
}
}

38
komp.commonMain.iml Normal file
View File

@@ -0,0 +1,38 @@
<?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.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="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>
<compilerSettings />
<compilerArguments>
<option name="languageVersion" value="1.4" />
<option name="apiVersion" value="1.4" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array>
<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.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.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.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.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
</array>
</option>
<option name="multiPlatform" value="true" />
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/src/commonMain" />
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<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" />
</component>
</module>

44
komp.commonTest.iml Normal file
View File

@@ -0,0 +1,44 @@
<?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.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="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>
<externalSystemTestTasks>
<externalSystemTestTask>jsLegacyBrowserTest|komp:jsTest|jsLegacy</externalSystemTestTask>
</externalSystemTestTasks>
<compilerSettings />
<compilerArguments>
<option name="languageVersion" value="1.4" />
<option name="apiVersion" value="1.4" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array>
<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.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.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.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.kotlin/kotlin-script-runtime/1.4.32/bac50b0748be017dbc13fc1cb7231b1c9da0e106/kotlin-script-runtime-1.4.32.jar" />
</array>
</option>
<option name="multiPlatform" value="true" />
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/src/commonTest" />
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="komp.commonMain" scope="TEST" />
<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.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
</component>
<component name="TestModuleProperties" production-module="komp.commonMain" />
</module>

View File

@@ -1,5 +1,5 @@
<?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.1.21-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">
<exclude-output />
<content url="file://$MODULE_DIR$">

174
komp.ipr
View File

@@ -13,6 +13,54 @@
<element id="module-output" name="komp_main" />
</root>
</artifact>
<artifact type="jar" name="komp-js-0.1.21-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-js-0.1.21-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-js-0.2.1-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-js-0.2.1-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-js-0.2.2-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-js-0.2.2-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.2.2-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.2.2-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.2.3-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.2.3-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.2.4-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.2.4-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.2.5-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.2.5-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</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 name="CheckStyle-IDEA">
<option name="configuration">
@@ -75,8 +123,6 @@
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useAutoImport" value="true" />
</GradleProjectSettings>
</option>
</component>
@@ -216,7 +262,11 @@
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" />
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/komp.iml" filepath="$PROJECT_DIR$/komp.iml" group="komp" />
<module fileurl="file://$PROJECT_DIR$/komp.iml" filepath="$PROJECT_DIR$/komp.iml" />
<module fileurl="file://$PROJECT_DIR$/komp.commonMain.iml" filepath="$PROJECT_DIR$/komp.commonMain.iml" />
<module fileurl="file://$PROJECT_DIR$/komp.commonTest.iml" filepath="$PROJECT_DIR$/komp.commonTest.iml" />
<module fileurl="file://$PROJECT_DIR$/komp.jsMain.iml" filepath="$PROJECT_DIR$/komp.jsMain.iml" />
<module fileurl="file://$PROJECT_DIR$/komp.jsTest.iml" filepath="$PROJECT_DIR$/komp.jsTest.iml" />
<module fileurl="file://$PROJECT_DIR$/komp_main.iml" filepath="$PROJECT_DIR$/komp_main.iml" group="komp" />
<module fileurl="file://$PROJECT_DIR$/komp_test.iml" filepath="$PROJECT_DIR$/komp_test.iml" group="komp" />
</modules>
@@ -254,6 +304,11 @@
<option name="name" value="maven" />
<option name="url" value="http://nexus.astraeus.nl/nexus/content/groups/public" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
</component>
<component name="RunManager">
<configuration default="true" type="Applet" factoryName="Applet">
@@ -317,41 +372,130 @@
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
<component name="accountSettings">
<option name="activeRegion" value="us-east-1" />
<option name="recentlyUsedRegions">
<list>
<option value="us-east-1" />
</list>
</option>
</component>
<component name="libraries-with-intellij-classes">
<option name="intellijApiContainingLibraries">
<list>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIU" />
<option name="groupId" value="com.jetbrains.intellij.idea" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIU" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIC" />
<option name="groupId" value="com.jetbrains.intellij.idea" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIC" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPY" />
<option name="groupId" value="com.jetbrains.intellij.pycharm" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPY" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPC" />
<option name="groupId" value="com.jetbrains.intellij.pycharm" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPC" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="clion" />
<option name="groupId" value="com.jetbrains.intellij.clion" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="clion" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="riderRD" />
<option name="groupId" value="com.jetbrains.intellij.rider" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="riderRD" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
</list>
</option>
</component>
<component name="libraryTable">
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" type="kotlin.common">
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/3fa8dd6c896d635e78201e5e811545f3846dec04/kotlin-stdlib-common-1.3.70.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>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.70/6eefff69d45d17fb1b50591729853e9f6a15d09/kotlin-stdlib-common-1.3.70-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>
</library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.3.70" type="kotlin.js">
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.4.32" type="kotlin.js">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.3.70/41513becdc85a89799b74028a81c39c364ba3465/kotlin-stdlib-js-1.3.70.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>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.3.70/8b0cd54438c595bb7c2c5e1e4a77a61f0eb39af5/kotlin-stdlib-js-1.3.70-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>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.1" type="kotlin.common">
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-annotations-common:1.4.32" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.1/26651a49fdc0e6abf8a61182b01d6bb0a6bb427e/kotlinx-html-common-0.7.1.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>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.1/5aba2eaa4e02d75d6e526900ee69d4e20a0d6d94/kotlinx-html-common-0.7.1-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>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.1" type="kotlin.js">
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-common:1.4.32" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.1/f3a744916effcbe728d38e4bfd732f9694d18fa9/kotlinx-html-js-0.7.1.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>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.1/435518751d3af45577f8af5f64c4850bc5989af2/kotlinx-html-js-0.7.1-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>
</library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.4.32" type="kotlin.js">
<CLASSES>
<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>
<JAVADOC />
<SOURCES>
<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>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.2" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.2/7da838083337896b558cab2884c4118709a58769/kotlinx-html-metadata-0.7.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.2/866bbbfe0a81d49cdf19fd149adafbb938b35be4/kotlinx-html-common-0.7.2-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.2" type="kotlin.js">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.2/baf7e494e549776d0ae3e0ee225804a513f5dc26/kotlinx-html-jslegacy-0.7.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.2/b09bfd3565058174afa3fd0888743f8b8dca5406/kotlinx-html-js-0.7.2-sources.jar!/" />
</SOURCES>
</library>
</component>

59
komp.jsMain.iml Normal file
View File

@@ -0,0 +1,59 @@
<?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.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<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">
<dependsOnModuleNames>komp:commonMain</dependsOnModuleNames>
<sourceSets>
<sourceSet>komp.commonMain</sourceSet>
</sourceSets>
<newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings />
<compilerArguments>
<option name="outputFile" value="$MODULE_DIR$/build/js/packages/komp-jsLegacy/kotlin/komp-jsLegacy.js" />
<option name="noStdlib" value="true" />
<option name="sourceMap" value="true" />
<option name="metaInfo" value="true" />
<option name="target" value="v5" />
<option name="moduleKind" value="umd" />
<option name="main" value="call" />
<option name="languageVersion" value="1.4" />
<option name="apiVersion" value="1.4" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array>
<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.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.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.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.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/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
</array>
</option>
<option name="multiPlatform" value="true" />
<option name="errors">
<ArgumentParseErrors />
</option>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/js/packages/komp-jsLegacy/kotlin" />
<exclude-output />
<content url="file://$MODULE_DIR$/build/externals/komp-jsIr/src" />
<content url="file://$MODULE_DIR$/build/externals/komp-jsLegacy/src" />
<content url="file://$MODULE_DIR$/src/jsMain">
<sourceFolder url="file://$MODULE_DIR$/src/jsMain/kotlin" type="kotlin-source" />
</content>
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<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.kotlin:kotlin-stdlib-js:1.4.32" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32" level="project" />
</component>
</module>

70
komp.jsTest.iml Normal file
View File

@@ -0,0 +1,70 @@
<?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.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<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">
<dependsOnModuleNames>komp:commonTest</dependsOnModuleNames>
<sourceSets>
<sourceSet>komp.commonTest</sourceSet>
<sourceSet>komp.jsMain</sourceSet>
<sourceSet>komp.commonMain</sourceSet>
</sourceSets>
<newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<externalSystemTestTasks>
<externalSystemTestTask>jsLegacyBrowserTest|komp:jsTest|jsLegacy</externalSystemTestTask>
</externalSystemTestTasks>
<compilerSettings />
<compilerArguments>
<option name="outputFile" value="$MODULE_DIR$/build/js/packages/komp-jsLegacy-test/kotlin/komp-jsLegacy-test.js" />
<option name="noStdlib" value="true" />
<option name="sourceMap" value="true" />
<option name="metaInfo" value="true" />
<option name="target" value="v5" />
<option name="moduleKind" value="umd" />
<option name="main" value="call" />
<option name="languageVersion" value="1.4" />
<option name="apiVersion" value="1.4" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array>
<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.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.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.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.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/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar" />
</array>
</option>
<option name="multiPlatform" value="true" />
<option name="errors">
<ArgumentParseErrors />
</option>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<output-test url="file://$MODULE_DIR$/build/js/packages/komp-jsLegacy-test/kotlin" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/jsTest">
<sourceFolder url="file://$MODULE_DIR$/src/jsTest/kotlin" type="kotlin-test" />
</content>
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="komp.commonMain" scope="TEST" />
<orderEntry type="module" module-name="komp.commonTest" scope="TEST" production-on-test="" />
<orderEntry type="module" module-name="komp.jsMain" scope="TEST" />
<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.kotlin:kotlin-test-js:1.4.32" 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.kotlin:kotlin-stdlib-common:1.4.32" 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.32" level="project" />
</component>
<component name="TestModuleProperties" production-module="komp.jsMain" />
</module>

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:main" 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.1.17-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/test/kotlin;/home/rnentjes/Development/komp/komp/src/main/kotlin">
<newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings />
<compilerArguments>
<option name="outputFile" value="$MODULE_DIR$/build/classes/kotlin/main/komp.js" />
<option name="noStdlib" value="true" />
<option name="sourceMap" value="true" />
<option name="metaInfo" value="true" />
<option name="target" value="v5" />
<option name="moduleKind" value="umd" />
<option name="main" value="call" />
<option name="languageVersion" value="1.3" />
<option name="apiVersion" value="1.3" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array />
</option>
<option name="multiPlatform" value="true" />
<option name="errors">
<ArgumentParseErrors />
</option>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/classes/kotlin/main" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/main/kotlin">
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" type="kotlin-source" />
</content>
<content url="file://$MODULE_DIR$/src/main/resources">
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="kotlin-resource" />
</content>
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.3.70" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.1" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.1" level="project" />
</component>
</module>

View File

@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:test" 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.1.17-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/test/kotlin;/home/rnentjes/Development/komp/komp/src/main/kotlin">
<sourceSets>
<sourceSet>komp_main</sourceSet>
</sourceSets>
<newMppModelJpsModuleKind>COMPILATION_AND_SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings />
<compilerArguments>
<option name="outputFile" value="$MODULE_DIR$/build/classes/kotlin/test/komp_test.js" />
<option name="noStdlib" value="true" />
<option name="sourceMap" value="true" />
<option name="metaInfo" value="true" />
<option name="target" value="v5" />
<option name="moduleKind" value="umd" />
<option name="main" value="call" />
<option name="languageVersion" value="1.3" />
<option name="apiVersion" value="1.3" />
<option name="pluginOptions">
<array />
</option>
<option name="pluginClasspaths">
<array />
</option>
<option name="multiPlatform" value="true" />
<option name="errors">
<ArgumentParseErrors />
</option>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<output-test url="file://$MODULE_DIR$/build/classes/kotlin/test" />
<exclude-output />
<content url="file://$MODULE_DIR$/src/test/kotlin" />
<content url="file://$MODULE_DIR$/src/test/resources" />
<orderEntry type="jdk" jdkName="Kotlin SDK" jdkType="KotlinSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="komp_main" scope="TEST" />
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/build/classes/kotlin/main" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.3.70" level="project" />
<orderEntry type="module-library" scope="RUNTIME">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/build/processedResources/Js/main" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.1" level="project" />
</component>
<component name="TestModuleProperties" production-module="komp_main" />
</module>

View File

@@ -3,349 +3,203 @@ package nl.astraeus.komp
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import org.w3c.dom.events.Event
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"
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 {
fun hashesMatch(oldNode: Node, newNode: Node): Boolean {
return (
oldNode is HTMLElement &&
newNode is HTMLElement &&
oldNode.nodeName == newNode.nodeName &&
oldNode.getKompHash() == newNode.getKompHash()
)
private fun updateKomponentOnNode(element: Node, newVdom: VDOMElement) {
val komponent = newVdom.komponent
if (komponent != null) {
if (Komponent.logReplaceEvent) {
console.log("Keeping oldNode, set oldNode element on Komponent", element, komponent)
}
komponent.element = element
}
}
fun updateNode(oldNode: Node, newNode: Node): Node {
if (hashesMatch(oldNode, newNode)) {
fun updateNode(element: Node, oldVdom: VDOMElement, newVdom: VDOMElement): Node {
if (oldVdom.hash == newVdom.hash) {
if (Komponent.logReplaceEvent) {
console.log("Hashes match", oldNode, newNode, oldNode.getKompHash(), newNode.getKompHash())
console.log("Hashes match", oldVdom, newVdom, oldVdom.hash, newVdom.hash)
}
return oldNode
// no change
return element
}
if (oldNode.nodeType == newNode.nodeType && oldNode.nodeType == 3.toShort()) {
if (oldNode.textContent != newNode.textContent) {
if (oldVdom.type == newVdom.type && oldVdom.type == VDOMElementType.TEXT) {
if (oldVdom.content != newVdom.content) {
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
}
return oldNode
return element
}
if (oldNode is HTMLElement && newNode is HTMLElement) {
if (oldNode.nodeName == newNode.nodeName) {
if (oldVdom.type == newVdom.type && oldVdom.type == VDOMElementType.TAG) {
if (oldVdom.content == newVdom.content) {
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) {
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) {
console.log("Update children", oldNode.nodeName, newNode.nodeName)
console.log("Update children", oldVdom.content, newVdom.content)
}
updateChildren(oldNode, newNode)
oldNode.setKompHash(newNode.getKompHash())
return oldNode
updateKomponentOnNode(element, newVdom)
updateChildren(element, oldVdom, newVdom)
return element
}
}
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)
//replaceNode(oldNode, newNode)
val newNode = newVdom.createElement()
updateKomponentOnNode(newNode, newVdom)
element.parentNode?.replaceChild(newNode, element)
return newNode
}
private fun updateAttributes(oldNode: HTMLElement, newNode: HTMLElement) {
private fun updateAttributes(element: HTMLElement, oldVdom: VDOMElement, newVdom: VDOMElement) {
// removed attributes
for (name in oldNode.getAttributeNames()) {
val attr = oldNode.attributes[name]
if (attr != null && newNode.getAttribute(name) == null) {
oldNode.removeAttribute(name)
for ((name, attr) in oldVdom.attributes) {
if (newVdom.attributes[name] == null) {
element.removeAttribute(name)
}
}
for (name in newNode.getAttributeNames()) {
val value = newNode.getAttribute(name)
val oldValue = oldNode.getAttribute(name)
for ((name, value) in newVdom.attributes) {
val oldValue = oldVdom.attributes[name]
if (value != oldValue) {
if (value != null) {
oldNode.setAttribute(name, value)
}else {
oldNode.removeAttribute(name)
}
element.setAttribute(name, value)
}
}
if (newNode is HTMLInputElement && oldNode is HTMLInputElement) {
oldNode.value = newNode.value
}
/*
for (index in 0 until newNode.attributes.length) {
val attr = newNode.attributes[index]
if (attr != null) {
val oldAttr = oldNode.attributes[attr.name]
if (oldAttr == null || oldAttr.value != attr.value) {
oldNode.setAttribute(attr.name, attr.value)
}
if (newVdom.content == "input" && oldVdom.content == "input") {
if (element is HTMLInputElement) {
element.value = newVdom.attributes["value"] ?: ""
element.checked = newVdom.attributes["checked"] == "true"
}
}
*/
}
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
private fun updateChildren(
element: HTMLElement,
oldVdom: VDOMElement,
newVdom: VDOMElement
) {
var oldIndex = 0
var newIndex = 0
if (Komponent.logReplaceEvent) {
console.log(
"updateChildren HTML old(${oldNode.childNodes.length})",
oldNode.innerHTML
"updateChildren HTML old(${oldVdom.childNodes.size})",
oldVdom.toString()
)
console.log(
"updateChildren HTML new(${newNode.childNodes.length})",
newNode.innerHTML
"updateChildren HTML new(${newVdom.childNodes.size})",
newVdom.toString()
)
}
while (newIndex < newNode.childNodes.length) {
while (oldIndex < oldVdom.childNodes.size && newIndex < newVdom.childNodes.size) {
if (Komponent.logReplaceEvent) {
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) {
console.log("Update node Old/new", oldChildNode, newChildNode)
}
if (!hashesMatch(oldChildNode, newChildNode) && newChildNode is HTMLElement && oldChildNode is HTMLElement) {
if (Komponent.logReplaceEvent) {
console.log("Hashes don't match")
}
val oldHash = oldChildNode.getKompHash()
val newHash = newChildNode.getKompHash()
if (newHash >= 0) {
val oldNodeWithNewHashIndex = oldNode.childNodes.findNodeHashIndex(newHash)
if (Komponent.logReplaceEvent) {
console.log("oldNodeWithNewHashIndex", newHash, oldNodeWithNewHashIndex)
}
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
}
}
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 >= 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)
}
}
oldIndex++
newIndex++
} else {
if (Komponent.logReplaceEvent) {
console.log("Append Old/new/node", oldIndex, newIndex, newChildNode)
}
oldNode.append(newChildNode)
oldIndex++
if (Komponent.logReplaceEvent) {
console.log("Update node Old/new", oldChildNode, newChildNode)
}
/*
if (Komponent.logReplaceEvent) {
console.log("<<< Updated Old/new", oldNode.innerHTML, newNode.innerHTML)
}
*/
// 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) {
console.log("Hashes don't match")
}
val oldHash = oldChildNode.hash.hashCode()
val newHash = newChildNode.hash.hashCode()
val newHashIndexInOld = oldVdom.findNodeHashIndex(newHash)
val oldHashIndexInNew = newVdom.findNodeHashIndex(oldHash)
if (newHashIndexInOld == oldIndex + 1 && oldHashIndexInNew == newIndex) {
// remove
element.removeChild(element.childNodes[oldIndex]!!)
oldIndex++
continue
} else if (newHashIndexInOld == oldIndex && oldHashIndexInNew == newIndex + 1) {
// insert
element.insertBefore(newChildNode.createElement(), element.childNodes[oldIndex]!!)
newIndex++
oldIndex++
continue
}
}
// update
updateNode(element.childNodes[oldIndex]!!, oldChildNode, newChildNode)
oldIndex++
newIndex++
}
while (oldIndex < oldNode.childNodes.length) {
oldNode.childNodes[oldIndex]?.also {
while (element.childNodes.length > newVdom.childNodes.size) {
element.childNodes[element.childNodes.length - 1]?.also {
if (Komponent.logReplaceEvent) {
console.log("Remove old node", it)
}
oldNode.removeChild(it)
element.removeChild(it)
}
}
while (newIndex < newVdom.childNodes.size) {
newVdom.childNodes[newIndex].also {
element.appendChild(it.createElement())
}
newIndex++
}
}
private fun updateEvents(oldNode: HTMLElement, newNode: HTMLElement) {
val oldEvents = mutableListOf<String>()
oldEvents.addAll((oldNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(","))
val newEvents = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
private fun updateEvents(element: HTMLElement, oldVdom: VDOMElement, newVdom: VDOMElement) {
val oldEvents = oldVdom.events
val newEvents = newVdom.events
if (Komponent.logReplaceEvent) {
console.log("Update events", oldNode.getAttribute(EVENT_ATTRIBUTE), newNode.getAttribute(EVENT_ATTRIBUTE))
console.log("Update events", oldEvents, newEvents)
}
for (event in newEvents) {
if (event.isNotBlank()) {
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
val newNodeEvent = newNode.asDynamic()["event-$event"]
if (oldNodeEvent != null) {
if (Komponent.logReplaceEvent) {
console.log("Remove old event $event")
}
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
}
if (newNodeEvent != null) {
if (Komponent.logReplaceEvent) {
console.log("Set event $event on", oldNode)
}
oldNode.setEvent(event, newNodeEvent as ((Event) -> Unit))
}
oldEvents.remove(event)
}
}
for (event in oldEvents) {
if (event.isNotBlank()) {
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
if (oldNodeEvent != null) {
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
}
}
}
newNode.getAttribute(EVENT_ATTRIBUTE)?.also {
oldNode.setAttribute(EVENT_ATTRIBUTE, it)
}
}
private fun replaceNode(oldNode: Node, newNode: Node) {
oldNode.parentNode?.also { parent ->
val clone = newNode.cloneNode(true)
cloneEvents(clone, newNode)
parent.replaceChild(clone, oldNode)
}
}
private fun cloneEvents(destination: Node, source: Node) {
if (source is HTMLElement && destination is HTMLElement) {
val events = (source.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
for (event in events) {
if (event.isNotBlank()) {
if (Komponent.logReplaceEvent) {
console.log("Clone event $event on", source)
}
val foundEvent = source.asDynamic()["event-$event"]
if (foundEvent != null) {
if (Komponent.logReplaceEvent) {
console.log("Clone add eventlistener", foundEvent)
}
destination.setEvent(event, foundEvent as ((Event) -> Unit))
} else {
if (Komponent.logReplaceEvent) {
console.log("Event not found $event", source)
}
}
}
}
}
for (index in 0 until source.childNodes.length) {
destination.childNodes[index]?.also { destinationChild ->
source.childNodes[index]?.also { sourceChild ->
cloneEvents(destinationChild, sourceChild)
}
for ((name, event) in oldEvents) {
element.removeEventListener(name, event)
}
for ((name, event) in newEvents) {
if (Komponent.logReplaceEvent) {
console.log("Set event $event on", element)
}
element.addEventListener(name, event)
}
}

View File

@@ -1,58 +1,224 @@
package nl.astraeus.komp
import kotlinx.browser.document
import kotlinx.html.*
import org.w3c.dom.*
import org.w3c.dom.css.CSSStyleDeclaration
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.events.Event
import kotlin.browser.document
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")
inline fun HTMLElement.setEvent(name: String, noinline callback: (Event) -> Unit): Unit {
val eventName = if (name.startsWith("on")) {
name.substring(2)
} else {
name
private fun attributeHash(key: String, value: String): Int =
3 * key.hashCode() +
5 * value.hashCode()
private fun MutableMap<String, String>.kompHash(): Int {
var result = 0
for ((name, value) in this) {
result += attributeHash(name, value)
}
addEventListener(eventName, callback, null)
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
//asDynamic()[name] = callback
val events = getAttribute(EVENT_ATTRIBUTE) ?: ""
setAttribute(
EVENT_ATTRIBUTE,
if (events.isBlank()) {
eventName
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 {
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 {
"$events,$eventName"
document.createElement(content)
}
)
asDynamic()["event-$eventName"] = callback
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<HTMLElement> {
fun append(node: Node)
}
fun HTMLElement.setStyles(cssStyle: CSSStyleDeclaration) {
for (index in 0 until cssStyle.length) {
val propertyName = cssStyle.item(index)
style.setProperty(propertyName, cssStyle.getPropertyValue(propertyName))
}
interface HtmlConsumer : TagConsumer<VDOMElement> {
fun append(node: VDOMElement)
}
class HtmlBuilder(
val komponent: Komponent,
val document: Document
val baseHash: Int
) : HtmlConsumer {
private val path = arrayListOf<HTMLElement>()
private var lastLeaved: HTMLElement? = null
private val path = arrayListOf<VDOMElement>()
private var lastLeaved: VDOMElement? = null
override fun onTagStart(tag: Tag) {
val element: HTMLElement = when {
tag.namespace != null -> document.createElementNS(tag.namespace!!, tag.tagName).asDynamic()
else -> document.createElement(tag.tagName) as HTMLElement
val element = VDOMElement(baseHash, tag.tagName, tag.namespace)
for (entry in tag.attributesEntries) {
element.setAttribute(entry.key, entry.value)
}
if (path.isNotEmpty()) {
@@ -64,9 +230,9 @@ class HtmlBuilder(
override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) {
when {
path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().tagName.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().let { node ->
path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().content.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().let { node ->
if (value == null) {
node.removeAttribute(attribute)
} else {
@@ -78,98 +244,19 @@ class HtmlBuilder(
override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) {
when {
path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().tagName.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().setEvent(event, value)
path.isEmpty() -> throw IllegalStateException("No current tag")
path.last().content.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
else -> path.last().setKompEvent(event, value)
}
}
override fun onTagEnd(tag: Tag) {
var hash = 0
if (path.isEmpty() || path.last().tagName.toLowerCase() != tag.tagName.toLowerCase()) {
if (path.isEmpty() || path.last().content.toLowerCase() != tag.tagName.toLowerCase()) {
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(hash)
//element.setAttribute(DiffPatch.HASH_ATTRIBUTE, hash.toString(16))
}
lastLeaved = path.removeAt(path.lastIndex)
lastLeaved?.updateChildHash()
}
override fun onTagContent(content: CharSequence) {
@@ -177,7 +264,7 @@ class HtmlBuilder(
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) {
@@ -185,16 +272,11 @@ class HtmlBuilder(
throw IllegalStateException("No current DOM node")
}
// stupid hack as browsers doesn't support createEntityReference
val s = document.createElement("span") as HTMLElement
s.innerHTML = entity.text
path.last().appendChild(s.childNodes.asList().first { it.nodeType == Node.TEXT_NODE })
// other solution would be
// pathLast().innerHTML += entity.text
// stupid hack as browsers don't support createEntityReference
path.last().appendChild(VDOMElement(baseHash, entity.text, type = VDOMElementType.ENTITY))
}
override fun append(node: Node) {
override fun append(node: VDOMElement) {
path.last().appendChild(node)
}
@@ -202,7 +284,7 @@ class HtmlBuilder(
with(DefaultUnsafe()) {
block()
path.last().innerHTML += toString()
path.last().appendChild(VDOMElement(baseHash, toString(), type = VDOMElementType.UNSAFE))
}
}
@@ -211,17 +293,17 @@ class HtmlBuilder(
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")
@Suppress("UNCHECKED_CAST")
private fun HTMLElement.asR(): HTMLElement = this.asDynamic()
override fun finalize(): VDOMElement {
return lastLeaved ?: throw IllegalStateException("We can't finalize as there was no tags")
}
companion object {
fun create(content: HtmlBuilder.() -> Unit): HTMLElement {
val consumer = HtmlBuilder(DummyKomponent(), document)
fun create(content: HtmlBuilder.() -> Unit): VDOMElement {
val komponent = DummyKomponent()
val consumer = HtmlBuilder(komponent.hashCode())
content.invoke(consumer)
return consumer.finalize()
}

View File

@@ -1,28 +1,38 @@
package nl.astraeus.komp
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.html.div
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import org.w3c.dom.css.CSSStyleDeclaration
import kotlin.browser.document
import kotlin.reflect.KProperty
typealias CssStyle = CSSStyleDeclaration.() -> Unit
fun HtmlConsumer.include(component: Komponent) {
if (Komponent.updateStrategy == UpdateStrategy.REPLACE) {
if (component.element != null) {
component.update()
} else {
component.refresh()
}
class StateDelegate<T>(
val komponent: Komponent,
initialValue: T
) {
var value: T = initialValue
component.element?.also {
append(it)
}
} else {
append(component.create())
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
if (this.value?.equals(value) != true) {
this.value = value
komponent.requestUpdate()
}
}
}
inline fun <reified T> Komponent.state(initialValue: T): StateDelegate<T> = StateDelegate(this, initialValue)
fun HtmlConsumer.include(component: Komponent) {
append(component.create())
}
class DummyKomponent: Komponent() {
@@ -33,27 +43,45 @@ class DummyKomponent: Komponent() {
}
}
enum class UpdateStrategy {
REPLACE,
DOM_DIFF
}
abstract class Komponent {
private var createIndex = getNextCreateIndex()
private var dirty: Boolean = true
var vdom: VDOMElement? = null
var element: Node? = null
val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap()
open fun create(): HTMLElement {
val consumer = HtmlBuilder(this, document)
consumer.render()
open fun create(): VDOMElement {
val consumer = HtmlBuilder(this.createIndex)
try {
consumer.render()
} catch (e: Throwable) {
println("Exception occurred in ${this::class.simpleName}.render() call!")
throw e
}
val result = consumer.finalize()
element = result
if (result.id.isBlank()) {
result.id = "komp_${createIndex}"
}
result.komponent = this
vdom = result
dirty = false
return result
}
abstract fun HtmlBuilder.render()
fun requestUpdate() {
dirty = true
scheduleForUpdate(this)
}
open fun style(className: String, vararg imports: CssStyle, block: CssStyle = {}) {
val style = (document.createElement("div") as HTMLDivElement).style
for (imp in imports) {
@@ -63,51 +91,46 @@ abstract class Komponent {
declaredStyles[className] = style
}
open fun update() = refresh()
open fun update() {
refresh()
}
internal fun refresh() {
val oldElement = vdom
open fun refresh() {
val oldElement = element
if (logRenderEvent) {
console.log("Rendering", this)
}
val newElement = create()
if (oldElement != null) {
if (updateStrategy == UpdateStrategy.REPLACE) {
if (logReplaceEvent) {
console.log("Replacing", oldElement, newElement)
}
oldElement.parentNode?.replaceChild(newElement, oldElement)
element = newElement
} else {
if (logReplaceEvent) {
console.log("DomDiffing", oldElement, newElement)
}
element = DiffPatch.updateNode(oldElement, newElement)
}
}
}
@JsName("remove")
fun remove() {
element?.let {
val parent = it.parentElement ?: throw IllegalArgumentException("Element has no parent!?")
element = if (oldElement != null && element != null) {
if (logReplaceEvent) {
console.log("Remove", it)
console.log("DomDiffing", oldElement, newElement)
}
parent.removeChild(it)
DiffPatch.updateNode(element!!, oldElement, newElement)
} else {
if (logReplaceEvent) {
console.log("Create", newElement)
}
newElement.createElement()
}
vdom = newElement
dirty = false
}
companion object {
private var nextCreateIndex: Int = 1
private var updateCallback: Int? = null
private var scheduledForUpdate = mutableSetOf<Komponent>()
var logRenderEvent = false
var logReplaceEvent = false
var updateStrategy = UpdateStrategy.DOM_DIFF
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) {
parent.insertBefore(element, parent.firstChild)
@@ -115,6 +138,52 @@ abstract class Komponent {
parent.appendChild(element)
}
}
private fun getNextCreateIndex() = nextCreateIndex++
private fun scheduleForUpdate(komponent: Komponent) {
scheduledForUpdate.add(komponent)
if (updateCallback == null) {
window.setTimeout({
runUpdate()
}, 0)
}
}
private fun runUpdate() {
val todo = scheduledForUpdate.sortedBy { komponent -> komponent.createIndex }
if (logRenderEvent) {
console.log("runUpdate")
}
todo.forEach { next ->
val element = next.element
if (element is HTMLElement) {
if (document.getElementById(element.id) != null) {
if (next.dirty) {
if (logRenderEvent) {
console.log("Update dirty ${next.createIndex}")
}
next.update()
} else {
if (logRenderEvent) {
console.log("Skip ${next.createIndex}")
}
}
} else {
console.log("Komponent element has no id, ", next, element)
}
} else {
console.log("Komponent element is null", next)
}
}
scheduledForUpdate.clear()
updateCallback = null
}
}
}

View File

@@ -2,46 +2,49 @@ package nl.astraeus.komp
import kotlinx.html.*
import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import org.w3c.dom.get
import kotlin.test.Test
import kotlin.test.assertTrue
fun nodesEqual(node1: Node, node2: Node): Boolean {
if (node1.childNodes.length != node1.childNodes.length) {
fun nodesEqual(element: Element, vdom: VDOMElement): Boolean {
if (element.childNodes.length != vdom.childNodes.size) {
return false
}
if (node1 is HTMLElement && node2 is HTMLElement) {
if (node1.attributes.length != node2.attributes.length) {
if (element.attributes.length != vdom.attributes.size) {
return false
}
for ((name, value) in vdom.attributes) {
if (value != element.getAttribute(name)) {
return false
}
for (index in 0 until node1.attributes.length) {
node1.attributes[index]?.also { attr1 ->
val attr2 = node2.getAttribute(attr1.name)
}
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
}
}
}
for (index in 0 until node1.childNodes.length) {
node1.childNodes[index]?.also { child1 ->
node2.childNodes[index]?.also { child2 ->
if (!nodesEqual(child1, child2)) {
return false
}
}
}
} else {
return false
}
}
return true
}
class TestUpdate {
@Test
fun testCompare1() {
fun testCompare() {
val dom1 = HtmlBuilder.create {
div {
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
@@ -88,7 +95,7 @@ class TestUpdate {
table {
tr {
th {
+ "Header"
+"Header"
}
}
tr {
@@ -105,7 +112,7 @@ class TestUpdate {
div {
div {
span {
+ "Other text"
+"Other text"
}
}
span {
@@ -123,10 +130,15 @@ class TestUpdate {
}
}
Komponent.logReplaceEvent = true
DiffPatch.updateNode(dom1, dom2)
Komponent.logRenderEvent = true
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")
}
}