summaryrefslogtreecommitdiff
path: root/lib/dojo/aspect.js.uncompressed.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dojo/aspect.js.uncompressed.js')
-rw-r--r--lib/dojo/aspect.js.uncompressed.js207
1 files changed, 207 insertions, 0 deletions
diff --git a/lib/dojo/aspect.js.uncompressed.js b/lib/dojo/aspect.js.uncompressed.js
new file mode 100644
index 000000000..506c4ca43
--- /dev/null
+++ b/lib/dojo/aspect.js.uncompressed.js
@@ -0,0 +1,207 @@
+define("dojo/aspect", [], function(){
+
+// TODOC: after/before/around return object
+// TODOC: after/before/around param types.
+
+/*=====
+ dojo.aspect = {
+ // summary: provides aspect oriented programming functionality, allowing for
+ // one to add before, around, or after advice on existing methods.
+ //
+ // example:
+ // | define(["dojo/aspect"], function(aspect){
+ // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
+ // | this will be called when targetObject.methodName() is called, after the original function is called
+ // | });
+ //
+ // example:
+ // The returned signal object can be used to cancel the advice.
+ // | signal.remove(); // this will stop the advice from being executed anymore
+ // | aspect.before(targetObject, "methodName", function(someArgument){
+ // | // this will be called when targetObject.methodName() is called, before the original function is called
+ // | });
+
+ after: function(target, methodName, advice, receiveArguments){
+ // summary: The "after" export of the aspect module is a function that can be used to attach
+ // "after" advice to a method. This function will be executed after the original method
+ // is executed. By default the function will be called with a single argument, the return
+ // value of the original method, or the the return value of the last executed advice (if a previous one exists).
+ // The fourth (optional) argument can be set to true to so the function receives the original
+ // arguments (from when the original method was called) rather than the return value.
+ // If there are multiple "after" advisors, they are executed in the order they were registered.
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called after the original method
+ // receiveArguments: Boolean?
+ // If this is set to true, the advice function receives the original arguments (from when the original mehtod
+ // was called) rather than the return value of the original/previous method.
+ // returns:
+ // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
+ // stop the advice function from being executed.
+ },
+
+ before: function(target, methodName, advice){
+ // summary: The "before" export of the aspect module is a function that can be used to attach
+ // "before" advice to a method. This function will be executed before the original method
+ // is executed. This function will be called with the arguments used to call the method.
+ // This function may optionally return an array as the new arguments to use to call
+ // the original method (or the previous, next-to-execute before advice, if one exists).
+ // If the before method doesn't return anything (returns undefined) the original arguments
+ // will be preserved.
+ // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
+ //
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called before the original method
+ },
+
+ around: function(target, methodName, advice){
+ // summary: The "around" export of the aspect module is a function that can be used to attach
+ // "around" advice to a method. The advisor function is immediately executed when
+ // the around() is called, is passed a single argument that is a function that can be
+ // called to continue execution of the original method (or the next around advisor).
+ // The advisor function should return a function, and this function will be called whenever
+ // the method is called. It will be called with the arguments used to call the method.
+ // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
+ //
+ // example:
+ // If there are multiple "around" advisors, the most recent one is executed first,
+ // which can then delegate to the next one and so on. For example:
+ // | around(obj, "foo", function(originalFoo){
+ // | return function(){
+ // | var start = new Date().getTime();
+ // | var results = originalFoo.apply(this, arguments); // call the original
+ // | var end = new Date().getTime();
+ // | console.log("foo execution took " + (end - start) + " ms");
+ // | return results;
+ // | };
+ // | });
+ //
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called around the original method
+ }
+
+ };
+=====*/
+
+ "use strict";
+ var nextId = 0;
+ function advise(dispatcher, type, advice, receiveArguments){
+ var previous = dispatcher[type];
+ var around = type == "around";
+ var signal;
+ if(around){
+ var advised = advice(function(){
+ return previous.advice(this, arguments);
+ });
+ signal = {
+ remove: function(){
+ signal.cancelled = true;
+ },
+ advice: function(target, args){
+ return signal.cancelled ?
+ previous.advice(target, args) : // cancelled, skip to next one
+ advised.apply(target, args); // called the advised function
+ }
+ };
+ }else{
+ // create the remove handler
+ signal = {
+ remove: function(){
+ var previous = signal.previous;
+ var next = signal.next;
+ if(!next && !previous){
+ delete dispatcher[type];
+ }else{
+ if(previous){
+ previous.next = next;
+ }else{
+ dispatcher[type] = next;
+ }
+ if(next){
+ next.previous = previous;
+ }
+ }
+ },
+ id: nextId++,
+ advice: advice,
+ receiveArguments: receiveArguments
+ };
+ }
+ if(previous && !around){
+ if(type == "after"){
+ // add the listener to the end of the list
+ var next = previous;
+ while(next){
+ previous = next;
+ next = next.next;
+ }
+ previous.next = signal;
+ signal.previous = previous;
+ }else if(type == "before"){
+ // add to beginning
+ dispatcher[type] = signal;
+ signal.next = previous;
+ previous.previous = signal;
+ }
+ }else{
+ // around or first one just replaces
+ dispatcher[type] = signal;
+ }
+ return signal;
+ }
+ function aspect(type){
+ return function(target, methodName, advice, receiveArguments){
+ var existing = target[methodName], dispatcher;
+ if(!existing || existing.target != target){
+ // no dispatcher in place
+ target[methodName] = dispatcher = function(){
+ var executionId = nextId;
+ // before advice
+ var args = arguments;
+ var before = dispatcher.before;
+ while(before){
+ args = before.advice.apply(this, args) || args;
+ before = before.next;
+ }
+ // around advice
+ if(dispatcher.around){
+ var results = dispatcher.around.advice(this, args);
+ }
+ // after advice
+ var after = dispatcher.after;
+ while(after && after.id < executionId){
+ results = after.receiveArguments ? after.advice.apply(this, args) || results :
+ after.advice.call(this, results);
+ after = after.next;
+ }
+ return results;
+ };
+ if(existing){
+ dispatcher.around = {advice: function(target, args){
+ return existing.apply(target, args);
+ }};
+ }
+ dispatcher.target = target;
+ }
+ var results = advise((dispatcher || existing), type, advice, receiveArguments);
+ advice = null;
+ return results;
+ };
+ }
+ return {
+ before: aspect("before"),
+ around: aspect("around"),
+ after: aspect("after")
+ };
+});