milo

DOMStorage

function
 DOMStorage() 

Option name Type Description
keyPrefix String

prefix that will be added to all keys followed by milo.config.domStorage.prefixSeparator ("/" by default).

sessionOnly Boolean

true to use sessionStorage. localStorage will be used by default.

win Window

window to work in

DOMStorage class to simplify storage and retrieval of multiple items with types preservation to DOM storage (localStorage and sessionStorage).
Types will be stored in the key created from value keys with appended milo.config.domStorage.typeSuffix

function DOMStorage(keyPrefix, sessionOnly, win) {
    if (typeof window == 'undefined') return;
    win = win || window;

    keyPrefix = config.domStorage.root +
                (keyPrefix
                    ? keyPrefix + config.domStorage.prefixSeparator
                    : '');

    _.defineProperties(this, {
        keyPrefix: keyPrefix,
        sessionOnly: !! sessionOnly,
        window: win,
        _storage: sessionOnly ? win.sessionStorage : win.localStorage,
        _typeSuffix: config.domStorage.typeSuffix,
        _keys: {}
    }, _.WRIT);
}


_.extendProto(DOMStorage, {
    get: DOMStorage$get,
    set: DOMStorage$set,
    remove: DOMStorage$remove,
    hasItem: DOMStorage$hasItem,
    getItem: DOMStorage$getItem,
    setItem: DOMStorage$setItem,
    removeItem: DOMStorage$removeItem,
    _storageKey: DOMStorage$_storageKey,
    _domStorageKey: DOMStorage$_domStorageKey,
    getAllKeys: DOMStorage$getAllKeys,
    getAllItems: DOMStorage$getAllItems,
    createMessenger: DOMStorage$createMessenger,
    destroy: DOMStorage$destroy
});

Expose Mesenger and MessageSource methods on DOMStorage

Messenger.useWith(DOMStorage, '_messenger', Messenger.defaultMethods);
StorageMessageSource.useWith(DOMStorage, '_messageSource', ['trigger']);


var _sessionStorage = new DOMStorage('', true)
    , _localStorage = new DOMStorage('', false);

var _domStorage = {
        true: _sessionStorage,
        false: _localStorage
    };

_.extend(DOMStorage, {
    registerDataType: DOMStorage$$registerDataType,
    local: _localStorage,
    session: _sessionStorage,
    storage: _domStorage,
    _storedKeys: _storedKeys // exposed for testing
});

DOMStorage$set

function
 DOMStorage$set() 

Option name Type Description
data Object

single object can be passed in which case keys will be used as keys in local storage.

arguments List

alternatively just the list of arguments can be passed where arguments can be sequentially used as keys and values.

Sets data to DOM storage. this.keyPrefix is prepended to keys.

function DOMStorage$set(data) { // or arguments
    if (typeof data == 'object')
        _.eachKey(data, function(value, key) {
            this.setItem(key, value);
        }, this);
    else {
        var argsLen = arguments.length;
        if (argsLen % 2)
            throw new Error('DOMStorage: set should have even number of arguments or object');

        for (var i = 0; i < argsLen; i++) {
            var key = arguments[i]
                , value = arguments[++i];

            this.setItem(key, value);
        }
    }
}

DOMStorage$get

function
 DOMStorage$get() 

Option name Type Description
arguments List

keys can be passed as strings or arrays of strings

Gets data from DOM storage. this.keyPrefix is prepended to passed keys, but returned object will have keys without root keys.

function DOMStorage$get() { // , ... arguments
    var data = {};
    _.deepForEach(arguments, function(key) {
        data[key] = this.getItem(key);
    }, this);
    return data;
}

DOMStorage$remove

function
 DOMStorage$remove() 

Option name Type Description
arguments List

keys can be passed as strings or arrays of strings

Removes keys from DOM storage. this.keyPrefix is prepended to passed keys.

function DOMStorage$remove() { //, ... arguments
    _.deepForEach(arguments, function(key) {
        this.removeItem(key);
    }, this);
}

