Changeset 71

Show
Ignore:
Timestamp:
08/26/08 10:50:29 (3 months ago)
Author:
achiumenti
Message:

CLAW html framework

Location:
trunk/main/claw-html
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • trunk/main/claw-html/claw-html.asd

    r66 r71  
    4242                                     ;(:file "logger" :depends-on ("misc")) 
    4343                                     ;(:file "session-manager" :depends-on ("misc")) 
    44                                      (:file "tags" :depends-on ("packages")) 
    45                                      (:file "meta" :depends-on ("packages")) 
     44                                     (:file "meta" :depends-on ("packages")) 
     45                                     (:file "tags" :depends-on ("packages" "meta"))                                      
    4646                                     (:file "components" :depends-on ("tags" "meta")) 
    4747                                     (:file "validators" :depends-on ("components")) 
  • trunk/main/claw-html/src/components.lisp

    r66 r71  
    6262             :accessor action 
    6363             :documentation "Function performed after user submission") 
     64     (action-object :initarg :action-object 
     65                    :accessor action-object 
     66                    :documentation "The object that will be applied to the ACTION property") 
    6467     (css-class :initarg :class 
    6568                :reader css-class 
     
    6871             :reader form-method 
    6972             :documentation "Form post method (may be \"get\" or \"post\")")) 
    70   (:default-initargs :action nil :class nil :method "post") 
     73  (:default-initargs :action nil :class nil :method "post" :action-object nil) 
    7174  (:documentation "Internal use component")) 
    7275 
     
    7780               action 
    7881               (cform-rewinding-p obj pobj)) 
    79       (funcall action pobj)))) 
     82      (funcall action (or (action-object obj) pobj))))) 
    8083 
    8184(defmethod cform-rewinding-p ((cform _cform) (page page)) 
     
    214217(defclass cinput (base-cinput) 
    215218    ((input-type :initarg :type 
    216                 :reader input-type 
    217                 :documentation "The html <input> TYPE attribute. For submit type, use the CSUBMIT> function.")) 
     219                 :reader input-type 
     220                 :documentation "The html <input> TYPE attribute. For submit type, use the CSUBMIT> function.")) 
    218221    (:metaclass metacomponent) 
    219222    (:default-initargs :reserved-parameters (list :value :name) :empty t :type "text") 
     
    255258          (validator (validator cinput)) 
    256259          (value (translator-decode (translator cinput) cinput))) 
    257 ;      (log-message :info "********************* ~a : ~a" cinput value) 
    258260      (unless (or (null value) (component-validation-errors cinput)) 
    259261        (when validator 
     
    368370          (submitted-p (page-req-parameter pobj (htcomponent-client-id obj)))) 
    369371      (unless (or (null current-form) (null submitted-p) (null action)) 
    370         (setf (action current-form) action))))) 
     372        (setf (action current-form) action 
     373              (action-object current-form) (or (action-object obj) (action-object current-form))))))) 
    371374 
    372375;----------------------------------------------------------------------------- 
  • trunk/main/claw-html/src/packages.lisp

    r66 r71  
    4848 
    4949           ;#:duplicate-back-slashes 
     50           #:attribute-value 
    5051           #:build-tagf 
    5152           #:page 
     53           #:page-before-render 
    5254           #:page-render 
    5355           #:make-page-renderer 
     
    8183           #:$raw> 
    8284                                        ;empty tags definition 
     85           #:*empty-tags* 
    8386           #:area> 
    8487           #:base> 
  • trunk/main/claw-html/src/tags.lisp

    r66 r71  
    6565 - PAGE is the page instance that must be given")) 
    6666 
     67(defgeneric page-before-render (page) 
     68  (:documentation "This method is called as first instruction of PAGE-RENDER. 
     69 - PAGE is the page instance that must be given")) 
     70 
    6771(defgeneric page-init-injections (page) 
    6872  (:documentation "This internal method is called during the request cycle phase to reset page slots that 
     
    248252 
    249253(defvar *claw-current-page* nil 
    250     "The CLAW page currently rendering") 
    251  
    252 (defvar *id-table-map*  
     254  "The CLAW page currently rendering") 
     255 
     256(defvar *id-table-map* (make-hash-table :test 'equal) 
    253257  "Holds an hash table of used components/tags id as keys and the number of their occurrences as values. 
    254258So if you have a :id \"compId\" given to a previous component, the second 
     
    262266  "*FILE-TRANSLATOR* is the default translator for any CINPUT component of type \"file\".") 
    263267 
    264  
     268(defstruct list-for-tag-attribute 
     269  "Since tag attributes values are flattened, it is impossible to pass lists as values. Use this struct to pass lists to values" 
     270  (value nil)) 
     271 
     272(defun attribute-value (value) 
     273  "Creates an unflattenable value for tag attributes. This is particularly useful when you need to pass a list as an attribute value" 
     274  (make-list-for-tag-attribute :value value)) 
    265275 
    266276(defun flatten (tree &optional result-list) 
     
    291301                   (or (keywordp elem) 
    292302                       (keywordp last-elem))) 
    293               (push elem attributes) 
     303              (push (or (when (list-for-tag-attribute-p elem) (list-for-tag-attribute-value elem)) elem) attributes) 
    294304              (when elem 
    295305                (push elem body)))) 
     
    357367 
    358368;;;---------------------------------------------------------------- 
    359 #| 
    360 (defclass message-dispatcher () 
    361   () 
    362   (:documentation "This is and interface for message dispatchers")) 
    363  
    364 (defclass simple-message-dispatcher (message-dispatcher) 
    365   ((locales :initform (make-hash-table :test #'equal) 
    366             :accessor simple-message-dispatcher-locales 
    367             :documentation "Hash table of locales strings and KEY/VALUE message pairs")) 
    368   (:documentation "A message disptcher that leave data unchanged during encoding and decoding phases.")) 
    369  
    370 (defclass i18n-aware (message-dispatcher) 
    371   ((message-dispatcher :initarg :message-dispatcher 
    372                        :accessor message-dispatcher 
    373                        :documentation "Reference to a MESSAGE-DISPATCHER instance")) 
    374   (:default-initargs :message-dispatcher nil) 
    375   (:documentation "All classes that need to dispatch messages are subclasses of I18N-AWARE")) 
    376 |# 
    377369 
    378370(defclass page() 
     
    413405                    :documentation "http request post parameters") 
    414406   (get-parameters :initarg :get-parameters 
    415                     :reader page-get-parameters 
    416                     :documentation "http request get parameters") 
     407                   :reader page-get-parameters 
     408                   :documentation "http request get parameters") 
    417409   (components-stack :initform nil 
    418410                     :accessor page-components-stack 
     
    425417                             :documentation "Symbol for page charset encoding \(Such as UTF-8)") 
    426418   (injection-writing-p :initform nil 
    427                        :accessor page-injection-writing-p 
    428                        :documentation "Flag that becomes true when rendering page injections")) 
     419                        :accessor page-injection-writing-p 
     420                        :documentation "Flag that becomes true when rendering page injections")) 
    429421  (:default-initargs :writer t 
    430422    :external-format-encoding :utf-8 
     
    445437  "Generates a lambda function from PAGE-RENDER method, that may be used into LISPLET-REGISTER-FUNCTION-LOCATION" 
    446438  #'(lambda () (with-output-to-string (*standard-output*) 
    447                  (page-render (make-instance page-class :post-parameters http-post-parameters :get-parameters http-get-parameters))))) 
     439                 (page-render (make-instance page-class  
     440                                             :post-parameters (if (functionp http-post-parameters) 
     441                                                                  (funcall http-post-parameters) 
     442                                                                  http-post-parameters)  
     443                                             :get-parameters (if (functionp http-get-parameters) 
     444                                                                 (funcall http-get-parameters) 
     445                                                                 http-get-parameters)))))) 
    448446 
    449447(defclass htcomponent () 
     
    662660    (subseq js-array 0 (1- (length js-array))))) 
    663661 
     662(defmethod page-before-render ((page page)) 
     663  nil) 
     664 
    664665(defmethod page-render ((page page)) 
    665666  (let ((*claw-current-page* page) 
    666         (*id-table-map* nil) 
     667        (*id-table-map* (make-hash-table :test 'equal)) 
    667668        (*validation-errors* nil) 
    668669        (*validation-compliances* nil) 
    669         (body (page-content page)) 
    670670        (jsonp (page-json-id-list page))) 
    671     (if (null body) 
    672         (format nil "null body for page ~a~%" (type-of page)) 
    673         (progn 
    674           (page-init page) 
    675           (when (page-req-parameter page *rewind-parameter*) 
    676             (htcomponent-rewind body page)) 
    677           (page-init page) 
    678           (htcomponent-prerender (page-content page) page) ;Here we need a fresh new body!!! 
    679           (page-render-headings page) 
    680           (page-init page) 
    681           (when jsonp 
    682             (page-format-raw page (page-json-prefix page)) 
    683             (page-format-raw page "{components:{")) 
    684           (htcomponent-render (page-content page) page) ;Here we need a fresh new body!!! 
    685           (when jsonp 
    686             (page-format-raw page "},classInjections:\"") 
    687             (setf (page-can-print page) t 
    688                   (page-injection-writing-p page) t) 
    689             (dolist (injection (page-init-injections page)) 
    690               (when injection 
    691                 (htcomponent-render injection page))) 
    692             (page-format-raw page "\",instanceInjections:\"") 
    693             (let ((init-scripts (htbody-init-scripts-tag page))) 
    694               (when init-scripts 
    695                 (htcomponent-render init-scripts page))) 
    696             (page-format-raw page "\",errors:") 
    697             (page-format-raw page (json-validation-errors)) 
    698             (page-format-raw page ",valid:") 
    699             (page-format-raw page (json-validation-compliances)) 
    700             (page-format-raw page "}") 
    701             (page-format-raw page (page-json-suffix page))))))) 
     671    (progn 
     672      (page-init page) 
     673      (page-before-render page) 
     674      (when (page-req-parameter page *rewind-parameter*) 
     675        (htcomponent-rewind (page-content page) page)) 
     676      (page-init page) 
     677      (htcomponent-prerender (page-content page) page) ;Here we need a fresh new body!!! 
     678      (page-render-headings page) 
     679      (page-init page) 
     680      (when jsonp 
     681        (page-format-raw page (page-json-prefix page)) 
     682        (page-format-raw page "{components:{")) 
     683      (htcomponent-render (page-content page) page) ;Here we need a fresh new body!!! 
     684      (when jsonp 
     685        (page-format-raw page "},classInjections:\"") 
     686        (setf (page-can-print page) t 
     687              (page-injection-writing-p page) t) 
     688        (dolist (injection (page-init-injections page)) 
     689          (when injection 
     690            (htcomponent-render injection page))) 
     691        (page-format-raw page "\",instanceInjections:\"") 
     692        (let ((init-scripts (htbody-init-scripts-tag page))) 
     693          (when init-scripts 
     694            (htcomponent-render init-scripts page))) 
     695        (page-format-raw page "\",errors:") 
     696        (page-format-raw page (json-validation-errors)) 
     697        (page-format-raw page ",valid:") 
     698        (page-format-raw page (json-validation-compliances)) 
     699        (page-format-raw page "}") 
     700        (page-format-raw page (page-json-suffix page)))))) 
    702701 
    703702(defmethod page-body-init-scripts ((page page)) 
     
    758757;;;========= HTCOMPONENT ============================ 
    759758(defmethod htcomponent-can-print ((htcomponent htcomponent)) 
    760   (let* ((id (when (slot-boundp htcomponent 'client-id) (htcomponent-client-id htcomponent))) 
    761          (page (htcomponent-page htcomponent)) 
    762          (print-status (page-can-print page)) 
    763          (validation-errors *validation-errors*) 
    764          (json-render-on-validation-errors-p (htcomponent-json-render-on-validation-errors-p htcomponent)) 
    765          (render-p (or (and (member id (page-json-id-list page) :test #'string=) 
    766                             (null validation-errors)) 
    767                        print-status))) 
    768     (or json-render-on-validation-errors-p print-status render-p))) 
     759(let* ((id (when (slot-boundp htcomponent 'client-id)  
     760             (htcomponent-client-id htcomponent))) 
     761       (page (htcomponent-page htcomponent)) 
     762       (print-status (page-can-print page)) 
     763       (validation-errors *validation-errors*) 
     764       (json-render-on-validation-errors-p (htcomponent-json-render-on-validation-errors-p htcomponent)) 
     765       (render-p (or (and (member id (page-json-id-list page) :test #'string=) 
     766                          (null validation-errors)) 
     767                     print-status))) 
     768  (or json-render-on-validation-errors-p print-status render-p))) 
    769769 
    770770(defmethod htcomponent-json-print-start-component ((htcomponent htcomponent)) 
    771   (let* ((page (htcomponent-page htcomponent)) 
    772          (jsonp (page-json-id-list page)) 
    773          (id (when (slot-boundp htcomponent 'client-id) (htcomponent-client-id htcomponent))) 
    774          (validation-errors *validation-errors*)) 
    775     (when (and jsonp 
    776                (or (and (null validation-errors) 
    777                         (member id jsonp :test #'string-equal)) 
    778                    (htcomponent-json-render-on-validation-errors-p htcomponent))) 
    779       (when (> (page-json-component-count page) 0) 
    780         (page-format page ",")) 
    781       (page-format-raw page "~a:\"" id) 
    782       (push id (page-json-component-id-list page)) 
    783       (incf (page-json-component-count page))))) 
     771(let* ((page (htcomponent-page htcomponent)) 
     772       (jsonp (page-json-id-list page)) 
     773       (id (when (slot-boundp htcomponent 'client-id)  
     774             (htcomponent-client-id htcomponent))) 
     775       (validation-errors *validation-errors*)) 
     776  (when (and jsonp 
     777             (or (and (null validation-errors) 
     778                      (member id jsonp :test #'string-equal)) 
     779                 (htcomponent-json-render-on-validation-errors-p htcomponent))) 
     780    (when (> (page-json-component-count page) 0) 
     781      (page-format page ",")) 
     782    (page-format-raw page "~a:\"" id) 
     783    (push id (page-json-component-id-list page)) 
     784    (incf (page-json-component-count page))))) 
    784785 
    785786(defmethod htcomponent-json-print-end-component ((htcomponent htcomponent)) 
    786   (let* ((page (htcomponent-page htcomponent)) 
    787          (jsonp (page-json-id-list page)) 
    788          (id (when (slot-boundp htcomponent 'client-id) (htcomponent-client-id htcomponent))) 
    789          (validation-errors *validation-errors*)) 
    790     (when (and jsonp 
    791                (or (and (null validation-errors) 
    792                         (member id jsonp :test #'string-equal)) 
    793                    (htcomponent-json-render-on-validation-errors-p htcomponent))) 
    794       (pop (page-json-component-id-list page)) 
    795       (page-format-raw page "\"")))) 
     787(let* ((page (htcomponent-page htcomponent)) 
     788       (jsonp (page-json-id-list page)) 
     789       (id (when (slot-boundp htcomponent 'client-id) (htcomponent-client-id htcomponent))) 
     790       (validation-errors *validation-errors*)) 
     791  (when (and jsonp 
     792             (or (and (null validation-errors) 
     793                      (member id jsonp :test #'string-equal)) 
     794                 (htcomponent-json-render-on-validation-errors-p htcomponent))) 
     795    (pop (page-json-component-id-list page)) 
     796    (page-format-raw page "\"")))) 
    796797 
    797798(defmethod htcomponent-rewind :before ((htcomponent htcomponent) (page page)) 
    798   (setf (htcomponent-page htcomponent) page) 
    799   (push htcomponent (page-components-stack page))) 
     799(setf (htcomponent-page htcomponent) page) 
     800(push htcomponent (page-components-stack page))) 
    800801 
    801802(defmethod htcomponent-prerender :before ((htcomponent htcomponent) (page page)) 
    802   (let ((render-condition (htcomponent-render-condition htcomponent))) 
    803     (unless (and render-condition (null (funcall render-condition))) 
    804       (setf (htcomponent-page htcomponent) page) 
    805       (push htcomponent (page-components-stack page))))) 
     803(let ((render-condition (htcomponent-render-condition htcomponent))) 
     804  (unless (and render-condition (null (funcall render-condition))) 
     805    (setf (htcomponent-page htcomponent) page) 
     806    (push htcomponent (page-components-stack page))))) 
    806807 
    807808(defmethod htcomponent-render :before ((htcomponent htcomponent) (page page)) 
    808   (let ((render-condition (htcomponent-render-condition htcomponent))) 
    809     (unless (and render-condition (null (funcall render-condition))) 
    810       (setf (htcomponent-page htcomponent) page) 
    811       (push htcomponent (page-components-stack page))))) 
     809(let ((render-condition (htcomponent-render-condition htcomponent))) 
     810  (unless (and render-condition (null (funcall render-condition))) 
     811    (setf (htcomponent-page htcomponent) page) 
     812    (push htcomponent (page-components-stack page))))) 
    812813 
    813814(defmethod htcomponent-rewind :after ((htcomponent htcomponent) (page page)) 
    814   (pop (page-components-stack page))) 
     815(pop (page-components-stack page))) 
    815816 
    816817(defmethod htcomponent-prerender :after ((htcomponent htcomponent) (page page)) 
    817   (let ((render-condition (htcomponent-render-condition htcomponent))) 
    818     (unless (and render-condition (null (funcall render-condition))) 
    819       (pop (page-components-stack page))))) 
     818(let ((render-condition (htcomponent-render-condition htcomponent))) 
     819  (unless (and render-condition (null (funcall render-condition))) 
     820    (pop (page-components-stack page))))) 
    820821 
    821822(defmethod htcomponent-render :after ((htcomponent htcomponent) (page page)) 
    822   (let ((render-condition (htcomponent-render-condition htcomponent))) 
    823     (unless (and render-condition (null (funcall render-condition))) 
    824       (pop (page-components-stack page))))) 
     823(let ((render-condition (htcomponent-render-condition htcomponent))) 
     824  (unless (and render-condition (null (funcall render-condition))) 
     825    (pop (page-components-stack page))))) 
    825826 
    826827(defmethod htcomponent-rewind ((htcomponent htcomponent) (page page)) 
    827   (dolist (tag (htcomponent-body htcomponent)) 
    828     (when (subtypep (type-of tag) 'htcomponent) 
    829       (htcomponent-rewind tag page)))) 
     828(dolist (tag (htcomponent-body htcomponent)) 
     829  (when (subtypep (type-of tag) 'htcomponent) 
     830    (htcomponent-rewind tag page)))) 
    830831 
    831832(defmethod htcomponent-prerender ((htcomponent htcomponent) (page page)) 
    832   (let ((previous-print-status (page-can-print page)) 
    833         (render-condition (htcomponent-render-condition htcomponent))) 
    834     (unless (and render-condition (null (funcall render-condition))) 
    835       (when (null previous-print-status) 
    836         (setf (page-can-print page) (htcomponent-can-print htcomponent))) 
    837       (dolist (tag (htcomponent-body htcomponent)) 
    838         (when (subtypep (type-of tag) 'htcomponent) 
    839           (htcomponent-prerender tag page))) 
    840       (when (null previous-print-status) 
    841         (setf (page-can-print page) nil))))) 
     833(let ((previous-print-status (page-can-print page)) 
     834      (render-condition (htcomponent-render-condition htcomponent))) 
     835  (unless (and render-condition (null (funcall render-condition))) 
     836    (when (null previous-print-status) 
     837      (setf (page-can-print page) (htcomponent-can-print htcomponent))) 
     838    (dolist (tag (htcomponent-body htcomponent)) 
     839      (when (subtypep (type-of tag) 'htcomponent) 
     840        (htcomponent-prerender tag page))) 
     841    (when (null previous-print-status) 
     842      (setf (page-can-print page) nil))))) 
    842843 
    843844(defmethod htcomponent-render ((htcomponent htcomponent) (page page)) 
    844   (let ((body-list (htcomponent-body htcomponent)) 
    845         (previous-print-status (page-can-print page)) 
    846         (render-condition (htcomponent-render-condition htcomponent))) 
    847     (unless (and render-condition (null (funcall render-condition))) 
    848       (when (null previous-print-status) 
    849         (setf (page-can-print page) (htcomponent-can-print htcomponent)) 
    850         (htcomponent-json-print-start-component htcomponent)) 
    851       (dolist (child-tag body-list) 
    852         (when child-tag 
    853           (cond 
    854             ((stringp child-tag) (htcomponent-render ($> child-tag) page)) 
    855             ((functionp child-tag) (htcomponent-render ($> (funcall child-tag)) page)) 
    856             (t (htcomponent-render child-tag page))))) 
    857       (when (null previous-print-status) 
    858         (setf (page-can-print page) nil) 
    859         (htcomponent-json-print-end-component htcomponent))))) 
     845(let ((body-list (htcomponent-body htcomponent)) 
     846      (previous-print-status (page-can-print page)) 
     847      (render-condition (htcomponent-render-condition htcomponent))) 
     848  (unless (and render-condition (null (funcall render-condition))) 
     849    (when (null previous-print-status) 
     850      (setf (page-can-print page) (htcomponent-can-print htcomponent)) 
     851      (htcomponent-json-print-start-component htcomponent)) 
     852    (dolist (child-tag body-list) 
     853      (when child-tag 
     854        (cond 
     855          ((stringp child-tag) (htcomponent-render ($> child-tag) page)) 
     856          ((functionp child-tag) (htcomponent-render ($> (funcall child-tag)) page)) 
     857          (t (htcomponent-render child-tag page))))) 
     858    (when (null previous-print-status) 
     859      (setf (page-can-print page) nil) 
     860      (htcomponent-json-print-end-component htcomponent))))) 
    860861 
    861862;;;========= TAG ===================================== 
    862863(defmethod tag-attributes ((tag tag)) 
    863   (htcomponent-attributes tag)) 
     864(htcomponent-attributes tag)) 
    864865 
    865866(defmethod tag-render-attributes ((tag tag) (page page)) 
    866   (when (htcomponent-attributes tag) 
    867     (loop for (k v) on (htcomponent-attributes tag) by #'cddr 
    868        do (progn 
    869             (assert (keywordp k)) 
    870             (when (and (functionp v) (not (eq k :render-condition))) 
    871               (setf v (funcall v))) 
    872             (when (numberp v) 
    873               (setf v (princ-to-string v))) 
    874             (when (and (not (eq k :render-condition)) v (string-not-equal v "")) 
    875               (page-format page " ~a=\"~a\"" 
    876                            (if (eq k :static-id) 
    877                                "id" 
    878                                (parenscript::symbol-to-js k)) 
    879                            (let ((s (if (eq k :id) 
    880                                         (prin1-to-string (htcomponent-client-id tag)) 
    881                                         (if (eq t v) 
    882                                             "\"true\"" 
    883                                             (prin1-to-string v))))) ;escapes double quotes 
    884                              (subseq s 1 (1- (length s)))))))))) 
     867(when (htcomponent-attributes tag) 
     868  (loop for (k v) on (htcomponent-attributes tag) by #'cddr 
     869     do (progn 
     870          (assert (keywordp k)) 
     871          (when (and (functionp v) (not (eq k :render-condition))) 
     872            (setf v (funcall v))) 
     873          (when (numberp v) 
     874            (setf v (princ-to-string v))) 
     875          (when (and (not (eq k :render-condition)) v (string-not-equal v "")) 
     876            (page-format page " ~a=\"~a\"" 
     877                         (if (eq k :static-id) 
     878                             "id" 
     879                             (parenscript::symbol-to-js k)) 
     880                         (let ((s (if (eq k :id) 
     881                                      (prin1-to-string (htcomponent-client-id tag)) 
     882                                      (if (eq t v) 
     883                                          "\"true\"" 
     884                                          (prin1-to-string v))))) ;escapes double quotes 
     885                           (subseq s 1 (1- (length s)))))))))) 
    885886 
    886887(defmethod tag-render-starttag ((tag tag) (page page)) 
    887   (let ((tagname (tag-name tag)) 
    888         (id (when (slot-boundp tag 'client-id) (htcomponent-client-id tag))) 
    889         (jsonp (page-json-id-list page)) 
    890         (emptyp (htcomponent-empty tag)) 
    891         (xml-p (page-xmloutput page)) 
    892         (injection-writing-p (page-injection-writing-p page))) 
    893     (setf (page-lasttag page) tagname) 
    894     (when (or injection-writing-p 
    895               (null jsonp) 
    896               (null (and jsonp  
    897                          (string= id (first (page-json-component-id-list page)))))) 
    898       (page-newline page) 
    899       (page-print-tabulation page) 
    900       (page-format page "<~a" tagname) 
    901       (tag-render-attributes tag page) 
    902       (if (null emptyp) 
     888(let ((tagname (tag-name tag)) 
     889      (id (when (slot-boundp tag 'client-id) (htcomponent-client-id tag))) 
     890      (jsonp (page-json-id-list page)) 
     891      (emptyp (htcomponent-empty tag)) 
     892      (xml-p (page-xmloutput page)) 
     893      (injection-writing-p (page-injection-writing-p page))) 
     894  (setf (page-lasttag page) tagname) 
     895  (when (or injection-writing-p 
     896            (null jsonp) 
     897            (null (and jsonp  
     898                       (string= id (first (page-json-component-id-list page)))))) 
     899    (page-newline page) 
     900    (page-print-tabulation page) 
     901    (page-format page "<~a" tagname) 
     902    (tag-render-attributes tag page) 
     903    (if (null emptyp) 
     904        (progn 
     905          (page-format page ">") 
     906          (incf (page-tabulator page))) 
     907        (if (null xml-p) 
     908            (page-format page ">") 
     909            (page-format page "/>")))))) 
     910 
     911(defmethod tag-render-endtag ((tag tag) (page page)) 
     912(let ((tagname (tag-name tag)) 
     913      (id (when (slot-boundp tag 'client-id) (htcomponent-client-id tag))) 
     914      (jsonp (page-json-id-list page)) 
     915      (previous-tagname (page-lasttag page)) 
     916      (emptyp (htcomponent-empty tag)) 
     917      (injection-writing-p (page-injection-writing-p page))) 
     918  (when (and (null emptyp) 
     919             (or injection-writing-p 
     920                 (null jsonp) 
     921                 (null (and jsonp  
     922                            (string= id (first (page-json-component-id-list page))))))) 
     923    (progn 
     924      (decf (page-tabulator page)) 
     925      (if (string= tagname previous-tagname) 
    903926          (progn 
    904             (page-format page ">") 
    905             (incf (page-tabulator page))) 
    906           (if (null xml-p) 
    907               (page-format page ">") 
    908               (page-format page "/>")))))) 
    909  
    910 (defmethod tag-render-endtag ((tag tag) (page page)) 
    911   (let ((tagname (tag-name tag)) 
    912         (id (when (slot-boundp tag 'client-id) (htcomponent-client-id tag))) 
    913         (jsonp (page-json-id-list page)) 
    914         (previous-tagname (page-lasttag page)) 
    915         (emptyp (htcomponent-empty tag)) 
    916         (injection-writing-p (page-injection-writing-p page))) 
    917     (when (and (null emptyp) 
    918                (or injection-writing-p 
    919                    (null jsonp) 
    920                    (null (and jsonp  
    921                               (string= id (first (page-json-component-id-list page))))))) 
    922       (progn 
    923         (decf (page-tabulator page)) 
    924         (if (string= tagname previous-tagname) 
    925             (progn 
    926               (page-format page "</~a>" tagname)) 
    927             (progn 
    928               (page-newline page) 
    929               (page-print-tabulation page) 
    930               (page-format page "</~a>" tagname))))) 
    931     (setf (page-lasttag page) nil))) 
     927            (page-format page "</~a>" tagname)) 
     928          (progn 
     929            (page-newline page) 
     930            (page-print-tabulation page) 
     931            (page-format page "</~a>" tagname))))) 
     932  (setf (page-lasttag page) nil))) 
    932933 
    933934(defmethod htcomponent-render ((tag tag) (page page)) 
    934   (let ((body-list (htcomponent-body tag)) 
    935         (previous-print-status (page-can-print page)) 
    936         (render-condition (htcomponent-render-condition tag))) 
    937     (unless (and render-condition (null (funcall render-condition))) 
    938       (when (null previous-print-status) 
    939         (setf (page-can-print page) (htcomponent-can-print tag)) 
    940         (htcomponent-json-print-start-component tag)) 
    941       (when (or (page-can-print page) previous-print-status) 
    942         (tag-render-starttag tag page)) 
    943       (dolist (child-tag body-list) 
    944         (when child-tag 
    945           (cond 
    946             ((stringp child-tag) (htcomponent-render ($> child-tag) page)) 
    947             ((functionp child-tag) (htcomponent-render ($> (funcall child-tag)) page)) 
    948             (t (htcomponent-render child-tag page))))) 
    949       (when (or (page-can-print page) previous-print-status) 
    950         (tag-render-endtag tag page)) 
    951       (unless previous-print-status 
    952         (setf (page-can-print page) nil)