From cdad1334a572b7764fa3f9e59ee17aabc8d86d6a Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Fri, 24 Jun 2022 02:01:50 -0400 Subject: [PATCH 1/7] Bump Kweb version number --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bf3a4f5fb2..a264055242 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ ext { } group 'com.github.kwebio' -version '0.11.3' +version '0.11.4' repositories { mavenCentral() From 9379a5819daaa491aeb935c19cbd960cedacc8d9 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 28 Jun 2022 11:25:47 -0400 Subject: [PATCH 2/7] Add initial value to InputElement --- src/main/kotlin/kweb/prelude.kt | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kweb/prelude.kt b/src/main/kotlin/kweb/prelude.kt index 6bcc878e19..4f37f806f9 100644 --- a/src/main/kotlin/kweb/prelude.kt +++ b/src/main/kotlin/kweb/prelude.kt @@ -325,7 +325,7 @@ fun ElementCreator.meta( } } -open class InputElement(override val element: Element) : ValueElement(element) { +open class InputElement(override val element: Element, initialValue: String? = null) : ValueElement(element, initialValue = initialValue) { fun select() = element.callJsFunction("document.getElementById({}).select();", id.json) fun setSelectionRange(start: Int, end: Int) = element.callJsFunction( @@ -365,7 +365,7 @@ fun ElementCreator.input( .set("value", JsonPrimitive(initialValue)) .set("placeholder", JsonPrimitive(placeholder)) .set("size", JsonPrimitive(size)) - ) + ), initialValue = initialValue ).also { if (new != null) new(ElementCreator(parent = it, insertBefore = null)) } @@ -476,7 +476,8 @@ fun ElementCreator.label( * * @param kvarUpdateEvent The [value] of this element will update on this event, defaults to [input](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) */ -abstract class ValueElement(open val element: Element, val kvarUpdateEvent: String = "input") : Element(element) { +abstract class ValueElement(open val element: Element, val kvarUpdateEvent: String = "input", + val initialValue: String? = null) : Element(element) { val valueJsExpression : String by lazy { "document.getElementById(\"$id\").value" } suspend fun getValue():String = element. @@ -514,7 +515,7 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri get() { synchronized(this) { if (_valueKvar == null) { - value = KVar("") + value = KVar(initialValue ?: "") } } return _valueKvar!! @@ -534,7 +535,8 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri data class DiffData(val prefixEndIndex: Int, val postfixOffset: Int, val diffString: String) private fun applyDiff(oldString: String, diffData: DiffData) : String { - return when { + + val newString = when { diffData.postfixOffset == -1 -> {//these 2 edge cases prevent the prefix or the postfix from being // repeated when you append text to the beginning of the text or the end of the text oldString.substring(0, diffData.prefixEndIndex) + diffData.diffString @@ -547,6 +549,8 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri oldString.substring(oldString.length - diffData.postfixOffset) } } + println("applied String = ${newString}") + return newString } fun setValue(toBind: KVar, updateOn: String = "input") { @@ -562,6 +566,12 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri val diffDataJson = it.retrieved val diffData = Json.decodeFromJsonElement(DiffData.serializer(), diffDataJson) toBind.value = applyDiff(toBind.value, diffData) + /*println("toBind.value = ${toBind.value}") + println("diffData = ${diffData}") + println("diffDataJson = ${diffDataJson}") + toBind.value = applyDiff(toBind.value, diffData) + println("toBind new value: ${toBind.value}") + println("---------------------------------")*/ } } } From 52589c09efee544da8e4490bc753922952faa11e Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 28 Jun 2022 16:30:07 -0400 Subject: [PATCH 3/7] Add initial value to SelectElement and TextAreaElement --- src/main/kotlin/kweb/prelude.kt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/kweb/prelude.kt b/src/main/kotlin/kweb/prelude.kt index 4f37f806f9..b51f960dad 100644 --- a/src/main/kotlin/kweb/prelude.kt +++ b/src/main/kotlin/kweb/prelude.kt @@ -325,7 +325,8 @@ fun ElementCreator.meta( } } -open class InputElement(override val element: Element, initialValue: String? = null) : ValueElement(element, initialValue = initialValue) { +open class InputElement(override val element: Element, initialValue: String? = null) : + ValueElement(element, initialValue = initialValue) { fun select() = element.callJsFunction("document.getElementById({}).select();", id.json) fun setSelectionRange(start: Int, end: Int) = element.callJsFunction( @@ -336,7 +337,8 @@ open class InputElement(override val element: Element, initialValue: String? = n id.json, ro.json) fun checked(initialValue : Boolean = false) : KVar { - val kv = bind(accessor = { "document.getElementById(\"$it\").checked" }, updateOnEvent = "change", initialValue = JsonPrimitive(initialValue)) + val kv = bind(accessor = { "document.getElementById(\"$it\").checked" }, updateOnEvent = "change", + initialValue = JsonPrimitive(initialValue)) return kv.map(object : ReversibleFunction("") { override fun invoke(from: JsonElement) = from.jsonPrimitive.boolean @@ -390,7 +392,8 @@ fun ElementCreator.textArea( /** * [](https://www.w3schools.com/tags/tag_select.asp) @@ -430,7 +433,7 @@ private fun select_sample() { /** * https://www.w3schools.com/tags/tag_textarea.asp */ -open class TextAreaElement(parent: Element) : ValueElement(parent) { +open class TextAreaElement(parent: Element, initialValue: String? = null) : ValueElement(parent, initialValue = initialValue) { //TODO ValueElement already provides a way to get the value of an element. I'm not sure why this function is here. //But, something needs to be done with it. override val read get() = TextAreaElementReader(this) @@ -554,7 +557,7 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri } fun setValue(toBind: KVar, updateOn: String = "input") { - setValue(toBind as KVal) + //setValue(toBind as KVal) on( //language=JavaScript @@ -566,12 +569,6 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri val diffDataJson = it.retrieved val diffData = Json.decodeFromJsonElement(DiffData.serializer(), diffDataJson) toBind.value = applyDiff(toBind.value, diffData) - /*println("toBind.value = ${toBind.value}") - println("diffData = ${diffData}") - println("diffDataJson = ${diffDataJson}") - toBind.value = applyDiff(toBind.value, diffData) - println("toBind new value: ${toBind.value}") - println("---------------------------------")*/ } } } From a6ddd4e0ec08f0b376da3f80eab7c4af7c2ff281 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 28 Jun 2022 16:31:08 -0400 Subject: [PATCH 4/7] Add test for diffs on input elements --- src/test/kotlin/kweb/StringDiffTest.kt | 62 +++++++++----------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/src/test/kotlin/kweb/StringDiffTest.kt b/src/test/kotlin/kweb/StringDiffTest.kt index 1d1152fdd6..7656b462d1 100644 --- a/src/test/kotlin/kweb/StringDiffTest.kt +++ b/src/test/kotlin/kweb/StringDiffTest.kt @@ -1,5 +1,4 @@ package kweb -/* import io.github.bonigarcia.seljup.Arguments import io.github.bonigarcia.seljup.SeleniumExtension import io.kotlintest.shouldBe @@ -20,7 +19,6 @@ import org.openqa.selenium.support.ThreadGuard @ExtendWith(SeleniumExtension::class) class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) { -//class StringDiffTest(private var driver: WebDriver) { init { //ThreadGuard.protect ensures that the webdriver can only be called by the thread that created it @@ -48,46 +46,32 @@ class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) { fun appendTextToBeginning() { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) - inputField.sendKeys(Keys.HOME) - inputField.sendKeys("Super ") - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Super Lazy Brown Fox") + inputField.sendKeys("${Keys.HOME}Super ") + inputField.getAttribute("value").shouldBe("Super Lazy Brown Fox") } @Test fun appendTextToMiddle() { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) - inputField.sendKeys(Keys.HOME) - for (i in 0 until 5) { - inputField.sendKeys(Keys.ARROW_RIGHT) - Thread.sleep(200); - } - inputField.sendKeys("Reddish ") - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Lazy Reddish Brown Fox") + inputField.sendKeys("${Keys.LEFT}${Keys.LEFT}1234") + inputField.getAttribute("value").shouldBe("Lazy Brown F1234ox") } @Test fun appendTextToEnd() { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) - inputField.sendKeys(Keys.END) inputField.sendKeys(" Jumped") - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Lazy Brown Fox Jumped") + inputField.getAttribute("value").shouldBe("Lazy Brown Fox Jumped") } @Test fun removeTextFromBeginning() { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) - inputField.sendKeys(Keys.HOME) - for (i in 0 until 5) { - inputField.sendKeys(Keys.DELETE) - } - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Brown Fox") + inputField.sendKeys("${Keys.HOME}${Keys.DELETE}${Keys.DELETE}${Keys.DELETE}${Keys.DELETE}${Keys.DELETE}") + inputField.getAttribute("value").shouldBe("Brown Fox") } @Test @@ -95,14 +79,14 @@ class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) inputField.sendKeys(Keys.HOME) - for (i in 0 until 5) { - inputField.sendKeys(Keys.ARROW_RIGHT) + for (i in 0 until 4) { + inputField.sendKeys(Keys.ARROW_LEFT) } for (i in 0 until 6) { - inputField.sendKeys(Keys.DELETE) + inputField.sendKeys(Keys.BACK_SPACE) } - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Lazy Fox") + + inputField.getAttribute("value").shouldBe("Lazy Fox") } @Test @@ -114,26 +98,20 @@ class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) { inputField.sendKeys(Keys.BACK_SPACE) } inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Lazy Brown") + inputField.getAttribute("value").shouldBe("Lazy Brown") } } class StringDiffTestApp { - private lateinit var input: InputElement - - val server: Kweb = Kweb(port= 7660) { - doc.body.new { - val inputKvar = KVar(false) - render(inputKvar) { - input = input(type = InputType.text, initialValue = "Lazy Brown Fox") - } - } - } + var inputString = KVar("") - suspend fun getValue(): String { - return input.getValue() + val server: Kweb = Kweb(port= 7660){ + doc.body { + val input = input(initialValue = "Lazy Brown Fox") + inputString = input.value + } } } -*/ + From 3cbe203d7549de6360877647db03557ef5738bac Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 28 Jun 2022 16:31:36 -0400 Subject: [PATCH 5/7] Add demo page for manually testing diffs on input elements --- .../kweb/demos/testPages/DiffTestPage.kt | 57 +++++++++++++++++++ src/main/resources/kweb/kweb_bootstrap.js | 4 +- 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/kweb/demos/testPages/DiffTestPage.kt diff --git a/src/main/kotlin/kweb/demos/testPages/DiffTestPage.kt b/src/main/kotlin/kweb/demos/testPages/DiffTestPage.kt new file mode 100644 index 0000000000..a11583f08f --- /dev/null +++ b/src/main/kotlin/kweb/demos/testPages/DiffTestPage.kt @@ -0,0 +1,57 @@ +package kweb.demos.testPages + +import kweb.* +import kweb.plugins.fomanticUI.fomantic +import kweb.plugins.fomanticUI.fomanticUIPlugin +import kweb.state.KVar +import kweb.state.render +import mu.KotlinLogging + + +fun main() { + DiffTestPage() +} + +class DiffTestPage { + + private val logger = KotlinLogging.logger {} + + val plugins = listOf(fomanticUIPlugin) + val server: Kweb + + var username = KVar("Initial") + init { + + /** Create a Kweb instance, and configure it to use the Fomantic + * UI framework. Build a simple to-do list app listening on + * http://localhost:7659/ + * */ + server = Kweb(port = 7659, debug = true, plugins = plugins, buildPage = { + + doc.head { + // Not required, but recommended by HTML spec + meta(name = "Description", content = "A page to test server updating") + } + + doc.body { + /** Kweb allows you to modularize your code however suits your needs + best. Here I use an extension function defined elsewhere to + draw some util outer page DOM elements */ + /** Kweb allows you to modularize your code however suits your needs + best. Here I use an extension function defined elsewhere to + draw some util outer page DOM elements */ + div(fomantic.ui.text.center.aligned.container) { + val usernameInput = input(initialValue = "Initial") + username = usernameInput.value + println("initialValue: ${usernameInput.value.value}") + usernameInput.setAttribute("class", "fomantic ui input") + + render(username) { + p().text("username: $it") + } + } + + } + }) + } +} \ No newline at end of file diff --git a/src/main/resources/kweb/kweb_bootstrap.js b/src/main/resources/kweb/kweb_bootstrap.js index eaa36ac67a..7cf6b31a31 100644 --- a/src/main/resources/kweb/kweb_bootstrap.js +++ b/src/main/resources/kweb/kweb_bootstrap.js @@ -241,12 +241,12 @@ function get_diff_changes(htmlInputElement) { let shorterStringLength = (oldString.length > newString.length) ? newString.length : oldString.length; for (let i = 0; i < shorterStringLength; i++) { - if (oldString.charAt(i) == newString.charAt(i)) { + if (oldString.charAt(i) === newString.charAt(i)) { commonPrefixEnd = i+1; } else break; } for(let offset = 0; offset < shorterStringLength - commonPrefixEnd; offset++) { - if (oldString.charAt(oldStringLastIndex - offset) == newString.charAt(newStringLastIndex - offset)) { + if (oldString.charAt(oldStringLastIndex - offset) === newString.charAt(newStringLastIndex - offset)) { commonPostfixOffset = offset+1; } else break; } From d7576fdf80a0aa7feb553a9514d61842b9b9472b Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 28 Jun 2022 17:13:12 -0400 Subject: [PATCH 6/7] fix a failing test --- src/test/kotlin/kweb/StringDiffTest.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/test/kotlin/kweb/StringDiffTest.kt b/src/test/kotlin/kweb/StringDiffTest.kt index 7656b462d1..e6dfd2c4b0 100644 --- a/src/test/kotlin/kweb/StringDiffTest.kt +++ b/src/test/kotlin/kweb/StringDiffTest.kt @@ -78,15 +78,8 @@ class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) { fun removeTextFromMiddle() { driver.get("http://localhost:7660/") val inputField = driver.findElement(By.tagName("input")) - inputField.sendKeys(Keys.HOME) - for (i in 0 until 4) { - inputField.sendKeys(Keys.ARROW_LEFT) - } - for (i in 0 until 6) { - inputField.sendKeys(Keys.BACK_SPACE) - } - - inputField.getAttribute("value").shouldBe("Lazy Fox") + inputField.sendKeys("${Keys.LEFT}${Keys.BACK_SPACE}${Keys.BACK_SPACE}") + inputField.getAttribute("value").shouldBe("Lazy Brown x") } @Test From 4b38420e6920f671f6b022e0f77b11f65aa1c7e9 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 5 Jul 2022 17:40:09 -0400 Subject: [PATCH 7/7] Refactor setValue() --- src/main/kotlin/kweb/prelude.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kweb/prelude.kt b/src/main/kotlin/kweb/prelude.kt index b51f960dad..59916a5375 100644 --- a/src/main/kotlin/kweb/prelude.kt +++ b/src/main/kotlin/kweb/prelude.kt @@ -525,7 +525,7 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri } set(v) { if (_valueKvar != null) error("`value` may only be set once, and cannot be set after it has been retrieved") - setValue(v, updateOn = kvarUpdateEvent) + updateKVar(v, updateOn = kvarUpdateEvent) _valueKvar = v } @@ -552,13 +552,10 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri oldString.substring(oldString.length - diffData.postfixOffset) } } - println("applied String = ${newString}") return newString } - fun setValue(toBind: KVar, updateOn: String = "input") { - //setValue(toBind as KVal) - + private fun updateKVar(toBind: KVar, updateOn: String = "input") { on( //language=JavaScript retrieveJs = "get_diff_changes(document.getElementById(\"${element.id}\"))")