/** * @ignore * dom-data * @author lifesinger@gmail.com, yiminghe@gmail.com */ KISSY.add('dom/base/data', function (S, Dom, undefined) { var win = S.Env.host, EXPANDO = '_ks_data_' + S.now(), // 让每一份 kissy 的 expando 都不同 dataCache = { }, // 存储 node 节点的 data winDataCache = { }, // 避免污染全局 // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. noData = { }; noData['applet'] = 1; noData['object'] = 1; noData['embed'] = 1; var commonOps = { hasData: function (cache, name) { if (cache) { if (name !== undefined) { if (name in cache) { return true; } } else if (!S.isEmptyObject(cache)) { return true; } } return false; } }; var objectOps = { hasData: function (ob, name) { // 只判断当前窗口,iframe 窗口内数据直接放入全局变量 if (ob == win) { return objectOps.hasData(winDataCache, name); } // 直接建立在对象内 var thisCache = ob[EXPANDO]; return commonOps.hasData(thisCache, name); }, data: function (ob, name, value) { if (ob == win) { return objectOps.data(winDataCache, name, value); } var cache = ob[EXPANDO]; if (value !== undefined) { cache = ob[EXPANDO] = ob[EXPANDO] || {}; cache[name] = value; } else { if (name !== undefined) { return cache && cache[name]; } else { cache = ob[EXPANDO] = ob[EXPANDO] || {}; return cache; } } }, removeData: function (ob, name) { if (ob == win) { return objectOps.removeData(winDataCache, name); } var cache = ob[EXPANDO]; if (name !== undefined) { delete cache[name]; if (S.isEmptyObject(cache)) { objectOps.removeData(ob); } } else { try { // ob maybe window in iframe // ie will throw error delete ob[EXPANDO]; } catch (e) { ob[EXPANDO] = undefined; } } } }; var domOps = { hasData: function (elem, name) { var key = elem[EXPANDO]; if (!key) { return false; } var thisCache = dataCache[key]; return commonOps.hasData(thisCache, name); }, data: function (elem, name, value) { if (noData[elem.nodeName.toLowerCase()]) { return undefined; } var key = elem[EXPANDO], cache; if (!key) { // 根本不用附加属性 if (name !== undefined && value === undefined) { return undefined; } // 节点上关联键值也可以 key = elem[EXPANDO] = S.guid(); } cache = dataCache[key]; if (value !== undefined) { // 需要新建 cache = dataCache[key] = dataCache[key] || {}; cache[name] = value; } else { if (name !== undefined) { return cache && cache[name]; } else { // 需要新建 cache = dataCache[key] = dataCache[key] || {}; return cache; } } }, removeData: function (elem, name) { var key = elem[EXPANDO], cache; if (!key) { return; } cache = dataCache[key]; if (name !== undefined) { delete cache[name]; if (S.isEmptyObject(cache)) { domOps.removeData(elem); } } else { delete dataCache[key]; try { delete elem[EXPANDO]; } catch (e) { elem[EXPANDO] = undefined; } if (elem.removeAttribute) { elem.removeAttribute(EXPANDO); } } } }; S.mix(Dom, /** * @override KISSY.DOM * @class * @singleton */ { __EXPANDO: EXPANDO, /** * Determine whether an element has any data or specified data name associated with it. * @param {HTMLElement[]|String|HTMLElement} selector Matched elements * @param {String} [name] A string naming the piece of data to set. * @return {Boolean} */ hasData: function (selector, name) { var ret = false, elems = Dom.query(selector); for (var i = 0; i < elems.length; i++) { var elem = elems[i]; if (elem.nodeType) { ret = domOps.hasData(elem, name); } else { // window ret = objectOps.hasData(elem, name); } if (ret) { return ret; } } return ret; }, /** * If name set and data unset Store arbitrary data associated with the specified element. Returns undefined. * or * If name set and data unset returns value at named data store for the element * or * If name unset and data unset returns the full data store for the element. * @param {HTMLElement[]|String|HTMLElement} selector Matched elements * @param {String} [name] A string naming the piece of data to set. * @param [data] The new data value. * @return {Object|undefined} */ data: function (selector, name, data) { var elems = Dom.query(selector), elem = elems[0]; // supports hash if (S.isPlainObject(name)) { for (var k in name) { Dom.data(elems, k, name[k]); } return undefined; } // getter if (data === undefined) { if (elem) { if (elem.nodeType) { return domOps.data(elem, name); } else { // window return objectOps.data(elem, name); } } } // setter else { for (var i = elems.length - 1; i >= 0; i--) { elem = elems[i]; if (elem.nodeType) { domOps.data(elem, name, data); } else { // window objectOps.data(elem, name, data); } } } return undefined; }, /** * Remove a previously-stored piece of data from matched elements. * or * Remove all data from matched elements if name unset. * @param {HTMLElement[]|String|HTMLElement} selector Matched elements * @param {String} [name] A string naming the piece of data to delete. */ removeData: function (selector, name) { var els = Dom.query(selector), elem, i; for (i = els.length - 1; i >= 0; i--) { elem = els[i]; if (elem.nodeType) { domOps.removeData(elem, name); } else { // window objectOps.removeData(elem, name); } } } }); return Dom; }, { requires: ['./api'] }); /* yiminghe@gmail.com:2011-05-31 - 分层,节点和普通对象分开处理 */