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;
        }
    }
}
