[Bese-devel] say a warm welcome to ucw+, event-based ucw implementing ajax.

Lou Vanek vanek at acd.net
Tue Jul 11 23:16:35 UTC 2006


wow!

it took me awhile to process and integrate, but with not too much
effort it seems to do everything you say.

I've appended a new 02_dom file to get around an IE7b3 security
constraint. IE7 uses mime sniffing
(http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/mangxpsp2/mngieps.mspx)
so trying to load an ajax 'text/html' response in ie7 will immediately abort if
a javascript tag is parsed at read time. The 02 file also corrects
the problem of accidentally inserting a whitespace dom node instead of
the nodeType 1 dom node that contains the actual html/script/css.

The corresponding server-side code is slightly revised to sneak
the javascript past ie7 by embedding it in a DIV:

(defmethod render-ajax ((window ajax-window))
   (let ((dirty-components (context.dirty-components *context*)))
     (dolist (ajax-widget dirty-components)
	;;; walk dirty-components list and look for self's parent nodes. we
	;;; don't need to render html if a parent node already exists.
	;;; this is not the case with css, we render css if it's dirty
	;;; anyway.
       (let ((found-p nil))
		(dolist (parent-component dirty-components)
		  (when (and (not found-p)
				     (descendant-p parent-component ajax-widget t))
			(setf found-p t)))
	
		(<:div :class "component"
	      	   :id (dom-id ajax-widget)

	       (when (not found-p)
			   (<:div :class "html"
				 (render ajax-widget)))
			
		   (<:div :class "css"
			  (<:style :type "text/css"
				   (render-css ajax-widget)))))))

   ;; All javascript which is to be immediately evaluated is embedded within
   ;; a div to sneak past IE7's mime sniffer.
   ;; Otherwise IE7 will stop loading the file if the mime type isn't text/javascript.
   ;; (Dojo would try to immediately eval this entire script if it
   ;; thought the whole file was javascript, which it isn't because there's also html.)
   (<:div :class "script"
	(render-javascript window))

   ;; This works on ff but not ie7 (see above comment).
   ;(<:script :type "text/javascript"
;	    (<:as-is ~% "// <![CDATA[" ~%)
;	    (render-javascript window)
;	    (<:as-is  ~% "// ]]>" ~%))
   ) ;; render-ajax



