Login Register

dojox.advice is here.

Hello,

I'm the process of migrating my codebase from 0.4.3 to 1.0.2

I've just found out in all its glory that kwConnect is gone.

Going through posts, I've seen mention that last time checked (circa 11/07), Alex was working on porting it back to a dojox.advice module.

What's the status on this at the moment. Does the dojo team need hands to make it complete ?

As I desperately need this feature I will just resurrect the 0.4.3 kwConnect code and make a dojo.advice module on my own until I hear from anyone.

Thx in advance,

Pascal Barbier.

I have a similar facility

I have a similar facility planned (dojox.lang.aop), but I don't have an ETA yet. Definitely not for Dojo 1.1.

Thanks for the reply. In the

Thanks for the reply.

In the meantime I've migrated dojo.event.kwConnect into a personal dojox.advice. To be replaced by your work once released.

Pascal.

Willing to share your dojox.advice?

Pascal,

I'm in the midst of porting an app from 0.4 to 1.0 and have quite a lot of reliance on the AO before/after/once stuff that hasn't landed in dojox yet.

Right now I'm looking pretty seriously at doing what you've already done with migrating the necessary things from dojo.event into a short-term stand-in for dojox.advice. Would you be willing to share your personal dojox.advice module?

Thanks.

A dojox.advice

[EDIT: Added formatting for clarity. For some reason the JS highlighting breaks on the two major code fragments, so formatted with code tags.]

Hi,

Sorry for the delay but I've been quiet waterboarded by my move to Dojo 1.0.

Anyway, without further due, here it is. This is essentially a self-contained port of the original code from 0.4.3. A very few dependencies have been added into the closure.

There are 1 folder and four files:

Folder:
dojox/advice
Files:
dojox/advice.js
dojox/advice/_baseAdvice.js
dojox/advice/common.js
dojox/advice/browser.js

File contents:
* Content of dojox/advice.js:

dojo.provide("dojox.advice");
dojo.require("dojox.advice._baseAdvice");

* Content of dojox/advice/_baseAdvice.js

dojo.provide("dojox.advice._baseAdvice");
dojo.require("dojox.advice.common");
dojo.require("dojox.advice.browser");

* Content of dojox/advice/common.js

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml

*/

dojo.provide("dojox.advice.common");
dojo.require("dojo.parser");

