2017-11-24 18:50:12 +01:00
|
|
|
import {
|
|
|
|
assign,
|
|
|
|
blankObject,
|
2018-02-11 23:32:48 +01:00
|
|
|
_differs,
|
|
|
|
_differsImmutable,
|
2017-11-24 18:50:12 +01:00
|
|
|
get,
|
2018-04-15 19:09:59 +02:00
|
|
|
on,
|
|
|
|
fire
|
2017-11-24 18:50:12 +01:00
|
|
|
} from './shared.js';
|
|
|
|
|
2018-02-09 05:04:29 +01:00
|
|
|
function Store(state, options) {
|
2018-04-15 19:09:59 +02:00
|
|
|
this._handlers = {};
|
2017-11-24 18:50:12 +01:00
|
|
|
this._dependents = [];
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
this._computed = blankObject();
|
|
|
|
this._sortedComputedProperties = [];
|
|
|
|
|
|
|
|
this._state = assign({}, state);
|
2018-02-11 23:32:48 +01:00
|
|
|
this._differs = options && options.immutable ? _differsImmutable : _differs;
|
2017-11-24 18:50:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
assign(Store.prototype, {
|
|
|
|
_add: function(component, props) {
|
|
|
|
this._dependents.push({
|
|
|
|
component: component,
|
|
|
|
props: props
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2017-11-24 19:56:32 +01:00
|
|
|
_init: function(props) {
|
|
|
|
var state = {};
|
2017-11-25 20:09:41 +01:00
|
|
|
for (var i = 0; i < props.length; i += 1) {
|
2017-11-24 19:56:32 +01:00
|
|
|
var prop = props[i];
|
|
|
|
state['$' + prop] = this._state[prop];
|
|
|
|
}
|
|
|
|
return state;
|
|
|
|
},
|
|
|
|
|
2017-11-24 18:50:12 +01:00
|
|
|
_remove: function(component) {
|
2017-11-25 20:09:41 +01:00
|
|
|
var i = this._dependents.length;
|
2017-11-24 18:50:12 +01:00
|
|
|
while (i--) {
|
|
|
|
if (this._dependents[i].component === component) {
|
|
|
|
this._dependents.splice(i, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-11-25 20:09:41 +01:00
|
|
|
_sortComputedProperties: function() {
|
2017-11-25 19:55:20 +01:00
|
|
|
var computed = this._computed;
|
|
|
|
var sorted = this._sortedComputedProperties = [];
|
|
|
|
var visited = blankObject();
|
2018-05-02 04:31:43 +02:00
|
|
|
var cycles = blankObject();
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2018-05-02 04:27:18 +02:00
|
|
|
function visit(key) {
|
2018-05-02 03:20:05 +02:00
|
|
|
if (visited[key]) return;
|
2018-05-02 04:27:18 +02:00
|
|
|
visited[key] = true;
|
2017-11-27 13:21:06 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
var c = computed[key];
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
if (c) {
|
2018-05-02 04:31:43 +02:00
|
|
|
if (!cycles[key]) cycles[key] = blankObject();
|
2018-05-02 04:27:18 +02:00
|
|
|
c.deps.forEach(dep => {
|
2018-05-02 04:31:43 +02:00
|
|
|
if (cycles[dep] && cycles[dep][key]) {
|
2018-05-02 04:27:18 +02:00
|
|
|
throw new Error(`Cyclical dependency detected between ${dep} <-> ${key}`);
|
|
|
|
}
|
2018-05-02 04:31:43 +02:00
|
|
|
cycles[key][dep] = true;
|
2018-05-02 04:27:18 +02:00
|
|
|
visit(dep);
|
2018-05-02 03:28:37 +02:00
|
|
|
});
|
2017-11-25 19:55:20 +01:00
|
|
|
sorted.push(c);
|
|
|
|
}
|
2017-11-25 18:07:28 +01:00
|
|
|
}
|
|
|
|
|
2017-11-27 13:21:06 +01:00
|
|
|
for (var key in this._computed) {
|
2018-05-02 04:27:18 +02:00
|
|
|
visit(key);
|
2017-11-27 13:21:06 +01:00
|
|
|
}
|
2017-11-25 19:55:20 +01:00
|
|
|
},
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
compute: function(key, deps, fn) {
|
|
|
|
var store = this;
|
|
|
|
var value;
|
|
|
|
|
|
|
|
var c = {
|
|
|
|
deps: deps,
|
|
|
|
update: function(state, changed, dirty) {
|
|
|
|
var values = deps.map(function(dep) {
|
|
|
|
if (dep in changed) dirty = true;
|
|
|
|
return state[dep];
|
|
|
|
});
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
if (dirty) {
|
|
|
|
var newValue = fn.apply(null, values);
|
2018-02-09 05:04:29 +01:00
|
|
|
if (store._differs(newValue, value)) {
|
2017-11-25 18:07:28 +01:00
|
|
|
value = newValue;
|
2017-11-25 19:55:20 +01:00
|
|
|
changed[key] = true;
|
|
|
|
state[key] = value;
|
2017-11-25 18:07:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-25 19:55:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
c.update(this._state, {}, true);
|
|
|
|
|
|
|
|
this._computed[key] = c;
|
|
|
|
this._sortComputedProperties();
|
2017-11-25 18:07:28 +01:00
|
|
|
},
|
|
|
|
|
2018-04-15 19:09:59 +02:00
|
|
|
fire: fire,
|
|
|
|
|
2017-11-25 20:09:41 +01:00
|
|
|
get: get,
|
|
|
|
|
2018-04-15 19:09:59 +02:00
|
|
|
on: on,
|
2018-02-23 14:47:49 +01:00
|
|
|
|
2017-11-24 18:50:12 +01:00
|
|
|
set: function(newState) {
|
|
|
|
var oldState = this._state,
|
2017-11-25 18:07:28 +01:00
|
|
|
changed = this._changed = {},
|
2017-11-24 18:50:12 +01:00
|
|
|
dirty = false;
|
|
|
|
|
|
|
|
for (var key in newState) {
|
2017-11-25 19:55:20 +01:00
|
|
|
if (this._computed[key]) throw new Error("'" + key + "' is a read-only property");
|
2018-02-09 05:04:29 +01:00
|
|
|
if (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;
|
2017-11-24 18:50:12 +01:00
|
|
|
}
|
|
|
|
if (!dirty) return;
|
|
|
|
|
2018-03-27 22:21:29 +02:00
|
|
|
this._state = assign(assign({}, oldState), newState);
|
2017-11-25 18:07:28 +01:00
|
|
|
|
2017-11-25 19:55:20 +01:00
|
|
|
for (var i = 0; i < this._sortedComputedProperties.length; i += 1) {
|
|
|
|
this._sortedComputedProperties[i].update(this._state, changed);
|
|
|
|
}
|
2017-11-24 18:50:12 +01:00
|
|
|
|
2018-04-15 19:09:59 +02:00
|
|
|
this.fire('state', {
|
|
|
|
changed: changed,
|
|
|
|
current: this._state,
|
|
|
|
previous: oldState
|
|
|
|
});
|
2017-11-24 18:50:12 +01:00
|
|
|
|
|
|
|
var dependents = this._dependents.slice(); // guard against mutations
|
|
|
|
for (var i = 0; i < dependents.length; i += 1) {
|
|
|
|
var dependent = dependents[i];
|
|
|
|
var componentState = {};
|
|
|
|
dirty = false;
|
|
|
|
|
|
|
|
for (var j = 0; j < dependent.props.length; j += 1) {
|
|
|
|
var prop = dependent.props[j];
|
|
|
|
if (prop in changed) {
|
|
|
|
componentState['$' + prop] = this._state[prop];
|
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dirty) dependent.component.set(componentState);
|
|
|
|
}
|
|
|
|
|
2018-04-15 19:09:59 +02:00
|
|
|
this.fire('update', {
|
|
|
|
changed: changed,
|
|
|
|
current: this._state,
|
|
|
|
previous: oldState
|
|
|
|
});
|
2017-11-24 18:50:12 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-02-16 10:58:18 +01:00
|
|
|
export { Store };
|