# src/modules/storage.js | tablefilter v0.4.16 API Document

# src/modules/storage.js

    import Cookie from '../cookie';
    import {root} from '../root';

    const JSON = root.JSON;
    const localStorage = root.localStorage;
    const location = root.location;

    /**
     * Checks if browser has Storage feature
     */
    export const hasStorage = () => {
        return 'Storage' in root;
    };

    /**
     * Stores the features state in browser's local storage or cookie
     *
     * @export
     * @class Storage
     */
    export class Storage {

        /**
         * Creates an instance of Storage
         *
         * @param {State} state Instance of State
         */
        constructor(state) {

            /**
             * State object
             * @type {State}
             * @private
             */
            this.state = state;

            /**
             * TableFilter object
             * @type {TableFilter}
             * @private
             */
            this.tf = state.tf;

            /**
             * Persist with local storage
             * @type {Boolean}
             * @private
             */
            this.enableLocalStorage = state.enableLocalStorage && hasStorage();

            /**
             * Persist with cookie
             * @type {Boolean}
             * @private
             */
            this.enableCookie = state.enableCookie && !this.enableLocalStorage;

            /**
             * Emitter object
             * @type {Emitter}
             * @private
             */
            this.emitter = state.emitter;

            /**
             * Cookie duration in hours from state object
             * @type {Number}
             * @private
             */
            this.duration = state.cookieDuration;
        }

        /**
         * Initializes the Storage object
         */
        init() {
            this.emitter.on(['state-changed'], (tf, state) => this.save(state));
            this.emitter.on(['initialized'], () => this.sync());
        }

        /**
         * Persists the features state on state changes
         *
         * @param {State} state Instance of State
         */
        save(state) {
            if (this.enableLocalStorage) {
                localStorage[this.getKey()] = JSON.stringify(state);
            } else {
                Cookie.write(this.getKey(), JSON.stringify(state), this.duration);
            }
        }

        /**
         * Turns stored string into a State JSON object
         *
         *  @returns {Object} JSON object
         */
        retrieve() {
            let state = null;
            if (this.enableLocalStorage) {
                state = localStorage[this.getKey()];
            } else {
                state = Cookie.read(this.getKey());
            }

            if (!state) {
                return null;
            }
            return JSON.parse(state);
        }

        /**
         * Removes persisted state from storage
         */
        remove() {
            if (this.enableLocalStorage) {
                localStorage.removeItem(this.getKey());
            } else {
                Cookie.remove(this.getKey());
            }
        }

        /**
         * Applies persisted state to features
         */
        sync() {
            let state = this.retrieve();
            if (!state) {
                return;
            }
            // override current state with persisted one and sync features
            this.state.overrideAndSync(state);
        }

        /**
         * Returns the storage key
         *
         * @returns {String} Key
         */
        getKey() {
            return JSON.stringify({
                key: `${this.tf.prfxTf}_${this.tf.id}`,
                path: location.pathname
            });
        }

        /**
         * Release Storage event subscriptions and clear fields
         */
        destroy() {
            this.emitter.off(['state-changed'], (tf, state) => this.save(state));
            this.emitter.off(['initialized'], () => this.sync());

            this.remove();

            this.state = null;
            this.emitter = null;
        }
    }