]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/aspect.js.uncompressed.js
1 define("dojo/aspect", [], function(){
7 var undefined, nextId
= 0;
8 function advise(dispatcher
, type
, advice
, receiveArguments
){
9 var previous
= dispatcher
[type
];
10 var around
= type
== "around";
13 var advised
= advice(function(){
14 return previous
.advice(this, arguments
);
18 signal
.cancelled
= true;
20 advice: function(target
, args
){
21 return signal
.cancelled
?
22 previous
.advice(target
, args
) : // cancelled, skip to next one
23 advised
.apply(target
, args
); // called the advised function
27 // create the remove handler
30 var previous
= signal
.previous
;
31 var next
= signal
.next
;
32 if(!next
&& !previous
){
33 delete dispatcher
[type
];
38 dispatcher
[type
] = next
;
41 next
.previous
= previous
;
47 receiveArguments
: receiveArguments
50 if(previous
&& !around
){
52 // add the listener to the end of the list
53 // note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bug
54 while(previous
.next
&& (previous
= previous
.next
)){}
55 previous
.next
= signal
;
56 signal
.previous
= previous
;
57 }else if(type
== "before"){
59 dispatcher
[type
] = signal
;
60 signal
.next
= previous
;
61 previous
.previous
= signal
;
64 // around or first one just replaces
65 dispatcher
[type
] = signal
;
69 function aspect(type
){
70 return function(target
, methodName
, advice
, receiveArguments
){
71 var existing
= target
[methodName
], dispatcher
;
72 if(!existing
|| existing
.target
!= target
){
73 // no dispatcher in place
74 target
[methodName
] = dispatcher = function(){
75 var executionId
= nextId
;
78 var before
= dispatcher
.before
;
80 args
= before
.advice
.apply(this, args
) || args
;
84 if(dispatcher
.around
){
85 var results
= dispatcher
.around
.advice(this, args
);
88 var after
= dispatcher
.after
;
89 while(after
&& after
.id
< executionId
){
90 if(after
.receiveArguments
){
91 var newResults
= after
.advice
.apply(this, args
);
92 // change the return value only if a new value was returned
93 results
= newResults
=== undefined ? results
: newResults
;
95 results
= after
.advice
.call(this, results
, args
);
102 dispatcher
.around
= {advice: function(target
, args
){
103 return existing
.apply(target
, args
);
106 dispatcher
.target
= target
;
108 var results
= advise((dispatcher
|| existing
), type
, advice
, receiveArguments
);
114 // TODOC: after/before/around return object
116 var after
= aspect("after");
118 after = function(target, methodName, advice, receiveArguments){
120 // The "after" export of the aspect module is a function that can be used to attach
121 // "after" advice to a method. This function will be executed after the original method
122 // is executed. By default the function will be called with a single argument, the return
123 // value of the original method, or the the return value of the last executed advice (if a previous one exists).
124 // The fourth (optional) argument can be set to true to so the function receives the original
125 // arguments (from when the original method was called) rather than the return value.
126 // If there are multiple "after" advisors, they are executed in the order they were registered.
128 // This is the target object
129 // methodName: String
130 // This is the name of the method to attach to.
132 // This is function to be called after the original method
133 // receiveArguments: Boolean?
134 // If this is set to true, the advice function receives the original arguments (from when the original mehtod
135 // was called) rather than the return value of the original/previous method.
137 // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
138 // stop the advice function from being executed.
142 var before
= aspect("before");
144 before = function(target, methodName, advice){
146 // The "before" export of the aspect module is a function that can be used to attach
147 // "before" advice to a method. This function will be executed before the original method
148 // is executed. This function will be called with the arguments used to call the method.
149 // This function may optionally return an array as the new arguments to use to call
150 // the original method (or the previous, next-to-execute before advice, if one exists).
151 // If the before method doesn't return anything (returns undefined) the original arguments
152 // will be preserved.
153 // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
155 // This is the target object
156 // methodName: String
157 // This is the name of the method to attach to.
159 // This is function to be called before the original method
163 var around
= aspect("around");
165 around = function(target, methodName, advice){
167 // The "around" export of the aspect module is a function that can be used to attach
168 // "around" advice to a method. The advisor function is immediately executed when
169 // the around() is called, is passed a single argument that is a function that can be
170 // called to continue execution of the original method (or the next around advisor).
171 // The advisor function should return a function, and this function will be called whenever
172 // the method is called. It will be called with the arguments used to call the method.
173 // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
175 // If there are multiple "around" advisors, the most recent one is executed first,
176 // which can then delegate to the next one and so on. For example:
177 // | around(obj, "foo", function(originalFoo){
178 // | return function(){
179 // | var start = new Date().getTime();
180 // | var results = originalFoo.apply(this, arguments); // call the original
181 // | var end = new Date().getTime();
182 // | console.log("foo execution took " + (end - start) + " ms");
187 // This is the target object
188 // methodName: String
189 // This is the name of the method to attach to.
191 // This is function to be called around the original method
197 // provides aspect oriented programming functionality, allowing for
198 // one to add before, around, or after advice on existing methods.
200 // | define(["dojo/aspect"], function(aspect){
201 // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
202 // | this will be called when targetObject.methodName() is called, after the original function is called
206 // The returned signal object can be used to cancel the advice.
207 // | signal.remove(); // this will stop the advice from being executed anymore
208 // | aspect.before(targetObject, "methodName", function(someArgument){
209 // | // this will be called when targetObject.methodName() is called, before the original function is called