UNPKG

fluxtuate

Version:

a javascript ES7 library for handling complex data transactions

554 lines (456 loc) 21.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _class; var _getOwnKeys = require("../utils/getOwnKeys"); var _getOwnKeys2 = _interopRequireDefault(_getOwnKeys); var _forEachPrototype = require("../utils/forEachPrototype"); var _forEachPrototype2 = _interopRequireDefault(_forEachPrototype); var _internals = require("./_internals"); var _retainEventDispatcher = require("../event-dispatcher/retain-event-dispatcher"); var _retainEventDispatcher2 = _interopRequireDefault(_retainEventDispatcher); var _internals2 = require("../event-dispatcher/_internals"); var _lang = require("lodash/lang"); var _reserved = require("./reserved"); var _reserved2 = _interopRequireDefault(_reserved); var _coreDecorators = require("core-decorators"); var _deepData = require("./deep-data"); var _deepData2 = _interopRequireDefault(_deepData); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var calculatedCache = Symbol("fluxtuateModel_calculatedCache"); var calculatedCacheValid = Symbol("fluxtuateModel_calculatedCacheValid"); var data = Symbol("fluxtuateModel_data"); var calculatedFields = Symbol("fluxtuateModel_calculatedFields"); var dispatchUpdate = Symbol("fluxtuateModel_dispatchUpdate"); var isDefault = Symbol("fluxtuateModel_isDefault"); var propertiesCache = Symbol("fluxtuateModel_propertiesCache"); var dataCache = Symbol("fluxtuateModel_dataCache"); var cleanDataCache = Symbol("fluxtuateModel_cleanDataCache"); var dataCacheValid = Symbol("fluxtuateModel_cacheValid"); var cleanCacheValid = Symbol("fluxtuateModel_cleanCacheValid"); var mName = Symbol("fluxtuateModel_mName"); var updateTimeout = Symbol("fluxtuateModel_updateTimeout"); var pCacheIndex = []; var pCache = []; function getPropertiesCache(model) { var proto = Object.getPrototypeOf(model); var pCacheI = pCacheIndex.indexOf(proto); var returnCache = void 0; if (pCacheI === -1) { returnCache = {}; (0, _forEachPrototype2.default)(model, function (proto) { if (Object.getOwnPropertySymbols(proto).indexOf(_internals.properties) !== -1) returnCache = Object.assign(returnCache, proto[_internals.properties]); }, Model); pCache.push(returnCache); pCacheIndex.push(proto); } else { returnCache = pCache[pCacheI]; } var realReturnCache = {}; var keys = Object.keys(returnCache); for (var i = 0; i < keys.length; i++) { realReturnCache[keys[i]] = Object.assign({}, returnCache[keys[i]]); } return realReturnCache; } var iCacheIndex = []; var iCache = []; function getCacheForType(modelType) { var iCacheI = iCacheIndex.indexOf(modelType); var modelCache = void 0; if (iCacheI === -1) { modelCache = []; iCacheIndex.push(modelType); iCache.push(modelCache); } else { modelCache = iCache[iCacheI]; } return modelCache; } var Model = (0, _coreDecorators.autobind)(_class = function (_RetainEventDispatche) { _inherits(Model, _RetainEventDispatche); _createClass(Model, null, [{ key: "getInstance", value: function getInstance(modelType, modelName) { var cache = getCacheForType(modelType); var model = void 0; if (cache.length > 0) { model = cache.shift(); model[mName] = modelName; model[_internals.configureDefaultValues](); } else { model = new modelType(modelName); var props = model[propertiesCache]; for (var k in props) { if (model[props[k].modelKey] !== undefined && props[k].defaultValue === undefined) props[k].defaultValue = model[props[k].modelKey]; } model[_internals.configureDefaultValues](); } return model; } }]); function Model(modelName) { _classCallCheck(this, Model); var _this = _possibleConstructorReturn(this, (Model.__proto__ || Object.getPrototypeOf(Model)).call(this)); _this[_internals.dataType] = "object"; var realSend = _this[_internals2.sendEvent]; _this[_internals2.sendEvent] = function (eventName, payload, eventMetaData) { if (payload.data !== _this.modelData) { _this.setValue(payload.data, payload[_internals.elementResponsible]); } realSend(eventName, payload, eventMetaData); }; _this[mName] = modelName; _this[data] = {}; _this[dataCacheValid] = false; _this[cleanCacheValid] = false; _this[calculatedCacheValid] = false; _this[calculatedCache] = {}; _this[propertiesCache] = getPropertiesCache(_this); _this[_internals.configureDefaultValues] = function () { var shouldUpdate = false; var props = _this[propertiesCache]; var _loop = function _loop(k) { var keyDescriptor = Object.getOwnPropertyDescriptor(_this, props[k].modelKey); if (keyDescriptor && keyDescriptor.configurable) { var self = _this; Object.defineProperty(_this, props[k].modelKey, { get: function get() { return self[data][k]; }, configurable: false }); } if (props[k].defaultValue !== undefined && _this[data][k] === undefined) { _this[data][k] = props[k].convert(props[k].defaultValue); shouldUpdate = true; if (_this[data][k] && (0, _lang.isFunction)(_this[data][k].addListener)) { props[k].listener = _this[data][k].addListener("update", function (ev, payload) { _this[dispatchUpdate](payload[_internals.elementResponsible]); }, 0, false); } props[k][isDefault] = true; } }; for (var k in props) { _loop(k); } if (shouldUpdate) { _this[dispatchUpdate](_this); } }; Object.defineProperty(_this, "modelData", { get: function get() { var _this2 = this; if (!this[calculatedCacheValid]) { this[calculatedFields].forEach(function (k) { _this2[calculatedCache][k] = _this2[k]; }); this[calculatedCacheValid] = true; this[dataCacheValid] = false; this[cleanCacheValid] = false; } if (!this[dataCacheValid]) { var calcs = this[calculatedCache]; var mData = {}; var props = this[propertiesCache]; for (var k in props) { if (this[data][k] !== undefined) { mData[k] = this[data][k]; } else { mData[k] = props[k].convert(props[k].defaultValue); } } this[dataCache] = (0, _deepData2.default)(Object.assign(mData, calcs), "modelData"); this[dataCacheValid] = true; } return this[dataCache]; } }); Object.defineProperty(_this, "cleanData", { get: function get() { var _this3 = this; if (!this[calculatedCacheValid]) { this[calculatedFields].forEach(function (k) { _this3[calculatedCache][k] = _this3[k]; }); this[calculatedCacheValid] = true; this[dataCacheValid] = false; this[cleanCacheValid] = false; } if (!this[cleanCacheValid]) { var calcs = this[calculatedCache]; var mData = {}; var props = this[propertiesCache]; for (var k in props) { if (this[data][k] !== undefined && !props[k][isDefault]) mData[k] = this[data][k]; } this[cleanDataCache] = (0, _deepData2.default)(Object.assign(mData, calcs), "cleanData"); this[cleanCacheValid] = true; } return this[cleanDataCache]; } }); Object.defineProperty(_this, "modelName", { get: function get() { return this[mName]; } }); _this[calculatedFields] = []; var descr = {}; (0, _forEachPrototype2.default)(_this, function (proto) { descr = Object.assign(descr, Object.getOwnPropertyDescriptors(proto)); }, Model); Object.keys(descr).forEach(function (key) { if (_reserved2.default.indexOf(key) !== -1) return; if (descr[key].set) { var value = void 0; try { value = _this[key]; } catch (e) { throw new Error("Setters are not supported in models, use pure functions to update data with logic"); } _this[key] = function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } value.apply(_this, args); _this[_internals.configureDefaultValues](); _this[dispatchUpdate](); }; } else { if (descr[key].get) { _this[calculatedFields].push(key); } } }); _this[dispatchUpdate] = function (elementR) { var _this4 = this; if (this[updateTimeout]) { clearTimeout(this[updateTimeout]); this[updateTimeout] = null; } this[calculatedCacheValid] = false; this[cleanCacheValid] = false; this[dataCacheValid] = false; this[updateTimeout] = setTimeout(function () { if (!_this4[calculatedCacheValid]) { _this4[calculatedFields].forEach(function (k) { _this4[calculatedCache][k] = _this4[k]; }); _this4[calculatedCacheValid] = true; } var payload = { data: _this4.modelData, name: _this4.modelName }; payload[_internals.elementResponsible] = elementR; _this4.dispatch("update", payload); _this4[updateTimeout] = null; }, 100); }; return _this; } _createClass(Model, [{ key: "addProperty", value: function addProperty(key, type, defaultValue) { if (this[propertiesCache][key]) { throw new Error("Model already has a property with key " + key); } if (_reserved2.default.indexOf(key) !== -1) { throw new Error("The key " + key + " is a reserved word, you can't add this key to a model!"); } this[propertiesCache][key] = { modelKey: key, convert: type, defaultValue: defaultValue }; this[_internals.configureDefaultValues](); this[dispatchUpdate](); } }, { key: "forceUpdate", value: function forceUpdate() { this[dispatchUpdate](); } }, { key: "setKeyValue", value: function setKeyValue(modelKey, v, elementR) { var _this5 = this; var props = this[propertiesCache]; var key = void 0; for (var k in props) { if (props.hasOwnProperty(k) && props[k].modelKey === modelKey) { key = k; break; } } if (key === undefined) return; var value = v; if (value && value.modelData) { value = value.modelData; } if (props[key].listener) { props[key].listener.remove(); props[key].listener = undefined; } if (value !== undefined && this[data][key] && (0, _lang.isFunction)(this[data][key].setValue)) { this[data][key].setValue(value, elementR); } else { if (this[data][key] && (0, _lang.isFunction)(this[data][key].destroy)) { this[data][key].destroy(); } this[data][key] = props[key].convert(value, this.modelName, key); } if (this[data][key] && (0, _lang.isFunction)(this[data][key].addListener)) { props[key].listener = this[data][key].addListener("update", function (ev, payload) { _this5[dispatchUpdate](payload[_internals.elementResponsible]); }, 0, false); } props[key][isDefault] = false; this[dispatchUpdate](elementR); } }, { key: "setValue", value: function setValue(v, elementResponsible) { var value = v; if (value && value.modelData) { value = value.modelData; } this.clear(); this.update(value, elementResponsible, true); } }, { key: "update", value: function update(updateData, elementR) { var _this6 = this; var resetChildren = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; if (updateData.cleanData) { updateData = updateData.cleanData; } else if (updateData.modelData) { updateData = updateData.modelData; } var keys = (0, _getOwnKeys2.default)(updateData); var _loop2 = function _loop2(i) { var key = keys[i]; var props = _this6[propertiesCache]; if (props[key] === undefined) return "continue"; var modelKey = key; if ((0, _lang.isObject)(props[key])) { var potentialKeyValue = updateData[key]; if (potentialKeyValue && potentialKeyValue.modelData) { potentialKeyValue = potentialKeyValue.modelData; } if (potentialKeyValue === undefined) { if (_this6[data][key] && (0, _lang.isFunction)(_this6[data][key].destroy)) { _this6[data][key].destroy(); } _this6[data][key] = undefined; } else if (_this6[data][key] && !resetChildren && (0, _lang.isFunction)(_this6[data][key].update)) { _this6[data][key].update(potentialKeyValue, elementR); } else if (_this6[data][key] && resetChildren && (0, _lang.isFunction)(_this6[data][key].setValue)) { _this6[data][key].setValue(potentialKeyValue, elementR); } else { if ((0, _lang.isFunction)(props[key].convert)) { potentialKeyValue = props[key].convert(potentialKeyValue, _this6.modelName, key); } modelKey = props[key].modelKey; if (_this6[data][key] !== undefined && _this6[data][key] !== null && (0, _lang.isFunction)(_this6[data][key].merge) && potentialKeyValue !== undefined && potentialKeyValue !== null && (0, _lang.isFunction)(potentialKeyValue.merge)) { _this6[data][key].merge(elementR, potentialKeyValue); } else if (potentialKeyValue !== undefined && potentialKeyValue !== null) { if (_this6[data][key] && (0, _lang.isFunction)(_this6[data][key].destroy)) { _this6[data][key].destroy(); } _this6[data][key] = potentialKeyValue; } } } else { if (_this6[data][key] && (0, _lang.isFunction)(_this6[data][key].destroy)) { _this6[data][key].destroy(); } _this6[data][key] = updateData[key]; } if (props[key].listener) { props[key].listener.remove(); props[key].listener = undefined; } if (_this6[data][key] && (0, _lang.isFunction)(_this6[data][key].addListener)) { props[key].listener = _this6[data][key].addListener("update", function (ev, payload) { _this6[dispatchUpdate](payload[_internals.elementResponsible]); }, 0, false); } var keyDescriptor = Object.getOwnPropertyDescriptor(_this6, props[key].modelKey); if (keyDescriptor && keyDescriptor.configurable) { var self = _this6; Object.defineProperty(_this6, modelKey, { get: function get() { return self[data][key]; }, configurable: false }); } props[key][isDefault] = false; }; for (var i = 0; i < keys.length; i++) { var _ret2 = _loop2(i); if (_ret2 === "continue") continue; } this[dispatchUpdate](elementR); } }, { key: "clear", value: function clear(elementResponsible) { var props = this[propertiesCache]; for (var _key2 in props) { if (props[_key2] && props[_key2].listener) { props[_key2].listener.remove(); props[_key2].listener = undefined; } } for (var _key3 in this[data]) { if (this[data][_key3] && this[data][_key3].destroy) { this[data][_key3].destroy(); } } this[data] = {}; this[_internals.configureDefaultValues](); if (elementResponsible) { this[dispatchUpdate](elementResponsible); } } }, { key: "compare", value: function compare(model) { if (!(model instanceof this.constructor)) { return false; } return this[this[_internals.primaryKey]] === model[model[_internals.primaryKey]]; } }, { key: "onUpdate", value: function onUpdate(callback) { return this.addListener("update", function (ev, payload) { callback(payload); }); } }, { key: "destroy", value: function destroy() { this.clear(this); this.dispatch("destroy", { data: {} }); this[dataCacheValid] = false; this[cleanCacheValid] = false; this[calculatedCacheValid] = false; this[_internals2.destroy](); var cache = getCacheForType(Object.getPrototypeOf(this)); cache.push(this); if (this[updateTimeout]) { clearTimeout(this[updateTimeout]); this[updateTimeout] = null; } } }]); return Model; }(_retainEventDispatcher2.default)) || _class; exports.default = Model;