b0fc0d02bd73a5a6d47863b9ca5e4e2864843a65.svn-base 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. if (!dojo._hasResource["dojo.behavior"]) { // _hasResource checks added by
  2. // build. Do not use _hasResource
  3. // directly in your code.
  4. dojo._hasResource["dojo.behavior"] = true;
  5. dojo.provide("dojo.behavior");
  6. dojo.behavior = new function() {
  7. function arrIn(obj, name) {
  8. if (!obj[name]) {
  9. obj[name] = [];
  10. }
  11. return obj[name];
  12. }
  13. var _inc = 0;
  14. function forIn(obj, scope, func) {
  15. var tmpObj = {};
  16. for (var x in obj) {
  17. if (typeof tmpObj[x] == "undefined") {
  18. if (!func) {
  19. scope(obj[x], x);
  20. } else {
  21. func.call(scope, obj[x], x);
  22. }
  23. }
  24. }
  25. }
  26. // FIXME: need a better test so we don't exclude nightly Safari's!
  27. this._behaviors = {};
  28. this.add = function(behaviorObj) {
  29. // summary:
  30. // add the specified behavior to the list of behaviors which will
  31. // be applied the next time apply() is called. Calls to add() for
  32. // an already existing behavior do not replace the previous rules,
  33. // but are instead additive. New nodes which match the rule will
  34. // have all add()-ed behaviors applied to them when matched.
  35. //
  36. // description:
  37. // behavior objects are specified in the following format(s):
  38. //
  39. // {
  40. // "#id": {
  41. // "found": function(element){
  42. // // ...
  43. // },
  44. //
  45. // "onblah": {targetObj: foo, targetFunc: "bar"},
  46. //
  47. // "onblarg": "/foo/bar/baz/blarg",
  48. //
  49. // "onevent": function(evt){
  50. // },
  51. //
  52. // "onotherevent: function(evt){
  53. // // ...
  54. // }
  55. // },
  56. //
  57. // "#id2": {
  58. // // ...
  59. // },
  60. //
  61. // "#id3": function(element){
  62. // // ...
  63. // },
  64. //
  65. // // publish the match on a topic
  66. // "#id4": "/found/topic/name",
  67. //
  68. // // match all direct descendants
  69. // "#id4 > *": function(element){
  70. // // ...
  71. // },
  72. //
  73. // // match the first child node that's an element
  74. // "#id4 > :first-child": { ... },
  75. //
  76. // // match the last child node that's an element
  77. // "#id4 > :last-child": { ... },
  78. //
  79. // // all elements of type tagname
  80. // "tagname": {
  81. // // ...
  82. // },
  83. //
  84. // "tagname1 tagname2 tagname3": {
  85. // // ...
  86. // },
  87. //
  88. // ".classname": {
  89. // // ...
  90. // },
  91. //
  92. // "tagname.classname": {
  93. // // ...
  94. // },
  95. // }
  96. //
  97. // The "found" method is a generalized handler that's called as soon
  98. // as the node matches the selector. Rules for values that follow
  99. // also
  100. // apply to the "found" key.
  101. //
  102. // The "on*" handlers are attached with dojo.connect().
  103. //
  104. // If the value corresponding to the ID key is a function and not a
  105. // list, it's treated as though it was the value of "found".
  106. var tmpObj = {};
  107. forIn(behaviorObj, this, function(behavior, name) {
  108. var tBehavior = arrIn(this._behaviors, name);
  109. if (typeof tBehavior["id"] != "number") {
  110. tBehavior.id = _inc++;
  111. }
  112. var cversion = [];
  113. tBehavior.push(cversion);
  114. if ((dojo.isString(behavior))
  115. || (dojo.isFunction(behavior))) {
  116. behavior = {
  117. found : behavior
  118. };
  119. }
  120. forIn(behavior, function(rule, ruleName) {
  121. arrIn(cversion, ruleName).push(rule);
  122. });
  123. });
  124. }
  125. var _applyToNode = function(node, action, ruleSetName) {
  126. if (dojo.isString(action)) {
  127. if (ruleSetName == "found") {
  128. dojo.publish(action, [node]);
  129. } else {
  130. dojo.connect(node, ruleSetName, function() {
  131. dojo.publish(action, arguments);
  132. });
  133. }
  134. } else if (dojo.isFunction(action)) {
  135. if (ruleSetName == "found") {
  136. action(node);
  137. } else {
  138. dojo.connect(node, ruleSetName, action);
  139. }
  140. }
  141. }
  142. this.apply = function() {
  143. // summary:
  144. // applies all currently registered behaviors to the document,
  145. // taking care to ensure that only incremental updates are made
  146. // since the last time add() or apply() were called. If new
  147. // matching nodes have been added, all rules in a behavior will be
  148. // applied to that node. For previously matched nodes, only
  149. // behaviors which have been added since the last call to apply()
  150. // will be added to the nodes.
  151. forIn(this._behaviors, function(tBehavior, id) {
  152. dojo.query(id).forEach(function(elem) {
  153. var runFrom = 0;
  154. var bid = "_dj_behavior_" + tBehavior.id;
  155. if (typeof elem[bid] == "number") {
  156. runFrom = elem[bid];
  157. // console.debug(bid, runFrom);
  158. if (runFrom == (tBehavior.length)) {
  159. return;
  160. }
  161. }
  162. // run through the versions, applying newer rules at
  163. // each step
  164. for (var x = runFrom, tver; tver = tBehavior[x]; x++) {
  165. // console.debug(tver);
  166. forIn(tver, function(ruleSet, ruleSetName) {
  167. if (dojo.isArray(ruleSet)) {
  168. dojo.forEach(ruleSet, function(
  169. action) {
  170. _applyToNode(elem,
  171. action,
  172. ruleSetName);
  173. });
  174. }
  175. });
  176. }
  177. // ensure that re-application only adds new rules to
  178. // the node
  179. elem[bid] = tBehavior.length;
  180. });
  181. });
  182. }
  183. }
  184. dojo.addOnLoad(dojo.behavior, "apply");
  185. }