dojox.advice = new function () {
	this._canTimeout = dojo.isFunction(dojo.global["setTimeout"]) || dojo.isAlien(dojo.global["setTimeout"]);

    function getNameInObj(ns, item) {
        if (!ns) {
            ns = dojo.global;
        }
        for (var x in ns) {
            if (ns[x] === item) {
                return new String(x);
            }
        }
        return null;
    };

    function interpolateArgs(args, searchForNames) {
		var ao = {srcObj:dojo.global, srcFunc:null, adviceObj:dojo.global, adviceFunc:null, aroundObj:null, aroundFunc:null, adviceType:(args.length > 2) ? args[0] : "after", precedence:"last", once:false, delay:null, rate:0, adviceMsg:false, maxCalls:-1};
		switch (args.length) {
		  case 0:
			return;
		  case 1:
			return;
		  case 2:
			ao.srcFunc = args[0];
			ao.adviceFunc = args[1];
			break;
		  case 3:
			if ((dojo.isObject(args[0])) && (dojo.isString(args[1])) && (dojo.isString(args[2]))) {
				ao.adviceType = "after";
				ao.srcObj = args[0];
				ao.srcFunc = args[1];
				ao.adviceFunc = args[2];
			} else {
				if ((dojo.isString(args[1])) && (dojo.isString(args[2]))) {
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
				} else {
					if ((dojo.isObject(args[0])) && (dojo.isString(args[1])) && (dojo.isFunction(args[2]))) {
						ao.adviceType = "after";
						ao.srcObj = args[0];
						ao.srcFunc = args[1];
						var tmpName = dojo.parser._nameAnonFunc(args[2], ao.adviceObj, searchForNames);
						ao.adviceFunc = tmpName;
					} else {
						if ((dojo.isFunction(args[0])) && (dojo.isObject(args[1])) && (dojo.isString(args[2]))) {
							ao.adviceType = "after";
							ao.srcObj = dojo.global;
							var tmpName = dojo.parser._nameAnonFunc(args[0], ao.srcObj, searchForNames);
							ao.srcFunc = tmpName;
							ao.adviceObj = args[1];
							ao.adviceFunc = args[2];
						}
					}
				}
			}
			break;
		  case 4:
			if ((dojo.isObject(args[0])) && (dojo.isObject(args[2]))) {
				ao.adviceType = "after";
				ao.srcObj = args[0];
				ao.srcFunc = args[1];
				ao.adviceObj = args[2];
				ao.adviceFunc = args[3];
			} else {
				if ((dojo.isString(args[0])) && (dojo.isString(args[1])) && (dojo.isObject(args[2]))) {
					ao.adviceType = args[0];
					ao.srcObj = dojo.global;
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				} else {
					if ((dojo.isString(args[0])) && (dojo.isFunction(args[1])) && (dojo.isObject(args[2]))) {
						ao.adviceType = args[0];
						ao.srcObj = dojo.global;
						var tmpName = dojo.parser._nameAnonFunc(args[1], dojo.global, searchForNames);
						ao.srcFunc = tmpName;
						ao.adviceObj = args[2];
						ao.adviceFunc = args[3];
					} else {
						if ((dojo.isString(args[0])) && (dojo.isObject(args[1])) && (dojo.isString(args[2])) && (dojo.isFunction(args[3]))) {
							ao.srcObj = args[1];
							ao.srcFunc = args[2];
							var tmpName = dojo.parser._nameAnonFunc(args[3], dojo.global, searchForNames);
							ao.adviceObj = dojo.global;
							ao.adviceFunc = tmpName;
						} else {
							if (dojo.isObject(args[1])) {
								ao.srcObj = args[1];
								ao.srcFunc = args[2];
								ao.adviceObj = dojo.global;
								ao.adviceFunc = args[3];
							} else {
								if (dojo.isObject(args[2])) {
									ao.srcObj = dojo.global;
									ao.srcFunc = args[1];
									ao.adviceObj = args[2];
									ao.adviceFunc = args[3];
								} else {
									ao.srcObj = ao.adviceObj = ao.aroundObj = dojo.global;
									ao.srcFunc = args[1];
									ao.adviceFunc = args[2];
									ao.aroundFunc = args[3];
								}
							}
						}
					}
				}
			}
			break;
		  case 6:
			ao.srcObj = args[1];
			ao.srcFunc = args[2];
			ao.adviceObj = args[3];
			ao.adviceFunc = args[4];
			ao.aroundFunc = args[5];
			ao.aroundObj = dojo.global;
			break;
		  default:
			ao.srcObj = args[1];
			ao.srcFunc = args[2];
			ao.adviceObj = args[3];
			ao.adviceFunc = args[4];
			ao.aroundObj = args[5];
			ao.aroundFunc = args[6];
			ao.once = args[7];
			ao.delay = args[8];
			ao.rate = args[9];
			ao.adviceMsg = args[10];
			ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1;
			break;
		}
		if (dojo.isFunction(ao.aroundFunc)) {
			var tmpName = dojo.parser._nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
			ao.aroundFunc = tmpName;
		}
		if (dojo.isFunction(ao.srcFunc)) {
			ao.srcFunc = getNameInObj(ao.srcObj, ao.srcFunc);
		}
		if (dojo.isFunction(ao.adviceFunc)) {
			ao.adviceFunc = getNameInObj(ao.adviceObj, ao.adviceFunc);
		}
		if ((ao.aroundObj) && (dojo.isFunction(ao.aroundFunc))) {
			ao.aroundFunc = getNameInObj(ao.aroundObj, ao.aroundFunc);
		}
		if (!ao.srcObj) {
			throw "bad srcObj for srcFunc: " + ao.srcFunc;
		}
		if (!ao.adviceObj) {
			throw "bad adviceObj for adviceFunc: " + ao.adviceFunc;
		}
		if (!ao.adviceFunc) {
			console.debug("bad adviceFunc for srcFunc: " + ao.srcFunc);
			console.debug(dojo.toJson(ao));
		}
		return ao;
	}
	this.connect = function () {
		if (arguments.length == 1) {
			var ao = arguments[0];
		} else {
			var ao = interpolateArgs(arguments, true);
		}
		if (dojo.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey")) {
			if (dojo.isIE) {
				ao.srcFunc = "onkeydown";
				this.connect(ao);
			}
			ao.srcFunc = "onkeypress";
		}
		if (dojo.isArray(ao.srcObj) && ao.srcObj != "") {
			var tmpAO = {};
			for (var x in ao) {
				tmpAO[x] = ao[x];
			}
			var mjps = [];
			dojo.forEach(ao.srcObj, function (src) {
				if (dojo.isString(src)) {
					src = dojo.byId(src);
				}
				tmpAO.srcObj = src;
				mjps.push(dojox.advice.connect.call(dojox.advice, tmpAO));
			});
			return mjps;
		}
		var mjp = dojox.advice.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
		if (ao.adviceFunc) {
			var mjp2 = dojox.advice.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
		}
		mjp.kwAddAdvice(ao);
		return mjp;
	};
	this.log = function (a1, a2) {
		var kwArgs;
		if ((arguments.length == 1) && (typeof a1 == "object")) {
			kwArgs = a1;
		} else {
			kwArgs = {srcObj:a1, srcFunc:a2};
		}
		kwArgs.adviceFunc = function () {
			var argsStr = [];
			for (var x = 0; x < arguments.length; x++) {
				argsStr.push(arguments[x]);
			}
			console.debug("(" + kwArgs.srcObj + ")." + kwArgs.srcFunc, ":", argsStr.join(", "));
		};
		this.kwConnect(kwArgs);
	};
	this.connectBefore = function () {
		var args = ["before"];
		for (var i = 0; i < arguments.length; i++) {
			args.push(arguments[i]);
		}
		return this.connect.apply(this, args);
	};
	this.connectAround = function () {
		var args = ["around"];
		for (var i = 0; i < arguments.length; i++) {
			args.push(arguments[i]);
		}
		return this.connect.apply(this, args);
	};
	this.connectOnce = function () {
		var ao = interpolateArgs(arguments, true);
		ao.once = true;
		return this.connect(ao);
	};
	this.connectRunOnce = function () {
		var ao = interpolateArgs(arguments, true);
		ao.maxCalls = 1;
		return this.connect(ao);
	};
	this._kwConnectImpl = function (kwArgs, disconnect) {
		var fn = (disconnect) ? "disconnect" : "connect";
		if (typeof kwArgs["srcFunc"] == "function") {
			kwArgs.srcObj = kwArgs["srcObj"] || dojo.global;
			var tmpName = dojo.parser._nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
			kwArgs.srcFunc = tmpName;
		}
		if (typeof kwArgs["adviceFunc"] == "function") {
			kwArgs.adviceObj = kwArgs["adviceObj"] || dojo.global;
			var tmpName = dojo.parser._nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
			kwArgs.adviceFunc = tmpName;
		}
		kwArgs.srcObj = kwArgs["srcObj"] || dojo.global;
		kwArgs.adviceObj = kwArgs["adviceObj"] || kwArgs["targetObj"] || dojo.global;
		kwArgs.adviceFunc = kwArgs["adviceFunc"] || kwArgs["targetFunc"];
		return dojox.advice[fn](kwArgs);
	};
	this.kwConnect = function (kwArgs) {
		return this._kwConnectImpl(kwArgs, false);
	};
	this.disconnect = function () {
		if (arguments.length == 1) {
			var ao = arguments[0];
		} else {
			var ao = interpolateArgs(arguments, true);
		}
		if (!ao.adviceFunc) {
			return;
		}
		if (dojo.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey")) {
			if (dojo.isIE) {
				ao.srcFunc = "onkeydown";
				this.disconnect(ao);
			}
			ao.srcFunc = "onkeypress";
		}
		if (!ao.srcObj[ao.srcFunc]) {
			return null;
		}
		var mjp = dojox.advice.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc, true);
		mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once);
		return mjp;
	};
	this.kwDisconnect = function (kwArgs) {
		return this._kwConnectImpl(kwArgs, true);
	};
};
dojox.advice.MethodInvocation = function (join_point, obj, args) {
	this.jp_ = join_point;
	this.object = obj;
	this.args = [];
	for (var x = 0; x < args.length; x++) {
		this.args[x] = args[x];
	}
	this.around_index = -1;
};
dojox.advice.MethodInvocation.prototype.proceed = function () {
	this.around_index++;
	if (this.around_index >= this.jp_.around.length) {
		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
	} else {
		var ti = this.jp_.around[this.around_index];
		var mobj = ti[0] || dojo.global;
		var meth = ti[1];
		return mobj[meth].call(mobj, this);
	}
};
dojox.advice.MethodJoinPoint = function (obj, funcName) {
	this.object = obj || dojo.global;
	this.methodname = funcName;
	this.methodfunc = this.object[funcName];
	this.squelch = false;
};
dojox.advice.MethodJoinPoint.getForMethod = function (obj, funcName) {
	if (!obj) {
		obj = dojo.global;
	}
	var ofn = obj[funcName];
	if (!ofn) {
		ofn = obj[funcName] = function () {
		};
		if (!obj[funcName]) {
			throw "Cannot set do-nothing method on that object " + funcName;
		}
	} else {
		if ((typeof ofn != "function") && (!dojo.isFunction(ofn)) && (!dojo.isAlien(ofn))) {
			return null;
		}
	}
	var jpname = funcName + "$joinpoint";
	var jpfuncname = funcName + "$joinpoint$method";
	var joinpoint = obj[jpname];
	if (!joinpoint) {
		var isNode = false;
		if (dojox.advice["browser"]) {
			if ((obj["attachEvent"]) || (obj["nodeType"]) || (obj["addEventListener"])) {
				isNode = true;
				dojox.advice.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
			}
		}
		var origArity = ofn.length;
		obj[jpfuncname] = ofn;
		joinpoint = obj[jpname] = new dojox.advice.MethodJoinPoint(obj, jpfuncname);
		if (!isNode) {
			obj[funcName] = function () {
				return joinpoint.run.apply(joinpoint, arguments);
			};
		} else {
			obj[funcName] = function () {
				var args = [];
				if (!arguments.length) {
					var evt = null;
					try {
						if (obj.ownerDocument) {
							evt = obj.ownerDocument.parentWindow.event;
						} else {
							if (obj.documentElement) {
								evt = obj.documentElement.ownerDocument.parentWindow.event;
							} else {
								if (obj.event) {
									evt = obj.event;
								} else {
									evt = window.event;
								}
							}
						}
					}
					catch (e) {
						evt = window.event;
					}
					if (evt) {
						args.push(dojox.advice.browser.fixEvent(evt, this));
					}
				} else {
					for (var x = 0; x < arguments.length; x++) {
						if ((x == 0) && (dojox.advice.browser.isEvent(arguments[x]))) {
							args.push(dojox.advice.browser.fixEvent(arguments[x], this));
						} else {
							args.push(arguments[x]);
						}
					}
				}
				return joinpoint.run.apply(joinpoint, args);
			};
		}
		obj[funcName].__preJoinArity = origArity;
	}
	return joinpoint;
};
dojo.extend(dojox.advice.MethodJoinPoint, {squelch:false, unintercept:function () {
	this.object[this.methodname] = this.methodfunc;
	this.before = [];
	this.after = [];
	this.around = [];
}, disconnect:function (){return this.unintercept.apply(this, arguments);
}, run:function () {
	var obj = this.object || dojo.global;
	var args = arguments;
	var aargs = [];
	for (var x = 0; x < args.length; x++) {
		aargs[x] = args[x];
	}
	var unrollAdvice = function (marr) {
		if (!marr) {
			console.debug("Null argument to unrollAdvice()");
			return;
		}
		var callObj = marr[0] || dojo.global;
		var callFunc = marr[1];
		if (!callObj[callFunc]) {
			throw "function \"" + callFunc + "\" does not exist on \"" + callObj + "\"";
		}
		var aroundObj = marr[2] || dojo.global;
		var aroundFunc = marr[3];
		var msg = marr[6];
		var maxCount = marr[7];
		if (maxCount > -1) {
			if (maxCount == 0) {
				return;
			}
			marr[7]--;
		}
		var undef;
		var to = {args:[], jp_:this, object:obj, proceed:function () {
			return callObj[callFunc].apply(callObj, to.args);
		}};
		to.args = aargs;
		var delay = parseInt(marr[4]);
		var hasDelay = ((!isNaN(delay)) && (marr[4] !== null) && (typeof marr[4] != "undefined"));
		if (marr[5]) {
			var rate = parseInt(marr[5]);
			var cur = new Date();
			var timerSet = false;
			if ((marr["last"]) && ((cur - marr.last) <= rate)) {
				if (dojox.advice._canTimeout) {
					if (marr["delayTimer"]) {
						clearTimeout(marr.delayTimer);
					}
					var tod = parseInt(rate * 2);
					var mcpy = dojo.mixin({}, marr);
                    marr.delayTimer = setTimeout(function () {
						mcpy[5] = 0;
						unrollAdvice(mcpy);
					}, tod);
				}
				return;
			} else {
				marr.last = cur;
			}
		}
		if (aroundFunc) {
			aroundObj[aroundFunc].call(aroundObj, to);
		} else {
			if ((hasDelay)) {
				dojo.global["setTimeout"](function () {
					if (msg) {
						callObj[callFunc].call(callObj, to);
					} else {
						callObj[callFunc].apply(callObj, args);
					}
				}, delay);
			} else {
				if (msg) {
					callObj[callFunc].call(callObj, to);
				} else {
					callObj[callFunc].apply(callObj, args);
				}
			}
		}
	};
	var unRollSquelch = function () {
		if (this.squelch) {
			try {
				return unrollAdvice.apply(this, arguments);
			}
			catch (e) {
				console.debug(e);
			}
		} else {
			return unrollAdvice.apply(this, arguments);
		}
	};
	if ((this["before"]) && (this.before.length > 0)) {
		dojo.forEach(this.before.concat(new Array()), unRollSquelch);
	}
	var result;
	try {
		if ((this["around"]) && (this.around.length > 0)) {
			var mi = new dojox.advice.MethodInvocation(this, obj, args);
			result = mi.proceed();
		} else {
			if (this.methodfunc) {
				result = this.object[this.methodname].apply(this.object, args);
			}
		}
	}
	catch (e) {
		if (!this.squelch) {
			console.debug(e, "when calling", this.methodname, "on", this.object, "with arguments", args);
			throw e;
		}
	}
	if ((this["after"]) && (this.after.length > 0)) {
		dojo.forEach(this.after.concat(new Array()), unRollSquelch);
	}
	return (this.methodfunc) ? result : null;
}, getArr:function (kind) {
	var type = "after";
	if ((typeof kind == "string") && (kind.indexOf("before") != -1)) {
		type = "before";
	} else {
		if (kind == "around") {
			type = "around";
		}
	}
	if (!this[type]) {
		this[type] = [];
	}
	return this[type];
}, kwAddAdvice:function (args) {
	this.addAdvice(args["adviceObj"], args["adviceFunc"], args["aroundObj"], args["aroundFunc"], args["adviceType"], args["precedence"], args["once"], args["delay"], args["rate"], args["adviceMsg"], args["maxCalls"]);
}, addAdvice:function (thisAdviceObj, thisAdvice, thisAroundObj, thisAround, adviceType, precedence, once, delay, rate, asMessage, maxCalls) {
	var arr = this.getArr(adviceType);
	if (!arr) {
		throw "bad this: " + this;
	}
	var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage, maxCalls];
	if (once) {
		if (this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0) {
			return;
		}
	}
	if (precedence == "first") {
		arr.unshift(ao);
	} else {
		arr.push(ao);
	}
}, hasAdvice:function (thisAdviceObj, thisAdvice, adviceType, arr) {
	if (!arr) {
		arr = this.getArr(adviceType);
	}
	var ind = -1;
	for (var x = 0; x < arr.length; x++) {
		var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
		var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
		if ((arr[x][0] == thisAdviceObj) && (a1o == aao)) {
			ind = x;
		}
	}
	return ind;
}, removeAdvice:function (thisAdviceObj, thisAdvice, adviceType, once) {
	var arr = this.getArr(adviceType);
	var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
	if (ind == -1) {
		return false;
	}
	while (ind != -1) {
		arr.splice(ind, 1);
		if (once) {
			break;
		}
		ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
	}
	return true;
}});

