2019-10-26 00:26:37 +02:00
|
|
|
const ls = {
|
2016-12-28 02:14:31 +01:00
|
|
|
// parse JSON without throwing an error.
|
|
|
|
parseJSON: function (str) {
|
|
|
|
try {
|
|
|
|
return JSON.parse(str);
|
|
|
|
} catch (err) {
|
2018-03-13 13:04:16 +01:00
|
|
|
return;
|
2016-12-28 02:14:31 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// check if the datestamp is from before now and if so return true.
|
|
|
|
isExpired: function (stamp) {
|
|
|
|
return new Date(stamp) < new Date();
|
|
|
|
},
|
|
|
|
|
|
|
|
// return the localStorage key that is bound to a version of a key.
|
|
|
|
formGetter: function (version, name) {
|
|
|
|
return "ls__" + version + "__" + name;
|
|
|
|
},
|
|
|
|
|
|
|
|
// create a formData object to put in the data, a signature that it was
|
|
|
|
// created with this library, and when it expires (if ever).
|
|
|
|
formData: function (data, expires) {
|
|
|
|
return {
|
|
|
|
data: data,
|
|
|
|
__valid: true,
|
|
|
|
expires: new Date().getTime() + expires,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
getData: function (version, name) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const key = this.formGetter(version, name);
|
|
|
|
let data = localStorage.getItem(key);
|
2016-12-28 02:14:31 +01:00
|
|
|
data = ls.parseJSON(data);
|
|
|
|
|
|
|
|
if (data) {
|
|
|
|
if (data.__valid) {
|
|
|
|
// JSON forms of data with `Infinity` turns into `null`,
|
|
|
|
// so if null then it hasn't expired since nothing was specified.
|
|
|
|
if (!ls.isExpired(data.expires) || data.expires === null) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// set the wrapped version of the data into localStorage.
|
|
|
|
setData: function (version, name, data, expires) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const key = this.formGetter(version, name);
|
|
|
|
const val = this.formData(data, expires);
|
2016-12-28 02:14:31 +01:00
|
|
|
|
|
|
|
localStorage.setItem(key, JSON.stringify(val));
|
|
|
|
},
|
|
|
|
|
|
|
|
// remove the key from localStorage and from memory.
|
|
|
|
removeData: function (version, name) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const key = this.formGetter(version, name);
|
2016-12-28 02:14:31 +01:00
|
|
|
|
|
|
|
localStorage.removeItem(key);
|
|
|
|
},
|
|
|
|
|
2017-04-26 12:35:56 +02:00
|
|
|
// Remove keys which match a regex.
|
|
|
|
removeDataRegex: function (version, regex) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const key_regex = new RegExp(this.formGetter(version, regex));
|
2020-07-02 01:45:54 +02:00
|
|
|
const keys = Object.keys(localStorage).filter((key) => key_regex.test(key));
|
2017-04-26 12:35:56 +02:00
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
keys.forEach((key) => {
|
2017-04-26 12:35:56 +02:00
|
|
|
localStorage.removeItem(key);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-12-28 02:14:31 +01:00
|
|
|
// migrate from an older version of a data src to a newer one with a
|
|
|
|
// specified callback function.
|
|
|
|
migrate: function (name, v1, v2, callback) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const old = this.getData(v1, name);
|
2016-12-28 02:14:31 +01:00
|
|
|
this.removeData(v1, name);
|
|
|
|
|
|
|
|
if (old && old.__valid) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const data = callback(old.data);
|
2016-12-28 02:14:31 +01:00
|
|
|
this.setData(v2, name, data, Infinity);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// return a new function instance that has instance-scoped variables.
|
2019-11-02 00:06:25 +01:00
|
|
|
const localstorage = function () {
|
|
|
|
const _data = {
|
2016-12-28 02:14:31 +01:00
|
|
|
VERSION: 1,
|
|
|
|
expires: Infinity,
|
|
|
|
expiresIsGlobal: false,
|
|
|
|
};
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const prototype = {
|
2016-12-28 02:14:31 +01:00
|
|
|
// `expires` should be a Number that represents the number of ms from
|
|
|
|
// now that this should expire in.
|
|
|
|
// this allows for it to either be set only once or permanently.
|
|
|
|
setExpiry: function (expires, isGlobal) {
|
|
|
|
_data.expires = expires;
|
|
|
|
_data.expiresIsGlobal = isGlobal || false;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
get: function (name) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const data = ls.getData(_data.VERSION, name);
|
2016-12-28 02:14:31 +01:00
|
|
|
|
|
|
|
if (data) {
|
|
|
|
return data.data;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
set: function (name, data) {
|
|
|
|
if (typeof _data.VERSION !== "undefined") {
|
|
|
|
ls.setData(_data.VERSION, name, data, _data.expires);
|
|
|
|
|
|
|
|
// if the expires attribute was not set as a global, then
|
|
|
|
// make sure to return it back to Infinity to not impose
|
|
|
|
// constraints on the next key.
|
|
|
|
if (!_data.expiresIsGlobal) {
|
|
|
|
_data.expires = Infinity;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
// remove a key with a given version.
|
|
|
|
remove: function (name) {
|
|
|
|
ls.removeData(_data.VERSION, name);
|
2017-04-26 12:35:56 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
// Remove keys which match the pattern given by name.
|
|
|
|
removeRegex: function (name) {
|
|
|
|
ls.removeDataRegex(_data.VERSION, name);
|
2016-12-28 02:14:31 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
migrate: function (name, v1, v2, callback) {
|
|
|
|
return ls.migrate(name, v1, v2, callback);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// set a new master version for the LocalStorage instance.
|
|
|
|
Object.defineProperty(prototype, "version", {
|
|
|
|
get: function () {
|
|
|
|
return _data.VERSION;
|
|
|
|
},
|
|
|
|
set: function (version) {
|
|
|
|
_data.VERSION = version;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return prototype;
|
|
|
|
};
|
2014-01-16 16:42:30 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let warned_of_localstorage = false;
|
2014-01-16 16:42:30 +01:00
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
localstorage.supported = function supports_localstorage() {
|
2014-01-16 16:42:30 +01:00
|
|
|
try {
|
2020-05-27 00:50:02 +02:00
|
|
|
return window.localStorage !== undefined && window.localStorage !== null;
|
2014-01-16 16:42:30 +01:00
|
|
|
} catch (e) {
|
|
|
|
if (!warned_of_localstorage) {
|
|
|
|
blueslip.error("Client browser does not support local storage, will lose socket message on reload");
|
|
|
|
warned_of_localstorage = true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
module.exports = localstorage;
|
2018-05-28 08:04:36 +02:00
|
|
|
window.localstorage = localstorage;
|