element-parent now works in lhtml --> to head
Sun Jul 3 08:25:45 UTC 2011 pix@kepibu.org
* Add support for XMLS-style lists, conflicting with LHTML-style lists
hunk ./notes 54
+** Differentiating between LHTML lists and XMLS lists
+While it would, in theory, be possible to inspect lists and determine if they
+are LHTML or XMLS lists, this is not currently done. You can, however, choose
+which type you'd like to work with by pushing =:lists-are-xmls= or
+=:lists-are-lhtml= to =*features*= before loading "Oh, Ducks!".
hunk ./notes 60
+Unfortunately, this means you can only expect to use one list type in a single
+lisp image. Patches to either automagically detect the list type, or to provide
+layered functions are welcome.
hunk ./oh-ducks.asd 35
- :components ((:file "lhtml")
+ :components (#-lists-are-xmls (:file "lhtml")
hunk ./oh-ducks.asd 42
- :components ((:file "dom")))))
+ :components ((:file "dom")
+ #-lists-are-lhtml (:file "xmls")))))
+
+;; In case you're wondering, we check the inverse of the :lists-are-* keywords
+;; so, in the event you only load cxml (or chtml), and don't specify which
+;; format lists are expected to take, you get the appropriate list operation by
+;; default.
addfile ./traversal/xmls.lisp
hunk ./traversal/xmls.lisp 1
+;;; WARNING: This conflicts with lhtml.
+(in-package #:oh-ducks.traversal)
+
+(defvar *xmls-family-tree* nil)
+
+(defun in-hash (key hash)
+ (multiple-value-bind (val present-p) (gethash key hash)
+ (declare (ignore val))
+ present-p))
+
+(defun %mark-parents (parent children)
+ (dolist (item children)
+ (setf (gethash item *xmls-family-tree*) parent)
+ (%mark-parents item (element-children item))))
+
+;; WARNING: This won't produce sane results for nested (match)es, because we
+;; have no way to bind in a large enough scope.
+(defmethod unify:unify ((template oh-ducks::css-selector-template)
+ (element list)
+ &optional (env (unify:make-empty-environment))
+ &key)
+ (if (and *xmls-family-tree*
+ (in-hash element *xmls-family-tree*))
+ (call-next-method)
+ (let ((*xmls-family-tree* (make-hash-table :test 'eq)))
+ (%mark-parents nil (list element))
+ (%mark-parents element (element-children element))
+ (call-next-method))))
+
+(defmethod unify:unify ((document list) (template oh-ducks::css-selector-template)
+ &optional (env (unify:make-empty-environment))
+ &key)
+ (unify:unify template document env))
+
+;;; general accessors
+
+(defmethod element-children ((element list))
+ (remove-if-not (lambda (x) (and (listp x) (stringp (car x))))
+ (cddr element)))
+
+(defmethod element-parent ((element list))
+ (multiple-value-bind (parent present?)
+ (gethash element *xmls-family-tree*)
+ (if present?
+ parent
+ (error "unable to determine parent"))))
+
+#+(or)
+(defmethod element-attribute ((attribute symbol) (element list))
+ (cadr (assoc attribute (cadr element))))
+(defmethod element-attribute ((attribute string) (element list))
+ (cadr (assoc attribute (cadr element) :test #'string=)))
+
+(defmethod element-type ((element list))
+ (car element))
+
+(defmethod element-content ((element list))
+ (cddr element))
Sun Jul 3 07:55:18 UTC 2011 pix@kepibu.org
* Minimal support for attribute-starts-with selector
hunk ./selectors.lisp 49
+(defclass attribute-starts-with-selector (attribute-selector) ())
hunk ./selectors.lisp 119
+ (#T(regexp$ ("[" $name "^=" $name "]") (?attribute ?value))
+ (cons (make-instance 'attribute-starts-with-selector :arg attribute :value value)
+ (parse-selector &rest)))
hunk ./selectors.lisp 287
+(defmethod subject-p ((selector attribute-starts-with-selector) element)
+ (when-let* ((val (element-attribute (selector-arg selector) element)))
+ (alexandria:starts-with-subseq (string-downcase (attribute-value selector)) (string-downcase val))))
+
Sun Jun 5 21:44:21 UTC 2011 pix@kepibu.org
* Update notes file
hunk ./notes 170
+Submitted. Was it ever accepted? Man, I don't remember.
hunk ./notes 181
+** Element structure templates
+For instance, sometimes it'd be nice to stuff the value of an attribute into a
+variable, like so:
+: (match #t(attr ("href" ?href) ("name" ?name)) "<a href='url' name='link'></a>"
+: (values href name)) =>
+: "url", "link"
+While it's certainly easy enough to do that using, say, XMLS-style lists, a
+general object-model-agnostic method would seem to be preferrable.
+** Layered functions so LHTML vs. XMLS support can be switched at runtime
Tue Apr 5 00:14:51 UTC 2011 pix@kepibu.org
* depend-on cl-unification-lib to work with stock cl-unification
hunk ./oh-ducks.asd 20
- :depends-on (:cl-unification :cl-ppcre :split-sequence :alexandria)
+ :depends-on (:cl-unification-lib :cl-unification :cl-ppcre :split-sequence :alexandria)
Wed Feb 10 08:50:16 UTC 2010 pix@kepibu.org
* Add attribute-equal selector
hunk ./notes 158
-*** attribute selectors [1/7]
+*** attribute selectors [2/7]
hunk ./notes 160
- * [ ] attribute-equal [att=val]
+ * [X] attribute-equal [att=val]
hunk ./selectors.lisp 45
-(defclass attribute-selector (simple-selector) ())
+(defclass attribute-selector (simple-selector)
+ ((val :reader attribute-value :initarg :value)))
hunk ./selectors.lisp 48
+(defclass attribute-equal-selector (attribute-selector) ())
hunk ./selectors.lisp 115
+ (#T(regexp$ ("[" $name "=" $name "]") (?attribute ?value))
+ (cons (make-instance 'attribute-equal-selector :arg attribute :value value)
+ (parse-selector &rest)))
hunk ./selectors.lisp 279
+(defmethod subject-p ((selector attribute-equal-selector) element)
+ (when-let* ((val (element-attribute (selector-arg selector) element)))
+ (string= val (attribute-value selector))))
+
hunk ./tests.lisp 160
+(serialize-values
+ (match (#T(html (:model dom)
+ ("[id=foo]" . ?id))
+ "<div><i id='bar'>bar id</i><i>no id</i><i id='foo'>foo id</i></div>")
+ id))
+
Wed Feb 10 08:28:34 UTC 2010 pix@kepibu.org
* Add attribute-present selector
hunk ./notes 158
-*** attribute selectors [0/7]
- * [ ] attribute-present [att]
+*** attribute selectors [1/7]
+ * [X] attribute-present [att]
hunk ./selectors.lisp 45
+(defclass attribute-selector (simple-selector) ())
+(defclass attribute-present-selector (attribute-selector) ())
+
hunk ./selectors.lisp 109
+ ;; attribute selectors
+ (#T(regexp$ ("[" $name "]") (?attribute))
+ (cons (make-instance 'attribute-present-selector :arg attribute)
+ (parse-selector &rest)))
hunk ./selectors.lisp 271
+(defmethod subject-p ((selector attribute-present-selector) element)
+ (element-attribute (selector-arg selector) element))
+
hunk ./tests.lisp 154
+(serialize-values
+ (match (#T(html (:model dom)
+ ("[id]" . ?ids))
+ "<div><i id=''>blank id</i>foo<b>no id</b>bar<i id='id'>id id</i></div>")
+ ids))
Wed Feb 10 08:27:56 UTC 2010 pix@kepibu.org
* Serialize returned tags so it's easier to see what was returned
hunk ./tests.lisp 44
-(match (#T(html (:model dom)
- ("i" . #t(list ?j ?i))
- ("span>i" . ?span))
- "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
- (values i span))
+(defun make-dom-document (child-node)
+ (make-instance 'rune-dom::document :children (rune-dom::make-node-list (list child-node))))
hunk ./tests.lisp 47
-(match (#T(html (:model dom)
- ("div:first-child" . ?div)
- ("i:nth-child(1)" . ?i))
- "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
- (values div i))
+(defun serialize (object)
+ (let ((document
+ (etypecase object
+ (rune-dom::document object)
+ (rune-dom::element (make-dom-document object))
+ (chtml:pt object)
+ (list object))))
+ (etypecase document
+ (rune-dom::document
+ (dom:map-document (cxml:make-string-sink :omit-xml-declaration-p t)
+ document))
+ (chtml:pt
+ (chtml:serialize-pt document (chtml:make-string-sink)))
+ (list (mapcar #'serialize document)))))
hunk ./tests.lisp 62
-(match (#T(html (:model dom)
- ("div:nth-last-child(1)" . ?div)
- ("div:last-child" . ?d2))
- "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
- (values div d2))
+(defmacro serialize-values (form)
+ `(let ((values (multiple-value-list ,form)))
+ (values-list (mapcar #'serialize values))))
hunk ./tests.lisp 66
-(match (#t(html (:model dom)
- (":nth-last-of-type(2)" . ?first)
- (":nth-of-type(2)" . ?last))
- "<div><span>1</span><i>i</i><span>2</span><i>i</i></div>")
- (values first last))
+(equal '("<i>cheese</i>" "<i>cheese</i>")
+ (serialize-values
+ (match (#T(html (:model dom)
+ ("i" . #t(list ?j ?i))
+ ("span>i" . ?span))
+ "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ (values i span))))
hunk ./tests.lisp 74
-(match (#T(html (:model dom)
- ("i:only-child" . ?i)
- ("i:only-of-type" . ?i-type))
- "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
- (values i i-type))
+(serialize-values
+ (match (#T(html (:model dom)
+ ("div:first-child" . ?div)
+ ("i:nth-child(1)" . ?i))
+ "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ (values div i)))
+
+(serialize-values
+ (match (#T(html (:model dom)
+ ("div:nth-last-child(1)" . ?div)
+ ("div:last-child" . ?d2))
+ "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ (values div d2)))
+
+(serialize-values
+ (match (#t(html (:model dom)
+ (":nth-last-of-type(2)" . ?first)
+ (":nth-of-type(2)" . ?last))
+ "<div><span>1</span><i>i</i><span>2</span><i>i</i></div>")
+ (values first last)))
hunk ./tests.lisp 95
-;; throws 'unification-failure
hunk ./tests.lisp 97
- "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
hunk ./tests.lisp 100
-(match (#T(html (:model dom)
- ("b + i" . ?i))
- "<div>I <b>really</b> <i>like</i> cheese. Do you not <i>dislike</i> cheese?</div>")
- (values i))
+;; throws 'unification-failure
+(serialize-values
+ (match (#T(html (:model dom)
+ ("i:only-child" . ?i)
+ ("i:only-of-type" . ?i-type))
+ "<div>I do <i>not</i> <i>like</i> cheese.</div><div><span><i>I</i> like <i>cheese</i>.</span></div>")
+ (values i i-type)))
hunk ./tests.lisp 108
-(match (#T(html (:model dom)
- ("b ~ i" . ?i))
- "<div>I <i>really</i> <b>like</b> cheese. Do you not <i>dislike</i> cheese?</div>")
- (values i))
+(serialize-values
+ (match (#T(html (:model dom)
+ ("b + i" . ?i))
+ "<div>I <b>really</b> <i>like</i> cheese. Do you not <i>dislike</i> cheese?</div>")
+ (values i)))
+
+(serialize-values
+ (match (#T(html (:model dom)
+ ("b ~ i" . ?i))
+ "<div>I <i>really</i> <b>like</b> cheese. Do you <i>not</i> <i>dislike</i> cheese?</div>")
+ (values i)))
hunk ./tests.lisp 120
-(match (#T(html (:model pt)
- ("body :empty" . ?empty))
- "<div><p><br></p><p>testing<i>i</i>testing</p></div>")
- (values empty))
+(serialize-values
+ (match (#T(html (:model pt)
+ ("body :empty" . ?empty))
+ "<div><p><br></p><p>testing<i>i</i>testing</p></div>")
+ (values empty)))
hunk ./tests.lisp 129
-(match (#T(html (:model dom)
- ("q" . ?q))
- "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i></div>")
- (match (#t(html ("> i" . ?i))
- (first q))
- i))
+(serialize-values
+ (match (#T(html (:model dom)
+ ("q" . ?q))
+ "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i></div>")
+ (match (#t(html ("> i" . ?i))
+ (first q))
+ i)))
hunk ./tests.lisp 138
-(match (#T(html (:model dom)
- ("q" . ?q))
- "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i><q></q><i>not match</i></div>")
- (match (#t(html ("+ i" . ?i))
- (first q))
- i))
+(serialize-values
+ (match (#T(html (:model dom)
+ ("q" . ?q))
+ "<div><i>ham</i> foo <q>bar <i>baz</i></q> quuz <i>spam</i><q></q><i>not match</i></div>")
+ (match (#t(html ("+ i" . ?i))
+ (first q))
+ i)))
hunk ./tests.lisp 146
-(match (#T(html (:model dom)
- ("q" . ?q))
- "<div> foo <q>outer q <i>baz <q>inner q</q></i></q> quuz</div>")
- (match (#t(html ("q" . ?i))
- (first q))
- i))
+(serialize-values
+ (match (#T(html (:model dom)
+ ("q" . ?q))
+ "<div> foo <q>outer q <i>baz <q>inner q</q></i></q> quuz</div>")
+ (match (#t(html ("q" . ?i))
+ (first q))
+ i)))
Wed Feb 10 08:26:34 UTC 2010 pix@kepibu.org
* Formatting.
hunk ./tests.lisp 10
- "<div id=\"id\">I <i>like</i> cheese.</div>")
+ "<div id=\"id\">I <i>like</i> cheese.</div>")
Wed Feb 10 08:26:25 UTC 2010 pix@kepibu.org
* Use named-readtables instead of set-dispatch-macro-character
hunk ./tests.lisp 2
+(named-readtables:in-readtable template-readtable)
hunk ./tests.lisp 6
-#.(set-dispatch-macro-character #\# #\T 'unify::|sharp-T-reader|)
-
Wed Feb 10 08:20:45 UTC 2010 pix@kepibu.org
* Return NIL if attribute was not present
hunk ./traversal/dom.lisp 26
- (dom:get-attribute element attribute))
+ (when-let* ((attribute-node (dom:get-attribute-node element attribute)))
+ (dom:value attribute-node)))
Sun Feb 7 09:21:16 UTC 2010 pix@kepibu.org
* Update notes to reflect updates to cl-unification.
hunk ./notes 14
+ * named-readtables
hunk ./notes 35
+or
+: (unify:enable-template-reader)
+or
+: (named-readtables:in-readtable unify:template-readtable)
hunk ./notes 40
+(The latter two currently only work if you have cl-unification from my
+darcs repo.)
hunk ./notes 81
- "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
Mon Jan 4 07:11:36 UTC 2010 pix@kepibu.org
* element-parent now works in lhtml
hunk ./tests.lisp 26
- (match (#T(html ("div" ("i" . ?i)))
- "<div>I do <i>not</i> like cheese.</div><div>I like <i>cheese</i>.</div>")
+ (match (#T(html (:model lhtml)
+ ("div" ("i" . ?i)))
+ "<div>I do <i>not</i> like cheese.</div><div>I like <i>cheese</i>.</div>")
hunk ./tests.lisp 32
- (match (#T(html (:model dom)
+ (match (#T(html (:model lhtml)
hunk ./tests.lisp 34
- "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
hunk ./tests.lisp 38
- (match (#T(html (:model dom)
+ (match (#T(html (:model lhtml)
hunk ./tests.lisp 42
- "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
+ "<div>I do <i>not</i> like cheese.</div><div><span>I like <i>cheese</i>.</span></div>")
hunk ./unify.lisp 40
- ((ignore-errors (element-parent document)) ; bleh. element-parent breaks lhtml nodes
+ ((element-parent document)