diff options
Diffstat (limited to 'devel-utf8/itjs')
-rw-r--r-- | devel-utf8/itjs/0.gif | bin | 0 -> 43 bytes | |||
-rw-r--r-- | devel-utf8/itjs/boot.js | 253 | ||||
-rw-r--r-- | devel-utf8/itjs/error.gif | 11 | ||||
-rwxr-xr-x | devel-utf8/itjs/http.js | 245 | ||||
-rw-r--r-- | devel-utf8/itjs/it.js | 297 | ||||
-rw-r--r-- | devel-utf8/itjs/loader.js | 141 | ||||
-rw-r--r-- | devel-utf8/itjs/state.html | 31 | ||||
-rw-r--r-- | devel-utf8/itjs/state.js | 120 | ||||
-rw-r--r-- | devel-utf8/itjs/timer.js | 64 |
9 files changed, 1162 insertions, 0 deletions
diff --git a/devel-utf8/itjs/0.gif b/devel-utf8/itjs/0.gif Binary files differnew file mode 100644 index 0000000..5bfd67a --- /dev/null +++ b/devel-utf8/itjs/0.gif diff --git a/devel-utf8/itjs/boot.js b/devel-utf8/itjs/boot.js new file mode 100644 index 0000000..059bd78 --- /dev/null +++ b/devel-utf8/itjs/boot.js @@ -0,0 +1,253 @@ +// $Id$ + +var it_boot_status = "boot"; +var it_panictimer = window.setTimeout("it_panic({reason:it_boot_status})", 31337), it_domtimer; +var it_starttime = new Date().getTime(); + +function it_stacktrace() +{ + var stacktrace = ""; + var callstack_done = false; + + try { i.dont.exist += 0; } // does not exist - that's the point + catch (e) + { + if (e.stack) // Firefox + { + stacktrace = e.stack.replace(/^[^\n]*\n/,'').replace(/(\n@)?\s+$/,'$1').replace(/^\(/g,'{anonymous}(') + ';'; + callstack_done = true; + } + else if (window.opera && e.message) // Opera + { + var entry, lines = e.message.split("\n"); + for (var i=1, len=lines.length; i < len; i++) + { + if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) + { + entry = lines[i]; + // append next line also since it has the file info + if (lines[i+1]) + { + entry += " at " + lines[i+1]; + i++; + } + stacktrace += entry + ','; + } + } + callstack_done = true; + } + } + + if (!callstack_done) // IE and Safari + { + var fname, current_func = arguments.callee.caller; + while (current_func) + { + fname = /function\s*([\w\-$]+)?\s*\(/.test(current_func.toString()) ? RegExp.$1 || 'anonymous' : 'anonymous'; + stacktrace += fname + ','; + current_func = current_func.caller; + } + } + + return stacktrace; +} + +function it_catcherr(msg, url, line) +{ + var stacktrace = it_stacktrace(); + + if (typeof it_boot.sequence != 'undefined') + { + // trigger it_boot retry if error occured while evaluating loaded script + if (it_boot.sequence == 'ble' && it_boot.file) + { + it_boot(it_boot.file, true); + return true; + } + + stacktrace += "it_boot=" + it_boot.sequence + ";it_boot_file=" + it_boot.file; + } + if (typeof window.it_loader != 'undefined' && it_loader.sequence) + stacktrace += "it_loader=" + it_loader.sequence + ";"; + + it_boot_report({msg:msg, url:url, line:line, stacktrace: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(p) +{ + if (!document.location.href.match(/[?&]static=/)) // Avoid loop + window.setTimeout("document.location.href = it_boot_addparam(document.location.href, 'static=" + p.reason + "')", 500); + + p.type = "panic"; + p.stacktrace = it_stacktrace(); + return it_boot_report(p); +} + +function it_boot_report(p) +{ + window.clearTimeout(window.it_domtimer); + window.clearTimeout(window.it_panictimer); + var loader = null; + var data = ""; + var postdata = p.data; + delete p.data; + p.time = new Date().getTime() - it_starttime; + + for (var k in p) + data += (data ? '&' : '') + k + "=" + escape(p[k]).replace(/\+/g, "%2B"); + + var url = "/itjs/error.gif?" + data; + + if (postdata) + { + loader = it_boot_getloader(); + for (var k in postdata) + data += (data ? '&' : '') + k + "=" + escape(postdata[k]).replace(/\+/g, "%2B"); + } + + try + { + loader.open('POST', url); + loader.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + loader.send(data); + } + catch (e) + { + new Image().src = url; + } +} + +function it_boot_checkcss(style, key, value) +{ + return navigator.userAgent.match(/konqueror/i) || (style && style.getPropertyValue && (style.getPropertyValue(key) == value)); +} + +function it_boot_init() +{ + it_boot.sequence += "i"; + 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({reason:"css"}); + + return; + } + + window.clearTimeout(window.it_panictimer); + it_boot.sequence += "s"; + window.it_boot_start(); + it_boot.sequence = ""; +} + +function it_boot_getloader() +{ + var result = null; + + if (!navigator.userAgent.match(/iPhone|iP.d/)) // Do not use XMLHttpRequest on iOS devices as it does not use cache + { + try + { + result = new XMLHttpRequest(); + } + catch (e) + { + var classnames = [ 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ]; + + for (var i in classnames) + try { result = new ActiveXObject(classnames[i]); break; } catch (e) {} + } + } + + return result; +} + +function it_boot(file, isretry) +{ + it_boot.file = file; + it_boot.sequence = isretry ? "r" : "b"; + var loader = it_boot_getloader(); + + if (loader) + { + it_boot.sequence += "l"; + loader.open("GET", it_boot_addparam(file, "boot=1" + (isretry ? "&retry=1" : ""))); + loader.onreadystatechange = function() + { + var error = "", code = ""; + + if (loader.readyState == 4) + { + if (loader.status < 400) // Opera gives back 304 if from cache + { + // check length cookie + var ln = String(loader.responseText).match(/\*sln:([0-9]+)[^\n]*\n([\s\S]*\n)\/\*eln:\1/); + code = ln ? unescape(ln[2]) : loader.responseText; + if (ln && ln[1]-0 == code.length) + { + it_boot.sequence += "e"; + if (!window.env || !!window.env.is_live_server) + code = "try {" + code + "} 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] + " != " + code.length : "no length cookie"); + } + else + error = loader.statusText; + + if (error) + { + if (isretry) + it_panic({reason:'load', error:error, data:{code:code}}); + 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&script=1')+'"><\/sc'+'ript>'); + else if (dom) + { + var tag = doc.createElement("script"); + tag.src = it_boot_addparam(file, 'boot=1&script=1&retry=1'); + dom.appendChild(tag); + } + else + window.it_domtimer = window.setTimeout("it_boot('" + file + "')" , 42); + } +} + +if (document.documentElement) + document.documentElement.className += ' js'; + diff --git a/devel-utf8/itjs/error.gif b/devel-utf8/itjs/error.gif new file mode 100644 index 0000000..d743ef1 --- /dev/null +++ b/devel-utf8/itjs/error.gif @@ -0,0 +1,11 @@ +<?php + +if ($_POST) +{ + $data = "<?php return " . var_export($_POST, true) . ";\n"; + @mkdir("/tmp/itjs"); + file_put_contents("/tmp/itjs/error-" . date("YmdHis"), $data); +} + +header("Content-Type: image/gif"); +readfile(dirname($_SERVER['SCRIPT_FILENAME']) . "/itjs/0.gif"); diff --git a/devel-utf8/itjs/http.js b/devel-utf8/itjs/http.js new file mode 100755 index 0000000..5773cda --- /dev/null +++ b/devel-utf8/itjs/http.js @@ -0,0 +1,245 @@ +/** + * Create http loader to request data from server + * + * @param cb Callback function or object with keys 'object', 'method', ['errorhandler'] + */ +function it_http(cb) +{ + this.instance = it_http.instances++; + this.callback = cb ? cb : {}; + this.req = null; + this.scrpt = []; + this.callid = 0; + this.busy = false; + + // register global reference + it_http['__inst' + this.instance] = this; +} + +/* Methods */ +it_http.prototype = { + +/* send GET request and trigger callback */ +get: function(url, callback) +{ + if (typeof callback != 'undefined') + this.callback = callback; + this.send(url, 'GET'); +}, + +/* send POST request with data and trigger callback */ +post: function(url, data, callback) +{ + if (typeof callback != 'undefined') + this.callback = callback; + + var postdata = ''; + if (typeof data == 'object') + { + for (var k in data) + postdata += (postdata ? '&' : '') + it_url_encode(k) + "=" + it_url_encode(data[k]); + } + else + postdata = data; + + this.send(url, 'POST', postdata); +}, + +/* private method that finally sends the request */ +send: function(url, method, postdata) +{ + this.stop(); + + this.busy = true; + this.req = null; + var samehost = (url.indexOf('http://') < 0 || url.indexOf(window.location.hostname) > 0); + + if (samehost) // use XMLHTTP request only if on same host + { + try + { + this.req = new XMLHttpRequest(); + } + catch (e) + { + var classnames = [ 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ]; + + for (var i=0; i < classnames.length; i++) + { + try + { + this.req = new ActiveXObject(classnames[i]); + break; + } + catch (e) { } + } + } + + try + { + this.req.open(method, url); + var me = this; + this.req.onreadystatechange = function() { me.ready_state_changed(); } + var workingxmlhttp = this.req.onreadystatechange; + + if (!workingxmlhttp) /* Old Konqueror */ + this.req = null; + } + catch (e) { } + } + + this.starttime = new Date().getTime(); + + if (this.req) + { + if (method == "POST") + this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + this.req.send(postdata); + } + else + { + url += (url.match(/\?/) ? "&" : "?") + "itjs_call=it_http.__inst" + this.instance + "&itjs_callid=" + (++this.callid) + (postdata ? '&' + postdata : ""); + + if (samehost || (window.opera && !window.XMLHttpRequest)) // Opera 7 only works with iframes + { + var scrpt = document.createElement("iframe"); + scrpt.style.width = scrpt.style.height = 1; + url += "&itjs_iframe=1"; + } + else + { + var scrpt = document.createElement("script"); + this.req = { starttime: this.starttime }; + try + { + this.scrpt[this.callid] = scrpt; + if (!document.all) scrpt.src = url; + document.body.appendChild(scrpt); + if (document.all) scrpt.src = url; + } + catch (e) { return false; } + } + } + + return true; +}, + +ready_state_changed: function() +{ + var req = this.req; // Avoid race conditions + + if (req && !req.aborted && req.readyState == 4) // Check aborted flag because IE9 throws error c00c023f when accessing aborted request + { + var data = null; + + try + { + if (req.responseText != "") + data = eval("(" + req.responseText + ")"); + } + catch (e) + { + // trigger error handler (if defined) + if (typeof this.callback == 'object' && this.callback.errorhandler) + { + var obj = this.callback.object ? this.callback.object : window; + if (typeof obj[this.callback.errorhandler] == 'function') + obj[this.callback.errorhandler](req.responseText); + } + else + ED(e, req.responseText); + } + + if (data) + this.dataReady(data, this.callid); + + this.unlink(this.callid); + } +}, + +dataReady: function(data, callid) +{ + var fixkonqueror33gcbug = this.req; + var loadtime = new Date().getTime() - this.starttime; + + this.req = null; + + if ((typeof data == "object") && (this.callid == callid)) + { + data.loadtime = loadtime; + + // trigger callback function + if (typeof this.callback == 'function') + this.callback(data); + else if (typeof this.callback == 'object' && this.callback.method) + { + // it_set(data, this.callback); + var obj = this.callback.object ? this.callback.object : window; + if (typeof obj[this.callback.method] == 'function') + obj[this.callback.method](data); + } + } +}, + +stop: function() +{ + try { + this.req.aborted = true; // Set aborted flag as IE9 throws error c00c023f when accessing aborted request + if (this.req.readyState >= 2) // Do not abort request never used before as it can throw error e.g. on Firefox + this.req.abort(); + } catch (e) { } + + this.unlink(this.callid); +}, + +unlink: function(callid) +{ + if (this.req) + this.req = null; + + if (this.scrpt[callid]) + { + if (!(document.all && String(navigator.userAgent).indexOf('MSIE 5.0') > 0)) + document.body.removeChild(this.scrpt[callid]); + this.scrpt[callid] = null; + } + + this.busy = false; +} /* NO COMMA */ + +} + + +// static properties +it_http.instances = 0; + +// static methods +it_http.get_instance = function() +{ + var inst; + for (var i=0; i < it_http.instances; i++) + if ((inst = it_http['__inst'+i]) && inst.pub && !inst.busy) + return inst; + + inst = new it_http(); + inst.pub = true; + return inst; +} + +/* send GET request and trigger callback */ +it_http.get = function(url, callback) +{ + var inst = it_http.get_instance(); + inst.callback = callback; + inst.get(url); +} + +/* send POST request and trigger callback */ +it_http.post = function(url, postdata, callback) +{ + var inst = it_http.get_instance(); + inst.callback = callback; + inst.post(url, postdata); +} + diff --git a/devel-utf8/itjs/it.js b/devel-utf8/itjs/it.js new file mode 100644 index 0000000..54fa5c7 --- /dev/null +++ b/devel-utf8/itjs/it.js @@ -0,0 +1,297 @@ +// $Id$ + +/** + * Clear contents of element 'jsdebug' + */ +function CED(txt) +{ + var element = document.getElementById('jsdebug'); + if (element) + element.innerHTML = txt ? txt : ""; +} + +/** + * Return string with dump of all arguments + */ +function D() +{ + var text = ""; + for (var i = 0; i < arguments.length; i++) + { + var variable = arguments[i]; + + if (typeof variable == "string") + variable = variable.replace(/&/g, '&').replace(new RegExp("<", "g"), '<'); + + 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"; + } + return text; +} + +/** + * Add debugging output to element 'jsdebug' + */ +function ED() +{ + var element; + + if (!(element = document.getElementById('jsdebug'))) + return; // var element = it_create_element(document.body, 'div', {id:'jsdebug', style:{position:'absolute', left:'100px', top:'10px', opacity:'0.8'}}) + + element.innerHTML += '<pre style="background-color:#FEE; margin:0">' + D.apply(this, arguments) + '<' + '/pre>'; +} + +/** + * Quote HTML special chars + * @return Text string with & " < > htmlentities-encoded + */ +function Q(value) +{ + return typeof value == "undefined" ? "" : value.toString().replace(/&/g, '&').replace(/\"/g, '"').replace(new RegExp("<", "g"), '<').replace(/>/g, '>'); +} + +/** + * 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; +} + +function it_add_event(p) +{ + if (!p.object || !p.method) // not enough arguments + return; + if (!p.element) + p.element = document; + + if (!p.object._it_events) + p.object._it_events = []; + + var evt = p.event; + var key = p.event + '*' + p.method; + var p_closure = p; // Needed for Konqueror 3.4.2 as p is (wrongly) shadowed by global input element named p + if (!p.object._it_events[key]) + p.object._it_events[key] = function(e){ return p_closure.object[p_closure.method](e, p_closure); }; + + if (p.element.addEventListener) + p.element.addEventListener(evt, p.object._it_events[key], false); + else if (p.element.attachEvent) + p.element.attachEvent('on'+evt, p.object._it_events[key]); + else + { + p.element['on'+evt] = function(e) + { + var ret = true; + for (var k in p_closure.object._it_events) + if (p_closure.object._it_events[k] && k.indexOf(evt) == 0) + ret = p_closure.object._it_events[k](e); + return ret; + }; + } +} + +function it_remove_event(p) +{ + if (!p.element) + p.element = document; + + var key = p.event + '*' + p.method; + if (p.object && p.object._it_events && p.object._it_events[key]) { + if (p.element.removeEventListener) + p.element.removeEventListener(p.event, p.object._it_events[key], false); + else if (p.element.detachEvent) + p.element.detachEvent('on'+p.event, p.object._it_events[key]); + + p.object._it_events[key] = null; + } +} + +/** + * Prevent event propagation and bubbeling + */ +function it_event_void(evt) +{ + var e = evt ? evt : window.event; + if (e.preventDefault) + e.preventDefault(); + if (e.stopPropagation) + e.stopPropagation(); + + e.cancelBubble = true; + e.returnValue = false; + return false; +} + +/* 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; +} + +/* Common accessor for dom elements */ +function it_find_obj(obj) +{ + if (document.getElementById) + return document.getElementById(obj); + else if (document.all) + return document.all[obj]; + else if (document.layers) + return document.layers[obj]; + return null; +} + +/* Get dom element by ID but always return a valid object */ +function it_element(label) +{ + var tmp = it_find_obj(label); + return tmp ? tmp : { style:{}, src:"", value:"", isundefined:true }; +} + +/* Get an iframe's content document in a compatible way */ +function it_get_iframe_document(iframe) +{ + return iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument; +} + +/* Create a new dom element and append to doc */ +function it_create_element(doc, type, init) +{ + var e = document.createElement(type); + it_set(e, init); + doc.appendChild(e); + return e; +} + +/** + * 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') + { + if (dst[i]) + 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(/%3B/gi, ";").replace(/%28/gi, "(").replace(/%29/gi, ")"); +} + +/** + * Patch PNG transparency for IE 5.5-6 on the given image + */ +function it_pngfix(img, w, h, mode) +{ + var old_IE = navigator.platform == "Win32" && String(navigator.userAgent).match(/MSIE ((5\.5)|6)/); + if (img.src && img.src.match(/\.png($|\?)/) && old_IE) { + img.style.width = (w || img.width) + 'px'; + img.style.height = (h || img.height) + 'px'; + img.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+img.src+"',sizingMethod='"+(mode ? mode : 'crop')+"')"; + img.src = '/images/0.gif'; + } + else if (img && old_IE) + img.style.filter = 'none'; +} + diff --git a/devel-utf8/itjs/loader.js b/devel-utf8/itjs/loader.js new file mode 100644 index 0000000..9fdb9a8 --- /dev/null +++ b/devel-utf8/itjs/loader.js @@ -0,0 +1,141 @@ +/** + * Create loader to request data from server + * Uses it_http class for communication + * + * @param handler Object providing clear()/render() function when data arrives + */ +function it_loader(handler) +{ + /* Clear cache etc. if completely new data */ + this.http = null; + this.handler = handler; + this.callback = { object:this, method:'dataReady', errorhandler:'onerror' }; + 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 }; + + 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; + + this.stop(); + + while ((num > 0) && (typeof this.entry[pos] != "undefined")) + { + pos++; + num--; + } + + if (this.attr.eof) + num = Math.min(num, this.end - pos); + + if (num > 0) + { + this.retry = retry; + this.http = it_http.get_instance(); + this.http.get(baseurl + (baseurl.match(/\?/) ? "&" : "?") + "pos=" + pos + "&num=" + num + (retry ? "&retry=" + retry : "") + (query_volatile ? query_volatile : ""), this.callback); + } + else + this.handler.render(this); + + return true; +}, + +/* deprecated: use it_http::post() instead */ +post: function(baseurl, data) +{ + this.clear(); + this.http = it_http.get_instance(); + this.http.post(baseurl, data, this.callback); +}, + +retryload: function(p) +{ + this.load(p.baseurl, p.pos, p.num, p.query_volatile, p.retry); +}, + +dataReady: function(data) +{ + if ((typeof data == "object")) + { + this.attr = {}; + + 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.http = null; + } +}, + +onerror: function(response) +{ + 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(response); +}, + +stop: function() +{ + if (this.http) + this.http.stop(); +} /* NO COMMA */ + +} + +// static properties +it_loader.sequence = ""; diff --git a/devel-utf8/itjs/state.html b/devel-utf8/itjs/state.html new file mode 100644 index 0000000..36f36c0 --- /dev/null +++ b/devel-utf8/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/devel-utf8/itjs/state.js b/devel-utf8/itjs/state.js new file mode 100644 index 0000000..e5e7c97 --- /dev/null +++ b/devel-utf8/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/devel-utf8/itjs/timer.js b/devel-utf8/itjs/timer.js new file mode 100644 index 0000000..ee04cbb --- /dev/null +++ b/devel-utf8/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); +} |