Permalink
Browse files

Closes #1030. Ability to remove attributes or individual parts of a c…

…lass attribute in CSS Selector Transforms
  • Loading branch information...
1 parent d2d60df commit b0b74787772a166417bbdd0998cea2c53146f68e @dpp dpp committed Aug 25, 2011
@@ -2036,6 +2036,41 @@ private class SelectorMap(binds: List[CssBind]) extends Function1[NodeSeq, NodeS
}
}
+
+ case (elem, (bind, AttrRemoveSubNode(attr))) => {
+ val org: NodeSeq = elem.attribute(attr).getOrElse(NodeSeq.Empty)
+ val calced = bind.calculate(elem).toList.map(findElemIfThereIsOne _)
+
+ if (calced.isEmpty || org.isEmpty) { // if either is empty, then return the Elem unmodified
+ elem
+ } else {
+ val filtered = elem.attributes.filter{
+ case up: UnprefixedAttribute => up.key != attr
+ case _ => true
+ }
+
+ val flat: Box[NodeSeq] = if (attr == "class") {
+ val set = Set(calced.map(_.text) :_*)
+ SuperString(org.text).charSplit(' ').toList.
+ filter(_.length > 0).filter(s => !set.contains(s)) match {
+ case Nil => Empty
+ case xs => Full(Text(xs.mkString(" ")))
+ }
+ } else {
+ if (org.text == calced.flatMap(a => a).text) Empty else Full(org)
+ }
+
+ val newAttr = flat match {
+ case Full(a) => new UnprefixedAttribute(attr, a, filtered)
+ case _ => filtered
+ }
+
+ new Elem(elem.prefix,
+ elem.label, newAttr,
+ elem.scope, elem.child :_*)
+
+ }
+ }
}
}
@@ -69,6 +69,7 @@ sealed trait AttributeRule
final case class AttrSubNode(attr: String) extends SubNode with AttributeRule
final case class AttrAppendSubNode(attr: String) extends SubNode with AttributeRule
+final case class AttrRemoveSubNode(attr: String) extends SubNode with AttributeRule
final case class SelectThisNode(kids: Boolean) extends SubNode
@@ -184,7 +185,9 @@ object CssSelectorParser extends Parsers with ImplicitConversions {
((opt('*') ~ '[' ~> attrName <~ '+' ~ ']' ^^ {
name => AttrAppendSubNode(name)
}) |
- (opt('*') ~ '[' ~> attrName <~ ']' ^^ {
+ (opt('*') ~ '[' ~> attrName <~ '!' ~ ']' ^^ {
+ name => AttrRemoveSubNode(name)
+ }) | (opt('*') ~ '[' ~> attrName <~ ']' ^^ {
name => AttrSubNode(name)
}) |
('-' ~ '*' ^^ (a => PrependKidsSubNode())) |
@@ -384,7 +384,7 @@ final class SuperListString(lst: List[String]) {
/**
* The SuperString class adds functionalities to the String class
*/
-final class SuperString(what: String) {
+final case class SuperString(what: String) {
/**
* Split a string according to a separator
* @param sep a regexp to use with the String::split method
@@ -574,6 +574,43 @@ object CssBindHelpersSpec extends Specification {
(res \ "@href").text must_== "q?z=r&a=b&b=d"
}
+ "remove an attribute from a class" in {
+ val func = ".foo [class!]" #> "andOther"
+
+ (func(<span class="foo andOther" />) \ "@class").text must_== "foo"
+ }
+
+ "remove an attribute from a class and the attribute if it's the only one left" in {
+ val func = ".foo [class!]" #> "foo"
+ val res = func(<span class="foo" />)
+
+ (res \ "@class").length must_== 0
+ }
+
+
+ "not remove a non-existant class" in {
+ val func = ".foo [class!]" #> "bar"
+ val res = func(<span class="foo" />)
+
+ (res \ "@class").text must_== "foo"
+ }
+
+
+ "remove an attribute from an attribute" in {
+ val func = "span [href!]" #> "foo"
+ val res = func(<span href="foo" />)
+
+ (res \ "@href").length must_== 0
+ }
+
+
+ "not remove a non-existant href" in {
+ val func = "span [href!]" #> "bar"
+ val res = func(<span href="foo bar" />)
+
+ (res \ "@href").text must_== "foo bar"
+ }
+
"option transform on *" in {
val opt: Option[Int] = Full(44)
val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
@@ -95,6 +95,28 @@ object CssSelectorSpec extends Specification("CSS Selector Specification") {
IdSelector("foo", Full(AttrSubNode("woof")))
}
+ "select an id with attr append subnodes" in {
+ CssSelectorParser.parse("#foo *[dog+] ").open_! must_==
+ IdSelector("foo", Full(AttrAppendSubNode("dog")))
+ }
+
+ "select an id with no star attr append subnodes" in {
+ CssSelectorParser.parse("#foo [woof+] ").open_! must_==
+ IdSelector("foo", Full(AttrAppendSubNode("woof")))
+ }
+
+ "select an id with attr append subnodes" in {
+ CssSelectorParser.parse("#foo *[dog!] ").open_! must_==
+ IdSelector("foo", Full(AttrRemoveSubNode("dog")))
+ }
+
+ "select an id with no star attr append subnodes" in {
+ CssSelectorParser.parse("#foo [woof!] ").open_! must_==
+ IdSelector("foo", Full(AttrRemoveSubNode("woof")))
+ }
+
+
+
"select attr/val pair" in {
CssSelectorParser.parse("frog=dog") must_==
Full(AttrSelector("frog", "dog", Empty))

0 comments on commit b0b7478

Please sign in to comment.