Skip to content
This repository has been archived by the owner on Aug 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #278 from Derek52/Bugfix-StringDiff
Browse files Browse the repository at this point in the history
Bugfix string diff
  • Loading branch information
Derek52 authored Jul 6, 2022
2 parents d482697 + 4b38420 commit 7a7e9ba
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 61 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ext {
}

group 'com.github.kwebio'
version '0.11.3'
version '0.11.4'

repositories {
mavenCentral()
Expand Down
57 changes: 57 additions & 0 deletions src/main/kotlin/kweb/demos/testPages/DiffTestPage.kt
Original file line number Diff line number Diff line change
@@ -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")
}
}

}
})
}
}
28 changes: 16 additions & 12 deletions src/main/kotlin/kweb/prelude.kt
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ fun ElementCreator<Element>.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(
Expand All @@ -336,7 +337,8 @@ open class InputElement(override val element: Element) : ValueElement(element) {
id.json, ro.json)

fun checked(initialValue : Boolean = false) : KVar<Boolean> {
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<JsonElement, Boolean>("") {
override fun invoke(from: JsonElement) = from.jsonPrimitive.boolean

Expand Down Expand Up @@ -365,7 +367,7 @@ fun ElementCreator<Element>.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))
}
Expand All @@ -390,7 +392,8 @@ fun ElementCreator<Element>.textArea(
/**
* [<SELECT>](https://www.w3schools.com/tags/tag_select.asp)
*/
class SelectElement(parent: Element) : ValueElement(parent, kvarUpdateEvent = "change")
class SelectElement(parent: Element, initialValue: String? = null) :
ValueElement(parent, kvarUpdateEvent = "change", initialValue = initialValue)

/**
* [<SELECT>](https://www.w3schools.com/tags/tag_select.asp)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -476,7 +479,8 @@ fun ElementCreator<Element>.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.
Expand Down Expand Up @@ -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
}

Expand All @@ -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
Expand All @@ -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<String>, updateOn: String = "input") {
setValue(toBind as KVal<String>)

private fun updateKVar(toBind: KVar<String>, updateOn: String = "input") {
on(
//language=JavaScript
retrieveJs = "get_diff_changes(document.getElementById(\"${element.id}\"))")
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/kweb/kweb_bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
63 changes: 17 additions & 46 deletions src/test/kotlin/kweb/StringDiffTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package kweb
/*
import io.github.bonigarcia.seljup.Arguments
import io.github.bonigarcia.seljup.SeleniumExtension
import io.kotlintest.shouldBe
Expand All @@ -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
Expand Down Expand Up @@ -48,61 +46,40 @@ class StringDiffTest(@Arguments("--headless") private var driver: WebDriver) {
fun appendTextToBeginning() {
driver.get("http://localhost:7660/")
val inputField = driver.findElement<WebElement>(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<WebElement>(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<WebElement>(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<WebElement>(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<WebElement>(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
Expand All @@ -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
}
}
}
*/


0 comments on commit 7a7e9ba

Please sign in to comment.