[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