fluxtuate
Version:
a javascript ES7 library for handling complex data transactions
554 lines (456 loc) • 21.5 kB
JavaScript
"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;