package kotlinx.html.injector
import kotlinx.html.*
import kotlinx.html.dom.*
import org.w3c.dom.*
import kotlin.reflect.*
fun F.injectTo(bean : T, field : KMutableProperty1) {
field.set(bean, this)
}
private fun F.injectToUnsafe(bean : T, field : KMutableProperty1) {
injectTo(bean, field.asDynamic())
}
interface InjectCapture
class InjectByClassName(val className : String) : InjectCapture
class InjectByTagName(val tagName : String) : InjectCapture
object InjectRoot : InjectCapture
interface CustomCapture : InjectCapture {
fun apply(element : HTMLElement) : Boolean
}
class InjectorConsumer(val downstream : TagConsumer, val bean : T, rules : List>>) : TagConsumer by downstream {
private val classesMap: Map>> = rules
.filter { it.first is InjectByClassName }
.map { it.first as InjectByClassName to it.second }
.groupBy ({ it.first.className }, { it.second })
private val tagNamesMap = rules
.filter { it.first is InjectByTagName }
.map { it.first as InjectByTagName to it.second }
.groupBy({ it.first.tagName.toLowerCase() }, { it.second })
private val rootCaptures = rules.filter { it.first == InjectRoot }.map { it.second }
private val customCaptures = rules.filter {it.first is CustomCapture}.map {it.first as CustomCapture to it.second}
override fun onTagEnd(tag: Tag) {
downstream.onTagEnd(tag)
val node = downstream.finalize()
if (classesMap.isNotEmpty()) {
node.classList.asList().flatMap { classesMap[it] ?: emptyList() }.forEach { field ->
node.injectToUnsafe(bean, field)
}
}
if (tagNamesMap.isNotEmpty()) {
tagNamesMap[node.tagName.toLowerCase()]?.forEach { field ->
node.injectToUnsafe(bean, field)
}
}
customCaptures.filter { it.first.apply(node) }.map {it.second}.forEach { field ->
node.injectToUnsafe(bean, field)
}
}
override fun finalize(): HTMLElement {
val node = downstream.finalize()
rootCaptures.forEach { field ->
node.injectToUnsafe(bean, field)
}
return node
}
}
fun TagConsumer.inject(bean : T, rules : List>>) : TagConsumer = InjectorConsumer(this, bean, rules)
fun HTMLElement.appendAndInject(bean : T, rules : List>>, block : TagConsumer.() -> Unit) : List = append {
InjectorConsumer(this@append, bean, rules).block()
Unit
}