@gamestdio/state-listener
Version:
Deeply compare JavaScript objects and listen to changes.
124 lines (123 loc) • 4.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var compare_1 = require("./compare");
var StateContainer = /** @class */ (function () {
function StateContainer(state) {
this.listeners = [];
this.matcherPlaceholders = {
":id": /^([a-zA-Z0-9\-_]+)$/,
":number": /^([0-9]+)$/,
":string": /^(\w+)$/,
":axis": /^([xyz])$/,
":*": /(.*)/,
};
this.state = state;
this.reset();
}
StateContainer.prototype.set = function (newState) {
var patches = compare_1.compare(this.state, newState);
this.state = newState;
this.checkPatches(patches, this.listeners, this.defaultListener);
return patches;
};
StateContainer.prototype.registerPlaceholder = function (placeholder, matcher) {
this.matcherPlaceholders[placeholder] = matcher;
};
StateContainer.prototype.listen = function (segments, callback, immediate) {
var _this = this;
var rules;
if (typeof (segments) === "function") {
rules = [];
callback = segments;
}
else {
rules = segments.split("/");
}
if (callback.length > 1) {
console.warn(".listen() accepts only one parameter.");
}
var listener = {
callback: callback,
rawRules: rules,
rules: rules.map(function (segment) {
if (typeof (segment) === "string") {
// replace placeholder matchers
return (segment.indexOf(":") === 0)
? _this.matcherPlaceholders[segment] || _this.matcherPlaceholders[":*"]
: new RegExp("^" + segment + "$");
}
else {
return segment;
}
})
};
if (rules.length === 0) {
this.defaultListener = listener;
}
else {
this.listeners.push(listener);
}
// immediatelly try to trigger this listener.
if (immediate) {
this.checkPatches(compare_1.compare({}, this.state), [listener]);
}
return listener;
};
StateContainer.prototype.removeListener = function (listener) {
for (var i = this.listeners.length - 1; i >= 0; i--) {
if (this.listeners[i] === listener) {
this.listeners.splice(i, 1);
}
}
};
StateContainer.prototype.removeAllListeners = function () {
this.reset();
};
StateContainer.prototype.checkPatches = function (patches, listeners, defaultListener) {
for (var j = 0, len = listeners.length; j < len; j++) {
var listener = listeners[j];
for (var i = patches.length - 1; i >= 0; i--) {
var pathVariables = listener && this.getPathVariables(patches[i], listener);
if (pathVariables) {
listener.callback({
path: pathVariables,
rawPath: patches[i].path,
operation: patches[i].operation,
value: patches[i].value
});
patches[i].matched = true;
}
}
}
// trigger default listener callback with each unmatched patch
if (defaultListener) {
for (var i = patches.length - 1; i >= 0; i--) {
if (!patches[i].matched) {
defaultListener.callback(patches[i]);
}
}
}
};
StateContainer.prototype.getPathVariables = function (patch, listener) {
// skip if rules count differ from patch
if (patch.path.length !== listener.rules.length) {
return false;
}
var path = {};
for (var i = 0, len = listener.rules.length; i < len; i++) {
var matches = patch.path[i].match(listener.rules[i]);
if (!matches || matches.length === 0 || matches.length > 2) {
return false;
}
else if (listener.rawRules[i].substr(0, 1) === ":") {
path[listener.rawRules[i].substr(1)] = matches[1];
}
}
return path;
};
StateContainer.prototype.reset = function () {
this.listeners = [];
};
return StateContainer;
}());
exports.StateContainer = StateContainer;