Skip to content
This repository
Browse code

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

…lass attribute in CSS Selector Transforms
  • Loading branch information...
commit b0b74787772a166417bbdd0998cea2c53146f68e 1 parent d2d60df
David Pollak authored
35  core/util/src/main/scala/net/liftweb/util/BindHelpers.scala
@@ -2036,6 +2036,41 @@ private class SelectorMap(binds: List[CssBind]) extends Function1[NodeSeq, NodeS
2036 2036
             
2037 2037
           }
2038 2038
         }
  2039
+
  2040
+        case (elem, (bind, AttrRemoveSubNode(attr))) => {
  2041
+          val org: NodeSeq = elem.attribute(attr).getOrElse(NodeSeq.Empty)
  2042
+          val calced = bind.calculate(elem).toList.map(findElemIfThereIsOne _)
  2043
+
  2044
+          if (calced.isEmpty || org.isEmpty) { // if either is empty, then return the Elem unmodified
  2045
+            elem
  2046
+          } else {
  2047
+            val filtered = elem.attributes.filter{
  2048
+              case up: UnprefixedAttribute => up.key != attr
  2049
+              case _ => true
  2050
+            }
  2051
+
  2052
+            val flat: Box[NodeSeq] = if (attr == "class") {
  2053
+                val set = Set(calced.map(_.text) :_*)
  2054
+                SuperString(org.text).charSplit(' ').toList.
  2055
+                  filter(_.length > 0).filter(s => !set.contains(s)) match {
  2056
+                  case Nil => Empty
  2057
+                  case xs => Full(Text(xs.mkString(" ")))
  2058
+                }
  2059
+            } else {
  2060
+              if (org.text == calced.flatMap(a => a).text) Empty else Full(org)
  2061
+            }
  2062
+
  2063
+            val newAttr = flat match {
  2064
+              case Full(a) => new UnprefixedAttribute(attr, a, filtered)
  2065
+              case _ => filtered
  2066
+            }
  2067
+
  2068
+            new Elem(elem.prefix,
  2069
+                     elem.label, newAttr,
  2070
+                     elem.scope, elem.child :_*)
  2071
+
  2072
+          }
  2073
+        }
2039 2074
       }
2040 2075
     }
2041 2076
 
5  core/util/src/main/scala/net/liftweb/util/CssSelector.scala
@@ -69,6 +69,7 @@ sealed trait AttributeRule
69 69
 
70 70
 final case class AttrSubNode(attr: String) extends SubNode with AttributeRule
71 71
 final case class AttrAppendSubNode(attr: String) extends SubNode with AttributeRule
  72
+final case class AttrRemoveSubNode(attr: String) extends SubNode with AttributeRule
72 73
 
73 74
 final case class SelectThisNode(kids: Boolean) extends SubNode
74 75
 
@@ -184,7 +185,9 @@ object CssSelectorParser extends Parsers with ImplicitConversions {
184 185
   ((opt('*') ~ '[' ~> attrName <~ '+' ~ ']' ^^ {
185 186
     name => AttrAppendSubNode(name)
186 187
   }) | 
187  
-   (opt('*') ~ '[' ~> attrName <~ ']' ^^ {
  188
+  (opt('*') ~ '[' ~> attrName <~ '!' ~ ']' ^^ {
  189
+    name => AttrRemoveSubNode(name)
  190
+  }) |    (opt('*') ~ '[' ~> attrName <~ ']' ^^ {
188 191
      name => AttrSubNode(name)
189 192
    }) | 
190 193
    ('-' ~ '*' ^^ (a => PrependKidsSubNode())) |
2  core/util/src/main/scala/net/liftweb/util/StringHelpers.scala
@@ -384,7 +384,7 @@ final class SuperListString(lst: List[String]) {
384 384
 /**
385 385
  * The SuperString class adds functionalities to the String class
386 386
  */
387  
-final class SuperString(what: String) {
  387
+final case class SuperString(what: String) {
388 388
   /**
389 389
    * Split a string according to a separator
390 390
    * @param sep a regexp to use with the String::split method
37  core/util/src/test/scala/net/liftweb/util/BindHelpersSpec.scala
@@ -574,6 +574,43 @@ object CssBindHelpersSpec extends Specification  {
574 574
       (res \ "@href").text must_== "q?z=r&a=b&b=d"
575 575
     }
576 576
 
  577
+    "remove an attribute from a class" in {
  578
+      val func = ".foo [class!]" #> "andOther"
  579
+
  580
+      (func(<span class="foo andOther" />) \ "@class").text must_== "foo"
  581
+    }
  582
+
  583
+    "remove an attribute from a class and the attribute if it's the only one left" in {
  584
+      val func = ".foo [class!]" #> "foo"
  585
+      val res = func(<span class="foo" />)
  586
+
  587
+      (res \ "@class").length must_== 0
  588
+    }
  589
+
  590
+
  591
+    "not remove a non-existant class" in {
  592
+      val func = ".foo [class!]" #> "bar"
  593
+      val res = func(<span class="foo" />)
  594
+
  595
+      (res \ "@class").text must_== "foo"
  596
+    }
  597
+
  598
+
  599
+    "remove an attribute from an attribute" in {
  600
+      val func = "span [href!]" #> "foo"
  601
+      val res = func(<span href="foo" />)
  602
+
  603
+      (res \ "@href").length must_== 0
  604
+    }
  605
+
  606
+
  607
+    "not remove a non-existant href" in {
  608
+      val func = "span [href!]" #> "bar"
  609
+      val res = func(<span href="foo bar" />)
  610
+
  611
+      (res \ "@href").text must_== "foo bar"
  612
+    }
  613
+    
577 614
     "option transform on *" in {
578 615
       val opt: Option[Int] = Full(44)
579 616
       val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
22  core/util/src/test/scala/net/liftweb/util/CssSelectorSpec.scala
@@ -95,6 +95,28 @@ object CssSelectorSpec extends Specification("CSS Selector Specification")  {
95 95
       IdSelector("foo", Full(AttrSubNode("woof")))
96 96
     }
97 97
 
  98
+    "select an id with attr append subnodes" in {
  99
+      CssSelectorParser.parse("#foo  *[dog+] ").open_! must_==
  100
+      IdSelector("foo", Full(AttrAppendSubNode("dog")))
  101
+    }
  102
+
  103
+    "select an id with no star attr append subnodes" in {
  104
+      CssSelectorParser.parse("#foo  [woof+] ").open_! must_==
  105
+      IdSelector("foo", Full(AttrAppendSubNode("woof")))
  106
+    }
  107
+
  108
+    "select an id with attr append subnodes" in {
  109
+      CssSelectorParser.parse("#foo  *[dog!] ").open_! must_==
  110
+      IdSelector("foo", Full(AttrRemoveSubNode("dog")))
  111
+    }
  112
+
  113
+    "select an id with no star attr append subnodes" in {
  114
+      CssSelectorParser.parse("#foo  [woof!] ").open_! must_==
  115
+      IdSelector("foo", Full(AttrRemoveSubNode("woof")))
  116
+    }
  117
+
  118
+
  119
+
98 120
     "select attr/val pair" in {
99 121
       CssSelectorParser.parse("frog=dog") must_==
100 122
       Full(AttrSelector("frog", "dog", Empty))

0 notes on commit b0b7478

Please sign in to comment.
Something went wrong with that request. Please try again.