DOMStorage$hasItem

function
 DOMStorage$hasItem() 

Option name Type Description
key String
return Boolean

Check for presence of single item in DOM storage. this.keyPrefix is prepended to passed key.

function DOMStorage$hasItem(key) {
    var pKey = this._storageKey(key);
    return this._storage.getItem(pKey) != null;
}

DOMStorage$getItem

function
 DOMStorage$getItem() 

Option name Type Description
key String
return Any

Gets single item from DOM storage prepending this.keyPrefix to passed key.
Reads type of the originally stored value from key + this._typeSuffix and converts data to the original type.

function DOMStorage$getItem(key) {
    var pKey = this._storageKey(key);
    var dataType = _getKeyDataType.call(this, pKey);
    var valueStr = this._storage.getItem(pKey);
    var value = _parseData(valueStr, dataType);
    return value;
}

DOMStorage$setItem

function
 DOMStorage$setItem() 

Option name Type Description
key String
return Any

Sets single item to DOM storage prepending this.keyPrefix to passed key.
Stores type of the stored value to key + this._typeSuffix.

function DOMStorage$setItem(key, value) {
    var pKey = this._storageKey(key);
    var dataType = _setKeyDataType.call(this, pKey, value);
    var valueStr = _serializeData(value, dataType);
    try {
        this._storage.setItem(pKey, valueStr);
    } catch(e) {
        if (e.name == 'QuotaExceededError') {
            var cfg = config.domStorage.quotaExceeded;
            if (cfg.message)
                milo.mail.postMessage('quotaexceedederror', value);
            if (cfg.throwError)
                throw e;
        } else
            throw e;
    }
    this._keys[key] = true;
    _domStorage[this.sessionOnly]._keys[pKey] = true;
}

DOMStorage$removeItem

function
 DOMStorage$removeItem() 

Option name Type Description
key String
return Any

Removes single item from DOM storage prepending this.keyPrefix to passed key.
Type of the stored value (in key + this._typeSuffix key) is also removed.

function DOMStorage$removeItem(key) {
    var pKey = this._storageKey(key);
    this._storage.removeItem(pKey);
    _removeKeyDataType.call(this, pKey);
    delete this._keys[key];
    delete _domStorage[this.sessionOnly]._keys[pKey];
}

DOMStorage$getAllKeys

function
 DOMStorage$getAllKeys() 

Returns the array of all keys stored by this instance of DOMStorage

function DOMStorage$getAllKeys() {
    var storedKeys = Object.keys(this._keys);
    var keysInStorage = storedKeys.filter(function(key) {
        if (this.hasItem(key)) return true;
        else delete this._keys[key];
    }, this);
    return keysInStorage;
}

DOMStorage$getAllItems

function
 DOMStorage$getAllItems() 

Returns the map with all keys and values (deserialized) stored using this instance of DOMStorage

function DOMStorage$getAllItems() {
    return this.get(this.getAllKeys());
}

DOMStorage$_storageKey

function
 DOMStorage$_storageKey() 

Option name Type Description
key String
return String

Returns prefixed key for DOM storage for given unprefixed key.

function DOMStorage$_storageKey(key) {
    return this.keyPrefix + key;
}

DOMStorage$_domStorageKey

function
 DOMStorage$_domStorageKey() 

Option name Type Description
storageKey String

actual key in local/session storage

return String

Returns unprefixed key to be used with this instance of DOMStorage fir given actual key in storage
If key has different prefix from the keyPrefix returns undefined

function DOMStorage$_domStorageKey(storageKey) {
    if (storageKey.indexOf(this._typeSuffix) >= 0) return;
    return _.unPrefix(storageKey, this.keyPrefix);
}

_getKeyDataType

function
 _getKeyDataType() 

Option name Type Description
pKey String

prefixed key of stored value

return String

Gets originally stored data type for given (prefixed) key.

