]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/behavior.js.uncompressed.js
Merge remote-tracking branch 'upstream/master'
[tt-rss.git] / lib / dojo / behavior.js.uncompressed.js
1 define("dojo/behavior", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/connect", "./query", "./ready"],
2 function(dojo, lang, darray, connect, query, ready){
3
4 // module:
5 //              dojo/behavior
6
7 dojo.deprecated("dojo.behavior", "Use dojo/on with event delegation (on.selector())");
8
9 var Behavior = function(){
10         // summary:
11         //              Deprecated.   dojo/behavior's functionality can be achieved using event delegation using dojo/on
12         //              and on.selector().
13         // description:
14         //              A very simple, lightweight mechanism for applying code to
15         //              existing documents, based around `dojo/query` (CSS3 selectors) for node selection,
16         //              and a simple two-command API: `add()` and `apply()`;
17         //
18         //              Behaviors apply to a given page, and are registered following the syntax
19         //              options described by `add()` to match nodes to actions, or "behaviors".
20         //
21         //              Added behaviors are applied to the current DOM when .apply() is called,
22         //              matching only new nodes found since .apply() was last called.
23
24         function arrIn(obj, name){
25                 if(!obj[name]){ obj[name] = []; }
26                 return obj[name];
27         }
28
29         var _inc = 0;
30
31         function forIn(obj, scope, func){
32                 var tmpObj = {};
33                 for(var x in obj){
34                         if(typeof tmpObj[x] == "undefined"){
35                                 if(!func){
36                                         scope(obj[x], x);
37                                 }else{
38                                         func.call(scope, obj[x], x);
39                                 }
40                         }
41                 }
42         }
43
44         // FIXME: need a better test so we don't exclude nightly Safari's!
45         this._behaviors = {};
46         this.add = function(/* Object */behaviorObj){
47                 // summary:
48                 //              Add the specified behavior to the list of behaviors, ignoring existing
49                 //              matches.
50                 // behaviorObj: Object
51                 //              The behavior object that will be added to behaviors list. The behaviors
52                 //              in the list will be applied the next time apply() is called.
53                 // description:
54                 //              Add the specified behavior to the list of behaviors which will
55                 //              be applied the next time apply() is called. Calls to add() for
56                 //              an already existing behavior do not replace the previous rules,
57                 //              but are instead additive. New nodes which match the rule will
58                 //              have all add()-ed behaviors applied to them when matched.
59                 //
60                 //              The "found" method is a generalized handler that's called as soon
61                 //              as the node matches the selector. Rules for values that follow also
62                 //              apply to the "found" key.
63                 //
64                 //              The "on*" handlers are attached with `dojo.connect()`, using the
65                 //              matching node
66                 //
67                 //              If the value corresponding to the ID key is a function and not a
68                 //              list, it's treated as though it was the value of "found".
69                 //
70                 //              dojo/behavior.add() can be called any number of times before
71                 //              the DOM is ready. `dojo/behavior.apply()` is called automatically
72                 //              by `dojo.addOnLoad`, though can be called to re-apply previously added
73                 //              behaviors anytime the DOM changes.
74                 //
75                 //              There are a variety of formats permitted in the behaviorObject
76                 //
77                 // example:
78                 //              Simple list of properties. "found" is special. "Found" is assumed if
79                 //              no property object for a given selector, and property is a function.
80                 //
81                 //      |       behavior.add({
82                 //      |               "#id": {
83                 //      |                       "found": function(element){
84                 //      |                               // node match found
85                 //      |                       },
86                 //      |                       "onclick": function(evt){
87                 //      |                               // register onclick handler for found node
88                 //      |                       }
89                 //      |               },
90                 //      |               "#otherid": function(element){
91                 //      |                       // assumes "found" with this syntax
92                 //      |               }
93                 //      |       });
94                 //
95                 // example:
96                 //               If property is a string, a dojo.publish will be issued on the channel:
97                 //
98                 //      |       behavior.add({
99                 //      |               // topic.publish() whenever class="noclick" found on anchors
100                 //      |               "a.noclick": "/got/newAnchor",
101                 //      |               "div.wrapper": {
102                 //      |                       "onclick": "/node/wasClicked"
103                 //      |               }
104                 //      |       });
105                 //      |       topic.subscribe("/got/newAnchor", function(node){
106                 //      |               // handle node finding when dojo/behavior.apply() is called,
107                 //      |               // provided a newly matched node is found.
108                 //      |       });
109                 //
110                 // example:
111                 //              Scoping can be accomplished by passing an object as a property to
112                 //              a connection handle (on*):
113                 //
114                 //      |       behavior.add({
115                 //      |                       "#id": {
116                 //      |                               // like calling dojo.hitch(foo,"bar"). execute foo.bar() in scope of foo
117                 //      |                               "onmouseenter": { targetObj: foo, targetFunc: "bar" },
118                 //      |                               "onmouseleave": { targetObj: foo, targetFunc: "baz" }
119                 //      |                       }
120                 //      |       });
121                 //
122                 // example:
123                 //              Behaviors match on CSS3 Selectors, powered by dojo/query. Example selectors:
124                 //
125                 //      |       behavior.add({
126                 //      |               // match all direct descendants
127                 //      |               "#id4 > *": function(element){
128                 //      |                       // ...
129                 //      |               },
130                 //      |
131                 //      |               // match the first child node that's an element
132                 //      |               "#id4 > :first-child": { ... },
133                 //      |
134                 //      |               // match the last child node that's an element
135                 //      |               "#id4 > :last-child":  { ... },
136                 //      |
137                 //      |               // all elements of type tagname
138                 //      |               "tagname": {
139                 //      |                       // ...
140                 //      |               },
141                 //      |
142                 //      |               "tagname1 tagname2 tagname3": {
143                 //      |                       // ...
144                 //      |               },
145                 //      |
146                 //      |               ".classname": {
147                 //      |                       // ...
148                 //      |               },
149                 //      |
150                 //      |               "tagname.classname": {
151                 //      |                       // ...
152                 //      |               }
153                 //      |       });
154                 //
155
156                 forIn(behaviorObj, this, function(behavior, name){
157                         var tBehavior = arrIn(this._behaviors, name);
158                         if(typeof tBehavior["id"] != "number"){
159                                 tBehavior.id = _inc++;
160                         }
161                         var cversion = [];
162                         tBehavior.push(cversion);
163                         if((lang.isString(behavior))||(lang.isFunction(behavior))){
164                                 behavior = { found: behavior };
165                         }
166                         forIn(behavior, function(rule, ruleName){
167                                 arrIn(cversion, ruleName).push(rule);
168                         });
169                 });
170         };
171
172         var _applyToNode = function(node, action, ruleSetName){
173                 if(lang.isString(action)){
174                         if(ruleSetName == "found"){
175                                 connect.publish(action, [ node ]);
176                         }else{
177                                 connect.connect(node, ruleSetName, function(){
178                                         connect.publish(action, arguments);
179                                 });
180                         }
181                 }else if(lang.isFunction(action)){
182                         if(ruleSetName == "found"){
183                                 action(node);
184                         }else{
185                                 connect.connect(node, ruleSetName, action);
186                         }
187                 }
188         };
189
190         this.apply = function(){
191                 // summary:
192                 //              Applies all currently registered behaviors to the document.
193                 //
194                 // description:
195                 //              Applies all currently registered behaviors to the document,
196                 //              taking care to ensure that only incremental updates are made
197                 //              since the last time add() or apply() were called.
198                 //
199                 //              If new matching nodes have been added, all rules in a behavior will be
200                 //              applied to that node. For previously matched nodes, only
201                 //              behaviors which have been added since the last call to apply()
202                 //              will be added to the nodes.
203                 //
204                 //              apply() is called once automatically by `dojo.addOnLoad`, so
205                 //              registering behaviors with `dojo/behavior.add()` before the DOM is
206                 //              ready is acceptable, provided the dojo.behavior module is ready.
207                 //
208                 //              Calling appy() manually after manipulating the DOM is required
209                 //              to rescan the DOM and apply newly .add()ed behaviors, or to match
210                 //              nodes that match existing behaviors when those nodes are added to
211                 //              the DOM.
212                 //
213                 forIn(this._behaviors, function(tBehavior, id){
214                         query(id).forEach(
215                                 function(elem){
216                                         var runFrom = 0;
217                                         var bid = "_dj_behavior_"+tBehavior.id;
218                                         if(typeof elem[bid] == "number"){
219                                                 runFrom = elem[bid];
220                                                 if(runFrom == (tBehavior.length)){
221                                                         return;
222                                                 }
223                                         }
224                                         // run through the versions, applying newer rules at each step
225
226                                         for(var x=runFrom, tver; tver = tBehavior[x]; x++){
227                                                 forIn(tver, function(ruleSet, ruleSetName){
228                                                         if(lang.isArray(ruleSet)){
229                                                                 darray.forEach(ruleSet, function(action){
230                                                                         _applyToNode(elem, action, ruleSetName);
231                                                                 });
232                                                         }
233                                                 });
234                                         }
235
236                                         // ensure that re-application only adds new rules to the node
237                                         elem[bid] = tBehavior.length;
238                                 }
239                         );
240                 });
241         };
242 };
243
244 dojo.behavior = new Behavior();
245
246 ready(dojo.behavior, "apply"); // FIXME: should this use a priority? before/after parser priority?
247
248 return dojo.behavior;
249 });