summaryrefslogtreecommitdiff
path: root/itjs
diff options
context:
space:
mode:
authorChristian Schneider2007-10-11 00:39:30 +0000
committerChristian Schneider2007-10-11 00:39:30 +0000
commit35fe33f7364329dacf415c950bff01b6de9ef88e (patch)
treeb0e6b018b50038ca20266723c53750268f508df5 /itjs
parent1f95711ff3e9697cd85a54545ab42e5fd3611317 (diff)
downloaditools-35fe33f7364329dacf415c950bff01b6de9ef88e.tar.gz
itools-35fe33f7364329dacf415c950bff01b6de9ef88e.tar.bz2
itools-35fe33f7364329dacf415c950bff01b6de9ef88e.zip
Populated release branch
Diffstat (limited to 'itjs')
-rw-r--r--itjs/boot.js162
-rw-r--r--itjs/error.gifbin0 -> 43 bytes
-rw-r--r--itjs/it.js177
-rw-r--r--itjs/loader.js275
-rw-r--r--itjs/state.html31
-rw-r--r--itjs/state.js120
-rw-r--r--itjs/timer.js64
7 files changed, 829 insertions, 0 deletions
diff --git a/itjs/boot.js b/itjs/boot.js
new file mode 100644
index 0000000..aaa0321
--- /dev/null
+++ b/itjs/boot.js
@@ -0,0 +1,162 @@
+// $Id$
+
+var it_boot_status = "boot";
+var it_panictimer = window.setTimeout("it_panic(it_boot_status)", 31337), it_domtimer;
+var it_catcherrstart = new Date().getTime();
+
+function it_catcherr(msg, url, line)
+{
+ var stacktrace = "";
+
+ for (var c = it_catcherr.caller; c != null; c = c.caller)
+ {
+ var funcname = c.toString().match(/function (\w*)/)[1];
+ stacktrace += (funcname ? funcname : "anon") + "/";
+
+ if (c.caller == c) // Break simple recursion
+ {
+ stacktrace += "*";
+ break;
+ }
+ }
+
+ if (typeof it_boot.sequence != 'undefined')
+ stacktrace += "it_boot=" + it_boot.sequence + ";";
+ if (typeof window.it_loader != 'undefined' && it_loader.sequence)
+ stacktrace += "it_loader=" + it_loader.sequence + ";";
+
+ it_boot_report(msg, url, line, stacktrace);
+
+ return !window.env || !!window.env.is_live_server; // No env or live server -> suppress error
+}
+
+window.onerror = it_catcherr;
+
+function it_boot_addparam(url, param)
+{
+ return url + (url.match(/\?/) ? "&" : "?") + param;
+}
+
+function it_panic(reason, msg)
+{
+ if (!document.location.href.match(/[?&]static=/)) // Avoid loop
+ window.setTimeout("document.location.href = it_boot_addparam(document.location.href, 'static=" + reason + "')", 500);
+
+ return it_boot_report('panic ' + reason, '-', -1, (msg ? msg : ''));
+}
+
+function it_boot_report(msg, file, line, more)
+{
+ window.clearTimeout(window.it_domtimer);
+ window.clearTimeout(window.it_panictimer);
+
+ new Image().src = "/itjs/error.gif/" + escape(msg) + "|" + escape(file) + "|" + line + "|" + (new Date().getTime() - it_catcherrstart) + "|" + escape(more);
+}
+
+function it_boot_checkcss(style, key, value)
+{
+ return navigator.userAgent.match(/konqueror/i) || (style && style.getPropertyValue && (style.getPropertyValue(key) == value));
+}
+
+function it_boot_init()
+{
+ window.it_domtimer = null;
+ var konqueror = navigator.userAgent.match(/konqueror/i);
+ var doc = document;
+ var dom = doc && (dom = doc.getElementById('it_boot_dom')); // HTML has been rendered
+ var view = dom && doc.defaultView; // We can check if stylesheet is active
+ var style = view && view.getComputedStyle && view.getComputedStyle(dom, '');
+ var css = window.it_boot_checkcss(style, "visibility", "hidden"); // CSS active (inline style on tag)
+ var stylesheet = window.it_boot_checkcss(style, "display", "none"); // External stylesheet loaded
+
+ if (!(doc || !(it_boot_status = "doc")) || !(dom || !(it_boot_status = "dom")) || (style && !(stylesheet || !(it_boot_status = "stylesheet"))))
+ {
+ window.it_domtimer = window.setTimeout("it_boot_init()" , 42);
+
+ if (style && !css)
+ it_panic("css");
+
+ return;
+ }
+
+ it_boot.sequence += "i";
+ window.clearTimeout(window.it_panictimer);
+ window.it_boot_start();
+ it_boot.sequence = "";
+}
+
+function it_boot(file, isretry)
+{
+ it_boot.file = file;
+ it_boot.sequence = isretry ? "r" : "b";
+
+ try
+ {
+ var loader = new XMLHttpRequest();
+ }
+ catch (e)
+ {
+ var classnames = [ 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ];
+
+ for (var i in classnames)
+ try { loader = new ActiveXObject(classnames[i]); break; } catch (e) {}
+ }
+
+ if (loader)
+ {
+ it_boot.sequence += "l";
+ loader.open("GET", it_boot_addparam(file, "boot=1" + (isretry ? "&retry=1" : "")));
+ loader.onreadystatechange = function()
+ {
+ var error = "";
+
+ if (loader.readyState == 4)
+ {
+ if (loader.status < 400) // Opera gives back 304 if from cache
+ {
+ // check length cookie
+ var ln = String(loader.responseText).substr(-16).match(/\*sln:\s*([0-9]+)\*/);
+ if (ln && ln[1]-0 == loader.responseText.length)
+ {
+ it_boot.sequence += "e";
+ var code = "try {" + loader.responseText + "} catch (e) { it_catcherr(e.message, it_boot.file, -1); }"; // Wrapped in try/catch as Konqueror does not support window.onerror
+ if (window.execScript)
+ window.execScript(code, "javascript"); // IE work-around to get script executed in global scope
+ else
+ window.setTimeout(code, 0); // Standard compliant version
+ }
+ else
+ error = (ln ? "length mismatch: "+ln[1]+" != "+loader.responseText.length : "no length cookie");
+ }
+ else
+ error = loader.statusText;
+
+ if (error)
+ {
+ if (isretry)
+ it_panic('load', error);
+ else
+ it_boot(file, true);
+ }
+ }
+ }
+ loader.send(null);
+ }
+ else
+ {
+ var doc = document;
+ var dom = doc && (dom = doc.getElementById('it_boot_dom')); // HTML has been rendered
+ it_boot.sequence += "n";
+
+ if (window.opera || (document.all && navigator.platform.indexOf("Mac") >= 0))
+ document.write('<sc'+'ript type="text/javascript" src="'+it_boot_addparam(file, 'boot=1')+'"><\/sc'+'ript>');
+ else if (dom)
+ {
+ var tag = doc.createElement("script");
+ tag.src = it_boot_addparam(file, 'boot=1&retry=1');
+ dom.appendChild(tag);
+ }
+ else
+ window.it_domtimer = window.setTimeout("it_boot('" + file + "')" , 42);
+ }
+}
diff --git a/itjs/error.gif b/itjs/error.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/itjs/error.gif
Binary files differ
diff --git a/itjs/it.js b/itjs/it.js
new file mode 100644
index 0000000..a43ba3a
--- /dev/null
+++ b/itjs/it.js
@@ -0,0 +1,177 @@
+// $Id$
+
+/**
+ * Clear contents of element 'jsdebug'
+ */
+function CED(txt)
+{
+ var element = document.getElementById('jsdebug');
+ if (element)
+ element.innerHTML = txt ? txt : "";
+}
+
+/**
+ * Add debugging output to element 'jsdebug'
+ */
+function ED()
+{
+ var element = document.getElementById('jsdebug');
+ if (element)
+ {
+ var text = "";
+
+ for (var i = 0; i < arguments.length; i++)
+ {
+ var variable = arguments[i];
+
+ if (typeof variable == "string")
+ variable = variable.replace(/&/g, '&amp;').replace(/</g, '&lt;');
+
+ text += (typeof variable) + " " + variable;
+
+ if (typeof variable == "object")
+ {
+ text += ":";
+
+ for (field in variable)
+ {
+ text += field + "=";
+
+ try { text += typeof variable[field] == 'function' ? 'function' : variable[field]; }
+ catch (e) { text += "*" + e + "*"; }
+
+ text += "\n";
+ }
+ text += "\n";
+ }
+
+ text += "\n";
+ }
+
+ element.innerHTML += '<pre style="background-color:#FEE; margin:0">' + text + '</pre>';
+ }
+}
+
+/**
+ * Quote HTML special chars
+ * @return Text string with & " < > htmlentities-encoded
+ */
+function Q(value)
+{
+ return typeof value == "undefined" ? "" : value.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+}
+
+/**
+ * String class: Replaces variables of the form {var} with values from given array
+ * @param values Associative array containing values to fill in (optional)
+ * @return Text string with variables replaced by their values
+ */
+String.prototype.T = function(values)
+{
+ var result = this;
+
+ for (key in values)
+ result = result.replace(new RegExp("{" + key + "}", "g"), values[key]);
+
+ return result;
+}
+
+/**
+ * Insert an event handler on top of chain
+ * @param p.element Element to handle event for
+ * @param p.event Name of event:'focus', 'click', ... (without 'on')
+ * @param p.object Object that contains handler method
+ * @param p.method Method of p.object to call on p.event
+ */
+function it_event(p)
+{
+ var oldhandler = p.element["on" + p.event];
+
+ p.element["on" + p.event] = function(ev)
+ {
+ var pp = arguments.callee.p ? arguments.callee.p : p;
+ var oo = arguments.callee.oldhandler ? arguments.callee.oldhandler : oldhandler;
+
+ var result = pp.object[pp.method](ev ? ev : window.event, pp);
+
+ if (result && oo)
+ result = oo(ev);
+
+ return result;
+ }
+ p.element["on" + p.event].p = p;
+ p.element["on" + p.event].oldhandler = oldhandler;
+}
+
+/* Get object pixel position. Based on quirksmode.org's code */
+function it_get_obj_x(obj)
+{
+ var curleft = 0;
+ if (obj.offsetParent)
+ while (obj)
+ {
+ curleft += obj.offsetLeft;
+ obj = obj.offsetParent;
+ }
+ else if (obj.x)
+ curleft += obj.x;
+ return curleft;
+}
+
+function it_get_obj_y(obj)
+{
+ var curtop = 0;
+ if (obj.offsetParent)
+ while (obj)
+ {
+ curtop += obj.offsetTop;
+ obj = obj.offsetParent;
+ }
+ else if (obj.y)
+ curtop += obj.y;
+ return curtop;
+}
+
+/* Get an iframe's content document in a compatible way */
+function it_get_iframe_document(iframe)
+{
+ return iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
+}
+
+/**
+ * Copy attributes from src to dst in a recursive manner.
+ * @param dst Destination object which gets attributes
+ * @param src Source object containing attributes
+ */
+function it_set(dst, src)
+{
+ if (dst)
+ {
+ for (var i in src)
+ {
+ if (typeof src[i] == 'object')
+ it_set(dst[i], src[i]);
+ else
+ dst[i] = src[i];
+ }
+ }
+}
+
+/**
+ * Return the current timestamp
+ */
+function it_now()
+{
+ return new Date().getTime();
+}
+
+/**
+ * Encodes arbitrary string for use in an url
+ * @param str string to be encoded
+ */
+function it_url_encode(str)
+{
+ var result = window.encodeURIComponent ? encodeURIComponent(str) : escape(str).replace(/\+/g, "%2B");
+
+ return result.replace(/%20/gi, "+").replace(/%2C/gi, ",").replace(/%28/gi, "(").replace(/%29/gi, ")");
+}
diff --git a/itjs/loader.js b/itjs/loader.js
new file mode 100644
index 0000000..de6ae9d
--- /dev/null
+++ b/itjs/loader.js
@@ -0,0 +1,275 @@
+/**
+ * Create loader to request data from server
+ *
+ * @param handler Object providing clear()/render() function when data arrives
+ */
+function it_loader(handler)
+{
+ /* Clear cache etc. if completely new data */
+ this.loader = null;
+ this.handler = handler;
+ this.instance = it_loader.instances++;
+ this.callid = 0;
+ this.clear();
+}
+
+/* Methods */
+it_loader.prototype =
+{
+
+/* Clear cache and initialize handler */
+clear: function()
+{
+ /* Clear cache etc. if completely new data */
+ this.entry = new Array();
+ this.start = this.end = 0;
+ this.attr = { num: 0, loadtime: 0 };
+ this.method = "GET";
+ this.post_data = null;
+
+ if (this.handler.clear)
+ this.handler.clear();
+},
+
+load: function(baseurl, pos, num, query_volatile, retry)
+{
+ /* Convert to int */
+ pos -= 0;
+ num -= 0;
+
+ if (isNaN(retry))
+ retry = 0;
+
+ if (baseurl != this.baseurl)
+ {
+ this.clear();
+ this.baseurl = baseurl;
+ this.start = this.end = pos;
+ }
+
+ this.pos = pos;
+ this.num = num;
+ this.query_volatile = query_volatile;
+
+ if (this.loader)
+ this.stop();
+
+ while ((num > 0) && this.entry[pos])
+ {
+ pos++;
+ num--;
+ }
+
+ if (this.attr.eof)
+ num = Math.min(num, this.end - pos);
+
+ if (num > 0)
+ {
+ this.loader = null;
+ var samehost = (baseurl.indexOf('http://') < 0 || baseurl.indexOf(window.location.hostname) > 0);
+
+ if (retry)
+ baseurl += "&retry=" + retry;
+
+ // use XMLHTTP request if available
+ if (samehost)
+ {
+ try
+ {
+ this.loader = new XMLHttpRequest();
+ }
+ catch (e)
+ {
+ var classnames = [ 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ];
+
+ for (var i in classnames)
+ {
+ try
+ {
+ this.loader = new ActiveXObject(classnames[i]);
+ break;
+ }
+ catch (e) { }
+ }
+ }
+
+ try
+ {
+ this.loader.open(this.method, baseurl + "&pos=" + pos + "&num=" + num + (query_volatile ? query_volatile : ""));
+ var me = this;
+ this.loader.onreadystatechange = function() { me.readyStateChanged(); }
+ var workingxmlhttp = this.loader.onreadystatechange;
+
+ if (!workingxmlhttp) /* Old Konqueror */
+ this.loader = null;
+ }
+ catch (e) { }
+ }
+
+ this.starttime = new Date().getTime();
+ this.retry = retry;
+
+ if (this.loader)
+ {
+ if (this.method == "POST")
+ this.loader.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ this.loader.send(this.post_data);
+ }
+ else
+ {
+ var req_url = baseurl + "&pos=" + pos + "&num=" + num + (query_volatile ? query_volatile : "") + (this.post_data ? '&' + this.post_data : "") + "&itjs_call=it_loader.__inst" + this.instance + "&itjs_callid=" + ++this.callid;
+
+ if (samehost || (window.opera && !window.XMLHttpRequest)) // Opera 7 only works with iframes
+ {
+ this.scrpt = document.createElement("iframe");
+ this.scrpt.style.width = this.scrpt.style.height = 1;
+ req_url += "&itjs_iframe=1";
+ }
+ else
+ this.scrpt = document.createElement("script");
+
+ it_loader['__inst'+this.instance] = this;
+ this.loader = { starttime: this.starttime, retry: retry };
+ try
+ {
+ if (!document.all) this.scrpt.src = req_url;
+ document.body.appendChild(this.scrpt);
+ if (document.all) this.scrpt.src = req_url;
+ }
+ catch (e) { return false; }
+ }
+ }
+ else
+ this.handler.render(this);
+
+ return true;
+},
+
+post: function(baseurl, data)
+{
+ this.clear();
+ this.method = "POST";
+ this.baseurl = baseurl;
+ this.start = this.end = 10;
+ this.post_data = "";
+
+ if (typeof data == 'object')
+ {
+ for (var k in data)
+ this.post_data += (this.post_data ? "&" : "") + k + "=" + escape(data[k]);
+ }
+ else
+ this.post_data = data;
+
+ this.load(baseurl, 0, 1, "");
+},
+
+readyStateChanged: function()
+{
+ var loader = this.loader; // Avoid race conditions
+ it_loader.sequence += "r";
+
+ if (loader && (loader.readyState == 4))
+ {
+ var data = null;
+
+ try
+ {
+ if (loader.responseText != "")
+ data = eval("("+ loader.responseText + ")");
+ }
+ catch (e)
+ {
+ var retry = this.retry + 1;
+
+ if (retry < 10)
+ it_timer({ object: this, method: "retryload", timeout: Math.pow(5, Math.min(retry, 5)), baseurl: this.baseurl, pos: this.pos, num: this.num, query_volatile: this.query_volatile, retry: retry });
+ else
+ ED(e, loader.responseText);
+ }
+
+ if (data)
+ this.dataReady(data, this.callid);
+ }
+},
+
+retryload: function(p)
+{
+ this.load(p.baseurl, p.pos, p.num, p.query_volatile, p.retry);
+},
+
+dataReady: function(data, callid)
+{
+ var fixkonqueror33gcbug = this.loader;
+ var loadtime = new Date().getTime() - this.starttime;
+ it_loader.sequence += "e";
+
+ this.loader = null;
+
+ if ((typeof data == "object") && (this.callid == callid))
+ {
+ this.attr = { loadtime: loadtime };
+
+ for (var key in data)
+ {
+ var value = data[key];
+ var id = key - 0;
+
+ if (!isNaN(id))
+ {
+ this.start = Math.min(this.start, id);
+ this.end = Math.max(this.end, id + 1);
+ this.entry[id] = data[key];
+ }
+ else
+ this.attr[key] = data[key];
+ }
+
+ if (this.attr.eof)
+ this.attr.num = this.end; /* Fix bogus # of result value */
+
+ this.handler.render(this);
+
+ if (!this.attr.eof && (this.end < this.pos + this.num))
+ this.load(this.baseurl, this.end, this.pos + this.num - this.end);
+
+ it_loader.sequence += "h";
+ }
+
+ this.unlink();
+},
+
+stop: function()
+{
+ try
+ {
+ this.loader.abort();
+ }
+ catch (e) { }
+
+ this.unlink();
+},
+
+unlink: function()
+{
+ if (it_loader['__inst'+this.instance])
+ it_loader['__inst'+this.instance] = null;
+
+ if (this.loader)
+ this.loader = null;
+
+ if (this.scrpt)
+ {
+ document.body.removeChild(this.scrpt);
+ this.scrpt = null;
+ }
+
+ it_loader.sequence = "";
+}/* NO COMMA */
+
+}
+
+// static properties
+it_loader.instances = 0;
+it_loader.sequence = "";
diff --git a/itjs/state.html b/itjs/state.html
new file mode 100644
index 0000000..36f36c0
--- /dev/null
+++ b/itjs/state.html
@@ -0,0 +1,31 @@
+<?php header("Cache-Control: max-age=3600");
+return <<<EOF
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+<head>
+<title></title>
+<script type="text/javascript">
+var state_load_attempts = 0;
+function state_onload()
+{
+ if (state_load_attempts < 30)
+ {
+ state_load_attempts++;
+ if (parent.it_state && parent.it_state.ready)
+ parent.setTimeout(parent.it_state_restore_history, 1);
+ else
+ window.setTimeout(state_onload, 90);
+ }
+ window[(document.all?'onbeforeunload':'onunload')] = state_onbeforeunload;
+}
+function state_onbeforeunload()
+{
+ if (parent.it_state && !parent.it_state.it_state_saved)
+ parent.it_state.store_state();
+}
+</script>
+</head>
+<body onload="state_onload()"><form action="/itjs/state.html" method="get"><input type="hidden" id="state" name="s" value=""></form></body>
+</html>
+EOF
+?>
diff --git a/itjs/state.js b/itjs/state.js
new file mode 100644
index 0000000..70d9909
--- /dev/null
+++ b/itjs/state.js
@@ -0,0 +1,120 @@
+/**
+ * Generic state object (singleton), for supporting Ajax browser history
+ *
+ * $Id$
+ *
+ */
+var it_state =
+{
+it_iframe: null,
+it_history_field: null,
+it_state_saved: false,
+it_store_handlers: [],
+it_restore_handlers: [],
+
+/**
+ * Register a handler that gets called before creating a history entry
+ * @param p.object Object that contains handler method
+ * @param p.method Method of p.object to call
+ */
+register_store_handler: function(p)
+{
+ this.it_store_handlers[this.it_store_handlers.length] = p;
+},
+
+
+/**
+ * Register a handler that gets called after restoring a history entry
+ * @param p.object Object that contains handler method
+ * @param p.method Method of p.object to call
+ * @param p.initial optional flag: if true, handler is called with initial empty state
+ */
+register_restore_handler: function(p)
+{
+ this.it_restore_handlers[this.it_restore_handlers.length] = p;
+
+ // If we already have state data, call registered handler immediately. Yup this _is_ needed!
+ if (this.it_history_field && this.it_history_field.value)
+ p.object[p.method]();
+},
+
+
+/**
+ * Create a new history entry, saving all values of it_state (asynchronously)
+ * Users must call this without parameter!
+ */
+new_history_entry: function(p)
+{
+ this.store_state();
+ this.it_state_saved = true;
+
+ if (!this.it_iframe && !(this.it_iframe = document.getElementById('it_state')))
+ ED('it_state::new_history_entry(): it_state object not found!');
+
+ var idoc;
+ if ((idoc = it_get_iframe_document(this.it_iframe)))
+ {
+ idoc.title = document.title;
+ idoc.forms[0].submit();
+ }
+ this.it_history_field = null;
+},
+
+
+/**
+ * Restore state from history, called from iframe's onload handler but in main window's context
+ */
+restore_history: function()
+{
+ if (!this.it_iframe && !(this.it_iframe = document.getElementById('it_state')))
+ ED('it_state::restore_history(): it_state object not found!');
+
+ var idoc = it_get_iframe_document(this.it_iframe);
+ this.it_history_field = idoc ? idoc.getElementById('state') : {}; // Work-around IE5 not returning iframe document
+ this.it_state_saved = false;
+
+ if (this.it_history_field.value)
+ {
+ var res = eval('({' + this.it_history_field.value + '})');
+ for (var key in res)
+ this[key] = res[key];
+ }
+
+ for (var i in this.it_restore_handlers)
+ {
+ if (this.it_history_field.value || (this.it_restore_handlers[i].initial && (!idoc || !idoc.location.href.match(/s=/))))
+ this.it_restore_handlers[i].object[this.it_restore_handlers[i].method]();
+ }
+},
+
+
+/**
+ * Call all store handlers and store state in it_history_field
+ */
+store_state: function()
+{
+ if (!this.it_iframe && !(this.it_iframe = document.getElementById('it_state')))
+ ED('it_state::store_state(): it_state object not found!');
+
+ var idoc = it_get_iframe_document(this.it_iframe);
+ this.it_history_field = idoc ? idoc.getElementById('state') : {}; // Work-around IE5 not returning iframe document
+
+ for (var i in this.it_store_handlers)
+ this.it_store_handlers[i].object[this.it_store_handlers[i].method]();
+
+ var ser = [];
+ for (var key in this)
+ {
+ var value = this[key], type = typeof(value);
+ if (!key.match(/^it_/) && type.match(/boolean|number|string/))
+ ser[ser.length] = key + ':' + ((type == 'string') ? "'" + value.replace(/([\\'])/g, '\\\1') + "'" : value);
+ }
+
+ this.it_history_field.value = ser.join(',');
+}/* NO COMMA */
+}
+
+function it_state_restore_history()
+{
+ it_state.restore_history();
+}
diff --git a/itjs/timer.js b/itjs/timer.js
new file mode 100644
index 0000000..25ebbec
--- /dev/null
+++ b/itjs/timer.js
@@ -0,0 +1,64 @@
+/**
+ * Start new timer
+ * Example: timer = new it_timer({ object:this, method:"timer", timeout:100});
+ *
+ * @param p.object Object to call method in when timer fires
+ * @param p.method String with method name to call in object
+ * @param p.timeout Timeout in milliseconds
+ * @param p.continuous One-shot or continuous timer, default is one-shot
+ * @return timer id (deprecated, use method stop() instead)
+ */
+function it_timer(p)
+{
+ this.func = p.continuous ? "Interval" : "Timeout";
+ return this.timer = window["set" + this.func](function() { p.object[p.method](p) }, p.timeout);
+}
+
+it_timer.prototype =
+{
+
+stop: function()
+{
+ if (this.timer)
+ {
+ window["clear" + this.func](this.timer);
+ this.timer = null;
+ }
+}/* NO COMMA */
+
+}
+
+/**
+ * Global helper function to benchmark javascript
+ * @parameter label Message label like "start" or "end"
+ * @paramter print Whether to output timerlog via ED() and clear it (optional)
+ */
+function it_timerlog(label, print)
+{
+ if (window.it_timerlog_active)
+ {
+ var end = new Date().getTime();
+
+ if (typeof window.it_timernow != "undefined")
+ {
+ var start = window.it_timernow;
+
+ if (window.it_timerlogmsg != "")
+ window.it_timerlogmsg += ", ";
+
+ window.it_timerlogmsg += label + ":" + (end - start);
+ }
+ else
+ window.it_timerlogmsg = "";
+
+ window.it_timernow = end;
+
+ if (print)
+ {
+ ED("timerlog: " + window.it_timerlogmsg);
+ window.it_timerlogmsg = "";
+ }
+ }
+}
+
+it_timerlog(""); // Set start time