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()
+67
View File
@@ -0,0 +1,67 @@
package kotlin.dom
import java.io.Closeable
import org.w3c.dom.*
import org.w3c.dom.events.*
/**
* Turns an event handler function into an [EventListener]
*/
public fun eventHandler(handler: (Event) -> Unit): EventListener {
return EventListenerHandler(handler)
}
private class EventListenerHandler(private val handler: (Event) -> Unit) : EventListener {
public override fun handleEvent(e: Event) {
handler(e)
}
public override fun toString(): String = "EventListenerHandler($handler)"
}
public fun mouseEventHandler(handler: (MouseEvent) -> Unit): EventListener {
return eventHandler { e ->
if (e is MouseEvent) {
handler(e)
}
}
}
/**
* Registers a handler on the named event
*/
public fun Node.on(name: String, capture: Boolean, handler: (Event) -> Unit): Closeable? {
return on(name, capture, eventHandler(handler))
}
/**
* Registers an [EventListener] on the named event
*/
public fun Node?.on(name: String, capture: Boolean, listener: EventListener): Closeable? {
return if (this is EventTarget) {
addEventListener(name, listener, capture)
CloseableEventListener(this, listener, name, capture)
} else {
null
}
}
private class CloseableEventListener(
private val target: EventTarget,
private val listener: EventListener,
private val name: String,
private val capture: Boolean
) : Closeable {
public override fun close() {
target.removeEventListener(name, listener, capture)
}
public override fun toString(): String = "CloseableEventListener($target, $name)"
}
public fun Node?.onClick(capture: Boolean = false, handler: (MouseEvent) -> Unit): Closeable? {
return on("click", capture, mouseEventHandler(handler))
}
public fun Node?.onDoubleClick(capture: Boolean = false, handler: (MouseEvent) -> Unit): Closeable? {
return on("dblclick", capture, mouseEventHandler(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)
}