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

Commit 5f4607a

Browse files
committed
really make addClasses work for SSR
1 parent e675c71 commit 5f4607a

File tree

1 file changed

+105
-53
lines changed

1 file changed

+105
-53
lines changed

src/main/kotlin/kweb/Element.kt

+105-53
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ import kotlin.reflect.KClass
2626

2727
@KWebDSL
2828
open class Element(
29-
override val browser: WebBrowser,
30-
val creator: ElementCreator<*>?,
31-
val tag: String? = null,
32-
@Volatile var id: String
29+
override val browser: WebBrowser,
30+
val creator: ElementCreator<*>?,
31+
val tag: String? = null,
32+
@Volatile var id: String
3333
) :
34-
EventGenerator<Element> {
34+
EventGenerator<Element> {
3535
constructor(element: Element) : this(element.browser, element.creator, tag = element.tag, id = element.id)
3636

3737
/**
@@ -56,7 +56,11 @@ open class Element(
5656
*
5757
* This uses the same template mechanism as [callJsFunction]
5858
*/
59-
suspend fun <O> callJsFunctionWithResult(js: String, outputMapper: (JsonElement) -> O, vararg args: JsonElement): O? {
59+
suspend fun <O> callJsFunctionWithResult(
60+
js: String,
61+
outputMapper: (JsonElement) -> O,
62+
vararg args: JsonElement
63+
): O? {
6064
val result = browser.callJsFunctionWithResult(js, *args)
6165
return outputMapper.invoke(result)
6266
}
@@ -98,7 +102,7 @@ open class Element(
98102
* A utility function to set multiple attributes in a single call, in the
99103
* style of [mapOf]. This is a wrapper around [setAttribute].
100104
*/
101-
fun setAttributes(vararg pair : Pair<String, JsonPrimitive>) : Element {
105+
fun setAttributes(vararg pair: Pair<String, JsonPrimitive>): Element {
102106
pair.forEach { (k, v) -> setAttribute(k, v) }
103107
return this
104108
}
@@ -111,20 +115,22 @@ open class Element(
111115
* with the specified namespace. If null then Kweb will use [Element.createElement](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute).
112116
113117
*/
114-
fun setAttribute(name: String, value: JsonPrimitive, namespace : String? = null): Element {
118+
fun setAttribute(name: String, value: JsonPrimitive, namespace: String? = null): Element {
115119
val jsoupDoc = browser.htmlDocument.get()
116-
val setAttributeJavaScript = when(namespace) {
120+
val setAttributeJavaScript = when (namespace) {
117121
null -> "document.getElementById({}).setAttribute({}, {});"
118122
else -> "document.getElementById({}).setAttributeNS(\"$namespace\", {}, {});"
119123
}
120124
when {
121125
jsoupDoc != null && (browser.isCatchingOutbound() == null || browser.isCatchingOutbound() == RENDER) -> {
122126
jsoupDoc.getElementById(this.id)!!.attr(name, value.content)
123127
}
128+
124129
else -> {
125130
callJsFunction(
126131
setAttributeJavaScript,
127-
id.json, name.json, value)
132+
id.json, name.json, value
133+
)
128134
}
129135
}
130136
if (name.equals("id", ignoreCase = true)) {
@@ -134,29 +140,22 @@ open class Element(
134140
}
135141

136142
@Deprecated("use setAttribute() instead", replaceWith = ReplaceWith(expression = "setAttribute(name, value)"))
137-
fun setAttributeRaw(name : String, value : JsonPrimitive)
138-
= setAttribute(name, value)
143+
fun setAttributeRaw(name: String, value: JsonPrimitive) = setAttribute(name, value)
139144

140145
@Deprecated("use setAttribute() instead", replaceWith = ReplaceWith(expression = "setAttribute(name, value)"))
141-
fun setAttributeRaw(name : String, value : String)
142-
= setAttribute(name, JsonPrimitive(value))
146+
fun setAttributeRaw(name: String, value: String) = setAttribute(name, JsonPrimitive(value))
143147

144148
@Deprecated("use setAttribute() instead", replaceWith = ReplaceWith(expression = "setAttribute(name, value)"))
145-
fun setAttributeRaw(name : String, value : Boolean)
146-
= setAttribute(name, JsonPrimitive(value))
149+
fun setAttributeRaw(name: String, value: Boolean) = setAttribute(name, JsonPrimitive(value))
147150

148151
@Deprecated("use setAttribute() instead", replaceWith = ReplaceWith(expression = "setAttribute(name, value)"))
149-
fun setAttributeRaw(name : String, value : Number)
150-
= setAttribute(name, JsonPrimitive(value))
152+
fun setAttributeRaw(name: String, value: Number) = setAttribute(name, JsonPrimitive(value))
151153

152-
fun setAttribute(name : String, value : String)
153-
= setAttribute(name, JsonPrimitive(value))
154+
fun setAttribute(name: String, value: String) = setAttribute(name, JsonPrimitive(value))
154155

155-
fun setAttribute(name : String, value : Boolean)
156-
= setAttribute(name, JsonPrimitive(value))
156+
fun setAttribute(name: String, value: Boolean) = setAttribute(name, JsonPrimitive(value))
157157

158-
fun setAttribute(name : String, value : Number)
159-
= setAttribute(name, JsonPrimitive(value))
158+
fun setAttribute(name: String, value: Number) = setAttribute(name, JsonPrimitive(value))
160159

161160
/**
162161
* Set an attribute to the value in a [KVal], if the value changes the attribute
@@ -179,6 +178,7 @@ open class Element(
179178
jsoupDoc != null && (browser.isCatchingOutbound() == null || browser.isCatchingOutbound() == RENDER) -> {
180179
jsoupDoc.getElementById(id)!!.removeAttr(name)
181180
}
181+
182182
else -> {
183183
callJsFunction("document.getElementById({}).removeAttribute({})", id.json, JsonPrimitive(name))
184184
}
@@ -198,6 +198,7 @@ open class Element(
198198
val thisEl = jsoupDoc.getElementById(this.id)!!
199199
thisEl.html(html)
200200
}
201+
201202
else -> {
202203
callJsFunction("document.getElementById({}).innerHTML = {}", id.json, JsonPrimitive(html))
203204
}
@@ -234,7 +235,7 @@ open class Element(
234235
* A convenience function to set the [class attribute](https://www.w3schools.com/html/html_classes.asp),
235236
* this is a wrapper around [setAttribute].
236237
*/
237-
fun classes(value : KVal<String>) = setAttribute("class", value.map { it.json })
238+
fun classes(value: KVal<String>) = setAttribute("class", value.map { it.json })
238239

239240
/**
240241
* A convenience function to set the [class attribute](https://www.w3schools.com/html/html_classes.asp),
@@ -256,19 +257,32 @@ open class Element(
256257
*/
257258
fun addClasses(vararg classes: String, onlyIf: Boolean = true): Element {
258259
if (onlyIf) {
259-
for (class_ in classes) {
260-
if (class_.contains(' ')) {
261-
error("Class names must not contain spaces")
260+
val jsoupDoc = browser.htmlDocument.get()
261+
when {
262+
jsoupDoc != null && (browser.isCatchingOutbound() == null || browser.isCatchingOutbound() == RENDER) -> {
263+
val thisEl = jsoupDoc.getElementById(this.id)!!
264+
classes.forEach { thisEl.addClass(it) }
262265
}
263-
//language=JavaScript
264-
callJsFunction("""
266+
else -> {
267+
for (class_ in classes) {
268+
if (class_.contains(' ')) {
269+
error("Class names must not contain spaces")
270+
}
271+
//language=JavaScript
272+
callJsFunction(
273+
"""
265274
let id = {};
266275
let className = {};
267276
let el = document.getElementById(id);
268277
if (el.classList) el.classList.add(className);
269278
else if (!hasClass(el, className)) el.className += " " + className;
270-
""".trimIndent(), id.json, JsonPrimitive(class_))
279+
""".trimIndent(), id.json, JsonPrimitive(class_)
280+
)
281+
}
282+
}
271283
}
284+
285+
272286
}
273287
return this
274288
}
@@ -285,7 +299,8 @@ open class Element(
285299
error("Class names must not contain spaces")
286300
}
287301
//language=JavaScript
288-
callJsFunction("""
302+
callJsFunction(
303+
"""
289304
let id = {};
290305
let className = {};
291306
let el = document.getElementById(id);
@@ -294,7 +309,8 @@ open class Element(
294309
var reg = new RegExp("(\\s|^)" + className + "(\\s|${'$'})");
295310
el.className = el.className.replace(reg, " ");
296311
}
297-
""".trimIndent(), id.json, JsonPrimitive(class_))
312+
""".trimIndent(), id.json, JsonPrimitive(class_)
313+
)
298314
}
299315
}
300316
return this
@@ -325,26 +341,29 @@ open class Element(
325341
when {
326342
jsoupDoc != null && (browser.isCatchingOutbound() == null || browser.isCatchingOutbound() == RENDER) -> {
327343
val jsoupElement = jsoupDoc.getElementById(this.id)
328-
jsoupElement!!.children().remove()
344+
jsoupElement!!.children().remove()
329345
}
346+
330347
else -> {
331348
//language=JavaScript
332-
callJsFunction("""
349+
callJsFunction(
350+
"""
333351
let id = {};
334352
if (document.getElementById(id) != null) {
335353
let element = document.getElementById(id);
336354
while (element.firstChild) {
337355
element.removeChild(element.firstChild);
338356
}
339357
}
340-
""".trimIndent(), id.json)
358+
""".trimIndent(), id.json
359+
)
341360
}
342361
}
343362

344363
return this
345364
}
346365

347-
fun removeChildrenBetweenSpans(startSpanId : String, endSpanId: String) : Element{
366+
fun removeChildrenBetweenSpans(startSpanId: String, endSpanId: String): Element {
348367
val jsoupDoc = browser.htmlDocument.get()
349368
when {
350369
jsoupDoc != null && (browser.isCatchingOutbound() == null || browser.isCatchingOutbound() == RENDER) -> {
@@ -359,17 +378,20 @@ open class Element(
359378
}
360379
}
361380
}
381+
362382
else -> {
363383
//language=JavaScript
364-
callJsFunction("""
384+
callJsFunction(
385+
"""
365386
let startSpan = document.getElementById({});
366387
let endSpan = document.getElementById({});
367388
let nextSibling = startSpan.nextSibling;
368389
while(nextSibling != endSpan) {
369390
startSpan.parentNode.removeChild(startSpan.nextSibling);
370391
nextSibling = startSpan.nextSibling;
371392
}
372-
""".trimIndent(), JsonPrimitive(startSpanId), JsonPrimitive(endSpanId))
393+
""".trimIndent(), JsonPrimitive(startSpanId), JsonPrimitive(endSpanId)
394+
)
373395
}
374396
}
375397
return this
@@ -384,11 +406,14 @@ open class Element(
384406
.children()[position]
385407
.remove()
386408
}
409+
387410
else -> {
388-
callJsFunction("""
411+
callJsFunction(
412+
"""
389413
let element = document.getElementById({});
390414
element.removeChild(element.children[{}]);
391-
""".trimIndent(), id.json, position.json)
415+
""".trimIndent(), id.json, position.json
416+
)
392417
}
393418
}
394419
return this
@@ -407,6 +432,7 @@ open class Element(
407432
val element = jsoupDoc.getElementById(this.id)
408433
element!!.text(value)
409434
}
435+
410436
else -> {
411437
callJsFunction(setTextJS, id.json, JsonPrimitive(value))
412438
}
@@ -451,6 +477,7 @@ open class Element(
451477
val element = jsoupDoc.getElementById(this.id)
452478
element!!.appendText(value)
453479
}
480+
454481
else -> {
455482
callJsFunction(createTextNodeJs, JsonPrimitive(value), id.json)
456483
}
@@ -467,10 +494,17 @@ open class Element(
467494
browser.callJsFunction(wrappedJS, id.json, JsonPrimitive(eventName))
468495
}
469496

470-
override fun addEventListener(eventName: String, returnEventFields: Set<String>, retrieveJs: String?, preventDefault : Boolean, callback: (JsonElement) -> Unit): Element {
497+
override fun addEventListener(
498+
eventName: String,
499+
returnEventFields: Set<String>,
500+
retrieveJs: String?,
501+
preventDefault: Boolean,
502+
callback: (JsonElement) -> Unit
503+
): Element {
471504
val callbackId = abs(random.nextInt())
472505
val retrievedJs = if (retrieveJs != null) ", \"retrieved\" : ($retrieveJs)" else ""
473-
val eventObject = "{" + returnEventFields.joinToString(separator = ", ") { "\"$it\" : event.$it" } + retrievedJs + "}"
506+
val eventObject =
507+
"{" + returnEventFields.joinToString(separator = ", ") { "\"$it\" : event.$it" } + retrievedJs + "}"
474508
/*It'd be nice to make eventObject a parameter, but it doesn't work.
475509
eventObject is a map that has entries that look like { "buttons" : event.buttons }
476510
the event field accessed here is the event parameter from the "function(event)" in the javascript
@@ -510,8 +544,17 @@ open class Element(
510544
* @param updateOnEvent The event to listen for that signifies this element has been updated
511545
* @param initialValue The initial value of the KVar
512546
*/
513-
fun bind(accessor : (elementId : String) -> String, updateOnEvent: String, initialValue : JsonElement = JsonPrimitive("")) : KVar<JsonElement> {
514-
return bind(reader = { accessor(it) }, writer = { id, value -> "${accessor(id)} = $value" }, updateOnEvent = updateOnEvent, initialValue = initialValue)
547+
fun bind(
548+
accessor: (elementId: String) -> String,
549+
updateOnEvent: String,
550+
initialValue: JsonElement = JsonPrimitive("")
551+
): KVar<JsonElement> {
552+
return bind(
553+
reader = { accessor(it) },
554+
writer = { id, value -> "${accessor(id)} = $value" },
555+
updateOnEvent = updateOnEvent,
556+
initialValue = initialValue
557+
)
515558
}
516559

517560
/**
@@ -526,19 +569,24 @@ open class Element(
526569
* @param updateOnEvent The event to listen for that signifies this element has been updated
527570
* @param initialValue The initial value of the KVar
528571
*/
529-
fun bind(reader : (elementId : String) -> String, writer : (elementId : String, value : String) -> String, updateOnEvent : String, initialValue : JsonElement = JsonPrimitive("")) : KVar<JsonElement> {
572+
fun bind(
573+
reader: (elementId: String) -> String,
574+
writer: (elementId: String, value: String) -> String,
575+
updateOnEvent: String,
576+
initialValue: JsonElement = JsonPrimitive("")
577+
): KVar<JsonElement> {
530578
val kv = KVar(initialValue)
531579
on(retrieveJs = reader(this.id)).event<Event>(updateOnEvent) { event ->
532580
kv.value = event.retrieved
533581
}
534582
val kvChangeHandler = kv.addListener { old, new ->
535-
callJsFunction(writer(this.id, "{}")+";", new)
583+
callJsFunction(writer(this.id, "{}") + ";", new)
536584
}
537585
creator?.onCleanup(true) {
538586
kv.removeListener(kvChangeHandler)
539587
kv.close(CloseReason("Ancestor ElementCreator cleaned up"))
540588
}
541-
callJsFunction(writer(this.id, "{}")+";", initialValue)
589+
callJsFunction(writer(this.id, "{}") + ";", initialValue)
542590
return kv
543591
}
544592

@@ -548,10 +596,12 @@ open class Element(
548596
*/
549597
fun delete() {
550598
//language=JavaScript
551-
callJsFunction("""
599+
callJsFunction(
600+
"""
552601
let element = document.getElementById({});
553602
element.parentNode.removeChild(element);
554-
""".trimIndent(), id.json)
603+
""".trimIndent(), id.json
604+
)
555605
}
556606

557607
/**
@@ -560,13 +610,15 @@ open class Element(
560610
*/
561611
fun deleteIfExists() {
562612
//language=JavaScript
563-
callJsFunction("""
613+
callJsFunction(
614+
"""
564615
let id = {}
565616
if (document.getElementById(id)) {
566617
let element = document.getElementById(id);
567618
element.parentNode.removeChild(element);
568619
}
569-
""".trimIndent(), id.json)
620+
""".trimIndent(), id.json
621+
)
570622
}
571623

572624
/**
@@ -579,7 +631,7 @@ open class Element(
579631
*/
580632
val style get() = StyleReceiver(this)
581633

582-
val flags : ConcurrentSkipListSet<String> by lazy { ConcurrentSkipListSet() }
634+
val flags: ConcurrentSkipListSet<String> by lazy { ConcurrentSkipListSet() }
583635

584636
/**
585637
* See [here](https://docs.kweb.io/en/latest/dom.html#listening-for-events).

0 commit comments

Comments
 (0)