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() 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/kotlin/kweb/prelude.kt b/src/main/kotlin/kweb/prelude.kt index 6bcc878e19..59916a5375 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) : 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( @@ -336,7 +337,8 @@ open class InputElement(override val element: Element) : ValueElement(element) { 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 @@ -365,7 +367,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)) } @@ -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) @@ -476,7 +479,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,14 +518,14 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri get() { synchronized(this) { if (_valueKvar == null) { - value = KVar("") + value = KVar(initialValue ?: "") } } return _valueKvar!! } 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 } @@ -534,7 +538,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,11 +552,10 @@ abstract class ValueElement(open val element: Element, val kvarUpdateEvent: Stri oldString.substring(oldString.length - diffData.postfixOffset) } } + 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}\"))") 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; } diff --git a/src/test/kotlin/kweb/StringDiffTest.kt b/src/test/kotlin/kweb/StringDiffTest.kt index 1d1152fdd6..e6dfd2c4b0 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,61 +46,40 @@ 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 fun removeTextFromMiddle() { 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 6) { - inputField.sendKeys(Keys.DELETE) - } - inputField.sendKeys(Keys.ENTER) - stringDiffTestApp.getValue().shouldBe("Lazy Fox") + inputField.sendKeys("${Keys.LEFT}${Keys.BACK_SPACE}${Keys.BACK_SPACE}") + inputField.getAttribute("value").shouldBe("Lazy Brown x") } @Test @@ -114,26 +91,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 + } } } -*/ +