downthemall/lib/objectoverlay.ts
2019-08-20 16:41:37 +02:00

104 lines
2.3 KiB
TypeScript

"use strict";
// License: MIT
import { storage } from "./browser";
function toJSON(overlay: any, object: any) {
const result: any = {};
for (const key of Object.keys(overlay)) {
const val: any = overlay[key];
if (val !== object[val]) {
result[key] = val;
}
}
return result;
}
function isOverridden(overlay: any, object: any, name: string) {
return name in overlay && name in object && overlay[name] !== object[name];
}
class Handler {
private readonly base: any;
constructor(base: any) {
this.base = base;
}
"has"(target: any, name: string) {
if (name === "toJSON") {
return true;
}
if (name === "isOverridden") {
return true;
}
return name in target || name in this.base;
}
"get"(target: any, name: string) {
if (name === "toJSON") {
return toJSON.bind(null, target, this.base);
}
if (name === "isOverridden") {
return isOverridden.bind(null, target, this.base);
}
if (name === "reset") {
return () => {
Object.keys(target).forEach(k => delete target[k]);
};
}
if (name in target) {
return target[name];
}
if (name in this.base) {
return this.base[name];
}
return null;
}
getOwnPropertyDescriptor(target: any, name: string) {
let res = Object.getOwnPropertyDescriptor(target, name);
if (!res) {
res = Object.getOwnPropertyDescriptor(this.base, name);
}
if (res) {
res.enumerable = res.writable = res.configurable = true;
}
return res;
}
"set"(target: any, name: string, value: any) {
target[name] = value;
return true;
}
ownKeys(target: any) {
const result = Object.keys(target);
result.push(...Object.keys(this.base));
return Array.from(new Set(result));
}
}
export function overlay(top: object) {
return new Proxy(top, new Handler(this));
}
export async function loadOverlay(
storageKey: string, sync: boolean, defaults: object) {
const bottom = Object.freeze(defaults);
const top = await storage[sync ? "sync" : "local"].get(storageKey);
return overlay.call(bottom, top[storageKey] || {});
}
export interface Overlayable {
overlay: Function;
}
Object.defineProperty(Object.prototype, "overlay", {
value: overlay
});
Object.defineProperty(Object, "loadOverlay", {
value: loadOverlay
});