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