fluxtuate
Version:
a javascript ES7 library for handling complex data transactions
613 lines (508 loc) • 28.4 kB
JavaScript
;
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 _eventDispatcher = require("../event-dispatcher");
var _eventDispatcher2 = _interopRequireDefault(_eventDispatcher);
var _lang = require("lodash/lang");
var _array = require("lodash/array");
var _internals = require("../context/_internals");
var _command = require("./command");
var _command2 = _interopRequireDefault(_command);
var _internals2 = require("./_internals");
var _internals3 = require("../guard/_internals");
var _commandModelWrapper = require("./command-model-wrapper");
var _commandModelWrapper2 = _interopRequireDefault(_commandModelWrapper);
var _model = require("../model");
var _model2 = _interopRequireDefault(_model);
var _modelWrapper = require("../model/model-wrapper");
var _modelWrapper2 = _interopRequireDefault(_modelWrapper);
var _internals4 = require("../model/_internals");
var _bluebird = require("bluebird");
var _bluebird2 = _interopRequireDefault(_bluebird);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
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 eventMap = Symbol("fluxtuateCommandMap_eventMap");
var addCommand = Symbol("fluxtuateCommandMap_addCommand");
var executeCommandFromEvent = Symbol("fluxtuateCommandMap_executeCommandFromEvent");
var eventDispatcher = Symbol("fluxtuateCommandMap_eventDispatcher");
var isPaused = Symbol("fluxtuateCommandMap_isPaused");
var commandsContext = Symbol("fluxtuateCommandMap_commandsContext");
var CommandMap = function (_EventDispatcher) {
_inherits(CommandMap, _EventDispatcher);
function CommandMap(ed, context) {
_classCallCheck(this, CommandMap);
var _this = _possibleConstructorReturn(this, (CommandMap.__proto__ || Object.getPrototypeOf(CommandMap)).call(this));
_this[commandsContext] = context;
_this[eventMap] = {};
_this[isPaused] = false;
_this[_internals2.pause] = function () {
_this[isPaused] = true;
};
_this[_internals2.resume] = function () {
_this[isPaused] = false;
};
_this[_internals2.destroy] = function () {
_this[eventMap] = {};
};
_this[addCommand] = function (eventName, command, commandProperties, oneShot) {
if (command === _command2.default || !(command.prototype instanceof _command2.default)) throw new Error("Commands must extend the Command class!");
if (!(0, _lang.isFunction)(command.prototype.execute)) {
throw new Error("Commands must implement a execute method!");
}
if (!_this[eventMap][eventName]) _this[eventMap][eventName] = {
listener: _this[eventDispatcher].capture(eventName, _this[executeCommandFromEvent]),
commands: []
};
var commandName = eventName + " (" + _this[eventMap][eventName].commands.length + ")";
var commandObject = {
command: command,
commandName: commandName,
commandProperties: commandProperties,
oneShot: oneShot
};
_this[eventMap][eventName].commands.push(commandObject);
return commandObject;
};
_this[executeCommandFromEvent] = function (event, payload) {
if (_this[isPaused]) return;
var eventName = event.eventName;
var commandCount = 0;
var completeCount = 0;
function errorForCommandOnEvent(command, eventName, payload, commandObject, error) {
var rootCommand = void 0;
if (commandObject.rootCommand) {
rootCommand = commandObject.rootCommand;
} else {
rootCommand = commandObject;
}
if (rootCommand.endings) {
for (var i = 0; i < rootCommand.endings.length; i++) {
processCommandGuard(eventName, payload, rootCommand.endings[i]);
}
}
throw new Error("There was a error while executing command " + command.commandName + " on event " + eventName + " with payload " + payload + "\n " + error.stack);
}
function completeCommandForEvent(command, eventName, payload, commandObject) {
if (commandObject.oneShot) {
this.unmapEvent(eventName, commandObject.command);
}
completeCount++;
this.dispatch("completeCommand", command);
if ((0, _lang.isFunction)(command.destroy)) {
command.destroy();
}
if (commandCount === completeCount) {
this.dispatch("complete", { event: eventName, payload: payload });
}
if (commandObject.events) {
for (var i = 0; i < commandObject.events.length; i++) {
ed.dispatch(commandObject.events[i].eventName, commandObject.events[i].payload || (commandObject.events[i].payloadProvider ? commandObject.events[i].payloadProvider(payload) : undefined));
}
}
if (commandObject.commandObjects) {
for (var _i = 0; _i < commandObject.commandObjects.length; _i++) {
processCommandGuard(eventName, payload, commandObject.commandObjects[_i]);
}
} else {
var rootCommand = void 0;
if (commandObject.rootCommand) {
rootCommand = commandObject.rootCommand;
} else {
rootCommand = commandObject;
}
if (rootCommand.endings) {
for (var _i2 = 0; _i2 < rootCommand.endings.length; _i2++) {
processCommandGuard(eventName, payload, rootCommand.endings[_i2]);
}
}
}
}
var executeInEvent = function executeInEvent(eventName, payload, commandObject) {
if (commandObject.hooks) {
commandObject.hooks.forEach(function (hookObject) {
var hook = void 0;
if (hookObject.hookProperties) {
var convertedProperties = hookObject.hookProperties.map(function (prop) {
if (prop instanceof _modelWrapper2.default) {
prop = prop[_internals4.model];
}
if (prop instanceof _model2.default) {
return new _modelWrapper2.default(prop, context);
}
return prop;
});
hook = new (Function.prototype.bind.apply(hookObject.hook, [_this].concat(_toConsumableArray(convertedProperties))))();
} else {
hook = new hookObject.hook();
}
context[_internals.applyGuardContext](hook, { payload: payload });
if (!(0, _lang.isFunction)(hook.hook)) {
throw new Error("Hooks must have a hook function! " + hook);
}
hook.dispatch = context.dispatch;
hook.hook();
});
}
commandCount++;
var command = _this.executeCommand.apply(_this, [eventName, commandObject.command, commandObject.payloadProvider ? commandObject.payloadProvider(payload) : payload].concat(_toConsumableArray(commandObject.commandProperties)));
if (!command.commandName) {
Object.defineProperty(command, "commandName", {
get: function get() {
return context.contextName + "->" + commandObject.commandName;
}
});
}
command.onComplete(completeCommandForEvent.bind(_this, command, eventName, payload, commandObject));
command.onError(errorForCommandOnEvent.bind(_this, command, eventName, payload, commandObject));
};
var processCommandGuard = function processCommandGuard(eventName, payload, commandObject) {
if (commandObject.guards) {
var guardPromises = commandObject.guards.map(function (guardObject) {
var guard = void 0;
if (guardObject.guardProperties) {
var convertedProperties = guardObject.guardProperties.map(function (prop) {
if (prop instanceof _modelWrapper2.default) {
prop = prop[_internals4.model];
}
if (prop instanceof _model2.default) {
return new _modelWrapper2.default(prop, context);
}
return prop;
});
guard = new (Function.prototype.bind.apply(guardObject.guard, [_this].concat(_toConsumableArray(convertedProperties))))();
} else {
guard = new guardObject.guard();
}
context[_internals.applyGuardContext](guard, { payload: payload });
if (!(0, _lang.isFunction)(guard[_internals3.approveGuard])) {
throw new Error("Guards must have be of type Guard! " + guard);
}
return guard[_internals3.approveGuard]().then(function (isApproved) {
if ((0, _lang.isFunction)(guard.destroy)) guard.destroy();
return guardObject.hasGuard && isApproved || !guardObject.hasGuard && !isApproved;
});
});
_bluebird2.default.all(guardPromises).then(function (results) {
for (var i = 0; i < results.length; i++) {
if (!results[i]) {
if (commandObject.commandObjects) {
for (var _i3 = 0; _i3 < commandObject.commandObjects.length; _i3++) {
processCommandGuard(eventName, payload, commandObject.commandObjects[_i3]);
}
} else {
var rootCommand = void 0;
if (commandObject.rootCommand) {
rootCommand = commandObject.rootCommand;
} else {
rootCommand = commandObject;
}
if (rootCommand.endings) {
for (var _i4 = 0; _i4 < rootCommand.endings.length; _i4++) {
processCommandGuard(eventName, payload, rootCommand.endings[_i4]);
}
}
}
return;
}
}
executeInEvent(eventName, payload, commandObject);
});
} else {
executeInEvent(eventName, payload, commandObject);
}
};
var commandMappings = _this[eventMap][eventName].commands.slice();
commandMappings.forEach(function (commandObject) {
if (commandObject.stopPropagation) {
event.stopPropagation();
}
processCommandGuard(eventName, payload, commandObject);
});
if (commandCount === 0) {
setTimeout(function () {
if (_this[commandsContext].destroyed) return;
_this.dispatch("complete", { event: eventName, payload: payload });
}, 0);
}
};
_this[eventDispatcher] = ed;
return _this;
}
_createClass(CommandMap, [{
key: "executeCommand",
value: function executeCommand(commandConstructor, payload) {
for (var _len = arguments.length, commandProperties = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
commandProperties[_key - 2] = arguments[_key];
}
var _this2 = this;
var command = void 0;
var eventName = "DirectCommandExecution";
if ((0, _lang.isString)(commandConstructor)) {
eventName = commandConstructor;
commandConstructor = payload;
payload = commandProperties[0];
commandProperties = commandProperties.slice(1);
}
if (commandProperties && commandProperties.length > 0) {
var injectedModels = [];
var convertedProperties = commandProperties.map(function (prop) {
if (prop instanceof _modelWrapper2.default) {
prop = prop[_internals4.model];
}
if (prop instanceof _model2.default) {
var modelWrapper = new _commandModelWrapper2.default(prop, _this2[commandsContext]);
injectedModels.push(modelWrapper);
return modelWrapper;
}
return prop;
});
command = new (Function.prototype.bind.apply(commandConstructor, [this].concat(_toConsumableArray(convertedProperties))))();
injectedModels.forEach(function (modelWrapper) {
modelWrapper[_internals2.command] = command;
});
} else {
command = new commandConstructor();
}
command[_internals2.event] = eventName;
command[_internals2.eventPayload] = payload;
this.dispatch("executeCommand", command);
setTimeout(function () {
if (_this2[commandsContext].destroyed) return;
_this2[commandsContext][_internals.applyCommandContext](command, { payload: payload });
var result = command.execute();
if (result && (0, _lang.isFunction)(result.then)) {
command[_internals2.release](result);
} else {
command[_internals2.release]();
}
}, 0);
return command;
}
}, {
key: "onComplete",
value: function onComplete(callback) {
if (!callback) return;
var listener = this.addListener("complete", function (ev, payload) {
callback(payload.event, payload.payload);
listener.remove();
});
}
}, {
key: "mapEvent",
value: function mapEvent(eventName) {
var self = this;
function mapEventReturn(commandObject) {
return {
withGuard: function withGuard(guardClass) {
for (var _len2 = arguments.length, guardProperties = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
guardProperties[_key2 - 1] = arguments[_key2];
}
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
if (!commandObject.guards) commandObject.guards = [];
commandObject.guards.push({
hasGuard: true,
guard: guardClass,
guardProperties: guardProperties
});
return mapEventReturn(commandObject);
},
withoutGuard: function withoutGuard(guardClass) {
for (var _len3 = arguments.length, guardProperties = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
guardProperties[_key3 - 1] = arguments[_key3];
}
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
if (!commandObject.guards) commandObject.guards = [];
commandObject.guards.push({
hasGuard: false,
guard: guardClass,
guardProperties: guardProperties
});
return mapEventReturn(commandObject);
},
withHook: function withHook(hookClass) {
for (var _len4 = arguments.length, hookProperties = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
hookProperties[_key4 - 1] = arguments[_key4];
}
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
if (!commandObject.hooks) commandObject.hooks = [];
commandObject.hooks.push({
hook: hookClass,
hookProperties: hookProperties
});
return mapEventReturn(commandObject);
},
stopPropagation: function stopPropagation() {
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
var rootObject = commandObject;
if (rootObject.rootCommand) {
rootObject = rootObject.rootCommand;
}
rootObject.stopPropagation = true;
return mapEventReturn(commandObject);
},
payloadProvider: function payloadProvider(providerFunction) {
if (!(0, _lang.isFunction)(providerFunction)) {
throw new Error("Payload provider must be a function that returns a payload value!");
}
commandObject.payloadProvider = providerFunction;
return mapEventReturn(commandObject);
},
toCommand: function toCommand(command) {
for (var _len5 = arguments.length, commandProps = Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
commandProps[_key5 - 1] = arguments[_key5];
}
var c = self[addCommand](eventName, command, commandProps, false);
if (commandObject) {
if (!commandObject.commandObjects) {
commandObject.commandObjects = [];
}
var commandIndex = self[eventMap][eventName].commands.indexOf(c);
if (commandIndex !== -1) {
self[eventMap][eventName].commands.splice(commandIndex, 1);
}
if (commandObject.rootCommand) {
c.rootCommand = commandObject.rootCommand;
} else if (commandObject && !commandObject.isEnding) {
c.rootCommand = commandObject;
}
commandObject.commandObjects.push(c);
}
return mapEventReturn(c);
},
endWith: function endWith(command) {
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
var rootCommand = void 0;
if (commandObject.rootCommand) {
rootCommand = commandObject.rootCommand;
} else {
rootCommand = commandObject;
}
for (var _len6 = arguments.length, commandProps = Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
commandProps[_key6 - 1] = arguments[_key6];
}
var c = self[addCommand](eventName, command, commandProps, false);
if (!rootCommand.endings) {
rootCommand.endings = [];
}
var commandIndex = self[eventMap][eventName].commands.indexOf(c);
if (commandIndex !== -1) {
self[eventMap][eventName].commands.splice(commandIndex, 1);
}
rootCommand.endings.push(c);
c.isEnding = true;
return mapEventReturn(c);
},
once: function once(command) {
for (var _len7 = arguments.length, commandProps = Array(_len7 > 1 ? _len7 - 1 : 0), _key7 = 1; _key7 < _len7; _key7++) {
commandProps[_key7 - 1] = arguments[_key7];
}
var c = self[addCommand](eventName, command, commandProps, true);
if (commandObject) {
if (!commandObject.commandObjects) {
commandObject.commandObjects = [];
}
var commandIndex = self[eventMap][eventName].commands.indexOf(c);
if (commandIndex !== -1) {
self[eventMap][eventName].commands.splice(commandIndex, 1);
}
if (commandObject.rootCommand) {
c.rootCommand = commandObject.rootCommand;
} else {
c.rootCommand = commandObject;
}
commandObject.commandObjects.push(c);
}
return mapEventReturn(c);
},
addEvent: function addEvent(addedEventName) {
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
if (!this[eventMap][eventName]) this[eventMap][eventName] = {
listener: this[eventDispatcher].capture(eventName, this[executeCommandFromEvent]),
commands: []
};
this[eventMap][addedEventName].commands.push(commandObject);
return mapEventReturn(commandObject);
},
withEvent: function withEvent(eventName, payload) {
if (!commandObject) {
throw new Error("No command is mapped yet!");
}
if (!commandObject.events) {
commandObject.events = [];
}
var event = {
eventName: eventName
};
if ((0, _lang.isFunction)(payload)) {
event.payloadProvider = payload;
} else {
event.payload = payload;
}
commandObject.events.push(event);
return mapEventReturn(commandObject);
}
};
}
return mapEventReturn();
}
}, {
key: "unmapEvent",
value: function unmapEvent(eventName, command) {
if (!this[eventMap][eventName]) return;
var index = (0, _array.findIndex)(this[eventMap][eventName].commands, { command: command });
if (index === -1) {
var commands = this[eventMap][eventName].commands;
if (!commands) return;
for (var i = 0; i < commands.length; i++) {
var _commandObject = commands[i];
while (_commandObject) {
var parentCommandObject = _commandObject;
_commandObject = _commandObject.commandObject;
if (_commandObject) {
if (_commandObject.command === command) {
parentCommandObject.commandObject = _commandObject.commandObject;
break;
}
}
}
}
return;
}
var commandObject = this[eventMap][eventName].commands[index];
if (commandObject.commandObject) {
this[eventMap][eventName].commands[index] = commandObject.commandObject;
} else {
this[eventMap][eventName].commands.splice(index, 1);
if (this[eventMap][eventName].commands.length === 0) {
this[eventMap][eventName].listener.remove();
this[eventMap][eventName] = undefined;
}
}
}
}, {
key: "hasEvent",
value: function hasEvent(eventName) {
return Boolean(this[eventMap][eventName] && this[eventMap][eventName].commands.length > 0);
}
}]);
return CommandMap;
}(_eventDispatcher2.default);
exports.default = CommandMap;