[483] | 1 | define(["./_base/kernel", "./_base/lang", "./_base/array", "./_base/connect", "./query", "./domReady"], |
---|
| 2 | function(dojo, lang, darray, connect, query, domReady){ |
---|
| 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 | domReady( function(){ dojo.behavior.apply(); } ); |
---|
| 247 | |
---|
| 248 | return dojo.behavior; |
---|
| 249 | }); |
---|