/// <reference path="./Variable.ts" /> /// <reference path="./IVariableState.ts" /> /// <reference path="./IVariableConfigOptions.ts" /> /// <reference path="../env/SessionStorage.ts" /> /// <reference path="../env/LocalStorage.ts" /> var econda; (function (econda) { var storage; (function (storage) { var Variable = econda.storage.Variable; var SessionStorage = econda.env.SessionStorage; var LocalStorage = econda.env.LocalStorage; /** * Just a place where we can store data in browsers local and / or session storage * @class econda.storage.ClientBag * @experimental */ var ClientBag = (function () { function ClientBag(cfg) { /** * setTimeout handle for save operation. See {@link _triggerSaveInBrowser} for details */ this._triggerSaveTimeout = null; /** * Timestamp when bag was saved. * @private * @property {Date} [_lastSaved=null] */ this._lastSaved = null; /** * Name of client bag. Defaults to "default". In most cases there's no sense to use multiple (named) client bags. * @cfg {String} [name=default] * @accessor */ this._name = 'default'; /** * Auto save data to browser * @cfg {Boolean} [autoSave=true] * @accessor */ this._autoSave = true; this._variables = {}; this._variableStates = {}; if (cfg instanceof ClientBag) { return cfg; } if (typeof cfg === 'object' && cfg !== null) { if (typeof cfg.name !== 'undefined') { this.setName((cfg.name)); } if (typeof cfg.autoSave !== 'undefined') { this.setAutoSave(cfg.autoSave); } } } ClientBag.prototype.setName = function (name) { this._name = name; return this; }; ClientBag.prototype.getName = function () { return this._name; }; ClientBag.prototype.setAutoSave = function (autoSave) { this._autoSave = autoSave; return this; }; //noinspection JSUnusedGlobalSymbols ClientBag.prototype.getAutoSave = function () { return this._autoSave; }; /** * Get key that we'll use to store data in browser * @returns {String} */ ClientBag.prototype.getStorageKey = function () { return 'econda.storage.ClientBag.' + this._name; }; /** * Get list of all loaded variables * @returns {econda.storage.Variable[]} */ ClientBag.prototype.getAllVariables = function () { var ret = []; for (var variableName in this._variables) { ret.push(this._variables[variableName]); } return ret; }; /** * Get variable by name * @param {String} variableName * @returns {econda.storage.Variable} variable or null */ ClientBag.prototype.get = function (variableName) { if (typeof this._variables[variableName] === 'undefined') { return null; } else { return this._variables[variableName]; } }; /** * Get state info for variable * @returns {Object} */ ClientBag.prototype.getState = function (variableName) { if (typeof this._variableStates[variableName] === 'undefined') { return null; } else { return this._variableStates[variableName]; } }; ClientBag.prototype.set = function (variable, replace) { if (replace === void 0) { replace = false; } variable = new Variable(variable); if (!variable.getName()) { throw 'Cannot set variable. Name attribute is required but empty.'; } var variableName = variable.getName(); if (replace === true || typeof this._variables[variableName] === 'undefined') { this._variables[variableName] = variable; } this._variableStates[variableName] = { added: new Date(), pageViews: 0 }; if (this._autoSave) { this._triggerSaveInBrowser(); } return this; }; /** * Update variable properties */ ClientBag.prototype.update = function (variableName, properties) { var c = this.get(variableName); if (c !== null) { c.set(properties); } if (this._autoSave) { this._triggerSaveInBrowser(); } return this; }; /** * Triggers save operation. Will wait 100ms so we'll have one save for multiple changes * @private */ ClientBag.prototype._triggerSaveInBrowser = function () { var cmp = this; if (this._triggerSaveTimeout !== null) { window.clearTimeout(this._triggerSaveTimeout); } this._triggerSaveTimeout = window.setTimeout(function () { cmp.saveInBrowser(); }, 100); }; /** * Save all data to browser. If using the default bag, there's no reason to call this function. */ ClientBag.prototype.saveInBrowser = function () { var forSessionStorage = { state: {}, variables: [] }; var forLocalStorage = { state: {}, variables: [] }; for (var variableName in this._variables) { var c = this._variables[variableName]; if (c.getPermanent()) { forLocalStorage.variables.push(c); if (typeof this._variableStates[variableName] !== 'undefined') { forLocalStorage.state[variableName] = this._variableStates[variableName]; } } else { forSessionStorage.variables.push(c); if (typeof this._variableStates[variableName] !== 'undefined') { forSessionStorage.state[variableName] = this._variableStates[variableName]; } } } var s = new econda.serialization.JsonSerializer(); LocalStorage.setItem(this.getStorageKey(), s.serialize(forLocalStorage)); SessionStorage.setItem(this.getStorageKey(), s.serialize(forSessionStorage)); this._lastSaved = new Date(); return this; }; /** * Load data from browser. If using the default bag, there's no reason to call this function */ ClientBag.prototype.loadFromBrowser = function () { var dataFromSessionStorage; var dataFromLocalStorage; var s = new econda.serialization.JsonSerializer(); dataFromLocalStorage = LocalStorage.getItem(this.getStorageKey()); dataFromSessionStorage = SessionStorage.getItem(this.getStorageKey()); if (dataFromLocalStorage) { var objectsFromLocalStorage = s.deserialize(dataFromLocalStorage); if (typeof objectsFromLocalStorage.state === 'undefined') { objectsFromLocalStorage.state = {}; } for (var n = 0; n < objectsFromLocalStorage.variables.length; n++) { var variableName = objectsFromLocalStorage.variables[n].getName(); this._variables[variableName] = objectsFromLocalStorage.variables[n]; if (typeof objectsFromLocalStorage.state[variableName] === 'object' && objectsFromLocalStorage.state[variableName] !== null) { this._variableStates[variableName] = objectsFromLocalStorage.state[variableName]; } } } if (dataFromSessionStorage) { var objectsFromSessionStorage = s.deserialize(dataFromSessionStorage); if (typeof objectsFromSessionStorage.state === 'undefined') { objectsFromSessionStorage.state = {}; } for (var n = 0; n < objectsFromSessionStorage.variables.length; n++) { var variableName = objectsFromSessionStorage.variables[n].getName(); this._variables[variableName] = objectsFromSessionStorage.variables[n]; if (typeof objectsFromSessionStorage.state[variableName] === 'object' && objectsFromSessionStorage.state[variableName] !== null) { this._variableStates[variableName] = objectsFromSessionStorage.state[variableName]; } } } return this; }; /** * Remove outdated variables. */ ClientBag.prototype.init = function () { this.loadFromBrowser() ._initVariablesAndState() .saveInBrowser(); return this; }; ClientBag.prototype._initVariablesAndState = function () { for (var variableName in this._variables) { this._updateVariableState(this._variables[variableName]); } // cleanup unused states for (var variableName in this._variableStates) { if (typeof this._variables[variableName] === 'undefined') { delete this._variableStates[variableName]; } } return this; }; ClientBag.prototype._updateVariableState = function (variable) { var variableName = variable.getName(); var state = (typeof this._variableStates[variableName] !== 'undefined') ? this._variableStates[variableName] : { added: new Date(), pageViews: 0 }; // update state state.pageViews++; // check if variable is still valid var isValid = true; var maxPageViews = variable.getPageViewsToLive(); isValid = isValid && (maxPageViews === null || state.pageViews <= maxPageViews); var expires = variable.getExpires(); isValid = isValid && (expires === null || expires >= (new Date())); // invalidate if required if (isValid === false) { this._invalidateVariable(variableName); } return this; }; ClientBag.prototype._invalidateVariable = function (variableName) { var c = this._variables[variableName]; switch (c.getInvalidationAction()) { case Variable.INVALIDATION_ACTION_REMOVE: this.destroy(variableName); break; case Variable.INVALIDATION_ACTION_KEEP: c._setIsValid(false); break; default: throw "Invalid invalidation action."; } return this; }; /** * Remove and destroy variable. * @param {String} variableName * @chainable */ ClientBag.prototype.destroy = function (variableName) { delete this._variables[variableName]; delete this._variableStates[variableName]; return this; }; return ClientBag; }()); storage.ClientBag = ClientBag; })(storage = econda.storage || (econda.storage = {})); })(econda || (econda = {}));