]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/aspect.js.uncompressed.js
1 define("dojo/aspect", [], function(){
3 // TODOC: after/before/around return object
4 // TODOC: after/before/around param types.
8 // summary: provides aspect oriented programming functionality, allowing for
9 // one to add before, around, or after advice on existing methods.
12 // | define(["dojo/aspect"], function(aspect){
13 // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
14 // | this will be called when targetObject.methodName() is called, after the original function is called
18 // The returned signal object can be used to cancel the advice.
19 // | signal.remove(); // this will stop the advice from being executed anymore
20 // | aspect.before(targetObject, "methodName", function(someArgument){
21 // | // this will be called when targetObject.methodName() is called, before the original function is called
24 after: function(target, methodName, advice, receiveArguments){
25 // summary: The "after" export of the aspect module is a function that can be used to attach
26 // "after" advice to a method. This function will be executed after the original method
27 // is executed. By default the function will be called with a single argument, the return
28 // value of the original method, or the the return value of the last executed advice (if a previous one exists).
29 // The fourth (optional) argument can be set to true to so the function receives the original
30 // arguments (from when the original method was called) rather than the return value.
31 // If there are multiple "after" advisors, they are executed in the order they were registered.
33 // This is the target object
35 // This is the name of the method to attach to.
37 // This is function to be called after the original method
38 // receiveArguments: Boolean?
39 // If this is set to true, the advice function receives the original arguments (from when the original mehtod
40 // was called) rather than the return value of the original/previous method.
42 // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
43 // stop the advice function from being executed.
46 before: function(target, methodName, advice){
47 // summary: The "before" export of the aspect module is a function that can be used to attach
48 // "before" advice to a method. This function will be executed before the original method
49 // is executed. This function will be called with the arguments used to call the method.
50 // This function may optionally return an array as the new arguments to use to call
51 // the original method (or the previous, next-to-execute before advice, if one exists).
52 // If the before method doesn't return anything (returns undefined) the original arguments
54 // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
57 // This is the target object
59 // This is the name of the method to attach to.
61 // This is function to be called before the original method
64 around: function(target, methodName, advice){
65 // summary: The "around" export of the aspect module is a function that can be used to attach
66 // "around" advice to a method. The advisor function is immediately executed when
67 // the around() is called, is passed a single argument that is a function that can be
68 // called to continue execution of the original method (or the next around advisor).
69 // The advisor function should return a function, and this function will be called whenever
70 // the method is called. It will be called with the arguments used to call the method.
71 // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
74 // If there are multiple "around" advisors, the most recent one is executed first,
75 // which can then delegate to the next one and so on. For example:
76 // | around(obj, "foo", function(originalFoo){
77 // | return function(){
78 // | var start = new Date().getTime();
79 // | var results = originalFoo.apply(this, arguments); // call the original
80 // | var end = new Date().getTime();
81 // | console.log("foo execution took " + (end - start) + " ms");
87 // This is the target object
89 // This is the name of the method to attach to.
91 // This is function to be called around the original method
99 function advise(dispatcher
, type
, advice
, receiveArguments
){
100 var previous
= dispatcher
[type
];
101 var around
= type
== "around";
104 var advised
= advice(function(){
105 return previous
.advice(this, arguments
);
109 signal
.cancelled
= true;
111 advice: function(target
, args
){
112 return signal
.cancelled
?
113 previous
.advice(target
, args
) : // cancelled, skip to next one
114 advised
.apply(target
, args
); // called the advised function
118 // create the remove handler
121 var previous
= signal
.previous
;
122 var next
= signal
.next
;
123 if(!next
&& !previous
){
124 delete dispatcher
[type
];
127 previous
.next
= next
;
129 dispatcher
[type
] = next
;
132 next
.previous
= previous
;
138 receiveArguments
: receiveArguments
141 if(previous
&& !around
){
143 // add the listener to the end of the list
149 previous
.next
= signal
;
150 signal
.previous
= previous
;
151 }else if(type
== "before"){
153 dispatcher
[type
] = signal
;
154 signal
.next
= previous
;
155 previous
.previous
= signal
;
158 // around or first one just replaces
159 dispatcher
[type
] = signal
;
163 function aspect(type
){
164 return function(target
, methodName
, advice
, receiveArguments
){
165 var existing
= target
[methodName
], dispatcher
;
166 if(!existing
|| existing
.target
!= target
){
167 // no dispatcher in place
168 target
[methodName
] = dispatcher = function(){
169 var executionId
= nextId
;
171 var args
= arguments
;
172 var before
= dispatcher
.before
;
174 args
= before
.advice
.apply(this, args
) || args
;
175 before
= before
.next
;
178 if(dispatcher
.around
){
179 var results
= dispatcher
.around
.advice(this, args
);
182 var after
= dispatcher
.after
;
183 while(after
&& after
.id
< executionId
){
184 results
= after
.receiveArguments
? after
.advice
.apply(this, args
) || results
:
185 after
.advice
.call(this, results
);
191 dispatcher
.around
= {advice: function(target
, args
){
192 return existing
.apply(target
, args
);
195 dispatcher
.target
= target
;
197 var results
= advise((dispatcher
|| existing
), type
, advice
, receiveArguments
);
203 before
: aspect("before"),
204 around
: aspect("around"),
205 after
: aspect("after")