UNPKG

fluxtuate

Version:

a javascript ES7 library for handling complex data transactions

613 lines (508 loc) 28.4 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 _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;