1 /**
  2  * @fileOverview dom-data
  3  * @author lifesinger@gmail.com,yiminghe@gmail.com
  4  */
  5 KISSY.add('dom/data', function (S, DOM, undefined) {
  6 
  7     var win = S.Env.host,
  8         EXPANDO = '__ks_data_' + S.now(), // 让每一份 kissy 的 expando 都不同
  9         dataCache = { }, // 存储 node 节点的 data
 10         winDataCache = { };    // 避免污染全局
 11 
 12 
 13     // The following elements throw uncatchable exceptions if you
 14     // attempt to add expando properties to them.
 15     var noData = {
 16     };
 17     noData['applet'] = 1;
 18     noData['object'] = 1;
 19     noData['embed'] = 1;
 20 
 21     var commonOps = {
 22         hasData:function (cache, name) {
 23             if (cache) {
 24                 if (name !== undefined) {
 25                     if (name in cache) {
 26                         return true;
 27                     }
 28                 } else if (!S.isEmptyObject(cache)) {
 29                     return true;
 30                 }
 31             }
 32             return false;
 33         }
 34     };
 35 
 36     var objectOps = {
 37         hasData:function (ob, name) {
 38             // 只判断当前窗口,iframe 窗口内数据直接放入全局变量
 39             if (ob == win) {
 40                 return objectOps.hasData(winDataCache, name);
 41             }
 42             // 直接建立在对象内
 43             var thisCache = ob[EXPANDO];
 44             return commonOps.hasData(thisCache, name);
 45         },
 46 
 47         data:function (ob, name, value) {
 48             if (ob == win) {
 49                 return objectOps.data(winDataCache, name, value);
 50             }
 51             var cache = ob[EXPANDO];
 52             if (value !== undefined) {
 53                 cache = ob[EXPANDO] = ob[EXPANDO] || {};
 54                 cache[name] = value;
 55             } else {
 56                 if (name !== undefined) {
 57                     return cache && cache[name];
 58                 } else {
 59                     cache = ob[EXPANDO] = ob[EXPANDO] || {};
 60                     return cache;
 61                 }
 62             }
 63         },
 64         removeData:function (ob, name) {
 65             if (ob == win) {
 66                 return objectOps.removeData(winDataCache, name);
 67             }
 68             var cache = ob[EXPANDO];
 69             if (name !== undefined) {
 70                 delete cache[name];
 71                 if (S.isEmptyObject(cache)) {
 72                     objectOps.removeData(ob);
 73                 }
 74             } else {
 75                 try {
 76                     // ob maybe window in iframe
 77                     // ie will throw error
 78                     delete ob[EXPANDO];
 79                 } catch (e) {
 80                     ob[EXPANDO] = undefined;
 81                 }
 82             }
 83         }
 84     };
 85 
 86     var domOps = {
 87         hasData:function (elem, name) {
 88             var key = elem[EXPANDO];
 89             if (!key) {
 90                 return false;
 91             }
 92             var thisCache = dataCache[key];
 93             return commonOps.hasData(thisCache, name);
 94         },
 95 
 96         data:function (elem, name, value) {
 97             if (noData[elem.nodeName.toLowerCase()]) {
 98                 return undefined;
 99             }
100             var key = elem[EXPANDO], cache;
101             if (!key) {
102                 // 根本不用附加属性
103                 if (name !== undefined &&
104                     value === undefined) {
105                     return undefined;
106                 }
107                 // 节点上关联键值也可以
108                 key = elem[EXPANDO] = S.guid();
109             }
110             cache = dataCache[key];
111             if (value !== undefined) {
112                 // 需要新建
113                 cache = dataCache[key] = dataCache[key] || {};
114                 cache[name] = value;
115             } else {
116                 if (name !== undefined) {
117                     return cache && cache[name];
118                 } else {
119                     // 需要新建
120                     cache = dataCache[key] = dataCache[key] || {};
121                     return cache;
122                 }
123             }
124         },
125 
126         removeData:function (elem, name) {
127             var key = elem[EXPANDO], cache;
128             if (!key) {
129                 return;
130             }
131             cache = dataCache[key];
132             if (name !== undefined) {
133                 delete cache[name];
134                 if (S.isEmptyObject(cache)) {
135                     domOps.removeData(elem);
136                 }
137             } else {
138                 delete dataCache[key];
139                 try {
140                     delete elem[EXPANDO];
141                 } catch (e) {
142                     elem[EXPANDO] = undefined;
143                     //S.log("delete expando error : ");
144                     //S.log(e);
145                 }
146                 if (elem.removeAttribute) {
147                     elem.removeAttribute(EXPANDO);
148                 }
149             }
150         }
151     };
152 
153 
154     S.mix(DOM,
155         /**
156          * @lends DOM
157          */
158         {
159 
160             __EXPANDO:EXPANDO,
161 
162             /**
163              * Determine whether an element has any data or specified data name associated with it.
164              * @param {HTMLElement[]|String|HTMLElement} selector Matched elements
165              * @param {String} [name] A string naming the piece of data to set.
166              * @returns {Boolean}
167              */
168             hasData:function (selector, name) {
169                 var ret = false,
170                     elems = DOM.query(selector);
171                 for (var i = 0; i < elems.length; i++) {
172                     var elem = elems[i];
173                     if (elem.nodeType) {
174                         ret = domOps.hasData(elem, name);
175                     } else {
176                         ret = objectOps.hasData(elem, name);
177                     }
178                     if (ret) {
179                         return ret;
180                     }
181                 }
182                 return ret;
183             },
184 
185             /**
186              * If name set and data unset Store arbitrary data associated with the specified element. Returns undefined.
187              * or
188              * If name set and data unset returns value at named data store for the element
189              * or
190              * If name unset and data unset returns the full data store for the element.
191              * @param {HTMLElement[]|String|HTMLElement} selector Matched elements
192              * @param {String} [name] A string naming the piece of data to set.
193              * @param [data] The new data value.
194              * @returns {Object|undefined}
195              */
196             data:function (selector, name, data) {
197 
198                 var elems = DOM.query(selector), elem = elems[0];
199 
200                 // supports hash
201                 if (S.isPlainObject(name)) {
202                     for (var k in name) {
203                         DOM.data(elems, k, name[k]);
204                     }
205                     return undefined;
206                 }
207 
208                 // getter
209                 if (data === undefined) {
210                     if (elem) {
211                         if (elem.nodeType) {
212                             return domOps.data(elem, name, data);
213                         } else {
214                             return objectOps.data(elem, name, data);
215                         }
216                     }
217                 }
218                 // setter
219                 else {
220                     for (var i = elems.length - 1; i >= 0; i--) {
221                         elem = elems[i];
222                         if (elem.nodeType) {
223                             domOps.data(elem, name, data);
224                         } else {
225                             objectOps.data(elem, name, data);
226                         }
227                     }
228                 }
229                 return undefined;
230             },
231 
232             /**
233              * Remove a previously-stored piece of data from matched elements.
234              * or
235              * Remove all data from matched elements if name unset.
236              * @param {HTMLElement[]|String|HTMLElement} selector Matched elements
237              * @param {String} [name] A string naming the piece of data to delete.
238              */
239             removeData:function (selector, name) {
240                 var els = DOM.query(selector), elem, i;
241                 for (i = els.length - 1; i >= 0; i--) {
242                     elem = els[i];
243                     if (elem.nodeType) {
244                         domOps.removeData(elem, name);
245                     } else {
246                         objectOps.removeData(elem, name);
247                     }
248                 }
249             }
250         });
251 
252     return DOM;
253 
254 }, {
255     requires:["./base"]
256 });
257 /**
258  * 承玉:2011-05-31
259  *  - 分层 ,节点和普通对象分开处理
260  **/