;; Adding 'typeof' fixes an ie7 problem where if the javascript object has already been defined
;; jscript will error-out instead of just overwriting the previous object.
;; FF doesn't have this problem, but the fix wont hurt.
(defmethod render-javascript :before ((self ajax-widget))
   (<:ai
    (js:js*
     `(when (= (typeof ,(js-component-id self)) "undefined")
	   (setf ,(js-component-id self) (new (*object)))))))



In your test application you probably want to change ":www-root" to
":www-roots". That bug cost me almost 2 hours of my life. heh.




And you may want to revise the 01 file to something like the following.
This keeps the encoding from being set to 'NIL', which i had problems with.

	:call-action
	(lambda (action-id element-id)
	  (let ((url (+ window.location.pathname
					"?" ,+session-parameter-name+ "=" server.current-session
					"&" ,+frame-parameter-name+ "=" server.current-frame
					"&" ,+action-parameter-name+ "=" action-id))
			(enc      ,(or (and (             application.charset (context.application *context*))
								(symbol-name (application.charset (context.application *context*))))
						   "UTF-8")))
	    (dojo.debug "calling-action:" url)
	    (if (= "FORM" (slot-value ($ element-id) 'node-name))
			(dojo.io.bind
			 (create :url url
				 :load server.dom.eval-response
				 :prevent-cache t
				 :form-node element-id
				 :method "post"
				 ;; don't change this to 'text/javascript' or else dojo will try to eval
				 ;; the entire blob that is returned to the client as javascript!
				 :mimetype "text/html"
				 :encoding enc))

			(dojo.io.bind
			 (create :url url
				 :load server.dom.eval-response
				 :prevent-cache t
				 :mimetype "text/html"
				 :encoding enc)))

	    (return false)))



Nice work,

Lou Vanek


Evrim ULU wrote:

> Hi,
> 
> I'm very proud to announce ucw+ implementing ajax. 
> 
> * What is ucw+?
> 
> This is an add-on to ucw_dev repository implementing event-based web
> programming. It makes easier to write web applications and provides a
> desktop like programming style to web applications.

[snip]
> 
> Have a nice day.
> Evrim.
> _______________________________________________
> bese-devel mailing list
> bese-devel at common-lisp.net
> http://common-lisp.net/cgi-bin/mailman/listinfo/bese-devel
> 

revised 02_dom.lisp (works with ie7b3 and ff 1.5.0.4):
;; One note of caution: the code below may break if using broken-parenscript
;; that is unable to accurately generate 'default' cases on switch statements.
;; If working with broken-parenscript you should be able to just comment out
;; the default case.

`(setf server.dom
        (create

	:is-script-node
	(lambda (node)
	  (return
		   (and (not (= nil node))
			 (= (slot-value node 'node-name) "SCRIPT")
			 (= "text/javascript" (node.get-attribute "type")))))

	:is-embedded-script-node
	(lambda (node)
	  (return
	       (= (dojo.html.get-class node) "script")))

	:is-component-node
	(lambda (node)
	  (dj_debug (+ "is-component-node: " (dojo.html.get-class node)))
	  (return
	    (= (dojo.html.get-class node) "component")))

	;;;; This function parses ajax response and calls specific
	;;;; loading functions. Ajax response is:
	;;;; <div class="component" id="dynamic-component-id">
	;;;;   <div class="html">
	;;;;     HTML Content rendered by render-html
	;;;;   </div>
	;;;;   <div class="css">
	;;;;     CSS Content rendered by render-css
	;;;;   </div>
	;;;; </div>
	;;;; <div class="component" id="next-component-id">
	;;;; .... similar to above
	;;;; </div>
	;;;; <div class="javascript">
	;;;;   controller for this window, evaluated automagically
	;;;;   to refresh actions and frames.
	;;;; </div>
	;;;; Ajax response is created by refresh-coretal-component action.
	:eval-response
	(lambda (type result evt)
	  (setf root (document.create-element "div"))
	  (setf root.inner-h-t-m-l result)
	  (server.event.disconnect-events)
	  (dolist (child root.child-nodes)
	    (dj_debug (+ "* current child innerHTML: " child.inner-h-t-m-l))
	    (when (server.dom.is-script-node child)
	      (try
	       (dj_eval child.inner-h-t-m-l)
	       (:catch (error)
		 	 (dj_debug (+ "error while evaluating controller:" error))))
	      continue)
	    (when (server.dom.is-embedded-script-node child)
		  (dj_debug "found embedded script node")
	      (try
	        (dolist (script-node (slot-value child 'child-nodes))
			  (when (= 1 script-node.node-type) ;; skip over whitespace text nodes
		            (dj_debug "found embedded script node with node type 1; eval it.")
			    (dj_eval script-node.inner-h-t-m-l)
				break))
	       (:catch (error)
		 	 (dj_debug (+ "error while evaluating controller for embedded script:" error))))
	      continue)
	    (when (server.dom.is-component-node child)
	      (try (server.dom.parse-component child)
		   (:catch (error)
		     (dj_debug (+ "got error while parsing component response:" error))))
	      continue)
	    (dj_debug "evalResponse: neither script nor component node.")
	   )
	  (server.event.connect-events)
	  (dj_debug "ended eval response"))

	;;;; This function parses any <div class="component"></div> and loads
	;;;; the data into current document.
	:parse-component
	(lambda (item)
	  (let ((id (dojo.html.get-attribute item "id")))
	    (dj_debug (+ "parsing component:" item ", id:" id))
	    (dolist (sub-item (slot-value item 'child-nodes))
		  (dj_debug (+ "parse-component: class: "
	      		(dojo.html.get-class sub-item)))
	      (case (dojo.html.get-class sub-item)
			("html" (progn
					  ;debugger
					  (this.parse-html id sub-item)))
			("css"  (this.parse-css id  sub-item))
			(default (dj_debug "ALERT: invalid class? (or no class)."))
			))))

	;;;; This function parses <div class="html></div> and loads html content
	;;;; into current document via altering innerHTML property of the HTML
	;;;; element. Event callbacks are temporarily disconnected to avoid
	;;;; memory leak in IE.
	  ;; 'let' acts like 'let*' in parenscript.
	:parse-html
	(lambda (id new-html-node)
	  (let ((old-html-node        (document.get-element-by-id id))
		(parent-html-node     (slot-value old-html-node    'parent-node)))
	    (if old-html-node
		(progn
		    (dj_debug "replacing previous node")
	            (dolist (a-child (slot-value new-html-node 'child-nodes))
			  (when (= 1 a-child.node-type) ;; skip over whitespace text nodes
				(parent-html-node.replace-child a-child old-html-node)
				break)))
		(progn
		    (dj_debug "appending node to document")
		    (document.body.append-child new-html-node)))))

	:style-index (new (*object))
	
	:style-index-size
	(lambda ()
	  (return (slot-value (document.get-elements-by-tag-name "style") 'length)))

	:parse-css
	;;;; This function parses <div class="css"></div> html element and
	;;;; loads css into current document via modifying browsers style
	;;;; cache.
	(lambda (id new-style-node)
	  (let ((css-index (slot-value this.style-index id))
		new-node
		(head (aref (document.get-elements-by-tag-name "head") 0)))
	   (dolist (a-child (slot-value new-style-node 'child-nodes))
		  (when (= 1 a-child.node-type) ;; skip over whitespace text nodes
			(setf new-node a-child)
			break))
	    (if (and new-node (> css-index -1))
			(let ((old-style-node (aref (document.get-elements-by-tag-name "style") css-index)))
				  (head.replace-child new-node old-style-node))
			(let ((css-index (this.style-index-size)))
			  (head.append-child new-node);;new-style-node)
			  (setf (slot-value server.dom.style-index id) css-index)))))))




More information about the bese-devel mailing list