if(typeof(customElements) !== "undefined") { try { class X_Properties extends HTMLElement {} class X_Property extends HTMLElement {} customElements.define('x-properties', X_Properties, { extends: 'div' }); customElements.define('x-property', X_Property, { extends: 'div' }); } catch(error) { console.warn("failed to define costum elements"); } } /* T = value type */ interface SettingsKey { key: string; fallback_keys?: string | string[]; fallback_imports?: {[key: string]:(value: string) => T}; description?: string; default_value?: T; } class SettingsBase { protected static readonly UPDATE_DIRECT: boolean = true; protected static transformStO?(input?: string, _default?: T, default_type?: string) : T { default_type = default_type || typeof _default; if (typeof input === "undefined") return _default; if (default_type === "string") return input as any; else if (default_type === "number") return parseInt(input) as any; else if (default_type === "boolean") return (input == "1" || input == "true") as any; else if (default_type === "undefined") return input as any; return JSON.parse(input) as any; } protected static transformOtS?(input: T) : string { if (typeof input === "string") return input as string; else if (typeof input === "number") return input.toString(); else if (typeof input === "boolean") return input ? "1" : "0"; else if (typeof input === "undefined") return undefined; return JSON.stringify(input); } protected static resolveKey(key: SettingsKey, _default: T, resolver: (key: string) => string | boolean, default_type?: string) : T { let value = resolver(key.key); if(!value) { /* trying fallbacks */ for(const fallback of key.fallback_keys || []) { value = resolver(fallback); if(typeof(value) === "string") { /* fallback key succeeded */ const importer = (key.fallback_imports || {})[fallback]; if(importer) return importer(value); break; } } } if(typeof(value) !== 'string') return _default; return SettingsBase.transformStO(value as string, _default, default_type); } protected static keyify(key: string | SettingsKey) : SettingsKey { if(typeof(key) === "string") return {key: key}; if(typeof(key) === "object" && key.key) return key; throw "key is not a key"; } } class StaticSettings extends SettingsBase { private static _instance: StaticSettings; static get instance() : StaticSettings { if(!this._instance) this._instance = new StaticSettings(true); return this._instance; } protected _handle: StaticSettings; protected _staticPropsTag: JQuery; protected constructor(_reserved = undefined) { super(); if(_reserved && !StaticSettings._instance) { this._staticPropsTag = $("#properties"); this.initializeStatic(); } else { this._handle = StaticSettings.instance; } } private initializeStatic() { let search; if(window.opener && window.opener !== window) { search = new URL(window.location.href).search; } else { search = location.search; } search.substr(1).split("&").forEach(part => { let item = part.split("="); $("") .attr("key", item[0]) .attr("value", item[1]) .appendTo(this._staticPropsTag); }); } static?(key: string | SettingsKey, _default?: T, default_type?: string) : T { if(this._handle) return this._handle.static(key, _default, default_type); key = StaticSettings.keyify(key); return StaticSettings.resolveKey(key, _default, key => { let result = this._staticPropsTag.find("[key='" + key + "']"); if(result.length > 0) return decodeURIComponent(result.last().attr('value')); return false; }, default_type); } deleteStatic(key: string | SettingsKey) { if(this._handle) { this._handle.deleteStatic(key); return; } key = StaticSettings.keyify(key); let result = this._staticPropsTag.find("[key='" + key.key + "']"); if(result.length != 0) result.detach(); } } class Settings extends StaticSettings { static readonly KEY_DISABLE_CONTEXT_MENU: SettingsKey = { key: 'disableContextMenu', description: 'Disable the context menu for the channel tree which allows to debug the DOM easier' }; static readonly KEY_DISABLE_UNLOAD_DIALOG: SettingsKey = { key: 'disableUnloadDialog', description: 'Disables the unload popup on side closing' }; static readonly KEY_DISABLE_VOICE: SettingsKey = { key: 'disableVoice', description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore' }; static readonly KEY_DISABLE_MULTI_SESSION: SettingsKey = { key: 'disableMultiSession', }; static readonly KEY_LOAD_DUMMY_ERROR: SettingsKey = { key: 'dummy_load_error', description: 'Triggers a loading error at the end of the loading process.' }; /* Control bar */ static readonly KEY_CONTROL_MUTE_INPUT: SettingsKey = { key: 'mute_input' }; static readonly KEY_CONTROL_MUTE_OUTPUT: SettingsKey = { key: 'mute_output' }; static readonly KEY_CONTROL_SHOW_QUERIES: SettingsKey = { key: 'show_server_queries' }; static readonly KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL: SettingsKey = { key: 'channel_subscribe_all' }; /* Connect parameters */ static readonly KEY_FLAG_CONNECT_DEFAULT: SettingsKey = { key: 'connect_default' }; static readonly KEY_CONNECT_ADDRESS: SettingsKey = { key: 'connect_address' }; static readonly KEY_CONNECT_PROFILE: SettingsKey = { key: 'connect_profile' }; static readonly KEY_CONNECT_USERNAME: SettingsKey = { key: 'connect_username' }; static readonly KEY_CONNECT_PASSWORD: SettingsKey = { key: 'connect_password' }; static readonly KEY_FLAG_CONNECT_PASSWORD: SettingsKey = { key: 'connect_password_hashed' }; static readonly KEY_CERTIFICATE_CALLBACK: SettingsKey = { key: 'certificate_callback' }; /* sounds */ static readonly KEY_SOUND_MASTER: SettingsKey = { key: 'audio_master_volume' }; static readonly KEY_SOUND_MASTER_SOUNDS: SettingsKey = { key: 'audio_master_volume_sounds' }; static readonly FN_SERVER_CHANNEL_SUBSCRIBE_MODE: (channel: ChannelEntry) => SettingsKey = channel => { return { key: 'channel_subscribe_mode_' + channel.getChannelId() } }; static readonly FN_PROFILE_RECORD: (name: string) => SettingsKey = name => { return { key: 'profile_record' + name } }; static readonly KEYS = (() => { const result = []; for(const key in Settings) { if(!key.toUpperCase().startsWith("KEY_")) continue; if(key.toUpperCase() == "KEYS") continue; result.push(key); } return result; })(); private cacheGlobal = {}; private saveWorker: NodeJS.Timer; private updated: boolean = false; constructor() { super(); this.cacheGlobal = JSON.parse(localStorage.getItem("settings.global")); if(!this.cacheGlobal) this.cacheGlobal = {}; this.saveWorker = setInterval(() => { if(this.updated) this.save(); }, 5 * 1000); } static_global?(key: string | SettingsKey, _default?: T) : T { const default_object = { seed: Math.random() } as any; let _static = this.static(key, default_object, typeof _default); if(_static !== default_object) return StaticSettings.transformStO(_static, _default); return this.global(key, _default); } global?(key: string | SettingsKey, _default?: T) : T { return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheGlobal[key]); } changeGlobal(key: string | SettingsKey, value?: T){ key = Settings.keyify(key); if(this.cacheGlobal[key.key] == value) return; this.updated = true; this.cacheGlobal[key.key] = StaticSettings.transformOtS(value); if(Settings.UPDATE_DIRECT) this.save(); } save() { this.updated = false; let global = JSON.stringify(this.cacheGlobal); localStorage.setItem("settings.global", global); if(localStorage.save) localStorage.save(); } } class ServerSettings extends SettingsBase { private cacheServer = {}; private currentServer: ServerEntry; private _server_save_worker: NodeJS.Timer; private _server_settings_updated: boolean = false; constructor() { super(); this._server_save_worker = setInterval(() => { if(this._server_settings_updated) this.save(); }, 5 * 1000); } server?(key: string | SettingsKey, _default?: T) : T { return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]); } changeServer(key: string | SettingsKey, value?: T) { key = Settings.keyify(key); if(this.cacheServer[key.key] == value) return; this._server_settings_updated = true; this.cacheServer[key.key] = StaticSettings.transformOtS(value); if(Settings.UPDATE_DIRECT) this.save(); } setServer(server: ServerEntry) { if(this.currentServer) { this.save(); this.cacheServer = {}; this.currentServer = undefined; } this.currentServer = server; if(this.currentServer) { let serverId = this.currentServer.properties.virtualserver_unique_identifier; this.cacheServer = JSON.parse(localStorage.getItem("settings.server_" + serverId)); if(!this.cacheServer) this.cacheServer = {}; } } save() { this._server_settings_updated = false; if(this.currentServer) { let serverId = this.currentServer.properties.virtualserver_unique_identifier; let server = JSON.stringify(this.cacheServer); localStorage.setItem("settings.server_" + serverId, server); if(localStorage.save) localStorage.save(); } } }