|
WT Toolkit 0.3.3 | |||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||
This file contains the most basic building blocks of WT Toolkit, such as the event handling system, the garbage collector,
and the AJAX RPC system.
| Class Summary | |
| _wtAsyncRPC | An internal AJAX RPC control object. |
| wtObject | wtObject is the base class of most other classes in WT Toolkit. |
| WTObjectManager | The WT Toolkit object manager singleton manages the weak reference emulation logic. |
| wtRemoteProcedureCall | An AJAX RPC object can be used to make AJAX-style calls back to the web server. |
| Method Summary | |
static Boolean
|
belongsToDocumentTree(<wtWidget> node)
Does this wtWidget belong to the DOM document tree? |
static void
|
clearCookie(<String> key)
Clears a cookie. |
static void
|
clearExpando(<Object> obj)
Clears all expando properties from an object in Internet Explorer. |
static void
|
CollectGarbage()
A stop-the-world, mark-and-sweep garbage collector, meant to be called internally by WT Toolkit only. |
static Integer
|
countKeys(<Object> obj)
Counts the number of keys inside a JavaScript object |
static String
|
dimension(dm)
Converts an integer to a CSS length quantity with the unit "px". |
static String
|
genId()
Generates a short random ID. |
static Object
|
getClientDimensions()
Tells the dimensions of browser's display area |
static String
|
getCookie(<String> key)
Gets a cookie. |
static String
|
getIEVersion()
Tells the Internet Explorer major version |
static Boolean
|
isDOMNode(x)
Is the data type DOM node? |
static Boolean
|
isInteger(x)
Is the data type integer? |
static Boolean
|
isMSIE()
Are we running in Internet Explorer or not? |
static Boolean
|
isNone(x)
Is the data type undefined or null? |
static Boolean
|
isNumber(x)
Is the data type a number? |
static Boolean
|
isString(x)
Is the data type string? |
static void
|
printDebug(msg)
Prints a debug message to window.debugView (if there is one) |
static void
|
printKeys(obj)
Prints all the keys of a JavaScript object in window.debugView (if there is one) |
static String
|
randomId()
Generates a long random ID |
static void
|
setCookie(<String> key, <String> value)
Sets a cookie. |
// vim: set tabstop=4 shiftwidth=4 foldmethod=marker: /* WT Toolkit - Web UI logic toolkit. Copyright (C) 2006 Kou Man Tong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * $Log: wt_core.js,v $ * Revision 1.28 2007/03/27 20:37:41 francium * -Fixed: DOM nodes with wtDependency are not checked by the garbage collector * correctly * -Added: wtGraph class * * Revision 1.27 2007/03/24 07:44:12 francium * -Added: WTObjectManager::scheduleGC() which enables the programmer to schedule * GC more liberally without worrying about repeated garbage collections * -Improved: wtGraphEdge can now let the user add/remove anchor points and * change the coordinates of anchor points by mouse actions * * Revision 1.26 2007/03/22 13:20:44 francium * *** empty log message *** * * Revision 1.25 2007/03/16 07:37:45 francium * -Fixed: wtRoot CSS class missing from wtWidget * -Added: wt_vector.js as one of the include JS files from wt_core.js * * Revision 1.24 2007/03/12 11:51:29 francium * -Removed: wt_widget_svg.js from being included from wt_core.js * * Revision 1.23 2007/03/09 16:56:44 marco * *** empty log message *** * * Revision 1.22 2007/03/08 17:57:53 francium * -Fixed: Disabling a wtSpreadsheet should have visual effects on the widget to * indicate that it is disabled to the user * -Added: JSDoc entries in wt_form.js * * Revision 1.21 2007/03/08 05:23:08 francium * -Added: JSDoc documentation compiler under redist * -Added: JSDoc entries in wt_widget.js * -Added: JSDoc entries in wt_algor.js * -Added: JSDoc entries in wt_wm.js * -Added: JSDoc entries in wt_mouse.js * -Added: JSDoc entries in wt_core.js * -Fixed: Random MSIE() not defined errors during page load * * Revision 1.20 2007/03/07 17:35:17 francium * -Added: JSDoc entries in wt_widget.js * * Revision 1.19 2007/02/20 00:29:58 francium * -Changed: Renamed wtObject::wtDependencies to wtObject::wtDependency and the * attribute is no longer an array * -Changed: Manual GC is now the default behavior * -Removed: window.disableGCThread flag since GC Thread is no longer the default * -Changed: Added a "?tx=ab" dummy argument after .js file references in HTML * files to avoid version synchronization problems * * Revision 1.18 2007/02/17 19:45:41 francium * -Improved: Better algorithm for CollectGarbage() * * Revision 1.17 2007/02/14 16:01:20 francium * -Fixed: Cached .JS and .CSS files on client side may not be in sync with the * server version * * Revision 1.16 2007/02/14 12:48:33 francium * -Added: window.disableGCThread flag to disable garbage collection thread * -Added: CollectGarbage() function for manually cleaning up no-longer used * weak references * * Revision 1.15 2007/01/18 19:33:53 francium * -Added: wt_algor.js for data structure and algorithm classes * * Revision 1.14 2007/01/12 16:24:33 francium * -Improved: Added a simple optimization to garbage collection helper algorithm * * Revision 1.13 2007/01/11 16:52:27 francium * -Fixed: Number of mandatory arguments for wtRemoteProcedureCall incorretly set * to 3, should be 2 instead * -Added: wtObject::addDependency() function * -Fixed: wtWindow::ucenter() does not work * -Fixed: wtModalWindow positioning algorithm does not work * * Revision 1.12 2007/01/11 11:54:22 francium * -Changed: Finished porting wt_form.js to 0.2.0 object structure * -Added: Unit test cases for all wt_form.js objects * -Changed: Generalized domDependencies into wtDependencies in garbage collector * helper algorithm * -Fixed: wtWindow::clearAll() crashes Internet Explorer * * Revision 1.11 2007/01/08 16:48:42 francium * -Fixed: wtRemoteProcedureCall:setHandler() and setFailHandler() not working * -Added: Unit test case for wtRemoteProcedureCall * * Revision 1.10 2007/01/07 18:25:07 francium * -Fixed: wtRemoteProcedureCall object cannot be created * -Changed: Converted wtForm, wtFormWidget and wtSingleLineTextInput to use new * object system * -Added: prevValue and getPrevValue() to wtFormWidget * -Added: prevValue as an argument to wtSingleLineTextInput validator * -Added: ValueChanged signal to wtForm * -Changed: General format of arguments to ValueChanged signal * * Revision 1.9 2007/01/06 17:42:17 francium * -Added: Weak reference emulation, proxy pattern and observer pattern * implenentation via wtObject * -Changed: Converted wt_core.js, wt_mouse.js, wt_wm.js, wt_widget.js to use * prototype based inheritance in Javascript and wtObject as the root * superclass * -Added: Unit test cases for all widgets in wt_widget.js and wt_wm.js * * Revision 1.8 2006/12/26 07:29:28 francium * -Changed: Improved GC Helper performance for Internet Explorer * * Revision 1.5 2006/12/24 16:04:04 francium * -Fixed: Memory leakage from improper use of observer pattern * -Fixed: Hidden form widget not working * * Revision 1.4 2006/12/24 12:39:36 francium * -Fixed: Converted Javascript source files to Unix format * * Revision 1.3 2006/12/24 12:29:16 francium * -Fixed: wtForm::submitExclude and wtRemoteProcedureCall::submitExclude * re-enables widgets that are disabled before sending RPC request to * server * -Fixed: Wrongly placed copyright notice, should be near the top of the file * * $Id: wt_core.js,v 1.28 2007/03/27 20:37:41 francium Exp $ */ /** @fileoverview This file contains the most basic building blocks of WT Toolkit, such as the event handling system, the garbage collector, and the AJAX RPC system. */ // {{{ Misc. utilities /** Are we running in Internet Explorer or not? @returns true if runningin Internet Explorer @type Boolean */ function isMSIE() { return navigator.userAgent.search("MSIE") != -1; } /** Tells the Internet Explorer major version @returns IE version number @type String */ function getIEVersion() { var a = navigator.userAgent.match("MSIE ([0-9.]+)"); if (a) return a[1]; return null; } /** Is the data type undefined or null? @param Object whose data type is to be tested @returns true if undefined or null @type Boolean */ function isNone(x) { return x == undefined || x == null; } /** Is the data type integer? @param Object whose data type is to be tested @returns true if object is integer @type Boolean */ function isInteger(x) { return parseInt(x) == x; } /** Is the data type a number? @param Object whose data type is to be tested @returns true if the object is a number @type Boolean */ function isNumber(x) { if (typeof(x) != "string" && typeof(x) != "number") return false; var re = new RegExp("^[0-9.]+$"); return String(x).search(re) != -1; } /** Is the data type DOM node? @param Object whose data type is to be tested @returns true if the object is a DOM node @type Boolean */ function isDOMNode(x) { if (x == null || x == undefined) return false; return typeof(x.tagName) == "string"; } /** Is the data type string? @param Object whose data type is to be tested @returns true if the object is a string @type Boolean */ function isString(x) { if (x == null || x == undefined) return false; return typeof(x.charAt) == "function"; } /** @private */ function checkBrowser() { var bv = navigator.userAgent; var warnstr = "WT Toolkit Warning:\nInternet Explorer 6.0+ or Firefox 1.5+ required.\nFirefox 1.0.x, Mozilla 1.x, IE5, Opera, etc. are not supported.\n"; if (bv.search("Gecko") != -1) { var geckoVer = bv.match(new RegExp("Gecko/([0-9]+)"))[1]; if (parseInt(geckoVer) < 20051111) alert(warnstr); } else if (bv.search("MSIE") != -1) { var IEVer = bv.match(new RegExp("MSIE ([0-9]+)"))[1]; if (parseInt(IEVer) < 6) alert(warnstr); } else alert(warnstr); } checkBrowser(); /** Converts an integer to a CSS length quantity with the unit "px". Does not change the input if it is not an integer. This function is used for "cleaning" CSS length quantity inputs. @param CSS length quantity or integer. @returns CSS length quantity. @type String */ function dimension(dm) { if (isInteger(dm)) return dm + "px"; if (!dm || dm == "") return "auto"; return dm; } /** Prints all the keys of a JavaScript object in window.debugView (if there is one) @param {Object} JavaScript object to be printed */ function printKeys(obj) { var ss = ""; for(var i in obj) ss += i+"\n"; printDebug(ss); } /** Prints a debug message to window.debugView (if there is one) @param {String} Debug message */ function printDebug(msg) { if (window.debugView) $_(window.debugView.addWidget(msg+"\n")).scrollIntoView(false); } /** Generates a short random ID. @returns Random ID @type String */ function genId() { var a = parseInt(Math.random() * 100000); var b = parseInt(Math.random() * 100000); return "_"+a+"_"+b; } /** Sets a cookie. @param {String} key Cookie key @param {String} value Cookie value */ function setCookie(key, value) { document.cookie = key + "=" + encodeURIComponent(value); } /** Gets a cookie. Returns null if cookie not found. @param {String} key Cookie key @returns Cookie value @type String */ function getCookie(key) { var cookies = document.cookie.split("; "); var ss = key + "="; var i; for (i=0;i<cookies.length;i++) { if (cookies[i].search(ss) == 0) return unescape(cookies[i].substr(ss.length)); } return null; } /** Clears a cookie. @param {String} key Cookie key to be cleared */ function clearCookie (key) { var d = new Date(); d.setTime(0); document.cookie = key + "=; expires=" + d.toGMTString(); } /** Tells the dimensions of browser's display area @returns "width": width of display area, "height": height of display area @type Object */ function getClientDimensions() { if (isMSIE()) return {"width" : document.documentElement.offsetWidth, "height" : document.documentElement.offsetHeight}; else return {"width" : window.innerWidth, "height" : window.innerHeight}; } /** Generates a long random ID @returns Random ID @type String */ function randomId() { return String(parseInt(Math.random() * 100000)) + String(parseInt(Math.random() * 100000)) + String(parseInt(Math.random() * 100000)) + String(parseInt(Math.random() * 100000)); } /** Counts the number of keys inside a JavaScript object @param {Object} obj The JavaScript object to be examined. @returns Number of keys @type Integer */ function countKeys(obj) { var count = 0; for(var i in obj) count++; return count; } // }}} // {{{ wtIncludeJS() and wtIncludeCSS() /** Includes a JavaScript file @param {String} URL to JavaScript file */ var wtIncludeJS = function(filename) { var str = "<script language='javascript' src='" + filename + "?tx=ab'></script>"; document.write(str); } /** Includes a CSS style sheet @param {String} URL to CSS style sheet */ var wtIncludeCSS = function(filename) { var str = "<link rel='stylesheet' type='text/css' href='"+filename+"?tx=ab'></link>"; document.write(str); } // }}} // {{{ wtObject skeleton - implements a proxy so that memory-expensive objects (e.g. DOM objects) can be referenced via a very cheap object /** Constructs a wtObject @constructor @class wtObject is the base class of most other classes in WT Toolkit. It simulates weak references and a proxy pattern such that memory-expensive objects (e.g. DOM nodes) can be referenced from other objects via a very cheap object. This helps mitigate the lapsed listener problem and avoids circular referencing memory leaks in Internet Explorer 6. */ var wtObject = function() { } /** Establishes a weak reference link to a memory expensive object. @param {String} obj The memory expensive object. @param {String} id (Optional) The ID of the weak reference. Leave this empty if you don't know what you're doing. */ wtObject.prototype.startProxy = function(obj, id) { if (id) { this.wtObjId = id; wtObjMgr.putId(id, obj); } else this.wtObjId = $$(obj); obj.wtObj = this; if (window.wtObjMgr) window.wtObjMgr.scheduleGC(); } /** Disconnects the weak reference and removes the memory expensive object from the weak reference table. */ wtObject.prototype.endProxy = function() { if ($_(this)) { $_(this).wtObj = undefined; wtObjMgr.pop(this); } delete this.wtObjId; } /** Tells the string ID of the weak reference. @returns String ID of weak reference. @type String */ wtObject.prototype.toString = function() { return this.wtObjId ? this.wtObjId : ""; } /** Connects a signal to a slot. <br/><br/> Signals of a wtObject are defined via the setSignal() operation. <br/> Slots of a wtObject are defined via the setSlot() operation. <br/> Slots are named function objects stored under a wtObject's weakly referenced image. <br/><br/> All slot function objects have a common argument format:<br/> <i>slotFunction(target, evt, source)</i><br/><br/> Where target is the object holding the slot function, source is the object receiving the event, and evt is the event-specific argument object. @param {String} connId The signal-slot connection ID, this ID can be used later to disconnect a signal from a slot. Each signal-slot connection ID should be unique for the same signal source and signal name. @param {String} signalName The signal name. @param {wtObject} target The target to deliever the event information to. @param {String} slotName The slot name. @see #setSignal @see #setSlot @see #emit @see #disconnect */ wtObject.prototype.connect = function(connId, signalName, target, slotName) { // check if the proxy objects can be converted into real objects if (!$_(this)) throw("wtObject::connect(): " + connId + "'s source '" + this + "' does not exist"); if (!$_(target)) throw("wtObject::connect(): " + connId + "'s target '" + target + "' does not exist"); // check if the signals and slots really do exist if ($_(this)["wtSignals"] == undefined) throw("wtObject::connect(): " + connId + " source doesn't have signals"); if ($_(target)["wtSlots"] == undefined) throw("wtObject::connect(): " + connId + " target doesn't have slots"); if ($_(this).wtSignals[signalName] == undefined) throw("wtObject::connect(): " + connId + " source doesn't have a '" + signalName + "' signal"); if ($_(target).wtSlots[slotName] == undefined) throw("wtObject::connect(): " + connId + " target doesn't have a '" + slotName + "' slot"); // construct the link, weak linking is used for the target // to further limit memory leakage $_(this).wtSignals[signalName][connId] = [target.toString(), slotName]; } /** Disconnects a signal from a slot. @param {String} connId The signal-slot connection ID @param {String} signalName The signal name */ wtObject.prototype.disconnect = function(connId, signalName) { // check if the source and the signal exist if (!$_(this)) throw("wtObject::disconnect(): Source '" + this + "' does not exist"); if ($_(this)["wtSignals"] == undefined) throw("wtObject::disconnect(): " + connId + " source doesn't have signals"); if ($_(this).wtSignals[signalName] == undefined) throw("wtObject::disconnect(): " + connId + " source doesn't have a '" + signalName + "' signal"); // disconnect the observer delete $_(this).wtSignals[signalName][connId]; } /** Emits a signal with an event argument. @param {String} signalName Signal name to be emitted. @param {Object} evt Event argument object. */ wtObject.prototype.emit = function(signalName, evt) { if (! $_(this)) //throw "wtObject::emit(): The proxy hasn't started yet!"; return; for(var i in $_(this).wtSignals[signalName]) { var target = $_(this).wtSignals[signalName][i][0]; if (!$_(target) || !_$($_(target))) { delete $_(this).wtSignals[signalName][i]; continue; } var slotName = $_(this).wtSignals[signalName][i][1]; if (!$_(target).wtSlots || !$_(target).wtSlots[slotName]) { delete $_(this).wtSignals[signalName][i]; continue; } if (typeof($_(target).wtSlots[slotName]) == "function") $_(target).wtSlots[slotName](_$($_(target)), evt, this); } } /** Assigned an event handler function to a named slot. @param {String} slotName The slot's name @param {Function} func The event handler function */ wtObject.prototype.setSlot = function(slotName, func) { if (! $_(this)) throw "wtObject:setSlot(): The proxy hasn't started yet!"; var slots = $_(this).wtSlots; if (!slots) { $_(this).wtSlots = {}; slots = $_(this).wtSlots; } slots[slotName] = func; } /** Removes a named slot @param {String} slotName The slot's name */ wtObject.prototype.removeSlot = function(slotName) { delete $_(this).wtSlots[slotName]; } /** Creates a named signal @param {String} signalName The signal's name */ wtObject.prototype.setSignal = function(signalName) { if (!$_(this).wtSignals) $_(this).wtSignals = {} $_(this).wtSignals[signalName] = {}; } /** Removes a named signl @param {String} signalName The signal's name */ wtObject.prototype.removeSignal = function(signalName) { if ($_(this).wtSignals && $_(this).wtSignals[signalName]) delete $_(this).wtSignals[signalName]; } /** Tells the value of an attribute under the weakly referenced image. @param {String} name The attribute name @returns The attribute value @type Object */ wtObject.prototype.get = function(name) { return $_(this)[name]; } /** Sets an attribute value under the weakly referenced image. @param {String} name The attribute name @param {Object} value The attribute value */ wtObject.prototype.set = function(name, value) { $_(this)[name] = value; } /** Assigns a dependency relation to this object for garbage collection purpose. @param {wtObject} obj The wtObject this object is dependent upon. If <i>obj</i> is garbage collected then this object would be garbage collected as wel. */ wtObject.prototype.addDependency = function(obj) { $_(this).wtDependency = obj.toString(); } /** Clears all lapsed event listeners attached to this object. */ wtObject.prototype.clearLapsedListeners = function() { if (! $_(this) || ! this.get("wtSignals")) return; var signals = this.get("wtSignals"); for(var i in signals) { for(var j in signals[i]) if (! $_(signals[i][j])) delete signals[i][j]; } } // }}} // {{{ _wtAsyncRPC() - do an RPC to web server and return data /** XMLHttpRequest object creator. This function handles how the XMLHttpRequest object should be created in different browsers. */ var _wtXMLHttp = function() { var retval; if (isMSIE()) retval = new ActiveXObject("Microsoft.XMLHTTP"); else retval = new XMLHttpRequest(); return retval; } // _wtAsyncRPC object // // methods: // _wtAsyncRPC() - the constructor // _wtAsyncRPC::setHandler(function(Object resultObj)) - what to do if the request succeeds? // _wtAsyncRPC::setFailHandler(function(Integer state, Integer status)) - what to do if the server does not reply? // state: the state of the X|v|L|-|ttp|2equ3st when the object has considered the RPC failed // status: the status of the returned HTTP message if the server has returned something (e.g. 404, 500) // _wtAsyncRPC::setTimeout(Integer milliseconds) - how long do we wait for reply before invoking failHandler? // _wtAsyncRPC::submit() - submit the remote procedure // // properties: // _wtAsyncRPC::requestObj - the X|v|L|-|ttp|2equ3st Object // _wtAsyncRPC::timeout - AJAX request timeout in milliseconds // _wtAsyncRPC::onSuccess(Object resultObj) - submitback function for successful operation // _wtAsyncRPC::onFailure(Integer state, Integer status, String text) - submitback function for failed operation // _wtAsyncRPC::cgiPath - path to cgi // _wtAsyncRPC::procName - remote function name // _wtAsyncRPC::procArgs - arguments to remote function // _wtAsyncRPC::tid - timer handler // _wtAsyncRPC::processed - was the request processed? /** @private */ window.RPCObjects = {}; /** Constructs an internal AJAX RPC control object. @class An internal AJAX RPC control object. This class is only useful if you need to modify WT Toolkit, use wtRemoteProcedureCall instead if you just want to make AJAX-style requests back to your web server. @see wtRemoteProcedureCall @constructor */ function _wtAsyncRPC(cgi, procName, procArgs) { if (arguments.length < 3) return; this.requestObj = null; this.timeout = 5000; this.onSuccess = function(resultObj){alert("Result of " + procName + "\n" + JSON.stringify(resultObj));} this.onFailure = function(readyState, status, text) { alert(procName + " failed\n" + "readyState = " + readyState + "\n" + "status = " + status + "\n"); } this.cgiPath = cgi; this.procName = procName; this.procArgs = procArgs; this.processed = false; } _wtAsyncRPC.prototype = new wtObject; _wtAsyncRPC.prototype._submit = function() { this.processed = false; var poststr = encodePOST(this.procName, this.procArgs); this.requestObj = _wtXMLHttp(); this.requestObj.open("POST", this.cgiPath); var objId = randomId(); window.RPCObjects[objId] = this; this.tid = setTimeout("_wtAsyncRPC_timeoutFunc('" + objId + "')", this.timeout); this.requestObj.onreadystatechange = new Function("_wtAsyncRPC_requestObj_onreadystatechange('"+ objId +"');"); this.requestObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); this.requestObj.send(poststr); } _wtAsyncRPC.prototype.setHandler = function(fn) { this.onSuccess = fn; } _wtAsyncRPC.prototype.setFailHandler = function(fn) { this.onFailure = fn; } _wtAsyncRPC.prototype.setTimeout = function(fn) { this.timeout = fn; } // to toolkit hackers: don't prototype-ize the following two functions. Dragons ahead. :) /** @private */ function _wtAsyncRPC_requestObj_onreadystatechange(objId) { var myself = window.RPCObjects[objId]; if (myself.requestObj.readyState == 4 && (!myself.processed)) { myself.processed = true; delete window.RPCObjects[objId]; clearTimeout(myself.tid); try { if (myself.requestObj.status == 200) myself.onSuccess(JSON.parse(myself.requestObj.responseText)); else myself.onFailure(myself.requestObj.readyState, myself.requestObj.status, myself.requestObj.responseText); } catch(e) { myself.onFailure(myself.requestObj.readyState, 0, myself.requestObj.responseText); } } } /** @private */ function _wtAsyncRPC_timeoutFunc(objId) { var myself = window.RPCObjects[objId]; if (!myself.processed) { myself.processed = true; delete window.RPCObjects[objId]; myself.requestObj.abort(); myself.onFailure(myself.requestObj.readyState, 0); } } var encodePOST = function(action, args) { var retval = "ProcName="; retval += encodeURIComponent(action); retval += "&ProcArgs="; retval += encodeURIComponent(JSON.stringify(args)); return retval; } // }}} // {{{ wtRemoteProcedureCall - RPC with signal/slot event handling /** Constructs an AJAX RPC object. @constructor @class An AJAX RPC object can be used to make AJAX-style calls back to the web server. <br/><br/> Available signals: <br/> <ul> <li>Success</li> <li>Failure</li> <li>BeforeRPC</li> <li>AfterRPC</li> </ul> @param {String} cgi URL path to CGI script @param {String} procName The RPC call name to be passed to the CGI script, this is used to distinguish between different types of RPC calls to the same CGI script @param {Object} procArgs The RPC call arguments to be passed to the CGI script */ var wtRemoteProcedureCall = function (cgi, procName, procArgs) { if (arguments.length < 2) return; this.base = _wtAsyncRPC; this.base(cgi, procName, procArgs); this.startProxy(this); this.setSignal("Success"); this.setSignal("Failure"); this.setSignal("BeforeRPC"); this.setSignal("AfterRPC"); /** A dictionary of widgets to be disabled during an AJAX RPC call. This dictionary is useful when you need to, for example, gray out a few buttons when an RPC call is being processed at the sever side. */ this.submitExclude = {}; // these two closures are safe from leakage because the Microsoft.XMLHTTP ActiveX object // is only created when submit() is called - it has not been created here var successHandler = function(resultObj) { var event = resultObj; this.emit("Success", event); this.emit("AfterRPC", {}); } var failureHandler = function(readyState, status, text) { var event = {"readyState" : readyState, "status" : status, "text" : text}; this.emit("Failure", event); this.emit("AfterRPC", {}); } this.setHandler(successHandler); this.setFailHandler(failureHandler); var abortHandler = function(myself, evt, source) { myself.processed = true; myself.requestObj.abort(); myself.emit("Failure", {"readyState" : 4, "status" : 0, "text" : ""}); myself.emit("AfterRPC", {}); } var callExclude = function(myself, evt, source) { myself.disabledWidgets = {}; for(var i in myself.submitExclude) { var widget = _$($_(myself.submitExclude[i])); if (!widget) { delete myself.submitExclude[i]; continue; } if (widget.isEnabled()) { widget.setEnabled(false); myself.disabledWidgets[i] = true; } else myself.disabledWidgets[i] = false; } } var revertCallExclude = function(myself, evt, source) { for(var i in myself.submitExclude) { var widget = _$($_(myself.submitExclude[i])); if (!widget) { delete myself.submitExclude[i]; continue; } if (myself.disabledWidgets[i] == true) widget.setEnabled(true); } } this.setSlot("Abort", abortHandler); this.setSlot("CallExclude", callExclude); this.setSlot("RevertCallExclude", revertCallExclude); this.connect("submit_exclude", "BeforeRPC", this, "CallExclude"); this.connect("revert_submit_exclude", "AfterRPC", this, "RevertCallExclude"); } wtRemoteProcedureCall.prototype = new _wtAsyncRPC; /** Sends to AJAX RPC call to web server. */ wtRemoteProcedureCall.prototype.submit = function() { this.emit("BeforeRPC", this.procArgs); this._submit(); } /** Aborts an ongoing AJAX RPC call. */ wtRemoteProcedureCall.prototype.abort = function() { $_(this).wtSlots.Abort(this, {}); } /** @private */ wtRemoteProcedureCall.prototype.__wtAsyncRPC_setHandler = _wtAsyncRPC.prototype.setHandler; /** Sets the RPC call success handler. @param {Function} hdl Success event handler. Input arguments of the handler follow the universal event handler format in WT Toolkit. @see wtObject#connect */ wtRemoteProcedureCall.prototype.setHandler = function(hdl) { if (!this.get("wtSlots")) { return this.__wtAsyncRPC_setHandler(hdl); } this.setSlot("SuccessHandler", hdl); this.connect("submit_success", "Success", this, "SuccessHandler"); } /** Alias to setHandler(). @see #setHandler @type Function */ wtRemoteProcedureCall.prototype.setSuccessHandler = wtRemoteProcedureCall.prototype.setHandler; /** @private */ wtRemoteProcedureCall.prototype.__wtAsyncRPC_setFailHandler = _wtAsyncRPC.prototype.setFailHandler; /** Sets the RPC call failure handler @param {Function} hdl Failure event handler Input arguments of the handler follow the universal event handler format in WT Toolkit. @see wtObject#connect */ wtRemoteProcedureCall.prototype.setFailHandler = function(hdl) { if (!this.get("wtSlots")) { return this.__wtAsyncRPC_setFailHandler(hdl); } this.setSlot("FailureHandler", hdl); this.connect("submit_failure", "Failure", this, "FailureHandler"); } /** Alias to setFailHandler(). @see #setFailHandler @type Function */ wtRemoteProcedureCall.prototype.setFailureHandler = wtRemoteProcedureCall.prototype.setFailHandler; // }}} // {{{ The WT toolkit object manager (singleton object) /** Constructs a WT Toolkit object manager singleton. @class The WT Toolkit object manager singleton manages the weak reference emulation logic. @constructor */ function WTObjectManager() { if (window.wtObjMgr) alert("Error: You can only create a single WTObjectManager"); /** The weak reference images are stored here. */ this.objects = {}; /** All weak reference IDs. @type Array */ this.keys = []; } /** Builds a weak refrence for an arbitary object. @param {Object} obj An arbitary object. @returns Weak reference ID @type String */ WTObjectManager.prototype.put = function(obj) { var id = randomId(); this.objects[id] = obj; obj.wtObjId = id; this.keys.push(id); return id; } /** Builds a weak reference for an arbitary object, with a specific weak reference ID. @param {String} id The weak reference ID specified. @param {String} obj An arbitary object. @returns Weak reference ID @type String */ WTObjectManager.prototype.putId = function(id, obj) { if (this.objects[id] == undefined) this.keys.push(id); this.objects[id] = obj; obj.wtObjId = id; return id; } /** Tells the weakly referenced object behind a weak reference ID. @param {String} id The weak reference ID. @returns An object, or null if reference invalid. @type Object */ WTObjectManager.prototype.get = function(id) { var retval = this.objects[id.toString()]; if (!retval) return null; return retval; } /** Removes a weak reference. @param {String} id The weak reference ID. @returns The previously referenced object, or null if reference invalid. @type Object */ WTObjectManager.prototype.pop = function(id) { id = id.toString(); var retval = this.get(id); if (retval) delete this.objects[id]; // this.keys not updated - it will be updated by gc helper thread return retval; } /** Tells all weak reference IDs in existence. @returns All weak reference IDs. @type Array */ WTObjectManager.prototype.getKeys = function() { return this.keys; } /** Displays the current number of weak references in existence with a dialog box. */ WTObjectManager.prototype.getMemoryStats = function() { var count = 0; for(var i in this.objects) count++; alert("Number of objects managed: " + count); } /** Tells the current count of all weak references in existence. @returns Number of weak references managed by WT Toolkit. @type Integer */ WTObjectManager.prototype.count = function() { var count = 0; for(var i in this.objects) count++; return count; } WTObjectManager.prototype.fastCount = function() { return this.keys.length; } WTObjectManager.prototype.scheduleId = null; WTObjectManager.prototype.scheduleGC = function() { if (this.lastGCCount == undefined) this.lastGCCount = 0; if (this.lastGCTime == undefined) this.lastGCTime = (new Date()).getTime(); var currentGCCount = this.fastCount(); var currentTime = (new Date()).getTime(); if ((currentTime > this.lastGCTime + 60000) || (currentTime > this.lastGCTime + 500 && currentGCCount > this.lastGCCount + 100)) { if (this.scheduleId != null) clearTimeout(this.scheduleId); this.scheduleId = setTimeout("wtObjMgr.scheduleId = null; CollectGarbage(); wtObjMgr.lastGCCount = wtObjMgr.fastCount();", 100); this.lastGCTime = currentTime; } } /** A stop-the-world, mark-and-sweep garbage collector, meant to be called internally by WT Toolkit only. This function clears up all weak references for which the image object no longer "exists". Existence for each weak reference image are tested with the following procedure: <br/><br/> <ol> <li>Assume object exists. </li> <li>If there is no <i>wtDependency</i> attribute in the image object, and the image object is a DOM node: <ul> <li> Check whether document.documentElement is an ancestor node to the image object. If document.documentElement is not its ancestor, we can conclude that the image object is detached from the DOM tree and thus is not displayed. Mark the image object as "not exist". </li> </ul> </li> <li> If there is a <i>wtDependency</i> attribute in the image object, then simply check for the existence of the wtDependency weak reference. Existence of the weak reference being checked follows the existence of the wtDependency weak reference. </li> </ol> <br/><br/> The existence checking procedure forms the mark phase of the garbage collector. The seemingly recursive checking in step 3 is actually implemented with a disjoint set data structure with path compression and union-by-rank optimizations. So ideally each invocation of step 3 should take O(Ack^-1(n)) for n weak references where Ack^-1() is the inverse Ackermann function, on average. Ack^-1(n) is a function that grows much slower than log(n). The actual running time, however, is subject to browser inefficiencies. e.g. JavaScript object creation latencies that deteriorate with memory usage in Internet Explorer. <br/><br/> The sweep phase simply removes all weak references marked as "not exist" by the mark phase. */ function CollectGarbage() { // get the keys var keys = wtObjMgr.getKeys(); // create a set universe, and a set object generator for the universe var universe = new SetUniverse(); var g = universe.getGenerator(); // closures for adding and checking objects wrt. the root set var belongsToRootSet = function(obj) { var r1 = g(obj).find(); var r2 = g(_$(document.documentElement)).find(); return r1 == r2; } var addToRootSet = function(obj) { var r = g(obj).find(); var rd = g(_$(document.documentElement)).find(); rd.union(r); } // mark phase for(var i=keys.length -1;i>=0;i--) { var k = keys[i]; // keys are assumed to be strings only, convert the string to a wtObject first var obj = _$($_(k)); // first, find if the key can be mapped to a real object or not // if no real object found, mark the key to be deleted at the end, and go to next key // don't use Array::splice() here because each call takes linear time to the size of array in IE if (!obj || !$_(obj)) { keys[i] = null; continue; } // then, if real object is a DOM node, ds-join it with the closest ancestor that can be mapped to a wtObject // if no such parent found, leave it alone s.t. it will be deleted in the sweep phase // else, look for the wtDependency attribute // if no wtDependency, then the object is one of the roots, union it with the root set // else, ds-join it with the wtDependecy object if (isDOMNode($_(obj)) && !($_(obj).wtDependency)) { var node = $_(obj).parentNode; while(node && !_$(node) && node.parentNode) node = node.parentNode; if (node && _$(node)) g(obj).union(g(_$(node))); } else { if ($_(obj).wtDependency && $_($_(obj).wtDependency)) g(obj).union(g(_$($_($_(obj).wtDependency)))); else if ($_(obj).wtDependency == undefined) addToRootSet(obj); } } // sweep phase // 1. delete objects not belonging to the root set, and mark their keys for deletion // 2. rebuild key array // 3. destroy the set universe var newkeys = []; for(var i=0;i<keys.length;i++) { var k = keys[i]; if (!k || !$_(k)) continue; var obj = _$($_(k)); if (! belongsToRootSet(obj)) { if (isDOMNode($_(obj)) && $_(obj).parentNode) $_(obj).parentNode.removeChild($_(obj)); obj.endProxy(); keys[i] = null; } } for(var i=0;i<keys.length;i++) if (keys[i] != null) newkeys.push(keys[i]); wtObjMgr.keys = newkeys; universe.clear(); } /** The global object manager */ window.wtObjMgr = new WTObjectManager(); function $_(id) { return wtObjMgr.get(id); } function $$(obj) { return wtObjMgr.put(obj); } function _$(obj) { if (!obj) return null; return obj.wtObj; } /** Does this wtWidget belong to the DOM document tree? @param {wtWidget} node The wtWidget to be checked @returns Whether the input wtWidget belongs to the document tree or not. @type Boolean */ function belongsToDocumentTree(node) { if (! node.toString) throw "Checking of raw DOM nodes is no longer supported"; node = $_(node); if (node == null) return false; if (document.getElementById(node.id)) return true; return false; } /** Clears all expando properties from an object in Internet Explorer. @param {Object} obj An arbitary object in Internet Explorer. */ function clearExpando(obj) { for (var i in obj) { try { if (i != "src") { obj[i] = ""; if (obj.removeAttribute) obj.removeAttribute(i); } } catch(err){} } } // }}} // {{{ Include other .js and .css files wtIncludeJS("js/json.js"); wtIncludeJS("js/wt_algor.js"); wtIncludeJS("js/wt_mouse.js"); wtIncludeJS("js/wt_widget.js"); wtIncludeJS("js/wt_form.js"); wtIncludeJS("js/wt_wm.js"); wtIncludeJS("js/wt_vector.js"); wtIncludeCSS("css/wt.css"); // }}}
|
WT Toolkit 0.3.3 | |||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||