diff --git a/stdlib/ktSrc/dom/Dom.kt b/stdlib/ktSrc/dom/Dom.kt index 92a03763049..6daaaa6bd58 100644 --- a/stdlib/ktSrc/dom/Dom.kt +++ b/stdlib/ktSrc/dom/Dom.kt @@ -65,22 +65,89 @@ set(value) { this.setAttribute("style", value) } -// TODO can we come up with a better name; 'class' is a reserved word? -var Element.cssClass : String +var Element.classes : String get() = this.getAttribute("class")?: "" set(value) { this.setAttribute("class", value) } +var Element.classSet : Set +get() { + val answer = LinkedHashSet() + val array = this.classes.split("""\s""") + for (s in array) { + if (s != null && s.size > 0) { + answer.add(s) + } + } + return answer +} +set(value) { + this.classes = value.join(" ") +} + // Helper methods + +/** Returns true if the element has the given CSS class style in its 'class' attribute */ fun Element.hasClass(cssClass: String): Boolean { - val c = this.cssClass + val c = this.classes return if (c != null) - c.matches("""(^|\s+)$cssClass($|\s+)""") + c.matches("""(^|.*\s+)$cssClass($|\s+.*)""") else false } +/** Adds the given CSS class to this element's 'class' attribute */ +fun Element.addClass(cssClass: String): Boolean { + val classSet = this.classSet + val answer = classSet.add(cssClass) + if (answer) { + this.classSet = classSet + } + return answer +} + +/** Removes the given CSS class to this element's 'class' attribute */ +fun Element.removeClass(cssClass: String): Boolean { + val classSet = this.classSet + val answer = classSet.remove(cssClass) + if (answer) { + this.classSet = classSet + } + return answer +} + +/** TODO this approach generates compiler errors... + +fun Element.addClass(varargs cssClasses: Array): Boolean { + val set = this.classSet + var answer = false + for (cs in cssClasses) { + if (set.add(cs)) { + answer = true + } + } + if (answer) { + this.classSet = classSet + } + return answer +} + +fun Element.removeClass(varargs cssClasses: Array): Boolean { + val set = this.classSet + var answer = false + for (cs in cssClasses) { + if (set.remove(cs)) { + answer = true + } + } + if (answer) { + this.classSet = classSet + } + return answer +} +*/ + /** Searches for elements using the element name, an element ID (if prefixed with dot) or element class (if prefixed with #) */ fun Document?.get(selector: String): List { val root = this?.getDocumentElement() diff --git a/testlib/test/dom/DomBuilderTest.kt b/testlib/test/dom/DomBuilderTest.kt index 5729a0f49d1..8884fc9f940 100644 --- a/testlib/test/dom/DomBuilderTest.kt +++ b/testlib/test/dom/DomBuilderTest.kt @@ -8,6 +8,7 @@ import org.w3c.dom.* class DomBuilderTest() : TestSupport() { + fun testBuildDocument() { var doc = createDocument() @@ -18,13 +19,20 @@ class DomBuilderTest() : TestSupport() { doc.addElement("foo") { id = "id1" style = "bold" - cssClass = "bar" + classes = "bar" addElement("child") { id = "id2" - cssClass = "another" + classes = "another" addElement("grandChild") { id = "id3" - cssClass = "tiny" + 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!" @@ -37,8 +45,8 @@ class DomBuilderTest() : TestSupport() { // test css selections on document assertEquals(0, doc[".doesNotExist"].size()) assertEquals(1, doc[".another"].size()) - assertEquals(1, doc[".tiny"].size()) - assertEquals(1, doc[".bar"].size()) + assertEquals(3, doc[".bar"].size()) + assertEquals(2, doc[".tiny"].size()) // element tag selections assertEquals(0, doc["doesNotExist"].size()) @@ -60,8 +68,8 @@ class DomBuilderTest() : TestSupport() { // test css selections on element assertEquals(0, root[".doesNotExist"].size()) assertEquals(1, root[".another"].size()) - assertEquals(1, root[".tiny"].size()) - assertEquals(0, root[".bar"].size()) + assertEquals(2, root[".bar"].size()) + assertEquals(2, root[".tiny"].size()) // element tag selections assertEquals(0, root["doesNotExist"].size()) @@ -77,12 +85,32 @@ class DomBuilderTest() : TestSupport() { fail("No root!") } - val grandChild = doc["grandChild"].first if (grandChild != null) { println("got element ${grandChild.toXmlString()} with text '${grandChild.text}`") assertEquals("Hello World!", grandChild.text) - assertEquals("tiny", grandChild.attribute("class") ?: "") + 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") } diff --git a/testlib/test/dom/DomTest.kt b/testlib/test/dom/DomTest.kt index 1cf469464bc..558d0f99f94 100644 --- a/testlib/test/dom/DomTest.kt +++ b/testlib/test/dom/DomTest.kt @@ -15,7 +15,7 @@ class DomTest() : TestSupport() { assertCssClass(e, "") // now lets update the cssClass property - e.cssClass = "foo" + e.classes = "foo" assertCssClass(e, "foo") // now using the attribute directly @@ -28,7 +28,7 @@ class DomTest() : TestSupport() { fun assertCssClass(e: Element, value: String?): Unit { - val cl = e.cssClass + val cl = e.classes val cl2 = e.getAttribute("class") println("element ${e.toXmlString()} has cssClass `${cl}` class attr `${cl2}`")