Add :nth-child selector
Mon Nov 30 04:09:10 UTC 2009 pix@kepibu.org
* Add :nth-child selector
diff -rN -u old-Oh, Ducks!/notes new-Oh, Ducks!/notes
--- old-Oh, Ducks!/notes 2013-07-24 10:31:19.000000000 +0000
+++ new-Oh, Ducks!/notes 2013-07-24 10:31:19.000000000 +0000
@@ -115,9 +115,9 @@
* [ ] selectors involving descendants
** write documentation
** improve selector support
-*** positional selectors [2/12]
+*** positional selectors [3/12]
* [X] :nth-child(n)
- * [ ] :nth-child(xn+y)
+ * [X] :nth-child(xn+y)
* [ ] :nth-last-child
* [X] :first-child
* [ ] :last-child
diff -rN -u old-Oh, Ducks!/selectors.lisp new-Oh, Ducks!/selectors.lisp
--- old-Oh, Ducks!/selectors.lisp 2013-07-24 10:31:19.000000000 +0000
+++ new-Oh, Ducks!/selectors.lisp 2013-07-24 10:31:19.000000000 +0000
@@ -34,6 +34,7 @@
(defclass id-selector (simple-selector) ())
(defclass class-selector (simple-selector) ())
(defclass nth-child-selector (simple-selector) ())
+(defclass nth-last-child-selector (nth-child-selector) ())
(defmethod print-object ((selector universal-selector) stream)
(format stream "#<universal-selector>"))
@@ -53,10 +54,25 @@
(#T(regexp$ "[ ]+" ())
(list (make-instance 'descendant-combinator :matcher (parse-selector &rest))))
;; simple selectors
- (#T(regexp$ ":nth-child\\([ ]*([0-9]+)[ ]*\\)" (?n))
- (cons (make-instance 'nth-child-selector :arg (parse-integer n)) (parse-selector &rest)))
+ ;; FIXME: fix cl-unification so it can handle non-matching groups,
+ ;; so we can merge all these nth-child-selector variants
+ ;; into one or two
+ (#T(regexp$ ":nth-child\\([ ]*([+-]?[0-9]+)n[ ]*([+-]?[0-9]+)[ ]*\\)" (?a ?b))
+ (cons (make-instance 'nth-child-selector :arg (cons (parse-integer a) (parse-integer b))) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*([+-]?[0-9]+)n[ ]*\\)" (?a))
+ (cons (make-instance 'nth-child-selector :arg (cons (parse-integer a) 0)) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*n[ ]*([+-]?[0-9]+)[ ]*\\)" (?b))
+ (cons (make-instance 'nth-child-selector :arg (cons 1 (parse-integer b))) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*-n[ ]*([+-]?[0-9]+)[ ]*\\)" (?b))
+ (cons (make-instance 'nth-child-selector :arg (cons -1 (parse-integer b))) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*([+-]?[0-9]+)[ ]*\\)" (?b))
+ (cons (make-instance 'nth-child-selector :arg (cons 0 (parse-integer b))) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*odd[ ]*\\)" ())
+ (cons (make-instance 'nth-child-selector :arg (cons 2 1)) (parse-selector &rest)))
+ (#T(regexp$ ":nth-child\\([ ]*even[ ]*\\)" ())
+ (cons (make-instance 'nth-child-selector :arg (cons 2 0)) (parse-selector &rest)))
(#T(regexp$ ":first-child" ())
- (cons (make-instance 'nth-child-selector :arg 1) (parse-selector &rest)))
+ (cons (make-instance 'nth-child-selector :arg (cons 0 1)) (parse-selector &rest)))
(#T(regexp$ "[#](\\w+)" (?id))
(cons (make-instance 'id-selector :arg id) (parse-selector &rest)))
(#T(regexp$ "[\\.](\\w+)" (?class))
@@ -88,8 +104,20 @@
(defmethod element-matches-p (element (selector nth-child-selector))
(when-let* ((parent (element-parent element))
- (pos (position element (element-children parent) :test #'eq)))
- (= (selector-arg selector) (1+ pos))))
+ (pos (position element (funcall (typecase selector
+ (nth-last-child-selector #'reverse)
+ (nth-child-selector #'identity))
+ (element-children parent)) :test #'eq)))
+ (let ((pos (1+ pos))
+ (a (car (selector-arg selector)))
+ (b (cdr (selector-arg selector))))
+ ;; pos = An + B
+ (cond
+ ;; pos = 0n + B
+ ((= 0 a) (= b pos))
+ ;; (pos - B)/A = n
+ (t (and (zerop (mod (- pos b) a))
+ (not (minusp (/ (- pos b) a)))))))))
(defmethod element-matches-p (element (selector class-selector))
(member (selector-arg selector)