User:Erutuon/scripts/storageUtils.js
< User:Erutuon | scripts
Note – after saving, you may have to bypass your browser’s cache to see the changes.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- This script lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • redirects • your own
// {{documentation}}
// implicit dependencies : mediawiki.cookie,mw.storage
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true, sub:true */
/* global mw, $ */
(function StorageUtilsIIFE() {
// Lowest level wrapper that wraps `localStorage` or `$.cookie` and
// `$.removeCookie`.
var StorageWrapper = function() {
this.cookiePreferences = {
expires: 30, path: '/'
};
};
StorageWrapper.prototype = {
constructor: StorageWrapper,
localStorageAvailable: mw.storage.get("localStorageTest") !== false,
cookieAvailable: navigator.cookieEnabled,
get storageAvailable () {
return this.localStorageAvailable || this.cookieAvailable;
},
set: function(name, value) {
if (this.localStorageAvailable) {
localStorage[name] = value;
} else {
mw.cookie.set(name, value, {});
}
},
get: function(name) {
if (this.localStorageAvailable) {
return localStorage[name];
} else {
return mw.cookie.get(name);
}
},
remove: function(name) {
if (this.localStorageAvailable) {
delete localStorage[name];
} else {
return $.removeCookie(name);
}
},
};
var StorageByAddress = function (path, version) {
console.log(arguments);
if (typeof path !== "string")
throw new TypeError("Expected string");
// Call constructor for StorageWrapper object.
StorageWrapper.call(this);
this.path = path;
this.version = version || 1;
};
StorageByAddress.prototype = Object.create(StorageWrapper.prototype, {
constructor: { value: StorageByAddress },
itemAddressPrefix: {
get: function () {
return "enwikt/" + this.path + "?v=" + this.version;
},
},
makeAddress: {
value: function(type) {
return this.itemAddressPrefix + "&Type=" + type;
},
},
getByType: {
value: function(type) {
return StorageWrapper.prototype.get.call(this, this.makeAddress(type));
},
},
setByType: {
value: function(type, value) {
return StorageWrapper.prototype.set.call(this, this.makeAddress(type), value);
},
},
removeByType: {
value: function(type) {
return StorageWrapper.prototype.remove.call(this, this.makeAddress(type));
},
}
});
// Expirable, lazy object storage. Inherits from `StorageByAddress` and
// `StorageWrapper`, but has its own `get` and `remove` methods and a `set` method
// that does nothing.
var CacheableStorage = function(productName, getProductCallback, expireInDays, version) {
StorageByAddress.call(this, "CacheableStorage/" + productName, version);
this.expireInDays = expireInDays;
this.productName = productName;
this.getProductCallback = getProductCallback;
this._cachedData = null;
};
var secondsPerDay = 24 * 3600;
// callback function that must be bound using `Function.prototype.bind` so that
// `this` refers to the correct object
CacheableStorage.refreshDataHelper = function (data) {
this.setByType("Type", typeof data);
this.setByType("Expiration", new Date().toISOString());
if (typeof(data) != "string")
data = JSON.stringify(data);
this.setByType("Data", data);
};
CacheableStorage.prototype = Object.create(StorageByAddress.prototype, {
constructor: { value: CacheableStorage },
refreshDataHelper: {
get: function () {
if (!this._refreshDataHelper)
this._refreshDataHelper = CacheableStorage.refreshDataHelper.bind(this);
return this._refreshDataHelper;
},
},
refreshData: {
value: function() {
this.getProductCallback().then(this.refreshDataHelper);
},
},
get: {
value: function() {
if (this._cachedData)
return this._cachedData;
var data = this.getByType("Data");
var type = this.getByType("Type");
var expiration = this.getByType("Expiration");
if (data) {
if (type != "string") data = JSON.parse(data);
if ((new Date() - new Date(expiration)) / 1000 > this.expireInDays * secondsPerDay) {
this.refreshData();
return data;
} else {
this._cachedData = data;
return this._cachedData;
}
} else {
this.refreshData();
}
return null;
},
},
// just to override `StorageWrapper.protoype.set`
set: { value: function doesNothing() {}, },
remove: {
value: function () {
this.removeByType("Type");
this.removeByType("Expiration");
this.removeByType("Data");
this._cachedData = null;
}
},
// For compatibility with old version, which had a different name for
// the equivalent method:
GetItem: {
get: function() {
return this.get;
},
},
});
/*
* Not expirable. Inherits from `StorageByAddress` and `StorageWrapper`, but has its
* own `get`, `set`, and `remove` methods.
*/
var ObjectStorage = function(contextName, version) {
StorageByAddress.call(this, "CacheableStorage/" + contextName, version);
this.contextName = contextName;
this._cachedData = null;
};
ObjectStorage.prototype = Object.create(StorageByAddress.prototype, {
constructor: { value: ObjectStorage },
set: {
value: function(obj) {
var s = typeof(obj) == "string" ? obj : JSON.stringify(obj);
this.setByType("Data", s);
this.setByType("Type", typeof(obj));
this._cachedData = obj;
},
},
get: {
value: function() {
if (this._cachedData)
return this._cachedData;
var data = this.getByType("Data");
var type = this.getByType("Type");
if (data) {
if (type != "string")
data = JSON.parse(data);
this._cachedData = data;
return this._cachedData;
}
return null;
},
},
remove: {
value: function () {
this.removeByType("Data");
this.removeByType("Type");
this._cachedData = null;
},
},
// For compatibility with old version, which had different names for
// equivalent methods:
Get: {
get: function () {
return this.get;
},
},
Set: {
get: function () {
return this.set;
},
},
});
window.MyStorage = {
StorageWrapper: StorageWrapper,
StorageByAddress: StorageByAddress,
CacheableStorage: CacheableStorage,
ObjectStorage: ObjectStorage,
};
})();