function _getKeyDataType(pKey) {
    pKey = _dataTypeKey.call(this, pKey);
    return this._storage.getItem(pKey);
}

_setKeyDataType

function
 _setKeyDataType() 

Option name Type Description
pKey String

prefixed key of stored value

value Any
return String

Stores data type for given (prefixed) key and value.
Returns data type for value.

function _setKeyDataType(pKey, value) {
    var dataType = _getValueType(value);
    pKey = _dataTypeKey.call(this, pKey);
    this._storage.setItem(pKey, dataType);
    return dataType;
}

_removeKeyDataType

function
 _removeKeyDataType() 

Option name Type Description
pKey String

prefixed key of stored value

Removes stored data type for given (prefixed) key.

function _removeKeyDataType(pKey) {
    pKey = _dataTypeKey.call(this, pKey);
    this._storage.removeItem(pKey);
}

_dataTypeKey

function
 _dataTypeKey() 

Option name Type Description
pKey String

prefixed key of stored value

return String

Returns the key to store data type for given (prefixed) key.

function _dataTypeKey(pKey) {
    return pKey + this._typeSuffix;
}

_getValueType

function
 _getValueType() 

Option name Type Description
value Any
return String

Returns type of value as string. Class name returned for objects ('null' for null).

function _getValueType(value) {
    var valueType = typeof value
        , className = value && value.constructor.name
        , dataType = valuesDataTypes[className];
    return dataType || (
            valueType != 'object'
                ? valueType
                : value == null
                    ? 'null'
                    : value.constructor.name);
}
var valuesDataTypes = {
    // can be registered with `registerDataType`
};

_serializeData

function
 _serializeData() 

Option name Type Description
value Any

value to be serialized

valueType String

optional data type to define serializer, _getValueType is used if not passed.

return String

Serializes value to be stored in DOM storage.

function _serializeData(value, valueType) {
    valueType = valueType || _getValueType(value);
    var serializer = dataSerializers[valueType];
    return serializer
            ? serializer(value, valueType)
            : value && value.toString == Object.prototype.toString
                ? JSON.stringify(value)
                : '' + value;
}
var dataSerializers = {
    'Array': JSON.stringify
};

_parseData

function
 _parseData() 

Option name Type Description
valueStr String
valueType String

data type that defines parser. Original sring will be returned if parser is not defined.

return Any

Parses string retrieved from DOM storage.

function _parseData(valueStr, valueType) {
    var parser = dataParsers[valueType];
    return parser
            ? parser(valueStr, valueType)
            : valueStr;
}
var dataParsers = {
    Object: _.jsonParse,
    Array: _.jsonParse,
    Date: function(valStr) { return new Date(valStr); },
    boolean: function(valStr) { return valStr == 'true'; },
    number: Number,
    function: _.toFunction,
    RegExp: _.toRegExp
};

DOMStorage$$registerDataType

function
 DOMStorage$$registerDataType() 

Option name Type Description
valueType String

class (constructor) name or the string returned by typeof.

serializer Function

optional serializer for this type

parser Function

optional parser for this type

[storeAsDataType] String

optional name of stored data type if different from valueType

Registers data type to be saved in DOM storage. Class name can be used or result of typeof operator for non-objects to override default conversions.

function DOMStorage$$registerDataType(valueType, serializer, parser, storeAsDataType) {
    if (serializer) dataSerializers[valueType] = serializer;
    if (parser) dataParsers[valueType] = parser;
    valuesDataTypes[valueType] = storeAsDataType || valueType;
}


function DOMStorage$createMessenger() {
    var storageMessageSource = new StorageMessageSource(this);
    var messenger = new Messenger(this, undefined, storageMessageSource);
    _.defineProperties(this, {
        _messenger: messenger,
        _messageSource: storageMessageSource
    }, _.WRIT);
}


function DOMStorage$destroy() {
    this._storage = undefined;
    this.window = undefined;
    if (this._messenger) this._messenger.destroy();
    this._destroyed = true;
}