lycabinet
Version:
A simple small JSON Object storage helper with good performance.
142 lines (126 loc) • 4.79 kB
text/typescript
/**
* Argument the robustness of the JSON data process.
* And preserve the atomicity when there is multi-tabs opened by user.
* Like delete the Item in Storage if it is vacant.
*/
import { LycabinetStatic } from "@/typings/lycabinet";
import { addStoreListener, arrayIndex, DEBUG, objectSupplement, removeArrayItem } from "../utils/util";
const ParticalToken = 'cabinetSyncTabs';
const TokenSeparator = '|';
export function addCheck(Lycabinet){
/**
* JSON analysis argumented.
*/
Lycabinet.mixin(function(cabinetIns){
cabinetIns._on("localLoaded", function(finalData, results){
let final = results.length? arrayIndex(results, -1): finalData;
// add pre-check for JSON Parse.
if(!final) final = '{}';
return final;
});
cabinetIns._on("localSaved", function(finalData, results){
let final = results.length? arrayIndex(results, -1): finalData;
return final;
});
});
/**
* Add a dirty flag.
*/
Lycabinet.mixin(function(cabinetIns){
// This is not that precious. Since user can directly modify the __storage Object without a record.
cabinetIns._on("setItem", function(){
this._dirty = true;
});
cabinetIns._on("saved", function(){
this._dirty = false;
})
});
/**
* Listening the storage event from other tabs(pages)
* Custom Events: `storageSync`
*/
var localContext: Array<LycabinetStatic> = [];
const cabinetSyncTab = (eve)=>{
let cabinetIns: LycabinetStatic;
let toMatchRoot = eve.key;
if(ParticalToken===eve.key){
toMatchRoot = eve.newValue.slice( - eve.newValue.lastIndexOf(TokenSeparator) );
};
for(let index=0; index<localContext.length; index++){
cabinetIns = localContext[index];
// Do not do redundant reload if current cabinet is shared by another.
// We think that in one page the cabinet has the same root is always shared, but there are troubles if
// the first instance is collected by GC.
if(cabinetIns.useLoadCache) continue;
// Reload. By default using deeepMerge mode.
if([cabinetIns.__root].indexOf(toMatchRoot) > -1){
DEBUG && console.log("[Lycabinet]: Synchronizing data from other tabs...");
// merge data using default options.
(cabinetIns.load as Function)({
onCloud: false,
concurrent: true,
deepMerge: true,
disableMerge: true,
}); // Considering of the latency on cloud, we only synchronize the data locally.
(cabinetIns._trigger as Function)("storageSync");
break;
}
}
};
addStoreListener( cabinetSyncTab );
/**
* Add auto tab synchronize listener options.
*/
Lycabinet.mixin(function(cabinetIns){
// save the localContext
localContext.push(cabinetIns);
// Remove the context.
cabinetIns._on("destroyed", ()=>{
removeArrayItem( localContext, cabinetIns);
});
// Add cache check token.
cabinetIns._on("loadFromCache", function(){
this.useLoadCache = true;
});
// add options for custom database which is not localStorage.
objectSupplement(cabinetIns.options, {
autoNotifyTabs: cabinetIns.options.localInterface.database!==window.localStorage,
});
cabinetIns._on("saved", function(onCloud, concurrent){
if(this.options.autoNotifyTabs){
const IgnoreLocal = onCloud && !concurrent;
if(IgnoreLocal){
return false;
}
this.notifyTabs();
}
});
});
/**
* If the database in configuration is not `LocalStorage` (eg Env `sessionStorage`)
* You should call this method to notify the other pages or set prop `autoNotifyTabs` to true.
*/
Lycabinet.prototype.notifyTabs = function(){
const randomToken = new Date().getTime();
// this will give other pages a notifycation.
const storage = window.localStorage;
try{
storage.setItem(ParticalToken, `${this.__root}${TokenSeparator}${randomToken}`);
} catch(err){
if(err instanceof DOMException && (
// everything except Firefox
err.code === 22 ||
// Firefox
err.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
err.name === 'QuotaExceededError' ||
// Firefox
err.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0)
)
console.error("[lycabinet]: Sync storage from tabs failed cause LocalStorage is not supportted!", err);
}
}
}