Files
zulip/static/js/localstorage.js
Anders Kaseorg 6ec808b8df js: Add "use strict" directive to CommonJS files.
ES and TypeScript modules are strict by default and don’t need this
directive.  ESLint will remind us to add it to new CommonJS files and
remove it from ES and TypeScript modules.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-31 22:09:46 -07:00

179 lines
4.9 KiB
JavaScript

"use strict";
const ls = {
// parse JSON without throwing an error.
parseJSON(str) {
try {
return JSON.parse(str);
} catch (err) {
return;
}
},
// check if the datestamp is from before now and if so return true.
isExpired(stamp) {
return new Date(stamp) < new Date();
},
// return the localStorage key that is bound to a version of a key.
formGetter(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(data, expires) {
return {
data,
__valid: true,
expires: new Date().getTime() + expires,
};
},
getData(version, name) {
const key = this.formGetter(version, name);
let data = localStorage.getItem(key);
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(version, name, data, expires) {
const key = this.formGetter(version, name);
const val = this.formData(data, expires);
localStorage.setItem(key, JSON.stringify(val));
},
// remove the key from localStorage and from memory.
removeData(version, name) {
const key = this.formGetter(version, name);
localStorage.removeItem(key);
},
// Remove keys which match a regex.
removeDataRegex(version, regex) {
const key_regex = new RegExp(this.formGetter(version, regex));
const keys = Object.keys(localStorage).filter((key) => key_regex.test(key));
keys.forEach((key) => {
localStorage.removeItem(key);
});
},
// migrate from an older version of a data src to a newer one with a
// specified callback function.
migrate(name, v1, v2, callback) {
const old = this.getData(v1, name);
this.removeData(v1, name);
if (old && old.__valid) {
const data = callback(old.data);
this.setData(v2, name, data, Infinity);
return data;
}
},
};
// return a new function instance that has instance-scoped variables.
const localstorage = function () {
const _data = {
VERSION: 1,
expires: Infinity,
expiresIsGlobal: false,
};
const prototype = {
// `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(expires, isGlobal) {
_data.expires = expires;
_data.expiresIsGlobal = isGlobal || false;
return this;
},
get(name) {
const data = ls.getData(_data.VERSION, name);
if (data) {
return data.data;
}
},
set(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(name) {
ls.removeData(_data.VERSION, name);
},
// Remove keys which match the pattern given by name.
removeRegex(name) {
ls.removeDataRegex(_data.VERSION, name);
},
migrate(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() {
return _data.VERSION;
},
set(version) {
_data.VERSION = version;
},
});
return prototype;
};
let warned_of_localstorage = false;
localstorage.supported = function supports_localstorage() {
try {
return window.localStorage !== undefined && window.localStorage !== null;
} 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;
}
};
module.exports = localstorage;
window.localstorage = localstorage;