* Content of dojox/advice/browser.js

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojox.advice.browser");
dojo.require("dojox.advice.common");
dojox.advice._ie_clobber = new function () {
	this.clobberNodes = [];
	function nukeProp(node, prop) {
		try {
			node[prop] = null;
		}
		catch (e) {
		}
		try {
			delete node[prop];
		}
		catch (e) {
		}
		try {
			node.removeAttribute(prop);
		}
		catch (e) {
		}
	}
	this.clobber = function (nodeRef) {
		var na;
		var tna;
		if (nodeRef) {
			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
			na = [nodeRef];
			for (var x = 0; x < tna.length; x++) {
				if (tna[x]["__doClobber__"]) {
					na.push(tna[x]);
				}
			}
		} else {
			try {
				window.onload = null;
			}
			catch (e) {
			}
			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
		}
		tna = null;
		var basis = {};
		for (var i = na.length - 1; i >= 0; i = i - 1) {
			var el = na[i];
			try {
				if (el && el["__clobberAttrs__"]) {
					for (var j = 0; j < el.__clobberAttrs__.length; j++) {
						nukeProp(el, el.__clobberAttrs__[j]);
					}
					nukeProp(el, "__clobberAttrs__");
					nukeProp(el, "__doClobber__");
				}
			}
			catch (e) {
			}
		}
		na = null;
	};
};
if (dojo.isIE) {
	dojo.addOnUnload(function () {
		dojox.advice._ie_clobber.clobber();
		try {
			window.onload = null;
		}
		catch(e){}
		try {
			window.onunload = null;
		}
		catch(e){}
		dojox.advice._ie_clobber.clobberNodes = [];
	});
}
dojox.advice.browser = new function () {
	var clobberIdx = 0;
	this.normalizedEventName = function (eventName) {
		switch (eventName) {
		  case "CheckboxStateChange":
		  case "DOMAttrModified":
		  case "DOMMenuItemActive":
		  case "DOMMenuItemInactive":
		  case "DOMMouseScroll":
		  case "DOMNodeInserted":
		  case "DOMNodeRemoved":
		  case "RadioStateChange":
			return eventName;
			break;
		  default:
			var lcn = eventName.toLowerCase();
			return (lcn.indexOf("on") == 0) ? lcn.substr(2) : lcn;
			break;
		}
	};
	this.clean = function (node) {
		if (dojo.isIE) {
			dojox.advice._ie_clobber.clobber(node);
		}
	};
	this.addClobberNode = function (node) {
		if (!dojo.isIE) {
			return;
		}
		if (!node["__doClobber__"]) {
			node.__doClobber__ = true;
			dojox.advice._ie_clobber.clobberNodes.push(node);
			node.__clobberAttrs__ = [];
		}
	};
	this.addClobberNodeAttrs = function (node, props) {
		if (!dojo.isIE) {
			return;
		}
		this.addClobberNode(node);
		for (var x = 0; x < props.length; x++) {
			node.__clobberAttrs__.push(props[x]);
		}
	};
	this.removeListener = function (node, evtName, fp, capture) {
		if (!capture) {
			var capture = false;
		}
		evtName = dojox.advice.browser.normalizedEventName(evtName);
		if (evtName == "key") {
			if (dojo.isIE) {
				this.removeListener(node, "onkeydown", fp, capture);
			}
			evtName = "keypress";
		}
		if (node.removeEventListener) {
			node.removeEventListener(evtName, fp, capture);
		}
	};
	this.addListener = function (node, evtName, fp, capture, dontFix) {
		if (!node) {
			return;
		}
		if (!capture) {
			var capture = false;
		}
		evtName = dojox.advice.browser.normalizedEventName(evtName);
		if (evtName == "key") {
			if (dojo.isIE) {
				this.addListener(node, "onkeydown", fp, capture, dontFix);
			}
			evtName = "keypress";
		}
		if (!dontFix) {
			var newfp = function (evt) {
				if (!evt) {
					evt = window.event;
				}
				var ret = fp(dojox.advice.browser.fixEvent(evt, this));
				if (capture) {
					dojox.advice.browser.stopEvent(evt);
				}
				return ret;
			};
		} else {
			newfp = fp;
		}
		if (node.addEventListener) {
			node.addEventListener(evtName, newfp, capture);
			return newfp;
		} else {
			evtName = "on" + evtName;
			if (typeof node[evtName] == "function") {
				var oldEvt = node[evtName];
				node[evtName] = function (e) {
					oldEvt(e);
					return newfp(e);
				};
			} else {
				node[evtName] = newfp;
			}
			if (dojo.isIE) {
				this.addClobberNodeAttrs(node, [evtName]);
			}
			return newfp;
		}
	};
	this.isEvent = function (obj) {
		return (typeof obj != "undefined") && (obj) && (typeof Event != "undefined") && (obj.eventPhase);
	};
	this.currentEvent = null;
	this.callListener = function (listener, curTarget) {
		if (typeof listener != "function") {
			throw "listener not a function: " + listener;
		}
		dojox.advice.browser.currentEvent.currentTarget = curTarget;
		return listener.call(curTarget, dojox.advice.browser.currentEvent);
	};
	this._stopPropagation = function () {
		dojox.advice.browser.currentEvent.cancelBubble = true;
	};
	this._preventDefault = function () {
		dojox.advice.browser.currentEvent.returnValue = false;
	};
	this.keys = {KEY_BACKSPACE:8, KEY_TAB:9, KEY_CLEAR:12, KEY_ENTER:13, KEY_SHIFT:16, KEY_CTRL:17, KEY_ALT:18, KEY_PAUSE:19, KEY_CAPS_LOCK:20, KEY_ESCAPE:27, KEY_SPACE:32, KEY_PAGE_UP:33, KEY_PAGE_DOWN:34, KEY_END:35, KEY_HOME:36, KEY_LEFT_ARROW:37, KEY_UP_ARROW:38, KEY_RIGHT_ARROW:39, KEY_DOWN_ARROW:40, KEY_INSERT:45, KEY_DELETE:46, KEY_HELP:47, KEY_LEFT_WINDOW:91, KEY_RIGHT_WINDOW:92, KEY_SELECT:93, KEY_NUMPAD_0:96, KEY_NUMPAD_1:97, KEY_NUMPAD_2:98, KEY_NUMPAD_3:99, KEY_NUMPAD_4:100, KEY_NUMPAD_5:101, KEY_NUMPAD_6:102, KEY_NUMPAD_7:103, KEY_NUMPAD_8:104, KEY_NUMPAD_9:105, KEY_NUMPAD_MULTIPLY:106, KEY_NUMPAD_PLUS:107, KEY_NUMPAD_ENTER:108, KEY_NUMPAD_MINUS:109, KEY_NUMPAD_PERIOD:110, KEY_NUMPAD_DIVIDE:111, KEY_F1:112, KEY_F2:113, KEY_F3:114, KEY_F4:115, KEY_F5:116, KEY_F6:117, KEY_F7:118, KEY_F8:119, KEY_F9:120, KEY_F10:121, KEY_F11:122, KEY_F12:123, KEY_F13:124, KEY_F14:125, KEY_F15:126, KEY_NUM_LOCK:144, KEY_SCROLL_LOCK:145};
	this.revKeys = [];
	for (var key in this.keys) {
		this.revKeys[this.keys[key]] = key;
	}
	this.fixEvent = function (evt, sender) {
		if (!evt) {
			if (window["event"]) {
				evt = window.event;
			}
		}
		if ((evt["type"]) && (evt["type"].indexOf("key") == 0)) {
			evt.keys = this.revKeys;
			for (var key in this.keys) {
				evt[key] = this.keys[key];
			}
			if (evt["type"] == "keydown" && dojo.isIE) {
				switch (evt.keyCode) {
				  case evt.KEY_SHIFT:
				  case evt.KEY_CTRL:
				  case evt.KEY_ALT:
				  case evt.KEY_CAPS_LOCK:
				  case evt.KEY_LEFT_WINDOW:
				  case evt.KEY_RIGHT_WINDOW:
				  case evt.KEY_SELECT:
				  case evt.KEY_NUM_LOCK:
				  case evt.KEY_SCROLL_LOCK:
				  case evt.KEY_NUMPAD_0:
				  case evt.KEY_NUMPAD_1:
				  case evt.KEY_NUMPAD_2:
				  case evt.KEY_NUMPAD_3:
				  case evt.KEY_NUMPAD_4:
				  case evt.KEY_NUMPAD_5:
				  case evt.KEY_NUMPAD_6:
				  case evt.KEY_NUMPAD_7:
				  case evt.KEY_NUMPAD_8:
				  case evt.KEY_NUMPAD_9:
				  case evt.KEY_NUMPAD_PERIOD:
					break;
				  case evt.KEY_NUMPAD_MULTIPLY:
				  case evt.KEY_NUMPAD_PLUS:
				  case evt.KEY_NUMPAD_ENTER:
				  case evt.KEY_NUMPAD_MINUS:
				  case evt.KEY_NUMPAD_DIVIDE:
					break;
				  case evt.KEY_PAUSE:
				  case evt.KEY_TAB:
				  case evt.KEY_BACKSPACE:
				  case evt.KEY_ENTER:
				  case evt.KEY_ESCAPE:
				  case evt.KEY_PAGE_UP:
				  case evt.KEY_PAGE_DOWN:
				  case evt.KEY_END:
				  case evt.KEY_HOME:
				  case evt.KEY_LEFT_ARROW:
				  case evt.KEY_UP_ARROW:
				  case evt.KEY_RIGHT_ARROW:
				  case evt.KEY_DOWN_ARROW:
				  case evt.KEY_INSERT:
				  case evt.KEY_DELETE:
				  case evt.KEY_F1:
				  case evt.KEY_F2:
				  case evt.KEY_F3:
				  case evt.KEY_F4:
				  case evt.KEY_F5:
				  case evt.KEY_F6:
				  case evt.KEY_F7:
				  case evt.KEY_F8:
				  case evt.KEY_F9:
				  case evt.KEY_F10:
				  case evt.KEY_F11:
				  case evt.KEY_F12:
				  case evt.KEY_F12:
				  case evt.KEY_F13:
				  case evt.KEY_F14:
				  case evt.KEY_F15:
				  case evt.KEY_CLEAR:
				  case evt.KEY_HELP:
					evt.key = evt.keyCode;
					break;
				  default:
					if (evt.ctrlKey || evt.altKey) {
						var unifiedCharCode = evt.keyCode;
						if (unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false) {
							unifiedCharCode += 32;
						}
						if (unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey) {
							unifiedCharCode += 96;
						}
						evt.key = String.fromCharCode(unifiedCharCode);
					}
				}
			} else {
				if (evt["type"] == "keypress") {
					if (dojo.isOpera) {
						if (evt.which == 0) {
							evt.key = evt.keyCode;
						} else {
							if (evt.which > 0) {
								switch (evt.which) {
								  case evt.KEY_SHIFT:
								  case evt.KEY_CTRL:
								  case evt.KEY_ALT:
								  case evt.KEY_CAPS_LOCK:
								  case evt.KEY_NUM_LOCK:
								  case evt.KEY_SCROLL_LOCK:
									break;
								  case evt.KEY_PAUSE:
								  case evt.KEY_TAB:
								  case evt.KEY_BACKSPACE:
								  case evt.KEY_ENTER:
								  case evt.KEY_ESCAPE:
									evt.key = evt.which;
									break;
								  default:
									var unifiedCharCode = evt.which;
									if ((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)) {
										unifiedCharCode += 32;
									}
									evt.key = String.fromCharCode(unifiedCharCode);
								}
							}
						}
					} else {
						if (dojo.isIE) {
							if (!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE) {
								evt.key = String.fromCharCode(evt.keyCode);
							}
						} else {
                            evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
						}
					}
				}
			}
		}
		if (dojo.isIE) {
			if (!evt.target) {
				evt.target = evt.srcElement;
			}
			if (!evt.currentTarget) {
				evt.currentTarget = (sender ? sender : evt.srcElement);
			}
			if (!evt.layerX) {
				evt.layerX = evt.offsetX;
			}
			if (!evt.layerY) {
				evt.layerY = evt.offsetY;
			}
			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
			var docBody = dojo.doc.body;
            if (!evt.pageX) {
				evt.pageX = evt.clientX + (docBody.scrollLeft || 0);
			}
			if (!evt.pageY) {
				evt.pageY = evt.clientY + (docBody.scrollTop || 0);
			}
			if (evt.type == "mouseover") {
				evt.relatedTarget = evt.fromElement;
			}
			if (evt.type == "mouseout") {
				evt.relatedTarget = evt.toElement;
			}
			this.currentEvent = evt;
			evt.callListener = this.callListener;
			evt.stopPropagation = this._stopPropagation;
			evt.preventDefault = this._preventDefault;
		}
		return evt;
	};
	this.stopEvent = function (evt) {
		if (window.event) {
			evt.cancelBubble = true;
			evt.returnValue = false;
		} else {
			evt.preventDefault();
			evt.stopPropagation();
		}
	};
};

Enjoy,

Pascal.