[Bese-devel] javascript

Lou Vanek vanek at acd.net
Mon Jun 26 23:55:14 UTC 2006


The following is what i use for clisp to delay javascripts
from executing until the page has finished loading.


(defmacro defun-inline (name bvl &body forms)
   `(progn (declaim (inline ,name))
     (defun ,name ,bvl , at forms)))

  (defun-inline string-append (&rest strings)
    (declare (dynamic-extent strings))
    (apply #'concatenate 'string strings))

(defun-inline js-bootstrap-code ()
"<script type=\"text/javascript\">
// <![CDATA[
function defined(v) {
	return( v != undefined );
}

function isUndefined(a) {
	return typeof a == 'undefined';
}

function isFunction(a) {
	return typeof a == 'function';
}

function isNull(a) {
	return typeof a == 'object' && !a;
}

function isNumber(a) {
	return typeof a == 'number' && isFinite(a);
}

function isBoolean(a) {
     return typeof a == 'boolean';
}

function isObject(a) {
	return (a && typeof a == 'object') || isFunction(a);
}

function isString(a) {
	return typeof a == 'string';
}

function isArray(a) {
	return isObject(a) && a.constructor == Array;
}


function getargs() {
	var args = new Object();
	var query = location.search.substring(1); // Get query string.
	var pairs = query.split(\"&\");// Break at comma.

	for(var i = 0; i < pairs.length; i++) {
		var pos = pairs[i].indexOf('=');  // Look for \"name=value\".
		if (pos == -1) continue;		  // If not found, skip.
		var argname = pairs[i].substring(0,pos); // Extract the name.
		var value = pairs[i].substring(pos+1);// Extract the value.
		args[argname] = unescape(value); // Store as a property.
	}

	return args;	// Return the object.
}

// 'onerror' doesn't work on opera, but opera is braindead, and is (mostly)
// silently ignored.
onerror = handleErrors

    var msg = null
    function handleErrors(errorMessage, url, line)
    {
	   if (defined(window.verbose)) {
		   msg  = \"There was an error on this page.\n\n\";
		   msg += \"An internal programming error may keep\n\";
		   msg += \"this page from displaying properly.\n\";
		   msg += \"Click OK to continue.\n\n\";
		   msg += \"Error message: \" + errorMessage + \"\n\";
		   msg += \"URL: \" + url + \"\n\";
		   msg += \"Line #: \" + line;
		   alert(msg);
	   }
	   return true
    }

var pageloadguard = 0;

function fnStartInit()
{
	// do not have to wait for images to load if using IE
	var  rs;
	if (document.all)
		rs =  document.readyState;
	else
		rs = \"complete\";

     if ((rs == \"interactive\" ||
	 rs == \"complete\"  ) &&
		pageloadguard < 1 )
     {
		pageloadguard++;
       	initPage();
     }
} // fnStartInit


function hookupInit()
{
	if (document.all) {
		document.onreadystatechange = fnStartInit;
	}
	else {
		window.onload = fnStartInit;
	}
}

var func_idx  = 0;
initPage = function () {
	// All functions that you want loaded AFTER the page has
	// fully loaded should be named '__function__000',
	// '__function__001', etc., and they will be automatically
	// invoked at the proper time (and in sequence).
	var fname = \"__function__\";
	var i = func_idx + \"\";
	while (i.length < 3) { i = \"0\" + i; } // pad with 0s
	// going to recurse calling this same function until
	// get an error (which should be trapped in 'handleErrors').
	var composition = \" \" + fname + i + \"(); initPage() \";
	func_idx = func_idx + 1;
	setTimeout( composition, 10);
}

hookupInit();
// ]]>
</script>
")

;; not used because it really isn't necessary and takes a lot of resources.
(defun move-scripts-into-head (s)
  (let* ((head_re   "(?i)</head>")
         (script_re "(?i)(?s)<script[ >](.*?)</script>")
         (h         (cl-ppcre:scan head_re s)))
   (when h
     (loop
	for x = h then end
	for (start end) = (multiple-value-bind (s e) (cl-ppcre:scan script_re s :start x) (list s e))
	while end
	collect (string-append (subseq s start end) '(#\Newline)) into scripts
	collect (subseq s x start) into nonscripts
	finally (return-from move-scripts-into-head
		  (arnesi:join-strings (append (list (subseq s 0 h))
					       scripts nonscripts
					       (list (subseq s x)))))
	))
   s))

(defun insert-into-head (html new-item)
  (let* ((head_re "(?i)</head>")
         (h (cl-ppcre:scan head_re html)))
   (if h
     (string-append (subseq html 0 h) new-item '(#\Newline) (subseq html h))
     html)))

(defun patch__function__XXX (tgt)
   (let ((regex (cl-ppcre:create-scanner "__function__\(XXX\) ="))
	(i -1))
     (flet ((patchNumber (target-string start end match-start match-end reg-starts reg-ends)
              (declare (ignore start end match-end reg-starts reg-ends))
              (format nil "__function__~3,'0D =" (incf i))))
       (setf tgt (cl-ppcre:regex-replace-all regex tgt #'patchNumber))
       (values tgt (incf i)))))

(defmethod shutdown ((resp araneida-response))
  (multiple-value-bind (content-type content-type/charset)
	(content-type-and-charset resp)
   (let* ((s (get-output-stream-string (html-stream resp)))
	 (content (if (starts-with content-type "text")
                       (string-to-octets (insert-into-head (patch__function__XXX s) (js-bootstrap-code))
	 and so on...




and i added a new tag to use when you want your script
executed after the page has finished loading (and the
code in dojo.js is guaranteed to have been defined):

(deftag-macro <ucw:script-delayed-load (&body body)
   `(<:script :type "text/javascript"
              (<:as-is ~% "// <![CDATA[" ~%
		"__function__XXX = function() {" ~%
                       (js:js* , at body)
		~% "}"
                 ~% "// ]]>" ~%)))



More information about the bese-devel mailing list