8 Commits

Author SHA1 Message Date
963e934953 Fix svg/unsafe, add onBeforeUpdate, v. 0.5.6 2021-08-19 09:53:16 +02:00
0ccbe105b7 Fix memoize option, v. 0.5.5 2021-08-18 11:07:26 +02:00
dac465a161 Fixes 2021-07-11 13:29:34 +02:00
48708580ca Cleanup 2021-07-02 19:47:27 +02:00
1e6e34bec5 Do updates immediatly 2021-07-02 19:45:48 +02:00
fa6252832b Cleanup 2021-06-29 17:21:27 +02:00
d0442e785f VDom implementation 2021-04-05 17:21:45 +02:00
68fde339a8 Remove replace option 2021-03-31 16:17:01 +02:00
24 changed files with 1131 additions and 819 deletions

5
.gitignore vendored
View File

@@ -4,8 +4,3 @@ build
web/js/generated
gradle.properties
local.properties
*.iml
*.ipr
*.iws
kotlin-js-store
.idea

View File

@@ -1,138 +1,79 @@
plugins {
kotlin("multiplatform") version "1.7.20"
`maven-publish`
signing
id("org.jetbrains.dokka") version "1.5.31"
kotlin("multiplatform") version "1.5.21"
`maven-publish`
}
group = "nl.astraeus"
version = "1.0.8-SNAPSHOT"
version = "0.5.7-SNAPSHOT"
repositories {
mavenCentral()
mavenCentral()
}
kotlin {
js(BOTH) {
browser {
testTask {
useKarma {
useChromiumHeadless()
js(BOTH) {
browser {
testTask {
useKarma {
useChromiumHeadless()
//useChromeHeadless()
}
}
}
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.3")
}
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.3")
}
}
val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
}
val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
extra["PUBLISH_GROUP_ID"] = group
extra["PUBLISH_VERSION"] = version
extra["PUBLISH_ARTIFACT_ID"] = name
// Stub secrets to let the project sync and build without the publication values set up
val signingKeyId: String? by project
val signingPassword: String? by project
val signingSecretKeyRingFile: String? by project
val ossrhUsername: String? by project
val ossrhPassword: String? by project
extra["signing.keyId"] = signingKeyId
extra["signing.password"] = signingPassword
extra["signing.secretKeyRingFile"] = signingSecretKeyRingFile
extra["ossrhUsername"] = ossrhUsername
extra["ossrhPassword"] = ossrhPassword
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}
publishing {
repositories {
mavenLocal()
maven {
name = "releases"
// change to point to your repo, e.g. http://my.org/repo
setUrl("https://nexus.astraeus.nl/nexus/content/repositories/releases")
credentials {
val nexusUsername: String? by project
val nexusPassword: String? by project
repositories {
if (project.properties["nexusUsername"] != null) {
maven {
name = "releases"
url = uri("https://nexus.astraeus.nl/nexus/content/repositories/releases")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
username = nexusUsername
password = nexusPassword
}
}
maven {
name = "snapshots"
// change to point to your repo, e.g. http://my.org/repo
setUrl("https://nexus.astraeus.nl/nexus/content/repositories/snapshots")
credentials {
val nexusUsername: String? by project
val nexusPassword: String? by project
username = nexusUsername
password = nexusPassword
}
}
maven {
name = "snapshots"
url = uri("https://nexus.astraeus.nl/nexus/content/repositories/snapshots")
credentials {
val nexusUsername: String by project
val nexusPassword: String by project
username = nexusUsername
password = nexusPassword
}
}
maven {
name = "sonatype"
setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2")
credentials {
username = ossrhUsername
password = ossrhPassword
}
}
}
// Configure all publications
publications.withType<MavenPublication> {
// Stub javadoc.jar artifact
artifact(javadocJar.get())
// Provide artifacts information requited by Maven Central
pom {
name.set("kotlin-komponent")
description.set("Kotlin komponent")
url.set("https://github.com/rnentjes/komponent")
licenses {
license {
name.set("MIT")
url.set("https://opensource.org/licenses/MIT")
username = nexusUsername
password = nexusPassword
}
}
} else {
println("Publishing disabled properties not found.")
}
}
developers {
developer {
id.set("rnentjes")
name.set("Rien Nentjes")
email.set("info@nentjes.com")
}
}
scm {
url.set("https://github.com/rnentjes/komponent")
}
}
}
}
signing {
sign(publishing.publications)
publications {
val kotlinMultiplatform by getting {}
}
}

View File

@@ -1,147 +0,0 @@
# Table of contents
* [Home](home.md)
* [Getting started](getting-started.md)
* [How it works](how-it-works.md)
# Getting started
To get started create a new kotlin project in intellij of the type 'Browser application'
![Create 'Browser Application' project](/docs/img/create-project.png)
Add the 'sourceSets' block with the kotlin-komponent dependency so your build.gradle.kts looks like this:
```gradle
plugins {
kotlin("js") version "1.6.10"
}
group = "com.test"
version = "1.0.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
api("nl.astraeus:kotlin-komponent-js:1.0.0")
testImplementation(kotlin("test"))
}
kotlin {
js(IR) {
binaries.executable()
browser {
commonWebpackConfig {
cssSupport.enabled = true
}
}
}
}
```
Refresh the gradle project to import the dependency.
There is now only one kotlin source file in the project called Simple.kt, it should look something like this:
```kotin
fun main() {
console.log("Hello, ${greet()}")
}
fun greet() = "world"
```
Replace the code in the file with the following for a simple click app:
```koltin
import kotlinx.browser.document
import kotlinx.html.button
import kotlinx.html.div
import kotlinx.html.hr
import kotlinx.html.js.onClickFunction
import nl.astraeus.komp.HtmlBuilder
import nl.astraeus.komp.Komponent
import nl.astraeus.komp.mutableCollectionState
import nl.astraeus.komp.state
import kotlin.js.Date
fun main() {
console.log("Hello, ${greet()}")
Komponent.create(document.body!!, TestKomponent())
}
fun greet() = "world"
class TestKomponent : Komponent() {
var clicks: Int = 0
val lines: MutableCollection<String> = mutableListOf()
override fun HtmlBuilder.render() {
div {
div {
+"Hello Komponent!"
}
div {
+"Clicks $clicks"
}
div {
button {
+"Click"
onClickFunction = {
clicks++
lines.add("click $clicks at ${Date()}")
requestUpdate()
}
}
}
hr()
for (line in lines) {
div {
+ line
}
}
}
}
}
```
First in the main we add our TestKomponent to the document body with the following line:
```kotlin
Komponent.create(document.body!!, TestKomponent())
```
The TestKomponent.render method will be called to render our Komponent.
As you can see events can be attached inline with the on<event>Function methods.
The requestUpdate method will call the render method again and update the page accordingly.
After building the application you will find it in /build/distributions.
In the index.html page you will find the following line:
```html
<div id="root"></div>
```
This line is not needed for kotlin-komponent.
If you like you can use some helpers that will automatically call the requestUpdate method if
the data changes, that would look like this:
```kotlin
var clicks: Int by state(0)
val lines: MutableCollection<String> = mutableCollectionState(mutableListOf())
```
In that case you can remove the requestUpdate call from the onClickFunction.
You can find a working repository of this example here: [example]()

View File

@@ -1,5 +0,0 @@
# Table of contents
* [Home](home.md)
* [Getting started](getting-started.md)
* [How it works](how-it-works.md)

View File

@@ -1,23 +0,0 @@
# Table of contents
* [Home](home.md)
* [Getting started](getting-started.md)
* [How it works](how-it-works.md)
# How it works
When the requestUpdate call is made to the [Komponent](src/jsMain/kotlin/nl/astraeus/komp/Komponent.kt)
the update is queued in a callback. The callback will be called after the current event is handled.
If there are multiple updates requested, these are sorted so that the top Komponents get executed first.
This way there will not be double updates of the same komponent.
The render call will be invoked and every html builder function (div, span etc.) will call the
different HtmlBuilder functions like onTagStart, onTagAttributeChange etc.
In these functions the HtmlBuilder will compare the dom against the call being made and it will update the DOM
if needed.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -1,5 +1,5 @@
#Wed Mar 04 13:29:12 CET 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists

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.5.6-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-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings />
<compilerArguments>
<option name="languageVersion" value="1.5" />
<option name="apiVersion" value="1.5" />
<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.5.21/8143908a15163e634fecb87013ed4a139170b032/kotlin-scripting-jvm-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.5.21/5d271f9e7e7191902f8c0d45c600f5b57a284036/kotlin-scripting-common-1.5.21.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.5.21/2f537cad7e9eeb9da73738c8812e1e4cf9b62e4e/kotlin-stdlib-1.5.21.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-stdlib-common/1.5.21/cc8bf3586fd2ebcf234058b9440bb406e62dfacb/kotlin-stdlib-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.5.21/96d49e89873fde985750af354b6eabb60cfa999b/kotlin-script-runtime-1.5.21.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.5.21" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.3" 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.5.6-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-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/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.5" />
<option name="apiVersion" value="1.5" />
<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.5.21/8143908a15163e634fecb87013ed4a139170b032/kotlin-scripting-jvm-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.5.21/5d271f9e7e7191902f8c0d45c600f5b57a284036/kotlin-scripting-common-1.5.21.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.5.21/2f537cad7e9eeb9da73738c8812e1e4cf9b62e4e/kotlin-stdlib-1.5.21.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-stdlib-common/1.5.21/cc8bf3586fd2ebcf234058b9440bb406e62dfacb/kotlin-stdlib-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.5.21/96d49e89873fde985750af354b6eabb60cfa999b/kotlin-script-runtime-1.5.21.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-common:1.5.21" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.5.21" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.3" level="project" />
</component>
<component name="TestModuleProperties" production-module="komp.commonMain" />
</module>

12
komp.iml Normal file
View File

@@ -0,0 +1,12 @@
<?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.5.6-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

570
komp.ipr Normal file
View File

@@ -0,0 +1,570 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ArtifactManager">
<artifact type="jar" name="komp-0.1.16-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-0.1.16-SNAPSHOT.jar">
<element id="module-output" name="komp_main" />
</root>
</artifact>
<artifact type="jar" name="komp-0.1.17-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-0.1.17-SNAPSHOT.jar">
<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>
<artifact type="jar" name="komp-jslegacy-0.4.0-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.4.0-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.4.1">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.4.1.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.1">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.1.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.2">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.2.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.3">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.3.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.4">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.4.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.6-SNAPSHOT">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.6-SNAPSHOT.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
</component>
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="checkstyle-version" value="8.5" />
<entry key="copy-libs" value="false" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="scan-before-checkin" value="false" />
<entry key="scanscope" value="JavaOnly" />
<entry key="suppress-errors" value="false" />
</map>
</option>
</component>
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="GradleLocalSettings">
<option name="modificationStamps">
<map>
<entry key="$PROJECT_DIR$/../../DestinationSol" value="5718836739744" />
<entry key="$PROJECT_DIR$/../../kotlin/kotlin-webgl-test" value="2938763414342" />
<entry key="$PROJECT_DIR$/../../music/dawjs" value="2921352917165" />
<entry key="$PROJECT_DIR$/../../music/mto" value="7363595652795" />
<entry key="$PROJECT_DIR$/../../music/synthesis" value="2936164705438" />
<entry key="$PROJECT_DIR$/../../simple/Simple-persistence" value="2890201693583" />
<entry key="$PROJECT_DIR$/../../simple/Simple-web" value="2890212261770" />
<entry key="$PROJECT_DIR$/../../simple/SimpleDatabase" value="2890191706466" />
<entry key="$PROJECT_DIR$/../../simple/Very-simple-templates" value="2890190703434" />
<entry key="$PROJECT_DIR$/../../simple/simple-password-manager" value="8877848261147" />
<entry key="$PROJECT_DIR$/../../simple/simple-password-manager/client" value="2959282312702" />
<entry key="$PROJECT_DIR$/../../simple/simple-password-manager/server" value="2952285414000" />
<entry key="$PROJECT_DIR$/../../simple/simple-social-media" value="2954146711424" />
<entry key="$PROJECT_DIR$/../../simple/stats/simple-stats-client" value="2941044635281" />
<entry key="$PROJECT_DIR$/../../simple/stats/simple-stats-server" value="2909544379158" />
<entry key="$PROJECT_DIR$/../../work/Kos/KosConvertBranchcode" value="2904315467504" />
</map>
</option>
<option name="externalProjectsViewState">
<projects_view />
</option>
</component>
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="delegatedBuild" value="false" />
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
<component name="HotSwapAgentPluginSettingsProvider">
<option name="agentPath" value="$APPLICATION_PLUGINS_DIR$/hotswap-agent-intellij-plugin/lib/agent/hotswap-agent-1.3.0.jar" />
</component>
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
<version value="1.0" />
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="qFcQOWli" />
</component>
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" />
<component name="ProjectCodeStyleConfiguration">
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="140" />
<H2CodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
</H2CodeStyleSettings>
<H2CodeStyleSettings version="6">
<option name="USE_GENERAL_STYLE" value="false" />
</H2CodeStyleSettings>
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="5" />
<option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="50" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="99" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="99" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
<option name="ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS" value="true" />
<option name="ALIGN_CONSECUTIVE_ASSIGNMENTS" value="true" />
<option name="ALIGN_SUBSEQUENT_SIMPLE_METHODS" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="5" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_WRAP" value="5" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="RESOURCE_LIST_WRAP" value="5" />
<option name="RESOURCE_LIST_LPAREN_ON_NEXT_LINE" value="true" />
<option name="RESOURCE_LIST_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="5" />
<option name="THROWS_LIST_WRAP" value="5" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="5" />
<option name="PARENTHESES_EXPRESSION_LPAREN_WRAP" value="true" />
<option name="PARENTHESES_EXPRESSION_RPAREN_WRAP" value="true" />
<option name="BINARY_OPERATION_WRAP" value="5" />
<option name="TERNARY_OPERATION_WRAP" value="5" />
<option name="FOR_STATEMENT_WRAP" value="5" />
<option name="FOR_STATEMENT_LPAREN_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="ASSERT_STATEMENT_WRAP" value="5" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="ENUM_CONSTANTS_WRAP" value="5" />
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
<component name="ProjectModuleManager">
<modules>
<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>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
<component name="PropertiesComponent">
<property name="GoToClass.includeLibraries" value="false" />
<property name="GoToClass.toSaveIncludeLibraries" value="false" />
<property name="GoToFile.includeJavaFiles" value="false" />
<property name="MemberChooser.sorted" value="false" />
<property name="MemberChooser.showClasses" value="true" />
<property name="MemberChooser.copyJavadoc" value="false" />
<property name="nodejs_interpreter_path" value="/usr/bin/node" />
</component>
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<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="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
<component name="RunManager">
<configuration default="true" type="Applet" factoryName="Applet">
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<module />
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
</configuration>
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
<module name="" />
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
<option name="PROGRAM_PARAMETERS" />
<predefined_log_file id="idea.log" enabled="true" />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
</configuration>
<configuration name="&lt;template&gt;" type="TestNG" default="true" selected="false">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
</configuration>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
<component name="accountSettings">
<option name="recentlyUsedRegions">
<list>
<option value="us-east-1" />
</list>
</option>
</component>
<component name="libraryTable">
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.5.21" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.21/cc8bf3586fd2ebcf234058b9440bb406e62dfacb/kotlin-stdlib-common-1.5.21.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.21/5b8f86fea035328fc9e8c660773037a3401ce25f/kotlin-stdlib-common-1.5.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.21/64f0dd5eac2d479a65bc077185d9a8f661c948fe/kotlin-stdlib-common-1.5.21-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.5.21" type="kotlin.js">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.5.21/9b22e0c2e0dcf2ba9f27e45a48944d05b0384bc3/kotlin-stdlib-js-1.5.21.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-js/1.5.21/dcd3e87dab1d7ecc7f2952d5d213e20f977bbddc/kotlin-stdlib-js-1.5.21-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.5.21" type="kotlin.js">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.5.21/db9c8e994715fb1edd5bef91db179b60347cbfc9/kotlin-test-js-1.5.21.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-js/1.5.21/8e70170db077d3f421c3fb6d24468b3938990f4a/kotlin-test-js-1.5.21-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.3" type="kotlin.common">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.3/4f3df7b2096bce6fef3e532ca1ccff13bd15a6bc/kotlinx-html-metadata-0.7.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-common/0.7.3/f4eb48699d893b863f142a3c30e1ddddd13b876/kotlinx-html-common-0.7.3-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.3" type="kotlin.js">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.3/93a1ab4269d3a567e05088d1237eedbc2efd03c9/kotlinx-html-jslegacy-0.7.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-html-js/0.7.3/fc3bc71d1c037b715689112c95c44b96bb2409ea/kotlinx-html-js-0.7.3-sources.jar!/" />
</SOURCES>
</library>
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.7</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

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.5.6-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-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/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-js-legacy/kotlin/komp-js-legacy.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.5" />
<option name="apiVersion" value="1.5" />
<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.5.21/96d49e89873fde985750af354b6eabb60cfa999b/kotlin-script-runtime-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.5.21/5d271f9e7e7191902f8c0d45c600f5b57a284036/kotlin-scripting-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.5.21/8143908a15163e634fecb87013ed4a139170b032/kotlin-scripting-jvm-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.21/cc8bf3586fd2ebcf234058b9440bb406e62dfacb/kotlin-stdlib-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.5.21/2f537cad7e9eeb9da73738c8812e1e4cf9b62e4e/kotlin-stdlib-1.5.21.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-js-legacy/kotlin" />
<exclude-output />
<content url="file://$MODULE_DIR$/build/externals/komp-js-ir/src" />
<content url="file://$MODULE_DIR$/build/externals/komp-js-legacy/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.3" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.5.21" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.5.21" level="project" />
</component>
</module>

86
komp.jsTest.iml Normal file
View File

@@ -0,0 +1,86 @@
<?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.5.6-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-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/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-js-legacy-test/kotlin/komp-js-legacy-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.5" />
<option name="apiVersion" value="1.5" />
<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.5.21/96d49e89873fde985750af354b6eabb60cfa999b/kotlin-script-runtime-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.5.21/5d271f9e7e7191902f8c0d45c600f5b57a284036/kotlin-scripting-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.5.21/8143908a15163e634fecb87013ed4a139170b032/kotlin-scripting-jvm-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.21/cc8bf3586fd2ebcf234058b9440bb406e62dfacb/kotlin-stdlib-common-1.5.21.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.5.21/2f537cad7e9eeb9da73738c8812e1e4cf9b62e4e/kotlin-stdlib-1.5.21.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-js-legacy-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="module-library" scope="PROVIDED">
<library name="Gradle: nl.astraeus:komp">
<CLASSES>
<root url="file://$MODULE_DIR$/build/classes/kotlin/jsIr/main" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library" scope="RUNTIME">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/build/processedResources/jsIr/main" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-js:0.7.3" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-test-js:1.5.21" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlinx:kotlinx-html-common:0.7.3" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-js:1.5.21" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.5.21" level="project" />
</component>
<component name="TestModuleProperties" production-module="komp.jsMain" />
</module>

View File

@@ -1,11 +1,7 @@
# Komponent
![Gradle CI](https://github.com/rnentjes/komponent/workflows/Gradle%20CI/badge.svg)
Very simple helper to build single page apps with the Kotlin javascript target using static html builders.
See the komp-todo repository for a basic example here: [komp-todo](https://github.com/rnentjes/komp-todo)
For a more complete example take a look at the simple-password-manager repository: [simple-password-manager](https://github.com/rnentjes/simple-password-manager)
Available on maven central: "nl.astraeus:kotlin-komponent-js:1.0.0"

View File

@@ -1,2 +1,14 @@
pluginManagement {
repositories {
rootProject.name = "kotlin-komponent"
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
mavenCentral()
maven { setUrl("https://plugins.gradle.org/m2/") }
}
}
rootProject.name = "komp"

View File

@@ -1 +0,0 @@
package nl.astraeus.komp

View File

@@ -1,157 +0,0 @@
package nl.astraeus.komp
import org.w3c.dom.Element
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import org.w3c.dom.get
private fun Int.asSpaces(): String {
val result = StringBuilder()
repeat(this) {
result.append(" ")
}
return result.toString()
}
fun Element.printTree(indent: Int = 0): String {
val result = StringBuilder()
result.append(indent.asSpaces())
result.append(tagName)
if (this.namespaceURI != "http://www.w3.org/1999/xhtml") {
result.append(" [")
result.append(namespaceURI)
result.append("]")
}
result.append(" (")
var first = true
if (hasAttributes()) {
for (index in 0 until attributes.length) {
if (!first) {
result.append(", ")
} else {
first = false
}
result.append(attributes[index]?.localName)
result.append("=")
result.append(attributes[index]?.value)
}
}
result.append(") {")
result.append("\n")
for ((name, event) in getKompEvents()) {
result.append(indent.asSpaces())
result.append("on")
result.append(name)
result.append(" -> ")
result.append(event)
result.append("\n")
}
for (index in 0 until childNodes.length) {
childNodes[index]?.let {
if (it is Element) {
result.append(it.printTree(indent + 2))
} else {
result.append((indent + 2).asSpaces())
result.append(it.textContent)
result.append("\n")
}
}
}
result.append(indent.asSpaces())
result.append("}\n")
return result.toString()
}
internal fun Element.setKompAttribute(attributeName: String, value: String?) {
//val attributeName = name.lowercase()
if (value == null || value.isBlank()) {
if (this is HTMLInputElement) {
when (attributeName) {
"checked" -> {
checked = false
}
/*
"class" -> {
className = ""
}
*/
"value" -> {
this.value = ""
}
else -> {
removeAttribute(attributeName)
}
}
} else {
removeAttribute(attributeName)
}
} else {
if (this is HTMLInputElement) {
when (attributeName) {
"checked" -> {
checked = "checked" == value
}
/*
"class" -> {
className = value
}
*/
"value" -> {
this.value = value
}
else -> {
setAttribute(attributeName, value)
}
}
} else if (this.getAttribute(attributeName) != value) {
setAttribute(attributeName, value)
}
}
}
internal fun Element.clearKompEvents() {
val events = getKompEvents()
for ((name, event) in getKompEvents()) {
removeEventListener(name, event)
}
events.clear()
}
internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
val eventName: String = if (name.startsWith("on")) {
name.substring(2)
} else {
name
}
getKompEvents()[eventName] = event
this.addEventListener(eventName, event)
}
internal fun Element.getKompEvents(): MutableMap<String, (Event) -> Unit> {
var result: MutableMap<String, (Event) -> Unit>? = this.asDynamic()["komp-events"] as MutableMap<String, (Event) -> Unit>?
if (result == null) {
result = mutableMapOf()
this.asDynamic()["komp-events"] = result
}
return result
}
internal fun Element.findElementIndex(): Int {
val childNodes = parentElement?.children
if (childNodes != null) {
for (index in 0 until childNodes.length) {
if (childNodes[index] == this) {
return index
}
}
}
return 0
}

View File

@@ -24,13 +24,158 @@ interface HtmlConsumer : TagConsumer<Element> {
fun debug(block: HtmlConsumer.() -> Unit)
}
fun Int.asSpaces(): String {
val result = StringBuilder()
repeat(this) {
result.append(" ")
}
return result.toString()
}
fun FlowOrMetaDataOrPhrasingContent.currentElement(): Element =
currentElement ?: error("No current element defined!")
fun Element.printTree(indent: Int = 0): String {
val result = StringBuilder()
result.append(indent.asSpaces())
result.append(tagName)
if (this.namespaceURI != "http://www.w3.org/1999/xhtml") {
result.append(" [")
result.append(namespaceURI)
result.append("]")
}
result.append(" (")
var first = true
if (hasAttributes()) {
for (index in 0 until attributes.length) {
if (!first) {
result.append(", ")
} else {
first = false
}
result.append(attributes[index]?.localName)
result.append("=")
result.append(attributes[index]?.value)
}
}
result.append(") {")
result.append("\n")
for ((name, event) in getKompEvents()) {
result.append(indent.asSpaces())
result.append("on")
result.append(name)
result.append(" -> ")
result.append(event)
result.append("\n")
}
for (index in 0 until childNodes.length) {
childNodes[index]?.let {
if (it is Element) {
result.append(it.printTree(indent + 2))
} else {
result.append((indent + 2).asSpaces())
result.append(it.textContent)
result.append("\n")
}
}
}
result.append(indent.asSpaces())
result.append("}\n")
return result.toString()
}
private fun Element.clearKompAttributes() {
val attributes = this.asDynamic()["komp-attributes"] as MutableSet<String>?
if (attributes == null) {
this.asDynamic()["komp-attributes"] = mutableSetOf<String>()
} else {
attributes.clear()
}
if (this is HTMLInputElement) {
this.checked = false
}
}
private fun Element.getKompAttributes(): MutableSet<String> {
var result: MutableSet<String>? = this.asDynamic()["komp-attributes"] as MutableSet<String>?
if (result == null) {
result = mutableSetOf()
this.asDynamic()["komp-attributes"] = result
}
return result
}
private fun Element.setKompAttribute(name: String, value: String) {
val setAttrs: MutableSet<String> = getKompAttributes()
setAttrs.add(name)
if (this is HTMLInputElement) {
when (name) {
"checked" -> {
this.checked = value == "checked"
}
"value" -> {
this.value = value
}
else -> {
setAttribute(name, value)
}
}
} else if (this.getAttribute(name) != value) {
setAttribute(name, value)
}
}
private fun Element.clearKompEvents() {
for ((name, event) in getKompEvents()) {
currentElement?.removeEventListener(name, event)
}
val events = this.asDynamic()["komp-events"] as MutableMap<String, (Event) -> Unit>?
if (events == null) {
this.asDynamic()["komp-events"] = mutableMapOf<String, (Event) -> Unit>()
} else {
events.clear()
}
}
private fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
val eventName: String = if (name.startsWith("on")) {
name.substring(2)
} else {
name
}
val events: MutableMap<String, (Event) -> Unit> = getKompEvents()
events[eventName]?.let {
println("Warn event already defined!")
currentElement?.removeEventListener(eventName, it)
}
events[eventName] = event
this.asDynamic()["komp-events"] = events
this.addEventListener(eventName, event)
}
private fun Element.getKompEvents(): MutableMap<String, (Event) -> Unit> {
return this.asDynamic()["komp-events"] ?: mutableMapOf()
}
private data class ElementIndex(
val parent: Node,
var childIndex: Int,
var setAttr: MutableSet<String> = mutableSetOf()
var childIndex: Int
)
private fun ArrayList<ElementIndex>.currentParent(): Node {
@@ -49,17 +194,8 @@ private fun ArrayList<ElementIndex>.currentElement(): Node? {
return null
}
private fun ArrayList<ElementIndex>.currentPosition(): ElementIndex? {
return if (this.size < 2) {
null
} else {
this[this.size - 2]
}
}
private fun ArrayList<ElementIndex>.nextElement() {
this.lastOrNull()?.let {
it.setAttr.clear()
it.childIndex++
}
}
@@ -74,10 +210,7 @@ private fun ArrayList<ElementIndex>.push(element: Node) {
private fun ArrayList<ElementIndex>.replace(new: Node) {
if (this.currentElement() != null) {
this.currentElement()?.parentElement?.replaceChild(
new,
this.currentElement()!!
)
this.currentElement()?.parentElement?.replaceChild(new, this.currentElement()!!)
} else {
this.last().parent.appendChild(new)
}
@@ -86,15 +219,14 @@ private fun ArrayList<ElementIndex>.replace(new: Node) {
private fun Node.asElement() = this as? HTMLElement
class HtmlBuilder(
private val komponent: Komponent?,
parent: Element,
childIndex: Int = 0
val parent: Element,
var childIndex: Int = 0
) : HtmlConsumer {
private var currentPosition = arrayListOf<ElementIndex>()
private var inDebug = false
private var exceptionThrown = false
private var currentNode: Node? = null
var currentNode: Node? = null
var root: Element? = null
val currentAttributes: MutableMap<String, String> = mutableMapOf()
init {
currentPosition.add(ElementIndex(parent, childIndex))
@@ -107,9 +239,7 @@ class HtmlBuilder(
) {
currentPosition.replace(komponent.element!!)
if (Komponent.logRenderEvent) {
console.log(
"Skipped include $komponent, memoize hasn't changed"
)
console.log("Skipped include $komponent, memoize hasn't changed")
}
} else {
komponent.create(
@@ -126,33 +256,27 @@ class HtmlBuilder(
}
override fun debug(block: HtmlConsumer.() -> Unit) {
val enableAssertions = Komponent.enableAssertions
Komponent.enableAssertions = true
inDebug = true
try {
block.invoke(this)
} finally {
inDebug = false
Komponent.enableAssertions = enableAssertions
}
}
private fun logReplace(msg: () -> String) {
fun logReplace(msg: String) {
if (Komponent.logReplaceEvent && inDebug) {
console.log(msg.invoke())
console.log(msg)
}
}
override fun onTagStart(tag: Tag) {
logReplace {
"onTagStart, [${tag.tagName}, ${tag.namespace}], currentPosition: $currentPosition"
}
//logReplace"onTagStart, [${tag.tagName}, ${tag.namespace}], currentPosition: $currentPosition")
currentNode = currentPosition.currentElement()
if (currentNode == null) {
logReplace { "onTagStart, currentNode1: $currentNode" }
//logReplace"onTagStart, currentNode1: $currentNode")
currentNode = if (tag.namespace != null) {
document.createElementNS(tag.namespace, tag.tagName)
} else {
@@ -168,13 +292,8 @@ class HtmlBuilder(
!currentNode?.asElement()?.namespaceURI.equals(tag.namespace, true)
)
) {
logReplace {
"onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}"
}
logReplace {
"onTagStart, currentElement, replace: ${currentNode?.asElement()?.tagName} -> ${tag.tagName}"
}
//logReplace"onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}")
//logReplace"onTagStart, currentElement, replace: ${currentNode?.asElement()?.tagName} -> ${tag.tagName}")
currentNode = if (tag.namespace != null) {
document.createElementNS(tag.namespace, tag.tagName)
} else {
@@ -182,6 +301,9 @@ class HtmlBuilder(
}
currentPosition.replace(currentNode!!)
} else {
//logReplace"onTagStart, same node type")
}
currentElement = currentNode as? Element ?: currentElement
@@ -192,18 +314,22 @@ class HtmlBuilder(
root = currentNode as Element
}
currentElement?.clearKompAttributes()
currentElement?.clearKompEvents()
(currentElement as? HTMLInputElement)?.checked = false
currentPosition.lastOrNull()?.setAttr?.clear()
// if currentElement = checkbox make sure it's cleared
for (entry in tag.attributesEntries) {
currentElement!!.setKompAttribute(entry.key, entry.value)
currentPosition.lastOrNull()?.setAttr?.add(entry.key)
currentElement!!.setKompAttribute(entry.key.lowercase(), entry.value)
}
if (tag.namespace != null) {
//logReplace"onTagStart, same node type")
(currentNode as? Element)?.innerHTML = ""
}
}
//logReplace"onTagStart, currentElement2: $currentNode")
currentPosition.push(currentNode!!)
}
@@ -218,81 +344,68 @@ class HtmlBuilder(
}
}
override fun onTagAttributeChange(
tag: Tag,
attribute: String,
value: String?
) {
logReplace { "onTagAttributeChange, ${tag.tagName} [$attribute, $value]" }
override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) {
logReplace("onTagAttributeChange, ${tag.tagName} [$attribute, $value]")
if (Komponent.enableAssertions) {
checkTag(tag)
}
checkTag(tag)
currentElement?.setKompAttribute(attribute, value)
if (value == null || value.isEmpty()) {
currentPosition.currentPosition()?.setAttr?.remove(attribute)
if (value == null) {
currentElement?.removeAttribute(attribute.lowercase())
} else {
currentPosition.currentPosition()?.setAttr?.add(attribute)
currentElement?.setKompAttribute(attribute.lowercase(), value)
}
}
override fun onTagEvent(
tag: Tag,
event: String,
value: (Event) -> Unit
) {
logReplace { "onTagEvent, ${tag.tagName} [$event, $value]" }
override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) {
//logReplace"onTagEvent, ${tag.tagName} [$event, $value]")
if (Komponent.enableAssertions) {
checkTag(tag)
}
checkTag(tag)
currentElement?.setKompEvent(event.toLowerCase(), value)
currentElement?.setKompEvent(event.lowercase(), value)
}
override fun onTagEnd(tag: Tag) {
if (exceptionThrown) {
return
}
while (currentPosition.currentElement() != null) {
currentPosition.currentElement()?.let {
it.parentElement?.removeChild(it)
}
}
if (Komponent.enableAssertions) {
checkTag(tag)
}
checkTag(tag)
if (currentElement != null) {
val setAttrs: Set<String> = currentPosition.currentPosition()?.setAttr ?: setOf()
currentPosition.pop()
// remove attributes that where not set
val element = currentElement
if (element?.hasAttributes() == true) {
for (index in 0 until element.attributes.length) {
val attribute = element.attributes[index]
if (attribute?.name != null) {
val attr = attribute.name
val setAttrs: List<String> = currentElement.asDynamic()["komp-attributes"] ?: listOf()
if (
!setAttrs.contains(attr) &&
attr != "style"
) {
element.setKompAttribute(attr, null)
// remove attributes that where not set
val element = currentElement
if (element?.hasAttributes() == true) {
for (index in 0 until element.attributes.length) {
val attr = element.attributes[index]
if (attr != null) {
if (element is HTMLElement && attr.name == "data-has-focus" && "true" == attr.value) {
element.focus()
}
if (!setAttrs.contains(attr.name)) {
if (element is HTMLInputElement) {
if (attr.name == "checkbox") {
element.checked = false
} else if (attr.name == "value") {
element.value = ""
}
} else {
if (Komponent.logReplaceEvent) {
console.log("Clear attribute [${attr.name}] on $element)")
}
element.removeAttribute(attr.name)
}
}
}
}
}
currentPosition.pop()
currentNode = currentPosition.currentElement()
currentElement = currentNode as? Element ?: currentElement
currentPosition.nextElement()
currentElement = currentElement?.parentElement as? HTMLElement
@@ -366,10 +479,7 @@ class HtmlBuilder(
//logReplace"onTagContentUnsafe, namespace: [$namespace]")
if (Komponent.unsafeMode == UnsafeMode.UNSAFE_ALLOWED ||
(
Komponent.unsafeMode == UnsafeMode.UNSAFE_SVG_ONLY &&
namespace == "http://www.w3.org/2000/svg"
)
(Komponent.unsafeMode == UnsafeMode.UNSAFE_SVG_ONLY && namespace == "http://www.w3.org/2000/svg")
) {
if (currentElement?.innerHTML != textContent) {
currentElement?.innerHTML += textContent
@@ -395,57 +505,17 @@ class HtmlBuilder(
currentPosition.nextElement()
}
override fun onTagError(tag: Tag, exception: Throwable) {
exceptionThrown = true
if (exception !is KomponentException) {
val position = mutableListOf<Element>()
var ce = currentElement
while (ce != null) {
position.add(ce)
ce = ce.parentElement
}
val builder = StringBuilder()
for (element in position.reversed()) {
builder.append("> ")
builder.append(element.tagName)
builder.append("[")
builder.append(element.findElementIndex())
builder.append("]")
if (element.hasAttribute("class")) {
builder.append("(")
builder.append(element.getAttribute("class"))
builder.append(")")
}
builder.append(" ")
}
throw KomponentException(
komponent,
currentElement,
tag,
builder.toString(),
exception.message ?: "error",
exception
)
} else {
throw exception
}
}
override fun finalize(): Element {
//logReplace"finalize, currentPosition: $currentPosition")
return root ?: throw IllegalStateException(
"We can't finalize as there was no tags"
)
return root ?: throw IllegalStateException("We can't finalize as there was no tags")
}
companion object {
fun create(content: HtmlBuilder.() -> Unit): Element {
val container = document.createElement("div") as HTMLElement
val consumer = HtmlBuilder(null, container)
val consumer = HtmlBuilder(container, 0)
content.invoke(consumer)
return consumer.root ?: error("No root element found after render!")
return consumer.root ?: error("error")
}
}
}

View File

@@ -1,15 +1,32 @@
package nl.astraeus.komp
import kotlinx.browser.window
import kotlinx.html.FlowOrMetaDataOrPhrasingContent
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import org.w3c.dom.css.CSSStyleDeclaration
import org.w3c.dom.get
import kotlin.reflect.KProperty
private var currentKomponent: Komponent? = null
class StateDelegate<T>(
val komponent: Komponent,
initialValue: T
) {
var value: T = initialValue
fun FlowOrMetaDataOrPhrasingContent.currentKomponent(): Komponent =
currentKomponent ?: error("No current komponent defined! Only call from render code!")
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)
enum class UnsafeMode {
UNSAFE_ALLOWED,
@@ -17,61 +34,28 @@ enum class UnsafeMode {
UNSAFE_SVG_ONLY
}
var Element.memoizeHash: String?
get() {
return getAttribute("memoize-hash")
}
set(value) {
if (value != null) {
setAttribute("memoize-hash", value.toString())
} else {
removeAttribute("memoize-hash")
}
}
abstract class Komponent {
val createIndex = getNextCreateIndex()
private var dirty: Boolean = true
private var lastMemoizeHash: Int? = null
var element: Element? = null
var element: Node? = null
val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap()
open fun create(parent: Element, childIndex: Int? = null) {
onBeforeUpdate()
val builder = HtmlBuilder(
this,
parent,
childIndex ?: parent.childNodes.length
childIndex ?: parent.childElementCount
)
try {
currentKomponent = this
builder.render()
} catch(e: KomponentException) {
errorHandler(e)
} finally {
currentKomponent = null
}
builder.render()
element = builder.root
updateMemoizeHash()
lastMemoizeHash = generateMemoizeHash()
onAfterUpdate()
}
fun memoizeChanged() = element?.memoizeHash == null || element?.memoizeHash != fullMemoizeHash()
fun updateMemoizeHash() {
element?.memoizeHash = fullMemoizeHash()
}
private fun fullMemoizeHash(): String? {
val generated = generateMemoizeHash()
return if (generated != null) {
"${this::class.simpleName}:${generateMemoizeHash()}"
} else {
null
}
}
fun memoizeChanged() = lastMemoizeHash != null && lastMemoizeHash != generateMemoizeHash()
abstract fun HtmlBuilder.render()
@@ -123,7 +107,7 @@ abstract class Komponent {
*/
open fun generateMemoizeHash(): Int? = null
private fun refresh() {
internal fun refresh() {
val currentElement = element
check(currentElement != null) {
@@ -132,23 +116,15 @@ abstract class Komponent {
val parent = currentElement.parentElement as? HTMLElement ?: error("parent is null!?")
var childIndex = 0
for (index in 0 until parent.childNodes.length) {
if (parent.childNodes[index] == currentElement) {
for (index in 0 until parent.children.length) {
if (parent.children[index] == currentElement) {
childIndex = index
}
}
val builder = HtmlBuilder(this, parent, childIndex)
try {
currentKomponent = this
builder.render()
} catch(e: KomponentException) {
errorHandler(e)
} finally {
currentKomponent = null
}
element = builder.root
val consumer = HtmlBuilder(parent, childIndex)
consumer.root = null
consumer.render()
element = consumer.root
dirty = false
}
@@ -159,34 +135,17 @@ abstract class Komponent {
companion object {
private var nextCreateIndex: Int = 1
private var updateCallback: Int? = null
private var errorHandler: (KomponentException) -> Unit = { ke ->
console.error("Render error in Komponent", ke)
ke.element?.innerHTML = """<div class="komponent-error">Render error!</div>"""
window.alert("""
Error in Komponent '${ke.komponent}', ${ke.message}
Tag: ${ke.tag.tagName}
See console log for details
Position: ${ke.position}""".trimIndent()
)
}
private var scheduledForUpdate = mutableSetOf<Komponent>()
private var interceptor: (Komponent, () -> Unit) -> Unit = { _, block -> block() }
var logRenderEvent = false
var logReplaceEvent = false
var enableAssertions = false
var unsafeMode = UnsafeMode.UNSAFE_DISABLED
fun create(parent: HTMLElement, component: Komponent) {
fun create(parent: HTMLElement, component: Komponent, insertAsFirst: Boolean = false) {
component.create(parent)
}
fun setErrorHandler(handler: (KomponentException) -> Unit) {
errorHandler = handler
}
fun setUpdateInterceptor(block: (Komponent, () -> Unit) -> Unit) {
interceptor = block
}
@@ -226,10 +185,10 @@ abstract class Komponent {
}
val memoizeHash = next.generateMemoizeHash()
if (next.memoizeChanged()) {
if (memoizeHash == null || next.lastMemoizeHash != memoizeHash) {
next.onBeforeUpdate()
next.update()
next.updateMemoizeHash()
next.lastMemoizeHash = memoizeHash
next.onAfterUpdate()
} else if (logRenderEvent) {
console.log("Skipped render, memoizeHash is equal $next-[$memoizeHash]")

View File

@@ -1,18 +0,0 @@
package nl.astraeus.komp
import kotlinx.html.Tag
import org.w3c.dom.Element
class KomponentException(
val komponent: Komponent?,
val element: Element?,
val tag: Tag,
val position: String,
message: String,
cause: Throwable
) : RuntimeException(message, cause) {
override fun toString(): String {
return "KompException(message='$message', tag='$tag', position='$position')"
}
}

View File

@@ -1,54 +0,0 @@
package nl.astraeus.komp
inline fun <reified T> Komponent.mutableCollectionState(
initialValue: MutableCollection<T>
): MutableCollection<T> = MutableCollectionStateDelegate(
this,
initialValue
)
class MutableCollectionStateDelegate<T>(
val komponent: Komponent,
val collection: MutableCollection<T>
): MutableCollection<T> by collection {
override fun add(element: T): Boolean {
komponent.requestUpdate()
return collection.add(element)
}
override fun addAll(elements: Collection<T>): Boolean {
komponent.requestUpdate()
return collection.addAll(elements)
}
override fun clear() {
komponent.requestUpdate()
collection.clear()
}
// todo: return iterator wrapper to update at changes?
// override fun iterator(): MutableIterator<T> = collection.iterator()
override fun remove(element: T): Boolean {
komponent.requestUpdate()
return collection.remove(element)
}
override fun removeAll(elements: Collection<T>): Boolean {
komponent.requestUpdate()
return collection.removeAll(elements)
}
override fun retainAll(elements: Collection<T>): Boolean {
komponent.requestUpdate()
return collection.retainAll(elements)
}
}

View File

@@ -1,56 +0,0 @@
package nl.astraeus.komp
import kotlin.reflect.KProperty
interface Delegate<T> {
operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): T
operator fun setValue(
thisRef: Any?,
property: KProperty<*>,
value: T
)
}
open class StateDelegate<T>(
val komponent: Komponent,
initialValue: T
) : Delegate<T> {
private var value: T = initialValue
init {
if (value is MutableCollection<*>) {
error("Use mutableList to create a collection!")
}
}
override operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): T {
return value
}
override 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
): Delegate<T> = StateDelegate(
this,
initialValue
)

View File

@@ -1,12 +1,9 @@
package nl.astraeus.komp
import kotlinx.browser.document
import kotlinx.html.InputType
import kotlinx.html.classes
import kotlinx.html.div
import kotlinx.html.i
import kotlinx.html.id
import kotlinx.html.input
import kotlinx.html.js.onClickFunction
import kotlinx.html.p
import kotlinx.html.span
@@ -38,7 +35,6 @@ class Child1 : Komponent() {
class Child2 : Komponent() {
override fun HtmlBuilder.render() {
div {
id ="1234"
+"Child 2"
}
}
@@ -54,10 +50,6 @@ class SimpleKomponent : Komponent() {
override fun HtmlBuilder.render() {
div("div_class") {
input(InputType.checkBox) {
name = "helloInput"
checked = hello
}
span {
svg {
unsafe {
@@ -211,8 +203,7 @@ class TestUpdate {
fun testCreate() {
var elemTest: Element? = null
val element = HtmlBuilder.create {
div(classes = "div_class") {
classes = classes + "bla'"
div("div_class") {
id = "123"
+"Test"