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);
}
};
});