UNPKG

@jsxc/jsxc

Version:

Real-time XMPP chat application with video calls, file transfer and encrypted communication

359 lines (274 loc) 8.95 kB
import Log from './util/Log'; import IStorage from './Storage.interface'; import Options from './Options'; const PREFIX = 'jsxc2'; const SEP = ':'; const IGNORE_KEY = ['rid']; export default class Storage implements IStorage { public static storageNotConform: boolean = false; public static tested: boolean = false; private hooks: any = {}; private static backend; public static toSNC: number; public static clear(name?: string) { Storage.getKeysWithPrefix(name).forEach(key => Storage.backend.removeItem(key)); } public static getKeysWithPrefix(name?: string): string[] { let prefix = ''; if (!name.startsWith(PREFIX)) { prefix = PREFIX + SEP; } if (name) { prefix = prefix + name + SEP; } let keys = []; for (let key in Storage.backend) { if (!Storage.backend.hasOwnProperty(key)) { continue; } if (key.startsWith(prefix)) { keys.push(key); } } return keys; } constructor(private name: string = null) { if (!Storage.backend) { Storage.backend = Options.getDefault('storage'); } if (!Storage.tested) { Storage.tested = true; this.testStorage(); } window.addEventListener('storage', this.onStorageEvent, false); } public getName(): string { return this.name; } public generateKey(...args: string[]): string { let key = ''; args.forEach(function (arg) { if (key !== '') { key += SEP; } key += arg; }); return key; } private testStorage() { let randomNumber = Math.round(Math.random() * 1000000000) + ''; let key = this.getPrefix() + randomNumber; let timeout; let listenerFunction = function (ev) { if (ev.newValue === randomNumber) { clearTimeout(timeout); cleanup(); Storage.storageNotConform = true; } }; let cleanup = function () { window.removeEventListener('storage', listenerFunction, false); Storage.backend.removeItem(key); }; window.addEventListener('storage', listenerFunction, false); timeout = setTimeout(function () { cleanup(); }, 20); Storage.backend.setItem(key, randomNumber); } public getPrefix(): string { let prefix = PREFIX + SEP; if (this.name) { prefix += this.name + SEP; } return prefix; } public getBackend() { return Storage.backend; } public setItem<Data = any>(type: string, key: string, value: Data): void; public setItem<Data = any>(key: string, value: Data): void; public setItem(): void { let key: string; let value: any; if (arguments.length === 2) { key = arguments[0]; value = arguments[1]; } else if (arguments.length === 3) { key = arguments[0] + SEP + arguments[1]; value = arguments[2]; } //@REVIEW why do we just stringify objects? if (typeof value === 'object') { // exclude jquery objects, because otherwise safari will fail try { value = JSON.stringify(value, function (key, val) { if (!(val instanceof jQuery)) { return val; } }); } catch (err) { Log.warn('Could not stringify value', err); } } let oldValue = Storage.backend.getItem(this.getPrefix() + key); Storage.backend.setItem(this.getPrefix() + key, value); if (!Storage.storageNotConform && oldValue !== value) { this.onStorageEvent({ key: this.getPrefix() + key, oldValue, newValue: value, }); } } public getItem<Data = any>(type: string, key: string): Data; public getItem<Data = any>(key: string): Data; public getItem(): any { let key: string; if (arguments.length === 1) { key = arguments[0]; } else if (arguments.length === 2) { key = arguments[0] + SEP + arguments[1]; } key = this.getPrefix() + key; if (!Storage.backend.hasOwnProperty(key)) { return undefined; } let value = Storage.backend.getItem(key); return this.parseValue(value); } public removeItem(type, key): void; public removeItem(key): void; public removeItem(): void { let key; if (arguments.length === 1) { key = arguments[0]; } else if (arguments.length === 2) { key = arguments[0] + SEP + arguments[1]; } Storage.backend.removeItem(this.getPrefix() + key); } public updateItem(type: string, key: string, variable: string, value: any): void; public updateItem(key: string, variable: string, value: any): void; public updateItem(): void { let key: string; let variable: string; let value: any; if (arguments.length === 4 || (arguments.length === 3 && typeof variable === 'object')) { key = arguments[0] + SEP + arguments[1]; variable = arguments[2]; value = arguments[3]; } else { key = arguments[0]; variable = arguments[1]; value = arguments[2]; } let data = this.getItem(key) || {}; if (typeof variable === 'object') { $.each(variable, function (key, val) { if (typeof data[key] === 'undefined') { Log.debug(`Variable ${key} doesn't exist in ${variable}. It was created.`); } data[key] = val; }); } else { if (typeof data[variable] === 'undefined') { Log.debug('Variable ' + variable + " doesn't exist. It was created."); } data[variable] = value; } this.setItem(key, data); } public increment(key: string): void { let value = Number(this.getItem(key)); this.setItem(key, value + 1); } public removeElement(type, key, name): void; public removeElement(key, name): void; public removeElement(): void { let key; let name; if (arguments.length === 2) { key = arguments[0]; name = arguments[1]; } else if (arguments.length === 3) { key = arguments[0] + SEP + arguments[1]; name = arguments[2]; } let item = this.getItem(key); if ($.isArray(item)) { item = $.grep(item, function (e) { return e !== name; }); } else if (typeof item === 'object' && item !== null) { delete item[name]; } this.setItem(key, item); } public getItemsWithKeyPrefix(keyPrefix: string) { let prefix = this.getPrefix() + keyPrefix; let fullKeys = Storage.getKeysWithPrefix(prefix); let items = {}; for (const fullKey of fullKeys) { let key = fullKey.replace(new RegExp('^' + this.getPrefix()), ''); items[key] = this.getItem(key); } return items; } public registerHook(eventName: string, func: (newValue: any, oldValue: any, key: string) => void) { if (!this.hooks[eventName]) { this.hooks[eventName] = []; } this.hooks[eventName].push(func); } public removeHook(eventName: string, func?: (newValue: any, oldValue: any, key: string) => void) { let eventNameList = this.hooks[eventName] || []; if (typeof func === 'undefined') { eventNameList = []; } else if (eventNameList.indexOf(func) > -1) { eventNameList = $.grep(eventNameList, function (i) { return func !== i; }); } this.hooks[eventName] = eventNameList; } public removeAllHooks() { this.hooks = {}; } public destroy() { this.removeAllHooks(); window.removeEventListener('storage', this.onStorageEvent, false); } private onStorageEvent = (ev: any) => { let prefix = this.getPrefix(); if (!ev.key || ev.key.indexOf(prefix) !== 0) { return; } let key = ev.key.slice(prefix.length); if (IGNORE_KEY.indexOf(key) > -1) { return; } let hooks = this.hooks; let oldValue = this.parseValue(ev.oldValue); let newValue = this.parseValue(ev.newValue); let eventNames = Object.keys(hooks); eventNames.forEach(function (eventName) { if (eventName === '*' || key === eventName || key.indexOf(eventName + ':') === 0) { let eventNameHooks = hooks[eventName] || []; eventNameHooks.forEach(function (hook) { hook(newValue, oldValue, key); }); } }); }; private parseValue(value: string) { if (value === 'undefined') { return; } try { return JSON.parse(value); } catch (e) { return value; } } }