| Current Path : /var/www/element/data/www/revenuestory.ru/bitrix/js/ui/vue/vuex/src/builder/ |
| Current File : /var/www/element/data/www/revenuestory.ru/bitrix/js/ui/vue/vuex/src/builder/model.js |
/**
* Bitrix Vuex wrapper
* Interface Vuex model (Vuex builder model)
*
* @package bitrix
* @subpackage ui
* @copyright 2001-2019 Bitrix
*/
import {BitrixVue} from "ui.vue";
import {Vuex} from "../vuex.js";
import {VuexBuilder} from "./builder.js";
import {VuexBuilderDatabaseIndexedDB} from "./database/indexeddb.js";
import {VuexBuilderDatabaseLocalStorage} from "./database/localstorage.js";
import {VuexBuilderDatabaseJnSharedStorage} from "./database/jnsharedstorage.js";
export class VuexBuilderModel
{
/**
* Create new instance of model.
*
* @returns {VuexBuilderModel}
*/
static create()
{
return new this;
}
/**
* Get name of model
*
* @override
*
* @returns {String}
*/
getName()
{
return '';
}
/**
* Get default state
*
* @override
*
* @returns {Object}
*/
getState()
{
return {};
}
/**
* Get default element state for models with collection.
*
* @override
*
* @returns {Object}
*/
getElementState(params = {}): object
{
return {};
}
/**
* Get object containing fields to exclude during the save to database.
*
* @override
*
* @returns {Object}
*/
getStateSaveException()
{
return undefined;
}
/**
* Get getters
*
* @override
*
* @returns {Object}
*/
getGetters()
{
return {};
}
/**
* Get actions
*
* @override
*
* @returns {Object}
*/
getActions()
{
return {};
}
/**
* Get mutations
*
* @override
*
* @returns {Object}
*/
getMutations()
{
return {};
}
/**
* Method for validation and sanitizing input fields before save in model
*
* @override
*
* @param fields {Object}
* @param options {Object}
*
* @returns {Object} - Sanitizing fields
*/
validate(fields, options = {})
{
return {};
}
/**
* Set external variable.
*
* @param variables {Object}
* @returns {VuexBuilderModel}
*/
setVariables(variables = {})
{
if (!(typeof variables === 'object' && variables))
{
this.logger('error', 'VuexBuilderModel.setVars: passed variables is not a Object', store);
return this;
}
this.variables = variables;
return this;
}
getVariable(name, defaultValue = undefined)
{
if (!name)
{
return defaultValue;
}
let nameParts = name.toString().split('.');
if (nameParts.length === 1)
{
return this.variables[nameParts[0]];
}
let result;
let variables = Object.assign({}, this.variables);
for (let i = 0; i < nameParts.length; i++)
{
if (typeof variables[nameParts[i]] !== 'undefined')
{
variables = result = variables[nameParts[i]];
}
else
{
result = defaultValue;
break;
}
}
return result;
}
/**
* Get namespace
*
* @returns {String}
*/
getNamespace()
{
return this.namespace? this.namespace: this.getName();
}
/**
* Set namespace
*
* @param name {String}
*
* @returns {VuexBuilderModel}
*/
setNamespace(name)
{
this.namespace = name.toString();
this.databaseConfig.name = this.namespace;
return this;
}
/**
* Set database config for model or disable this feature.
*
* @param active {boolean}
* @param config {{name: String, siteId: String, userId: Number, type: VuexBuilder.DatabaseType}}
*
* @returns {VuexBuilderModel}
*/
useDatabase(active, config = {})
{
this.databaseConfig.active = !!active;
let updateDriver = this.db === null;
if (config.type)
{
this.databaseConfig.type = config.type.toString();
updateDriver = true;
}
if (config.storage)
{
this.databaseConfig.storage = config.storage.toString();
}
if (config.siteId)
{
this.databaseConfig.siteId = config.siteId.toString();
}
if (config.userId)
{
this.databaseConfig.userId = config.userId;
}
if (typeof config.timeout === 'number')
{
this.databaseConfig.timeout = config.timeout;
}
if (!this.databaseConfig.active && this.db !== null)
{
this.databaseConfig.type = null;
updateDriver = true;
}
if (updateDriver)
{
if (this.databaseConfig.type === VuexBuilder.DatabaseType.indexedDb)
{
this.db = new VuexBuilderDatabaseIndexedDB(this.databaseConfig);
}
else if (this.databaseConfig.type === VuexBuilder.DatabaseType.localStorage)
{
this.db = new VuexBuilderDatabaseLocalStorage(this.databaseConfig);
}
else if (this.databaseConfig.type === VuexBuilder.DatabaseType.jnSharedStorage)
{
this.db = new VuexBuilderDatabaseJnSharedStorage(this.databaseConfig);
}
else
{
this.db = null;
}
}
return this;
}
/**
* @returns {VuexBuilderModel}
* @deprecated
*/
useNamespace(active)
{
if (BitrixVue.developerMode)
{
if (active)
{
console.warn('VuexBuilderModel: Method `useNamespace` is deprecated, please remove this call.');
}
else
{
console.error('VuexBuilderModel: Method `useNamespace` is deprecated, using VuexBuilder without namespaces is no longer supported.');
}
}
return this;
}
/**
* @returns {Promise}
* @deprecated use getModule instead.
*/
getStore()
{
return this.getModule();
}
/**
* Get Vuex module.
*
* @returns {Promise}
*/
getModule()
{
return new Promise((resolve, reject) => {
let namespace = this.namespace? this.namespace: this.getName();
if (!namespace)
{
this.logger('error', 'VuexBuilderModel.getStore: current model can not be run in Vuex modules mode', this.getState());
reject();
}
if (this.db)
{
this._getStoreFromDatabase().then(state => resolve({
namespace,
module: this._createStore(state)
}));
}
else
{
resolve({
namespace,
module: this._createStore(this.getState())
});
}
});
}
/**
* Get default state of Vuex module.
*
* @returns {Object}
*/
getModuleWithDefaultState()
{
let namespace = this.namespace? this.namespace: this.getName();
if (!namespace)
{
this.logger('error', 'VuexBuilderModel.getStore: current model can not be run in Vuex modules mode', this.getState());
return null;
}
return {
namespace,
module: this._createStore(this.getState())
};
}
/**
* Get timeout for save to database
*
* @override
*
* @returns {number}
*/
getSaveTimeout()
{
return 150;
}
/**
* Get timeout for load from database
*
* @override
*
* @returns {number|boolean}
*/
getLoadTimeout()
{
return 1000;
}
/**
* Get state after load from database
*
* @param state {Object}
*
* @override
*
* @returns {Object}
*/
getLoadedState(state = {})
{
return state;
}
/**
* Save current state after change state to database
*
* @param state {Object|function}
*
* @returns {Promise}
*/
saveState(state = {})
{
if (!this.isSaveAvailable())
{
return true;
}
this.lastSaveState = state;
if (this.saveStateTimeout)
{
this.logger('log', 'VuexModel.saveState: wait save...', this.getName());
return true;
}
this.logger('log', 'VuexModel.saveState: start saving', this.getName());
let timeout = this.getSaveTimeout();
if (typeof this.databaseConfig.timeout === 'number')
{
timeout = this.databaseConfig.timeout;
}
this.saveStateTimeout = setTimeout(() =>
{
this.logger('log', 'VuexModel.saveState: saved!', this.getName());
let lastState = this.lastSaveState;
if (typeof lastState === 'function')
{
lastState = lastState();
if (typeof lastState !== 'object' || !lastState)
{
return false;
}
}
this.db.set(
this.cloneState(lastState, this.getStateSaveException())
);
this.lastState = null;
this.saveStateTimeout = null;
}, timeout);
return true
}
/**
* Reset current store to default state
**
* @returns {Promise}
*/
clearState()
{
if (this.store)
{
this.store.commit(this.getNamespace()+'/'+'vuexBuilderModelClearState');
return true;
}
return this.saveState(
this.getState()
);
}
/**
* Clear database only, store state does not change
**
* @returns {Promise}
*/
clearDatabase()
{
if (!this.isSaveAvailable())
{
return true;
}
this.db.clear();
return true;
}
isSaveAvailable()
{
return this.db && this.databaseConfig.active;
}
isSaveNeeded(payload)
{
if (!this.isSaveAvailable())
{
return false;
}
let checkFunction = function(payload, filter = null)
{
if (!filter)
{
return true;
}
for (let field in payload)
{
if (!payload.hasOwnProperty(field))
{
continue;
}
if (typeof filter[field] === 'undefined')
{
return true;
}
else if (typeof filter[field] === 'object' && filter[field])
{
let result = checkFunction(payload[field], filter[field]);
if (result)
{
return true;
}
}
}
return false;
};
return checkFunction(payload, this.getStateSaveException());
}
/**
* Create new instance of model.
*/
constructor()
{
this.databaseConfig = {
type: VuexBuilder.DatabaseType.indexedDb,
active: null,
storage: 'default',
name: this.getName(),
siteId: 'default',
userId: 0,
timeout: null
};
this.db = null;
this.store = null;
this.namespace = null;
this.variables = {};
}
setStore(store)
{
if (!(store instanceof Vuex.Store))
{
this.logger('error', 'VuexBuilderModel.setStore: passed store is not a Vuex.Store', store);
return this;
}
this.store = store;
return this;
}
_getStoreFromDatabase()
{
clearTimeout(this.cacheTimeout);
return new Promise((resolve) =>
{
const loadTimeout = this.getLoadTimeout();
if ((loadTimeout !== false) && (typeof loadTimeout === 'number'))
{
this.cacheTimeout = setTimeout(() => {
this.logger('warn', 'VuexModel.getStoreFromDatabase: Cache loading timeout', this.getName());
resolve(this.getState());
}, loadTimeout);
}
else
{
this.cacheTimeout = null;
}
this.db.get().then(cache =>
{
clearTimeout(this.cacheTimeout);
cache = this.getLoadedState(cache? cache: {});
let state = this.getState();
if (cache)
{
state = this._mergeState(state, cache);
}
resolve(state);
}, (error) =>
{
clearTimeout(this.cacheTimeout);
resolve(this.getState());
})
});
}
_mergeState(currentState, newState)
{
for (let key in currentState)
{
if (!currentState.hasOwnProperty(key))
{
continue;
}
if (typeof newState[key] === 'undefined')
{
newState[key] = currentState[key];
}
else if (
!(newState[key] instanceof Array) &&
typeof newState[key] === 'object' && newState[key] &&
typeof currentState[key] === 'object' && currentState[key]
)
{
newState[key] = Object.assign({}, currentState[key], newState[key]);
}
}
return newState;
}
_createStore(state)
{
let result = {
namespaced: true,
state,
getters: this.getGetters(),
actions: this.getActions(),
mutations: this.getMutations()
};
result.mutations.vuexBuilderModelClearState = (state) => {
state = Object.assign(state, this.getState());
this.saveState(state);
};
return result;
}
/**
* Utils. Convert Object to Array
* @param object
* @returns {Array}
*/
static convertToArray(object)
{
let result = [];
for (let i in object)
{
if (object.hasOwnProperty(i))
{
result.push(object[i]);
}
}
return result;
}
/**
* Clone state without observers
* @param element {object}
* @param exceptions {object}
*/
cloneState(element, exceptions = undefined)
{
let result;
if (element instanceof Array)
{
result = [].concat(
element.map(element => this.cloneState(element))
);
}
else if (element instanceof Date)
{
result = new Date(element.toISOString());
}
else if (typeof element === 'object' && element)
{
result = {};
for (let param in element)
{
if (!element.hasOwnProperty(param))
{
continue;
}
if (
typeof exceptions === 'undefined'
|| typeof exceptions[param] === 'undefined'
)
{
result[param] = this.cloneState(element[param])
}
else if (typeof exceptions[param] === 'object' && exceptions[param])
{
result[param] = this.cloneState(element[param], exceptions[param])
}
}
}
else
{
result = element;
}
return result;
}
logger(type, ...args)
{
if (type === 'error')
{
console.error(...args);
return undefined;
}
else if (typeof BX.VueDevTools === 'undefined')
{
return undefined;
}
if (type === 'log')
{
console.log(...args);
}
else if (type === 'info')
{
console.info(...args);
}
else if (type === 'warn')
{
console.warn(...args);
}
}
}