declare module ItemsHoldr { /** * A mapping of ItemValue values to triggered callbacks. */ export interface ITriggers { [i: string]: Function; [i: number]: Function; } /** * A container of default values to pass to IItemValues, keyed by the * IItemValue keys.m */ export interface IItemValueDefaults { [i: string]: IItemValueSettings; } /** * Settings to initialize a new instance of the IItemValue interface. */ export interface IItemValueSettings { /** * An initial value to store. */ value?: any; /** * A default initial value to store, if value isn't provided. */ valueDefault?: any; /** * Whether the value should be stored in the IItemHoldr's localStorage. */ storeLocally?: boolean; /** * A mapping of values to callbacks that should be triggered when value * is equal to them. */ triggers?: ITriggers; /** * Whether an Element should be created and synced to the value. */ hasElement?: boolean; /** * An Element tag to use in creating the element, if hasElement is true. */ elementTag?: string; /** * A minimum value for the value to equal, if value is a number. */ minimum?: number; /** * A callback to call when the value reaches the minimum value. */ onMinimum?: Function; /** * A maximum value for the value to equal, if value is a number. */ maximum?: number; /** * A callback to call when the value reaches the maximum value. */ onMaximum?: Function; /** * A maximum number to modulo the value against, if value is a number. */ modularity?: number; /** * A callback to call when the value reaches modularity. */ onModular?: Function; /** * A Function to transform the value when it's being set. */ transformGet?: Function; /** * A Function to transform the value when it's being retrieved. */ transformSet?: Function; } /** * Storage container for a single IItemsHoldr value. The value may have triggers * assigned to value, modularity, and other triggers, as well as an HTML element. */ export interface IItemValue { /** * The container IItemsHoldr governing usage of this ItemsValue. */ ItemsHolder: IItemsHoldr; /** * The unique key identifying this ItemValue in the ItemsHoldr. */ key: string; /** * A default initial value to store, if value isn't provided. */ valueDefault: any; /** * Whether the value should be stored in the ItemHoldr's localStorage. */ storeLocally: boolean; /** * A mapping of values to callbacks that should be triggered when value * is equal to them. */ triggers: ITriggers; /** * An HTML element whose second child's textContent is always set to that of the element. */ element: HTMLElement; /** * Whether an Element should be created and synced to the value. */ hasElement: boolean; /** * An Element tag to use in creating the element, if hasElement is true. */ elementTag: string; /** * A maximum value for the value to equal, if value is a number. */ maximum: number; /** * A callback to call when the value reaches the maximum value. */ onMaximum: Function; /** * A minimum value for the value to equal, if value is a number. */ minimum: number; /** * A callback to call when the value reaches the minimum value. */ onMinimum: Function; /** * A maximum number to modulo the value against, if value is a number. */ modularity: number; /** * A callback to call when the value reaches modularity. */ onModular: Function; /** * A Function to transform the value when it's being set. */ transformGet?: Function; /** * A Function to transform the value when it's being retrieved. */ transformSet?: Function; /** * @returns The value being stored, with a transformGet applied if one exists. */ getValue(): any; /** * Sets the value being stored, with a is a transformSet applied if one exists. * Any attached triggers to the new value will be called. * * @param value The desired value to now store. */ setValue(value: any): void; /** * General update Function to be run whenever the internal value is changed. * It runs all the trigger, modular, etc. checks, updates the HTML element * if there is one, and updates localStorage if needed. */ update(): void; /** * Stores a ItemValue's value in localStorage under the prefix plus its key. * * @param overrideAutoSave Whether the policy on saving should be ignored * so saving happens regardless. By default, false. */ updateLocalStorage(overrideAutoSave?: boolean): void; } /** * Settings to initialize a new instance of the IItemsHoldr interface. */ export interface IItemsHoldrSettings { /** * Initial settings for IItemValues to store. */ values?: IItemValueDefaults; /** * Whether new items are allowed to be added (by default, true). */ allowNewItems?: boolean; /** * Whether values should be saved immediately upon being set. */ autoSave?: boolean; /** * Arguments to pass to triggered callback Functions. */ callbackArgs?: any[]; /** * A localStorage object to use instead of the global localStorage. */ localStorage?: any; /** * A prefix to add before IItemsValue keys */ prefix?: string; /** * Default attributes for IItemValues. */ defaults?: IItemValueDefaults; /** * Any hardcoded changes to element content. */ displayChanges?: { [i: string]: string }; /** * Whether an HTML container should be created to house the IItemValue elements. */ doMakeContainer?: boolean; /** * Arguments to pass to create the container, if not the default div and className. */ containersArguments?: [string, any][]; } /** * A versatile container to store and manipulate values in localStorage, and * optionally keep an updated HTML container showing these values. */ export interface IItemsHoldr { /** * @returns The values contained within, keyed by their keys. */ getValues(): { [i: string]: IItemValue }; /** * @returns Default attributes for values. */ getDefaults(): IItemValueDefaults; /** * @returns A reference to localStorage or a replacment object. */ getLocalStorage(): Storage; /** * @returns Whether this should save changes to localStorage automatically. */ getAutoSave(): boolean; /** * @returns The prefix to store thigns under in localStorage. */ getPrefix(): string; /** * @returns The container HTML element, if it exists. */ getContainer(): HTMLElement; /** * @returns createElement arguments for HTML containers, outside-to-inside. */ getContainersArguments(): [string, any][]; /** * @returns Any hard-coded changes to element content. */ getDisplayChanges(): { [i: string]: string }; /** * @returns Arguments to be passed to triggered event callbacks. */ getCallbackArgs(): any[]; /** * @returns String keys for each of the stored IItemValues. */ getKeys(): string[]; /** * @param key The key for a known value. * @returns The known value of a key, assuming that key exists. */ getItem(key: string): any; /** * @param key The key for a known value. * @returns The settings for that particular key. */ getObject(key: string): any; /** * @param key The key for a potentially known value. * @returns Whether there is a value under that key. */ hasKey(key: string): boolean; /** * @returns A mapping of key names to the actual values of all objects being stored. */ exportItems(): any; /** * Adds a new key & value pair to by linking to a newly created ItemValue. * * @param key The key to reference by new ItemValue by. * @param settings The settings for the new ItemValue. * @returns The newly created ItemValue. */ addItem(key: string, settings: any): IItemValue; /** * Clears a value from the listing, and removes its element from the * container (if they both exist). * * @param key The key of the element to remove. */ removeItem(key: string): void; /** * Completely clears all values from the ItemsHoldr, removing their * elements from the container (if they both exist) as well. */ clear(): void; /** * Sets the value for the ItemValue under the given key, then updates the ItemValue * (including the ItemValue's element and localStorage, if needed). * * @param key The key of the ItemValue. * @param value The new value for the ItemValue. */ setItem(key: string, value: any): void; /** * Increases the value for the ItemValue under the given key, via addition for * Numbers or concatenation for Strings. * * @param key The key of the ItemValue. * @param amount The amount to increase by (by default, 1). */ increase(key: string, amount?: number | string): void; /** * Increases the value for the ItemValue under the given key, via addition for * Numbers or concatenation for Strings. * * @param key The key of the ItemValue. * @param amount The amount to increase by (by default, 1). */ decrease(key: string, amount?: number): void; /** * Toggles whether a value is true or false. * * @param key The key of the ItemValue. */ toggle(key: string): void; /** * Ensures a key exists in values. If it doesn't, and new values are * allowed, it creates it; otherwise, it throws an Error. * * @param key */ checkExistence(key: string): void; /** * Manually saves an item's value to localStorage, ignoring the autoSave flag. * * @param key The key of the item to save. */ saveItem(key: string): void; /** * Manually saves all values to localStorage, ignoring the autoSave flag. */ saveAll(): void; /** * Hides the container Element by setting its visibility to hidden. */ hideContainer(): void; /** * Shows the container Element by setting its visibility to visible. */ displayContainer(): void; /** * Creates the container Element, which contains a child for each ItemValue that * specifies hasElement to be true. * * @param containers An Array representing the Element to be created and the * children between it and the contained ItemValues. * Each contained Object has a String tag name as its * first member, followed by any number of Objects to apply * via createElement. * @returns A newly created Element that can be used as a container. */ makeContainer(containers: [string, any][]): HTMLElement; /** * @returns Whether displayChanges has an entry for a particular value. */ hasDisplayChange(value: string): boolean; /** * @returns The displayChanges entry for a particular value. */ getDisplayChange(value: string): string; /** * Creates a new HTMLElement of the given type. For each Object given as * arguments after, each member is proliferated onto the element. * * @param tag The type of the HTMLElement (by default, "div"). * @param args Any number of Objects to be proliferated onto the * new HTMLElement. * @returns A newly created HTMLElement of the given tag. */ createElement(tag?: string, ...args: any[]): HTMLElement; /** * Proliferates all members of the donor to the recipient recursively, as * a deep copy. * * @param recipient An object receiving the donor's members. * @param donor An object whose members are copied to recipient. * @param noOverride If recipient properties may be overriden (by * default, false). * @returns The recipient, which should have the donor proliferated onto it. */ proliferate(recipient: any, donor: any, noOverride?: boolean): any; /** * Identical to proliferate, but tailored for HTML elements because many * element attributes don't play nicely with JavaScript Array standards. * Looking at you, HTMLCollection! * * @param recipient An HTMLElement receiving the donor's members. * @param donor An object whose members are copied to recipient. * @param noOverride If recipient properties may be overriden (by * default, false). * @returns The recipient, which should have the donor proliferated onto it. */ proliferateElement(recipient: any, donor: any, noOverride?: boolean): any; } } module ItemsHoldr { "use strict"; /** * Storage container for a single ItemsHoldr value. The value may have triggers * assigned to value, modularity, and other triggers, as well as an HTML element. */ export class ItemValue implements IItemValue { /** * The container ItemsHoldr governing usage of this ItemsValue. */ ItemsHolder: ItemsHoldr; /** * The unique key identifying this ItemValue in the ItemsHoldr. */ key: string; /** * A default initial value to store, if value isn't provided. */ valueDefault: any; /** * Whether the value should be stored in the ItemHoldr's localStorage. */ storeLocally: boolean; /** * A mapping of values to callbacks that should be triggered when value * is equal to them. */ triggers: ITriggers; /** * An HTML element whose second child's textContent is always set to that of the element. */ element: HTMLElement; /** * Whether an Element should be created and synced to the value. */ hasElement: boolean; /** * An Element tag to use in creating the element, if hasElement is true. */ elementTag: string; /** * A minimum value for the value to equal, if value is a number. */ minimum: number; /** * A callback to call when the value reaches the minimum value. */ onMinimum: Function; /** * A maximum value for the value to equal, if value is a number. */ maximum: number; /** * A callback to call when the value reaches the maximum value. */ onMaximum: Function; /** * A maximum number to modulo the value against, if value is a number. */ modularity: number; /** * A callback to call when the value reaches modularity. */ onModular: Function; /** * A Function to transform the value when it's being set. */ transformGet: Function; /** * A Function to transform the value when it's being retrieved. */ transformSet: Function; /** * The value being stored. */ private value: any; /** * Creates a new ItemValue with the given key and settings. Defaults are given * to the value via proliferate before the settings. * * @constructor * @param ItemsHolder The container for this value. * @param key The key to reference this new ItemValue by. * @param settings Any optional custom settings. */ constructor(ItemsHolder: ItemsHoldr, key: string, settings: any = {}) { this.ItemsHolder = ItemsHolder; ItemsHolder.proliferate(this, ItemsHolder.getDefaults()); ItemsHolder.proliferate(this, settings); this.key = key; if (!this.hasOwnProperty("value")) { this.value = this.valueDefault; } if (this.hasElement) { this.element = ItemsHolder.createElement(this.elementTag || "div", { className: ItemsHolder.getPrefix() + "_value " + key }); this.element.appendChild(ItemsHolder.createElement("div", { "textContent": key })); this.element.appendChild(ItemsHolder.createElement("div", { "textContent": this.value })); } if (this.storeLocally) { // If there exists an old version of this property, get it if (ItemsHolder.getLocalStorage().hasOwnProperty(ItemsHolder.getPrefix() + key)) { this.value = this.retrieveLocalStorage(); } else { // Otherwise save the new version to memory this.updateLocalStorage(); } } } /** * @returns The value being stored, with a transformGet applied if one exists. */ getValue(): any { if (this.transformGet) { return this.transformGet(this.value); } return this.value; } /** * Sets the value being stored, with a is a transformSet applied if one exists. * Any attached triggers to the new value will be called. * * @param value The desired value to now store. */ setValue(value: any): void { if (this.transformSet) { this.value = this.transformSet(value); } else { this.value = value; } this.update(); } /** * General update Function to be run whenever the internal value is changed. * It runs all the trigger, modular, etc. checks, updates the HTML element * if there is one, and updates localStorage if needed. */ update(): void { // Mins and maxes must be obeyed before any other considerations if (this.hasOwnProperty("minimum") && Number(this.value) <= Number(this.minimum)) { this.value = this.minimum; if (this.onMinimum) { this.onMinimum.apply(this, this.ItemsHolder.getCallbackArgs()); } } else if (this.hasOwnProperty("maximum") && Number(this.value) <= Number(this.maximum)) { this.value = this.maximum; if (this.onMaximum) { this.onMaximum.apply(this, this.ItemsHolder.getCallbackArgs()); } } if (this.modularity) { this.checkModularity(); } if (this.triggers) { this.checkTriggers(); } if (this.hasElement) { this.updateElement(); } if (this.storeLocally) { this.updateLocalStorage(); } } /** * Stores a ItemValue's value in localStorage under the prefix plus its key. * * @param {Boolean} [overrideAutoSave] Whether the policy on saving should * be ignored (so saving happens * regardless). By default, false. */ updateLocalStorage(overrideAutoSave?: boolean): void { if (overrideAutoSave || this.ItemsHolder.getAutoSave()) { this.ItemsHolder.getLocalStorage()[this.ItemsHolder.getPrefix() + this.key] = JSON.stringify(this.value); } } /** * Checks if the current value should trigger a callback, and if so calls it. */ private checkTriggers(): void { if (this.triggers.hasOwnProperty(this.value)) { this.triggers[this.value].apply(this, this.ItemsHolder.getCallbackArgs()); } } /** * Checks if the current value is greater than the modularity (assuming * modular is a non-zero Numbers), and if so, continuously reduces value and * calls this.onModular. */ private checkModularity(): void { if (this.value.constructor !== Number || !this.modularity) { return; } while (this.value >= this.modularity) { this.value = Math.max(0, this.value - this.modularity); if (this.onModular) { this.onModular.apply(this, this.ItemsHolder.getCallbackArgs()); } } } /** * Updates the ItemValue's element's second child to be the ItemValue's value. */ private updateElement(): void { if (this.ItemsHolder.hasDisplayChange(this.value)) { this.element.children[1].textContent = this.ItemsHolder.getDisplayChange(this.value); } else { this.element.children[1].textContent = this.value; } } /** * Retrieves a ItemValue's value from localStorage, making sure not to try to * JSON.parse an undefined or null value. * * @returns {Mixed} */ private retrieveLocalStorage(): void { var value: any = localStorage.getItem(this.ItemsHolder.getPrefix() + this.key); if (value === "undefined") { return undefined; } if (value.constructor !== String) { return value; } return JSON.parse(value); } } /** * A versatile container to store and manipulate values in localStorage, and * optionally keep an updated HTML container showing these values. */ export class ItemsHoldr implements IItemsHoldr { /** * The ItemValues being stored, keyed by name. */ private items: { [i: string]: ItemValue }; /** * A listing of all the String keys for the stored items. */ private itemKeys: string[]; /** * Default attributes for ItemValues. */ private defaults: IItemValueDefaults; /** * A reference to localStorage or a replacement object. */ private localStorage: Storage; /** * A prefix to store things under in localStorage. */ private prefix: string; /** * Whether new items are allowed to be created using setItem. */ private allowNewItems: boolean; /** * Whether this should save changes to localStorage automatically. */ private autoSave: boolean; /** * A container element containing children for each value's element. */ private container: HTMLElement; /** * An Array of elements as createElement arguments, outside-to-inside. */ private containersArguments: [string, any][]; /** * Any hardcoded changes to element content, such as "INF" for Infinity. */ private displayChanges: { [i: string]: string }; /** * Arguments to be passed to triggered callback Functions. */ private callbackArgs: any[]; /** * Initializes a new instance of the ItemsHoldr class. * * @param settings Any optional custom settings. */ constructor(settings: IItemsHoldrSettings = {}) { var key: string; this.prefix = settings.prefix || ""; this.autoSave = settings.autoSave; this.callbackArgs = settings.callbackArgs || []; this.allowNewItems = settings.allowNewItems === undefined ? true : settings.allowNewItems; if (settings.localStorage) { this.localStorage = settings.localStorage; } else if (typeof localStorage === "undefined") { this.localStorage = this.createPlaceholderStorage(); } else { this.localStorage = localStorage; } this.defaults = settings.defaults || {}; this.displayChanges = settings.displayChanges || {}; this.items = {}; if (settings.values) { this.itemKeys = Object.keys(settings.values); for (key in settings.values) { if (settings.values.hasOwnProperty(key)) { this.addItem(key, settings.values[key]); } } } else { this.itemKeys = []; } if (settings.doMakeContainer) { this.containersArguments = settings.containersArguments || [ ["div", { "className": this.prefix + "_container" }] ]; this.container = this.makeContainer(settings.containersArguments); } } /* Simple gets */ /** * */ key(index: number): string { return this.itemKeys[index]; } /** * @returns The values contained within, keyed by their keys. */ getValues(): { [i: string]: IItemValue } { return this.items; } /** * @returns {Mixed} Default attributes for values. */ getDefaults(): any { return this.defaults; } /** * @returns A reference to localStorage or a replacment object. */ getLocalStorage(): Storage { return this.localStorage; } /** * @returns Whether this should save changes to localStorage automatically. */ getAutoSave(): boolean { return this.autoSave; } /** * @returns The prefix to store thigns under in localStorage. */ getPrefix(): string { return this.prefix; } /** * @returns The container HTML element, if it exists. */ getContainer(): HTMLElement { return this.container; } /** * @returns createElement arguments for HTML containers, outside-to-inside. */ getContainersArguments(): [string, any][] { return this.containersArguments; } /** * @returns Any hard-coded changes to element content. */ getDisplayChanges(): { [i: string]: string } { return this.displayChanges; } /** * @returns Arguments to be passed to triggered event callbacks. */ getCallbackArgs(): any[] { return this.callbackArgs; } /* Retrieval */ /** * @returns String keys for each of the stored ItemValues. */ getKeys(): string[] { return Object.keys(this.items); } /** * @param key The key for a known value. * @returns The known value of a key, assuming that key exists. */ getItem(key: string): any { this.checkExistence(key); return this.items[key].getValue(); } /** * @param key The key for a known value. * @returns The settings for that particular key. */ getObject(key: string): any { return this.items[key]; } /** * @param key The key for a potentially known value. * @returns Whether there is a value under that key. */ hasKey(key: string): boolean { return this.items.hasOwnProperty(key); } /** * @returns A mapping of key names to the actual values of all objects being stored. */ exportItems(): any { var output: any = {}, i: string; for (i in this.items) { if (this.items.hasOwnProperty(i)) { output[i] = this.items[i].getValue(); } } return output; } /* ItemValues */ /** * Adds a new key & value pair to by linking to a newly created ItemValue. * * @param key The key to reference by new ItemValue by. * @param settings The settings for the new ItemValue. * @returns The newly created ItemValue. */ addItem(key: string, settings: any = {}): ItemValue { this.items[key] = new ItemValue(this, key, settings); this.itemKeys.push(key); return this.items[key]; } /** * Clears a value from the listing, and removes its element from the * container (if they both exist). * * @param key The key of the element to remove. */ removeItem(key: string): void { if (!this.items.hasOwnProperty(key)) { return; } if (this.container && this.items[key].hasElement) { this.container.removeChild(this.items[key].element); } this.itemKeys.splice(this.itemKeys.indexOf(key), 1); delete this.items[key]; } /** * Completely clears all values from the ItemsHoldr, removing their * elements from the container (if they both exist) as well. */ clear(): void { var i: string; if (this.container) { for (i in this.items) { if (this.items[i].hasElement) { this.container.removeChild(this.items[i].element); } } } this.items = {}; this.itemKeys = []; } /** * Sets the value for the ItemValue under the given key, then updates the ItemValue * (including the ItemValue's element and localStorage, if needed). * * @param key The key of the ItemValue. * @param value The new value for the ItemValue. */ setItem(key: string, value: any): void { this.checkExistence(key); this.items[key].setValue(value); } /** * Increases the value for the ItemValue under the given key, via addition for * Numbers or concatenation for Strings. * * @param key The key of the ItemValue. * @param amount The amount to increase by (by default, 1). */ increase(key: string, amount: number | string = 1): void { this.checkExistence(key); var value: any = this.items[key].getValue(); value += amount; this.items[key].setValue(value); } /** * Increases the value for the ItemValue under the given key, via addition for * Numbers or concatenation for Strings. * * @param key The key of the ItemValue. * @param amount The amount to increase by (by default, 1). */ decrease(key: string, amount: number = 1): void { this.checkExistence(key); var value: any = this.items[key].getValue(); value -= amount; this.items[key].setValue(value); } /** * Toggles whether a value is true or false. * * @param key The key of the ItemValue. */ toggle(key: string): void { this.checkExistence(key); var value: any = this.items[key].getValue(); value = value ? false : true; this.items[key].setValue(value); } /** * Ensures a key exists in values. If it doesn't, and new values are * allowed, it creates it; otherwise, it throws an Error. * * @param key */ checkExistence(key: string): void { if (!this.items.hasOwnProperty(key)) { if (this.allowNewItems) { this.addItem(key); } else { throw new Error("Unknown key given to ItemsHoldr: '" + key + "'."); } } } /** * Manually saves an item's value to localStorage, ignoring the autoSave flag. * * @param key The key of the item to save. */ saveItem(key: string): void { if (!this.items.hasOwnProperty(key)) { throw new Error("Unknown key given to ItemsHoldr: '" + key + "'."); } this.items[key].updateLocalStorage(true); } /** * Manually saves all values to localStorage, ignoring the autoSave flag. */ saveAll(): void { var key: string; for (key in this.items) { if (this.items.hasOwnProperty(key)) { this.items[key].updateLocalStorage(true); } } } /* HTML helpers */ /** * Hides the container Element by setting its visibility to hidden. */ hideContainer(): void { this.container.style.visibility = "hidden"; } /** * Shows the container Element by setting its visibility to visible. */ displayContainer(): void { this.container.style.visibility = "visible"; } /** * Creates the container Element, which contains a child for each ItemValue that * specifies hasElement to be true. * * @param containers An Array representing the Element to be created and the * children between it and the contained ItemValues. * Each contained Object has a String tag name as its * first member, followed by any number of Objects to apply * via createElement. * @returns A newly created Element that can be used as a container. */ makeContainer(containers: [string, any][]): HTMLElement { var output: HTMLElement = this.createElement.apply(this, containers[0]), current: HTMLElement = output, child: HTMLElement, key: string, i: number; for (i = 1; i < containers.length; ++i) { child = this.createElement.apply(this, containers[i]); current.appendChild(child); current = child; } for (key in this.items) { if (this.items[key].hasElement) { child.appendChild(this.items[key].element); } } return output; } /** * @returns Whether displayChanges has an entry for a particular value. */ hasDisplayChange(value: string): boolean { return this.displayChanges.hasOwnProperty(value); } /** * @returns The displayChanges entry for a particular value. */ getDisplayChange(value: string): string { return this.displayChanges[value]; } /* Utilities */ /** * Creates a new HTMLElement of the given type. For each Object given as * arguments after, each member is proliferated onto the element. * * @param tag The type of the HTMLElement (by default, "div"). * @param args Any number of Objects to be proliferated onto the * new HTMLElement. * @returns A newly created HTMLElement of the given tag. */ createElement(tag: string = "div", ...args: any[]): HTMLElement { var element: HTMLElement = document.createElement(tag), i: number; // For each provided object, add those settings to the element for (i = 0; i < args.length; i += 1) { this.proliferateElement(element, args[i]); } return element; } /** * Proliferates all members of the donor to the recipient recursively, as * a deep copy. * * @param recipient An object receiving the donor's members. * @param donor An object whose members are copied to recipient. * @param noOverride If recipient properties may be overriden (by * default, false). * @returns The recipient, which should have the donor proliferated onto it. */ proliferate(recipient: any, donor: any, noOverride?: boolean): any { var setting: any, i: string; // For each attribute of the donor: for (i in donor) { if (donor.hasOwnProperty(i)) { // If noOverride, don't override already existing properties if (noOverride && recipient.hasOwnProperty(i)) { continue; } // If it's an object, recurse on a new version of it setting = donor[i]; if (typeof setting === "object") { if (!recipient.hasOwnProperty(i)) { recipient[i] = new setting.constructor(); } this.proliferate(recipient[i], setting, noOverride); } else { // Regular primitives are easy to copy otherwise recipient[i] = setting; } } } return recipient; } /** * Identical to proliferate, but tailored for HTML elements because many * element attributes don't play nicely with JavaScript Array standards. * Looking at you, HTMLCollection! * * @param recipient An HTMLElement receiving the donor's members. * @param donor An object whose members are copied to recipient. * @param noOverride If recipient properties may be overriden (by * default, false). * @returns The recipient, which should have the donor proliferated onto it. */ proliferateElement(recipient: any, donor: any, noOverride?: boolean): HTMLElement { var setting: any, i: string, j: number; // For each attribute of the donor: for (i in donor) { if (donor.hasOwnProperty(i)) { // If noOverride, don't override already existing properties if (noOverride && recipient.hasOwnProperty(i)) { continue; } setting = donor[i]; // Special cases for HTML elements switch (i) { // Children: just append all of them directly case "children": if (typeof (setting) !== "undefined") { for (j = 0; j < setting.length; j += 1) { recipient.appendChild(setting[j]); } } break; // Style: proliferate (instead of making a new Object) case "style": this.proliferate(recipient[i], setting); break; // By default, use the normal proliferate logic default: // If it's an object, recurse on a new version of it if (typeof setting === "object") { if (!recipient.hasOwnProperty(i)) { recipient[i] = new setting.constructor(); } this.proliferate(recipient[i], setting, noOverride); } else { // Regular primitives are easy to copy otherwise recipient[i] = setting; } break; } } } return recipient; } /** * Creates an Object that can be used to create a new LocalStorage * replacement, if the JavaScript environment doesn't have one. * * @returns {Object} */ private createPlaceholderStorage(): Storage { var i: string, output: any = { "keys": [], "getItem": function (key: string): any { return this.localStorage[key]; }, "setItem": function (key: string, value: string): void { this.localStorage[key] = value; }, "clear": function (): void { for (i in this) { if (this.hasOwnProperty(i)) { delete this[i]; } } }, "removeItem": function (key: string): void { delete this[key]; }, "key": function (index: number): string { return this.keys[index]; } }; Object.defineProperties(output, { "length": { "get": function (): number { return output.keys.length; } }, "remainingSpace": { "get": function (): number { return 9001; // Is there a way to calculate this? } } }); return output; } } }