Remove DOM utilities, put temporary to JS to keep compatibility

This commit is contained in:
Sergey Mashkov
2015-10-26 18:48:38 +03:00
parent e9fbebaea2
commit 29973d714e
17 changed files with 387 additions and 1204 deletions
-44
View File
@@ -1,44 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.w3c.dom
import java.util.AbstractList
private class HTMLCollectionListView(val collection: HTMLCollection) : AbstractList<HTMLElement>() {
override val size: Int get() = collection.length
override fun get(index: Int): HTMLElement =
when {
index in 0..size() - 1 -> collection.item(index) as HTMLElement
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size() - 1})")
}
}
public fun HTMLCollection.asList(): List<HTMLElement> = HTMLCollectionListView(this)
public fun HTMLCollection?.toElementList(): List<Element> = this?.asList() ?: emptyList()
private class DOMTokenListView(val delegate: DOMTokenList) : AbstractList<String>() {
override val size: Int get() = delegate.length
override fun get(index: Int) =
when {
index in 0..size() - 1 -> delegate.item(index)!!
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size() - 1})")
}
}
public fun DOMTokenList.asList(): List<String> = DOMTokenListView(this)
+41
View File
@@ -0,0 +1,41 @@
package kotlin.dom.build
import org.w3c.dom.*
import kotlin.dom.*
/**
* Creates a new element which can be configured via a function
*/
fun Document.createElement(name: String, init: Element.() -> Unit): Element {
val elem = this.createElement(name)!!
elem.init()
return elem
}
/**
* Creates a new element to an element which has an owner Document which can be configured via a function
*/
fun Element.createElement(name: String, doc: Document? = null, init: Element.() -> Unit): Element {
val elem = ownerDocument(doc).createElement(name)!!
elem.init()
return elem
}
/**
* Adds a newly created element which can be configured via a function
*/
fun Document.addElement(name: String, init: Element.() -> Unit): Element {
val child = createElement(name, init)
this.appendChild(child)
return child
}
/**
* Adds a newly created element to an element which has an owner Document which can be configured via a function
*/
fun Element.addElement(name: String, doc: Document? = null, init: Element.() -> Unit): Element {
val child = createElement(name, doc, init)
this.appendChild(child)
return child
}
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.dom
import org.w3c.dom.*
/** Returns true if the element has the given CSS class style in its 'class' attribute */
fun Element.hasClass(cssClass: String): Boolean = className.matches("""(^|.*\s+)$cssClass($|\s+.*)""".toRegex())
/**
* Adds CSS class to element. Has no effect if all specified classes are already in class attribute of the element
*
* @return true if at least one class has been added
*/
fun Element.addClass(vararg cssClasses: String): Boolean {
val missingClasses = cssClasses.filterNot { hasClass(it) }
if (missingClasses.isNotEmpty()) {
val presentClasses = className.trim()
className = buildString {
append(presentClasses)
if (!presentClasses.isEmpty()) {
append(" ")
}
missingClasses.joinTo(this, " ")
}
return true
}
return false
}
/**
* Removes all [cssClasses] from element. Has no effect if all specified classes are missing in class attribute of the element
*
* @return true if at least one class has been removed
*/
fun Element.removeClass(vararg cssClasses: String): Boolean {
if (cssClasses.any { hasClass(it) }) {
val toBeRemoved = cssClasses.toSet()
className = className.trim().split("\\s+".toRegex()).filter { it !in toBeRemoved }.joinToString(" ")
return true
}
return false
}
+182
View File
@@ -0,0 +1,182 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.dom
import org.w3c.dom.*
import java.util.*
import kotlin.dom.*
import kotlin.support.*
// Properties
/** Returns the children of the element as a list */
fun Element?.children(): List<Node> {
return this?.childNodes?.asList() ?: emptyList()
}
/** Returns the child elements of this element */
fun Element?.childElements(): List<Element> = this?.childNodes?.filterElements() ?: emptyList()
/** Returns the child elements of this element with the given name */
fun Element?.childElements(name: String): List<Element> = this?.childNodes?.filterElements()?.filter { it.nodeName == name } ?: emptyList()
/** The descendant elements of this document */
@Deprecated("Use elements() function instead", ReplaceWith("this.elements()"))
val Document?.elements: List<Element>
get() = this.elements()
/** The descendant elements of this elements */
@Deprecated("Use elements() function instead", ReplaceWith("this?.elements() ?: emptyList()"))
val Element?.elements: List<Element>
get() = this?.elements() ?: emptyList()
@Deprecated("Use non-nullable receiver version elements()", ReplaceWith("this?.elements(localName) ?: emptyList()"))
fun Element?.elements(localName: String): List<Element> = this?.elements(localName) ?: emptyList()
/** Returns all the descendant elements given the local element name */
fun Element.elements(localName: String = "*"): List<Element> {
return this.getElementsByTagName(localName).asElementList()
}
/** Returns all the descendant elements given the local element name */
fun Document?.elements(localName: String = "*"): List<Element> {
return this?.getElementsByTagName(localName)?.asElementList() ?: emptyList()
}
@Deprecated("Use non-nullable elements function instead", ReplaceWith("this?.elements(namespaceUri, localName) ?: emptyList()"))
fun Element?.elements(namespaceUri: String, localName: String): List<Element> = this?.elements(namespaceUri, localName) ?: emptyList()
/** Returns all the descendant elements given the namespace URI and local element name */
fun Element.elements(namespaceUri: String, localName: String): List<Element> {
return this.getElementsByTagNameNS(namespaceUri, localName).asElementList()
}
/** Returns all the descendant elements given the namespace URI and local element name */
fun Document?.elements(namespaceUri: String, localName: String): List<Element> {
return this?.getElementsByTagNameNS(namespaceUri, localName)?.asElementList() ?: emptyList()
}
@Deprecated("Use non-null function instead with elvis", ReplaceWith("this?.asList() ?: emptyList()"))
fun NodeList?.asList(): List<Node> = this?.asList() ?: emptyList()
fun NodeList.asList() : List<Node> = NodeListAsList(this)
@Deprecated("Use asElementList() instead", ReplaceWith("this?.asElementList() ?: emptyList()"))
fun NodeList?.toElementList(): List<Element> = this?.asElementList() ?: emptyList()
/**
* Returns view with assumption that it contains only elements. Will crash in runtime if there are non-element nodes in
* the list during access such items. So [filterElements] would be better solution.
*/
fun NodeList.asElementList(): List<Element> = if (length == 0) emptyList() else ElementListAsList(this)
@Suppress("UNCHECKED_CAST")
fun List<Node>.filterElements(): List<Element> = filter { it.isElement } as List<Element>
fun NodeList.filterElements(): List<Element> = asList().filterElements()
private class NodeListAsList(private val delegate: NodeList) : AbstractList<Node>() {
override val size: Int get() = delegate.length
override fun get(index: Int): Node = when {
index in 0..size - 1 -> delegate.item(index)!!
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size - 1})")
}
}
private class ElementListAsList(private val nodeList: NodeList) : AbstractList<Element>() {
override fun get(index: Int): Element {
val node = nodeList.item(index)
if (node == null) {
throw IndexOutOfBoundsException("NodeList does not contain a node at index: " + index)
} else if (node.nodeType == Node.ELEMENT_NODE) {
return node as Element
} else {
throw IllegalArgumentException("Node is not an Element as expected but is $node")
}
}
override val size: Int get() = nodeList.length
}
/** Returns an [Iterator] over the next siblings of this node */
fun Node.nextSiblings(): Iterable<Node> = NextSiblings(this)
private class NextSiblings(private var node: Node) : Iterable<Node> {
override fun iterator(): Iterator<Node> = object : AbstractIterator<Node>() {
override fun computeNext(): Unit {
val nextValue = node.nextSibling
if (nextValue != null) {
setNext(nextValue)
node = nextValue
} else {
done()
}
}
}
}
/** Returns an [Iterator] over the next siblings of this node */
fun Node.previousSiblings(): Iterable<Node> = PreviousSiblings(this)
private class PreviousSiblings(private var node: Node) : Iterable<Node> {
override fun iterator(): Iterator<Node> = object : AbstractIterator<Node>() {
override fun computeNext(): Unit {
val nextValue = node.previousSibling
if (nextValue != null) {
setNext(nextValue)
node = nextValue
} else {
done()
}
}
}
}
/**
* it is *true* when [Node.nodeType] is TEXT_NODE or CDATA_SECTION_NODE
*/
val Node.isText : Boolean
get() = nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE
/**
* `true` if it's an element node
*/
val Node.isElement: Boolean
get() = nodeType == Node.ELEMENT_NODE
/** Returns the attribute value or empty string if its not present */
@Deprecated("Use getAttribute with elvis operator", ReplaceWith("this.getAttribute(name) ?: \"\""))
fun Element.attribute(name: String): String {
return this.getAttribute(name) ?: ""
}
@Deprecated("Use asList().firstOrNull() instead", ReplaceWith("this?.asList()?.firstOrNull()"))
val NodeList?.head: Node?
get() = this?.asList()?.firstOrNull()
@Deprecated("Use asList().firstOrNull() instead", ReplaceWith("this?.asList()?.firstOrNull()"))
val NodeList?.first: Node?
get() = this?.asList()?.firstOrNull()
@Deprecated("Use asList().lastOrNull() instead", ReplaceWith("this?.asList()?.lastOrNull()"))
val NodeList?.last: Node?
get() = this?.asList()?.lastOrNull()
@Deprecated("Use asList().lastOrNull() instead instead", ReplaceWith("last"))
val NodeList?.tail: Node?
get() = this?.asList()?.lastOrNull()
@@ -1,5 +1,3 @@
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("DomEventsKt")
package kotlin.dom
import java.io.Closeable
@@ -31,7 +29,7 @@ public fun mouseEventHandler(handler: (MouseEvent) -> Unit): EventListener {
/**
* Registers a handler on the named event
*/
public fun Node?.on(name: String, capture: Boolean, handler: (Event) -> Unit): Closeable? {
public fun Node.on(name: String, capture: Boolean, handler: (Event) -> Unit): Closeable? {
return on(name, capture, eventHandler(handler))
}
+42
View File
@@ -0,0 +1,42 @@
package kotlin.dom
import org.w3c.dom.*
import org.w3c.dom.DOMTokenList
import org.w3c.dom.HTMLCollection
import org.w3c.dom.HTMLElement
import java.util.*
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
operator fun Document?.get(selector: String): List<Element> {
return this?.querySelectorAll(selector)?.asList()?.filterElements() ?: emptyList()
}
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
operator fun Element.get(selector: String): List<Element> {
return querySelectorAll(selector).asList().filterElements()
}
private class HTMLCollectionListView(val collection: HTMLCollection) : AbstractList<HTMLElement>() {
override val size: Int get() = collection.length
override fun get(index: Int): HTMLElement =
when {
index in 0..size - 1 -> collection.item(index) as HTMLElement
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size - 1})")
}
}
public fun HTMLCollection.asList(): List<HTMLElement> = HTMLCollectionListView(this)
private class DOMTokenListView(val delegate: DOMTokenList) : AbstractList<String>() {
override val size: Int get() = delegate.length
override fun get(index: Int) =
when {
index in 0..size - 1 -> delegate.item(index)!!
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size - 1})")
}
}
public fun DOMTokenList.asList(): List<String> = DOMTokenListView(this)
internal fun HTMLCollection.asElementList(): List<Element> = asList()
+61
View File
@@ -0,0 +1,61 @@
package kotlin.dom
import org.w3c.dom.*
/** Removes all the children from this node */
fun Node.clear() {
while (hasChildNodes()) {
removeChild(firstChild!!)
}
}
/**
* Removes this node from parent node. Does nothing if no parent node
*/
fun Node.removeFromParent() {
parentNode?.removeChild(this)
}
operator fun Node.plus(child: Node): Node {
appendChild(child)
return this
}
operator fun Element.plus(text: String): Element = appendText(text)
operator fun Element.plusAssign(text: String): Unit {
appendText(text)
}
/** Returns the owner document of the element or uses the provided document */
fun Node.ownerDocument(doc: Document? = null): Document = when {
nodeType == Node.DOCUMENT_NODE -> this as Document
else -> doc ?: ownerDocument ?: throw IllegalArgumentException("Neither node contains nor parameter doc provides an owner document for $this")
}
/**
* Adds a newly created text node to an element which either already has an owner Document or one must be provided as a parameter
*/
@Deprecated("Use appendText() instead", ReplaceWith("appendText(text, doc)"))
fun Element.addText(text: String, doc: Document? = null): Element = appendText(text, doc)
/**
* Adds a newly created text node to an element which either already has an owner Document or one must be provided as a parameter
*/
@Deprecated("Use appendText() instead", ReplaceWith("appendText(text)"))
fun Element.addText(text: String): Element = appendText(text)
/**
* Creates text node and append it to the element
*/
fun Element.appendText(text: String, doc : Document? = null): Element {
appendChild(ownerDocument(doc).createTextNode(text))
return this
}
/**
* Appends the node to the specified parent element
*/
fun Node.appendTo(parent: Element) {
parent.appendChild(this)
}
@@ -35,8 +35,6 @@ public class StdLibTestToJSTest extends StdLibQUnitTestSupport {
"../../../js/js.libraries/test/core/assertTypeEquals.kt",
"text/StringTest.kt",
// TODO review: somethings FAILED if run:
"js/JsDomTest.kt",
"dom/DomTest.kt",
"collections/SequenceTest.kt",
"collections/IterableTests.kt",
"collections/ArraysTest.kt",
@@ -1,6 +1,6 @@
package test.browser
import kotlin.js.dom.html.document
import kotlin.browser.document
import org.w3c.dom.Node
fun box(): String {
-405
View File
@@ -1,405 +0,0 @@
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("DomKt")
package kotlin.dom
import kotlin.support.*
import java.util.*
import org.w3c.dom.*
// TODO should not need this - its here for the JS stuff
import java.lang.IllegalArgumentException
import java.lang.IndexOutOfBoundsException
// Properties
@Deprecated("use textContent directly instead")
public var Node.text: String
get() = textContent ?: ""
set(value) {
textContent = value
}
@Deprecated("You shouldn't use it as setter will drop all elements and get may return not exactly content user can expect")
public var Element.childrenText: String
get() {
val buffer = StringBuilder()
val nodeList = this.childNodes
var i = 0
val size = nodeList.length
while (i < size) {
val node = nodeList.item(i)
if (node != null) {
if (node.isText) {
buffer.append(node.nodeValue)
}
}
i++
}
return buffer.toString()
}
set(value) {
val element = this
// lets remove all the previous text nodes first
for (node in element.children()) {
if (node.isText) {
removeChild(node)
}
}
element.addText(value)
}
public var Element.classes: String
get() = this.getAttribute("class") ?: ""
set(value) {
this.setAttribute("class", value)
}
/** Returns true if the element has the given CSS class style in its 'class' attribute */
public fun Element.hasClass(cssClass: String): Boolean = classes.matches("""(^|.*\s+)$cssClass($|\s+.*)""".toRegex())
/** Returns the children of the element as a list */
public fun Element?.children(): List<Node> {
return this?.childNodes.toList()
}
/** Returns the child elements of this element */
public fun Element?.childElements(): List<Element> {
return children().filter<Node>{ it.nodeType == Node.ELEMENT_NODE }.map { it as Element }
}
/** Returns the child elements of this element with the given name */
public fun Element?.childElements(name: String): List<Element> {
return children().filter<Node>{ it.nodeType == Node.ELEMENT_NODE && it.nodeName == name }.map { it as Element }
}
/** The descendant elements of this document */
public val Document?.elements: List<Element>
get() = this?.getElementsByTagName("*").toElementList()
/** The descendant elements of this elements */
public val Element?.elements: List<Element>
get() = this?.getElementsByTagName("*").toElementList()
/** Returns all the descendant elements given the local element name */
public fun Element?.elements(localName: String): List<Element> {
return this?.getElementsByTagName(localName).toElementList()
}
/** Returns all the descendant elements given the local element name */
public fun Document?.elements(localName: String): List<Element> {
return this?.getElementsByTagName(localName).toElementList()
}
/** Returns all the descendant elements given the namespace URI and local element name */
public fun Element?.elements(namespaceUri: String, localName: String): List<Element> {
return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
}
/** Returns all the descendant elements given the namespace URI and local element name */
public fun Document?.elements(namespaceUri: String, localName: String): List<Element> {
return this?.getElementsByTagNameNS(namespaceUri, localName).toElementList()
}
public fun NodeList?.asList() : List<Node> = if (this == null) emptyList() else NodeListAsList(this)
@Deprecated("use asList instead", ReplaceWith("asList()"))
public fun NodeList?.toList(): List<Node> = asList()
public fun NodeList?.toElementList(): List<Element> {
return if (this == null) {
// TODO the following is easier to convert to JS
//emptyElementList()
ArrayList<Element>()
}
else {
ElementListAsList(this)
}
}
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
public operator fun Document?.get(selector: String): List<Element> {
val root = this?.documentElement
return if (root != null) {
if (selector == "*") {
elements
} else if (selector.startsWith(".")) {
elements.filter{ it.hasClass(selector.substring(1)) }.toList()
} else if (selector.startsWith("#")) {
val id = selector.substring(1)
val element = this?.getElementById(id)
return if (element != null)
arrayListOf(element)
else
emptyList()
} else {
// assume its a vanilla element name
elements(selector)
}
} else {
emptyList()
}
}
/** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */
public operator fun Element.get(selector: String): List<Element> {
return if (selector == "*") {
elements
} else if (selector.startsWith(".")) {
elements.filter{ it.hasClass(selector.substring(1)) }.toList()
} else if (selector.startsWith("#")) {
val element = this.ownerDocument?.getElementById(selector.substring(1))
return if (element != null)
arrayListOf(element)
else
emptyList()
} else {
// assume its a vanilla element name
elements(selector)
}
}
// Helper methods
/**
* Adds CSS class to element. Has no effect if all specified classes are already in class attribute of the element
*/
public fun Element.addClass(vararg cssClasses: String): Boolean {
val missingClasses = cssClasses.filterNot { hasClass(it) }
if (missingClasses.isNotEmpty()) {
val presentClasses = classes.trim()
classes = buildString {
append(presentClasses)
if (!presentClasses.isEmpty()) {
append(" ")
}
missingClasses.joinTo(this, " ")
}
return true
}
return false
}
/**
* Removes all [cssClasses] from element. Has no effect if all specified classes are missing in class attribute of the element
*/
public fun Element.removeClass(vararg cssClasses: String): Boolean {
if (cssClasses.any { hasClass(it) }) {
val toBeRemoved = cssClasses.toSet()
classes = classes.trim().split("\\s+".toRegex()).filter { it !in toBeRemoved }.joinToString(" ")
return true
}
return false
}
/** Removes all the children from this node */
public fun Node.clear() {
while (hasChildNodes()) {
removeChild(firstChild!!)
}
}
/**
* Removes this node from parent node. Does nothing if no parent node
*/
public fun Node.removeFromParent() {
parentNode?.removeChild(this)
}
private class NodeListAsList(private val delegate: NodeList) : AbstractList<Node>() {
override val size: Int get() = delegate.length
override fun get(index: Int): Node = when {
index in 0..size() - 1 -> delegate.item(index)!!
else -> throw IndexOutOfBoundsException("index $index is not in range [0 .. ${size() - 1})")
}
}
private class ElementListAsList(private val nodeList: NodeList) : AbstractList<Element>() {
override fun get(index: Int): Element {
val node = nodeList.item(index)
if (node == null) {
throw IndexOutOfBoundsException("NodeList does not contain a node at index: " + index)
} else if (node.nodeType == Node.ELEMENT_NODE) {
return node as Element
} else {
throw IllegalArgumentException("Node is not an Element as expected but is $node")
}
}
override val size: Int get() = nodeList.length
}
/** Returns an [Iterator] over the next siblings of this node */
public fun Node.nextSiblings(): Iterable<Node> = NextSiblings(this)
private class NextSiblings(private var node: Node) : Iterable<Node> {
override fun iterator(): Iterator<Node> = object : AbstractIterator<Node>() {
override fun computeNext(): Unit {
val nextValue = node.nextSibling
if (nextValue != null) {
setNext(nextValue)
node = nextValue
} else {
done()
}
}
}
}
/** Returns an [Iterator] over the next siblings of this node */
public fun Node.previousSiblings(): Iterable<Node> = PreviousSiblings(this)
private class PreviousSiblings(private var node: Node) : Iterable<Node> {
override fun iterator(): Iterator<Node> = object : AbstractIterator<Node>() {
override fun computeNext(): Unit {
val nextValue = node.previousSibling
if (nextValue != null) {
setNext(nextValue)
node = nextValue
} else {
done()
}
}
}
}
/**
* it is *true* when [Node.nodeType] is TEXT_NODE or CDATA_SECTION_NODE
*/
public val Node.isText : Boolean
get() = nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE
/** Returns the attribute value or empty string if its not present */
public fun Element.attribute(name: String): String {
return this.getAttribute(name) ?: ""
}
public val NodeList?.head: Node?
get() = if (this != null && this.length > 0) this.item(0) else null
public val NodeList?.first: Node?
get() = this.head
public val NodeList?.tail: Node?
get() {
if (this == null) {
return null
}
else {
val s = this.length
return if (s > 0) this.item(s - 1) else null
}
}
public val NodeList?.last: Node?
get() = this.tail
/** Converts the node list to an XML String */
public fun NodeList?.toXmlString(xmlDeclaration: Boolean = false): String {
return if (this == null)
"" else {
nodesToXmlString(this.toList(), xmlDeclaration)
}
}
/** Converts the collection of nodes to an XML String */
public fun nodesToXmlString(nodes: Iterable<Node>, xmlDeclaration: Boolean = false): String {
return nodes.map { it.toXmlString(xmlDeclaration) }.joinToString()
}
// Syntax sugar
public operator fun Node.plus(child: Node?): Node {
if (child != null) {
this.appendChild(child)
}
return this
}
public operator fun Element.plus(text: String?): Element = this.addText(text)
public operator fun Element.plusAssign(text: String?): Element = this.addText(text)
// Builder
/**
* Creates a new element which can be configured via a function
*/
public fun Document.createElement(name: String, init: Element.() -> Unit): Element {
val elem = this.createElement(name)!!
elem.init()
return elem
}
/**
* Creates a new element to an element which has an owner Document which can be configured via a function
*/
public fun Element.createElement(name: String, doc: Document? = null, init: Element.() -> Unit): Element {
val elem = ownerDocument(doc).createElement(name)!!
elem.init()
return elem
}
/** Returns the owner document of the element or uses the provided document */
public fun Node.ownerDocument(doc: Document? = null): Document {
val answer = if (this.nodeType == Node.DOCUMENT_NODE) this as Document
else if (doc == null) this.ownerDocument
else doc
if (answer == null) {
throw IllegalArgumentException("Element does not have an ownerDocument and none was provided for: ${this}")
} else {
return answer
}
}
/**
Adds a newly created element which can be configured via a function
*/
public fun Document.addElement(name: String, init: Element.() -> Unit): Element {
val child = createElement(name, init)
this.appendChild(child)
return child
}
/**
Adds a newly created element to an element which has an owner Document which can be configured via a function
*/
public fun Element.addElement(name: String, doc: Document? = null, init: Element.() -> Unit): Element {
val child = createElement(name, doc, init)
this.appendChild(child)
return child
}
/**
Adds a newly created text node to an element which either already has an owner Document or one must be provided as a parameter
*/
public fun Element.addText(text: String?, doc: Document? = null): Element {
if (text != null) {
val child = this.ownerDocument(doc).createTextNode(text)!!
this.appendChild(child)
}
return this
}
/**
* Creates text node and append it to the element
*/
public fun Element.appendText(text: String, doc : Document = this.ownerDocument!!) {
appendChild(doc.createTextNode(text))
}
/**
* Appends the node to the specified parent element
*/
public fun Node.appendTo(parent: Element) {
parent.appendChild(this)
}
@@ -1,76 +0,0 @@
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("DomEventsKt")
package kotlin.dom
import org.w3c.dom.events.Event
import org.w3c.dom.events.EventTarget
import org.w3c.dom.events.MouseEvent
// JavaScript style properties for JVM : TODO could auto-generate these
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("bubbles"), level = DeprecationLevel.HIDDEN)
public val Event.bubbles: Boolean
get() = getBubbles()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("cancelable"), level = DeprecationLevel.HIDDEN)
public val Event.cancelable: Boolean
get() = getCancelable()
public val Event.getCurrentTarget: EventTarget?
get() = getCurrentTarget()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("eventPhase"), level = DeprecationLevel.HIDDEN)
public val Event.eventPhase: Short
get() = getEventPhase()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("target"), level = DeprecationLevel.HIDDEN)
public val Event.target: EventTarget?
get() = getTarget()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("timeStamp"), level = DeprecationLevel.HIDDEN)
public val Event.timeStamp: Long
get() = getTimeStamp()
// TODO we can't use 'type' as the property name in Kotlin so we should fix it in JS
public val Event.eventType: String
get() = getType()!!
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("altKey"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.altKey: Boolean
get() = getAltKey()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("button"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.button: Short
get() = getButton()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("clientX"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.clientX: Int
get() = getClientX()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("clientY"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.clientY: Int
get() = getClientY()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("ctrlKey"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.ctrlKey: Boolean
get() = getCtrlKey()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("metaKey"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.metaKey: Boolean
get() = getMetaKey()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("relatedTarget"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.relatedTarget: EventTarget?
get() = getRelatedTarget()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("screenX"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.screenX: Int
get() = getScreenX()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("screenY"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.screenY: Int
get() = getScreenY()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("shiftKey"), level = DeprecationLevel.HIDDEN)
public val MouseEvent.shiftKey: Boolean
get() = getShiftKey()
-253
View File
@@ -1,253 +0,0 @@
/**
* JVM specific API implementations using JAXP and so forth which would not be used when compiling to JS
*/
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("DomKt")
package kotlin.dom
import org.w3c.dom.*
import org.xml.sax.InputSource
import java.io.File
import java.io.InputStream
import java.io.StringWriter
import java.io.Writer
import java.util.*
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.Source
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
// JavaScript style properties - TODO could auto-generate these
@Deprecated("Use getNodeName()", ReplaceWith("getNodeName() ?: \"\""))
public val Node.nodeName: String
get() = getNodeName() ?: ""
@Deprecated("Use getNodeValue()", ReplaceWith("getNodeValue() ?: \"\""))
public val Node.nodeValue: String
get() = getNodeValue() ?: ""
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("nodeType"), level = DeprecationLevel.HIDDEN)
public val Node.nodeType: Short
get() = getNodeType()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("parentNode"), level = DeprecationLevel.HIDDEN)
public val Node.parentNode: Node?
get() = getParentNode()
@Deprecated("Use getChildNodes()", ReplaceWith("getChildNodes()!!"))
public val Node.childNodes: NodeList
get() = getChildNodes()!!
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("firstChild"), level = DeprecationLevel.HIDDEN)
public val Node.firstChild: Node?
get() = getFirstChild()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("lastChild"), level = DeprecationLevel.HIDDEN)
public val Node.lastChild: Node?
get() = getLastChild()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("nextSibling"), level = DeprecationLevel.HIDDEN)
public val Node.nextSibling: Node?
get() = getNextSibling()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("previousSibling"), level = DeprecationLevel.HIDDEN)
public val Node.previousSibling: Node?
get() = getPreviousSibling()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("attributes"), level = DeprecationLevel.HIDDEN)
public val Node.attributes: NamedNodeMap?
get() = getAttributes()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("ownerDocument"), level = DeprecationLevel.HIDDEN)
public val Node.ownerDocument: Document?
get() = getOwnerDocument()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("documentElement"), level = DeprecationLevel.HIDDEN)
public val Document.documentElement: Element?
get() = this.getDocumentElement()
@Deprecated("Use getNamespaceURI()", ReplaceWith("getNamespaceURI() ?: \"\""))
public val Node.namespaceURI: String
get() = getNamespaceURI() ?: ""
@Deprecated("Use getPrefix()", ReplaceWith("getPrefix() ?: \"\""))
public val Node.prefix: String
get() = getPrefix() ?: ""
@Deprecated("Use getLocalName()", ReplaceWith("getLocalName() ?: \"\""))
public val Node.localName: String
get() = getLocalName() ?: ""
@Deprecated("Use getBaseURI", ReplaceWith("getBaseURI() ?: \"\""))
public val Node.baseURI: String
get() = getBaseURI() ?: ""
@Deprecated("Use getTextContent()/setTextContent()")
public var Node.textContent: String
get() = getTextContent() ?: ""
set(value) {
setTextContent(value)
}
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val DOMStringList.length: Int
get() = this.getLength()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val NameList.length: Int
get() = this.getLength()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val DOMImplementationList.length: Int
get() = this.getLength()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val NodeList.length: Int
get() = this.getLength()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val CharacterData.length: Int
get() = this.getLength()
@Deprecated("Is replaced with automatic synthetic extension", ReplaceWith("length"), level = DeprecationLevel.HIDDEN)
public val NamedNodeMap.length: Int
get() = this.getLength()
public var Element.id: String
get() = this.getAttribute("id") ?: ""
set(value) {
this.setAttribute("id", value)
this.setIdAttribute("id", true)
}
public var Element.style: String
get() = this.getAttribute("style") ?: ""
set(value) {
this.setAttribute("style", value)
}
/**
* Returns the HTML representation of the node
*/
public val Node.outerHTML: String
get() = toXmlString()
/**
* Returns the HTML representation of the node
*/
public val Node.innerHTML: String
get() = childNodes.outerHTML
/**
* Returns the HTML representation of the nodes
*/
public val NodeList.outerHTML: String
get() = toList().map { it.innerHTML }.joinToString("")
/** Returns an [Iterator] of all the next [Element] siblings */
public fun Node.nextElements(): List<Element> = nextSiblings().filterIsInstance<Element>()
/** Returns an [Iterator] of all the previous [Element] siblings */
public fun Node.previousElements(): List<Element> = previousSiblings().filterIsInstance<Element>()
public var Element.classSet: MutableSet<String>
get() {
val answer = LinkedHashSet<String>()
val array = this.classes.split("""\s""".toPattern())
for (s in array) {
if (s.length() > 0) {
answer.add(s)
}
}
return answer
}
set(value) {
this.classes = value.joinToString(" ")
}
/** Creates a new document with the given document builder*/
public fun createDocument(builder: DocumentBuilder): Document {
return builder.newDocument()
}
/** Creates a new document with an optional DocumentBuilderFactory */
public fun createDocument(builderFactory: DocumentBuilderFactory = defaultDocumentBuilderFactory()): Document {
return createDocument(builderFactory.newDocumentBuilder())
}
/**
* Returns the default [DocumentBuilderFactory]
*/
public fun defaultDocumentBuilderFactory(): DocumentBuilderFactory {
return DocumentBuilderFactory.newInstance()!!
}
/**
* Returns the default [DocumentBuilder]
*/
public fun defaultDocumentBuilder(builderFactory: DocumentBuilderFactory = defaultDocumentBuilderFactory()): DocumentBuilder {
return builderFactory.newDocumentBuilder()
}
/**
* Parses the XML document using the given *file*
*/
public fun parseXml(file: File, builder: DocumentBuilder = defaultDocumentBuilder()): Document {
return builder.parse(file)!!
}
/**
* Parses the XML document using the given *uri*
*/
public fun parseXml(uri: String, builder: DocumentBuilder = defaultDocumentBuilder()): Document {
return builder.parse(uri)!!
}
/**
* Parses the XML document using the given *inputStream*
*/
public fun parseXml(inputStream: InputStream, builder: DocumentBuilder = defaultDocumentBuilder()): Document {
return builder.parse(inputStream)!!
}
/**
* Parses the XML document using the given *inputSource*
*/
public fun parseXml(inputSource: InputSource, builder: DocumentBuilder = defaultDocumentBuilder()): Document {
return builder.parse(inputSource)!!
}
/** Creates a new TrAX transformer */
public fun createTransformer(source: Source? = null, factory: TransformerFactory = TransformerFactory.newInstance()!!): Transformer {
val transformer = if (source != null) {
factory.newTransformer(source)
} else {
factory.newTransformer()
}
return transformer!!
}
/** Converts the node to an XML String */
public fun Node.toXmlString(): String = toXmlString(false)
/** Converts the node to an XML String */
public fun Node.toXmlString(xmlDeclaration: Boolean): String {
val writer = StringWriter()
writeXmlString(writer, xmlDeclaration)
return writer.toString()
}
/** Converts the node to an XML String and writes it to the given [Writer] */
public fun Node.writeXmlString(writer: Writer, xmlDeclaration: Boolean): Unit {
val transformer = createTransformer()
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, if (xmlDeclaration) "no" else "yes")
transformer.transform(DOMSource(this), StreamResult(writer))
}
@@ -1,33 +0,0 @@
package test.browser
import kotlin.dom.*
import kotlin.browser.*
import kotlin.test.*
import org.junit.Test as test
class BrowserTest {
@test fun accessBrowserDOM() {
registerBrowserPage()
val h1 = document["h1"].first()
assertEquals("Hello World!", h1.textContent)
}
protected fun registerBrowserPage() {
// lets simulate being a browser registering its DOM
val doc = createDocument()
doc.addElement("html") {
addElement("body") {
addElement("h1") {
addText("Hello World!")
}
addElement("p") {
addText("This is some text content")
}
}
}
document = doc
}
}
-130
View File
@@ -1,130 +0,0 @@
package test.dom
import kotlin.dom.*
import kotlin.test.*
import org.w3c.dom.*
import org.junit.Test as test
class DomBuilderTest() {
@test fun buildDocument() {
var doc = createDocument()
assertTrue {
doc["grandchild"].isEmpty()
}
doc.addElement("foo") {
id = "id1"
style = "bold"
classes = "bar"
addElement("child") {
id = "id2"
classes = "another"
addElement("grandChild") {
id = "id3"
classes = " bar tiny"
addText("Hello World!")
// TODO support neater syntax sugar for adding text?
// += "Hello World!"
}
addElement("grandChild2") {
id = "id3"
classes = "tiny thing bar "
addText("Hello World!")
// TODO support neater syntax sugar for adding text?
// += "Hello World!"
}
}
}
// test css selections on document
assertEquals(0, doc[".doesNotExist"].size())
assertEquals(1, doc[".another"].size())
assertEquals(3, doc[".bar"].size())
assertEquals(2, doc[".tiny"].size())
// element tag selections
assertEquals(0, doc["doesNotExist"].size())
assertEquals(1, doc["foo"].size())
assertEquals(1, doc["child"].size())
assertEquals(1, doc["grandChild"].size())
// id selections
assertEquals(1, doc["#id1"].size())
assertEquals(1, doc["#id2"].size())
assertEquals(1, doc["#id3"].size())
val root = doc.documentElement
if (root == null) {
fail("No root!")
} else {
assertTrue {
root.hasClass("bar")
}
// test css selections on element
assertEquals(0, root[".doesNotExist"].size())
assertEquals(1, root[".another"].size())
assertEquals(2, root[".bar"].size())
assertEquals(2, root[".tiny"].size())
// element tag selections
assertEquals(0, root["doesNotExist"].size())
assertEquals(0, root["foo"].size())
assertEquals(1, root["child"].size())
assertEquals(1, root["grandChild"].size())
// id selections
assertEquals(1, root["#id1"].size())
assertEquals(1, root["#id2"].size())
assertEquals(1, root["#id3"].size())
// iterating through next element siblings
for (e in root.nextElements()) {
println("found element: $e")
}
}
val grandChild = doc["grandChild"].firstOrNull()
if (grandChild != null) {
assertEquals("Hello World!", grandChild.textContent)
assertEquals(" bar tiny", grandChild.attribute("class"))
// test the classSet
val classSet = grandChild.classSet
assertTrue(classSet.contains("bar"))
assertTrue(classSet.contains("tiny"))
assertTrue(classSet.size() == 2 )
assertFalse(classSet.contains("doesNotExist"))
// lets add a new class and some existing classes
grandChild.addClass("bar")
grandChild.addClass("newThingy")
assertEquals("bar tiny newThingy", grandChild.classes)
// remove
grandChild.removeClass("bar")
assertEquals("tiny newThingy", grandChild.classes)
grandChild.removeClass("tiny")
assertEquals("newThingy", grandChild.classes)
} else {
fail("Not an Element $grandChild")
}
val child = doc["child"].firstOrNull()
if (child != null) {
val gc1 = child.childElements("grandChild")
assertEquals(1, gc1.size(), "Expected a single child but found $gc1")
val gc2 = child.childElements("grandChild2")
assertEquals(1, gc2.size(), "Expected a single child but found $gc2")
} else {
fail("No child found!")
}
val children = doc.documentElement.children()
assertEquals(1, children.size())
}
}
-54
View File
@@ -1,54 +0,0 @@
package test.dom
import kotlin.*
import kotlin.dom.*
import kotlin.test.*
import org.w3c.dom.*
import org.junit.Test as test
class DomTest {
@test fun testCreateDocument() {
var doc = createDocument()
assertNotNull(doc, "Should have created a document")
val e = doc.createElement("foo")!!
assertCssClass(e, "")
// now lets update the cssClass property
e.classes = "foo"
assertCssClass(e, "foo")
// now using the attribute directly
e.setAttribute("class", "bar")
assertCssClass(e, "bar")
doc + e
println("document ${doc.toXmlString()}")
}
@test fun addText() {
var doc = createDocument()
assertNotNull(doc, "Should have created a document")
val e = doc.createElement("foo")!!
e + "hello"
val xml = e.toXmlString()
println("element after text ${xml}")
assertEquals("hello", e.textContent)
}
fun assertCssClass(e: Element, value: String?): Unit {
val cl = e.classes
val cl2 = e.getAttribute("class")
val xml = e.toXmlString()
println("element ${xml} has cssClass `${cl}` class attr `${cl2}`")
assertEquals(value, cl, "value of element.cssClass")
assertEquals(value, cl2, "value of element.getAttribute(\"class\")")
}
}
@@ -1,48 +0,0 @@
package test.dom
import kotlin.dom.*
import kotlin.test.*
import org.w3c.dom.*
import org.junit.Test as test
class NextSiblingTest {
@test fun nextSibling() {
val doc = createDocument()
doc.addElement("foo") {
id = "id1"
style = "bold"
classes = "bar"
addElement("child") {
id = "id2"
classes = "another"
addElement("grandChild") {
id = "id3"
classes = " bar tiny"
addText("Hello World!")
// TODO support neater syntax sugar for adding text?
// += "Hello World!"
}
addElement("grandChild2") {
id = "id4"
classes = "tiny thing bar "
addText("Hello World!")
// TODO support neater syntax sugar for adding text?
// += "Hello World!"
}
}
}
println("builder document: ${doc.toXmlString()}")
val elems = doc["#id3"]
val element = elems.first()
val elements = element.nextElements()
val nodes = element.nextSiblings().toList()
assertEquals(1, elements.size())
assertEquals(1, nodes.size())
}
}
-155
View File
@@ -1,155 +0,0 @@
package test.js
import kotlin.*
import kotlin.browser.*
import kotlin.dom.*
import kotlin.test.*
import org.w3c.dom.*
import org.junit.Test as test
class JsDomTest {
@test fun testCreateDocument() {
var doc = document
assertNotNull(doc, "Should have created a document")
val e = doc.createElement("foo")!!
assertCssClass(e, "")
// now lets update the cssClass property
e.classes = "foo"
assertCssClass(e, "foo")
// now using the attribute directly
e.setAttribute("class", "bar")
assertCssClass(e, "bar")
}
@test fun addText() {
var doc = document
assertNotNull(doc, "Should have created a document")
val e = doc.createElement("foo")!!
e + "hello"
val xml = e.toXmlString()
println("element after text ${xml}")
assertEquals("hello", e.textContent)
}
@test fun testAddClassMissing() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class1"
e.addClass("class1", "class2")
assertEquals("class1 class2", e.classes)
}
@test fun testAddClassPresent() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class2 class1"
e.addClass("class1", "class2")
assertEquals("class2 class1", e.classes)
}
@test fun testAddClassUndefinedClasses() {
val e = document.createElement("e")!!
e.addClass("class1")
assertEquals("class1", e.classes)
}
@test fun testRemoveClassMissing() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class2 class1"
e.removeClass("class3")
assertEquals("class2 class1", e.classes)
}
@test fun testRemoveClassPresent1() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class2 class1"
e.removeClass("class2")
assertEquals("class1", e.classes)
}
@test fun testRemoveClassPresent2() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class2 class1"
e.removeClass("class1")
assertEquals("class2", e.classes)
}
@test fun testRemoveClassPresent3() {
val e = document.createElement("e")!!
assertNotNull(e)
e.classes = "class2 class1 class3"
e.removeClass("class1")
assertEquals("class2 class3", e.classes)
}
@test fun testRemoveClassUndefinedClasses() {
val e = document.createElement("e")!!
e.removeClass("class1")
assertEquals("", e.classes)
}
@test fun testRemoveFromParent() {
val doc = document
val parent = doc.createElement("pp")
val child = doc.createElement("cc")
parent.appendChild(child)
assertEquals(parent, child.parentNode)
assertEquals(listOf(child), parent.childNodes.asList())
child.removeFromParent()
assertNull(child.parentNode)
assertEquals(emptyList<Node>(), parent.childNodes.asList())
child.removeFromParent()
assertNull(child.parentNode)
}
@test fun testRemoveFromParentOrphanNode() {
val child = document.createElement("cc")
child.removeFromParent()
assertNull(child.parentNode)
}
private fun assertCssClass(e: Element, value: String?): Unit {
val cl = e.classes
val cl2 = e.getAttribute("class") ?: ""
val xml = e.toXmlString()
println("element ${xml} has cssClass `${cl}` class attr `${cl2}`")
assertEquals(value, cl, "value of element.cssClass")
assertEquals(value, cl2, "value of element.getAttribute(\"class\")")
}
}