Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/common.js URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/common.js?view=auto&rev=474551 ============================================================================== --- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/common.js (added) +++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/common.js Mon Nov 13 14:54:45 2006 @@ -0,0 +1,878 @@ +/* + 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("dojo.event.common"); + +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.lang.func"); + +// TODO: connection filter functions +// these are functions that accept a method invocation (like around +// advice) and return a boolean based on it. That value determines +// whether or not the connection proceeds. It could "feel" like around +// advice for those who know what it is (calling proceed() or not), +// but I think presenting it as a "filter" and/or calling it with the +// function args and not the MethodInvocation might make it more +// palletable to "normal" users than around-advice currently is +// TODO: execution scope mangling +// YUI's event facility by default executes listeners in the context +// of the source object. This is very odd, but should probably be +// supported as an option (both for the source and for the dest). It +// can be thought of as a connection-specific hitch(). +// TODO: more resiliency for 4+ arguments to connect() + +dojo.event = new function(){ + this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]); + + // FIXME: where should we put this method (not here!)? + function interpolateArgs(args, searchForNames){ + var dl = dojo.lang; + var ao = { + srcObj: dj_global, + srcFunc: null, + adviceObj: dj_global, + adviceFunc: null, + aroundObj: null, + aroundFunc: null, + adviceType: (args.length>2) ? args[0] : "after", + precedence: "last", + once: false, + delay: null, + rate: 0, + adviceMsg: false + }; + + switch(args.length){ + case 0: return; + case 1: return; + case 2: + ao.srcFunc = args[0]; + ao.adviceFunc = args[1]; + break; + case 3: + if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + ao.adviceFunc = args[2]; + }else if((dl.isString(args[1]))&&(dl.isString(args[2]))){ + ao.srcFunc = args[1]; + ao.adviceFunc = args[2]; + }else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + var tmpName = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames); + ao.adviceFunc = tmpName; + }else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = dj_global; + var tmpName = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames); + ao.srcFunc = tmpName; + ao.adviceObj = args[1]; + ao.adviceFunc = args[2]; + } + break; + case 4: + if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){ + // we can assume that we've got an old-style "connect" from + // the sigslot school of event attachment. We therefore + // assume after-advice. + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){ + ao.adviceType = args[0]; + ao.srcObj = dj_global; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){ + ao.adviceType = args[0]; + ao.srcObj = dj_global; + var tmpName = dl.nameAnonFunc(args[1], dj_global, searchForNames); + ao.srcFunc = tmpName; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){ + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + var tmpName = dl.nameAnonFunc(args[3], dj_global, searchForNames); + ao.adviceObj = dj_global; + ao.adviceFunc = tmpName; + }else if(dl.isObject(args[1])){ + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + ao.adviceObj = dj_global; + ao.adviceFunc = args[3]; + }else if(dl.isObject(args[2])){ + ao.srcObj = dj_global; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else{ + ao.srcObj = ao.adviceObj = ao.aroundObj = dj_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 = dj_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]; + break; + } + + if(dl.isFunction(ao.aroundFunc)){ + var tmpName = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames); + ao.aroundFunc = tmpName; + } + + if(dl.isFunction(ao.srcFunc)){ + ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc); + } + + if(dl.isFunction(ao.adviceFunc)){ + ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc); + } + + if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){ + ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc); + } + + if(!ao.srcObj){ + dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc); + } + if(!ao.adviceObj){ + dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc); + } + + if(!ao.adviceFunc){ + dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc); + dojo.debugShallow(ao); + } + + return ao; + } + + this.connect = function(/*...*/){ + // summary: + // dojo.event.connect is the glue that holds most Dojo-based + // applications together. Most combinations of arguments are + // supported, with the connect() method attempting to disambiguate + // the implied types of positional parameters. The following will + // all work: + // dojo.event.connect("globalFunctionName1", "globalFunctionName2"); + // dojo.event.connect(functionReference1, functionReference2); + // dojo.event.connect("globalFunctionName1", functionReference2); + // dojo.event.connect(functionReference1, "globalFunctionName2"); + // dojo.event.connect(scope1, "functionName1", "globalFunctionName2"); + // dojo.event.connect("globalFunctionName1", scope2, "functionName2"); + // dojo.event.connect(scope1, "functionName1", scope2, "functionName2"); + // dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2"); + // dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2"); + // dojo.event.connect("around", scope1, "functionName1", + // scope2, "functionName2", + // aroundFunctionReference); + // dojo.event.connect("around", scope1, "functionName1", + // scope2, "functionName2", + // scope3, "aroundFunctionName"); + // dojo.event.connect("before-around", scope1, "functionName1", + // scope2, "functionName2", + // aroundFunctionReference); + // dojo.event.connect("after-around", scope1, "functionName1", + // scope2, "functionName2", + // aroundFunctionReference); + // dojo.event.connect("after-around", scope1, "functionName1", + // scope2, "functionName2", + // scope3, "aroundFunctionName"); + // dojo.event.connect("around", scope1, "functionName1", + // scope2, "functionName2", + // scope3, "aroundFunctionName", true, 30); + // dojo.event.connect("around", scope1, "functionName1", + // scope2, "functionName2", + // scope3, "aroundFunctionName", null, null, 10); + // adviceType: + // Optional. String. One of "before", "after", "around", + // "before-around", or "after-around". FIXME + // srcObj: + // the scope in which to locate/execute the named srcFunc. Along + // with srcFunc, this creates a way to dereference the function to + // call. So if the function in question is "foo.bar", the + // srcObj/srcFunc pair would be foo and "bar", where "bar" is a + // string and foo is an object reference. + // srcFunc: + // the name of the function to connect to. When it is executed, + // the listener being registered with this call will be called. + // The adviceType defines the call order between the source and + // the target functions. + // adviceObj: + // the scope in which to locate/execute the named adviceFunc. + // adviceFunc: + // the name of the function being conected to srcObj.srcFunc + // aroundObj: + // the scope in which to locate/execute the named aroundFunc. + // aroundFunc: + // the name of, or a reference to, the function that will be used + // to mediate the advice call. Around advice requires a special + // unary function that will be passed a "MethodInvocation" object. + // These objects have several important properties, namely: + // - args + // a mutable array of arguments to be passed into the + // wrapped function + // - proceed + // a function that "continues" the invocation. The result + // of this function is the return of the wrapped function. + // You can then manipulate this return before passing it + // back out (or take further action based on it). + // once: + // boolean that determines whether or not this connect() will + // create a new connection if an identical connect() has already + // been made. Defaults to "false". + // delay: + // an optional delay (in ms), as an integer, for dispatch of a + // listener after the source has been fired. + // rate: + // an optional rate throttling parameter (integer, in ms). When + // specified, this particular connection will not fire more than + // once in the interval specified by the rate + // adviceMsg: + // boolean. Should the listener have all the parameters passed in + // as a single argument? + + /* + ao.adviceType = args[0]; + 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]; + */ + if(arguments.length == 1){ + var ao = arguments[0]; + }else{ + var ao = interpolateArgs(arguments, true); + } + if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){ + if(dojo.render.html.ie){ + ao.srcFunc = "onkeydown"; + this.connect(ao); + } + ao.srcFunc = "onkeypress"; + } + + + if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){ + var tmpAO = {}; + for(var x in ao){ + tmpAO[x] = ao[x]; + } + var mjps = []; + dojo.lang.forEach(ao.srcObj, function(src){ + if((dojo.render.html.capable)&&(dojo.lang.isString(src))){ + src = dojo.byId(src); + // dojo.debug(src); + } + tmpAO.srcObj = src; + // dojo.debug(tmpAO.srcObj, tmpAO.srcFunc); + // dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc); + mjps.push(dojo.event.connect.call(dojo.event, tmpAO)); + }); + return mjps; + } + + // FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!! + var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc); + if(ao.adviceFunc){ + var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc); + } + + mjp.kwAddAdvice(ao); + + // advanced users might want to fsck w/ the join point manually + return mjp; // a MethodJoinPoint object + } + + this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){ + // summary: + // a function that will wrap and log all calls to the specified + // a1.a2() function. If only a1 is passed, it'll be used as a + // function or function name on the global context. Logging will + // be sent to dojo.debug + // a1: + // if a2 is passed, this should be an object. If not, it can be a + // function or function name. + // a2: + // a function name + 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]); + } + dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", ")); + } + this.kwConnect(kwArgs); + } + + this.connectBefore = function(){ + // summary: + // takes the same parameters as dojo.event.connect(), except that + // the advice type will always be "before" + var args = ["before"]; + for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); } + return this.connect.apply(this, args); // a MethodJoinPoint object + } + + this.connectAround = function(){ + // summary: + // takes the same parameters as dojo.event.connect(), except that + // the advice type will always be "around" + var args = ["around"]; + for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); } + return this.connect.apply(this, args); // a MethodJoinPoint object + } + + this.connectOnce = function(){ + // summary: + // takes the same parameters as dojo.event.connect(), except that + // the "once" flag will always be set to "true" + var ao = interpolateArgs(arguments, true); + ao.once = true; + return this.connect(ao); // a MethodJoinPoint object + } + + this._kwConnectImpl = function(kwArgs, disconnect){ + var fn = (disconnect) ? "disconnect" : "connect"; + if(typeof kwArgs["srcFunc"] == "function"){ + kwArgs.srcObj = kwArgs["srcObj"]||dj_global; + var tmpName = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true); + kwArgs.srcFunc = tmpName; + } + if(typeof kwArgs["adviceFunc"] == "function"){ + kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global; + var tmpName = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true); + kwArgs.adviceFunc = tmpName; + } + kwArgs.srcObj = kwArgs["srcObj"]||dj_global; + kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global; + kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"]; + // pass kwargs to avoid unrolling/repacking + return dojo.event[fn](kwArgs); + } + + this.kwConnect = function(/*Object*/ kwArgs){ + // summary: + // A version of dojo.event.connect() that takes a map of named + // parameters instead of the positional parameters that + // dojo.event.connect() uses. For many advanced connection types, + // this can be a much more readable (and potentially faster) + // alternative. + // kwArgs: + // An object that can have the following properties: + // - adviceType + // - srcObj + // - srcFunc + // - adviceObj + // - adviceFunc + // - aroundObj + // - aroundFunc + // - once + // - delay + // - rate + // - adviceMsg + // As with connect, only srcFunc and adviceFunc are generally + // required + + return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object + + } + + this.disconnect = function(){ + // summary: + // Takes the same parameters as dojo.event.connect() but destroys + // an existing connection instead of building a new one. For + // multiple identical connections, multiple disconnect() calls + // will unroll one each time it's called. + if(arguments.length == 1){ + var ao = arguments[0]; + }else{ + var ao = interpolateArgs(arguments, true); + } + if(!ao.adviceFunc){ return; } // nothing to disconnect + if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){ + if(dojo.render.html.ie){ + ao.srcFunc = "onkeydown"; + this.disconnect(ao); + } + ao.srcFunc = "onkeypress"; + } + var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc); + return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object + } + + this.kwDisconnect = function(kwArgs){ + // summary: + // Takes the same parameters as dojo.event.kwConnect() but + // destroys an existing connection instead of building a new one. + return this._kwConnectImpl(kwArgs, true); + } +} + +// exactly one of these is created whenever a method with a joint point is run, +// if there is at least one 'around' advice. +dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){ + // summary: + // a class the models the call into a function. This is used under the + // covers for all method invocations on both ends of a + // connect()-wrapped function dispatch. This allows us to "pickle" + // calls, such as in the case of around advice. + // join_point: + // a dojo.event.MethodJoinPoint object that represents a connection + // obj: + // the scope the call will execute in + // args: + // an array of parameters that will get passed to the callee + this.jp_ = join_point; + this.object = obj; + this.args = []; + // make sure we don't lock into a mutable object which can change under us. + // It's ok if the individual items change, though. + for(var x=0; x<args.length; x++){ + this.args[x] = args[x]; + } + // the index of the 'around' that is currently being executed. + this.around_index = -1; +} + +dojo.event.MethodInvocation.prototype.proceed = function(){ + // summary: + // proceed with the method call that's represented by this invocation + // object + this.around_index++; + if(this.around_index >= this.jp_.around.length){ + return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args); + // return this.jp_.run_before_after(this.object, this.args); + }else{ + var ti = this.jp_.around[this.around_index]; + var mobj = ti[0]||dj_global; + var meth = ti[1]; + return mobj[meth].call(mobj, this); + } +} + + +dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){ + this.object = obj||dj_global; + this.methodname = funcName; + this.methodfunc = this.object[funcName]; + this.squelch = false; + // this.before = []; + // this.after = []; + // this.around = []; +} + +dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){ + // summary: + // "static" class function for returning a MethodJoinPoint from a + // scoped function. If one doesn't exist, one is created. + // obj: + // the scope to search for the function in + // funcName: + // the name of the function to return a MethodJoinPoint for + if(!obj){ obj = dj_global; } + if(!obj[funcName]){ + // supply a do-nothing method implementation + obj[funcName] = function(){}; + if(!obj[funcName]){ + // e.g. cannot add to inbuilt objects in IE6 + dojo.raise("Cannot set do-nothing method on that object "+funcName); + } + }else if((!dojo.lang.isFunction(obj[funcName]))&&(!dojo.lang.isAlien(obj[funcName]))){ + // FIXME: should we throw an exception here instead? + return null; + } + // we hide our joinpoint instance in obj[funcName + '$joinpoint'] + var jpname = funcName + "$joinpoint"; + var jpfuncname = funcName + "$joinpoint$method"; + var joinpoint = obj[jpname]; + if(!joinpoint){ + var isNode = false; + if(dojo.event["browser"]){ + if( (obj["attachEvent"])|| + (obj["nodeType"])|| + (obj["addEventListener"]) ){ + isNode = true; + dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]); + } + } + var origArity = obj[funcName].length; + obj[jpfuncname] = obj[funcName]; + // joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName); + joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname); + obj[funcName] = function(){ + var args = []; + + if((isNode)&&(!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){ //obj is a window + evt = obj.event; + }else{ + evt = window.event; + } + }catch(e){ + evt = window.event; + } + + if(evt){ + args.push(dojo.event.browser.fixEvent(evt, this)); + } + }else{ + for(var x=0; x<arguments.length; x++){ + if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){ + args.push(dojo.event.browser.fixEvent(arguments[x], this)); + }else{ + args.push(arguments[x]); + } + } + } + // return joinpoint.run.apply(joinpoint, arguments); + return joinpoint.run.apply(joinpoint, args); + } + obj[funcName].__preJoinArity = origArity; + } + return joinpoint; // dojo.event.MethodJoinPoint +} + +dojo.lang.extend(dojo.event.MethodJoinPoint, { + unintercept: function(){ + // summary: + // destroy the connection to all listeners that may have been + // registered on this joinpoint + this.object[this.methodname] = this.methodfunc; + this.before = []; + this.after = []; + this.around = []; + }, + + disconnect: dojo.lang.forward("unintercept"), + + run: function(){ + // summary: + // execute the connection represented by this join point. The + // arguments passed to run() will be passed to the function and + // its listeners. + var obj = this.object||dj_global; + var args = arguments; + + // optimization. We only compute once the array version of the arguments + // pseudo-arr in order to prevent building it each time advice is unrolled. + var aargs = []; + for(var x=0; x<args.length; x++){ + aargs[x] = args[x]; + } + + var unrollAdvice = function(marr){ + if(!marr){ + dojo.debug("Null argument to unrollAdvice()"); + return; + } + + var callObj = marr[0]||dj_global; + var callFunc = marr[1]; + + if(!callObj[callFunc]){ + dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\""); + } + + var aroundObj = marr[2]||dj_global; + var aroundFunc = marr[3]; + var msg = marr[6]; + 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(dojo.event._canTimeout){ + if(marr["delayTimer"]){ + clearTimeout(marr.delayTimer); + } + var tod = parseInt(rate*2); // is rate*2 naive? + var mcpy = dojo.lang.shallowCopy(marr); + marr.delayTimer = setTimeout(function(){ + // FIXME: on IE at least, event objects from the + // browser can go out of scope. How (or should?) we + // deal with it? + mcpy[5] = 0; + unrollAdvice(mcpy); + }, tod); + } + return; + }else{ + marr.last = cur; + } + } + + // FIXME: need to enforce rates for a connection here! + + if(aroundFunc){ + // NOTE: around advice can't delay since we might otherwise depend + // on execution order! + aroundObj[aroundFunc].call(aroundObj, to); + }else{ + // var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname); + if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){ // FIXME: the render checks are grotty! + dj_global["setTimeout"](function(){ + if(msg){ + callObj[callFunc].call(callObj, to); + }else{ + callObj[callFunc].apply(callObj, args); + } + }, delay); + }else{ // many environments can't support delay! + 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){ + dojo.debug(e); + } + }else{ + return unrollAdvice.apply(this, arguments); + } + } + + if((this["before"])&&(this.before.length>0)){ + // pass a cloned array, if this event disconnects this event forEach on this.before wont work + dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch); + } + + var result; + try{ + if((this["around"])&&(this.around.length>0)){ + var mi = new dojo.event.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){ dojo.raise(e); } } + + if((this["after"])&&(this.after.length>0)){ + // see comment on this.before above + dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch); + } + + return (this.methodfunc) ? result : null; + }, + + getArr: function(/*String*/kind){ + // summary: return a list of listeners of the past "kind" + // kind: + // can be one of: "before", "after", "around", "before-around", or + // "after-around" + var type = "after"; + // FIXME: we should be able to do this through props or Array.in() + if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){ + type = "before"; + }else if(kind=="around"){ + type = "around"; + } + if(!this[type]){ this[type] = []; } + return this[type]; // Array + }, + + kwAddAdvice: function(/*Object*/args){ + // summary: + // adds advice to the joinpoint with arguments in a map + // args: + // An object that can have the following properties: + // - adviceType + // - adviceObj + // - adviceFunc + // - aroundObj + // - aroundFunc + // - once + // - delay + // - rate + // - adviceMsg + this.addAdvice( args["adviceObj"], args["adviceFunc"], + args["aroundObj"], args["aroundFunc"], + args["adviceType"], args["precedence"], + args["once"], args["delay"], args["rate"], + args["adviceMsg"]); + }, + + addAdvice: function( thisAdviceObj, thisAdvice, + thisAroundObj, thisAround, + adviceType, precedence, + once, delay, rate, asMessage){ + // summary: + // add advice to this joinpoint using positional parameters + // thisAdviceObj: + // the scope in which to locate/execute the named adviceFunc. + // thisAdviceFunc: + // the name of the function being conected + // thisAroundObj: + // the scope in which to locate/execute the named aroundFunc. + // thisAroundFunc: + // the name of the function that will be used to mediate the + // advice call. + // adviceType: + // Optional. String. One of "before", "after", "around", + // "before-around", or "after-around". FIXME + // once: + // boolean that determines whether or not this advice will create + // a new connection if an identical advice set has already been + // provided. Defaults to "false". + // delay: + // an optional delay (in ms), as an integer, for dispatch of a + // listener after the source has been fired. + // rate: + // an optional rate throttling parameter (integer, in ms). When + // specified, this particular connection will not fire more than + // once in the interval specified by the rate + // adviceMsg: + // boolean. Should the listener have all the parameters passed in + // as a single argument? + var arr = this.getArr(adviceType); + if(!arr){ + dojo.raise("bad this: " + this); + } + + var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage]; + + 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){ + // summary: + // returns the array index of the first existing connection + // betweened the passed advice and this joinpoint. Will be -1 if + // none exists. + // thisAdviceObj: + // the scope in which to locate/execute the named adviceFunc. + // thisAdviceFunc: + // the name of the function being conected + // adviceType: + // Optional. String. One of "before", "after", "around", + // "before-around", or "after-around". FIXME + // arr: + // Optional. The list of advices to search. Will be found via + // adviceType if not passed + 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; // Integer + }, + + removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){ + // summary: + // returns the array index of the first existing connection + // betweened the passed advice and this joinpoint. Will be -1 if + // none exists. + // thisAdviceObj: + // the scope in which to locate/execute the named adviceFunc. + // thisAdviceFunc: + // the name of the function being conected + // adviceType: + // Optional. String. One of "before", "after", "around", + // "before-around", or "after-around". FIXME + // once: + // Optional. Should this only remove the first occurance of the + // connection? + 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; + } +});
Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/common.js ------------------------------------------------------------------------------ svn:eol-style = native Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/topic.js URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/topic.js?view=diff&rev=474551&r1=474550&r2=474551 ============================================================================== --- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/topic.js (original) +++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/event/topic.js Mon Nov 13 14:54:45 2006 @@ -1,5 +1,5 @@ /* - Copyright (c) 2004-2005, The Dojo Foundation + Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the @@ -8,82 +8,193 @@ http://dojotoolkit.org/community/licensing.shtml */ -dojo.require("dojo.event"); +dojo.require("dojo.event.common"); dojo.provide("dojo.event.topic"); dojo.event.topic = new function(){ this.topics = {}; - this.getTopic = function(topicName){ - if(!this.topics[topicName]){ - this.topics[topicName] = new this.TopicImpl(topicName); + this.getTopic = function(/*String*/topic){ + // summary: + // returns a topic implementation object of type + // dojo.event.topic.TopicImpl + // topic: + // a unique, opaque string that names the topic + if(!this.topics[topic]){ + this.topics[topic] = new this.TopicImpl(topic); } - return this.topics[topicName]; + return this.topics[topic]; // a dojo.event.topic.TopicImpl object } - this.registerPublisher = function(topic, obj, funcName){ + this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ + // summary: + // registers a function as a publisher on a topic. Subsequent + // calls to the function will cause a publish event on the topic + // with the arguments passed to the function passed to registered + // listeners. + // topic: + // a unique, opaque string that names the topic + // obj: + // the scope to locate the function in + // funcName: + // the name of the function to register var topic = this.getTopic(topic); topic.registerPublisher(obj, funcName); } - this.subscribe = function(topic, obj, funcName){ + this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ + // summary: + // susbscribes the function to the topic. Subsequent events + // dispached to the topic will create a function call for the + // obj.funcName() function. + // topic: + // a unique, opaque string that names the topic + // obj: + // the scope to locate the function in + // funcName: + // the name of the function to being registered as a listener var topic = this.getTopic(topic); topic.subscribe(obj, funcName); } - this.unsubscribe = function(topic, obj, funcName){ + this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ + // summary: + // unsubscribes the obj.funcName() from the topic + // topic: + // a unique, opaque string that names the topic + // obj: + // the scope to locate the function in + // funcName: + // the name of the function to being unregistered as a listener var topic = this.getTopic(topic); topic.unsubscribe(obj, funcName); } - this.publish = function(topic, message){ + this.destroy = function(/*String*/topic){ + // summary: + // destroys the topic and unregisters all listeners + // topic: + // a unique, opaque string that names the topic + this.getTopic(topic).destroy(); + delete this.topics[topic]; + } + + this.publishApply = function(/*String*/topic, /*Array*/args){ + // summary: + // dispatches an event to the topic using the args array as the + // source for the call arguments to each listener. This is similar + // to JavaScript's built-in Function.apply() + // topic: + // a unique, opaque string that names the topic + // args: + // the arguments to be passed into listeners of the topic + var topic = this.getTopic(topic); + topic.sendMessage.apply(topic, args); + } + + this.publish = function(/*String*/topic, /*Object*/message){ + // summary: + // manually "publish" to the passed topic + // topic: + // a unique, opaque string that names the topic + // message: + // can be an array of parameters (similar to publishApply), or + // will be treated as one of many arguments to be passed along in + // a "flat" unrolling var topic = this.getTopic(topic); // if message is an array, we treat it as a set of arguments, // otherwise, we just pass on the arguments passed in as-is var args = []; - if((arguments.length == 2)&&(message.length)&&(typeof message != "string")){ - args = message; - }else{ - var args = []; - for(var x=1; x<arguments.length; x++){ - args.push(arguments[x]); - } + // could we use concat instead here? + for(var x=1; x<arguments.length; x++){ + args.push(arguments[x]); } topic.sendMessage.apply(topic, args); } } dojo.event.topic.TopicImpl = function(topicName){ + // summary: a class to represent topics + this.topicName = topicName; - var self = this; - self.subscribe = function(listenerObject, listenerMethod){ + this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){ + // summary: + // use dojo.event.connect() to attach the passed listener to the + // topic represented by this object + // listenerObject: + // if a string and listenerMethod is ommitted, this is treated as + // the name of a function in the global namespace. If + // listenerMethod is provided, this is the scope to find/execute + // the function in. + // listenerMethod: + // Optional. The function to register. var tf = listenerMethod||listenerObject; var to = (!listenerMethod) ? dj_global : listenerObject; - dojo.event.kwConnect({ - srcObj: self, + return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint + srcObj: this, srcFunc: "sendMessage", adviceObj: to, adviceFunc: tf }); } - self.unsubscribe = function(listenerObject, listenerMethod){ + this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){ + // summary: + // use dojo.event.disconnect() to attach the passed listener to the + // topic represented by this object + // listenerObject: + // if a string and listenerMethod is ommitted, this is treated as + // the name of a function in the global namespace. If + // listenerMethod is provided, this is the scope to find the + // function in. + // listenerMethod: + // Optional. The function to unregister. var tf = (!listenerMethod) ? listenerObject : listenerMethod; var to = (!listenerMethod) ? null : listenerObject; - dojo.event.kwDisconnect({ - srcObj: self, + return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint + srcObj: this, srcFunc: "sendMessage", adviceObj: to, adviceFunc: tf }); } - self.registerPublisher = function(publisherObject, publisherMethod){ - dojo.event.connect(publisherObject, publisherMethod, self, "sendMessage"); + this._getJoinPoint = function(){ + return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage"); + } + + this.setSquelch = function(/*Boolean*/shouldSquelch){ + // summary: + // determine whether or not exceptions in the calling of a + // listener in the chain should stop execution of the chain. + this._getJoinPoint().squelch = shouldSquelch; + } + + this.destroy = function(){ + // summary: disconnects all listeners from this topic + this._getJoinPoint().disconnect(); + } + + this.registerPublisher = function( /*Object*/publisherObject, + /*Function or String*/publisherMethod){ + // summary: + // registers the passed function as a publisher on this topic. + // Each time the function is called, an event will be published on + // this topic. + // publisherObject: + // if a string and listenerMethod is ommitted, this is treated as + // the name of a function in the global namespace. If + // listenerMethod is provided, this is the scope to find the + // function in. + // publisherMethod: + // Optional. The function to register. + dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage"); } - self.sendMessage = function(message){ + this.sendMessage = function(message){ + // summary: a stub to be called when a message is sent to the topic. + // The message has been propagated } } Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/experimental.js URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/experimental.js?view=auto&rev=474551 ============================================================================== --- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/experimental.js (added) +++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/experimental.js Mon Nov 13 14:54:45 2006 @@ -0,0 +1,30 @@ +/* + 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("dojo.experimental"); + +dojo.experimental = function(/* String */ moduleName, /* String? */ extra){ + // summary: Marks code as experimental. + // description: + // This can be used to mark a function, file, or module as experimental. + // Experimental code is not ready to be used, and the APIs are subject + // to change without notice. Experimental code may be completed deleted + // without going through the normal deprecation process. + // moduleName: The name of a module, or the name of a module file or a specific function + // extra: some additional message for the user + + // examples: + // dojo.experimental("dojo.data.Result"); + // dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA"); + var message = "EXPERIMENTAL: " + moduleName; + message += " -- Not yet ready for use. APIs subject to change without notice."; + if(extra){ message += " " + extra; } + dojo.debug(message); +} Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/experimental.js ------------------------------------------------------------------------------ svn:eol-style = native