%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /proc/985914/root/data/old/home/stash/stash/atlassian-stash/static/util/
Upload File :
Create Path :
Current File : //proc/985914/root/data/old/home/stash/stash/atlassian-stash/static/util/client-storage.js

define('util/client-storage', [
    'jquery',
    'underscore',
    'util/feature-detect',
    'model/page-state',
    'exports'
], function(
    $,
    _,
    featureDetect,
    pageState,
    exports
) {

    'use strict';

    /**
     * Types of storage.
     * @readonly
     * @enum {string}
     */
    var storageType = {
        SESSION : 'session',
        LOCAL : 'local'
    };

    var FLASH_PREFIX = '_flash.';

    var componentDelimiter = "_",
        cleanupKey = 'lastCleanup',
        hasCheckedCleanUpKey = 'hasCheckedCleanUp',
        oneMonth = 1000 * 60 * 60 * 24 * 30;

    var dummy = {};
    // Visible for testing
    exports._resetDummy = function() { dummy = {}; };

    /**
     * Build a key for use in client storage
     * @param {string[]|string} components a string or array of strings to build into a key for client storage.
     * @param {string} [context] One of 'pull-request', 'repo', 'project', or 'user' for scoping the key. Each is progressively more weakly scoped than the previous.
     * @return {string} key
     */
    function buildKey(components, context) {
        if (_.isString(components)) {
            components = [components];
        }

        if (!_.isArray(components)) {
            throw new Error('keyBuilder requires an array of components');
        }

        if (context) {
            // Add the context to the key
            components.push(context);

            // This switch falls through each level adding progressively more detail the higher up you start
            // e.g. the user context just adds the current user's username,
            // but `pull-request` adds the pull request id, the repo slug, the project key and the current username
            // This may need refactoring if we introduce new contexts that don't fit into this waterfall.
            switch(context) {
                case 'pull-request':
                    components.push(pageState.getPullRequest() && pageState.getPullRequest().getId());
                    /* falls through */
                case 'repo':
                    components.push(pageState.getRepository() && pageState.getRepository().getSlug());
                    /* falls through */
                case 'project':
                    components.push(pageState.getProject() && pageState.getProject().getKey());
                    /* falls through */
                case 'user':
                    components.push(pageState.getCurrentUser() && pageState.getCurrentUser().getName());
                break;
            }
        }

        return components.join(componentDelimiter);
    }

    /**
     * Get an item directly from client storage, without the clientStorage wrapping/unwrapping being applied.
     * The item will be JSON.parse'd if appropriate.
     * @param {string} key the identifier of the item to retrieve from storage
     * @param {string} [type='local'] one of 'local' or 'session' to get the value from.
     * @return {*}
     */
    function getRawItem(key, type) {
        //Get the entire JSON object from localStorage.
        //Use if you want to access the metadata of an entry
        var rawItem,
            item;

        if (featureDetect.localStorage()) {
            rawItem = window[(type || storageType.LOCAL) + "Storage"].getItem(key);
        } else {
            rawItem = _.has(dummy, key) ? dummy[key] : null;
        }

        try {
            item = JSON.parse(rawItem);
        } catch(exception) {
            //rawItem was invalid JSON, so just return the raw string
            item = rawItem;
        }

        return item;
    }

    /**
     * Get an item from client storage, invoking JSON and clientStorage-specific unwrapping transforms on it.
     * @param {string} key the identifier of the item to retrieve from storage
     * @param {string} [type='local'] one of 'local' or 'session' to get the value from.
     * @return {*}
     */
    function getItem(key, type) {
        //Return the `data` attribute of the JSON object stored in localStorage, or the raw value if it's not wrapped in a object.
        //`type` is LOCAL by default
        var item = getRawItem(key, type);
        return $.isPlainObject(item) && _.has(item, 'data') ? item.data : item;
    }

    /**
     * Get an item from sessionStorage, invoking JSON and clientStorage-specific unwrapping transforms on it.
     * @param {string} key the identifier of the item to retrieve from storage
     * @return {*}
     */
    function getSessionItem(key) {
        return getItem(key, storageType.SESSION);
    }

    /**
     * Get a flash item (from sessionStorage), invoking JSON and clientStorage-specific unwrapping transforms on it.
     * The item will be removed from storage and won't be available when next requested.
     * @param {string} key the identifier of the item to retrieve from storage
     * @return {*}
     */
    function getFlashItem(key) {
        var val = getItem(FLASH_PREFIX + key, storageType.SESSION);
        removeFlashItem(key);
        return val;
    }

    /**
     * Set an item in client storage, invoking only a JSON.stringify transform on it.
     * @param {string} key the identifier for this item in storage
     * @param {*} obj the object to store. Note that circular references within the object, or DOM objects will cause this method to throw errors.
     * @param {string} [type='local'] one of 'local' or 'session' to set the value in.
     */
    function setRawItem(key, obj, type) {
        //Save the object as is to client storage, don't add meta data
        if (featureDetect.localStorage()) {
            window[(type || storageType.LOCAL) + "Storage"].setItem(key, JSON.stringify(obj));
        } else {
            dummy[key] = JSON.stringify(obj);
        }

    }

    /**
     * Set an item in client storage, invoking JSON and clientStorage-specific wrapping transforms on it.
     * @param {string} key the identifier for this item in storage
     * @param {*} obj the object to store. Note that circular references within the object, or DOM objects will cause this method to throw errors.
     * @param {Object} [extraProperties] Extra metadata to store about the object that will not be returned with it.
     * @param {boolean} [extraProperties.noCleanup] If specified as true, this object will not be cleaned up after a month.
     * @param {string} [type='local'] one of 'local' or 'session' to set the value in.
     */
    function setItem(key, obj, extraProperties, type) {
        //Don't allow extraProperties to overwrite the core attributes, `data` and `timestamp`;
        //Currently the only useful extraProperty is Boolean `noCleanup`
        //`type` is LOCAL by default
        var item = _.extend({}, extraProperties, {
            timestamp: new Date().getTime(),
            data: obj
        });

        setRawItem(key, item, type);

        if (!type || type === storageType.LOCAL) {
            //Defer cleanup task until after the calling code has finished executing
            _.defer(checkCleanup);
        }
    }

    /**
     * Set an item in sessionStorage, invoking JSON and clientStorage-specific wrapping transforms on it.
     * @param {string} key the identifier for this item in storage
     * @param {*} obj the object to store. Note that circular references within the object, or DOM objects will cause this method to throw errors.
     * @param {Object} [extraProperties] Extra metadata to store about the object that will not be returned with it.
     * @param {boolean} [extraProperties.noCleanup] If specified as true, this object will not be cleaned up after a month. This is not very useful for session storage.
     */
    function setSessionItem(key, obj, extraProperties) {
        setItem.call(this, key, obj, extraProperties, storageType.SESSION);
    }

    /**
     * Set a flash item's value (in sessionStorage), invoking JSON and clientStorage-specific wrapping transforms on it.
     * @param {string} key the identifier for this item in storage
     * @param {*} obj the object to store. Note that circular references within the object, or DOM objects will cause this method to throw errors.
     * @param {Object} [extraProperties] Extra metadata to store about the object that will not be returned with it.
     * @param {boolean} [extraProperties.noCleanup] If specified as true, this object will not be cleaned up after a month. This is not very useful for flash storage.
     */
    function setFlashItem(key, obj, extraProperties) {
        setItem.call(this, FLASH_PREFIX + key, obj, extraProperties, storageType.SESSION);
    }

    /**
     * Remove an item from client storage.
     * @param {string} key the identifier for which item to remove
     * @param {string} [type='local'] one of 'local' or 'session' to remove the value from
     */
    function removeItem(key, type){
        if (featureDetect.localStorage()) {
            window[(type || storageType.LOCAL) + "Storage"].removeItem(key);
        } else {
            delete dummy[key];
        }

    }

    /**
     * Remove an item from sessionStorage.
     * @param {string} key the identifier for which item to remove
     */
    function removeSessionItem(key) {
        removeItem(key, storageType.SESSION);
    }

    /**
     * Remove a flash item (from sessionStorage).
     * @param {string} key the identifier for which item to remove
     */
    function removeFlashItem(key) {
        removeItem(FLASH_PREFIX + key, storageType.SESSION);
    }

    function checkCleanup(){
        if (!!getRawItem(hasCheckedCleanUpKey, storageType.SESSION)) {
            //Short circuit if we have already checked for cleanup this page/session
            return;
        }

        var lastCleanup = getRawItem(cleanupKey);

        if (!lastCleanup || new Date().getTime() - lastCleanup > oneMonth) {
            doCleanup();
        }

        setRawItem(hasCheckedCleanUpKey, true, storageType.SESSION); //Prevent reruns of the cleanup check for the life of this session
    }

    function doCleanup(){
        var currTime = new Date().getTime();
        _.each(_.keys(localStorage), function(key){
            if (key !== cleanupKey) {
                //don't cleanup the cleanup tracker
                var item = getRawItem(key);
                if (item && item.timestamp && !item.noCleanup && (currTime - item.timestamp > oneMonth)) {
                    removeItem(key);
                }
            }
        });
        setRawItem(cleanupKey, new Date().getTime());
    }

    exports.LOCAL = storageType.LOCAL;
    exports.SESSION = storageType.SESSION;
    exports.buildKey = buildKey;
    exports.getItem = getItem;
    exports.getFlashItem = getFlashItem;
    exports.getSessionItem = getSessionItem;
    exports.setItem = setItem;
    exports.setFlashItem = setFlashItem;
    exports.setSessionItem = setSessionItem;
    exports.removeItem = removeItem;
    exports.removeFlashItem = removeFlashItem;
    exports.removeSessionItem = removeSessionItem;
});

Zerion Mini Shell 1.0