NodeList-data.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. define([
  2. "./_base/kernel", "./query", "./_base/lang", "./_base/array", "./dom-attr"
  3. ], function(dojo, query, lang, array, attr){
  4. // module:
  5. // dojo/NodeList-data
  6. /*=====
  7. return function(){
  8. // summary:
  9. // Adds data() and removeData() methods to NodeList, and returns NodeList constructor.
  10. };
  11. =====*/
  12. var NodeList = query.NodeList;
  13. var dataCache = {}, x = 0, dataattr = "data-dojo-dataid",
  14. dopid = function(node){
  15. // summary:
  16. // Return a uniqueish ID for the passed node reference
  17. var pid = attr.get(node, dataattr);
  18. if(!pid){
  19. pid = "pid" + (x++);
  20. attr.set(node, dataattr, pid);
  21. }
  22. return pid;
  23. }
  24. ;
  25. //>>excludeStart("debugging", true);
  26. // An alias to the private dataCache for NodeList-data. NEVER USE THIS!
  27. // This private is only exposed for the benefit of unit testing, and is
  28. // removed during the build process.
  29. NodeList._nodeDataCache = dojo._nodeDataCache = dataCache;
  30. //>>excludeEnd("debugging");
  31. var dodata = dojo._nodeData = function(node, key, value){
  32. // summary:
  33. // Private helper for dojo/NodeList.data for single node data access. Refer to NodeList.data
  34. // documentation for more information.
  35. //
  36. // node: String|DomNode
  37. // The node to associate data with
  38. //
  39. // key: Object|String?
  40. // If an object, act as a setter and iterate over said object setting data items as defined.
  41. // If a string, and `value` present, set the data for defined `key` to `value`
  42. // If a string, and `value` absent, act as a getter, returning the data associated with said `key`
  43. //
  44. // value: Anything?
  45. // The value to set for said `key`, provided `key` is a string (and not an object)
  46. //
  47. var pid = dopid(node), r;
  48. if(!dataCache[pid]){ dataCache[pid] = {}; }
  49. // API discrepency: calling with only a node returns the whole object. $.data throws
  50. if(arguments.length == 1){ return dataCache[pid]; }
  51. if(typeof key == "string"){
  52. // either getter or setter, based on `value` presence
  53. if(arguments.length > 2){
  54. dataCache[pid][key] = value;
  55. }else{
  56. r = dataCache[pid][key];
  57. }
  58. }else{
  59. // must be a setter, mix `value` into data hash
  60. // API discrepency: using object as setter works here
  61. r = lang.mixin(dataCache[pid], key);
  62. }
  63. return r; // Object|Anything|Nothing
  64. };
  65. var removeData = dojo._removeNodeData = function(node, key){
  66. // summary:
  67. // Remove some data from this node
  68. // node: String|DomNode
  69. // The node reference to remove data from
  70. // key: String?
  71. // If omitted, remove all data in this dataset.
  72. // If passed, remove only the passed `key` in the associated dataset
  73. var pid = dopid(node);
  74. if(dataCache[pid]){
  75. if(key){
  76. delete dataCache[pid][key];
  77. }else{
  78. delete dataCache[pid];
  79. }
  80. }
  81. };
  82. NodeList._gcNodeData = dojo._gcNodeData = function(){
  83. // summary:
  84. // super expensive: GC all data in the data for nodes that no longer exist in the dom.
  85. // description:
  86. // super expensive: GC all data in the data for nodes that no longer exist in the dom.
  87. // MUCH safer to do this yourself, manually, on a per-node basis (via `NodeList.removeData()`)
  88. // provided as a stop-gap for exceptionally large/complex applications with constantly changing
  89. // content regions (eg: a dijit/layout/ContentPane with replacing data)
  90. // There is NO automatic GC going on. If you dojo.destroy() a node, you should _removeNodeData
  91. // prior to destruction.
  92. var livePids = query("[" + dataattr + "]").map(dopid);
  93. for(var i in dataCache){
  94. if(array.indexOf(livePids, i) < 0){ delete dataCache[i]; }
  95. }
  96. };
  97. // make nodeData and removeNodeData public on dojo/NodeList:
  98. lang.extend(NodeList, {
  99. data: NodeList._adaptWithCondition(dodata, function(a){
  100. return a.length === 0 || a.length == 1 && (typeof a[0] == "string");
  101. }),
  102. removeData: NodeList._adaptAsForEach(removeData)
  103. });
  104. /*=====
  105. lang.extend(NodeList, {
  106. data: function(key, value){
  107. // summary:
  108. // stash or get some arbitrary data on/from these nodes.
  109. //
  110. // description:
  111. // Stash or get some arbitrary data on/from these nodes. This private _data function is
  112. // exposed publicly on `dojo/NodeList`, eg: as the result of a `dojo/query` call.
  113. // DIFFERS from jQuery.data in that when used as a getter, the entire list is ALWAYS
  114. // returned. EVEN WHEN THE LIST IS length == 1.
  115. //
  116. // A single-node version of this function is provided as `dojo._nodeData`, which follows
  117. // the same signature, though expects a String ID or DomNode reference in the first
  118. // position, before key/value arguments.
  119. //
  120. // node: String|DomNode
  121. // The node to associate data with
  122. //
  123. // key: Object|String?
  124. // If an object, act as a setter and iterate over said object setting data items as defined.
  125. // If a string, and `value` present, set the data for defined `key` to `value`
  126. // If a string, and `value` absent, act as a getter, returning the data associated with said `key`
  127. //
  128. // value: Anything?
  129. // The value to set for said `key`, provided `key` is a string (and not an object)
  130. //
  131. // example:
  132. // Set a key `bar` to some data, then retrieve it.
  133. // | require(["dojo/query", "dojo/NodeList-data"], function(query){
  134. // | query(".foo").data("bar", "touched");
  135. // | var touched = query(".foo").data("bar");
  136. // | if(touched[0] == "touched"){ alert('win'); }
  137. // | });
  138. //
  139. // example:
  140. // Get all the data items for a given node.
  141. // | require(["dojo/query", "dojo/NodeList-data"], function(query){
  142. // | var list = query(".foo").data();
  143. // | var first = list[0];
  144. // | });
  145. //
  146. // example:
  147. // Set the data to a complex hash. Overwrites existing keys with new value
  148. // | require(["dojo/query", "dojo/NodeList-data"], function(query){
  149. // | query(".foo").data({ bar:"baz", foo:"bar" });
  150. // Then get some random key:
  151. // | query(".foo").data("foo"); // returns [`bar`]
  152. // | });
  153. //
  154. // returns: Object|Anything|Nothing
  155. // When used as a setter via `dojo/NodeList`, a NodeList instance is returned
  156. // for further chaining. When used as a getter via `dojo/NodeList` an ARRAY
  157. // of items is returned. The items in the array correspond to the elements
  158. // in the original list. This is true even when the list length is 1, eg:
  159. // when looking up a node by ID (#foo)
  160. },
  161. removeData: function(key){
  162. // summary:
  163. // Remove the data associated with these nodes.
  164. // key: String?
  165. // If omitted, clean all data for this node.
  166. // If passed, remove the data item found at `key`
  167. }
  168. });
  169. =====*/
  170. // TODO: this is the basic implementation of adaptWithConditionAndWhenMappedConsiderLength, for lack of a better API name
  171. // it conflicts with the the `dojo/NodeList` way: always always return an arrayLike thinger. Consider for 2.0:
  172. //
  173. // NodeList.prototype.data = function(key, value){
  174. // var a = arguments, r;
  175. // if(a.length === 0 || a.length == 1 && (typeof a[0] == "string")){
  176. // r = this.map(function(node){
  177. // return d._data(node, key);
  178. // });
  179. // if(r.length == 1){ r = r[0]; } // the offending line, and the diff on adaptWithCondition
  180. // }else{
  181. // r = this.forEach(function(node){
  182. // d._data(node, key, value);
  183. // });
  184. // }
  185. // return r; // NodeList|Array|SingleItem
  186. // };
  187. return NodeList;
  188. });