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