define(function() { var win = window, doc = document, decode = function(s) { // 参考jquery cookie的实现: https://github.com/carhartl/jquery-cookie/blob/master/jquery.cookie.js if (s.indexOf('"') === 0) { s = s.slice(1, - 1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); } try { return decodeURIComponent(s); } catch (e) { return null; } }, encode = encodeURIComponent, isSupportLocalStorage = (function() { try { var support = 'localStorage' in win && win['localStorage'] !== null, test = { k: 'test key', v: 'test value' }; if (support) { localStorage.setItem(test.k, test.v); support = test.v === localStorage.getItem(test.k); } return support; } catch (e) { return false; } }()), stringify = function(v) { if (!_.isString(v)) { v = JSON.stringify(v); } return encode(v); }, validateCookieName = function(name) { if (!_.isString(name) || name === '') { throw new TypeError('Cookie name must be a non-empty string!'); } }, // TODO: 不支持localStorage时换用cookie存储 // 现在在某些浏览器下可能存在cookie数量的限制 // 之后可能的优化是使用subcookie的方式: https://developer.yahoo.com/yui/cookie/#subcookies s = isSupportLocalStorage ? localStorage : { setItem: function(k, v, days) { validateCookieName(k); // 默认cookie中的结果缓存7天 days = days || 7; var expires = new Date(); expires.setDate(expires.getDate() + days); v = _m.utils.evaluate(v); if (_.isArray(v)) { v = v[0]; expires = new Date(parseInt(v[1], 10)); } k = stringify(k); v = stringify(v); // 高端浏览器中一般合并字符用+比用join('')更高效 // 参考: http://photo.weibo.com/2785671884/wbphotos/large/photo_id/3453950944633013?refer=weibofeedv5 doc.cookie = k + '=' + v + '; expires=' + expires.toGMTString(); }, getItem: function(k) { validateCookieName(k); k = stringify(k) + '='; var v = null, cookie = doc.cookie, start = cookie.indexOf(k); if (start > -1) { var end = cookie.indexOf(';', start); if (end === -1) { end = cookie.length; } v = decode(cookie.substring(start + k.length, end)); } return v; }, removeItem: function(k) { this.setItem(k, '', - 1); } }, prefix = '_BM:'; return { isSupportLocalStorage: isSupportLocalStorage, set: function(k, v, expires) { if (_.isNumber(expires)) { expires = +new Date() + expires; } s.setItem(prefix + k, JSON.stringify({ value: v, expires: expires })); }, get: function(k) { var v = JSON.parse(s.getItem(prefix + k)), expires; if (!v) { return; } expires = parseInt(v.expires, 10); v = v.value; if (expires) { if (+new Date() < expires) { return v; } this.remove(k); } return v; }, remove: function(k, v) { if (v) { try { this.set(k, _.difference(this.get(k), v)); } catch (e) {} } else { s.removeItem(prefix + k); } }, add: function(k, v, options) { var h = this.get(k), defaults = { type: 'set', limit: 10 }, opts = _.extend(defaults, options), type = opts.type, expires = opts.expires, limit = opts.limit; // TODO: 有潜在的bug, 比如之前存储的是array类型 if (!_.isArray(h)) { h = _.isUndefined(h) && [] || [h]; } if (type === 'set' && _.indexOf(h, v) !== -1) { return; } if (_.isNumber(limit) && limit > 0) { h = _.first(h, limit - 1); } h.unshift(v); this.set(k, h, expires); } }; });